Introduction to julia set
In the context of complex dynamics, a topic of mathematics, the Julia set and the Fatou set are two complementary sets (Julia ‘laces’ and Fatou ‘dusts’) defined from a function. Informally, the Fatou set of the function consists of values with the property that all nearby values behave similarly under repeated iteration of the function, and the Julia set consists of values such that an arbitrarily small perturbation can cause drastic changes in the sequence of iterated function values. Thus the behavior of the function on the Fatou set is ‘regular’, while on the Julia set its behavior is ‘chaotic’. The Julia set of a function f is commonly denoted J(f), and the Fatou set is denoted F(f). These sets are named after the French mathematicians Gaston Julia and Pierre Fatou whose work began the study of complex dynamics during the early 20th century. [Source Wiki] The equation to generate Julia fractal is f_c(z) = z^2 + c
where c is a complex parameter. The Julia set for this system is the subset of the complex plane given by:
J(f_{c})=\left \{ z\in \mathbb{ C}:\forall n\in\mathbb{N},|f_{c}^{n}(z)|\leq 2 \right \}
So let’s now try to create one of the fractals in the above image. To do so we need the Pillow
module of Python which makes it easy to deal with images and stuff. To install pillow through pip type the following command in the command prompt.
pip install Pillow
Now using this library to create the fractal image.
Python3
# Python code for Julia Fractal
from PIL import Image
# driver function
if __name__ == "__main__":
# setting the width, height and zoom
# of the image to be created
w, h, zoom = 1920,1080,1
# creating the new image in RGB mode
bitmap = Image.new("RGB", (w, h), "white")
# Allocating the storage for the image and
# loading the pixel data.
pix = bitmap.load()
# setting up the variables according to
# the equation to create the fractal
cX, cY = -0.7, 0.27015
moveX, moveY = 0.0, 0.0
maxIter = 255
for x in range(w):
for y in range(h):
zx = 1.5*(x - w/2)/(0.5*zoom*w) + moveX
zy = 1.0*(y - h/2)/(0.5*zoom*h) + moveY
i = maxIter
while zx*zx + zy*zy < 4 and i > 1:
tmp = zx*zx - zy*zy + cX
zy,zx = 2.0*zx*zy + cY, tmp
i -= 1
# convert byte to RGB (3 bytes), kinda
# magic to get nice colors
pix[x,y] = (i << 21) + (i << 10) + i*8
# to display the created fractal
bitmap.show()
Creating the fractal using Matplotlib
Now let us create an instance of the fractal using matplotlib library (if you are not familiar, it is a mathematical plotting library for python. You may refer to https://matplotlib.org/). Using this library allows to create an image which is interactive (can be zoomed, panned, etc.) and has labels displaying information about it (like titles, axes-labels, etc.). Here is the code with the comments containing the detailed description of the steps –
import matplotlib.pyplot as plt
# ADJUSTABLE VARIABLES/PARAMETERS
center = -0.05 - 0.66j
range_ = 0.9 + abs(center)
max_iterations = 100
x1, y1 = -1.5, -1 # plotting julia-set in the rectangle with opposite corners (x1,y1) and (x2,y2)
x2, y2 = 1.5, 1
linearResolution = 500
# PERFORMING CALCULATIONS TO GET THE 2D-ARRAY REPRESENTING JULIA SET
M,N = int((y2-y1)*linearResolution), int((x2-x1)*linearResolution)
xcoordinates = [(x1 + ((x2-x1)/N)*i) for i in range(N)] # x-coordinates of the sample-points
ycoordinates = [(y1 + ((y2-y1)/M)*i) for i in range(M)] # y-coordinates of the sample-points
# juliaSet is a 2D Array representing values at the sample points of the julia-set.
# Initially all the values are set to None.
# Later, if the sample-point belongs to the julia set, set the corresponding location in
# juliaSet array to 0. If it does not, set it to the number of iterations it too to cross range_ .
juliaSet = [[None for i in xcoordinates] for j in ycoordinates]
for y in range(len(ycoordinates)):
for x in range(len(xcoordinates)):
z = complex(xcoordinates[x],ycoordinates[y])
iteration = 0
while(abs(z) < range_ and iteration < max_iterations):
iteration += 1
z = z**2 + center
if(iteration == max_iterations): juliaSet[y][x] = 0 # the corresponding sample point belongs to the julia set
else: juliaSet[y][x] = iteration # the corresponding sample point doesn't belong to the julia set
# PLOTTING THE DATA
ax = plt.axes()
ax.set_aspect('equal') # directing matplotlib to keep the scale of x and y axes same.
plot = ax.pcolormesh(xcoordinates, ycoordinates, juliaSet, cmap = 'magma') # creating the heatmap of julia-set array
plt.colorbar(plot) # adding scale of the colors
plt.title('Julia-set \ncenter = {}, range = {:.3f}, max-iterations = {}'.format(center, range_, max_iterations)) # setting title
plt.show()
Output :
You can try changing the adjustable parameters to get different instances of Julia-sets. Remember, greater the value of max_iterations, greater the accuracy of the image because the ideal value of max_iterations is infinity! But it comes at the price of computation time. Below are the asymptotic complexities of the above program –
Asymptotic time complexity: O(M x N x max_iterations)
Asymptotic space complexity: O(M x N),where M is the number of divisions of x-coordinate and N is the number of divisions of y-coordinate.