Cubic chaotic map and its chaotic walk
Bertin's cubic chaotic map and the chaotic walk it drives, which spreads like a random walk.
- Name:
cubic_chaotic_map - Field:
mathematics/dynamical_systems - Formalism: discrete map
- References: Bertin 2021
Factory
@register_model(
field='mathematics/dynamical_systems',
tags=('chaos', 'discrete map', 'random walk', 'diffusion'),
references=('Bertin 2021',),
defaults={'until': 200, 'dt': 1},
observables=lambda system: [
ObservableSpec('displacement', group='walkers', variable='y'),
ObservableSpec('square_displacement', _square_displacement, group='walkers'),
],
)
def cubic_chaotic_map(n_walkers: int = 100, *, lam: float = 15.0) -> System:
"""Return Bertin's cubic chaotic map and the chaotic walk it drives, which spreads like a random walk.
Every walker follows the map `x' = λ x (x - 1/2)(x - 1) + x` on the unit interval and moves by the step
`y' = y + 2x - 1`. The fixed point `x = 1/2` is stable for `λ < 8`, a period-two cycle follows, and the map is
chaotic at `λ = 15`. The walkers start at `y = 0` with values of `x` spread regularly over the unit interval, so
the mean position stays near 0 while the mean square position grows linearly in time, and the invariant density of
`x` is not uniform.
Args:
n_walkers:
The number of walkers of the ensemble.
lam:
The parameter `λ` of the map, between 0 and 16.
Returns:
system:
The model, with the states `walkers.x` and `walkers.y` and the observables `displacement`, the mean
position, and `square_displacement`, the mean square position.
Examples:
>>> from skcomplex.models.mathematics.dynamical_systems import cubic_chaotic_map
>>> cubic_chaotic_map(4, lam=10.0).elements[0].check().prepare().size_
4
"""
start = (np.arange(n_walkers) + 1 / 4) / n_walkers
walkers = GroupElement(
'walkers',
element=CubicWalkElement,
size=n_walkers,
values={'x': start, 'y': 0.0, 'lam': lam},
)
return System(
elements=[walkers],
)
Components
class CubicWalkElement(Element):
"""Walker driven by Bertin's cubic chaotic map `x' = lam x (x - 1/2)(x - 1) + x`, which steps by `2 x - 1`.
Args:
label:
The label of the element.
visual:
How the element is drawn, or `None` for `default_visual_`.
x:
The initial value of the map, between 0 and 1.
y:
The initial position of the walker.
lam:
The nonlinearity of the map.
Examples:
>>> from skcomplex.models.mathematics.dynamical_systems import CubicWalkElement
>>> CubicWalkElement('walker', lam=8.0).lam
8.0
"""
visual = VisualSpec('points', {'position': 'y', 'states': ()})
def __init__(
self,
label: str,
visual: VisualSpec | None = None,
*,
x: Annotated[object, StateSpec()] = 0.3,
y: Annotated[object, StateSpec(role='position')] = 0.0,
lam: Annotated[object, ParameterSpec(domain=RealSet.open(0, oo))] = 15.0,
) -> None:
super().__init__(label, visual=visual)
self.x = x
self.y = y
self.lam = lam
def _get_laws(self, s: SimpleNamespace) -> list[BaseLaw]:
"""Return the next value of the map and the next position.
Args:
s:
The namespace of the symbols the laws can use.
Returns:
laws:
The laws of the element.
"""
return [
UpdateLaw(s.x, s.lam * s.x * (s.x - 1 / 2) * (s.x - 1) + s.x),
UpdateLaw(s.y, s.y + 2 * s.x - 1),
]
Equations
walkers.x[i]: x_walkers(t + 1) == lam_walkers*(x_walkers(t) - 0.5)*(x_walkers(t) - 1)*x_walkers(t) + x_walkers(t)
walkers.y[i]: y_walkers(t + 1) == 2*x_walkers(t) + y_walkers(t) - 1