Evo-Simulator — The Laboratory
Run evolutionary dynamics computationally. Take a game specification, simulate the population over time, and produce interpretable results — trajectory plots, phase portraits, fixation probabilities, and stability assessments. Essential for games where analytical solutions are intractable or where intuition needs verification.
How to Run
Input
The user provides:
- A game — payoff matrix, or a description to be formalized
- A dynamics model — replicator, Moran, best response, logit, or other revision protocol
- Initial conditions — starting population state(s)
- Parameters — population size (for stochastic), selection intensity, mutation rate, time horizon
Defaults (if not specified):
- Dynamics: Replicator (continuous-time)
- Initial conditions: Multiple starting points across the simplex
- Population size: Infinite (deterministic) unless finite specified
- Time horizon: Until convergence or 1000 generations
Steps
Step 1 — Formalize the Game
Convert the user's input to a payoff matrix A where Aᵢⱼ = payoff to strategy i against strategy j.
For a 2-strategy game:
A = [[a, b],
[c, d]]
For a 3-strategy game:
A = [[a, b, c],
[d, e, f],
[g, h, i]]
Step 2 — Analytical Pre-Analysis
Before simulating, identify:
- Fixed points: Interior NE (solve f₁ = f₂ = ... = fₙ) and boundary NE (pure strategies)
- ESS candidates: Check the ESS conditions for each NE
- Expected behavior: Predict convergence, cycling, or chaos based on game structure
This provides a reference for validating the simulation.
Step 3 — Choose and Run Dynamics
Deterministic replicator dynamics (default for infinite population):
import numpy as np
from scipy.integrate import odeint
def replicator(x, t, A):
fitness = A @ x
avg_fitness = x @ fitness
return x * (fitness - avg_fitness)
# Solve ODE from initial condition x0 over time grid t
trajectory = odeint(replicator, x0, t, args=(A,))
Moran process (for finite population N):
def moran_step(population, A, N):
# Compute fitness of each strategy type
counts = np.bincount(population, minlength=len(A))
freqs = counts / N
fitness = A @ freqs
# Select reproducer proportional to fitness
reproducer_type = np.random.choice(len(A), p=fitness*freqs/sum(fitness*freqs))
# Select random individual to die
death_idx = np.random.randint(N)
population[death_idx] = reproducer_type
return population
Fixation probability (for finite populations): Run many independent Moran process simulations starting with a single mutant. The fraction that fixate estimates the fixation probability.
Step 4 — Visualize Results
For 2-strategy games: Plot strategy frequency x₁ over time. Multiple trajectories from different initial conditions. Mark fixed points and basins of attraction.
For 3-strategy games: Plot trajectories on the 2-simplex (triangle). Use barycentric coordinates. Color-code by basin of attraction. Mark fixed points.
For finite populations: Plot frequency over time (single runs show stochasticity). Histogram of fixation times. Fixation probability as a function of initial frequency.
Step 5 — Interpret and Compare
Compare simulation results to analytical predictions:
- Do trajectories converge to predicted ESS?
- Are basins of attraction as expected?
- For finite populations, how do fixation probabilities compare to the analytical formula?
- Does stochasticity qualitatively change the prediction?
Report discrepancies — they often reveal interesting phenomena (e.g., stochastic effects favoring a strategy that's not an ESS in the infinite-population limit).
Step 6 — Sensitivity Analysis
Vary key parameters and report how results change:
- Selection intensity: Weak selection (nearly neutral) vs. strong selection
- Population size: Small (N=20) vs. large (N=1000) — stochastic effects diminish
- Mutation rate: Zero mutation (absorbing states) vs. positive mutation (stationary distribution)
- Initial conditions: Which initial states lead to which outcomes?
Output
A simulation report containing:
## Evolutionary Simulation: [Game Name]
### Game Specification
[Payoff matrix, dynamics model, parameters]
### Analytical Predictions
[Fixed points, ESS, expected basins of attraction]
### Simulation Results
[Trajectory descriptions, convergence behavior, fixation probabilities]
[Visualizations or descriptions of phase portraits]
### Sensitivity Analysis
[How results change with parameter variation]
### Interpretation
[What the dynamics tell us about the strategic situation]
Error Handling
Game matrix is not square: A payoff matrix must have the same number of rows and columns (same number of strategies). Ask the user to verify the game specification.
Dynamics don't converge: This may be correct — RPS-type games cycle indefinitely. Report the cycling behavior and the nature of the orbits (closed orbits, spirals in/out, chaos).
Stochastic simulation too slow: For large populations or many strategies, the Moran process is slow. Suggest reducing N, using a diffusion approximation, or switching to deterministic dynamics with finite-population corrections.
Multiple basins of attraction: This is a feature, not a bug. Report all basins and the critical thresholds between them. Identify which initial conditions lead to which outcomes.
Numerical instability: Replicator dynamics can produce frequencies below 0 or above 1 due to numerical errors. Use projection onto the simplex after each integration step. If x_i < ε, set to 0 and renormalize.