Data update

This commit is contained in:
Ingy döt Net 2026-02-01 16:33:20 -08:00
parent 5150844a7d
commit 4bb20c9b71
7735 changed files with 38060 additions and 199180 deletions

View file

@ -1,123 +1,8 @@
import numba
numba.config.CUDA_ENABLE_PYNVJITLINK = True # prevent cuda ptx version errors
import numpy as np
import matplotlib.pyplot as plt
import cupy as cp
import numba.cuda as cuda
import decimal as dc # decimal floating point arithmetic with arbitrary precision
dc.getcontext().prec = 80 # set precision to 80 digits (about 256 bits)
d, h = 1600, 1000 # pixel density (= image width) and image height
n, r = 100000, 100000.0 # number of iterations and escape radius (r > 2)
a = dc.Decimal("-1.256827152259138864846434197797294538253477389787308085590211144291")
b = dc.Decimal(".37933802890364143684096784819544060002129071484943239316486643285025")
S = np.zeros(n+1, dtype=np.complex128)
u, v = dc.Decimal(0), dc.Decimal(0)
for i in range(n+1):
S[i] = float(u) + float(v) * 1j
if u * u + v * v < r * r:
u, v = u * u - v * v + a, 2 * u * v + b
else:
print("The reference sequence diverges within %s iterations." % i)
break
x = np.linspace(0, 2, num=d+1, dtype=np.float64)
y = np.linspace(0, 2 * h / d, num=h+1, dtype=np.float64)
A, B = np.meshgrid(x - 1, y - h / d)
C = 5.0e-35 * (A + B * 1j)
def iteration_cupy_cuda(S, C):
I = cp.zeros(C.shape, dtype=np.int32)
E, Z, dZ = cp.zeros_like(C), cp.zeros_like(C), cp.zeros_like(C)
iteration = cp.RawKernel("""
#include <cupy/complex.cuh>
extern "C" __global__
void iterate(int dim_x, int dim_y, int n, double r,
complex<double> *S, complex<double> *C,
int *I, complex<double> *E, complex<double> *Z, complex<double> *dZ) {
int x = blockIdx.x * blockDim.x + threadIdx.x;
int y = blockIdx.y * blockDim.y + threadIdx.y;
if (x < dim_x and y < dim_y) { // prevent memory access errors
int x_y = x * dim_y + y; // cupy arrays are in row-major order
complex<double> delta = C[x_y];
int index = I[x_y];
complex<double> epsilon = E[x_y];
complex<double> z = Z[x_y];
complex<double> dz = dZ[x_y];
double abs2_r = r * r;
double abs2_z, abs2_e;
for (int i = 0; i < n; i++) {
abs2_z = z.real() * z.real() + z.imag() * z.imag();
abs2_e = epsilon.real() * epsilon.real() + epsilon.imag() * epsilon.imag();
if (abs2_z < abs2_e) { // rebase when z is closer to zero
epsilon = z; index = 0; // reset reference orbit
}
if (abs2_z < abs2_r) {
epsilon = (2. * S[index] + epsilon) * epsilon + delta; index = index + 1;
dz = 2. * z * dz + 1.; z = S[index] + epsilon;
}
else {
break;
}
}
I[x_y] = index; E[x_y] = epsilon; Z[x_y] = z; dZ[x_y] = dz;
}
}
""", "iterate")
griddim, blockdim = (h // 32 + 1, d // 32 + 1), (32, 32)
iteration(griddim, blockdim, (h+1, d+1, n, r, cp.asarray(S), cp.asarray(C), I, E, Z, dZ))
return I.get(), E.get(), Z.get(), dZ.get()
def iteration_numba_cuda(S, C):
I = cp.zeros(C.shape, dtype=np.int32)
E, Z, dZ = cp.zeros_like(C), cp.zeros_like(C), cp.zeros_like(C)
@cuda.jit()
def iteration(S, C, I, E, Z, dZ):
x, y = cuda.grid(2)
if x < h+1 and y < d+1: # prevent memory access errors
delta, index, epsilon, z, dz = C[x, y], I[x, y], E[x, y], Z[x, y], dZ[x, y]
def abs2(z):
return z.real * z.real + z.imag * z.imag
for i in range(n):
if abs2(z) < abs2(epsilon): # rebase when z is closer to zero
index, epsilon = 0, z # reset reference orbit
if abs2(z) < abs2(r):
index, epsilon = index + 1, (2 * S[index] + epsilon) * epsilon + delta
z, dz = S[index] + epsilon, 2 * z * dz + 1
else:
break
I[x, y], E[x, y], Z[x, y], dZ[x, y] = index, epsilon, z, dz
griddim, blockdim = (h // 32 + 1, d // 32 + 1), (32, 32)
iteration[griddim, blockdim](cp.asarray(S), cp.asarray(C), I, E, Z, dZ)
return I.get(), E.get(), Z.get(), dZ.get()
I, E, Z, dZ = iteration_numba_cuda(S, C) # use iteration_cupy_cuda or iteration_numba_cuda
D = np.zeros(C.shape, dtype=np.float64)
N = abs(Z) > 2 # exterior distance estimation
D[N] = np.log(abs(Z[N])) * abs(Z[N]) / abs(dZ[N])
plt.imshow(D ** 0.15, cmap=plt.cm.jet, origin="lower")
plt.savefig("Mandelbrot_deep_zoom.png", dpi=300)
print(
'\n'.join(
''.join(
' *'[(z:=0, c:=x/50+y/50j, [z:=z*z+c for _ in range(99)], abs(z))[-1]<2]
for x in range(-100,25)
)
for y in range(-50,50)
))

View file

@ -1,8 +1,5 @@
print(
'\n'.join(
''.join(
' *'[(z:=0, c:=x/50+y/50j, [z:=z*z+c for _ in range(99)], abs(z))[-1]<2]
for x in range(-100,25)
)
for y in range(-50,50)
))
from functools import reduce
def mandelbrot(x, y, c): return ' *'[abs(reduce(lambda z, _: z*z + c, range(99), 0)) < 2]
print('\n'.join(''.join(mandelbrot(x, y, x/50 + y/50j) for x in range(-100, 25)) for y in range(-50, 50)))

View file

@ -1,5 +0,0 @@
from functools import reduce
def mandelbrot(x, y, c): return ' *'[abs(reduce(lambda z, _: z*z + c, range(99), 0)) < 2]
print('\n'.join(''.join(mandelbrot(x, y, x/50 + y/50j) for x in range(-100, 25)) for y in range(-50, 50)))

View file

@ -14,14 +14,14 @@ def iteration(C):
S, T = np.zeros(C.shape), np.zeros(C.shape)
Z, dZ = np.zeros_like(C), np.zeros_like(C)
def iterate(C, S, T, Z, dZ):
def iterate1(C, S, T, Z, dZ):
S, T = S + np.exp(- abs(Z)), T + 1
Z, dZ = Z * Z + C, 2 * Z * dZ + 1
return S, T, Z, dZ
for i in range(n):
for i in range(0, n, 1):
M = abs(Z) < r
S[M], T[M], Z[M], dZ[M] = iterate(C[M], S[M], T[M], Z[M], dZ[M])
S[M], T[M], Z[M], dZ[M] = iterate1(C[M], S[M], T[M], Z[M], dZ[M])
return S, T, Z, dZ

View file

@ -17,14 +17,14 @@ def iteration(C):
S, T = np.zeros(C.shape), np.zeros(C.shape)
Z, dZ, ddZ = np.zeros_like(C), np.zeros_like(C), np.zeros_like(C)
def iterate(C, S, T, Z, dZ, ddZ):
def iterate1(C, S, T, Z, dZ, ddZ):
S, T = S + np.sin(density * np.angle(Z)), T + 1
Z, dZ, ddZ = Z * Z + C, 2 * Z * dZ + 1, 2 * (dZ * dZ + Z * ddZ)
return S, T, Z, dZ, ddZ
for i in range(n):
for i in range(0, n, 1):
M = abs(Z) < r
S[M], T[M], Z[M], dZ[M], ddZ[M] = iterate(C[M], S[M], T[M], Z[M], dZ[M], ddZ[M])
S[M], T[M], Z[M], dZ[M], ddZ[M] = iterate1(C[M], S[M], T[M], Z[M], dZ[M], ddZ[M])
return S, T, Z, dZ, ddZ

View file

@ -16,13 +16,17 @@ C = 8.0 * np.exp((A + B * 1j) * 1j) + (a + b * 1j)
def iteration(C):
Z, dZ = np.zeros_like(C), np.zeros_like(C)
def iterate(C, Z, dZ):
def iterate5(C, Z, dZ):
Z, dZ = Z * Z + C, 2 * Z * dZ + 1
Z, dZ = Z * Z + C, 2 * Z * dZ + 1
Z, dZ = Z * Z + C, 2 * Z * dZ + 1
Z, dZ = Z * Z + C, 2 * Z * dZ + 1
Z, dZ = Z * Z + C, 2 * Z * dZ + 1
return Z, dZ
for i in range(n):
for i in range(0, n, 5):
M = abs(Z) < r
Z[M], dZ[M] = iterate(C[M], Z[M], dZ[M])
Z[M], dZ[M] = iterate5(C[M], Z[M], dZ[M])
return Z, dZ

View file

@ -1,4 +1,5 @@
import numba
import numba.cuda as cuda
import numpy as np
import matplotlib.pyplot as plt
@ -12,10 +13,10 @@ n, r = 80000, 100000.0 # number of iterations and escape radius (r > 2)
a = dc.Decimal("-1.256827152259138864846434197797294538253477389787308085590211144291")
b = dc.Decimal(".37933802890364143684096784819544060002129071484943239316486643285025")
S = np.zeros(n+1, dtype=np.complex128)
S = np.zeros(n + 2, dtype=np.complex128) # 2 iterations are chained
u, v = dc.Decimal(0), dc.Decimal(0)
for i in range(n+1):
for i in range(n + 2):
S[i] = float(u) + float(v) * 1j
if u * u + v * v < r * r:
u, v = u * u - v * v + a, 2 * u * v + b
@ -41,25 +42,70 @@ def iteration_numba(S, C):
def abs2(z):
return z.real * z.real + z.imag * z.imag
def iterate(C, I, E, Z, dZ):
I, E = I + 1, (2 * S[I] + E) * E + C
Z, dZ = S[I] + E, 2 * Z * dZ + 1
return I, E, Z, dZ
def iterate2(delta, index, epsilon, z, dz):
index, epsilon = index + 1, (2 * S[index] + epsilon) * epsilon + delta
z, dz = S[index] + epsilon, 2 * z * dz + 1
index, epsilon = index + 1, (2 * S[index] + epsilon) * epsilon + delta
z, dz = S[index] + epsilon, 2 * z * dz + 1
return index, epsilon, z, dz
for i in range(n):
M = abs2(Z) < abs2(E) # rebase when z is closer to zero
I[M], E[M] = 0, Z[M] # reset the reference orbit
M = abs2(Z) < abs2(r)
I[M], E[M], Z[M], dZ[M] = iterate(C[M], I[M], E[M], Z[M], dZ[M])
for k in range(len(C)):
delta, index, epsilon, z, dz = C[k], I[k], E[k], Z[k], dZ[k]
for i in range(0, n, 2):
if abs2(z) < abs2(r):
if abs2(z) < abs2(epsilon):
index, epsilon = 0, z # reset the reference orbit
index, epsilon, z, dz = iterate2(delta, index, epsilon, z, dz)
else:
break
I[k], E[k], Z[k], dZ[k] = index, epsilon, z, dz
return I, E, Z, dZ
for j in numba.prange(d+1):
for j in numba.prange(C.shape[1]):
I[:, j], E[:, j], Z[:, j], dZ[:, j] = iteration(S, C[:, j])
return I, E, Z, dZ
I, E, Z, dZ = iteration_numba(S, C)
def iteration_numba_cuda(S, C):
I = np.zeros(C.shape, dtype=np.intp)
E, Z, dZ = np.zeros_like(C), np.zeros_like(C), np.zeros_like(C)
@cuda.jit()
def iteration(S, C, I, E, Z, dZ):
def abs2(z):
return z.real * z.real + z.imag * z.imag
def iterate2(delta, index, epsilon, z, dz):
index, epsilon = index + 1, (2 * S[index] + epsilon) * epsilon + delta
z, dz = S[index] + epsilon, 2 * z * dz + 1
index, epsilon = index + 1, (2 * S[index] + epsilon) * epsilon + delta
z, dz = S[index] + epsilon, 2 * z * dz + 1
return index, epsilon, z, dz
x, y = cuda.grid(2)
if x < C.shape[0] and y < C.shape[1]:
delta, index, epsilon, z, dz = C[x, y], I[x, y], E[x, y], Z[x, y], dZ[x, y]
for i in range(0, n, 2):
if abs2(z) < abs2(r):
if abs2(z) < abs2(epsilon):
index, epsilon = 0, z # reset the reference orbit
index, epsilon, z, dz = iterate2(delta, index, epsilon, z, dz)
else:
break
I[x, y], E[x, y], Z[x, y], dZ[x, y] = index, epsilon, z, dz
griddim, blockdim = ((C.shape[0] - 1) // 32 + 1, (C.shape[1] - 1) // 32 + 1), (32, 32)
I, E, Z, dZ = cuda.to_device(I), cuda.to_device(E), cuda.to_device(Z), cuda.to_device(dZ)
iteration[griddim, blockdim](cuda.to_device(S), cuda.to_device(C), I, E, Z, dZ)
return I.copy_to_host(), E.copy_to_host(), Z.copy_to_host(), dZ.copy_to_host()
I, E, Z, dZ = iteration_numba(S, C) # use iteration_numba or iteration_numba_cuda
D = np.zeros(C.shape, dtype=np.float64)
N = abs(Z) > 2 # exterior distance estimation

View file

@ -1,25 +1,21 @@
import jax
jax.config.update("jax_enable_x64", True) # faster on GPU P100 than on GPU T4
import numba
import numpy as np
import matplotlib.pyplot as plt
import cupy as cp
import jax.numpy as jnp
import decimal as dc # decimal floating point arithmetic with arbitrary precision
dc.getcontext().prec = 80 # set precision to 80 digits (about 256 bits)
d, h = 100, 2000 # pixel density (= image width) and image height
n, r = 100000, 100000.0 # number of iterations and escape radius (r > 2)
d, h = 1600, 1000 # pixel density (= image width) and image height
n, r = 80000, 100000.0 # number of iterations and escape radius (r > 2)
a = dc.Decimal("-1.256827152259138864846434197797294538253477389787308085590211144291")
b = dc.Decimal(".37933802890364143684096784819544060002129071484943239316486643285025")
S = np.zeros(n+1, dtype=np.complex128)
S = np.zeros(n + 100, dtype=np.complex128) # 100 iterations are chained
u, v = dc.Decimal(0), dc.Decimal(0)
for i in range(n+1):
for i in range(n + 100):
S[i] = float(u) + float(v) * 1j
if u * u + v * v < r * r:
u, v = u * u - v * v + a, 2 * u * v + b
@ -30,56 +26,91 @@ for i in range(n+1):
x = np.linspace(0, 2, num=d+1, dtype=np.float64)
y = np.linspace(0, 2 * h / d, num=h+1, dtype=np.float64)
A, B = np.meshgrid(x * np.pi, y * np.pi)
C = (- 8.0) * np.exp((A + B * 1j) * 1j)
A, B = np.meshgrid(x - 1, y - h / d)
C = 5.0e-35 * (A + B * 1j)
def iteration_cupy(S, C):
@numba.njit(parallel=True, fastmath=True)
def iteration_numba_bla(S, C):
I, J = np.zeros(C.shape, dtype=np.intp), np.zeros(C.shape, dtype=np.complex128)
E, Z, dZ = np.zeros_like(C), np.zeros_like(C), np.zeros_like(C)
def iteration(S, C):
I = cp.zeros(C.shape, dtype=np.intp)
E, Z, dZ = cp.zeros_like(C), cp.zeros_like(C), cp.zeros_like(C)
for i in range(n):
M = cp.absolute(Z) < cp.absolute(E) # rebase when z is closer to zero
I, E = cp.where(M, 0, I), cp.where(M, Z, E) # reset reference orbit
M = cp.absolute(Z) < r
I, E = cp.where(M, I + 1, I), cp.where(M, (2 * S[I] + E) * E + C, E)
Z, dZ = cp.where(M, S[I] + E, Z), cp.where(M, 2 * Z * dZ + 1, dZ)
return I, E, Z, dZ
I, E, Z, dZ = iteration(cp.asarray(S), cp.asarray(C))
return I.get(), E.get(), Z.get(), dZ.get()
def iteration_jax(S, C):
def iteration(S, C):
I = jnp.zeros(C.shape, dtype=np.intp)
E, Z, dZ = jnp.zeros_like(C), jnp.zeros_like(C), jnp.zeros_like(C)
def iteration(S, dS, R, A, B, C):
I, J = np.zeros(C.shape, dtype=np.intp), np.zeros(C.shape, dtype=np.complex128)
E, Z, dZ = np.zeros_like(C), np.zeros_like(C), np.zeros_like(C)
def abs2(z):
return z.real * z.real + z.imag * z.imag
def iterate(i, V):
I, E, Z, dZ = V
M = abs2(Z) < abs2(E) # rebase when z is closer to zero
I, E = jnp.where(M, 0, I), jnp.where(M, Z, E) # reset reference orbit
M = abs2(Z) < abs2(r)
I, E = jnp.where(M, I + 1, I), jnp.where(M, (2 * S[I] + E) * E + C, E)
Z, dZ = jnp.where(M, S[I] + E, Z), jnp.where(M, 2 * Z * dZ + 1, dZ)
return I, E, Z, dZ
def iterate2(delta, index, epsilon, z, dz):
index, epsilon = index + 1, (2 * S[index] + epsilon) * epsilon + delta
z, dz = S[index] + epsilon, 2 * z * dz + 1
index, epsilon = index + 1, (2 * S[index] + epsilon) * epsilon + delta
z, dz = S[index] + epsilon, 2 * z * dz + 1
return index, epsilon, z, dz
I, E, Z, dZ = jax.lax.fori_loop(0, n, iterate, (I, E, Z, dZ), unroll=10)
return I, E, Z, dZ
def skip100(delta, index, e, z, dz):
de = dz - dS[index] # no catastrophic cancellation (don't try that with e)
# for l in range(100): # skip 100 iterations (using linear approximations)
# index, e, de = index + 1, 2 * S[index] * e + delta, 2 * S[index] * de
index, e, de = index + 100, A[index] * e + B[index] * delta, A[index] * de
z, dz = S[index] + e, dS[index] + de
return index, e, z, dz
I, E, Z, dZ = iteration(jnp.asarray(S), jnp.asarray(C))
return np.asarray(I), np.asarray(E), np.asarray(Z), np.asarray(dZ)
for k in range(len(C)):
delta, index, epsilon, z, dz = C[k], I[k], E[k], Z[k], dZ[k]
I, E, Z, dZ = iteration_jax(S, C) # use iteration_cupy or iteration_jax
D = np.zeros(C.shape, dtype=np.float64)
i, j = 0, 0
while i + j < n:
if abs2(z) < abs2(r):
if abs2(epsilon) < abs2(1e-10 * R[index]):
index, epsilon, z, dz = skip100(delta, index, epsilon, z, dz)
j = j + 100
else:
if abs2(z) < abs2(epsilon):
index, epsilon = 0, z # reset the reference orbit
index, epsilon, z, dz = iterate2(delta, index, epsilon, z, dz)
i = i + 2
else:
break
I[k], E[k], Z[k], dZ[k], J[k] = index, epsilon, z, dz, complex(i + j, j)
return I, E, Z, dZ, J
A, B = np.ones(n, dtype=np.complex128), np.zeros(n, dtype=np.complex128)
R, aS = np.full(n, 2, dtype=np.float64), np.where(np.abs(S) < 2, np.abs(S), 0)
dS = np.zeros(n + 100, dtype=np.complex128)
for i in range(1, n + 100): # derivation of the series (accuracy is not required)
dS[i] = 2 * S[i - 1] * dS[i - 1] + 1
for i in numba.prange(n): # coefficients und radii for the bilinear approximation
for l in range(100):
A[i], B[i] = 2 * S[i + l] * A[i], 2 * S[i + l] * B[i] + 1
R[i] = min(R[i], aS[i + l]) # validity radii and skip barriers (zeros)
for i in numba.prange(C.shape[0]):
I[i, :], E[i, :], Z[i, :], dZ[i, :], J[i, :] = iteration(S, dS, R, A, B, C[i, :])
return I, E, Z, dZ, J
I, E, Z, dZ, J = iteration_numba_bla(S, C)
D, T = np.zeros(C.shape, dtype=np.float64), J.real.copy()
skipped = J.imag.sum() / J.real.sum()
print("%.1f%% of all iterations were skipped." % (skipped * 100))
N = abs(Z) > 2 # exterior distance estimation
D[N] = np.log(abs(Z[N])) * abs(Z[N]) / abs(dZ[N])
plt.imshow(D.T ** 0.015, cmap=plt.cm.gist_ncar, origin="lower")
plt.savefig("Mercator_Mandelbrot_deep_map.png", dpi=200)
plt.imshow(D ** 0.15, cmap=plt.cm.turbo, origin="lower")
plt.savefig("Mandelbrot_deep_zoom.png", dpi=200)
N = abs(Z) >= r # normalized iteration count
T[N] = T[N] - np.log2(np.log(abs(Z[N])) / np.log(r))
T = np.minimum(T, n) # truncation
T = (T - T.min()) / (T.max() - T.min()) # scaling
plt.imshow(T ** 0.2, cmap=plt.cm.jet, origin="lower")
plt.savefig("Mandelbrot_deep_time.png", dpi=200)