# mathr / blog / #

## Lyapunov atom domains for negative multibrot sets

Back at the end of April last year I think I was futzing about on math.stackexchange.com answering a question about rendering negative multibrot sets, for example one produced by iterations of $$z \to z^{-2} + c$$. I tried applying the atom domain colouring from the regular Mandelbrot set, but found it looked better if I accumulated all the partials with additive blending, not just the final domain. Here's a zoomed in view:

I implemented it as a GLSL fragment shader in Fragmentarium, here's the source code (which you can download too):

// Mandelbrot set for $$z \to z^{-n} + c$$ coloured by Lyapunov atom domains
// Created: Thu Apr 30 15:10:00 2015
#include "Progressive2D.frag"
#include "Complex.frag"

const float pi = 3.141592653589793;
const float phi = (sqrt(5.0) + 1.0) / 2.0;

#group Lyapunov atom domains

uniform int Iterations; slider[10,200,5000]
uniform int Power; slider[-16,-2,16]

vec3 color(vec2 c) {
// critical point is $$0$$ for positive Power, and $$0^Power + c = c$$
// critical point is $$\infty$$ for negative Power, and $$\infty^Power + c = c$$
// so start iterating from $$c$$
vec2 z = c;
// Lyapunov exponent accumulator
float le = 0.0;
// atom domain accumulator
float minle = 0.0;
int mini = 1;
// accumulated colour
vec4 rgba = vec4(0.0);

for (int i = 0; i < Iterations; ++i) {
// $$zn1 \gets z^{Power - 1}$$
vec2 zn1 = vec2(1.0, 0.0);
for (int j = 0; j < abs(Power - 1); ++j) {
zn1 = cMul(zn1, z);
}
if (Power < 0) {
zn1 = cInverse(zn1);
}
// $$dz \gets Power z^{Power - 1}$$
vec2 dz = float(Power) * zn1;
// $$z \gets z^{Power} + c$$
z = cMul(zn1,z) + c;
// $$le \gets le + 2 log |dz|$$
float dle = log(dot(dz, dz));
le += dle;
// if the delta is smaller than any previous, accumulate the atom domain domain
if (dle < minle) {
minle = dle;
mini = i + 1;
float hue = 2.0 * pi / (36.0 + 1.0/(phi*phi)) * float(mini);
vec3 rainbow = 2.0 * pi / 3.0 * vec3(0.0, 1.0, 2.0);
vec3 domain = clamp(vec3(0.5) + 0.5 * sin(vec3(hue) + rainbow), 0.0, 1.0);
rgba += vec4(domain, 1.0);
}
}

// accumulated 'iterations' logs of squared magnitudes
// so divide by 2 iterations
le /= 2.0 * float(Iterations);
// scale accumulated colour and blacken interior
return mix(rgba.rgb / rgba.a, vec3(le < 0.001 ? 0.0 : tanh(exp(le))), 0.5);
}

More negative powers than -2 don't look very good, though.