Mandelbrot Microscope

Discussion in 'Python' started by Nick J Chackowsky, Jul 9, 2004.

1. Nick J ChackowskyGuest

Okay, here's my naive solution to coding a Mandelbrot microscope in
Python... but it's so slow! Would anyone care to comment on how to speed
this up?

Nick.

#
#This uses the graphics.py module included with John Zelle's
#textbook. File available at http://mcsp.wartburg.edu/zelle/python/
#

from graphics import *

DEPTH = 200

def f(z):
C = z
count = 0
while count < DEPTH and abs(z) < 3.0:
z = z*z + C
count += 1
if count == DEPTH:
count -= 1
return count

def rangecolors(n):
""" Return a list of nicely blended colors """
clist = []
r, g, b = 13, 101, 137
for c in range(n):
clist.append(color_rgb(r,g,b))
r += 5
g += 7
b += 11
if r > 255: r = 0
if g > 255: g = 0
if b > 255: b = 0
#end for
return clist

def main():
llx, lly = -3.0, 3.0 #lower left
urx, ury = 3.0, -3.0 #upper right
WINDOWSZ = 200
clist = rangecolors(DEPTH)
keepgoing = True
while keepgoing:
w = GraphWin("Mandelbrot", WINDOWSZ, WINDOWSZ)
w.setCoords(llx, lly, urx, ury)
delta = abs(llx-urx)/float(WINDOWSZ)
re = llx
while re < urx:
im = ury
while im < lly:
w.plot(re, im, clist[f(complex(re, im))])
im += delta
#end while
re += delta
#end while
p1 = w.getMouse()
p2 = w.getMouse()

#for now, assume that the user input a square area correctly
llx = p1.getX()
lly = p1.getY()
urx = p2.getX()
ury = p2.getY()

w.close()
#end while

main()

Nick J Chackowsky, Jul 9, 2004

2. Jeff EplerGuest

IMO you won't render mandelbrots at an acceptable speed using Python.

Psyco might help, but probably not (it's unlikely to support complex
numbers)

You might speed things up a bit by doing something clever in numarray,
effectively doing an iteration of the whole window worth of points in
each loop, avoiding lots of interpreter overhead.

But to get it competitive with a native code mandelbrot calculator,
well, you'll probably have to write something that compiles to native
code.

You could also wait a few years, computers should be faster by then...

Jeff

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.4 (GNU/Linux)

iD8DBQFA7eKHJd01MZaTXX0RArlUAKCBh1t3nyHXkfyvO7NVSD2pWVqTjACeKskd
48lvGJhqQIMTT4S8I7elAqQ=
=cWy3
-----END PGP SIGNATURE-----

Jeff Epler, Jul 9, 2004

3. Jeff EplerGuest

Here's a program which calculates a similar result to yours using
numarray. It calculates a 50x50 grid to 500 iterations in 3 seconds on
a 750MHz machine. I didn't run yours so I don't know how this compares.

Hm, timeit.py says that your function runs in 2 ms per point with DEPTH=500
given the point 0+0j, and of course runs faster on points that escape.
Your program may run faster than the numarray implementation, or it
might be a wash. (2ms * 50 * 50 == 5 seconds)

This isn't a natural application for numarray.

from numarray import *

DEPTH = 500

def coords(x0, y0, x1, y1, w, h):
rx = arange(w).astype(Float32) * (x1-x0) / (w-1) + x0
ry = arange(w).astype(Float32) * (y1-y0) / (h-1) + y0

def f(z):
C = z.copy()
d = zeros(z.shape)

for c in range(DEPTH):
z = z*z - C
zz = abs(z) > 3
#print zz; print
d += where(zz, 1, 0)
if not (c % 8): # keep contents of z from overflowing
z = where(zz, 10, z)
return d

# 20x20 prints nicely on the screen in numbers
c = coords(-1.5, -1.5, 1.5, 1.5, 20, 20)
print f(c)/10

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.4 (GNU/Linux)

iD4DBQFA7emfJd01MZaTXX0RAhxLAJddbaghpLsaap8jSaPot8kYq9OOAJ9UqAcb
kSCcEb0lb3ZacK36KYOAGw==
=ZFA5
-----END PGP SIGNATURE-----

Jeff Epler, Jul 9, 2004