Plotting algorithms for the Mandelbrot set

There are many programs andalgorithmsused to plot theMandelbrot setand otherfractals, some of which are described infractal-generating software. These programs use a variety of algorithms to determine the color of individualpixelsefficiently.
The simplest algorithm for generating a representation of the Mandelbrot set is known as the "escape time" algorithm. A repeating calculation is performed for eachx,ypoint in the plot area and based on the behavior of that calculation, a color is chosen for that pixel.
In both the unoptimized and optimized escape time algorithms, thexandylocations of each point are used as starting values in a repeating, or iterating calculation (described in detail below). The result of each iteration is used as the starting values for the next. The values are checked during each iteration to see whether they have reached a critical "escape" condition, or "bailout". If that condition is reached, the calculation is stopped, the pixel is drawn, and the nextx,ypoint is examined. For some starting values, escape occurs quickly, after only a small number of iterations. For starting values very close to but not in the set, it may take hundreds or thousands of iterations to escape. For values within the Mandelbrot set, escape will never occur. The programmer or user must choose how many iterations–or how much "depth"–they wish to examine. The higher the maximal number of iterations, the more detail and subtlety emerge in the final image, but the longer time it will take to calculate the fractal image.
Escape conditions can be simple or complex. Because nocomplex numberwith a real or imaginary part greater than 2 can be part of the set, a common bailout is to escape when either coefficient exceeds 2. A more computationally complex method that detects escapes sooner, is to compute distance from the origin using thePythagorean theorem, i.e., to determine theabsolute value, ormodulus, of the complex number. If this value exceeds 2, or equivalently, when the sum of the squares of the real and imaginary parts exceed 4, the point has reached escape. More computationally intensive rendering variations include theBuddhabrotmethod, which finds escaping points and plots their iterated coordinates.
The color of each point represents how quickly the values reached the escape point. Often black is used to show values that fail to escape before the iteration limit, and gradually brighter colors are used for points that escape. This gives a visual representation of how many cycles were required before reaching the escape condition.
To render such an image, the region of the complex plane we are considering is subdivided into a certain number ofpixels. To color any such pixel, letc{\displaystyle c}be the midpoint of that pixel. We now iterate the critical point 0 underPc{\displaystyle P_{c}}, checking at each step whether the orbit point has modulus larger than 2. When this is the case, we know thatc{\displaystyle c}does not belong to the Mandelbrot set, and we color our pixel according to the number of iterations used to find out. Otherwise, we keep iterating up to a fixed number of steps, after which we decide that our parameter is "probably" in the Mandelbrot set, or at least very close to it, and color the pixel black.
Inpseudocode, this algorithm would look as follows. The algorithm does not use complex numbers and manually simulates complex-number operations using two real numbers, for those who do not have acomplex data type. The program may be simplified if the programming language includes complex-data-type operations.
Here, relating the pseudocode toc{\displaystyle c},z{\displaystyle z}andPc{\displaystyle P_{c}}:
and so, as can be seen in the pseudocode in the computation ofxandy:
To get colorful images of the set, the assignment of a color to each value of the number of executed iterations can be made using one of a variety of functions (linear, exponential, etc.). One practical way, without slowing down calculations, is to use the number of executed iterations as an entry to apaletteinitialized at startup. If the color table has, for instance, 500 entries, then the color selection isnmod 500, wherenis the number of iterations.
The code in the previous section uses an unoptimized inner while loop for clarity. In the unoptimized version, one must perform five multiplications per iteration. To reduce the number of multiplications the following code for the inner while loop may be used instead:
The above code works via some algebraic simplification of the complex multiplication:
(iy+x)2=−y2+2iyx+x2=x2−y2+2iyx{\displaystyle {\begin{aligned}(iy+x)^{2}&=-y^{2}+2iyx+x^{2}\\&=x^{2}-y^{2}+2iyx\end{aligned}}}
Using the above identity, the number of multiplications can be reduced to three instead of five.
The above inner while loop can be further optimized by expandingwto
w=x2+2xy+y2{\displaystyle w=x^{2}+2xy+y^{2}}
Substitutingwintoy=w−x2−y2+y0{\displaystyle y=w-x^{2}-y^{2}+y_{0}}yieldsy=2xy+y0{\displaystyle y=2xy+y_{0}}and hence calculatingwis no longer needed.
The further optimized pseudocode for the above is:
Note that in the above pseudocode,2xy{\displaystyle 2xy}seems to increase the number of multiplications by 1, but since 2 is the multiplier the code can be optimized via(x+x)y{\displaystyle (x+x)y}.
It is common to check the magnitude of z after every iteration, but there is another method we can use that can converge faster and reveal structure withinjulia sets.
Instead of checking if themagnitude of zafter every iteration is larger than a given value, we can instead check if the sum of eachderivativeof z up to the current iteration step is larger than a given bailout value:
zn′:=(2∗zn−1′∗zn−1)+1{\displaystyle z_{n}^{\prime }:=(2*z_{n-1}^{\prime }*z_{n-1})+1}
The size of the dbail value can enhance the detail in the structures revealed within the dbail method with very large values.
It is possible to find derivatives automatically by leveragingAutomatic differentiationand computing the iterations usingDual numbers.
Rendering fractals with the derbail technique can often require a large number of samples per pixel, as there can beprecisionissues which lead to fine detail and can result innoisyimages even withsamplesin the hundreds or thousands.
Python code:
In addition to plotting the set, a variety of algorithms have been developed to
A more complex coloring method involves using ahistogramwhich pairs each pixel with said pixel's maximum iteration count before escape/bailout. This method will equally distribute colors to the same overall area, and, importantly, is independent of the maximum number of iterations chosen.
This algorithm has four passes. The first pass involves calculating the iteration counts associated with each pixel (but without any pixels being plotted). These are stored in an array: IterationCounts[x][y], where x and y are the x and y coordinates of said pixel on the screen respectively.
The first step of the second pass is to create an array of sizen, which is the maximum iteration count: NumIterationsPerPixel. Next, one must iterate over the array of pixel-iteration count pairs, IterationCounts[][], and retrieve each pixel's saved iteration count,i, via e.g.i= IterationCounts[x][y]. After each pixel's iteration countiis retrieved, it is necessary to index the NumIterationsPerPixel byiand increment the indexed value (which is initially zero) -- e.g. NumIterationsPerPixel[i] = NumIterationsPerPixel[i] + 1 .
The third pass iterates through the NumIterationsPerPixel array and adds up all the stored values, saving them intotal. The array index represents the number of pixels that reached that iteration count before bailout.
After this, the fourth pass begins and all the values in the IterationCounts array are indexed, and, for each iteration counti, associated with each pixel, the count is added to a global sum of all the iteration counts from 1 toiin the NumIterationsPerPixel array . This value is then normalized by dividing the sum by thetotalvalue computed earlier.
Finally, the computed value is used, e.g. as an index to a color palette.
This method may be combined with the smooth coloring method below for more aesthetically pleasing images.
The escape time algorithm is popular for its simplicity. However, it creates bands of color, which, as a type ofaliasing, can detract from an image's aesthetic value. This can be improved using an algorithm known as "normalized iteration count",which provides a smooth transition of colors between iterations. The algorithm associates a real numberν{\displaystyle \nu }with each value ofzby using the connection of the iteration number with thepotential function. This function is given by
whereznis the value afterniterations andPis the power for whichzis raised to in the Mandelbrot set equation (zn+1=zn+c,Pis generally 2).
If we choose a large bailout radiusN(e.g., 10), we have that
for some real numberν(z){\displaystyle \nu (z)}, and this is
and asnis the first iteration number such that |zn| >N, the number we subtract fromnis in the interval [0, 1).
For the coloring we must have a cyclic scale of colors (constructed mathematically, for instance) and containingHcolors numbered from 0 toH− 1 (H= 500, for instance). We multiply the real numberν(z){\displaystyle \nu (z)}by a fixed real number determining the density of the colors in the picture, take the integral part of this number moduloH, and use it to look up the corresponding color in the color table.
For example, modifying the above pseudocode and also using the concept oflinear interpolationwould yield
Typically when we render a fractal, the range of where colors from a given palette appear along the fractal is static. If we desire to offset the location from the border of the fractal, or adjust their palette to cycle in a specific way, there are a few simple changes we can make when taking the final iteration count before passing it along to choose an item from our palette.
When we have obtained the iteration count, we can make the range of colors non-linear. Raising a value normalized to the range [0,1] to a powern, maps a linear range to an exponential range, which in our case can nudge the appearance of colors along the outside of the fractal, and allow us to bring out other colors, or push in the entire palette closer to the border.
v=((i/maxi)SN)1.5modN{\displaystyle v=((\mathbf {i} /max_{i})^{\mathbf {S} }\mathbf {N} )^{1.5}{\bmod {\mathbf {N} }}}
whereiis our iteration count after bailout,max_iis our iteration limit,Sis the exponent we are raising iters to, andNis the number of items in our palette. This scales the iter count non-linearly and scales the palette to cycle approximately proportionally to the zoom.
We can then plug v into whatever algorithm we desire for generating a color.
One thing we may want to consider is avoiding having to deal with a palette or color blending at all. There are actually a handful of methods we can leverage to generate smooth, consistent coloring by constructing the color on the spot.
A naive method for generating a color in this way is by directly scalingvto 255 and passing it into RGB as such
One flaw with this is that RGB is non-linear due to gamma; consider linear sRGB instead. Going from RGB to sRGB uses an inverse companding function on the channels. This makes the gamma linear, and allows us to properly sum the colors for sampling.
HSV Coloring can be accomplished by mapping iter count from [0,max_iter) to [0,360), taking it to the power of 1.5, and then modulo 360.We can then simply take the exponentially mapped iter count into the value and return
This method applies to HSL as well, except we pass a saturation of 50% instead.
One of the most perceptually uniform coloring methods involves passing in the processed iter count into LCH. If we utilize the exponentially mapped and cyclic method above, we can take the result of that into the Luma and Chroma channels. We can also exponentially map the iter count and scale it to 360, and pass this modulo 360 into the hue.
x∈Q+si=(i/maxi)xv=1.0−cos2(πsi)L=75−(75v)C=28+(75−75v)H=(360si)1.5mod360{\textstyle {\begin{array}{lcl}x&\in &\mathbb {Q+} \\s_{i}&=&(i/max_{i})^{\mathbf {x} }\\v&=&1.0-cos^{2}(\pi s_{i})\\L&=&75-(75v)\\C&=&28+(75-75v)\\H&=&(360s_{i})^{1.5}{\bmod {3}}60\end{array}}}
One issue we wish to avoid here is out-of-gamut colors. This can be achieved with a little trick based on the change in in-gamut colors relative to luma and chroma. As we increase luma, we need to decrease chroma to stay within gamut.
In addition to the simple and slow escape time algorithms already discussed, there are many other more advanced algorithms that can be used to speed up the plotting process.
One can compute thedistancefrom pointc(inexteriororinterior) to nearest point on theboundaryof the Mandelbrot set.
The proof of theconnectednessof the Mandelbrot set in fact gives a formula for theuniformizing mapof thecomplementofM{\displaystyle M}(and thederivativeof this map). By theKoebe quarter theorem, one can then estimate the distance between the midpoint of ourpixeland the Mandelbrot set up to a factor of 4.
In other words, provided that the maximal number of iterations is sufficiently high, one obtains a picture of the Mandelbrot set with the following properties:
The upper boundbfor the distance estimate of a pixelc(a complex number) from the Mandelbrot set is given by
where
The idea behind this formula is simple: When theequipotentiallines for the potential functionϕ(z){\displaystyle \phi (z)}lie close, the number|ϕ′(z)|{\displaystyle |\phi '(z)|}is large, and conversely, therefore the equipotential lines for the functionϕ(z)/|ϕ′(z)|{\displaystyle \phi (z)/|\phi '(z)|}should lie approximately regularly.
From a mathematician's point of view, this formula only works in limit wherengoes to infinity, but very reasonable estimates can be found with just a few additional iterations after the main loop exits.
Oncebis found, by the Koebe 1/4-theorem, we know that there is no point of the Mandelbrot set with distance fromcsmaller thanb/4.
The distance estimation can be used for drawing of the boundary of the Mandelbrot set, see the articleJulia set. In this approach, pixels that are sufficiently close to M are drawn using a different color. This creates drawings where the thin "filaments" of the Mandelbrot set can be easily seen. This technique is used to good effect in the B&W images of Mandelbrot sets in the books "The Beauty of Fractals" and "The Science of Fractal Images".
Here is a sample B&W image rendered using Distance Estimates:
Distance Estimation can also be used to render3D images of Mandelbrot and Julia sets
It is also possible to estimate the distance of a limitly periodic (i.e.,hyperbolic) point to the boundary of the Mandelbrot set. The upper boundbfor the distance estimate is given by
where
Analogous to the exterior case, oncebis found, we know that all points within the distance ofb/4 fromcare inside the Mandelbrot set.
There are two practical problems with the interior distance estimate: first, we need to findz0{\displaystyle z_{0}}precisely, and second, we need to findp{\displaystyle p}precisely.
The problem withz0{\displaystyle z_{0}}is that the convergence toz0{\displaystyle z_{0}}by iteratingPc(z){\displaystyle P_{c}(z)}requires, theoretically, an infinite number of operations.
The problem with any givenp{\displaystyle p}is that, sometimes, due to rounding errors, a period is falsely identified to be an integer multiple of the real period (e.g., a period of 86 is detected, while the real period is only 43=86/2). In such case, the distance is overestimated, i.e., the reported radius could contain points outside the Mandelbrot set.
One way to improve calculations is to find out beforehand whether the given point lies within the cardioid or in the period-2 bulb. Before passing the complex value through the escape time algorithm, first check that:
wherexrepresents the real value of the point andythe imaginary value. The first two equations determine that the point is within the cardioid, the last the period-2 bulb.
The cardioid test can equivalently be performed without the square root:
3rd- and higher-order buds do not have equivalent tests, because they are not perfectly circular.However, it is possible to find whether the points are within circles inscribed within these higher-order bulbs, preventing many, though not all, of the points in the bulb from being iterated.
To prevent having to do huge numbers of iterations for points inside the set, one can perform periodicity checking, which checks whether a point reached in iterating a pixel has been reached before. If so, the pixel cannot diverge and must be in the set. Periodicity checking is a trade-off, as the need to remember points costs data management instructions and memory, but saves computational instructions. However, checking against only one previous iteration can detect many periods with little performance overhead. For example, within the while loop of the pseudocode above, make the following modifications:
The above code stores away a new x and y value on every 20th iteration, thus it can detect periods that are up to 20 points long.
Because the Mandelbrot set isfull,any point enclosed by a closed shape whose borders lie entirely within the Mandelbrot set must itself be in the Mandelbrot set. Border tracing works by following thelemniscatesof the various iteration levels (colored bands) all around the set, and then filling the entire band at once. This also provides a speed increase because large numbers of points can be now skipped.
In the animation shown, points outside the set are colored with a 1000-iteration escape time algorithm. Tracing the set border and filling it, rather than iterating the interior points, reduces the total number of iterations by 93.16%. With a higher iteration limit the benefit would be even greater.
Rectangle checking is an older and simpler method for plotting the Mandelbrot set. The basic idea of rectangle checking is that if every pixel in a rectangle's border shares the same amount of iterations, then the rectangle can be safely filled using that number of iterations. There are several variations of the rectangle checking method, however, all of them are slower than the border tracing method because they end up calculating more pixels. One variant just calculates the corner pixels of each rectangle, however, this causes damaged pictures more often than calculating the entire border, thus it only works reasonably well if only small boxes of around 6x6 pixels are used, and no recursing in from bigger boxes. (Fractintmethod.)
The most simple rectangle checking method lies in checking the borders of equally sized rectangles, resembling a grid pattern. (Mariani's algorithm.)
A faster and slightly more advanced variant is to first calculate a bigger box, say 25x25 pixels. If the entire box border has the same color, then just fill the box with the same color. If not, then split the box into four boxes of 13x13 pixels, reusing the already calculated pixels as outer border, and sharing the inner "cross" pixels between the inner boxes. Again, fill in those boxes that has only one border color. And split those boxes that don't, now into four 7x7 pixel boxes. And then those that "fail" into 4x4 boxes. (Mariani-Silver algorithm.)
Even faster is to split the boxes in half instead of into four boxes. Then it might be optimal to use boxes with a 1.4:1aspect ratio, so they can be split likehow A3 papers are foldedinto A4 and A5 papers. (The DIN approach.)
As with border tracing, rectangle checking only works on areas with one discrete color. But even if the outer area uses smooth/continuous coloring then  rectangle checking will still speed up the costly inner area of the Mandelbrot set. Unless the inner area also uses some smooth coloring method, for instanceinterior distance estimation.
The horizontal symmetry of the Mandelbrot set allows for portions of the rendering process to be skipped upon the presence of the real axis in the final image. However, regardless of the portion that gets mirrored, the same number of points will be rendered.
Julia sets have symmetry around the origin. This means that quadrant 1 and quadrant 3 are symmetric, and quadrants 2 and quadrant 4 are symmetric. Supporting symmetry for both Mandelbrot and Julia sets requires handling symmetry differently for the two different types of graphs.
Escape-time rendering of Mandelbrot and Julia sets lends itself extremely well to parallel processing. On multi-core machines the area to be plotted can be divided into a series of rectangular areas which can then be provided as a set of tasks to be rendered by a pool of rendering threads. This is anembarrassingly parallelcomputing problem. (Note that one gets the best speed-up by first excluding symmetric areas of the plot, and then dividing the remaining unique regions into rectangular areas.)
Here is a short video showing the Mandelbrot set being rendered using multithreading and symmetry, but without boundary following:
Finally, here is a video showing the same Mandelbrot set image being rendered using multithreading, symmetry,andboundary following:
Very highly magnified images require more than the standard 64–128 or so bits of precision that most hardwarefloating-point unitsprovide, requiring renderers to use slow "BigNum" or "arbitrary-precision" math libraries to calculate. However, this can be sped up by the exploitation ofperturbation theory. Given
as the iteration, and a small epsilon and delta, it is the case that
or
so if one defines
one can calculate a single point (e.g. the center of an image) using high-precision arithmetic (z), giving areference orbit, and then compute many points around it in terms of various initial offsets delta plus the above iteration for epsilon, where epsilon-zero is set to 0. For most iterations, epsilon does not need more than 16 significant figures, and consequently hardware floating-point may be used to get a mostly accurate image.There will often be some areas where the orbits of points diverge enough from the reference orbit that extra precision is needed on those points, or else additional local high-precision-calculated reference orbits are needed. By measuring the orbit distance between the reference point and the point calculated with low precision, it can be detected that it is not possible to calculate the point correctly, and the calculation can be stopped. These incorrect points can later be re-calculated e.g. from another closer reference point.
Further, it is possible to approximate the starting values for the low-precision points with a truncatedTaylor series, which often enables a significant amount of iterations to be skipped.Renderers implementing these techniques arepublicly availableand offer speedups for highly magnified images by around two orders of magnitude.
An alternate explanation of the above:
For the central point in the discc{\displaystyle c}and its iterationszn{\displaystyle z_{n}}, and an arbitrary point in the discc+δ{\displaystyle c+\delta }and its iterationszn′{\displaystyle z'_{n}}, it is possible to define the following iterative relationship:
Withϵ1=δ{\displaystyle \epsilon _{1}=\delta }. Successive iterations ofϵn{\displaystyle \epsilon _{n}}can be found using the following:
Now from the original definition:
It follows that:
As the iterative relationship relates an arbitrary point to the central point by a very small changeδ{\displaystyle \delta }, then most of the iterations ofϵn{\displaystyle \epsilon _{n}}are also small and can be calculated using floating point hardware.
However, for every arbitrary point in the disc it is possible to calculate a value for a givenϵn{\displaystyle \epsilon _{n}}without having to iterate through the sequence fromϵ0{\displaystyle \epsilon _{0}}, by expressingϵn{\displaystyle \epsilon _{n}}as apower seriesofδ{\displaystyle \delta }.
WithA1=1,B1=0,C1=0,…{\displaystyle A_{1}=1,B_{1}=0,C_{1}=0,\dotsc }.
Now given the iteration equation ofϵ{\displaystyle \epsilon }, it is possible to calculate the coefficients of the power series for eachϵn{\displaystyle \epsilon _{n}}:
Therefore, it follows that:
The coefficients in the power series can be calculated as iterative series using only values from the central point's iterationsz{\displaystyle z}, and do not change for any arbitrary point in the disc. Ifδ{\displaystyle \delta }is very small,ϵn{\displaystyle \epsilon _{n}}should be calculable to sufficient accuracy using only a few terms of the power series. As the Mandelbrot Escape Contours are 'continuous' over the complex plane, if a points escape time has been calculated, then the escape time of that points neighbours should be similar. Interpolation of the neighbouring points should provide a good estimation of where to start in theϵn{\displaystyle \epsilon _{n}}series.
Further, separate interpolation of both real axis points and imaginary axis points should provide both an upper and lower bound for the point being calculated. If both results are the same (i.e. both escape or do not escape) then the differenceΔn{\displaystyle \Delta n}can be used to recuse until both an upper and lower bound can be established. If floating point hardware can be used to iterate theϵ{\displaystyle \epsilon }series, then there exists a relation between how many iterations can be achieved in the time it takes to use BigNum software to compute a givenϵn{\displaystyle \epsilon _{n}}. If the difference between the bounds is greater than the number of iterations, it is possible to perform binary search using BigNum software, successively halving the gap until it becomes more time efficient to find the escape value using floating point hardware.