In [88]:
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as ani
import IPython.display as disp
import time

Christmas Specital (Tut09), Motivationsübung (nicht Prüfungsrelevant)

Using Numpy and Matplotlib (Ex: Mandelbrod Set)

Jochen Illerhaus

Def: Mandelbrot Set

Let $z_n \in \mathbb{C}$ be the series recursively defined by: $$z_{n+1} = z_n^2 + c$$ with $z_0 = 0$.


The Mandelbrot Set $M$ is defined by: $$M := \big\{ c \in \mathbb{C} \big| \; \; z_n \text{convergent} \big\}$$

Lemma: Mandelbrot convergence Test

(Proof omitted) $$\exists n \in \mathbb{N}: \big| z_n \big| > 2 \Rightarrow z_n \text{divergent}$$

In [2]:
X = np.linspace(-2, 1, 842)
Y = np.linspace(-1.3, 1.3, 624)

x, y = np.meshgrid(X, Y)
c = x + 1j*y

z = np.zeros_like(c)

for n in range(50):
    z = z**2 + c
    
M = np.abs(z) <= 2
M = M.astype(np.float32)
/usr/local/lib/python3.5/dist-packages/ipykernel_launcher.py:10: RuntimeWarning: overflow encountered in square
  # Remove the CWD from sys.path while we load stuff.
/usr/local/lib/python3.5/dist-packages/ipykernel_launcher.py:10: RuntimeWarning: invalid value encountered in square
  # Remove the CWD from sys.path while we load stuff.
/usr/local/lib/python3.5/dist-packages/ipykernel_launcher.py:12: RuntimeWarning: invalid value encountered in less_equal
  if sys.path[0] == '':
In [3]:
plt.figure(figsize=(15, 7))
plt.imshow(M)
plt.colorbar()
Out[3]:
<matplotlib.colorbar.Colorbar at 0x7f0d08bfd710>

Improving visual appeal

Idea: Encode info about speed of convergence in color

Def: Convergence Number $N$ $$ N = \Big\{ n \in \mathbb{N} \; \Big| \; \big| z_n \big| > 2 \wedge \big| z_{n-1} \big| \le 2 \Big\}$$

In [5]:
max_iterations = 50

extent = [-2, 0.8, -1.3, 1.3]

X = np.linspace(*extent[:2], 842)
Y = np.linspace(*extent[2:], 624)

x, y = np.meshgrid(X, Y)
c = x + 1j*y

z = np.zeros_like(c)
z_next = np.zeros_like(c)

N = max_iterations*np.ones_like(c).astype(np.int32)

abs2 = lambda z: np.real(z)**2 + np.imag(z)**2

for n in range(max_iterations):
    z_next = z**2 + c
    N[np.logical_and(abs2(z_next)>4, abs2(z)<=4)] = n
    z = z_next
/usr/local/lib/python3.5/dist-packages/ipykernel_launcher.py:14: ComplexWarning: Casting complex values to real discards the imaginary part
  
/usr/local/lib/python3.5/dist-packages/ipykernel_launcher.py:16: RuntimeWarning: overflow encountered in square
  app.launch_new_instance()
/usr/local/lib/python3.5/dist-packages/ipykernel_launcher.py:16: RuntimeWarning: overflow encountered in add
  app.launch_new_instance()
/usr/local/lib/python3.5/dist-packages/ipykernel_launcher.py:19: RuntimeWarning: overflow encountered in square
/usr/local/lib/python3.5/dist-packages/ipykernel_launcher.py:19: RuntimeWarning: invalid value encountered in square
/usr/local/lib/python3.5/dist-packages/ipykernel_launcher.py:20: RuntimeWarning: invalid value encountered in greater
/usr/local/lib/python3.5/dist-packages/ipykernel_launcher.py:20: RuntimeWarning: invalid value encountered in less_equal
In [6]:
plt.figure(figsize=(15, 7))
plt.imshow(N, cmap=plt.cm.brg, extent=extent)
plt.colorbar()
Out[6]:
<matplotlib.colorbar.Colorbar at 0x7f0d0bc4d080>

Putting it all together

In [114]:
def mandelbrot_img(iterations=50, extent=[-2, 0.8, -1.3, 1.3], res=(480, 320)):
    with np.warnings.catch_warnings():
        np.warnings.filterwarnings('ignore')
        X = np.linspace(*extent[:2], res[0])
        Y = np.linspace(*extent[2:], res[1])

        x, y = np.meshgrid(X, Y)
        c = x + 1j*y

        z = np.zeros_like(c)
        z_next = np.zeros_like(c)

        N = iterations*np.ones_like(c).astype(np.int32)

        abs2 = lambda z: np.real(z)**2 + np.imag(z)**2
        
        abs_old = np.zeros_like(c)
        abs_next = np.zeros_like(c)

        for n in range(iterations):
            z_next = z**2 + c
            abs_next = abs2(z_next)
            N[np.logical_and(abs_next>4, abs_old<=4)] = n
            z = z_next
            abs_old = abs_next
            
        return np.log(N)/np.log(2)
    
def mandelbrot(ax=None, iterations=50, extent=[-2, 0.8, -1.3, 1.3], img=None, res=(480, 320)):
        data = mandelbrot_img(iterations, extent, res)
        
        if ax is not None:
            img = ax.imshow(data, cmap=plt.cm.hot, extent=extent)
        else:
            img.set_data(data)
            img.set_extent(extent)
        return img
In [87]:
t_start = time.time()
a, b = 3, 3
I = a*b
fig, axs = plt.subplots(a, b, figsize=(15, 15))
c0 = -0.7499-0.0303j
s = 1.5
t_img = time.time()
for l in range(a):
    for m in range(b):
        mandelbrot(axs[l][m], 1000, [c0.real-s, c0.real+s, c0.imag-s, c0.imag+s])
        axs[l][m].set_title("scale = %3.3e" % s)
        axs[l][m].ticklabel_format(style='sci', axis='x', scilimits=(0,0))
        axs[l][m].ticklabel_format(style='sci', axis='y', scilimits=(0,0))
        t = time.time()
        print("plotet img s = %3.3e, total itime: %3.3fs" % (s, t - t_img))
        t_img = t
        s *= 0.3
print("Done ploting toatal time: %3.3fs" % (time.time() - t_start))
plotet img s = 1.500e+00, total itime: 5.363s
plotet img s = 4.500e-01, total itime: 5.389s
plotet img s = 1.350e-01, total itime: 5.543s
plotet img s = 4.050e-02, total itime: 5.282s
plotet img s = 1.215e-02, total itime: 5.109s
plotet img s = 3.645e-03, total itime: 5.339s
plotet img s = 1.093e-03, total itime: 5.478s
plotet img s = 3.280e-04, total itime: 5.359s
plotet img s = 9.841e-05, total itime: 5.319s
Done ploting toatal time: 49.351s
In [141]:
t_start = time.time()
fig, ax = plt.subplots(1, 1, figsize=(10, 10))
c0 = -0.7499-0.0303j
s = 1.5
img = mandelbrot(ax, 3000, [c0.real-s, c0.real+s, c0.imag-s, c0.imag+s], None)
ax.set_title("scale = %3.3e" % s)
ax.ticklabel_format(style='sci', axis='x', scilimits=(0,0))
ax.ticklabel_format(style='sci', axis='y', scilimits=(0,0))

def animate(i):
    global s, c0, img, ax
    img = mandelbrot(None, 9000, [c0.real-s, c0.real+s, c0.imag-s, c0.imag+s], img, res=(320, 320))
    ax.set_title("scale = %3.3e" % s)
    s *= 0.84
    print("rendered frame number = %s" % i)
    return img, ax

animation = ani.FuncAnimation(fig, animate, np.linspace(0, 240, 240), interval=1, blit=False)

animation.save('m-ani.mp4', fps=30, extra_args=['-vcodec', 'libx264'])

print("Done ploting toatal time: %3.3fs" % (time.time() - t_start))

plt.close()
rendered frame number = 0.0
rendered frame number = 0.0
rendered frame number = 1.00418410042
rendered frame number = 2.00836820084
rendered frame number = 3.01255230126
rendered frame number = 4.01673640167
rendered frame number = 5.02092050209
rendered frame number = 6.02510460251
rendered frame number = 7.02928870293
rendered frame number = 8.03347280335
rendered frame number = 9.03765690377
rendered frame number = 10.0418410042
rendered frame number = 11.0460251046
rendered frame number = 12.050209205
rendered frame number = 13.0543933054
rendered frame number = 14.0585774059
rendered frame number = 15.0627615063
rendered frame number = 16.0669456067
rendered frame number = 17.0711297071
rendered frame number = 18.0753138075
rendered frame number = 19.0794979079
rendered frame number = 20.0836820084
rendered frame number = 21.0878661088
rendered frame number = 22.0920502092
rendered frame number = 23.0962343096
rendered frame number = 24.10041841
rendered frame number = 25.1046025105
rendered frame number = 26.1087866109
rendered frame number = 27.1129707113
rendered frame number = 28.1171548117
rendered frame number = 29.1213389121
rendered frame number = 30.1255230126
rendered frame number = 31.129707113
rendered frame number = 32.1338912134
rendered frame number = 33.1380753138
rendered frame number = 34.1422594142
rendered frame number = 35.1464435146
rendered frame number = 36.1506276151
rendered frame number = 37.1548117155
rendered frame number = 38.1589958159
rendered frame number = 39.1631799163
rendered frame number = 40.1673640167
rendered frame number = 41.1715481172
rendered frame number = 42.1757322176
rendered frame number = 43.179916318
rendered frame number = 44.1841004184
rendered frame number = 45.1882845188
rendered frame number = 46.1924686192
rendered frame number = 47.1966527197
rendered frame number = 48.2008368201
rendered frame number = 49.2050209205
rendered frame number = 50.2092050209
rendered frame number = 51.2133891213
rendered frame number = 52.2175732218
rendered frame number = 53.2217573222
rendered frame number = 54.2259414226
rendered frame number = 55.230125523
rendered frame number = 56.2343096234
rendered frame number = 57.2384937238
rendered frame number = 58.2426778243
rendered frame number = 59.2468619247
rendered frame number = 60.2510460251
rendered frame number = 61.2552301255
rendered frame number = 62.2594142259
rendered frame number = 63.2635983264
rendered frame number = 64.2677824268
rendered frame number = 65.2719665272
rendered frame number = 66.2761506276
rendered frame number = 67.280334728
rendered frame number = 68.2845188285
rendered frame number = 69.2887029289
rendered frame number = 70.2928870293
rendered frame number = 71.2970711297
rendered frame number = 72.3012552301
rendered frame number = 73.3054393305
rendered frame number = 74.309623431
rendered frame number = 75.3138075314
rendered frame number = 76.3179916318
rendered frame number = 77.3221757322
rendered frame number = 78.3263598326
rendered frame number = 79.3305439331
rendered frame number = 80.3347280335
rendered frame number = 81.3389121339
rendered frame number = 82.3430962343
rendered frame number = 83.3472803347
rendered frame number = 84.3514644351
rendered frame number = 85.3556485356
rendered frame number = 86.359832636
rendered frame number = 87.3640167364
rendered frame number = 88.3682008368
rendered frame number = 89.3723849372
rendered frame number = 90.3765690377
rendered frame number = 91.3807531381
rendered frame number = 92.3849372385
rendered frame number = 93.3891213389
rendered frame number = 94.3933054393
rendered frame number = 95.3974895397
rendered frame number = 96.4016736402
rendered frame number = 97.4058577406
rendered frame number = 98.410041841
rendered frame number = 99.4142259414
rendered frame number = 100.418410042
rendered frame number = 101.422594142
rendered frame number = 102.426778243
rendered frame number = 103.430962343
rendered frame number = 104.435146444
rendered frame number = 105.439330544
rendered frame number = 106.443514644
rendered frame number = 107.447698745
rendered frame number = 108.451882845
rendered frame number = 109.456066946
rendered frame number = 110.460251046
rendered frame number = 111.464435146
rendered frame number = 112.468619247
rendered frame number = 113.472803347
rendered frame number = 114.476987448
rendered frame number = 115.481171548
rendered frame number = 116.485355649
rendered frame number = 117.489539749
rendered frame number = 118.493723849
rendered frame number = 119.49790795
rendered frame number = 120.50209205
rendered frame number = 121.506276151
rendered frame number = 122.510460251
rendered frame number = 123.514644351
rendered frame number = 124.518828452
rendered frame number = 125.523012552
rendered frame number = 126.527196653
rendered frame number = 127.531380753
rendered frame number = 128.535564854
rendered frame number = 129.539748954
rendered frame number = 130.543933054
rendered frame number = 131.548117155
rendered frame number = 132.552301255
rendered frame number = 133.556485356
rendered frame number = 134.560669456
rendered frame number = 135.564853556
rendered frame number = 136.569037657
rendered frame number = 137.573221757
rendered frame number = 138.577405858
rendered frame number = 139.581589958
rendered frame number = 140.585774059
rendered frame number = 141.589958159
rendered frame number = 142.594142259
rendered frame number = 143.59832636
rendered frame number = 144.60251046
rendered frame number = 145.606694561
rendered frame number = 146.610878661
rendered frame number = 147.615062762
rendered frame number = 148.619246862
rendered frame number = 149.623430962
rendered frame number = 150.627615063
rendered frame number = 151.631799163
rendered frame number = 152.635983264
rendered frame number = 153.640167364
rendered frame number = 154.644351464
rendered frame number = 155.648535565
rendered frame number = 156.652719665
rendered frame number = 157.656903766
rendered frame number = 158.661087866
rendered frame number = 159.665271967
rendered frame number = 160.669456067
rendered frame number = 161.673640167
rendered frame number = 162.677824268
rendered frame number = 163.682008368
rendered frame number = 164.686192469
rendered frame number = 165.690376569
rendered frame number = 166.694560669
rendered frame number = 167.69874477
rendered frame number = 168.70292887
rendered frame number = 169.707112971
rendered frame number = 170.711297071
rendered frame number = 171.715481172
rendered frame number = 172.719665272
rendered frame number = 173.723849372
rendered frame number = 174.728033473
rendered frame number = 175.732217573
rendered frame number = 176.736401674
rendered frame number = 177.740585774
rendered frame number = 178.744769874
rendered frame number = 179.748953975
rendered frame number = 180.753138075
rendered frame number = 181.757322176
rendered frame number = 182.761506276
rendered frame number = 183.765690377
rendered frame number = 184.769874477
rendered frame number = 185.774058577
rendered frame number = 186.778242678
rendered frame number = 187.782426778
rendered frame number = 188.786610879
rendered frame number = 189.790794979
rendered frame number = 190.794979079
rendered frame number = 191.79916318
rendered frame number = 192.80334728
rendered frame number = 193.807531381
rendered frame number = 194.811715481
rendered frame number = 195.815899582
rendered frame number = 196.820083682
rendered frame number = 197.824267782
rendered frame number = 198.828451883
rendered frame number = 199.832635983
rendered frame number = 200.836820084
rendered frame number = 201.841004184
rendered frame number = 202.845188285
rendered frame number = 203.849372385
rendered frame number = 204.853556485
rendered frame number = 205.857740586
rendered frame number = 206.861924686
rendered frame number = 207.866108787
rendered frame number = 208.870292887
rendered frame number = 209.874476987
rendered frame number = 210.878661088
rendered frame number = 211.882845188
rendered frame number = 212.887029289
rendered frame number = 213.891213389
rendered frame number = 214.89539749
rendered frame number = 215.89958159
rendered frame number = 216.90376569
/usr/local/lib/python3.5/dist-packages/matplotlib/axes/_base.py:2917: UserWarning: Attempting to set identical left==right results
in singular transformations; automatically expanding.
left=-0.7499, right=-0.7499
  'left=%s, right=%s') % (left, right))
rendered frame number = 217.907949791
/usr/local/lib/python3.5/dist-packages/matplotlib/axes/_base.py:2917: UserWarning: Attempting to set identical left==right results
in singular transformations; automatically expanding.
left=-0.7499, right=-0.7499
  'left=%s, right=%s') % (left, right))
rendered frame number = 218.912133891
/usr/local/lib/python3.5/dist-packages/matplotlib/axes/_base.py:2917: UserWarning: Attempting to set identical left==right results
in singular transformations; automatically expanding.
left=-0.7499, right=-0.7499
  'left=%s, right=%s') % (left, right))
rendered frame number = 219.916317992
/usr/local/lib/python3.5/dist-packages/matplotlib/axes/_base.py:2917: UserWarning: Attempting to set identical left==right results
in singular transformations; automatically expanding.
left=-0.7499, right=-0.7499
  'left=%s, right=%s') % (left, right))
rendered frame number = 220.920502092
/usr/local/lib/python3.5/dist-packages/matplotlib/axes/_base.py:2917: UserWarning: Attempting to set identical left==right results
in singular transformations; automatically expanding.
left=-0.7499, right=-0.7499
  'left=%s, right=%s') % (left, right))
rendered frame number = 221.924686192
/usr/local/lib/python3.5/dist-packages/matplotlib/axes/_base.py:2917: UserWarning: Attempting to set identical left==right results
in singular transformations; automatically expanding.
left=-0.7499, right=-0.7499
  'left=%s, right=%s') % (left, right))
rendered frame number = 222.928870293
/usr/local/lib/python3.5/dist-packages/matplotlib/axes/_base.py:2917: UserWarning: Attempting to set identical left==right results
in singular transformations; automatically expanding.
left=-0.7499, right=-0.7499
  'left=%s, right=%s') % (left, right))
rendered frame number = 223.933054393
/usr/local/lib/python3.5/dist-packages/matplotlib/axes/_base.py:2917: UserWarning: Attempting to set identical left==right results
in singular transformations; automatically expanding.
left=-0.7499, right=-0.7499
  'left=%s, right=%s') % (left, right))
rendered frame number = 224.937238494
/usr/local/lib/python3.5/dist-packages/matplotlib/axes/_base.py:2917: UserWarning: Attempting to set identical left==right results
in singular transformations; automatically expanding.
left=-0.7499, right=-0.7499
  'left=%s, right=%s') % (left, right))
rendered frame number = 225.941422594
/usr/local/lib/python3.5/dist-packages/matplotlib/axes/_base.py:2917: UserWarning: Attempting to set identical left==right results
in singular transformations; automatically expanding.
left=-0.7499, right=-0.7499
  'left=%s, right=%s') % (left, right))
rendered frame number = 226.945606695
/usr/local/lib/python3.5/dist-packages/matplotlib/axes/_base.py:2917: UserWarning: Attempting to set identical left==right results
in singular transformations; automatically expanding.
left=-0.7499, right=-0.7499
  'left=%s, right=%s') % (left, right))
rendered frame number = 227.949790795
/usr/local/lib/python3.5/dist-packages/matplotlib/axes/_base.py:2917: UserWarning: Attempting to set identical left==right results
in singular transformations; automatically expanding.
left=-0.7499, right=-0.7499
  'left=%s, right=%s') % (left, right))
rendered frame number = 228.953974895
/usr/local/lib/python3.5/dist-packages/matplotlib/axes/_base.py:2917: UserWarning: Attempting to set identical left==right results
in singular transformations; automatically expanding.
left=-0.7499, right=-0.7499
  'left=%s, right=%s') % (left, right))
rendered frame number = 229.958158996
/usr/local/lib/python3.5/dist-packages/matplotlib/axes/_base.py:2917: UserWarning: Attempting to set identical left==right results
in singular transformations; automatically expanding.
left=-0.7499, right=-0.7499
  'left=%s, right=%s') % (left, right))
rendered frame number = 230.962343096
/usr/local/lib/python3.5/dist-packages/matplotlib/axes/_base.py:2917: UserWarning: Attempting to set identical left==right results
in singular transformations; automatically expanding.
left=-0.7499, right=-0.7499
  'left=%s, right=%s') % (left, right))
rendered frame number = 231.966527197
/usr/local/lib/python3.5/dist-packages/matplotlib/axes/_base.py:2917: UserWarning: Attempting to set identical left==right results
in singular transformations; automatically expanding.
left=-0.7499, right=-0.7499
  'left=%s, right=%s') % (left, right))
rendered frame number = 232.970711297
/usr/local/lib/python3.5/dist-packages/matplotlib/axes/_base.py:2917: UserWarning: Attempting to set identical left==right results
in singular transformations; automatically expanding.
left=-0.7499, right=-0.7499
  'left=%s, right=%s') % (left, right))
rendered frame number = 233.974895397
/usr/local/lib/python3.5/dist-packages/matplotlib/axes/_base.py:2917: UserWarning: Attempting to set identical left==right results
in singular transformations; automatically expanding.
left=-0.7499, right=-0.7499
  'left=%s, right=%s') % (left, right))
rendered frame number = 234.979079498
/usr/local/lib/python3.5/dist-packages/matplotlib/axes/_base.py:2917: UserWarning: Attempting to set identical left==right results
in singular transformations; automatically expanding.
left=-0.7499, right=-0.7499
  'left=%s, right=%s') % (left, right))
rendered frame number = 235.983263598
/usr/local/lib/python3.5/dist-packages/matplotlib/axes/_base.py:2917: UserWarning: Attempting to set identical left==right results
in singular transformations; automatically expanding.
left=-0.7499, right=-0.7499
  'left=%s, right=%s') % (left, right))
/usr/local/lib/python3.5/dist-packages/matplotlib/axes/_base.py:3193: UserWarning: Attempting to set identical bottom==top results
in singular transformations; automatically expanding.
bottom=-0.0303, top=-0.0303
  'bottom=%s, top=%s') % (bottom, top))
rendered frame number = 236.987447699
/usr/local/lib/python3.5/dist-packages/matplotlib/axes/_base.py:2917: UserWarning: Attempting to set identical left==right results
in singular transformations; automatically expanding.
left=-0.7499, right=-0.7499
  'left=%s, right=%s') % (left, right))
/usr/local/lib/python3.5/dist-packages/matplotlib/axes/_base.py:3193: UserWarning: Attempting to set identical bottom==top results
in singular transformations; automatically expanding.
bottom=-0.0303, top=-0.0303
  'bottom=%s, top=%s') % (bottom, top))
rendered frame number = 237.991631799
/usr/local/lib/python3.5/dist-packages/matplotlib/axes/_base.py:2917: UserWarning: Attempting to set identical left==right results
in singular transformations; automatically expanding.
left=-0.7499, right=-0.7499
  'left=%s, right=%s') % (left, right))
/usr/local/lib/python3.5/dist-packages/matplotlib/axes/_base.py:3193: UserWarning: Attempting to set identical bottom==top results
in singular transformations; automatically expanding.
bottom=-0.0303, top=-0.0303
  'bottom=%s, top=%s') % (bottom, top))
rendered frame number = 238.9958159
/usr/local/lib/python3.5/dist-packages/matplotlib/axes/_base.py:2917: UserWarning: Attempting to set identical left==right results
in singular transformations; automatically expanding.
left=-0.7499, right=-0.7499
  'left=%s, right=%s') % (left, right))
/usr/local/lib/python3.5/dist-packages/matplotlib/axes/_base.py:3193: UserWarning: Attempting to set identical bottom==top results
in singular transformations; automatically expanding.
bottom=-0.0303, top=-0.0303
  'bottom=%s, top=%s') % (bottom, top))
rendered frame number = 240.0
Done ploting toatal time: 6540.023s
In [143]:
%%html
<h3>9000 iterations, -0.7499-0.0303j, s*=0.84</h3>
<video width="640" height="480" controls>
  <source src="m-ani.mp4" type="video/mp4">
</video>
<h3>Older verison 3866.420s, 3000 iterations, -0.7499-0.0303j, s*=0.86</h3>
<video width="640" height="480" controls>
  <source src="m-ani-3000.mp4" type="video/mp4">
</video>

9000 iterations, -0.7499-0.0303j, s*=0.84

Older verison 3866.420s, 3000 iterations, -0.7499-0.0303j, s*=0.86

How to improve this??

  • This is a demo for matplotlib and numpy (not a good demo on rendering Mandelbrot Sets)
  • Use difrent libraries, (TensorFlow, DataShader, ...)
  • Implement multi-threading yourself see here
  • USE THE GPU (e.g. through GLSL and vispy or PyOpenGL)
In [135]:
# Render Nice title Img
fig, ax = plt.subplots(1, 1, figsize=(18, 7))
mandelbrot(ax, 3000, [-0.74995, -0.74985, -0.030225, -0.030375], res=(820, 640))
ax.set_aspect(9/16)
fig.savefig("mandel.png")
In [ ]: