Mandelbrot set
The Mandelbrot set, the points c whose orbit of z -> z^2 + c from 0 stays bounded (Dilão 2023 §5.5).
- Name:
mandelbrot - Field:
mathematics/fractals - Formalism: discrete map
- References: Dilão 2023
Factory
@register_model(
field='mathematics/fractals',
tags=('fractal', 'complex', 'map', 'lattice', 'escape time'),
references=('Dilão 2023',),
defaults={'until': 100, 'dt': 1},
)
def mandelbrot(
shape: Sequence[int] = (240, 300),
*,
window: Window = MANDELBROT_WINDOW,
radius: float = 2.0,
) -> System:
"""Return the Mandelbrot set, the points `c` whose orbit of `z -> z^2 + c` from 0 stays bounded (Dilão 2023 §5.5).
Every site holds one point `c` of the window and iterates the map, and `n` counts the steps the orbit stays within
the escape radius. The points of the set never escape, so their count grows with every step, while the count of an
escaped point stops at its escape time and colors the bands around the set.
Args:
shape:
The number of sites along the imaginary and the real axis.
window:
The smallest and largest real part, then the smallest and largest imaginary part, of the points.
radius:
The escape radius, 2 for the quadratic map.
Returns:
system:
The model, with the field `plane`, its orbits `plane.z`, and its escape times `plane.n`.
Examples:
>>> from skcomplex.models.mathematics.fractals import mandelbrot
>>> mandelbrot((3, 3), window=(-1, 1, -1, 1)).elements[0].values['c'][1].tolist()
[(-1+0j), 0j, (1+0j)]
"""
points = _plane(shape, window)
return _escape_field(shape, np.zeros_like(points), points, radius)
Components
class QuadraticMapElement(Element):
"""Complex quadratic map `z' = z^2 + c` while `|z|` stays within the escape radius, with a count of its steps.
Args:
label:
The label of the element.
visual:
How the element is drawn, or `None` for `default_visual_`.
z:
The initial point.
n:
The initial number of steps within the escape radius.
c:
The constant of the map.
radius:
The escape radius.
Examples:
>>> from skcomplex.models.mathematics.fractals import QuadraticMapElement
>>> QuadraticMapElement('point', c=0.25).c
0.25
"""
def __init__(
self,
label: str,
visual: VisualSpec | None = None,
*,
z: Annotated[object, StateSpec(domain=CC)] = 0.0,
n: Annotated[object, StateSpec(domain=NN)] = 0.0,
c: Annotated[object, ParameterSpec(domain=CC)] = 0.0,
radius: Annotated[object, ParameterSpec(domain=RealSet.open(0, oo))] = 2.0,
) -> None:
super().__init__(label, visual=visual)
self.z = z
self.n = n
self.c = c
self.radius = radius
def _get_laws(self, s: SimpleNamespace) -> list[BaseLaw]:
"""Return the next point, which stays once it escaped, and the next count.
Args:
s:
The namespace of the symbols the laws can use.
Returns:
laws:
The laws of the element.
"""
return [
UpdateLaw(s.z, where(abs(s.z) <= s.radius, s.z**2 + s.c, s.z)),
UpdateLaw(s.n, s.n + where(abs(s.z) <= s.radius, 1, 0)),
]
Equations
plane.z[i].real: z_plane_re(t + 1) == where(sqrt(z_plane_im(t)^2 + z_plane_re(t)^2) <= radius_plane, -z_plane_im(t)^2 + z_plane_re(t)^2 + c_plane_re, z_plane_re(t))
plane.z[i].imag: z_plane_im(t + 1) == where(sqrt(z_plane_im(t)^2 + z_plane_re(t)^2) <= radius_plane, 2*z_plane_im(t)*z_plane_re(t) + c_plane_im, z_plane_im(t))
plane.n[i]: n_plane(t + 1) == n_plane(t) + where(sqrt(z_plane_im(t)^2 + z_plane_re(t)^2) <= radius_plane, 1, 0)