Escape-time arrays and vectorised masks
1.1 Question 1 (20 marks)
For c in the rectangle -2 <= Re(c) <= 1, -1.5 <= Im(c) <= 1.5, start z0=0 and iterate z <-
z^2+c. The escape time is the first iteration number r for which |z|>=2; use max_iterations
when no escape occurs.
1
(a) Build 300-point coordinate arrays, meshgrid arrays, a complex parameter array c, and a zero
complex array z0 of matching shape. [4]
(b) Create an escape-time array initially -1 and use a Boolean mask to record points already
satisfying |z0|>=2 at time 0. [3]
(c) Perform one vectorised iteration only on unresolved positions and record newly escaped points
at time 1. [3]
(d) Write escape_time(c,z0,max_iterations) using Boolean masks. The function must not
modify the caller’s z0. [7]
(e) Evaluate it for the grid above with max_iterations=80 and display log(times+1) with
correct extent and origin. [3]
[ ]: # YOUR CODE HERE
raise NotImplementedError()
Worked solution and marking guidance
1.2 Question 1 (20 marks)
For c in the rectangle -2 <= Re(c) <= 1, -1.5 <= Im(c) <= 1.5, start z0=0 and iterate z <-
z^2+c. The escape time is the first iteration number r for which |z|>=2; use max_iterations
when no escape occurs.
1
(a) Build 300-point coordinate arrays, meshgrid arrays, a complex parameter array c, and a zero
complex array z0 of matching shape. [4]
(b) Create an escape-time array initially -1 and use a Boolean mask to record points already
satisfying |z0|>=2 at time 0. [3]
(c) Perform one vectorised iteration only on unresolved positions and record newly escaped points
at time 1. [3]
(d) Write escape_time(c,z0,max_iterations) using Boolean masks. The function must not
modify the caller’s z0. [7]
(e) Evaluate it for the grid above with max_iterations=80 and display log(times+1) with
correct extent and origin. [3]
[2]: xr = np.linspace(-2,1,300)
yr = np.linspace(-1.5,1.5,300)
x,y = np.meshgrid(xr,yr)
c = x + 1j*y
z0 = np.zeros_like(c, dtype =complex)
et = -np.ones(c.shape, dtype =int)
et[(et==-1) & (np.abs(z0)>=2)] = 0
mask = et==-1
z0[mask] = z0[mask]**2 + c[mask]
et[(et==-1) & (np.abs(z0)>=2)] = 1
def escape_time(c,z0,max_iterations):
z = np.array(z0, dtype =complex, copy =True)
result = -np.ones(c.shape, dtype =int)
for r in range(max_iterations+1):
2
newly = (result==-1) & (np.abs(z)>=2)
result[newly] = r
unresolved = result==-1
if r == max_iterations or not np.any(unresolved):
break
z[unresolved] = z[unresolved]**2 + c[unresolved]
result[result==-1] = max_iterations
return result
times = escape_time(c, np .zeros_like(c), 80)
plt.figure()
plt.imshow(np.log(times+1), origin ='lower', extent =[-2,1,-1.5,1.5],␣
↪aspect='auto')
plt.show()
assert times.shape == c.shape and times.min() >= 0 and times.max() <= 80
Marking points / verification. 4 marks for correctly shaped coordinates, c and z0; 3 for initial
state/mask; 3 for one selective update; 7 for a complete non-mutating function with correct time
labels and stopping; 3 for the final logged image. The reference implementation explicitly copies
z0.
3