Source code for neurokit2.complexity.utils_fractal_mandelbrot

# -*- coding: utf-8 -*-
import matplotlib.pyplot as plt
import numpy as np


[docs] def fractal_mandelbrot( size=1000, real_range=(-2, 2), imaginary_range=(-2, 2), threshold=4, iterations=25, buddha=False, show=False, ): """**Mandelbrot (or a Buddhabrot) Fractal** Vectorized function to efficiently generate an array containing values corresponding to a Mandelbrot fractal. Parameters ----------- size : int The size in pixels (corresponding to the width of the figure). real_range : tuple The mandelbrot set is defined within the -2, 2 complex space (the real being the x-axis and the imaginary the y-axis). Adjusting these ranges can be used to pan, zoom and crop the figure. imaginary_range : tuple The mandelbrot set is defined within the -2, 2 complex space (the real being the x-axis and the imaginary the y-axis). Adjusting these ranges can be used to pan, zoom and crop the figure. iterations : int Number of iterations. threshold : int The threshold used, increasing it will increase the sharpness (not used for buddhabrots). buddha : bool Whether to return a buddhabrot. show : bool Visualize the fractal. Returns ------- ndarray Array of values. Examples --------- Create the Mandelbrot fractal .. ipython:: python import neurokit2 as nk @savefig p_fractal_mandelbrot1.png scale=100% m = nk.fractal_mandelbrot(show=True) @suppress plt.close() Zoom at the Seahorse Valley .. ipython:: python @savefig p_fractal_mandelbrot2.png scale=100% m = nk.fractal_mandelbrot(real_range=(-0.76, -0.74), imaginary_range=(0.09, 0.11), iterations=100, show=True) @suppress plt.close() Draw manually .. ipython:: python import matplotlib.pyplot as plt m = nk.fractal_mandelbrot(real_range=(-2, 0.75), imaginary_range=(-1.25, 1.25)) @savefig p_fractal_mandelbrot3.png scale=100% plt.imshow(m.T, cmap="viridis") plt.axis("off") @suppress plt.close() Generate a Buddhabrot fractal .. ipython:: python b = nk.fractal_mandelbrot(size=1500, real_range=(-2, 0.75), imaginary_range=(-1.25, 1.25), buddha=True, iterations=200) @savefig p_fractal_mandelbrot4.png scale=100% plt.imshow(b.T, cmap="gray") plt.axis("off") @suppress plt.close() Mixed MandelBuddha .. ipython:: python m = nk.fractal_mandelbrot() b = nk.fractal_mandelbrot(buddha=True, iterations=200) mixed = m - b @savefig p_fractal_mandelbrot5.png scale=100% plt.imshow(mixed.T, cmap="gray") plt.axis("off") @suppress plt.close() """ if buddha is False: img = _mandelbrot( size=size, real_range=real_range, imaginary_range=imaginary_range, threshold=threshold, iterations=iterations, ) else: img = _buddhabrot( size=size, real_range=real_range, imaginary_range=imaginary_range, iterations=iterations ) if show is True: plt.imshow(img, cmap="rainbow") plt.axis("off") return img
# ============================================================================= # Internals # ============================================================================= def _mandelbrot(size=1000, real_range=(-2, 2), imaginary_range=(-2, 2), iterations=25, threshold=4): img, c = _mandelbrot_initialize( size=size, real_range=real_range, imaginary_range=imaginary_range ) optim = _mandelbrot_optimize(c) z = np.copy(c) for i in range(1, iterations + 1): # pylint: disable=W0612 # Continue only where smaller than threshold mask = (z * z.conjugate()).real < threshold mask = np.logical_and(mask, optim) if np.all(~mask) is True: break # Increase img[mask] += 1 # Iterate based on Mandelbrot equation z[mask] = z[mask] ** 2 + c[mask] # Fill otpimized area img[~optim] = np.max(img) return img def _mandelbrot_initialize(size=1000, real_range=(-2, 2), imaginary_range=(-2, 2)): # Image space width = size height = _mandelbrot_width2height(width, real_range, imaginary_range) img = np.full((height, width), 0) # Complex space real = np.array([np.linspace(*real_range, width)] * height) imaginary = np.array([np.linspace(*imaginary_range, height)] * width).T c = 1j * imaginary c += real return img, c # ============================================================================= # Buddhabrot # ============================================================================= def _buddhabrot(size=1000, iterations=100, real_range=(-2, 2), imaginary_range=(-2, 2)): # Find original width and height (postdoc enforcing so that is has the same size than mandelbrot) width = size height = _mandelbrot_width2height(width, real_range, imaginary_range) # Inflate size to match -2, 2 x = np.array((np.array(real_range) + 2) / 4 * size, int) size = int(size * (size / (x[1] - x[0]))) img = np.zeros([size, size], int) c = _buddhabrot_initialize( size=img.size, iterations=iterations, real_range=real_range, imaginary_range=imaginary_range ) # use these c-points as the initial 'z' points. z = np.copy(c) while len(z) > 0: # translate z points into image coordinates x = np.array((z.real + 2) / 4 * size, int) y = np.array((z.imag + 2) / 4 * size, int) # add value to all occupied pixels img[y, x] += 1 # apply mandelbrot dynamic z = z ** 2 + c # shed the points that have escaped mask = np.abs(z) < 2 c = c[mask] z = z[mask] # Crop parts not asked for xrange = np.array((np.array(real_range) + 2) / 4 * size).astype(int) yrange = np.array((np.array(imaginary_range) + 2) / 4 * size).astype(int) img = img[yrange[0] : yrange[0] + height, xrange[0] : xrange[0] + width] return img def _buddhabrot_initialize(size=1000, iterations=100, real_range=(-2, 2), imaginary_range=(-2, 2)): # Allocate an array to store our non-mset points as we find them. sets = np.zeros(size, dtype=np.complex128) sets_found = 0 # create an array of random complex numbers (our 'c' points) c = np.random.uniform(*real_range, size) + (np.random.uniform(*imaginary_range, size) * 1j) c = c[_mandelbrot_optimize(c)] z = np.copy(c) for i in range(iterations): # pylint: disable=W0612 # apply mandelbrot dynamic z = z ** 2 + c # collect the c points that have escaped mask = np.abs(z) < 2 new_sets = c[~mask] sets[sets_found : sets_found + len(new_sets)] = new_sets sets_found += len(new_sets) # then shed those points from our test set before continuing. c = c[mask] z = z[mask] # return only the points that are not in the mset return sets[:sets_found] # ============================================================================= # Utils # ============================================================================= def _mandelbrot_optimize(c): # Optimizations: most of the mset points lie within the # within the cardioid or in the period-2 bulb. (The two most # prominent shapes in the mandelbrot set. We can eliminate these # from our search straight away and save alot of time. # see: http://en.wikipedia.org/wiki/Mandelbrot_set#Optimizations # First eliminate points within the cardioid p = (((c.real - 0.25) ** 2) + (c.imag ** 2)) ** 0.5 mask1 = c.real > p - (2 * p ** 2) + 0.25 # Next eliminate points within the period-2 bulb mask2 = ((c.real + 1) ** 2) + (c.imag ** 2) > 0.0625 # Combine masks mask = np.logical_and(mask1, mask2) return mask def _mandelbrot_width2height(size=1000, real_range=(-2, 2), imaginary_range=(-2, 2)): return int( np.rint((imaginary_range[1] - imaginary_range[0]) / (real_range[1] - real_range[0]) * size) )