Category Archives: Fractals

Hyperbolic numbers and fractals

What happens when we change the identity defining the imaginary unit for the complex numbers i^2=-1 to i^2=1 and enforcing different from 1?  We get the somewhat less known numbers called hyperbolic or split-complex numbers. The most common thing about complex numbers that comes to mind are the nice pictures of fractals and there are plenty of tools already out there to generate them. However, for the hyperbolic numbers there aren't as much fractal generators out there, so let us now make a simple tool in Python to generate these fractals as well.

First, we need to implement the basic arithmetic with hyperbolic numbers. This will enable us to write simple arithmetic expressions like z**2 + c directly instead of writing out the expressions in component form using real and imaginary parts. I made a simple Python implementation of class HyperbolicNumber in module hyperbolic doing exactly this. Using this module, we can now write a function for counting the number of iterations of f(z)=z**2+c before "escaping to infinity" in a straightforward way. In fact the formula is identical to the escape-time algorithm for fractals over complex numbers.

from hyperbolic import HyperbolicNumber

def f(z):
    w = z
    total = 0
    while mag(w) < 2.0 and total < 300:
        w = w**2 + z
        total += 1
    return total

Basically, the above function is all we need to start generating images of fractals. Here of course, we focus only on the famous map family { f_c(z) = z**2+c}  indexed by c, but any other could be used as well. We add an additional parameter to f(z)  to become f(z,c) and encapsulate it in the HyperbolicFractal class. This class will be our abstraction for a fractal calculation, which basically just calculates a 2**-k approximation of the Julia set of f_c over some given rectangular region in the hyperbolic number plane. For all the points z of a given rectangular range [centerx-rangex, centerx+rangex]x[centery-rangey, centery+rangey] we perform the call to f(z, c). To avoid recalculation every time of the same region, or part of a region, points which have already been calculated are stored in an internal cache so any subsequent calls to f(z, c) are just looked up instead of being recalculated. Here's the code.

import numpy
import matplotlib.cm as cm
import matplotlib.pylab as plt
from hyperbolic import HyperbolicNumber

class HyperbolicFractal:
    def __init__(self):
        self.hits = 0
        self.cache = {}
        
    def f(self, z, c):
        if (z, c) in self.cache:
            self.hits += 1
            return self.cache[(z, c)]
        w = z
        total = 0
        while mag(w) < 2.0 and total < 300:
            w = w**2 + c
            total += 1
        self.cache[(z, c)] = total
        return self.cache[(z, c)]

    def show(self, scale, centerx, centery, rangex, rangey, c):
        """Draw a 2**(-scale) approximation of a hyperbolic fractal 
        over a given rectangular range."""
        self.hits = 0
        totals = (centerx+rangex - (centerx-rangex))\
        * (centery+rangey - (centery-rangey))
        t = [[self.f((1.0/(2**scale)) * HyperbolicNumber(x,y), c)\
        for x in range(centerx-rangex, centerx+rangex)]\
        for y in range(centery-rangey, centery+rangey)]
        matrix = numpy.matrix(t)
        fig = plt.figure()
        plt.imshow(matrix, interpolation='bilinear', cmap=cm.RdYlGn, origin='center')
        print ("Total: {} Cache hits: {} Cache hit ratio: {}".format(totals, self.hits, (1.0 * self.hits)/totals))
        plt.show()

Let's try it out.

H = HyperbolicFractal()
H.show(7, 0, 0, 200, 200, HyperbolicNumber(-20/2**3, 0))

And there you have it! We now have a tool to look into the hyperbolic number world.

The full code for this implementation can be found on Github.

 

Copyright © 2017, Konrad Burnik