Some Theory: The mathematical study of fractals and chaos theory begins by considering points of the plane as being identified by complex numbers. Thus, the point (x, y) corresponds to the complex number x+yi, where i is the square root of -1.
Now suppose we are given a function w = f(z), for complex numbers z = x+yi. For any z, we can look at the sequence: z, f(z), f(f(z)), f(f(f(z))), ..., fn(z), ... where fn(z) means that the function f has been applied n times. This sequence of points is called the orbit of z under function f.
The study of chaos and dynamical systems considers the types of orbits that might exist and various properties of these orbits.
For our purposes, we simply ask whether the sequence converges to a point, diverges toward infinity, or wanders somewhere in a contained region of the plane. More specifically, for a given function f, we can ask what points z are such that the orbit diverges toward infinity. For points where the orbit diverges, we can ask how quickly divergence occurs. (For example, does z, f(z), f(f(z)), ... stay in a relatively confined area for awhile before it moves away toward infinity?) In drawing fractals, we consider each point z in the plane and color it according to such divergence properties. More specifically, the Julia Set for f(z) is the set of points which lie at the boundary of those points for which the orbit diverges.
Some Practical Notes and an Example: As an example, consider f(z) = z² - 1. If z = x+yi, then z² is x²-y² + 2xyi, and f(x+yi) = x² - y² - 1 + 2xyi. That is, the x (or real) coordinate of f(x+yi) is x² - y² - 1, and the y (or imaginary) coordinate is 2xyi. Also, the size of z = x+yi is defined to be x² + y².
Next consider the squaring operation. If the size of a complex number is bigger than 1, then its square is still larger in size. Now suppose that the size of a complex number z is at least 2. Then its square has size at least 4, and z² - 1 has size at least 3. In fact, one could prove that if z has size at least 2, then the size of f(z) is increased by at least a factor of 3/2. Thus, if z is any number of size greater than 2, the sequence z, f(z), f(f(z)), ... must diverge toward infinity -- the sizes of successive numbers get bigger and bigger.
Now suppose we take any number z, and suppose that after we apply f to it several times we obtain a value whose size exceeds 2. Then the above shows that subsequent iterations of f will cause the result to diverge. This observation provides a simple algorithm for us to gain information for a fractal and the Julia Set.
For a given z, compute f(z), f(f(z)), f(f(f(z))), etc.
If the size of any element in the sequence exceeds 2, then
the sequence diverges toward infinity.
To give a geometric picture of a fractal or of the Julia Set, we compute
some terms in the sequence z, f(z), f(f(z)), etc. If we obtain a
size of at least 2, we record how many iterations were required for this to
occur. On the other hand, if after 25 or 30 iterations the size of
f(z) is small (less than 2), we might conclude the orbit of z is
unlikely to diverge. Once we know the number of iterations for each
z, we might color each point as follows:
| Number of Iterations to Diverge | Color |
|---|---|
| 0, 1 | black |
| 2, 3, 4 | white |
| 5, 6, 7 | lightblue |
| 8, 9, 10 | red |
| 11, 12, 13, 14 | cyan |
| 15, 16, 17, 18 | green |
| 19, 20, 21, 22 | orange |
| 23, 24, 25, 26 | maroon |
| 27, 28, 29, 30+ | light pink |
Using the above coloring scheme (or a set of colors of your own choosing), the resulting plot (or fractal) often is interesting visually. Fractals have nice aesthetic qualities, so graphing them seems worthwhile.
As a practical matter, we normally divide a rectangular region (e.g. -2 <= x <= 2, -2 <= y <= 2) into many pieces and sample a point for each piece. The color of the small pieces then depends upon the conclusions we reach for the orbits for that representative point.
In particular, we may define an increment (e.g., 0.05) and perform
computations for the Julia Set for the points
x = -2, -1.95, -1.9, -1.85, ..., 1.90, 1.95, 2.0, and
y = -2, -1.95, -1.9, -1.85, ..., 1.90, 1.95, 2.0.
Then, we may color the small rectangle around each point according to the
result of the computation.
From the standpoint of parallelism, fractals have the useful properties that they require considerable computation at each point and the results for one point are independent from those of another point. Thus, parallelism can be used in several ways to allow multiple processors to compute the appropriate colors independently for each point. For these reasons, the computation of fractals is a very common application for the testing of parallel hardware and architecture. The resulting programs are relatively simple, and the results are aesthetically pleasing.
While there is not sufficient time in one lab to fully work through many of the possibilities for parallelism, this lab uses both pipes and sockets to take some steps toward parallelism.
Consider the following three programs:
For more information on samba, see http://www.cc.gatech.edu/gvu/softviz/algoanim/.
The following steps will get you started with these programs:
Copy julia-set.c and julia-set-coord.c to your account, and compile them to programs julia-set and julia-set-coord, respectively.
Prepare to use samba by issuing the following commands in a terminal window:
xrdb -merge /home/walker/polka/polka/polka-athena.res julia-set | julia-set-coord | /home/walker/polka/polka/Samba/samba
The first of these commands allocates required resources for the samba program. The second command runs the three programs as a pipelined sequence.
To understand more clearly how these programs interact, review the output of the first two programs as follows:
Run program julia-set by itself. Observe that the output consists of an x and y value, followed by the number of iterations n for | fn(n) | > 2.
Run the sequence julia-set | julia-set-coord. Observe that the output consists of graphics commands:
The coords command gives the bounds of the box to be displayed, with coordinates x-min, y-min, x-max, and y-max.
Both julia-set.c and julia-set-coord.c allow command-line arguments to modify the computations done:
julia-set.c may take 6 arguments:
julia-set-coord.c may take 2 parameters:
Experiment with these parameters. For example, try the following command to double the number of x-values and the number of y-values for the computation:
julia-set -2 2 -2 2 0.025 0 | julia-set-coord 1 0.025 | /home/walker/polka/polka/Samba/samba
Note that while this processing takes longer, the resolution of the picture is better. (Actually, much of the slower processing is due to the graphics display program, as the visualization program must handle many more graphical objects.)
Since the background color is black and since black also is used for the display of points with 0 or 1 iterations for comutations to become large, many graphical objects can be omitted if objects are not created for regions with 0 or 1 iterations. Changing the threshhold for printing from 0 to 2 accomplishes this efficiency, as shown below:
julia-set -2 2 -2 2 0.025 2 | julia-set-coord 1 0.025 | /home/walker/polka/polka/Samba/samba
Explain why this picture is exactly the same as the previous, but goes somewhat faster. Would the picture remain the same if the threshhold were changed to 3 or 4? Why or why not?
We now use some of the constructs from the semester to replace the shell-based pipes above with explicit pipes written into the program.
Using fork-5.c or fork-5.c in An Introduction to Concurrency in Unix-based [GNU] C Through Annotated Examples as a base, modify julia-set-coord.c so that it establishes a pipe and spawns a child process. The child process should bind its input to standard in and use some form of exec to call /home/walker/polka/polka/Samba/samba. The parent process can do the current work of julia-set-coord.c to read data from standard input and write to graphics commands to standard output and through the pipe to the child process for display.
Using the socket programs as example, modify julia-set-coord.c further and also modify julia-set.c so that they communicate through a socket rather than through a pipe. Note that in this case the data to be communicated for each point are just two double precision real numbers (8 bytes each) and an integer (4 bytes). Thus, rather than using formatted output, the data may be written and read directly.
When the programs from the previous step are working correctly, run them so that they execute on different processors.
Since julia-set.c is constructed to make computations for a region of (x,y) values, one could have different copies of julia-set run on different processors to compute values in different regions. These could all connect through a socket to julia-set-coord.c to report their results for graphical display.
Extra Credit: Implement the computation of the Julia Set on different processors, with connections made to julia-set-coord.c through one or more pipes.
This document is available on the World Wide Web as
http://www.cs.grinnell.edu/~walker/courses/213.fa00/lab-julia.html