How to "inform" successive ContourPlot calculations?



I need to draw some contour plots of very non-linear functions. As a simple example, take a Mandelbrot Set divergence contour near $z\approx i$. (Just to be clear, I'm not trying to write a Mandelbrot viewer, just picking a simple example of a recursive calculation).

z[n_, c_] := If[n > 0, z[n - 1, c]^2 + c, c];
iter = 25;
ContourPlot[Abs[z[iter, x + I*y]] == 2, {x, -.00001, .00001}, {y, .99999, 1.00001}, 
   MaxRecursion -> 5]


The higher we set iter, the higher we need to set MaxRecursion. But this slows the computation waaaay down, and I need to zoom in to smaller regions and display plots for higher values of iter.

However, we could avoid a lot of needless computation and make this calculation more efficient if we could somehow use our knowledge that:

  • If $z[r<n,c_0]>2$, then $c_0$ is outside the $z[n,c]==2$ contour, so there is no need to calculate the successive values $z[m>r,c_0]$.

  • The contours of successive values of iter are nested, i.e., the $z[n,c]==2$ contour is entirely inside or tangential to the $z[n-1,c]=2$ contour for all n.

I can think of ways to do this, but they involve having to write my own contour-plotting routine instead of using ContourPlot[]. Does anyone have a better idea than that?

Jerry Guern

Posted 2016-01-09T00:31:00.943

Reputation: 4 197

Image processing? E.g., i = Import[""]; ColorNegate@EdgeDetect@Binarize[ColorConvert[i, "Grayscale"], 0.3] ->

– Michael E2 – 2016-01-09T01:11:16.167

@MichaelE2 I'm just using the M-Set here as example that's simpler than my actual recursive function, I'm not writing an M-Set viewer. :-) – Jerry Guern – 2016-01-09T01:13:53.640

My suggestion has nothing to do with writing an M-set viewer. It's about interpolating between values of the function, similar to a contour plot. Perhaps you mean something different by an "M-set viewer," but it's confusing to show a plot of a set and say you don't want to view it. (By the M-set, I'm assuming you don't mean the Mandelbrot set, but the contour for your own function(s).) – Michael E2 – 2016-01-09T01:54:47.873

@MichaelE2 I am completely confused by what you are telling me. Your comment above suggests Importing some mandelbrot png. I'm just asking how to make a faster contour plot of a really non-linear function. – Jerry Guern – 2016-01-09T02:01:35.773

The main point in my comment is about finding the contour in the array of function values (represented by the image data) with Binarize. There are efficient ways to generate images of the M-set. If there are such ways for your function, then I thought the approach might work for you. – Michael E2 – 2016-01-09T02:10:36.310

The first thing I'd try is to change your function to stop iterating as soon as $|z(r,c)|>2$. This doesn't change the contour, but now for most of the points in the plot range it won't have to iterate all the way to $n$. I don't know if it's easy to do this efficiently in a functional style; I would write an explicit loop instead. – None – 2016-01-09T04:34:36.123

@Rahul Right, that's what I was getting at when I said in the Question that I can think of ways to do this but I'd have to write my own contour plot routine instead of using ContourPlot[] – Jerry Guern – 2016-01-09T04:49:22.637

1No, just change your definition of z to stop iterating sooner and use that in ContourPlot. – None – 2016-01-09T05:04:51.883



Using NestWhile seems to work well

z[n_, c_] := NestWhile[(#^2 + c) &, c, Abs[#] <= 2 &, 1, n];
ContourPlot[Abs[z[iter, x + I*y]] == 2, {x, -.00001, .00001}, {y, .99999, 1.00001}, 
    MaxRecursion -> 5] // AbsoluteTiming

producing the plot in the question in about 10 seconds, as opposed to 180 seconds for the code in the question.

enter image description here


Posted 2016-01-09T00:31:00.943

Reputation: 55 236

That's definitely an improvement from my z[n,c] function, and it does accomplish the first improvement I asked about, but when I zoom in its gets veeeery slow again, so I still wonder if that second improvement is possible. ContourPlot[ Abs[z[35, x + I*y]] == 2, {x, -.000001, .000001}, {y, .999999, 1.000001}, MaxRecursion -> 10] – Jerry Guern – 2016-01-09T05:37:44.397

...or any other improvement anyone can think of. – Jerry Guern – 2016-01-09T05:44:59.883


@JerryGuern If you're looking for faster ways to code an iterative function, there are some ideas here:

– Michael E2 – 2016-01-09T05:51:10.093

@JerryGuern When running the ContourPlot computation given in your comment above, Mathematica 10.3.0 quickly devours all the memory on my computer and then saturates disk transfer. If you are experiencing the same behavior, it would cause your computer to come to almost a halt. MaxRecursion is the issue. – bbgodfrey – 2016-01-09T10:45:51.083

1@JerryGuern Continuing my last comment, MaxRecursion -> 5 required 14 sec, 6 required 78 sec, and 7 required longer than I cared to wait (at least 2000 sec). The reference provided by MichaelE2 suggests that plotting sometimes may take more time than computing z does, and I can easily believe that this is so. In any event, I do not believe that your second approach in the question will offer any significant savings over that obtained from the first approach. – bbgodfrey – 2016-01-09T11:39:28.333


Many years of Lisp and Scheme programming has given me a fondness for recursive functions. They don't have to have poor performance if some care is taken when writing them. For your problem I would write z as

z[n_, c0_] := z[n - 1, c0, c0^2 + c0]
z[0, _, c_] := c
z[n_, c0_, c_?(Abs[#] > 2 &)] := c
z[n_, c0_, c_] := z[n - 1, c0, c^2 + c0]

Note there is a cutoff for Abs[c] > 2. That gives a pretty good performance boost.

ContourPlot[Abs[z[25, x + I*y]] == 2, {x, -.00001, .00001}, {y, .99999, 1.00001}, 
  MaxRecursion -> 5] // AbsoluteTiming


Not as fast as bbgodfrey's NestWhile solution, but not too shabby.


Posted 2016-01-09T00:31:00.943

Reputation: 104 223