Skip to content

Commit 787c055

Browse files
authored
Merge pull request #4 from DuncDennis/feature/get_ready_for_v002
Feature/get ready for v002
2 parents 07bc781 + 64e40fa commit 787c055

17 files changed

+288
-61
lines changed

CHANGELOG.md

+9
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@
22

33
<!--next-version-placeholder-->
44

5+
### v0.0.2 (21.10.2023)
6+
- Added more Systems.
7+
- Added new Lyapunov spectrum measure.
8+
- Added new simulation backend.
9+
- Added example folder.
10+
- Added static folder to generate animation in readme.
11+
- Changed plotly dependency to matplotlib.
12+
- More smaller improvements...
13+
514
### v0.0.1 (16.03.2023)
615
- First release of `lorenzpy`
716

README.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ $ pip install lorenzpy
2222
```
2323

2424
To install with the additional plotting functionality.
25-
This also installs `plotly` and `matplotlib`. ⚠️ Plotting functionality not in a useful
26-
state.
25+
This also installs `matplotlib`. ⚠️ Plotting functionality not in a useful state.
2726
```bash
2827
$ pip install lorenzpy[plot]
2928
```
@@ -59,6 +58,8 @@ lle = lpy.measures.largest_lyapunov_exponent(
5958
The calculated largest Lyapunov exponent of *0.9051...* is very close to the literature
6059
value of *0.9056*[^SprottChaos].
6160

61+
For more examples see the [examples folder](examples/README.md).
62+
6263
## 💫 Supported systems
6364

6465

docs/reference.md

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
### simulations module:
55
::: lorenzpy.simulations
6+
::: lorenzpy.simulations.solvers
67

78
### measures module:
89
::: lorenzpy.measures

examples/README.md

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Examples:
2+
3+
### Contents
4+
- ``double_pendulum_lyapunov_spectrum.py``: Plot the lyapunov spectrum of the double pendulum.
5+
- ``lyapunov_spectra_of_3d_autonomous_flows.py``: Plot the Lyapunov spectrum of all 3D autonomous dissipative flow systems.
6+
7+
⚠️ Not many examples here yet, and examples might be flawed.
8+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
"""Lyapunov Spectrum of single system."""
2+
3+
import matplotlib.pyplot as plt
4+
import numpy as np
5+
6+
from lorenzpy import measures as meas
7+
from lorenzpy import simulations as sims
8+
9+
sys_obj = sims.DoublePendulum(dt=0.1)
10+
dt = sys_obj.dt
11+
12+
# Calculate exponents:
13+
m = 4
14+
deviation_scale = 1e-10
15+
steps = 1000
16+
part_time_steps = 15
17+
steps_skip = 0
18+
19+
iterator_func = sys_obj.iterate
20+
starting_point = sys_obj.get_default_starting_pnt()
21+
22+
le_spectrum = meas.lyapunov_exponent_spectrum(
23+
iterator_func=iterator_func,
24+
starting_point=starting_point,
25+
deviation_scale=deviation_scale,
26+
steps=steps,
27+
part_time_steps=part_time_steps,
28+
steps_skip=steps_skip,
29+
dt=dt,
30+
m=m,
31+
initial_pert_directions=None,
32+
return_convergence=True,
33+
)
34+
35+
fig, ax = plt.subplots(
36+
1, 1, figsize=(6, 6), layout="constrained", sharex=True, sharey=False
37+
)
38+
39+
# x and y titles:
40+
fig.supxlabel("Number of renormalization steps")
41+
fig.supylabel("Lyapunov exponent convergence")
42+
43+
x = np.arange(1, steps + 1)
44+
ax.plot(
45+
x,
46+
le_spectrum,
47+
linewidth=1,
48+
)
49+
ax.grid(True)
50+
51+
final_les = np.round(le_spectrum[-1, :], 4).tolist()
52+
final_les = [str(x) for x in final_les]
53+
le_string = "\n".join(final_les)
54+
le_string = "Final LEs: \n" + le_string
55+
x_position = 0.1 # X-coordinate of the upper-left corner for each subplot
56+
y_position = 0.5
57+
ax.text(
58+
x_position,
59+
y_position,
60+
le_string,
61+
fontsize=10,
62+
bbox=dict(facecolor="white", edgecolor="black", boxstyle="round"),
63+
verticalalignment="center",
64+
horizontalalignment="left",
65+
transform=ax.transAxes,
66+
)
67+
68+
plt.show()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
"""Calculate and plot the Lyapunov Spectra of all three-dimensional chaotic flows."""
2+
3+
import matplotlib.pyplot as plt
4+
import numpy as np
5+
6+
from lorenzpy import measures as meas
7+
from lorenzpy import simulations as sims
8+
9+
systems = [
10+
"Lorenz63",
11+
"Chen",
12+
"ChuaCircuit",
13+
"ComplexButterfly",
14+
"DoubleScroll",
15+
"Halvorsen",
16+
"Roessler",
17+
"Rucklidge",
18+
"Thomas",
19+
"WindmiAttractor",
20+
]
21+
22+
# Calculate exponents:
23+
m = 3
24+
deviation_scale = 1e-10
25+
steps = 1000
26+
part_time_steps = 15
27+
steps_skip = 50
28+
29+
solver = "rk4"
30+
# solver = sims.solvers.create_scipy_ivp_solver(method="RK45")
31+
32+
lyap_dict = {}
33+
for i_sys, system in enumerate(systems):
34+
print(system)
35+
sys_obj = getattr(sims, system)(solver=solver)
36+
iterator_func = sys_obj.iterate
37+
starting_point = sys_obj.get_default_starting_pnt()
38+
dt = sys_obj.dt
39+
40+
lyap_dict[system] = meas.lyapunov_exponent_spectrum(
41+
iterator_func=iterator_func,
42+
starting_point=starting_point,
43+
deviation_scale=deviation_scale,
44+
steps=steps,
45+
part_time_steps=part_time_steps,
46+
steps_skip=steps_skip,
47+
dt=dt,
48+
m=m,
49+
initial_pert_directions=None,
50+
return_convergence=True,
51+
)
52+
53+
fig, axs = plt.subplots(
54+
2, 5, figsize=(15, 8), layout="constrained", sharex=True, sharey=False
55+
)
56+
57+
# x and y titles:
58+
fig.supxlabel("Number of renormalization steps")
59+
fig.supylabel("Lyapunov exponent convergence")
60+
61+
axs = axs.flatten()
62+
x = np.arange(1, steps + 1)
63+
for i_ax, ax in enumerate(axs):
64+
system = systems[i_ax]
65+
le_spectrum = lyap_dict[system]
66+
ax.title.set_text(system)
67+
ax.plot(
68+
x,
69+
le_spectrum,
70+
linewidth=1,
71+
)
72+
ax.grid(True)
73+
74+
final_les = np.round(le_spectrum[-1, :], 4).tolist()
75+
final_les = [str(x) for x in final_les]
76+
le_string = "\n".join(final_les)
77+
le_string = "Final LEs: \n" + le_string
78+
x_position = 0.1 # X-coordinate of the upper-left corner for each subplot
79+
y_position = 0.5
80+
ax.text(
81+
x_position,
82+
y_position,
83+
le_string,
84+
fontsize=10,
85+
bbox=dict(facecolor="white", edgecolor="black", boxstyle="round"),
86+
verticalalignment="center",
87+
horizontalalignment="left",
88+
transform=ax.transAxes,
89+
)
90+
91+
plt.show()

pyproject.toml

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[project]
22
name = "lorenzpy"
33
readme = "README.md"
4-
version = "0.0.1"
4+
version = "0.0.2"
55
description = "A Python package to simulate and measure chaotic dynamical systems."
66
authors = [
77
{name = "Dennis Duncan", email = "[email protected]"},
@@ -41,7 +41,6 @@ dev = [
4141
"pre-commit==3.1.1", # add version?
4242
]
4343
plot = [
44-
"plotly>=5.11",
4544
"matplotlib>=3.5"
4645
]
4746

@@ -59,7 +58,7 @@ line-length = 88
5958
files = "src/lorenzpy/"
6059

6160
[[tool.mypy.overrides]]
62-
module = ['plotly.*', 'numpy', 'pytest', "scipy.*", "matplotlib.*", "PIL"] # ignore missing imports from the plotly package.
61+
module = ['numpy', 'pytest', "scipy.*", "matplotlib.*", "PIL", "mpl_toolkits.*"]
6362
ignore_missing_imports = true
6463

6564
[tool.ruff]

src/lorenzpy/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@
88

99
from . import measures, simulations
1010

11-
__version__ = "0.0.1"
11+
__version__ = "0.0.2"

src/lorenzpy/plot/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
"""Submodule to plot chaotic dynamics systems."""
2+
from .plot import create_3d_line_plot

src/lorenzpy/plot/plot.py

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"""Plot the data of dynamical systems."""
2+
3+
import matplotlib.pyplot as plt
4+
import numpy as np
5+
from mpl_toolkits.mplot3d import Axes3D
6+
7+
8+
def create_3d_line_plot(data: np.ndarray, ax: "Axes3D" = None, **kwargs) -> "Axes3D":
9+
"""Create a three-dimensional line plot of data.
10+
11+
Args:
12+
data (np.ndarray): A NumPy array containing 3D data points with shape (n, 3).
13+
ax (Axes3D, optional): A Matplotlib 3D axis to use for plotting.
14+
If not provided, a new 3D plot will be created.
15+
**kwargs: Additional keyword arguments to pass to ax.plot.
16+
17+
Returns:
18+
Axes3D: The Matplotlib 3D axis used for the plot.
19+
20+
Example:
21+
>>> data = np.random.rand(100, 3) # Replace this with your own 3D data
22+
>>> create_3d_line_plot(data, color='b', linestyle='--')
23+
>>> plt.show()
24+
"""
25+
if ax is None:
26+
fig = plt.figure()
27+
ax = fig.add_subplot(111, projection="3d")
28+
29+
x = data[:, 0]
30+
y = data[:, 1]
31+
z = data[:, 2]
32+
33+
ax.plot(x, y, z, **kwargs) # Use plot for a line plot with kwargs
34+
35+
ax.set_xlabel("X Axis")
36+
ax.set_ylabel("Y Axis")
37+
ax.set_zlabel("Z Axis")
38+
39+
return ax

src/lorenzpy/plot/plot_3d.py

-17
This file was deleted.

src/lorenzpy/simulations/__init__.py

+1-17
Original file line numberDiff line numberDiff line change
@@ -28,24 +28,8 @@
2828
>>> data = sims.Lorenz63().simulate(1000)
2929
>>> data.shape
3030
(1000, 3)
31-
32-
33-
TODO <below>
34-
- Probably for each concrete simulation class + public methods. Compare with sklearn
35-
- Find out which functionality is missing. E.g. Raising error when wrong values are
36-
parsed.
37-
- Check where to add proper tests and how to add them efficiently. Fixtures?
38-
Parametrization?
39-
- Implement all the other dynamical systems.
40-
- Check if the names of files and functions make sense?
41-
- Add functionality to add your own dynamical system? As my base-classes are
42-
protected this is maybe not so easy? -> Make ABC public?
43-
- Think about adding NARMA? Maybe I need a random number generator framework.
44-
- Check if I can further reduce code duplication. Maybe regarding solvers.
45-
- Check for proper doc-generation. It seems that the methods of inhereted members
46-
is not implemented yet. See:
47-
https://github.com/mkdocstrings/mkdocstrings/issues/78
4831
"""
32+
4933
from . import solvers
5034
from .autonomous_flows import (
5135
Chen,

src/lorenzpy/simulations/autonomous_flows.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ class ComplexButterfly(_BaseSimFlow):
9595
def __init__(
9696
self,
9797
a: float = 0.55,
98-
dt: float = 0.05,
98+
dt: float = 0.1,
9999
solver: str | str | Callable[[Callable, float, np.ndarray], np.ndarray] = "rk4",
100100
):
101101
"""Initialize the ComplexButterfly simulation object.

src/lorenzpy/simulations/others.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -228,13 +228,12 @@ def simulate(
228228
if starting_point.size == self.history_steps + 1:
229229
initial_history = starting_point
230230
elif starting_point.size == 1:
231-
initial_history = np.repeat(starting_point, self.history_steps)
231+
initial_history = np.repeat(starting_point, self.history_steps + 1)
232232
else:
233233
raise ValueError("Wrong size of starting point.")
234234

235235
traj_w_hist = np.zeros((self.history_steps + time_steps, 1))
236-
traj_w_hist[: self.history_steps, :] = initial_history[:, np.newaxis]
237-
traj_w_hist[self.history_steps, :] = starting_point
236+
traj_w_hist[: self.history_steps + 1, :] = initial_history[:, np.newaxis]
238237

239238
for t in range(1, time_steps + transient):
240239
t_shifted = t + self.history_steps

static/attractor_animation.gif

-221 KB
Loading

0 commit comments

Comments
 (0)