Skip to content

Populations

Population realizations shared by the biology packages: growth, mortality, predation, and replicators.

ExponentialGrowthInteraction(label, *, target, r=1.0, state='n')

Bases: GrowthInteraction

Growth at a constant per capita rate, dn/dt = r n, the exponential form of a growth without harvest.

Parameters:

Name Type Description Default
label str

The label of the interaction.

required
target Annotated[str, RoleSpec()]

The path of the element or group whose population grows.

required
r Annotated[float, ParameterSpec()]

The per capita growth rate, negative for decay.

1.0
state str

The name of the state of the target that holds the population.

'n'

Examples:

>>> from skcomplex.core.components.elements import CompartmentElement
>>> from skcomplex.core.components.system import System
>>> from skcomplex.core.engine.runtime import Simulation
>>> from skcomplex.systems.biology.common.populations import ExponentialGrowthInteraction
>>> system = System(
...     elements=[CompartmentElement('rabbits', n=1.0)],
...     interactions=[ExponentialGrowthInteraction('growth', target='rabbits', r=0.5)],
... )
>>> trajectory = Simulation(system, dt=0.01).prepare().run(until=2.0).trajectory_
>>> round(float(trajectory.values('rabbits.n')[-1]), 3)
2.718
Source code in src/skcomplex/systems/biology/common/populations/_exponential_growth.py
45
46
47
48
49
50
51
52
53
def __init__(
    self,
    label: str,
    *,
    target: Annotated[str, RoleSpec()],
    r: Annotated[float, ParameterSpec()] = 1.0,
    state: str = 'n',
) -> None:
    super().__init__(label, target=target, r=r, form='exponential', state=state)

GrowthInteraction(label, *, target, r=1.0, K=1.0, p=1.0, h=0.0, tau=0.0, form='exponential', state='n')

Bases: RateInteraction

Growth or decay of a population n of a target by a growth law, with a proportional harvest.

The rate of n is the growth of the form minus the harvest h n:

  • 'exponential': r n.
  • 'logistic': r n (1 - (n / K) ** p), the generalized logistic of Richards, with p = 1 for Verhulst's.
  • 'gompertz': r n log(K / n).
  • 'power': r n ** p.
  • 'bertalanffy': r n ** p (1 - (n / K) ** (1 - p)), von Bertalanffy's growth towards K, with p < 1.

With a delay tau above 0, the limiting factor of the logistic and the Gompertz forms reads the population n(t - tau), as in Hutchinson's delayed logistic equation. A negative rate r gives decay.

Parameters:

Name Type Description Default
label str

The label of the interaction, unique among the parts of its system.

required
target Annotated[str, RoleSpec()]

The path of the element or group whose population grows.

required
r Annotated[float, ParameterSpec()]

The growth rate.

1.0
K Annotated[float, ParameterSpec(domain=open(0, oo))]

The capacity of the logistic, Gompertz, and von Bertalanffy forms.

1.0
p Annotated[float, ParameterSpec()]

The exponent of the logistic, power, and von Bertalanffy forms.

1.0
h Annotated[float, ParameterSpec(domain=closed_open(0, oo))]

The harvest rate, the fraction of the population removed per unit time.

0.0
tau Annotated[float, ParameterSpec(domain=closed_open(0, oo))]

The delay of the limiting factor, 0 for none.

0.0
form str

The growth law, 'exponential', 'logistic', 'gompertz', 'power', or 'bertalanffy'.

'exponential'
state str

The name of the state of the target that holds the population.

'n'

Raises:

Type Description
ValidationError

The form is unknown, the state is not a name, a delay is given to a form without a limiting factor, or the von Bertalanffy exponent is 1.

Examples:

>>> from skcomplex.core.components.elements import CompartmentElement
>>> from skcomplex.systems.biology.common.populations import GrowthInteraction
>>> from skcomplex.core.components.system import System
>>> from skcomplex.core.engine.runtime import Simulation
>>> system = System(
...     elements=[CompartmentElement('population', n=1.0)],
...     interactions=[GrowthInteraction('growth', target='population', r=1.0, K=100.0, form='logistic')],
... )
>>> trajectory = Simulation(system, dt=1.0).prepare().run(until=30).trajectory_
>>> round(float(trajectory.values('population.n')[-1]), 2)
100.0
Source code in src/skcomplex/systems/biology/common/populations/_growth.py
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
def __init__(
    self,
    label: str,
    *,
    target: Annotated[str, RoleSpec()],
    r: Annotated[float, ParameterSpec()] = 1.0,
    K: Annotated[float, ParameterSpec(domain=RealSet.open(0, oo))] = 1.0,
    p: Annotated[float, ParameterSpec()] = 1.0,
    h: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 0.0,
    tau: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 0.0,
    form: str = 'exponential',
    state: str = 'n',
) -> None:
    super().__init__(label, target=target, state=state)
    self.r = r
    self.K = K
    self.p = p
    self.h = h
    self.tau = tau
    self.form = form

LogisticGrowthInteraction(label, *, target, r=1.0, K=100.0, tau=0.0, state='n')

Bases: GrowthInteraction

Verhulst's growth limited by a carrying capacity, dn/dt = r n (1 - n(t - tau) / K), without harvest.

A delay tau above 0 gives Hutchinson's delayed logistic equation. The generalized logistic of Richards, with an exponent on the crowding, is GrowthInteraction(form='logistic', p=...).

Parameters:

Name Type Description Default
label str

The label of the interaction.

required
target Annotated[str, RoleSpec()]

The path of the element or group whose population grows.

required
r Annotated[float, ParameterSpec(domain=closed_open(0, oo))]

The per capita growth rate at low density.

1.0
K Annotated[float, ParameterSpec(domain=open(0, oo))]

The carrying capacity.

100.0
tau Annotated[float, ParameterSpec(domain=closed_open(0, oo))]

The delay of the limiting factor, 0 for none.

0.0
state str

The name of the state of the target that holds the population.

'n'

Examples:

>>> from skcomplex.core.components.elements import CompartmentElement
>>> from skcomplex.core.components.system import System
>>> from skcomplex.core.engine.runtime import Simulation
>>> from skcomplex.systems.biology.common.populations import LogisticGrowthInteraction
>>> system = System(
...     elements=[CompartmentElement('rabbits', n=1.0)],
...     interactions=[LogisticGrowthInteraction('growth', target='rabbits', r=1.0, K=50.0)],
... )
>>> trajectory = Simulation(system, dt=0.1).prepare().run(until=30.0).trajectory_
>>> round(float(trajectory.values('rabbits.n')[-1]), 3)
50.0
Source code in src/skcomplex/systems/biology/common/populations/_logistic_growth.py
55
56
57
58
59
60
61
62
63
64
65
def __init__(
    self,
    label: str,
    *,
    target: Annotated[str, RoleSpec()],
    r: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 1.0,
    K: Annotated[float, ParameterSpec(domain=RealSet.open(0, oo))] = 100.0,
    tau: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 0.0,
    state: str = 'n',
) -> None:
    super().__init__(label, target=target, r=r, K=K, tau=tau, form='logistic', state=state)

MortalityInteraction(label, *, target, d=0.1, state='n')

Bases: RateInteraction

Death or loss at a constant per capita rate, dn/dt = -d n.

Parameters:

Name Type Description Default
label str

The label of the interaction.

required
target Annotated[str, RoleSpec()]

The path of the element or group whose population dies.

required
d Annotated[float, ParameterSpec(domain=closed_open(0, oo))]

The per capita death rate.

0.1
state str

The name of the state of the target that holds the population.

'n'

Examples:

>>> from skcomplex.core.components.elements import CompartmentElement
>>> from skcomplex.core.components.system import System
>>> from skcomplex.core.engine.runtime import Simulation
>>> from skcomplex.systems.biology.common.populations import MortalityInteraction
>>> system = System(
...     elements=[CompartmentElement('foxes', n=8.0)],
...     interactions=[MortalityInteraction('death', target='foxes', d=0.5)],
... )
>>> trajectory = Simulation(system, dt=0.01).prepare().run(until=2.0).trajectory_
>>> round(float(trajectory.values('foxes.n')[-1]), 3)
2.943
Source code in src/skcomplex/systems/biology/common/populations/_mortality.py
46
47
48
49
50
51
52
53
54
55
def __init__(
    self,
    label: str,
    *,
    target: Annotated[str, RoleSpec()],
    d: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 0.1,
    state: str = 'n',
) -> None:
    super().__init__(label, target=target, state=state)
    self.d = d

PopulationElement(label, visual=None, *, n=1.0)

Bases: CompartmentElement

Population of one species, a compartment whose state n holds its size.

Parameters:

Name Type Description Default
label str

The label of the population.

required
visual VisualSpec | None

How the element is drawn, or None for default_visual_.

None
n Annotated[float, StateSpec(domain=closed_open(0, oo))]

The initial size.

1.0

Examples:

>>> from skcomplex.systems.biology.common.populations import PopulationElement
>>> PopulationElement('rabbits', n=40).n
40
Source code in src/skcomplex/systems/biology/common/populations/_population.py
34
35
36
37
38
39
40
41
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)

PredationInteraction(label, *, predator, prey, a=0.1, b=0.01)

Bases: Interaction

Lotka-Volterra predation, where encounters remove prey and feed predators.

The prey loses a * predator * prey per unit time and the predator gains b * predator * prey.

Parameters:

Name Type Description Default
label str

The label of the interaction.

required
predator Annotated[str, RoleSpec()]

The path of the predator population.

required
prey Annotated[str, RoleSpec()]

The path of the prey population.

required
a Annotated[float, ParameterSpec(domain=closed_open(0, oo))]

The attack rate.

0.1
b Annotated[float, ParameterSpec(domain=closed_open(0, oo))]

The conversion rate of eaten prey into predators.

0.01

Examples:

>>> from skcomplex.systems.biology.ecology import PredationInteraction
>>> PredationInteraction('hunt', predator='foxes', prey='rabbits', a=0.4, b=0.1).prey
'rabbits'
Source code in src/skcomplex/systems/biology/common/populations/_predation.py
44
45
46
47
48
49
50
51
52
53
54
55
56
57
def __init__(
    self,
    label: str,
    *,
    predator: Annotated[str, RoleSpec()],
    prey: Annotated[str, RoleSpec()],
    a: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 0.1,
    b: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 0.01,
) -> None:
    super().__init__(label)
    self.predator = predator
    self.prey = prey
    self.a = a
    self.b = b

ReplicatorInteraction(label, *, target, fitness=None, state='x')

Bases: RateInteraction

Replicator equation of the shares of a group, dx_i/dt = x_i (f_i - sum_j x_j f_j), with a fitness f.

Every member of the group holds the share of a type, such as a strategy or a molecule. Its share grows with its fitness above the mean fitness of the group, so the total of the shares stays constant.

Parameters:

Name Type Description Default
label str

The label of the interaction.

required
target Annotated[str, RoleSpec()]

The path of the group of types.

required
fitness Callable[[SimpleNamespace], object] | None

The function of the namespace that returns the fitness of a member, such as lambda s: total(s.target.x, weights=payoff) for the payoff of a strategy against the others.

None
state str

The name of the state of the members that holds the share.

'x'

Raises:

Type Description
ValidationError

The fitness is not callable, raised by prepare.

Examples:

>>> from skcomplex.core.components.elements import GroupElement
>>> from skcomplex.core.components.laws.utils import total
>>> from skcomplex.core.components.system import System
>>> from skcomplex.core.engine.runtime import Simulation
>>> from skcomplex.systems.biology.evolution import ReplicatorInteraction, StrategyElement
>>> population = GroupElement('population', element=StrategyElement, size=2, values={'x': [0.5, 0.5]})
>>> payoff = ((1.0, 1.0), (0.0, 0.0))
>>> replication = ReplicatorInteraction(
...     'replication', target='population', fitness=lambda s: total(s.target.x, weights=payoff)
... )
>>> system = System(elements=[population], interactions=[replication])
>>> trajectory = Simulation(system, dt=0.1).prepare().run(until=20.0).trajectory_
>>> [round(float(share), 3) for share in trajectory.values('population.x')[-1]]
[1.0, 0.0]
Source code in src/skcomplex/systems/biology/common/populations/_replicator.py
59
60
61
62
63
64
65
66
67
68
def __init__(
    self,
    label: str,
    *,
    target: Annotated[str, RoleSpec()],
    fitness: Callable[[SimpleNamespace], object] | None = None,
    state: str = 'x',
) -> None:
    super().__init__(label, target=target, state=state)
    self.fitness = fitness