egttools.numerical.numerical_.PairwiseComparisonNumerical

class PairwiseComparisonNumerical

Bases: pybind11_object

Numerical solver for evolutionary dynamics under the pairwise comparison rule.

Construct a numerical solver for a finite-population game.

Parameters:
  • pop_size (int) – Number of individuals in the population.

  • game (egttools.games.AbstractGame) – Game object implementing the payoff and fitness structure.

  • cache_size (int) – Maximum cache size for fitness computations.

Methods

change_game

Replace the game used for fitness computation.

estimate_absorption_probabilities

Estimate the absorption probability for each strategy from a given initial state.

estimate_fixation_probability

Estimate the fixation probability of an invading strategy in a resident population.

estimate_mean_absorption_time

Estimate the mean absorption time (fixation time) from a given initial state.

estimate_stationary_distribution

Estimate the stationary distribution of population states.

estimate_stationary_distribution_sparse

Estimate the stationary distribution in sparse format.

estimate_stationary_indicators

Estimate expected indicator values under the stationary distribution.

estimate_stationary_indicators_precomputed

Estimate expected indicator values under the stationary distribution without computing the full distribution first.

estimate_strategy_distribution

Estimate the average frequency of each strategy over time.

evolve

Simulate the pairwise comparison process with mutation.

run

run_with_mutation

Simulate the stochastic dynamics with mutation.

run_without_mutation

Simulate the stochastic dynamics without mutation.

set_mutation_matrix

Set a full source-strategy-dependent mutation bias.

set_mutation_weights

Set the mutation bias.

Attributes

cache_size

Maximum number of cached fitness values.

mutation_matrix

Current (nb_strategies, nb_strategies) mutation matrix.

nb_states

Number of discrete states in the population.

nb_strategies

Number of strategies in the population.

payoffs

Payoff matrix used for selection dynamics.

pop_size

Current population size.

__init__()

Construct a numerical solver for a finite-population game.

Parameters:
  • pop_size (int) – Number of individuals in the population.

  • game (egttools.games.AbstractGame) – Game object implementing the payoff and fitness structure.

  • cache_size (int) – Maximum cache size for fitness computations.

__new__(**kwargs)
change_game()

Replace the game used for fitness computation.

The solver retains a pointer to the new game object; the caller must ensure the game stays alive for the lifetime of this solver (enforced automatically when called from Python via the keep-alive policy).

Parameters:

game (egttools.games.AbstractGame) – New game object. Must have the same number of strategies as the current game.

estimate_absorption_probabilities()

Estimate the absorption probability for each strategy from a given initial state.

Runs independent trajectories of the mutation-free Moran process from init_state and records which strategy fixed in each run. Generalises estimate_fixation_probability to k > 2 strategies.

Parameters:
  • beta (float) – Intensity of selection.

  • init_state (numpy.ndarray) – Initial population state — array of strategy counts summing to pop_size.

  • nb_runs (int) – Number of independent trajectories.

Returns:

Array of shape (nb_strategies,) with the empirical fixation probability for each strategy.

Return type:

numpy.ndarray

estimate_fixation_probability()

Estimate the fixation probability of an invading strategy in a resident population.

estimate_mean_absorption_time()

Estimate the mean absorption time (fixation time) from a given initial state.

Runs independent trajectories of the mutation-free Moran process from init_state and counts generations until any strategy reaches pop_size.

Parameters:
  • beta (float) – Intensity of selection.

  • init_state (numpy.ndarray) – Initial population state — array of strategy counts summing to pop_size.

  • nb_runs (int) – Number of independent trajectories.

Return type:

dict with keys "mean" (float), "stderr" (float), "nb_runs" (int).

estimate_stationary_distribution()

Estimate the stationary distribution of population states.

When tolerance > 0, runs are processed in batches of check_every (default: max(1, nb_runs // 10)). After each batch the L1 norm of the change in the normalised estimate is computed; if it falls below tolerance the simulation stops early. This can save significant computation when the distribution converges before all nb_runs are exhausted.

Warning

If mu * (nb_generations - transitory) is much less than 10 (i.e. fewer than ~10 mutations are expected in the counting window) a UserWarning is raised. The geometric-skip approximation becomes inaccurate in this regime. Increase nb_generations, decrease transitory, or raise mu.

Parameters:
  • nb_runs (int) – Maximum number of independent simulation runs.

  • nb_generations (int) – Number of generations per run.

  • transitory (int) – Transient period (generations not counted toward the distribution).

  • beta (float) – Intensity of selection.

  • mu (float) – Mutation probability (must be > 0).

  • tolerance (float, optional) – Convergence threshold on the L1 norm of the change between consecutive batch estimates. 0.0 (default) disables early stopping.

  • check_every (int, optional) – Number of runs per convergence-check batch. 0 (default) uses max(1, nb_runs // 10).

Returns:

Estimated stationary distribution.

Return type:

numpy.ndarray

estimate_stationary_distribution_sparse()

Estimate the stationary distribution in sparse format.

Identical to estimate_stationary_distribution but returns a sparse matrix. Use this method when the number of population states is very large, since most entries of the stationary distribution will be zero.

Warning

If mu * (nb_generations - transitory) is much less than 10 a UserWarning is raised. See estimate_stationary_distribution for details.

Parameters:
  • nb_runs (int) – Maximum number of independent simulation runs.

  • nb_generations (int) – Number of generations per run.

  • transitory (int) – Transient period (generations not counted toward the distribution).

  • beta (float) – Intensity of selection.

  • mu (float) – Mutation probability (must be > 0).

  • tolerance (float, optional) – Convergence threshold on the L1 norm; 0.0 disables early stopping.

  • check_every (int, optional) – Runs per convergence-check batch; 0 uses max(1, nb_runs // 10).

Returns:

Estimated stationary distribution in sparse format.

Return type:

scipy.sparse.csr_matrix

estimate_stationary_indicators()

Estimate expected indicator values under the stationary distribution.

Runs stochastic simulations and accumulates indicator values at each post-transitory step. The time-average converges to the true expectation by the ergodic theorem without storing the full stationary distribution.

For indicator_type='state', when nb_states * len(indicators) exceeds precompute_limit this method automatically falls back to evaluating the indicators directly on the live simulation state at each recorded step, instead of precomputing a dense (nb_states, nb_indicators) matrix upfront. This keeps memory use at O(nb_indicators) regardless of the size of the state space, at the cost of a Python callback per indicator per recorded generation (slower per-step than the matrix lookup, but the only way to stay within memory when nb_states is too large to enumerate). This fallback is not yet available for indicator_type='group', which raises a clear error instead of attempting the same allocation.

Parameters:
  • indicators (callable or list[callable]) –

    One or more indicator functions.

    • indicator_type='state': receives a population state as np.ndarray of shape (nb_strategies,) with integer counts summing to pop_size, returns a float. Use for quantities like the fraction of cooperators.

    • indicator_type='group': receives a group configuration as np.ndarray of shape (nb_strategies,) summing to group_size, returns a float. The expectation is marginalised over group configs using the multivariate hypergeometric distribution. Requires group_size.

  • nb_runs (int) – Maximum number of independent simulation runs.

  • nb_generations (int) – Number of generations per run.

  • transitory (int) – Transitory period (generations excluded from accumulation).

  • beta (float) – Intensity of selection.

  • mu (float) – Mutation probability (must be > 0).

  • indicator_type ({'state', 'group'}, default 'state') – Whether indicators operate on population states or group configurations.

  • group_size (int, optional) – Required when indicator_type='group'.

  • tolerance (float, default 0.0) – L1 convergence threshold on column-means between batches. 0.0 disables early stopping.

  • check_every (int, default 0) – Batch size for convergence checks. 0 → max(1, nb_runs // 10).

  • confidence (float, default 0.95) – Confidence level for the bootstrap CI.

  • verbose (bool, default False) – If True, attach per-run values to the result.

  • n_bootstrap (int, default 9999) – Number of bootstrap resamples.

  • precompute_limit (int, default 20_000_000) – Maximum number of elements (nb_states * len(indicators)) allowed in the precomputed indicator matrix. Above this, indicator_type='state' switches automatically to a direct, memory-bounded estimator (see above); indicator_type='group' raises instead.

Returns:

.mean — grand mean, shape (nb_indicators,). .confidence_interval(low, high) non-parametric bootstrap CI. .nb_runs_used — runs actually completed. .convergedTrue if tolerance-based early stopping triggered. .per_run_values — per-run means (nb_runs_used, nb_indicators) when verbose=True, else None.

Return type:

StationaryIndicatorResult

See also

estimate_stationary_indicators_precomputed

low-level fast path accepting a precomputed indicator matrix directly.

egttools.precompute_group_to_state_indicator_matrix

build the indicator matrix manually for repeated reuse.

estimate_stationary_indicators_precomputed()

Estimate expected indicator values under the stationary distribution without computing the full distribution first.

At each post-transitory simulation step the method looks up the precomputed indicator values for the current population state and accumulates them. The per-run time-average converges to E[f_k] = sum_s sd(s) * indicator_values(s, k) by the ergodic theorem.

indicator_values must be a dense matrix of shape (nb_states, nb_indicators) where row s contains the values of all indicators for the population state at index s.

  • For state-level indicators f(state): build indicator_values by evaluating f on egttools.sample_simplex(s, pop_size, nb_strategies) for each state index s.

  • For group-level indicators f(group_config): use egttools.precompute_group_to_state_indicator_matrix to marginalise over group configurations first, then pass the resulting matrix here.

Returns a matrix of shape (nb_runs_used, nb_indicators) — one row per completed run. Prefer the high-level method estimate_stationary_indicators (same class) which accepts Python callables, builds the indicator matrix automatically, and returns a StationaryIndicatorResult with mean and bootstrap CI.

Warning

If mu * (nb_generations - transitory) is much less than 10 a UserWarning is raised. See estimate_stationary_distribution for details.

Parameters:
  • nb_runs (int) – Maximum number of independent simulation runs.

  • nb_generations (int) – Number of generations per run.

  • transitory (int) – Transitory period (not counted toward indicator accumulation).

  • beta (float) – Intensity of selection.

  • mu (float) – Mutation probability (must be > 0).

  • indicator_values (numpy.ndarray) – Precomputed matrix of shape (nb_states, nb_indicators).

  • tolerance (float, optional) – L1 convergence threshold on column-means between batches. 0.0 (default) disables early stopping.

  • check_every (int, optional) – Batch size for convergence checks; 0 uses max(1, nb_runs // 10).

Returns:

Per-run means of shape (nb_runs_used, nb_indicators).

Return type:

numpy.ndarray

estimate_strategy_distribution()

Estimate the average frequency of each strategy over time.

This method bypasses state indexing and is safe when the total number of population states exceeds MAX_LONG_INT.

Warning

If mu * (nb_generations - transitory) is much less than 10 a UserWarning is raised. See estimate_stationary_distribution for details.

Parameters:
  • nb_runs (int) – Maximum number of independent simulation runs.

  • nb_generations (int) – Number of generations per run.

  • transitory (int) – Transient period (generations not counted toward the distribution).

  • beta (float) – Intensity of selection.

  • mu (float) – Mutation probability (must be > 0).

  • tolerance (float, optional) – Convergence threshold on the L1 norm; 0.0 disables early stopping.

  • check_every (int, optional) – Runs per convergence-check batch; 0 uses max(1, nb_runs // 10).

Returns:

Average frequency of each strategy.

Return type:

numpy.ndarray

evolve()

Simulate the pairwise comparison process with mutation.

Parameters:
  • nb_generations (int) – Number of generations to simulate.

  • beta (float) – Intensity of selection.

  • mu (float) – Mutation rate.

  • init_state (numpy.ndarray) – Initial population state.

Returns:

Final population state.

Return type:

numpy.ndarray

run()
run_with_mutation()

Simulate the stochastic dynamics with mutation.

Returns:

Matrix containing all intermediate population states.

Return type:

numpy.ndarray

Simulate the stochastic dynamics with mutation, skipping the transient phase.

Returns:

Matrix containing the population states after the transient period.

Return type:

numpy.ndarray

run_without_mutation()

Simulate the stochastic dynamics without mutation.

Returns:

Matrix containing all intermediate population states.

Return type:

numpy.ndarray

Simulate the stochastic dynamics without mutation, skipping the transient phase.

Returns:

Matrix containing the population states after the transient period.

Return type:

numpy.ndarray

set_mutation_matrix()

Set a full source-strategy-dependent mutation bias.

Row i of mutation_matrix is the (unnormalized) distribution over target strategies when an individual currently playing strategy i mutates. The diagonal is always ignored (mutation always changes strategy). Each row must have at least one strictly positive entry among the other strategies.

Parameters:

mutation_matrix (numpy.ndarray) – Shape (nb_strategies, nb_strategies), non-negative entries.

See also

set_mutation_weights

convenience method accepting a single vector (bias shared by all source strategies) or a full matrix.

set_mutation_weights()

Set the mutation bias. Accepts either shape:

  • 1D, length nb_strategies: a target-strategy bias shared by all source strategies (broadcast into every row of the mutation matrix; equivalent to set_mutation_matrix with every row equal to this vector and the diagonal zeroed).

  • 2D, shape (nb_strategies, nb_strategies): row i is the bias over target strategies when mutating away from strategy i (equivalent to calling set_mutation_matrix directly).

Diagonal entries are always ignored (mutation always changes strategy). Each row must have at least one positive entry among the other strategies, otherwise mutating away from that strategy would have no valid target and this raises. A solver’s mutation is uniform by default; call this to bias it.

Parameters:

weights (array_like) – 1D (length nb_strategies) or 2D (nb_strategies x nb_strategies).

__annotations__ = {}
property cache_size

Maximum number of cached fitness values.

property mutation_matrix

Current (nb_strategies, nb_strategies) mutation matrix. Row i is the (unnormalized) distribution over target strategies when mutating away from strategy i; the diagonal is always 0. Defaults to uniform (all-ones off-diagonal) until set_mutation_weights is called.

property nb_states

Number of discrete states in the population.

property nb_strategies

Number of strategies in the population.

property payoffs

Payoff matrix used for selection dynamics.

property pop_size

Current population size.