Choose an evolutionary dynamic

A population dynamic defines how the population transitions between states. There are four dynamics in ludics, falling into two categories:

Extrinsic dynamics (Moran, Fermi): players update by comparing themselves to others. The resulting Markov chain is absorbing and the key quantity is the fixation probability.

Intrinsic dynamics (introspection, aspiration): players update based on their own payoff alone. The resulting chain is ergodic and the key quantity is the stationary distribution.

See What is a population dynamic? for the mathematical definitions of each dynamic.

The Moran process

Use the compute_moran_transition_probability function. Takes selection_intensity and fitness_map arguments in addition to standard parameters.

>>> import ludics
>>> import numpy as np

>>> def example_fitness_function(state):
...    return np.array([np.sum(state) - player_action for player_action in state])

>>> source = np.array([0,1,2])
>>> target = np.array([1,1,2])
>>> selection_intensity = 0.5

>>> ludics.compute_moran_transition_probability(
... source=source,
... target=target,
... selection_intensity=selection_intensity,
... fitness_function=example_fitness_function,
... fitness_map=ludics.linear_fitness_map
... )
np.float64(0.1111111111111111)

Note: selection_intensity must satisfy the following equation for all players \(i\):

\(1 -\) selection_intensity \(+\) selection_intensity \(\cdot\) fitness_function(state)[i] \(\gt 0\)

selection_intensity is passed as a numpy.array. If player \(i\) changes, then player \(j\)'s fitness is scaled by entry \((i,j)\) during selection.

Fermi imitation dynamics

Use the compute_fermi_transition_probability function. Takes a choice_intensity argument in addition to standard parameters.

>>> import ludics
>>> import numpy as np

>>> def example_fitness_function(state):
...     return np.array([np.sum(state) - player_action for player_action in state])

>>> source = np.array([0, 1, 2])
>>> target = np.array([1, 1, 2])
>>> choice_intensity = 0.5

>>> ludics.compute_fermi_transition_probability(
...     source=source,
...     target=target,
...     choice_intensity=choice_intensity,
...     fitness_function=example_fitness_function,
... )
0.0629234447996909

choice_intensity controls the rationality of the Fermi function. It is passed as a numpy.array with shape (N,N). Like selection_intensity, player \(i\) considers player \(j\)'s fitness with rationality equal to entry \((i,j)\).

Introspection dynamics

Use the compute_introspection_transition_probability function. Takes choice_intensity and number_of_strategies arguments in addition to standard parameters.

>>> import ludics
>>> import numpy as np

>>> def example_fitness_function(state):
...     return np.array([np.sum(state) - player_action for player_action in state])

>>> source = np.array([0, 1, 2])
>>> target = np.array([1, 1, 2])
>>> choice_intensity = 0.5

>>> ludics.compute_introspection_transition_probability(
...     source=source,
...     target=target,
...     choice_intensity=choice_intensity,
...     fitness_function=example_fitness_function,
...     number_of_strategies=3,
... )
0.0833333333333333

In introspection dynamics, choice_intensity is given as an (N,K) numpy.array, where \(K\) is the number of strategies. Entry \((i,k)\) is the rationality used by player \(i\) when considering strategy \(k\). For a homogeneous value, np.full can be used.

Aspiration dynamics

Use the compute_aspiration_transition_probability function. Takes choice_intensity and aspiration_vector arguments in addition to standard parameters. State space must include exactly two actions.

>>> import ludics
>>> import numpy as np

>>> def example_fitness_function(state):
...     return np.array([np.sum(state) - player_action for player_action in state])

>>> source = np.array([0, 1, 1])
>>> target = np.array([1, 1, 1])
>>> choice_intensity = 0.5
>>> aspiration_vector = np.array([3, 2, 1])

>>> ludics.compute_aspiration_transition_probability(
...     source=source,
...     target=target,
...     choice_intensity=choice_intensity,
...     fitness_function=example_fitness_function,
...     aspiration_vector=aspiration_vector,
... )
0.207486443733952