1. How to Program, Part I
  2. How to Program, Part II
  3. How to Program, Part III
  4. How to Program, Part IV
  5. How to Program, Part V
  6. exercises
  7. pyMPI tutorial
  8. Calculating PI, Part I
  9. Calculating PI, Part II
  10. Calculating PI, Part III
  11. Poogle - Web Search
  12. Mandelbrot Sets
  13. Mandelbrot, The Code
  14. Mandelbrot, The Images
  15. Conway's Life, Part I
  16. Life Code Listing
  17. Conway's Life, Part II
  18. MPI Life Code Listing

Mandelbrot, The Code

The Code: Please try changing the function on line 21 to discover new and interesting patterns. Things to try:

  1. z = z**3+c
  2. z = z**4+c
  3. z = z**3-z**2+c
  4. You can also consider the Julia sets, which use a slightly different function:
    file: alternate.py
    1 # use different values of c for fun patterns!
    2 def pixel(z):
    3     #c = .285
    4     #c = -.125+.75j
    5     #c = 0.45+0.1428j
    6     c = -0.835-0.2321j
    7     for i in range(MAX_ITER):
    8         z = z**2+c
    9         if abs(z) >= 2.0:
    10             return i
    11     return MAX_ITER
     

The Mandelbrot program needs to generate a bitmap image file. We've chosen to hide the details of this process in a file -- as an explanation goes beyond the scope of this course. However, we provide you a link to download bmp.py so that you can run this program on your own:

download: bmp.py

file: mandelbrot_mw.py
1 ####################################################
2 #  Calculate Mandelbrot set and save it as a bmp image
3 #
4 #  Parallel version uses master-worker approach,
5 #  with master node at rank zero; the work units
6 #  distributed to workers consist of a single
7 #  image row. It needs at least two MPI nodes to work.
8 #
9 ####################################################
10 
11 import mpi
12 import bmp
13 
14 # maximal number of iterations to compute a pixel
15 MAX_ITER = 256
16 
17 # pixel computation function
18 def pixel(c):
19     z = 0
20     for i in range(MAX_ITER):
21         z = z*z+c
22         if abs(z) >= 2.0:
23             return i
24     return MAX_ITER
25 
26 # image dimensions
27 nx = 1024
28 ny = 1024
29 
30 workers = []
31 for i in range(mpi.size):
32     workers.append(0)
33 
34 if mpi.rank == 0:
35     # "master" node:
36 
37     # number of jobs being processed and the next image row to compute
38     jobs = 0
39     row  = 0
40 
41     # initialize list of image rows
42     image = []
43     for i in range(ny):
44         image.append(-1)
45 
46     # assign initial work units to all workers
47     for n in range(1, mpi.size):
48         mpi.send(row, n)
49         row += 1
50         jobs += 1
51         
52     # master's main loop:
53     while jobs > 0:
54         # receive computed result from any worker
55         result, status = mpi.recv()
56 
57         workers[status.source] += 1
58 
59         # incorporate newly computed row into image data
60         image[result[0]] = result[1]
61 
62         if row < ny:
63             # send new work unit to the (now) idle worker
64             mpi.send(row, status.source)
65             row += 1
66         else:
67             # no jobs remain: dismiss the worker
68             mpi.send(-1, status.source)
69             jobs -= 1
70 
71     # convert data to color image and save it in a file
72     bmp.write_image('image.bmp', nx, ny, image, MAX_ITER)
73     for w in range(1,mpi.size):
74         print workers[w],"tasks completed by worker",w
75     
76 else:
77     # "worker" node:
78     while 1:
79         # receive work unit info
80         row, status = mpi.recv()
81         # check if we're still needed
82         if row == -1:
83             break
84         # compute row of image
85         rdata = []
86 
87         # Magic here... 4.0j is a complex number
88         c = 4.0j*row/ny-2.0j
89         for x in range(nx):
90             rdata += [pixel(c+4.0*x/nx-2.0)]
91         # send the result to master
92         mpi.send([row, rdata], 0)