Skip to main content

differential equations - Vector ParametricNDSolve and FindRoot interaction


This question came out of this question.


I have a set of differential equations, written in vector form. I'm only interested in the value of these at the endpoint, and so I use ParametricNDSolve, asking it to only return that function of the vectors. This works fine on its own, and is slightly quicker than asking for the whole solution to be returned:


Clear[test];

A = {{0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}, {q, 0, 0, 0}}; test =
ParametricNDSolveValue[{Y'[x] == A.Y[x], Y[0] == Table[1, 4]},
Y[4].Y[4], {x, 0, 4}, q];
First@AbsoluteTiming[test /@ Range[0, 10, 0.1];]
(* 0.037914 *)

However, if I try to use this same function in FindRoot, it now takes much longer to evaluate at the same points afterwards:


Clear[test];
A = {{0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}, {q, 0, 0, 0}}; test =
ParametricNDSolveValue[{Y'[x] == A.Y[x], Y[0] == Table[1, 4]},

Y[4].Y[4], {x, 0, 4}, q];
Quiet[FindRoot[test[q], {q, 3}]];
First@AbsoluteTiming[test /@ Range[0, 10, 0.1];]
(* 0.24924 *)

Which is 6 times longer than it took to do exactly the same calculation. Note that the function definition is identical, just the use of the function in the FindRoot has changed (which is also much slower than just getting the entire interpolation functions out and then calculating only the part I need).


Can anyone explain what is going on? I get the same timings on 11.3 and 12.0 on my mac.



Answer



We can add a method, then the time is reduced by an order. In this example, the test-1000 has a root


Clear[test];

A = {{0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}, {q, 0, 0, 0}}; test =
ParametricNDSolveValue[{Y'[x] == A.Y[x], Y[0] == Table[1, 4]},
Y[4].Y[4], {x, 0, 4}, q];
Quiet[FindRoot[test[q] - 1000, {q,0,1}, Method -> "Secant"]];
First@AbsoluteTiming[test /@ Range[0, 10, 0.1];]
(*0.0341673*)

Compare without method and without FindRoot[]


 Clear[test];
A = {{0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}, {q, 0, 0, 0}}; test =

ParametricNDSolveValue[{Y'[x] == A.Y[x], Y[0] == Table[1, 4]},
Y[4].Y[4], {x, 0, 4}, q];
Quiet[FindRoot[test[q] - 1000, {q, 3}]];
First@AbsoluteTiming[test /@ Range[0, 10, 0.1];]

(*0.271661*)

Clear[test];
A = {{0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}, {q, 0, 0, 0}}; test =
ParametricNDSolveValue[{Y'[x] == A.Y[x], Y[0] == Table[1, 4]},

Y[4].Y[4], {x, 0, 4}, q];
First@AbsoluteTiming[test /@ Range[0, 10, 0.1];]

(* 0.0395219 *)

With the option Method -> "Secant" code works even faster than without FindRoot[].If we use the option Method -> "AffineCovariantNewton", then the time increases:


 Clear[test];
A = {{0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}, {q, 0, 0, 0}}; test =
ParametricNDSolveValue[{Y'[x] == A.Y[x], Y[0] == Table[1, 4]},
Y[4].Y[4], {x, 0, 4}, q];

Quiet[FindRoot[test[q] - 1000, {q, 1},
Method -> "AffineCovariantNewton"]];
First@AbsoluteTiming[test /@ Range[0, 10, 0.1];]

(* 0.298559 *)

Consequently, Newton's method (the default method) can slow down the code in this combination.


Comments

Popular posts from this blog

plotting - Filling between two spheres in SphericalPlot3D

Manipulate[ SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, Mesh -> None, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], {n, 0, 1}] I cant' seem to be able to make a filling between two spheres. I've already tried the obvious Filling -> {1 -> {2}} but Mathematica doesn't seem to like that option. Is there any easy way around this or ... Answer There is no built-in filling in SphericalPlot3D . One option is to use ParametricPlot3D to draw the surfaces between the two shells: Manipulate[ Show[SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], ParametricPlot3D[{ r {Sin[t] Cos[1.5 Pi], Sin[t] Sin[1.5 Pi], Cos[t]}, r {Sin[t] Cos[0 Pi], Sin[t] Sin[0 Pi], Cos[t]}}, {r, 1, 2 - n}, {t, 0, Pi}, PlotStyle -> Yellow, Mesh -> {2, 15}]], {n, 0, 1}]

plotting - Plot 4D data with color as 4th dimension

I have a list of 4D data (x position, y position, amplitude, wavelength). I want to plot x, y, and amplitude on a 3D plot and have the color of the points correspond to the wavelength. I have seen many examples using functions to define color but my wavelength cannot be expressed by an analytic function. Is there a simple way to do this? Answer Here a another possible way to visualize 4D data: data = Flatten[Table[{x, y, x^2 + y^2, Sin[x - y]}, {x, -Pi, Pi,Pi/10}, {y,-Pi,Pi, Pi/10}], 1]; You can use the function Point along with VertexColors . Now the points are places using the first three elements and the color is determined by the fourth. In this case I used Hue, but you can use whatever you prefer. Graphics3D[ Point[data[[All, 1 ;; 3]], VertexColors -> Hue /@ data[[All, 4]]], Axes -> True, BoxRatios -> {1, 1, 1/GoldenRatio}]

plotting - Adding a thick curve to a regionplot

Suppose we have the following simple RegionPlot: f[x_] := 1 - x^2 g[x_] := 1 - 0.5 x^2 RegionPlot[{y < f[x], f[x] < y < g[x], y > g[x]}, {x, 0, 2}, {y, 0, 2}] Now I'm trying to change the curve defined by $y=g[x]$ into a thick black curve, while leaving all other boundaries in the plot unchanged. I've tried adding the region $y=g[x]$ and playing with the plotstyle, which didn't work, and I've tried BoundaryStyle, which changed all the boundaries in the plot. Now I'm kinda out of ideas... Any help would be appreciated! Answer With f[x_] := 1 - x^2 g[x_] := 1 - 0.5 x^2 You can use Epilog to add the thick line: RegionPlot[{y < f[x], f[x] < y < g[x], y > g[x]}, {x, 0, 2}, {y, 0, 2}, PlotPoints -> 50, Epilog -> (Plot[g[x], {x, 0, 2}, PlotStyle -> {Black, Thick}][[1]]), PlotStyle -> {Directive[Yellow, Opacity[0.4]], Directive[Pink, Opacity[0.4]],