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. How to Program, Part VI
  7. exercises
  8. pyMPI tutorial
  9. Calculating PI, Part I
  10. Calculating PI, Part II
  11. Calculating PI, Part III
  12. Dividing Work
  13. More MPI
  14. Poogle - Web Search
  15. Mandelbrot Sets
  16. Mandelbrot, The Code
  17. Mandelbrot, The Images
  18. Mandelbrot In CUDA
  19. Conway's Life, Part I
  20. Life Code Listing
  21. Conway's Life, Part II
  22. 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:
    alternate.py
    1# use different values of c for fun patterns!
    2def 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

When you're done creating your artwork, you can upload it to http://stevenrbrandt.com/mandelbrot

.
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 jobs_completed_by_worker consist of a single
7#  image next_row_to_assign. It needs at least two MPI nodes to work.
8#
9####################################################
10 
11import mpi
12import bmp
13 
14# maximal number of iterations to compute a pixel
15MAX_ITER = 256
16 
17# pixel computation function
18def 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
27nx = 1024
28ny = 1024
29 
30jobs_completed_by_worker = []
31for i in range(mpi.size):
32    jobs_completed_by_worker.append(0)
33 
34if mpi.rank == 0:
35    # "master" node:
36 
37    workers_running = 0
38    next_row_to_assign  = 0
39 
40    # initialize list of image rows
41    image = []
42    for i in range(ny):
43        image.append(-1)
44 
45    # get all workers started on tasks
46    for n in range(1, mpi.size):
47        mpi.send(next_row_to_assign, n)
48        next_row_to_assign += 1
49        workers_running += 1
50 
51    # master's main loop:
52    while workers_running > 0:
53        # receive computed result from any worker
54        result, status = mpi.recv(mpi.ANY_SOURCE)
55        worker_id = status.source
56        row_completed, row_data = result
57 
58        jobs_completed_by_worker[worker_id] += 1
59 
60        # incorporate newly computed next_row_to_assign into image data
61        image[row_completed] = row_data
62 
63        if next_row_to_assign < ny:
64            # send new work unit to the (now) idle worker
65            mpi.send(next_row_to_assign, worker_id) 
66            next_row_to_assign += 1
67        else:
68            # use -1 as the row number to signal all done
69            mpi.send(-1, worker_id)
70            workers_running -= 1
71 
72    # convert data to color image and save it in a file
73    bmp.write_image('image.bmp', nx, ny, image, MAX_ITER)
74    for w in range(1,mpi.size):
75        print jobs_completed_by_worker[w],"tasks completed by worker",w
76 
77else:
78    # "worker" node:
79    while 1:
80        # receive work unit info
81        row, status = mpi.recv(mpi.ANY_SOURCE)
82        # check if we're still needed
83        if row == -1:
84            break
85        # compute row of image
86        rdata = []
87 
88        # Magic here... 4.0j is a complex number
89        c = 4.0j*row/ny-2.0j
90        for x in range(nx):
91            rdata += [pixel(c+4.0*x/nx-2.0)]
92        # send the result to master
93        mpi.send([row, rdata], 0)