Skip to content

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, from CompartmentElement

Interactions:

  • RickerGrowthInteraction, from Interaction
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)