Created
February 20, 2012 22:26
-
-
Save smukkejohan/1871929 to your computer and use it in GitHub Desktop.
Python port of Paul Bourke's random strange attractor generator using lyapunov exponents
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Python port of Paul Bourke's http://local.wasp.uwa.edu.au/~pbourke/fractals/lyapunov/gen.c | |
# By Johan Bichel Lindegaard - http://johan.cc | |
import math | |
import random | |
from PIL import Image, ImageDraw | |
import argparse | |
import os | |
parser = argparse.ArgumentParser(description='Search for chaos.') | |
#parser.add_argument('-i', dest='maxiterations' metavar='N', type=int, | |
# help='Maximum iterations.') | |
args = parser.parse_args() | |
MAXITERATIONS = 100000 | |
NEXAMPLES = 1000 | |
def createAttractor(): | |
for n in range(NEXAMPLES): | |
lyapunov = 0 | |
xmin= 1e32 | |
xmax=-1e32 | |
ymin= 1e32 | |
ymax=-1e32 | |
ax, ay, x, y = [], [], [], [] | |
# Initialize coefficients for this attractor | |
for i in range(6): | |
ax.append(random.uniform(-2, 2)) | |
ay.append(random.uniform(-2, 2)) | |
# Calculate the attractor | |
drawit = True; | |
x.append(random.uniform(-0.5, 0.5)) | |
y.append(random.uniform(-0.5, 0.5)) | |
d0 = -1 | |
while d0 <= 0: | |
xe = x[0] + random.uniform(-0.5, 0.5) / 1000.0 | |
ye = y[0] + random.uniform(-0.5, 0.5) / 1000.0 | |
dx = x[0] - xe | |
dy = y[0] - ye | |
d0 = math.sqrt(dx * dx + dy * dy) | |
for i in range(MAXITERATIONS): | |
# Calculate next term | |
x.append(ax[0] + ax[1]*x[i-1] + ax[2]*x[i-1]*x[i-1] + ax[3]*x[i-1]*y[i-1] + ax[4]*y[i-1] + ax[5]*y[i-1]*y[i-1]) | |
y.append(ay[0] + ay[1]*x[i-1] + ay[2]*x[i-1]*x[i-1] + ay[3]*x[i-1]*y[i-1] + ay[4]*y[i-1] + ay[5]*y[i-1]*y[i-1]) | |
xenew = ax[0] + ax[1]*xe + ax[2]*xe*xe + ax[3]*xe*ye + ax[4]*ye + ax[5]*ye*ye | |
yenew = ay[0] + ay[1]*xe + ay[2]*xe*xe + ay[3]*xe*ye + ay[4]*ye + ay[5]*ye*ye | |
# Update the bounds | |
xmin = min(xmin,x[i]) | |
ymin = min(ymin,y[i]) | |
xmax = max(xmax,x[i]) | |
ymax = max(ymax,y[i]) | |
# Does the series tend to infinity | |
if xmin < -1e10 or ymin < -1e10 or xmax > 1e10 or ymax > 1e10: | |
drawit = False | |
print "infinite attractor" | |
break | |
# Does the series tend to a point | |
dx = x[i] - x[i-1] | |
dy = y[i] - y[i-1] | |
if abs(dx) < 1e-10 and abs(dy) < 1e-10: | |
drawit = False | |
print "point attractor" | |
break | |
# Calculate the lyapunov exponents | |
if i > 1000: | |
dx = x[i] - xenew | |
dy = y[i] - yenew | |
dd = math.sqrt(dx * dx + dy * dy) | |
lyapunov += math.log(math.fabs(dd / d0)) | |
xe = x[i] + d0 * dx / dd | |
ye = y[i] + d0 * dy / dd | |
# Classify the series according to lyapunov | |
if drawit: | |
if abs(lyapunov) < 10: | |
print "neutrally stable" | |
drawit = False | |
elif lyapunov < 0: | |
print "periodic {} ".format(lyapunov) | |
drawit = False | |
else: | |
print "chaotic {} ".format(lyapunov) | |
# Save the image | |
if drawit: | |
saveAttractor(n,ax,ay,xmin,xmax,ymin,ymax,x,y) | |
def saveAttractor(n,a,b,xmin,xmax,ymin,ymax,x,y): | |
width, height = 500, 500 | |
if not os.path.exists("output"): | |
os.makedirs("output") | |
# Save the parameters | |
with open("output/{}.txt".format(n), "w") as f: | |
f.write("{} {} {} {}\n".format(xmin, ymin, xmax, ymax)) | |
for i in range(6): | |
f.write("{} {}\n".format(a[i], b[i])) | |
f.close() | |
# Save the image | |
image = Image.new("RGBA", (width, height)) | |
draw = ImageDraw.Draw(image) | |
for i in range(MAXITERATIONS): | |
ix = width * (x[i] - xmin) / (xmax - xmin) | |
iy = height * (y[i] - ymin) / (ymax - ymin) | |
if i > 100: | |
draw.point([ix, iy], fill="black") | |
image.save("output/{}.png".format(n), "PNG") | |
print "saved attractor to ./output/{}.png".format(n) | |
createAttractor() |
Yes, awesome. Works as written in a Jupyter notebook in Python 2
mode if you delete lines 10 through 14 involving arguments from command line that don't seem to get used.
Go spin up a live, interactive Jupyter notebook of it in your browser here and see it in action, thanks to mybinder.org. Specifically, select the converted gist example
listed in the index to launch a version of this.
Get the list of file names in output by entering cd output
in one cell followed by ls
in the next cell, To see a resulting image you enter as a markdown cell ![image](output/170.png)
where you replace '170.png' with your image file name.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Awesome Gist - had to change all the '{}'.format() style commands in my version of python to get it to run.