Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

I have some questions for multiroot search for transcendental equations. Is there any clever solution to find all the roots for a transcendental equation in a specific range?

Perhaps FindRoot is the most efficient way to solve transcendental equations, but it only gives one root around a specific value. For example,

FindRoot[BesselJ[1, x]^2 + BesselK[1, x]^2 - Sin[Sin[x]], {x, 10}]

Of course, one can first Plot the equation and then choose several start values around each root and then use FindRoot to get the exact value.

  1. Is there any elegant way to find all the roots at once?

    Actually, I come up with this question when I solve the eigenequation for optical waveguides and I want to get the dispersion relation. I find ContourPlot is very useful to get the curve of the dispersion relation. For example,

    ContourPlot[BesselJ[1, x]^2 + BesselK[1, x]^2 - Sin@Sin[a*x] == 0, {x, 0, 10}, {a, 0, 4}]
    

    You can get

    enter image description here

  2. Is there any elegant way to get all the values in the ContourPlot for x when a==0 ?

  3. Is it possible to know how the ContourPlot gets all the points shown in the figure? Perhaps we can harness it to get all the roots for the transcendental equation.

share|improve this question
1  
You might be interested in this question. Anyway, for finding the roots of a function of a single variable, you can directly use the output of Plot[] to find initial approximations for FindRoot[]. If you're interested in that approach, I can write up an answer. –  Guess who it is. May 17 '12 at 8:09
    
@J.M. I'm expecting for your answer ~~ –  yulinlinyu May 17 '12 at 8:53
1  
A tiny comment on Reduce[]-based methods: all of these hinge on the fact that Reduce[] apparently knows quite a fair bit about the transcendental functions built within Mathematica. If, however, you are dealing with a black-box function that can only evaluate at numerical values (you can simulate this behavior with something like f[x_?NumericQ] := Haversine[Pi x]), then Reduce[] won't be able to do much. –  Guess who it is. May 17 '12 at 9:15
    
@J.M. Yes, I have tried Reduce[], and I find it is very slow, especially for a much more complex equation. –  yulinlinyu May 17 '12 at 9:50
2  
@yulinlinyu It seems you wanted to accept a few answers. You can accept ONLY ONE of the answers (clicking the green tick beside an answer), which you find the most helpful. You can upvote EVERY answer you find helpful or downvote if you find an answer misleading. –  Artes May 18 '12 at 1:57

8 Answers 8

up vote 18 down vote accepted

Borrowing almost verbatim from a recent response about finding extrema, here is a method that is useful when your function is differentiable and hence can be "tracked" by NDSolve.

f[x_] := BesselJ[1, x]^2 + BesselK[1, x]^2 - Sin[Sin[x]]

In[191]:= zeros = 
 Reap[soln = 
    y[x] /. First[
      NDSolve[{y'[x] == Evaluate[D[f[x], x]], y[10] == (f[10])}, 
       y[x], {x, 10, 0}, 
       Method -> {"EventLocator", "Event" -> y[x], 
         "EventAction" :> Sow[{x, y[x]}]}]]][[2, 1]]


During evaluation of In[191]:=
NDSolve::mxst: Maximum number of 10000 steps reached at the point
x == 1.5232626281716416`*^-124. >>

Out[191]= {{9.39114, 8.98587*10^-16}, {6.32397, -3.53884*10^-16},
           {3.03297, -8.45169*10^-13}, {0.886605, -4.02456*10^-15}}

Plot[f[x], {x, 0, 10}, 
 Epilog -> {PointSize[Medium], Red, Point[zeros]}]

function and roots

If it were a trickier function, one might use Method -> {"Projection", ...} to enforce the condition that y[x] is really the same as f[x]. This method may be useful in situations (if you can find them) where we have one function in one variable, and Reduce either cannot handle it or takes a long time to do so.

share|improve this answer
    
@ Daniel Lichtblau, it's a very clever, efficient and general way to get all the roots and can be harnessed in many other cases. Thank you a lot. –  yulinlinyu May 18 '12 at 1:39
1  
Excellent! However, this method doesn't work in v10, possibly a bug. –  luyuwuli Nov 25 '14 at 4:00
1  
@luyuwuli Thanks for pointing that out. I tracked this to an option setting change wherein the default became MaxSteps -> Infinity. You can restore the behavior shown above with an explicit MaxSteps -> 10000. I will ask in house about whether the change is for the better given that it can cause an example like this to hang. –  Daniel Lichtblau Nov 25 '14 at 20:44
1  
@DanielLichtblau, this is a bug that I think is not related to the MaxSteps -> Automatic change - at least not directly. You can get this to work with NDSolve[{y'[x] == Evaluate[D[f[x], x]], y[10] == (f[10])}, y[x], {x, 10, 10^-16}, Method -> {"EventLocator", "Event" -> y[x], "EventAction" :> Sow[{x, y[x]}]}] where the 0 is replaced with a small number. –  user21 Nov 26 '14 at 8:58

Very recently, I learned a useful procedure due to J. P. Boyd (see also this and this) that involves expanding a function as a Chebyshev polynomial series, forming the so-called "colleague matrix", and then finding the eigenvalues of this matrix, which are often good approximations to the roots of the original function. I shall present how to do a barebones version of this strategy in Mathematica.

The first step is to form the Chebyshev series coefficients of the function concerned. One could either start from the Taylor series and use any number of methods to convert these coefficients to Chebyshev coefficients. Another route, which I shall present here, is to evaluate the function at the so-called "Chebyshev nodes", suitably transformed to the domain of the original function, and then using the (type-1) discrete cosine transform to produce the Chebyshev coefficients. It goes like this:

f[x_] := BesselJ[1, x]^2 + BesselK[1, x]^2 - Sin[Sin[x]];
{xmin, xmax} = {1/2, 100};

n = 128; (* arbitrarily chosen integer *)
prec = 20; (* precision *)
cnodes = Rescale[N[Cos[Pi Range[0, n]/n], prec], {-1, 1}, {xmin, xmax}];
cc = Sqrt[2/n] FourierDCT[f[cnodes], 1];
cc[[{1, -1}]] /= 2;

n here should be chosen to be greater than or equal to the number of roots known to be in the given interval. That is usually determined through a preliminary Plot[]. (A more sophisticated version, as used in the Chebfun package for MATLAB, does an adaptive increase in the order of the underlying Chebyshev approximation using a technique similar to Clenshaw-Curtis quadrature. I have chosen not to implement this for now.)

With the Chebyshev coefficients now available, one can then form the colleague matrix like so:

colleague = SparseArray[{{i_, j_} /; i + 1 == j :> 1/2,
                         {i_, j_} /; i == j + 1 :> 1/(2 - Boole[j == 1])},
                        {n, n}] -
            SparseArray[{{i_, n} :> cc[[i]]/(2 cc[[n + 1]])}, {n, n}];

We can then find the eigenvalues of this matrix, filter out irrelevant eigenvalues, and then use Rescale[] to transform the remaining eigenvalues back to the domain of the original function:

rts = Sort[Select[DeleteCases[
                  Rescale[Eigenvalues[colleague], {-1, 1}, {xmin, xmax}],
                  _Complex | _DirectedInfinity], xmin <= # <= xmax &]]
   {0.8866466, 3.0327390, 6.3243343, 9.3928065, 12.5892495, 15.6908929,
    18.8634014, 21.9762069, 25.1562125, 28.3003285, 31.4743983, 34.5197235,
    37.6746911, 40.8716527, 43.9572481, 47.1394116, 50.2776398, 53.3723848,
    56.5916636, 59.6440741, 62.8638369, 66.0001767, 69.0778338, 72.2121395,
    75.3874360, 78.5335813, 81.6858635, 84.8171664, 87.9688503, 91.1015749,
    94.2504610, 97.3866141}

The eigenvalues obtained look a bit rough here; this is due to the ill-behavior of the original function near the origin. For more well-behaved functions, the eigenvalues are often quite accurate roots of the original function. Of course, one can always use FindRoot[] to polish off these approximations.

share|improve this answer

For a finite range of interest, NSolve works well

f[x_] = BesselJ[1, x]^2 + BesselK[1, x]^2 - Sin[Sin[x]];

Manipulate[
 Module[{sol},
  Column[{
    sol = NSolve[{f[x] == 0, 0 <= x <= xmax}, x],
    Plot[f[x], {x, 0, xmax},
     Epilog -> {Red, AbsolutePointSize[6],
       Point[{x, f[x]} /. sol]},
     ImageSize -> 360]}]],
 {{xmax, 16}, 1, 31, 3, Appearance -> "Labeled"}]

enter image description here

share|improve this answer

I might as well elaborate on my comment. Here is a modification of Stan Wagon's FindAllCrossings[] function (from his book Mathematica in Action, second edition) that uses Plot[] to generate the initial approximations to be subsequently polished by FindRoot[]:

Options[FindAllCrossings] = 
  Sort[Join[Options[FindRoot], {MaxRecursion -> Automatic,
       PerformanceGoal :> $PerformanceGoal, PlotPoints -> Automatic}]];

FindAllCrossings[f_, {t_, a_, b_}, opts___] := Module[{r, s, s1, ya},
       {r, ya} = Transpose[First[Cases[Normal[
                 Plot[f, {t, a, b}, Method -> Automatic,
                 Evaluate[Sequence @@ 
                 FilterRules[Join[{opts}, Options[FindAllCrossings]], 
                             Options[Plot]]]]], Line[l_] :> l, Infinity]]];
       s1 = Sign[ya]; If[ ! MemberQ[Abs[s1], 1], Return[{}]];
       s = Times @@@ Partition[s1, 2, 1];
       If[MemberQ[s, -1] || MemberQ[Take[s, {2, -2}], 0], 
          Union[Join[Pick[r, s1, 0], 
                Select[t /. Map[FindRoot[f, {t, r[[#]], r[[# + 1]]}, 
                       Evaluate[Sequence @@ 
                       FilterRules[Join[{opts}, Options[FindAllCrossings]], 
                                   Options[FindRoot]]]] &,
                       Flatten[Position[s, -1]]], a <= # <= b &]]], {}]]

Try it out:

FindAllCrossings[BesselJ[1, x]^2 + BesselK[1, x]^2 - Sin[Sin[x]], {x, 0, 100},
      WorkingPrecision -> 20]

{0.88660463531346207679, 3.0329660890136683539,
 6.3239665137114782212, 9.3911434075850854017, 12.589067252797192964,
 15.687789316501627036, 18.865248000326751595, 21.976728589463954937,
 25.144727536576135544, 28.263111694495812775, 31.425621611972587076,
 34.548333253230934213, 37.707250582575859710, 40.832929091244028281,
 43.989309899299199529, 47.117149292753968158, 50.271642808648651326,
 53.401126310508732375, 56.554160423425108364, 59.684936863656120488,
 62.836808589969946989, 65.968628466404205710, 69.119552435670107405,
 72.252232119042699356, 75.402368490602128759, 78.535768909563822046,
 81.685240379776511855, 84.819253682565487033, 87.968156330842562535,
 91.102697190752433240, 94.251107663305908556, 97.386107414041584404}

A different implementation involves the use of the MeshFunctions option of Plot[] to generate the seeds. Here's how it looks:

FindAllCrossings[f_, {t_, a_, b_}, opts : OptionsPattern[]] := Module[{r},
    r = Cases[Normal[Plot[f, {t, a, b}, MeshFunctions -> (#2 &), Mesh -> {{0}},
                          Method -> Automatic, Evaluate[Sequence @@
                          FilterRules[Join[{opts}, Options[FindAllCrossings]],
                                      Options[Plot]]]]], 
              Point[p_] :> SetPrecision[p[[1]], OptionValue[WorkingPrecision]], 
              Infinity];
    If[r =!= {},
       Union[Select[t /. Map[FindRoot[f, {t, #}, Evaluate[Sequence @@
                                      FilterRules[Join[{opts},
                                                  Options[FindAllCrossings]],
                                                  Options[FindRoot]]]] &, r],
                              a <= # <= b &]], {}]]

This version might be a bit faster in some cases, but it no longer has the safety feature of root bracketing in the previous version.

share|improve this answer
    
Of course, this method won't find even-order multiple roots (i.e. tangencies to the horizontal axis), but almost any numerical method will have difficulty with them anyway. If you know for certain that there are tangencies within the range of interest, you might consider using FindMinimum[]/FindMaximum[] instead to find them. –  Guess who it is. May 17 '12 at 8:49

One can use Solve as well, e.g.

s = Solve[ BesselJ[1, x]^2 + BesselK[1, x]^2 - Sin[ Sin[x]] == 0 && 0 < x < 10, x]
Solve::incs: Warning: Solve was unable to prove that the solution set found is complete. >> 

{{x -> Root[{BesselJ[1, #1]^2 + BesselK[1, #1]^2 - Sin[Sin[#1]] &, 
 0.886604635313462076794393681674}]}, 
{x -> Root[{BesselJ[1, #1]^2 + BesselK[1, #1]^2 - Sin[Sin[#1]] &, 
 3.03296608901366835385376172847}]}, 
{x -> Root[{BesselJ[1, #1]^2 + BesselK[1, #1]^2 - Sin[Sin[#1]] &, 
 6.32396651371147786252003752922}]}, 
{x -> Root[{BesselJ[1, #1]^2 + BesselK[1, #1]^2 - Sin[Sin[#1]] &, 
 9.39114340758508579766919382120}]}}  

Solve similarly as Reduce cannot prove that the set of solutions is complete. It returns the result in the form of rules but we can show that they return the same set solutions e.g. :

r = Reduce[ BesselJ[1, x]^2 + BesselK[1, x]^2 - Sin[ Sin[x]] == 0 && 0 < x < 10, x];
s[[All, 1, 2]] == List @@ r[[All, 2]]
Reduce::incs: Warning: Reduce was unable to prove that the solution set found is complete. >>

True

Edit

Defining

f[x_, a_] := BesselJ[1, x]^2 + BesselK[1, x]^2 - Sin[Sin[a x]]

we can visualise the graphs of functions f[x,a] making use of ParametricPlot3D (look at answers to this question), e.g.

Show[ 
  ParametricPlot3D[ Evaluate[ Table[{x, a, f[x, a]}, {a, 0, 5}]],
                    {x, 0, 10}, BoxRatios -> {10, 8, 4}], 
  ParametricPlot3D[{x, 1, f[x, 1]}, 
                   {x, 0, 10}, PlotStyle->{Thick, Darker@Red}, BoxRatios -> {10, 8, 4}]

enter image description here

Red thick curve is the graph of f[x,1],

or we can make use of Plot3D as well in the following way :

Plot3D[ f[x, a], {x, 0, 10}, {a, 0, 4}, 
        MeshFunctions -> {#1 &, #2 &, #3 &}, Mesh -> {9, 3, 5}, Filling -> 0,
        PlotPoints -> 200, MaxRecursion -> 5]

]

enter image description here

The option Filling ->0 makes an impression that the level f[x,a] == 0 is like the surface of water.

share|improve this answer

Regarding your first question:

For a certain set of equations, Reduce is able to find all roots and prove that no more roots exist in a given interval. I am not sure how this works, but there's an interesting blog post about it here.

When it is not able to guarantee (using symbolic methods) that there are no more solutions, it may still return a set, but it will give a warning. This is the case for your equation.

Plot[BesselJ[1, x]^2 + BesselK[1, x]^2 - Sin[Sin[x]], {x, 0, 10}]

Mathematica graphics

Reduce[BesselJ[1, x]^2 + BesselK[1, x]^2 - Sin[Sin[x]] == 0 && 0 < x < 10, x]

(*
Reduce::incs: Warning: Reduce was unable to prove that the solution set found is complete. >>
*)

(* ==>
x == Root[{BesselJ[1, #1]^2 + BesselK[1, #1]^2 - Sin[Sin[#1]] &, 
    0.886604635313462076794393681674}] || 
 x == Root[{BesselJ[1, #1]^2 + BesselK[1, #1]^2 - Sin[Sin[#1]] &, 
    3.03296608901366835385376172847}] || 
 x == Root[{BesselJ[1, #1]^2 + BesselK[1, #1]^2 - Sin[Sin[#1]] &, 
    6.32396651371147786252003752922}] || 
 x == Root[{BesselJ[1, #1]^2 + BesselK[1, #1]^2 - Sin[Sin[#1]] &, 
    9.39114340758508579766919382120}]
*)

ToRules@N[%]

(* ==>
  Sequence[{x -> 0.886605}, {x -> 3.03297}, {x -> 6.32397}, {x -> 9.39114}]
*)

Note that it is important to restrict the search to an interval. Otherwise Reduce will tell you that it can't find any solutions.

share|improve this answer

For your first question one can use Ted Ersek's package here :

RootSearch[BesselJ[1, x]^2 + BesselK[1, x]^2 - Sin[Sin[x]] == 0, {x, 0, 10}]

(* {{x -> 0.886605}, {x -> 3.03297}, {x -> 6.32397}, {x -> 9.39114}} *)
share|improve this answer

For finding all roots in a given interval I use the following function:

Clear[findRoots]
Options[findRoots] = Options[Reduce];
findRoots[gl_Equal, {x_, von_, bis_}, 
   prec : (_Integer?Positive | MachinePrecision | Infinity) : MachinePrecision, 
   wrap_: Identity, opts : OptionsPattern[]] := 
  Module[{work, glp, vonp, bisp}, 
   {glp, vonp, bisp} = {gl, von, bis} /. r_Real :> SetPrecision[r, prec];
   work = wrap@Reduce[{glp, vonp <= x <= bisp}, opts];
   work = {ToRules[work]};
   If[prec === Infinity, work, N[work, prec]]];

Example

bes[x_] := BesselJ[1, x]^2 + BesselK[1, x]^2 - Sin[Sin[x]]
findRoots[bes[x] == 0, {x, 0, 100}]

During evaluation of In[35]:= Reduce::incs: Warning: Reduce was unable to prove that the solution set found is complete. >>

Out[39]= {{x -> 0.886605}, {x -> 3.03297}, {x -> 6.32397}, {x -> 9.39114}, {x -> 
   12.5891}, {x -> 15.6878}, {x -> 18.8652}, {x -> 21.9767}, {x -> 
   25.1447}, {x -> 28.2631}, {x -> 31.4256}, {x -> 34.5483}, {x -> 
   37.7073}, {x -> 40.8329}, {x -> 43.9893}, {x -> 47.1171}, {x -> 
   50.2716}, {x -> 53.4011}, {x -> 56.5542}, {x -> 59.6849}, {x -> 
   62.8368}, {x -> 65.9686}, {x -> 69.1196}, {x -> 72.2522}, {x -> 
   75.4024}, {x -> 78.5358}, {x -> 81.6852}, {x -> 84.8193}, {x -> 
   87.9682}, {x -> 91.1027}, {x -> 94.2511}, {x -> 97.3861}}

The parameter prec gives the precision for the calculation. prec=Infinity gives exact solutions.
wrap (default: Identity) is a function you can apply to the solutions.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.