Ricker map of overcompensating generations
Ricker's map of a population in discrete generations, n' = r n exp(-n), chaotic for large r.
- Name:
ricker - Field:
biology/ecology - Formalism: discrete map
- References: Ricker 1954; Dilão 2023
Select this system
from skcomplex.systems import create_simulation, create_system, describe
system = create_system('ricker')
entry = describe('ricker')
trajectory = create_simulation('ricker').prepare().run().trajectory_
Factory
@register_system(
field='biology/ecology',
tags=('population', 'discrete map', 'period doubling', 'chaos'),
references=('Ricker 1954', 'Dilão 2023'),
defaults={'until': 100, 'dt': 1},
)
def ricker(n: float = 0.5, *, r: float = 2.0) -> System:
"""Return Ricker's map of a population in discrete generations, `n' = r n exp(-n)`, chaotic for large `r`.
The positive fixed point `n = ln r` is stable while `1 < r < e^2`. Beyond `e^2`, about 7.39, it loses stability to
a two-cycle, and further period doublings lead to chaos near `r = 14.8`. The size stays positive for every `r`.
Args:
n:
The initial size, in units of the density at which the offspring peak.
r:
The growth factor at low density.
Returns:
system:
The system, with the population `population.n`.
Examples:
>>> from skcomplex.systems.biology.ecology import ricker
>>> ricker(r=12.0).interactions[0].r
12.0
"""
return System(
elements=[PopulationElement('population', n=n)],
interactions=[RickerGrowthInteraction('growth', target='population', r=r)],
)
Components
Elements:
PopulationElement, fromCompartmentElement
Interactions:
RickerGrowthInteraction, fromInteraction
class PopulationElement(CompartmentElement):
"""Population of one species, a compartment whose state `n` holds its size.
Args:
label:
The label of the population.
visual:
How the element is drawn, or `None` for `default_visual_`.
n:
The initial size.
Examples:
>>> from skcomplex.systems.biology.common.populations import PopulationElement
>>> PopulationElement('rabbits', n=40).n
40
"""
def __init__(
self,
label: str,
visual: VisualSpec | None = None,
*,
n: Annotated[float, StateSpec(domain=RealSet.closed_open(0, oo))] = 1.0,
) -> None:
super().__init__(label, visual=visual, n=n)
class RickerGrowthInteraction(Interaction):
"""Ricker growth of a population in discrete generations, `n' = r n exp(-n)`, overcompensating at high density.
The size `n` is in units of the density at which the offspring of a generation peak, so a crowded generation
leaves fewer offspring than a sparser one.
Args:
label:
The label of the interaction.
target:
The path of the population.
r:
The growth factor at low density.
Examples:
>>> from skcomplex.systems.biology.ecology import RickerGrowthInteraction
>>> RickerGrowthInteraction('growth', target='fish', r=12.0).r
12.0
"""
def __init__(
self,
label: str,
*,
target: Annotated[str, RoleSpec()],
r: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 2.0,
) -> None:
super().__init__(label)
self.target = target
self.r = r
def _get_laws(self, s: SimpleNamespace) -> list[BaseLaw]:
"""Return the next generation `r n exp(-n)`.
Args:
s:
The namespace of the symbols the laws can use.
Returns:
laws:
The laws of the component.
"""
n = s.target.n
return [UpdateLaw(n, s.r * n * exp(-n))]
Equations
population.n: n_population(t + 1) == r_growth*e^(-n_population(t))*n_population(t)