Message Boards Message Boards

Extending FindRoot

GROUPS:

The function FindRoot can only find one root; there is no way of finding multiple roots. User J. M. extended the FindRoot by the function FindAllCrossings by changing Stan Wagon's book Mathematica in Action slightly.

The implementation is pretty slick. You plot the function on a certain range and tell the Plot function to mesh the zeros, then you extract the zeros and use FindRoot with them as an initial condition.

But this implementation has a major drawback; it fails in cases where the function doesn't cross zero, as x^2 for example.

To extend J. M. implementation I added some more lines of code, and I've made the code more verbose. The new lines of code calculate the zeros of the derivative of the function and then test it is close to zero and later refine it with FindRoot, this works even with functions as Abs[x], which has discontinuous derivative and never crosses zero, but thanks to the interpolated result, it takes care of that.

Options@FindRoots = Sort@Join[Options@FindRoot, {MaxRecursion -> Automatic, PerformanceGoal :> $PerformanceGoal, PlotPoints -> Automatic, Debug -> False, ZeroTolerance -> 10^-2}];

FindRoots[fun_, {var_, min_, max_}, opts:OptionsPattern[]] := Module[{PlotRules, RootRules, g, g2, pts, pts2, lpts, F, sol},
    (* Extract the Options *)
    PlotRules = Sequence @@ FilterRules[Join[{opts}, Options@FindRoots], Options@Plot];
    RootRules = Sequence @@ FilterRules[Join[{opts}, Options@FindRoots], Options@FindRoot];

    (* Plot the function and "mesh" the point with y-coordinate 0 *)
    g = Normal@Plot[fun, {var, min, max}, MeshFunctions -> (#2 &), Mesh -> {{0}}, Method -> Automatic, Evaluate@PlotRules];

    (* Get the meshes zeros *)
    pts = Cases[g, Point[p_] :> SetPrecision[p[[1]], OptionValue@WorkingPrecision], Infinity];
    (* Get all plot points *)
    lpts = Join@@Cases[g, Line[p_] :> SetPrecision[p, OptionValue@WorkingPrecision], Infinity];

    (* Derive the interpolated data to find other zeros *)
    F = Interpolation[lpts, InterpolationOrder->2];
    g2 = Normal@Plot[Evaluate@D[F@var, var], {var, min, max}, MeshFunctions -> (#2 &), Mesh -> {{0}}, Method -> Automatic, Evaluate@PlotRules];

    (* Get the meshes zeros and retain only small ones *)
    pts2 = Cases[g2, Point[p_] :> SetPrecision[p[[1]], OptionValue@WorkingPrecision], Infinity];
    pts2 = Select[pts2, Abs[F@#] < OptionValue@ZeroTolerance &];
    pts = Join[pts, pts2]; (* Join all zeros *)

    (* Refine zeros by passing each point through FindRoot *)
    If[Length@pts > 0,
       pts = Map[FindRoot[fun, {var, #}, Evaluate@RootRules]&, pts];
       sol = Union@Select[pts, min <= Last@Last@# <= max &];
       (* For debug purposes *)
       If[OptionValue@Debug, Print@Show[g, Graphics@{PointSize@0.02, Red, Point[{var, fun} /. sol]}]];
       sol
    ,
       If[OptionValue@Debug, Print@g];
       {}
    ]
]

Example:

enter image description here

My primary use of this function is to find the eigenvalues of dielectric waveguides. They are found as the zeros of the boundary conditions.

POSTED BY: Thales Fernandes
Answer
24 days ago

Another use, finding zeroes of Zeta-function:

y /. FindRoots[Abs@Zeta[1/2 + I y], {y, 0, 100}, PlotPoints -> 500] // Quiet
Im@*ZetaZero /@ Range@Length@% // N;
(* Output *)
{14.1347, 21.022, 25.0109, 30.4249, 32.9351, 37.5862, 40.9187, \
43.3271, 48.0052, 49.7738, 52.9703, 56.4462, 59.347, 60.8318, \
65.1125, 67.0798, 69.5464, 72.0672, 75.7047, 77.1448, 79.3374, \
82.9104, 84.7355, 87.4253, 88.8091, 92.4919, 94.6513, 95.8706, \
98.8312}
(* Calculate error *)
%% - % // Abs // Mean;
(* Output *)
1.58635*10^-8
POSTED BY: Thales Fernandes
Answer
24 days ago

Group Abstract Group Abstract