Skip to content

Commit

Permalink
Added the MackeyGlass class to simulations.py including a simple test.
Browse files Browse the repository at this point in the history
- There are still some TODOs to take care of, but the main functionality seems to work.
  • Loading branch information
DuncDennis committed Apr 18, 2023
1 parent 34174cc commit d1797b9
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 0 deletions.
111 changes: 111 additions & 0 deletions src/lorenzpy/simulations.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,114 @@ def flow(self, x: np.ndarray) -> np.ndarray:
x[0] * x[1] - self.beta * x[2],
]
)


class MackeyGlass:
"""Simulate the Mackey-Glass delay differential system.
TODO: Add literature values for Lyapunov etc.
TODO: Hint the differences between this class and the other Sim classes (delay).
TODO: Check if the structure is really good?
TODO: Add Proper Tests.
TODO: Decide whether to use the simple forward-euler or RK4-style update.
Note: As the Mackey-Glass system is a delay-differential equation, the class does
not contain a simple iterate function.
"""

default_parameters = {"a": 0.2, "b": 0.1, "c": 10, "tau": 23.0, "dt": 0.1}
default_starting_point = np.array([0.9])
sys_dim = 1

def __init__(
self,
a: float | None = None,
b: float | None = None,
c: int | None = None,
tau: float | None = None,
dt: float | None = None,
) -> None:
"""Define the Mackey-Glass system parameters.
Args:
a: "a" parameter of the Mackey-Glass equation.
b: "b" parameter of the Mackey-Glass equation.
c: "c" parameter of the Mackey-Glass equation.
tau: Time delay of Mackey-Glass equation.
dt: Size of time steps.
"""
self.a = self.default_parameters["a"] if a is None else a
self.b = self.default_parameters["b"] if b is None else b
self.c = self.default_parameters["c"] if c is None else c
self.tau = self.default_parameters["tau"] if tau is None else tau
self.dt = self.default_parameters["dt"] if dt is None else dt

# The number of time steps between t=0 and t=-tau:
self.history_steps = int(self.tau / self.dt)

def flow_mg(self, x: np.ndarray, x_past: np.ndarray) -> np.ndarray:
"""Calculate the flow of the Mackey-Glass equation.
Args:
x: The immediate value of the system. Needs to have shape (1,).
x_past: The delayed value of the system. Needs to have shape (1,).
Returns:
: The flow corresponding to x and x_past.
"""
return np.array(
[self.a * x_past[0] / (1 + x_past[0] ** self.c) - self.b * x[0]]
)

def iterate_mg(self, x: np.ndarray, x_past: np.ndarray) -> np.ndarray:
"""Calculate the next time step in the Mackey-Glass equation.
Args:
x: The immediate value of the system. Needs to have shape (1,).
x_past: The delayed value of the system. Needs to have shape (1,).
Returns:
: The next value given the immediate and delayed values.
"""
return x + self.dt * self.flow_mg(x, x_past)
# f = lambda x_use: self.flow_mg(x_use, x_past)
# return _runge_kutta(f, self.dt, x)

def simulate(
self, time_steps: int, starting_point: np.ndarray | None = None
) -> np.ndarray:
"""Simulate the Mackey-Glass trajectory.
Args:
time_steps: Number of time steps t to simulate.
starting_point: Starting point of the trajectory shape (sys_dim,).
If None, take the default starting point.
Returns:
Trajectory of shape (t, sys_dim).
"""
if starting_point is None:
starting_point = self.default_starting_point
else:
if starting_point.size != self.sys_dim:
raise ValueError(
"Provided starting_point has the wrong dimension. "
f"{self.sys_dim} was expected and {starting_point.size} "
"was given"
)

traj_w_hist = np.zeros((self.history_steps + time_steps, self.sys_dim))
traj_w_hist[: self.history_steps, :] = np.repeat(
starting_point, self.history_steps
)[:, np.newaxis]
traj_w_hist[self.history_steps, :] = starting_point

for t in range(1, time_steps):
t_shifted = t + self.history_steps
traj_w_hist[t_shifted] = self.iterate_mg(
traj_w_hist[t_shifted - 1],
traj_w_hist[t_shifted - 1 - self.history_steps],
)

return traj_w_hist[self.history_steps :, :]
6 changes: 6 additions & 0 deletions tests/test_simulations.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,9 @@ def test_simulation_with_wrong_dim_of_starting_point():

with pytest.raises(ValueError):
test_sim.simulate(time_steps=10, starting_point=wrong_starting_point)


def test_mackey_glass_simulation_shape():
"""Testing that the MackeyGlass simulation outputs the correct shape."""
shape = simulations.MackeyGlass().simulate(2).shape
assert shape == (2, 1)

0 comments on commit d1797b9

Please sign in to comment.