OK, I think based on the number of times I crashed my kernel trying to reproduce that plot that brute force is not the right answer. Here's a technique that's a little more clever:
- Select a random point in the region of interest.
- Use a solver (steepest descent, say) to get onto a zero contour.
- Find the gradient at that point and take a vector orthogonal to it.
- Follow that contour until you hit a singularity or the edge of the region of interest.
- Goto 1, but at step 2 check whether you've landed on a contour you already solved earlier.
Here's some code that should get you started. Probably there are a lot more improvements that could be made! For example, instead of using random starting points, you could use a grid or try to find points far from your current set of lines. Or you could speed up some of the checking by using a search tree that would give only nearby lines.
Edit: New updates add a reminimization step and prune the recent line to improve performance. This allows us to lengthen the step size substantially.
Second edit: Fixed a bug in the pruning logic. Added a starting set of points to make sure the grid is reasonably well sampled.
Third edit: Added reversal along each contour so you get both directions at once. Compiled the more expensive bits. Made the starting grid more suited to the structure of the function.
Fourth edit: Minor edit to remove a v9ism (Grad).
randomzero := nearestzero[RandomReal[#] & /@ range]
nearestzero[{x0_, y0_}] :=
Quiet[{x, y} /. FindMinimum[(f[x, y])^2, {{x, x0}, {y, y0}}][[2]]]
update :=
Module[{},
Do[x0 = If[Length[lines] < Length[initx],
nearestzero[initx[[Length[lines] + 1]]], randomzero];
While[Min[Norm[#1 - x0] & /@ Join @@ lines] < thin step,
x0 = randomzero];
xlist = {x0}; cnt = -thin/2; newline = {}; Do[
While[x1 = Last[xlist];
x1[[1]] > range[[1, 1]] && x1[[2]] > range[[2, 1]] &&
x1[[1]] < range[[1, 2]] &&
x1[[2]] <
range[[2,
2]] && (m1 = Min[#.# &[#1 - x1] & /@ Most[xlist]]) > (step/
1.01)^2 && (m2 =
If[Length[newline] == 0, 1,
Min[#.# &[#1 - x1] & /@
Join[Sequence @@ lines, newline]]]) > (step thin/2)^2,
AppendTo[xlist, x1 + dir follow @@ x1];
If[cnt++ == thin, AppendTo[newline, First[xlist]];
xlist = Drop[xlist, thin];
Block[{xtmp = Last[xlist]},
ReplacePart[xlist, -1 -> nearestzero[xtmp]];
If[#.# &[xtmp - Last[xlist]] > step^2,
Print[Norm[xtmp - Last[xlist]]];
ReplacePart[xlist, -1 -> xtmp]; Break[]]]; cnt = 1;]];
newline = Reverse@Join[newline, xlist[[-1 ;; ;; -thin]]];
If[dir > 0, xlist = {Last[newline]}; newline = Most[newline];
cnt = -thin/2], {dir, {1, -1}}];
AppendTo[lines, newline], {Length[initx] + nadd}]; newline = {};
xlist = {Last[xlist]};]
range = {{-10, 10}, {-10, 10}}; step = 0.003; thin = 30;
f[x_, y_] := Csc[1 - x^2] Cot[2 - y^2] - x y
follow = With[{orth = RotationMatrix[\[Pi]/2],
grad = {D[f[x, y], x], D[f[x, y], y]}},
Compile[{{x, _Real}, {y, _Real}}, step orth.Normalize[grad]]];
initx = Join @@
Outer[List, Join[-#, #] &@Sqrt[Range[0.5, 100, 2]],
Join[-#, #] &@Sqrt[Range[0.5, 100, 2]]]; lines = {}; nadd = 0;
Dynamic[Graphics[{Line[
Append[Select[lines, Length[#] > 3 &],
Append[newline, First[xlist]]]], Pink, Line[xlist], Blue,
Point[x0], Red, Point[Last[xlist]]}, PlotRange -> range]]
update
Here's a picture of the tracer after about 4 minutes:
Plot[Csc[1 - x^2] Cot[2 - y^2] - x y /. y -> 0.5, {x, -10, 10}]
for instance. $\endgroup$ContourPlot
code for convenience. $\endgroup$Sow
, and such calls are very expensive (and prevent parallelization). Overall,Compile
is not achieving much here and may even be net harmful. $\endgroup$