Mandelbrot Set
Mandelbrot Set
Adam Cunningham
State University of New York at Buffalo
Numerical Analysis MTH 537
[email protected]
Abstract
Although images of the Mandelbrot set are ubiquitous, the detail shown in the majority of
such images is in fact of the complement of the set, while the set itself is usually displayed in
black. We consider here the problem of the display of the internal structure of the Mandelbrot
set itself. Seven different methods for displaying the internal structure are developed, based on
considerations of the geometry of the orbit associated with each point in the set. The resulting
images obtained using these methods are presented and analyzed.
1 Introduction
Since the work of Benoit Mandelbrot (1924 - 2010) first came to widespread public attention in the
1980s[1], images of the “Mandelbrot Set” have become ubiquitous. These images, both complex and
beautiful, introduced a wider audience to the areas of fractal geometry and to the deep connections
between this area of mathematics and self-similar structures present in the natural world. The detail
present in most of these images is however for points not in the set, but rather in its complement.
Points in the set are usually colored black, and show no internal features.
The problem addressed by this report is that of displaying the internal structure of the Mandelbrot
set. Several methods for doing so are developed and presented, either variations of existing techniques
or (to the authors best knowledge), new techniques for displaying this structure.
In section 2 following we first give a definition of the Mandelbrot set and describe its main com-
ponents. We then describe the most common algorithm used to create images of the set, the
“escape-time algorithm”, and discuss some issues that arise with this algorithm when images of the
set are magnified.
Section 3 on “The Internal Structure of the Mandelbrot Set” constitutes the main part of this report.
Several different methods for displaying the internal structure are described and images generated
using these methods are presented and discussed.
In section 4 we compare some of the results obtained with previously published images, and discuss
their similarities and differences.
In the final section 5, we analyze roundoff errors involved in computing images of the set and the
degree to which the images presented are accurate under magnification.
1
2 THE MANDELBROT SET 2
Pc (z) = z 2 + c
Starting with the initial value z0 = 0, a sequence of complex values 0, c, c2 + c, . . . can be defined by
z0 = 0
zn+1 = Pc (zn ) = zn2 + c (1)
We shall denote the nth element of the sequence generated using the complex parameter c by Pcn .
It can be readily seen that the behavior of this sequence is dependent on the particular value chosen
for c, as the following examples demonstrate:
c = 0: 0 → 0 → 0 → 0 → 0 → 0 ...
c = −0.5 : 0 → −0.5 → −0.25 → −0.4375 → −0.3086 → −.4048 ...
c = −1 : 0 → −1 → 0 → −1 → 0 → −1 ...
c = 1: 0 → 1 → 2 → 5 → 26 → 677 ...
Depending on the value of c, the sequences shown above either converge (to a single point or periodic
cycle), or else diverge to infinity. In the first case, the sequence is bounded, while in the second case,
the sequence is unbounded. Denoting the Mandelbrot set by M , a formal definition is given by
M = {c ∈ C | Pcn 6→ ∞}
The Mandelbrot set is therefore defined as the set of all values of the parameter c in the complex
plane C for which the sequence (1) remains bounded. From the examples above it can be seen that
the points 0, -1/2 and -1 are in the set, while the point 1 is not.
The following figures illustrate the two kinds of sequences, which shall be referred to as orbits.
The diagrams will be referred to as orbit diagrams, which plot successive points in an orbit as
points in the complex plane connected by straight lines. The first two figures show orbits which do
not diverge, and which therefore correspond to points c ∈ M . The orbit on the left converges to
a single point, while that on the right converges to a cycle of period 3, a sequence of three points
that repeats infinitely.
2 THE MANDELBROT SET 3
The figures below illustrate two orbits which do diverge, and which therefore correspond to parame-
ters c 6∈ M . In both cases |Pcn | > 2 for some positive integer n. The value of n at which this occurs
is shown in the upper left of each figure.
1. The large central component is the main cardioid, which contains the parameters c for which
Pcn converges to a single point.
2. Tangent to the main cardioid are an infinite number of period bulbs, each one corresponding
to a set of parameters giving rise to orbits of the same period. The largest of these is shown to
2 THE MANDELBROT SET 4
the left of the main cardioid and contains parameters c which give rise to orbits that converge
to cycles of period 2. The next largest bulbs directly above and below the main cardioid
contain parameters c which give rise to orbits that converge to cycles of period 3.
3. Surrounding the set, although connected to it by thin structures that are in the set, are an
infinite number of satellites, smaller copies of the main body of the set.
The ratio between the period of the attracting cycle of the main cardioid and the tangent period
bulbs is reproduced between these period bulbs and the bulbs which are tangent to them in turn.
So, the bulbs tangent to the main period 2 bulb have periods that are multiples of 2, the bulbs
tangent to the main period 3 bulbs have periods that are multiple of 3, and this same pattern is
repeated for period bulbs of longer period.
The following diagram labels these components, which we shall refer to throughout this report. The
numbers indicate the period of the cycle that orbits converge to in the closest period bulb.
2 THE MANDELBROT SET 5
It can be shown that if the absolute value attained by a point zn in an orbit is ever larger than both
|c| and 2, then the sequence Pcn will diverge to infinity. Suppose that both |zn | > 2 and |zn | > c.
Then |z| ≥ 2 + for some positive , and we therefore have:
|zn+1 | = |zn2 + c|
≥ |zn2 | − |c|
≥ |zn2 | − |z|
= |z|(|z| − 1)
≥ |z|(1 + )
Repeating this process yields |zn+k | ≥ |z|(1 + )k → ∞. The “escape time algorithm” described
below uses this test of divergence to color pixels according to how many iterations n are needed until
|zn | > 2.
The following images were created using the escape-time algorithm and different color maps.
2 THE MANDELBROT SET 6
We note that:
1. For points in the set, the threshhold |zn | > 2 is never reached. In the images of the Mandelbrot
set most commonly seen these points are usually colored black.
2. For points far from the set (and immediately for points outside the circle |z| = 2), this value
is reached in just a few iterations.
3. For points outside of but close to the set, the number of iterations needed until |zn | > 2
increases without bound as the distance from the set decreases.
2 THE MANDELBROT SET 7
Width = 3.0
Width = 0.09
2 THE MANDELBROT SET 8
Width = 0.0046
Width = 0.0002
The following observations can be made:
1. The mean number of iterations needed is greater with increasing magnification, resulting in
longer times for the image to display.
2. The histograms are “right-shifted” with increasing magnification, as the minimum number of
iterations needed to display any point in the image increases. When displaying an image, this
results in an increasing delay before any part of the image is shown, as a greater number of
iterations are needed before the magnitude of any orbit becomes greater than 2.
The issue of magnifying the Mandelbrot set is only pursued further in this report in the context
of a discussion of roundoff error in section 5. It can however be noted that magnification can be
conceptualized as a form of interpolation between points for which the number of iterations needed
is already known. Speeding up the magnification of a region of the set using previously calculated
results would therefore require an analysis of how values calculated using the escape-time algorithm
could be interpolated to a new set of points.
3 THE INTERNAL STRUCTURE OF THE MANDELBROT SET 9
Orbit Magnitude Plot Plot the distance |Pcn − c| against n for a single point c.
Feigenbaum Diagrams Plot the distances |Pcn − c| between points in an orbit against |c| for a
restricted set of points c ∈ C once the orbit has converged to a periodic cycle.
Orbit Convergence Display for a region the number of iterations needed for the orbit to converge
to a periodic cycle to within a given tolerance.
Orbit Cycle Display for a region plane the period of the cycle that the orbit converges to.
Orbit Period Display for a region plane the index n for which |Pcn − c| is a minimum.
Orbit Infimum Display for a region the minimum value of |Pcn − c|.
Total Orbit Angle Display for a region the sum of the internal angles of the first period of the
orbit Pcn .
3 THE INTERNAL STRUCTURE OF THE MANDELBROT SET 10
The images and graphs in the following sections show the orbits and orbit magnitude plots for several
points c in the complex plane. The image on the right shows the orbit Pcn . The graph on the left
shows the distance |Pcn − c| plotted against n.
The orbit shown below was generated for a point in the largest period 4 bulb tangent to the upper
right of the main cardioid. On the left, the orbit can be seen to converge to the shape of a convex
quadrilateral. On the right, the orbit magnitude plot shows a stable cycle of period 4. So, from the
orbit magnitude plot - a graph - we can find information about the shape of the orbit.
The next orbit shown below was generated for a point in the period 5 bulb tangent to the upper left
of the main cardioid. On the left, the orbit can be seen to converge to a five-pointed star polygon.
On the right, the orbit magnitude plot shows a stable cycle of period 5, corresponding to the shape
of the orbit.
The orbit shown below was generated for a point inside the main cardioid, but close to the largest
upper period 3 bulb. The orbit on the left can be seen to be a “shrinking triangle” that converges
to a point. The orbit magnitude plot on the right shows this as a cycle of period 3 bounded by an
exponentially decaying envelope.
The orbit shown below was generated for a point in the largest secondary period bulb tangent to the
largest upper period 3 bulb. The orbit on the left can be seen to be a “double triangle” attractor of
period 6. The orbit magnitude plot on the right shows this as a cycle of period 6..
In the next section we turn to ways in which information about orbits can be displayed for sets of
points, rather than just for single points.
As a further step towards displaying the interior of the Mandelbrot set, we developed a method for
producing similar Feigenbaum diagrams for points in the interior of the set. Given a point z ∈ C, a
set of equally-spaced points on the straight line between the origin and z is generated. For each of
these points c, the orbit Pc1000 is generated, after which it is assumed that convergence to a periodic
cycle has occurred. The distances |Pcn − c| for the next 1000 points on the orbit are then plotted
against |c|.
Some representative examples of the diagrams produced by this method are shown below. The image
on the left shows the Mandelbrot set and a radial line from the origin to a point in the complex
plane. The diagram on the right shows the Feigenbaum diagram produced for the points on this
line.
3 THE INTERNAL STRUCTURE OF THE MANDELBROT SET 14
The Feigenbaum diagram below was produced for a line between the origin and a point in the largest
upper period 3 bulb. As the line passes from the main cardioid into the period 3 bulb, it can be
seen from the diagram on the right that a “trifurcation” occurs as the orbit changes from one that
converges to a single point into one that converges to a cycle of period 3.
Radial line into bulb of period 3 orbits Diagram showing trifurcation inside period 3 bulb
The Feigenbaum diagram below was produced for a line between the origin and a point in the
largest period 5 bulb tangent to the main cardioid on the upper left. As the line passes from the
main cardioid into the period 5 bulb, it can be seen that the corresponding line on the Feigenbaum
diagram splits into 5. This occurs when the orbit changes from one that converges to a single point
into one that converges to a cycle of period 5.
Radial line into bulb of period 5 orbits Feigenbaum diagram showing split into 5
3 THE INTERNAL STRUCTURE OF THE MANDELBROT SET 15
The Feigenbaum diagram below was produced for a line between the origin and a point in the
secondary period 2 bulb tangent to the large period 2 bulb to the left of the main cardioid. As the
line passes from the main cardioid into the period 2 bulb, it can be seen that a “bifurcation” occurs
as the orbit changes from one that converges to a single point into one that converges to a cycle of
period 2. As the line then passes into the secondary period 2 bulb, a further bifurcation occurs as
the orbit changes to one that converges to a cycle of period 4.
1. The number of iterations needed to converge is least at the center of the main cardioid and
increases towards the edge.
2. The same pattern is seen in the period bulbs tangent to the main cardioid, where the orbits
at the center of each bulb converge to a periodic cycle the fastest.
The capture-time algorithm does not however show any features relating to the geometry of the
orbits, so better methods to do so are developed in the following sections.
3 THE INTERNAL STRUCTURE OF THE MANDELBROT SET 17
3.4 The “Orbit Cycle” Algorithm: Displaying the Period of the Cycles
The capture-time algorithm can be modified to display the period of the cycle to which orbits
converge, instead of the number of iterations needed to converge.
An image generated using the “orbit cycle” algorithm is given below, labelled with the period of the
cycles to which orbits converge.
It can be seen that the orbit cycle algorithm partitions the set into distinct components, as described
in section 2.3 on the geometry of the Mandelbrot set.
3 THE INTERNAL STRUCTURE OF THE MANDELBROT SET 18
3.5 The “Orbit Index” Algorithm: Displaying the Period of the Orbit
It was seen from the orbit magnitude plots presented in section 3.1 that orbits have a characteristic
period associated with them that is different from the period of the cycle to which they converge.
As shown previously, the index of the closest point in the orbit Pcn to the point c provides a way to
measure this period.
The orbit index is defined here as one less than the index of the point of minimum distance in the
orbit Pcn from c. The diagram below shows how this index can be interpreted graphically. It can
be seen that in this case the orbit most closely resembles a triangle, and that the orbit index is
calculated to be three. Similarly, if the shape of the orbit approximates a polygon of a given number
of external points, either a simple polygon or a star polygon, then the orbit index will represent
the geometry of the shape by a single number. In a following section we will also present a more
sophisticated way to capture greater detail about the geometry of an orbit - the “total internal
angle” algorithm.
z2 z1 = c
@ z5 z4@
I
@ @ @
@
@
@ @
@ @
@ @ orbit index = 4 - 1 = 3
@
@
@
@
z3
origin = z0
It can be seen immediately that there is an interesting and detailed structure. We can make the
following observations:
1. The interior of the Mandelbrot set has been partitioned into distinct regions.
2. The regions span the main cardioid and the period bulbs, including both orbits that converge
to a periodic cycle in a bulb with decaying orbits of the same period in the main cardioid.
3. The regions become smaller and more numerous closer to the edge of the main cardioid.
4. The period bulbs show an internal structure similar to that of the main cardioid.
3 THE INTERNAL STRUCTURE OF THE MANDELBROT SET 20
To show this internal structure in greater detail, the following image was produced using a different
color map and with the regions labelled with their corresponding indexes.
1. The period of the orbits is greater closer to the edge of the main cardioid.
2. A relationship exists between any two regions and the new region that arises between them
closer to the edge of the set. This is referred to as a “Fibonacci” relationship, so called because
the period of the new region is the sum of those for the two regions it borders.
3 THE INTERNAL STRUCTURE OF THE MANDELBROT SET 21
3. The period bulbs tangent to the main cardioid are partitioned into similar regions as the main
cardioid, but the period of each region is multiplied by the period associated with the bulb.
For example, where the main cardioid has regions of period 1, 2, 3, . . ., the main period 2 bulb
has regions of period 2, 4, 6, . . ., the period 3 bulbs have regions of period 3, 6, 9, . . . and so on.
The following two images show the Fibonacci relationships in more detail for the upper left and
upper right regions of the main cardioid.
Due to the Fibonacci relationships, it can be seen that bordering each region there exists a sequence
of smaller regions whose orbit periods differ by the period of the main region they border. For
example, bordering the region of period 2, there exists (going counter-clockwise) a sequence of
regions of period 5, 7, 9, 11, 13, . . .. Similarly, bordering the region of period 3, there exists (going
clockwise) a sequence of regions of period 5, 8, 11, 14, . . ..
It can also be seen that each region in the main cardioid is associated with one period bulb of
the same period, and spans the boundary between the two. We therefore have the following key
observation:
The structure of the regions in the orbit index map mirrors the structure of the period bulbs.
Hence, where a Fibonacci relationship exists between regions, an identical Fibonacciship relation
exists between the period of the orbits in their associated period bulbs.
3 THE INTERNAL STRUCTURE OF THE MANDELBROT SET 22
Magnification of the satellite “mini-Mandelbrot sets” surrounding the main body of the Mandelbrot
set shows that the structures seen in the main body of the set are duplicated in these satellites on a
smaller scale. It can also be seen that, associated with each satellite, there is a characteristic number
multiplying each period relative to the corresponding period in the main body of the set. This is
demonstrated in the following images.
The image on the left above shows the multiplier 4 associated with the satellite. Hence, the index
associated with each region is 4 times those seen in the corresponding regions in the main body of
the set. Similarly for the image on the right, there is a characteristic number 7 by which the indexes
are multiplied.
3 THE INTERNAL STRUCTURE OF THE MANDELBROT SET 23
inf{|Pcn − c| | n > 1}
c
@
@ I
@
@ @
@
@
@ @
@ @
@ @
orbit infimum
@
@
@
@
origin
Comparing the image above to those produced by the orbit index algorithm, it can be seen that
the lines seen in the image above correspond to boundaries between regions. This is described more
fully in the following section.
3 THE INTERNAL STRUCTURE OF THE MANDELBROT SET 25
3.6.2 The Orbit Infimum and the Boundaries between Orbit Index Regions
The following images compare the orbit index on the left with the orbit infimum on the right.
It can be seen that the lines in the orbit infimum image correspond to boundaries in the orbit index
image. The reason for this becomes clear if we examine what happens to an orbit when a parameter
c crosses the boundary between regions. If |c| is held constant while arg c is increased, then since
multiplication in the complex plane corresponds to the addition of angles, as arg c increases the
internal angles of the orbit will decrease. Visually, it can be seen that the orbit “closes up” when
this occurs. This is shown in the following set of images.
So, the boundaries in the orbit index map are regions where the orbit infimum is at a local maximum
with respect to arg c, as one point in the orbit moves further from c, but the next point in the orbit
has not yet come to its closest approach. This manifests as the darker lines between regions in the
orbit infimum map. If the orbit infimum were plotted as a surface, this would result in ridges of
3 THE INTERNAL STRUCTURE OF THE MANDELBROT SET 26
3.6.3 The Interaction between |c| and arg c in the Orbit Index Map
The partitioning of the Mandelbrot set into the regions seen in the orbit index map can also be
understood as arising from the interaction between changes in the orbit as arg c increases (which
decreases the internal angles of the orbit) and changes in the orbit as |c| increases (which decreases
the rate of convergence to a periodic cycle, as seen in the capture-time map).
This can be seen in the orbit index images below, which show the change in an orbit as arg c is held
approximately constant while |c| is increased along the border between the two largest regions in the
main cardioid of period 3 and 4. When c is closer to the origin, |c| is less and the rate of convergence
to a single point is greater. As a result, points in the orbit Pcn for which n is higher converge faster
to the fixed point. This is seen in the image below on the left, where the fourth and fifth points Pc4
and Pc5 are closer to c than the eights point Pc8 .
As |c| is increased, the rate of convergence decreases, which alters the relative distances Pcn . In the
center image below, the points Pc4 and Pc5 are now equidistant from c with the point Pc8 . In the
image below on the right, as |c| is increased further, the point Pc8 has moved closer to c than either
Pc4 or Pc5 , and the orbit has changed to one of period 7.
Between period 3 and 4 orbit Between period 3, 4 and 7 orbit Period 7 orbit
angles change. Mapping this angle to a color then provides a way to display the changing shapes of
orbits within a given region.
The diagram below shows the total internal angle for an orbit, defined as the sum of the internal
angles up to the point of minimum distance in the orbit from c. It can be seen in this example that
the orbit most closely resembles a triangle, and that the sum of the internal angles will accordingly
be approximately 180◦ .
c
@θ2 θ1
@
@
total internal angle = θ1 + θ2 + θ3 ≈ 180◦
@ @
@
@
@ @
@
@
@ θ3
@
origin
It can be seen that this algorithm does show more detail of the internal structure of regions of the
orbit index map, while still preserving the boundaries of the map.
4 COMPARISON OF RESULTS 29
4 Comparison of Results
The images shown in this report are not the first ones that have been presented of the internal
structure of the Mandelbrot set. For example, the book “The Beauty of Fractals” [2] contains the
following figures:
Beauty of Fractals, Figure 33, page 60 Beauty of Fractals, Figure 34, page 61
The image on the left therefore displays the minimum distance from the origin for any point in the
orbit Pck (0), while the images on the right shows the value of the index k for which this is so.
It can be seen that the image on the left is similar to that produced in this report using the orbit
infimum algorithm, while the image on the right is similar to that produced using the orbit index
algorithm. The following analysis shows why this is the case. If we consider successive points in an
orbit Pcn , then we have:
zn+1 = zn2 + c
⇒ zn+1 − c = zn2
⇒ |zn+1 − c| = |zn |2
⇒ inf{|zn+1 − c|} = inf{|zn |2 }
5 ERRORS IN COMPUTING THE INTERNAL MAPS 30
So, the relationship is this: the index of the point in the orbit which is closest to c is one greater
than the index of the point in the orbit that is closest to the origin. This explains the similarity
between the two orbit infimum and orbit index images developed for this report and those shown
in “The Beauty of Fractals”. Although the orbit infimum images were developed with the goal of
displaying the underlying period of orbits corresponding to different parameters c, these particular
algorithms are therefore similar to those that have been produced before.
|f l(x) − x| 1 |f l(y) − y| 1
≤ mach ≈ 1.11 × 10−16 and ≤ mach ≈ 1.11 × 10−16
|x| 2 |y| 2
For sufficiently small regions, the roundoff that occurs when c is represented by a pair of finite-
precision floating-point numbers results in neighboring pixels on the screen being represented by the
same complex floating-point number. The escape-time algorithm will then take the same number of
iterations to diverge for both points, so they will be displayed in the same color. The visual effect
is that the image loses resolution and becomes segmented into blocks of the same color.
The effect of roundoff error on the divergence map created using the escape-time algorithm can
be seen graphically in the following two images. The image on the left shows a region of the
complex plane that is 4.3 ∗ 10−14 wide. With an 800 × 800 screen, the pixel separation in this
case is approximately 5.4 ∗ 10−17 , which is less than mach . The image on the right shows a region
magnified from the center of the image on the left that is 5.9 ∗ 10−15 wide, with a pixel separation
of approximately 7.4 ∗ 10−18 . The loss of resolution and the division of the image into solid blocks
of color can be clearly seen.
5 ERRORS IN COMPUTING THE INTERNAL MAPS 31
The fact that roundoff error is relative rather than absolute can be seen in the following images.
The image on the left is from a region of the complex plane close to the real axis. The image on
the right is from a region close to the imaginary axis. Since the absolute values of the real and
imaginary parts of c are significantly different in both of these regions, the effect of roundoff error
in these cases is greater along one axis than another. This results in the elongated blocks of color
seen in the two images.
Divergence map at real axis, width = 1.6 ∗ 10−15 Divergence map at imaginary axis, width = 1.5 ∗ 10−15
6 CONCLUSION 32
Roundoff error in the parameter c will also be an issue when any of the internal maps are magnified.
We show below two images from the orbit index map for the main cardioid. The image on the left
is from the boundary where regions of period 2, 3 and 5 meet. The image on the right is close to to
the edge of the main cardioid where regions of period 61, 112 and 173 meet.
Regions 2, 3, and 5, width = 8.6 ∗ 10−15 Regions 61, 112, and 173, width = 9.6 ∗ 10−16
It is in some ways surprising how good these images are at this degree of magnification. Despite the
relative separation of neighboring pixels being less than machine epsilon, the error seen is roundoff
error in the parameter c, rather than accumulated error due to the application of an iterative
algorithm.
6 Conclusion
This report described the successful implementation of several different methods for displaying the
internal structure of the Mandelbrot set and similarities to existing methods were discussed. In one
case, the “total internal angle” method, the images presented are (as far as the author is aware) new
images of the internal structure of the set.
Some of the errors involved in displaying the set were also discussed. It was seen that roundoff
error in the parameter c prevents magnification of the set beyond a certain point, where the relative
separation of pixels representing values in the complex plane approaches machine epsilon.
References
[1] Benoit Mandelbrot The Fractal Geometry of Nature 1983: W. H. Freeman and Company
[2] H.-O. Peitgen and P. H. Richter The Beauty of Fractals 1986: Springer-Verlag
[3] Timothy Sauer Numerical Analysis, Second Edition 2012: Pearson
A THE “MANDELBROT EXPLORER” PROGRAM 33
Main Screen This displays one of several different types of image of the Mandelbrot set, described
below under “Maps”.
Title Bar This shows the region in the complex plane that is being displayed, as well as the width
of the region being displayed.
Iteration Number The number in the top left-hand corner displays the current iteration for the
map being displayed.
Point number The number in the top right-hand corner displays information associated with the
pixel that the cursor is over. Information about any point can therefore be obtained by moving
the cursor over the point. The interpretation of this number depends on the type of map being
displayed as follows:
Divergence map Number of iterations needed to diverge.
Convergence map Number of iterations needed to converge to a given tolerance.
Cycle map Period of periodic cycle to which orbit converges.
Index map Period of orbit calculated using orbit index.
Infimum map Distance of closest point in orbit to c.
Angle map Sum of internal angles of first periodic orbit (in degrees).
Magnify Selecting a section of the screen using the mouse results in the selected area being mag-
nified and displayed. The number in the top left corner of the screen shows the number of
iterations used for the current image.
Orbit Left mouse clicking on the screen results in the orbit Pcn being displayed, where c is the
complex number corresponding to the point on the screen that was clicked. The number in
the top left corner of the screen shows the number of iterations taken for the orbit shown to
diverge i.e. the value n such that |Pcn | > 2.
Cycle Color cycles (palette shifts) using the currently selected palette.
A.3 Maps
Six different maps of the Mandelbrot set can be displayed.
Divergence Map Colors pixels by the number of iterations needed to diverge according to the
escape-time algorithm.
Convergence Map Colors pixels by the number of iterations needed for points in the Mandelbrot
set to converge to an orbit to a given tolerance.
Cycle Map Colors pixels by the period of the periodic cycle to which the orbit converges.
A THE “MANDELBROT EXPLORER” PROGRAM 35
Period Map Colors pixels by the period of the orbit magnitude plot.
Infimum Map Colors pixels by the closest distance to the parameter c attained by any point in
an orbit.
Angle Map Colors pixels by the sum of the internal angles of the first period in the orbit magnitude
plot.
Distribution Produces a histogram of the number of iterations needed to diverge for the pixels in
the current image.
Feigenbaum Diagram Produces a Feigenbaum diagram for points from the origin to where the
mouse is clicked.
Orbit Plot Produces an orbit magnitude plot for the point where the mouse is clicked.
c Toggles color cycling for the current palette Off - Forward - Backward.
d Switches the map to show the orbit divergence.
f Switch to Feigenbaum mode. Left mouse click then produces a Feigenbaum diagram for the radial
line selected.
↓ Double the quantization level on color palettes. Doubles the difference needed to show two pixels
as a different color.
1. Variable names beginning with lower case letters denote single variables.
2. Variable names beginning with upper case letters denote arrays.
Note that the implementation of these algorithms in the Mandelbrot Explorer involves additional
steps for efficiency. At each step, the test for divergence is applied and points that have diverged are
removed from further computations. This ensures that we do not perform repeated (and unnecessary)
computations for points not in the set. Depending on the particular algorithm, points that have
already been classified (by capture-time, orbit index etc) are also removed from further computations.
These steps have been omitted from this report to simplify the descriptions. The code listing included
with this report provides full details of the implementation.
C Python Implementation
C.1 Packages Used
Mandelbrot Explorer was written using the following Python packages:
import pygame
import numpy
from pygame.locals import *
from sys import exit
from random import randint
from numpy import *
from pylab import hist, plot, show, xlabel, ylabel, title
import time
HEIGHT = 800
SCREEN_SIZE = (HEIGHT, HEIGHT)
ORBIT_MODE = 1
MAGNIFY_MODE = 2
CYCLE_MODE = 3
FEIGENBAUM_MODE = 4
DISTANCE_MODE = 5
DIVERGENCE_MAP = 1
HANKEL_MAP = 2
CONVERGENCE_MAP = 3
PERIOD_MAP = 4
ANGLE_MAP = 5
CYCLE_MAP = 6
NO_GRID = 0
CARTESIAN_GRID = 1
POLAR_GRID = 2
EPSILON = 10**(-6)
NUMBER_KEYS = (K_0, K_1, K_2, K_3, K_4, K_5, K_6, K_7, K_8, K_9)
#--------------------------------------------------------------------------
#
# Color maps
#
#--------------------------------------------------------------------------
blackwhite = make_palette([(0,0,0),
(255, 255, 255),
(255, 255, 255)],
[1, 255])
heatmap = make_palette([(0, 0, 0),
(0,0,160), # Dark blue
(0, 255, 255), # Light blue
(128, 255, 128), # Light green
(255, 255, 0), # Yellow
(255, 128, 64), #Light orange
(255, 128, 0)], # Orange
[2, 16, 32, 64, 128, 255])
jewel = make_palette([(0, 0, 0),
(0, 0, 128),
(128, 0, 128),
(221, 155, 34),
(0, 64, 0),
(0, 0, 255),
(0, 0, 128),
(0, 64, 0),
(221, 155, 34),
(128, 0, 128),
(0, 0, 255)],
[2, 4, 6, 8, 12, 16, 32, 64, 128, 255])
redgreen = make_palette([(0, 0, 0),
(0, 255, 0),
(255, 255, 0),
(255, 0, 0)],
C PYTHON IMPLEMENTATION 39
#--------------------------------------------------------------------------
#
# Escape-time algorithm
#
#--------------------------------------------------------------------------
#--------------------------------------------------------------------------
#
# Class to hold all the data
#
#--------------------------------------------------------------------------
class Mandelbrot():
def __init__(self):
self.mode = MAGNIFY_MODE
self.map = DIVERGENCE_MAP
C PYTHON IMPLEMENTATION 41
self.cycle = True
self.paused = False
# Magnification rectangle
self.startx = 0
self.starty = 0
self.endx = 0
self.endy = 0
self.magnify = False
# Iterations
self.itermax = 10000
self.iter = 0
# Orbits
self.orbit = []
self.zorbit = []
self.zc = 0
# Colors
self.palettes = [blackwhite, heatmap, jewel, rainbow,
blackyellow, redpurple, indi, indi2, ocean]
self.palette_index = 0
self.palette = heatmap
self.color = [0, 0, 128]
self.word_color = [255, 255, 0]
self.rect_color = [255, 255, 0]
self.line_color = [255, 255, 0]
self.grid_color = [127, 127, 127]
# Display options
self.levels = 256
self.normalize = False
self.grid = NO_GRID
self.origin = ((HEIGHT * 2)/3, HEIGHT/2)
self.current_pos = (0, 0)
C PYTHON IMPLEMENTATION 42
# Fonts
self.word_font = pygame.font.SysFont("courant", 16, True)
self.button_font = pygame.font.SysFont("courant", 28, True)
pygame.surfarray.use_arraytype(’numpy’)
def initialize_area(self):
pygame.display.set_caption("Mandelbrot Explorer by Adam Cunningham")
self.xmin = -2.
self.xmax = 1.
self.ymin = -1.5
self.ymax = 1.5
#------------------------------------------------------------------------
# Magnify image functions
#------------------------------------------------------------------------
def initialize_image(self):
self.iter = 0
n = self.width
m = self.width
ix, iy = mgrid[0:n, 0:m]
x = linspace(self.xmin, self.xmax, self.width)[ix]
# Array image is stored upside down for correct display
y = linspace(self.ymax, self.ymin, m)[iy]
# c holds the complex plane - the parameter space
c = x+complex(0,1)*y
del x, y
# img holds the number of iterations needed for divergence
img = zeros(c.shape, dtype=int)
ix.shape = n*m
iy.shape = n*m
c.shape = n*m
self.ix = ix
self.iy = iy
self.c = c
# z holds the results of the latest iteration
self.z = copy(c)
# Initialize the internal map - the first closest point is c itself
self.closest = abs(copy(c))
self.previous = zeros_like(c)
self.saved = [self.previous]
C PYTHON IMPLEMENTATION 43
#------------------------------------------------------------------------
# Functions for updating the image using different maps
#------------------------------------------------------------------------
def update(self):
’’’ Perform one iteration of the currently active mode’’’
if self.paused == True:
return
elif self.mode == MAGNIFY_MODE:
if self.map == DIVERGENCE_MAP:
self.update_divergence()
elif self.map == HANKEL_MAP:
self.update_hankel()
elif self.map == CONVERGENCE_MAP:
self.update_internal()
elif self.map == PERIOD_MAP:
self.update_period()
elif self.map == ANGLE_MAP:
self.update_angle()
elif self.map == CYCLE_MAP:
self.update_time()
elif self.mode == ORBIT_MODE:
self.update_orbit()
elif self.mode == CYCLE_MODE:
self.update_cycle()
def update_divergence(self):
’’’Updates the divergence map by one iteration’’’
z = self.z
c = self.c
i = self.iter
img = self.img
ix = self.ix
iy = self.iy
# Update z to the next point in the orbit
multiply(z, z, z)
add(z, c, z)
# Check which points have diverged
rem = abs(z)>2.0
# Save the iterations needed for the points which just diverged
img[ix[rem], iy[rem]] = i+1
# rem is now an array showing the remaining points
rem = -rem
# z becomes any points which have not yet diverged
self.z = z[rem]
self.ix, self.iy = ix[rem], iy[rem]
self.c = c[rem]
C PYTHON IMPLEMENTATION 44
self.iter += 1
self.img = img
def update_convergence(self):
’’’Updates the convergence map by one iteration’’’
z = self.z
c = self.c
i = self.iter
img = self.img
ix = self.ix
iy = self.iy
closest = self.closest
multiply(z, z, z)
add(z, c, z)
# Find the distance of the latest point in orbit from c
distance = abs(c - z)
closer = distance < closest
# If latest point in orbit is closer than previous, update image
img[ix[closer], iy[closer]] = i + 1
# Save the new minimum distance
minimum(distance, closest, closest)
# Check which points have diverged
rem = abs(z)>2.0
# Points which just diverged get set to zero in the image
img[ix[rem], iy[rem]] = 0
# rem is now an array showing the remaining points
rem = -rem
# z becomes any points which have not yet diverged
self.z = z[rem]
self.ix, self.iy = ix[rem], iy[rem]
self.c = c[rem]
self.closest = closest[rem]
self.iter += 1
self.img = img
def update_hankel(self):
’’’Updates the hankel map by one iteration’’’
z = self.z
previous = copy(z)
c = self.c
i = self.iter
saved = self.saved
img = self.img
ix = self.ix
iy = self.iy
# Update z to the next point in the orbit
multiply(z, z, z)
add(z, c, z)
# Check which points have diverged
rem1 = abs(z)>2.0
C PYTHON IMPLEMENTATION 45
def update_time(self):
’’’Updates the cycle map by one iteration’’’
z = self.z
previous = copy(z)
c = self.c
i = self.iter
saved = self.saved
img = self.img
ix = self.ix
iy = self.iy
# Update z to the next point in the orbit
multiply(z, z, z)
add(z, c, z)
# Check which points have diverged
rem1 = abs(z)>2.0
# Check which points have converged
rem2 = abs(previous - z) < EPSILON
rem = logical_or(rem1, rem2)
img[ix[rem2], iy[rem2]] = 1
# Compare with all previous points in orbit
for j, s in enumerate(saved):
rem3 = abs(s - z) < EPSILON
# Points which converge get set to the period of the cycle
img[ix[rem3], iy[rem3]] = i - j + 1
rem2 = logical_or(rem2, rem3)
#rem = logical_or(rem1, rem2)
#rem = rem1
C PYTHON IMPLEMENTATION 46
# Any points which have diverged get set to zero in the image
img[ix[rem1], iy[rem1]] = 0
# rem is now an array showing the remaining points
rem = -rem
# Update z and c to only include points which have not yet diverged
self.z = z[rem]
self.ix, self.iy = ix[rem], iy[rem]
self.c = c[rem]
for j, s in enumerate(saved):
saved[j] = s[rem]
self.saved = saved + [copy(self.z)]
self.iter += 1
self.img = img
def update_internal(self):
’’’Updates the internal map by one iteration’’’
z = self.z
c = self.c
i = self.iter
img = self.img
ix = self.ix
iy = self.iy
closest = self.closest
# Update z to the next point in the orbit
multiply(z, z, z)
add(z, c, z)
# Save minimum distance of any point from c
minimum(abs(z - c), closest, closest)
# Check which points have diverged
rem = abs(z)>2.0
# Points which just diverged get set to zero in the image
img[ix[rem], iy[rem]] = 0
# rem is now an array showing the remaining points
rem = -rem
# z becomes any points which have not yet diverged
self.z = z[rem]
self.ix, self.iy = ix[rem], iy[rem]
self.c = c[rem]
self.closest = closest[rem]
img[ix[rem], iy[rem]] = ceil(self.closest * self.levels)
self.iter += 1
self.img = img
def update_period(self):
’’’Updates the period map by one iteration’’’
z = self.z
c = self.c
i = self.iter
img = self.img
ix = self.ix
C PYTHON IMPLEMENTATION 47
iy = self.iy
closest = self.closest
multiply(z, z, z)
add(z, c, z)
# Find the distance of the latest point in orbit from c
distance = abs(c - z)
closer = distance < closest
# If latest point in orbit is closer than previous, update image
img[ix[closer], iy[closer]] = i + 1
# Save the new minimum distance
minimum(distance, closest, closest)
# Check which points have diverged
rem = abs(z)>2.0
# Points which just diverged get set to zero in the image
img[ix[rem], iy[rem]] = 0
# rem is now an array showing the remaining points
rem = -rem
# z becomes any points which have not yet diverged
self.z = z[rem]
self.ix, self.iy = ix[rem], iy[rem]
self.c = c[rem]
self.closest = closest[rem]
self.iter += 1
self.img = img
def update_angle(self):
’’’Updates the angle map by one iteration’’’
z = self.z
c = self.c
i = self.iter
img = self.img
ix = self.ix
iy = self.iy
closest = self.closest
previous = self.previous
angles = self.angles
previous_line = previous - z
previous[:] = z
# Update z to the next point in the orbit
multiply(z, z, z)
add(z, c, z)
# Keep cumulative total of the angles in the orbit
angles += abs(angle(previous_line/(z - previous), deg=True))
# Find the distance of the latest point in orbit from c
distance = abs(c - z)
closer = distance < closest
# If latest point in orbit is closer than previous, update image
# Show sum of interior angles of orbit
img[ix[closer], iy[closer]] = maximum(1, rint(angles[closer]))
# Save the new minimum distance
C PYTHON IMPLEMENTATION 48
def update_cycle(self):
’’’ Perform one iteration of currently active mode’’’
palette = self.palette
# If cycling forward
if self.cycle:
if isinstance(palette, tuple):
self.palette = (palette[-1],) + palette[:-1]
else:
self.palette = [palette[-1]] + palette[:-1]
# Else must be cycling backward
else:
if isinstance(palette, tuple):
self.palette = palette[1:] + (palette[1],)
else:
self.palette = palette[1:] + [palette[1]]
#------------------------------------------------------------------------
# Render the image and associated information
#------------------------------------------------------------------------
r = Rect(self.startx, self.starty,
self.endx - self.startx, self.endy - self.starty)
pygame.draw.rect(surface, self.rect_color, r, 1)
# Draw the overlying grid
if self.grid == CARTESIAN_GRID:
intervals = 12
for i in arange(1, intervals):
x = (i * HEIGHT)/intervals
pygame.draw.line(surface, self.grid_color, (x, 0), (x, HEIGHT))
pygame.draw.line(surface, self.grid_color, (0, x), (HEIGHT, x))
elif self.grid == POLAR_GRID:
origin_x, origin_y = self.origin
# Draw the lines of constant radius
for i in arange(1, 10):
radius = (i * HEIGHT)/12
pygame.draw.circle(surface, self.grid_color, self.origin, radius, 1)
# Draw the lines of constant angle
lines = 6
radius = (HEIGHT * 2)/3
pygame.draw.line(surface, self.grid_color, (0, origin_y), (HEIGHT, origin_y))
for i in arange(1, lines):
theta = (i*pi)/lines
end_y = origin_y + (radius* sin(theta))
end_x = origin_x + (radius* cos(theta))
pygame.draw.line(surface, self.grid_color, self.origin,
(end_x, end_y))
end_y = origin_y - (radius* sin(theta))
pygame.draw.line(surface, self.grid_color, self.origin,
(end_x, end_y))
# Update the iteration number on the screen
text_surface = self.button_font.render(str(self.iter), False, self.word_color)
surface.blit(text_surface, (self.text_x, self.text_y))
# Update the cursor image number on the screen
pixel_value = self.img[self.current_pos[0], self.current_pos[1]]
text_surface = self.button_font.render(str(pixel_value), False, self.word_color)
surface.blit(text_surface, (self.cursor_x, self.cursor_y))
# Draw the orbit
if self.mode == ORBIT_MODE and (len(self.orbit) > 1):
pygame.draw.lines(surface, self.line_color, False, self.orbit)
# Draw the line along which the Feigenbaum disgram is created
if self.mode == FEIGENBAUM_MODE:
pygame.draw.line(surface, self.line_color, self.origin, self.current_pos)
# If the palette is being cycled, make sure it shows up
if self.mode == CYCLE_MODE:
screen.set_palette(self.palette)
#------------------------------------------------------------------------
# Magnify mode
#------------------------------------------------------------------------
C PYTHON IMPLEMENTATION 50
self.starty = top
self.endx = left + width
self.endy = top + width
return
#------------------------------------------------------------------------
# Conversions between screen coordinates and points in complex plane
#------------------------------------------------------------------------
#------------------------------------------------------------------------
# Orbit mode
#------------------------------------------------------------------------
def initialize_orbit(self):
startz = complex(0, 0)
self.zorbit = [startz]
self.orbit = [self.z_to_xy(startz)]
def update_orbit(self):
z = self.zorbit[-1]
zn = (z * z) + self.zc
if abs(zn) <= 2 and (self.iter < self.itermax):
self.iter += 1
self.zorbit.append(zn)
self.orbit.append(self.z_to_xy(zn))
z = c
saved = empty(max_iters)
saved[0] = 0
total = 1
for i in xrange(1, max_iters):
z = z*z + c
distance = abs(z - c)
saved[i] = distance
plot(i, distance, ’ro’)
total += 1
# Check if orbit has diverged
if abs(z) > 2.0:
break
# Plot the points in the orbit
plot(arange(total), saved[:total])
xlabel(’Iteration’)
ylabel(’Distance from c’)
title(’Distance of orbit from c, c = ’ + str(c))
show()
#------------------------------------------------------------------------
# Distribution of iterations needed to diverge
#------------------------------------------------------------------------
def show_distribution(self):
image = self.img
flat = reshape(image, HEIGHT*HEIGHT)
hist(flat, bins=self.iter + 1, fc=’b’, ec=’b’, normed=’True’)
av = mean(flat)
xlabel(’Number of iterations’)
ylabel(’Relative frequency’)
title(’Iterations needed to diverge, mean = ’ + str(int(av)))
show()
#------------------------------------------------------------------------
# Feigenbaum diagram for points on a given constant angle from the origin
#------------------------------------------------------------------------
add(z, c, z)
# Check which points have diverged
rem = abs(z)>2.0
# z becomes any points which have not yet diverged
z[rem] = 2.0
# Plot the orbits
for t in arange(max_iters):
plot(r, abs(z), ’k.’, markersize=0.1)
multiply(z, z, z)
add(z, c, z)
# Check which points have diverged
rem = abs(z)>2.0
# z becomes any points which have not yet diverged
z[rem] = 2.0
xlabel(’|c|’)
ylabel(’Distance from origin of orbit’)
title(r’$z = ’ + str(z_pt) + ’$’)
show()
#------------------------------------------------------------------------
# Event handlers
#------------------------------------------------------------------------
self.initialize_image()
elif key == K_r:
# restart in magnify mode
self.mode = MAGNIFY_MODE
self.initialize_area()
self.initialize_orbit()
self.initialize_image()
elif key == K_s:
# Show the distibution of iterations needed to diverge
self.show_distribution()
elif key == K_t:
# Cycle map - period of the orbit
self.map = CYCLE_MAP
self.mode = MAGNIFY_MODE
self.initialize_orbit()
self.initialize_image()
elif key == K_SPACE:
# Turn pause on or off
self.paused = not self.paused
elif key == K_UP:
# Halve the spacing shown between levels
self.levels *= 2
elif key == K_DOWN:
# Double the spacing shown between levels
if self.levels > 1:
self.levels /= 2
elif key in NUMBER_KEYS:
# Change the palette
palettes = self.palettes
num_palettes = len(palettes)
key_num = NUMBER_KEYS.index(key)
# Check we’re not indexing past the end of the palettes
if key_num < num_palettes:
new_palette = palettes[key_num]
self.palette = new_palette
screen.set_palette(new_palette)
#--------------------------------------------------------------------------
#
# Pygame Initialization
#
#--------------------------------------------------------------------------
pygame.init()
screen = pygame.display.set_mode(SCREEN_SIZE, 0, 8)
main = Mandelbrot()
main.render(screen)
pygame.display.update()
#--------------------------------------------------------------------------
#
# Main event handling loop
#
#--------------------------------------------------------------------------
on = True
while on:
if event.type == KEYDOWN:
main.keydown(event.key, screen)
if event.type == MOUSEBUTTONDOWN:
x, y = pygame.mouse.get_pos()
main.mousedown(x, y, screen)
if event.type == MOUSEBUTTONUP:
x, y = pygame.mouse.get_pos()
main.mouseup(x, y, screen)
if event.type == MOUSEMOTION:
x, y = pygame.mouse.get_pos()
main.mousemove(x, y, screen)
main.update()
main.render(screen)
pygame.display.flip()