Strange Attractor¶

Visualizations of the Lorenz System¶

This is just a showcase of some cool matplotlib features, and also a visual treat 😎

We will, in various fashions, visualize a system of three ordinary differential equations that define the so-called Lorenz attractor:

equations

Our figures will be defined by various parameters:

  • Equation parameters σ, ρ, β
  • Number of iterations through 'time'
  • Observer vantage (pitch, roll and yaw of graph)
  • Line properties (color, weight, translucency)

Attributions: The Lorenz function code is derived from the Matplotlib project website. The temporal animation code is entirely based on work by Ruben Esteche.

Table of Contents:¶

(Click blue links to navigate)

Part 1: Nested Plots

Part 2: Spatial Animation

Part 3: Temporal Animation


Appendix A: Offline code for spatial animation

Appendix B: Offline code for temporal animation

"From the beginning of chaos research until today, the unpredictability of chaos has been a central theme. It is widely believed and claimed by philosophers, mathematicians and physicists alike that chaos has a new implication for unpredictability, meaning that chaotic systems are unpredictable in a way that other deterministic systems are not. [...] However, this is not the case. [...] I will propose that the sought-after new implication of chaos for unpredictability is the following: for predicting any event, all sufficiently past events are approximately probabilistically irrelevant. [...] Chaotic behaviour is multi-faceted and takes various forms. Yet if the aim is to identify a general new implication of chaos for unpredictability, I think this is the best we can get."

Charlotte Werndl: What Are the New Implications of Chaos for Unpredictability? Brit. J. Phil. Sci. 60 (2009), 195–220)

Part 1: Nested Plots¶

Displayed below are 12 sets of respectively 3 solutions each.

Parameters are randomized for:

  • yaw (rotation),
  • equations parameters σ, ρ, β.

For non-mathematicians, this may facilitate developing an intuition for the interplay of the equations' parameters, and for the fundamental insight into the proximity of order and chaos.

In [64]:
# Import libraries
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from IPython.display import display, HTML
display(HTML("<style>div.output_scroll { height: 999 }</style>"))

# Figure properties
fig = plt.figure(figsize=(10,120))
print("Lorenz Attractors (sigma, rho, beta )")
print("[ 3 partial solutions per subplot ]")

# Iterators and counters
plot_index = 0

for outer in range(12):
    plot_index = plot_index + 1
    
    # Projection parameters
    is_pitch = 20
    is_roll = 0
    is_yaw = np.random.uniform(0, 90)
    
    # Subplot properties
    ax = fig.add_subplot(12, 1, plot_index, projection='3d')
    ax.view_init(elev=is_pitch, azim=is_yaw, roll=is_roll)
    ax.set(xticklabels=[]) 
    ax.set(yticklabels=[])
    ax.set(zticklabels=[])
    ax.set_xlabel("X Axis", fontsize=7)
    ax.set_ylabel("Y Axis", fontsize=7)
    
    s_param = np.empty(3)
    r_param = np.empty(3)
    b_param = np.empty(3)
    
    subplot_label = ("[" + (str(outer)) + "]: " + "Azimuth " + (str(int(is_yaw)) +"° ("))
    color_list = ['blue', 'red', 'green']
    series_label = ["", "", ""]
    
    for inner in range (3):
        s_param[inner]= np.random.uniform(5, 25)
        r_param[inner] = np.random.uniform(5, 25)
        b_param[inner] = np.random.uniform(0.1, 5)   
        
        # Lorenz function
        def lorenz(xyz, *, s=s_param[inner], r=r_param[inner], b=b_param[inner]):
            x, y, z = xyz
            x_dot = s*(y - x)
            y_dot = r*x - y - x*z
            z_dot = x*y - b*z
            return np.array([x_dot, y_dot, z_dot])

        dt = 0.01
        num_steps = 3000

        xyzs = np.empty((num_steps + 1, 3))  # Need one more for the initial values
        xyzs[0] = (0., 1., 1.05)  # Set initial values
        # Step through "time", calculating the partial derivatives at the current point
        # and using them to estimate the next point
        for i in range(num_steps):
            xyzs[i + 1] = xyzs[i] + lorenz(xyzs[i]) * dt

        # Plot partial solution
        ax.plot(*xyzs.T, lw=0.5, color=color_list[inner])
        # Append parameters for labeling
        series_label[inner] = (color_list[inner] + ": " + str(int(s_param[inner])) + ", " + 
                               str(int(r_param[inner])) + ", " + str(round(b_param[inner],1)) + " | ")    
    
        subplot_label = subplot_label + series_label[inner]
    
    subplot_label = subplot_label[:-2] + ")"
    ax.set_title(label=subplot_label, fontsize=10, color='DarkGrey')
    
    #ax.plot(*xyzs.T, lw=0.5, color=(np.random.uniform(0, 0.7), np.random.uniform(0, 0.7), np.random.uniform(0, 0.7)))
    
plt.show()
Lorenz Attractors (sigma, rho, beta )
[ 3 partial solutions per subplot ]