import numpy as np
from typing import Union
from hetero.stims.utils import get_seed, rand_generator, check_vector, RandomState
[docs]
def henon_map(
n_timesteps: int,
a: float = 1.4,
b: float = 0.3,
x0: Union[list, np.ndarray] = [0.0, 0.0],
**kwargs,
) -> np.ndarray:
"""Hénon map discrete timeseries [2]_ [3]_.
.. math::
x(n+1) &= 1 - ax(n)^2 + y(n)\\\\
y(n+1) &= bx(n)
Parameters
----------
n_timesteps : int
Number of timesteps to generate.
a : float, default to 1.4
:math:`a` parameter of the system.
b : float, default to 0.3
:math:`b` parameter of the system.
x0 : array-like of shape (2,), default to [0.0, 0.0]
Initial conditions of the system.
Returns
-------
array of shape (n_timesteps, 2)
Hénon map discrete timeseries.
"""
states = np.zeros((n_timesteps, 2))
states[0] = np.asarray(x0)
for i in range(1, n_timesteps):
states[i][0] = 1 - a * states[i - 1][0] ** 2 + states[i - 1][1]
states[i][1] = b * states[i - 1][0]
return states
[docs]
def logistic_map(n_timesteps: int, r: float = 3.9, x0: float = 0.5, **kwargs) -> np.ndarray:
"""Logistic map discrete timeseries [4]_ [5]_.
.. math::
x(n+1) = rx(n)(1-x(n))
Parameters
----------
n_timesteps : int
Number of timesteps to generate.
r : float, default to 3.9
:math:`r` parameter of the system.
x0 : float, default to 0.5
Initial condition of the system.
Returns
-------
array of shape (n_timesteps, 1)
Logistic map discrete timeseries.
"""
if r > 0 and 0 < x0 < 1:
X = np.zeros(n_timesteps)
X[0] = x0
for i in range(1, n_timesteps):
X[i] = r * X[i - 1] * (1 - X[i - 1])
return X.reshape(-1, 1)
elif r <= 0:
raise ValueError("r should be positive.")
else:
raise ValueError("Initial condition x0 should be in ]0;1[.")
[docs]
def narma(
n_timesteps: int,
order: int = 30,
a1: float = 0.2,
a2: float = 0.04,
b: float = 1.5,
c: float = 0.001,
x0: Union[list, np.ndarray] = [0.0],
seed: Union[int, RandomState] = None,
) -> np.ndarray:
"""Non-linear Autoregressive Moving Average (NARMA) timeseries,
as first defined in [14]_, and as used in [15]_.
NARMA n-th order dynamical system is defined by the recurrent relation:
.. math::
y[t+1] = a_1 y[t] + a_2 y[t] (\\sum_{i=0}^{n-1} y[t-i]) + b u[t-(
n-1)]u[t] + c
where :math:`u[t]` are sampled following a uniform distribution in
:math:`[0, 0.5]`.
Parameters
----------
n_timesteps : int
Number of timesteps to generate.
order: int, default to 30
Order of the system.
a1 : float, default to 0.2
:math:`a_1` parameter of the system.
a2 : float, default to 0.04
:math:`a_2` parameter of the system.
b : float, default to 1.5
:math:`b` parameter of the system.
c : float, default to 0.001
:math:`c` parameter of the system.
x0 : array-like of shape (init_steps,), default to [0.0]
Initial conditions of the system.
seed : int or :py:class:`numpy.random.Generator`, optional
Random state seed for reproducibility.
Returns
-------
array of shape (n_timesteps, 1)
NARMA timeseries.
"""
if seed is None:
seed = get_seed()
rs = rand_generator(seed)
y = np.zeros((n_timesteps + order, 1))
x0 = check_vector(np.atleast_2d(np.asarray(x0)))
y[: x0.shape[0], :] = x0
noise = rs.uniform(0, 0.5, size=(n_timesteps + order, 1))
for t in range(order, n_timesteps + order - 1):
y[t + 1] = (
a1 * y[t]
+ a2 * y[t] * np.sum(y[t - order : t])
+ b * noise[t - order] * noise[t]
+ c
)
return y[order:, :]