§1Overview
This library provides a complete JavaScript implementation of the evolutionary dynamics from William Sandholm's Dynamo software. It covers the five major families of population dynamics — imitative, excess payoff, pairwise comparison, perturbed best response, and target — together with an adaptive numerical integrator, Lyapunov functions for convergence analysis, and simplex geometry utilities.
The design follows a strict observer/model separation: all functions are pure (no DOM, no side effects), operating on plain arrays. A population state $\mathbf{x} = (x_1, \ldots, x_n)$ is an array of length $n$ summing to 1. A payoff function $F$ maps states to payoff vectors of the same length. Each dynamic is a function $(\mathbf{x}, F) \to \dot{\mathbf{x}}$ returning a velocity in the tangent space of the simplex.
| Dynamic | Family | Export | Rest Points |
|---|---|---|---|
| Replicator | Imitative | replicator | NE + faces |
| Maynard Smith Replicator | Imitative | maynardSmithReplicator | NE + faces |
| BNN | Excess Payoff | bnn | Nash equilibria |
| Smith | Pairwise | smith | Nash equilibria |
| Logit | Perturbed BR | logit(η) | Logit equilibria |
| Independent Logit | Perturbed BR | ilogit(η) | Logit equilibria |
| Best Response | Perturbed BR | bestResponse() | Nash equilibria |
| Projection | Target | projection | Nash equilibria |
§2Getting Started
Installation
The library is a single ES module with no dependencies. Copy
evolutionary-dynamics.js into your project and import
what you need:
import {
normalFormPayoff, replicator, bnn, smith, logit,
integrate, baryToCart, simplexGrid
} from './evolutionary-dynamics.js';
Basic Example
Compute a trajectory of the replicator dynamic on Rock–Paper–Scissors:
// Define the game
const A = [
[ 0, -1, 1],
[ 1, 0, -1],
[-1, 1, 0]
];
const F = normalFormPayoff(A);
// Integrate from an initial condition
const trajectory = integrate(replicator, F,
[0.6, 0.3, 0.1], // x₀
50, // T = 50 time units
{ outputInterval: 0.1 }
);
// Convert to 2D coordinates for plotting
for (const { t, x } of trajectory) {
const [px, py] = baryToCart(x);
// draw point at (px, py)...
}
Velocity Fields
Compute the velocity field on a grid for visualisation:
import { velocityField, baryToCart } from './evolutionary-dynamics.js';
const field = velocityField(replicator, F, 20); // 20 subdivisions
for (const { x, v } of field) {
const [px, py] = baryToCart(x);
const [vx, vy] = baryToCart(v);
// draw arrow from (px, py) with direction (vx, vy)...
}
§3Interactive Demo
Click anywhere on the simplex to launch a trajectory from that initial condition. The three vertices represent the three pure strategies. Edit the payoff matrix or choose a preset game to explore different phase portraits.
§4Dynamics Reference
Replicator Dynamic
The canonical imitative dynamic. Each strategy grows in proportion to its current share times its excess payoff over the population average $\bar{F}(\mathbf{x}) = \mathbf{x} \cdot F(\mathbf{x})$. Rest points include all faces of the simplex plus any interior Nash equilibria. In potential games the replicator converges to Nash equilibria; in zero-sum games like Rock–Paper–Scissors it cycles.
import { replicator } from './evolutionary-dynamics.js';
const dxdt = replicator(x, F); // velocity at state x
Maynard Smith Replicator
The replicator in continuous-time Maynard Smith form, normalised by average fitness. Requires all payoffs to be positive (add a constant to the payoff matrix if needed). Orbits are identical to the standard replicator, but the speed along orbits differs.
BNN Dynamic
Brown–von Neumann–Nash (1950). An excess payoff dynamic where agents switch toward strategies with positive excess payoff $[\hat{F}_j]^{+} = \max(0, F_j - \bar{F})$. Rest points are exactly the Nash equilibria. In stable games the Lyapunov function $V(\mathbf{x}) = \sum_j [\hat{F}_j]^{+2}$ ensures convergence.
Smith Dynamic
Smith (1984). A pairwise comparison dynamic: agents compare their payoff with a randomly encountered opponent and switch if better off. The conditional switch rate $\rho_{jk} = [F_j - F_k]^{+}$ gives the rate of switching from $k$ to $j$. Rest points are Nash equilibria.
Logit Dynamic
Fudenberg & Levine (1998). A perturbed best response dynamic where agents drift toward the logit (softmax) choice distribution. The noise parameter $\eta > 0$ controls the precision of choice: as $\eta \to 0$ the dynamic approaches the best response dynamic; as $\eta \to \infty$ it approaches uniform randomisation. Rest points are logit equilibria — perturbed versions of Nash equilibria.
const dyn = logit(0.3); // create logit with η = 0.3
const dxdt = dyn(x, F); // evaluate at state x
Projection Dynamic
Nagurney & Zhang (1997). Projects the payoff vector onto the tangent cone of the simplex at the current state. On the interior this is simply the mean-centred payoff; on the boundary it clips components that would push the state outside the simplex. Particularly clean mathematical properties but more complex to implement than other dynamics.
§5Payoff Helpers
These functions construct payoff functions and compute the derived
quantities that the dynamics use internally. They correspond to
Sandholm's Fbar, Fhat, Fhatplus,
and PhiF.
Normal-Form Payoff
For a symmetric game with $n \times n$ payoff matrix $A$, the payoff function is $F(\mathbf{x}) = A\mathbf{x}$:
const A = [[3,0],[5,1]];
const F = normalFormPayoff(A);
const payoffs = F([0.6, 0.4]); // → [1.8, 3.4]
Derived Quantities
averagePayoff(x, F) // x·F(x), scalar
excessPayoff(x, F) // F(x) − x·F(x), vector
excessPayoffPlus(x, F) // max(0, F(x) − x·F(x)), vector
projectedPayoff(x, F) // P·F(x), projected onto TΔ
§6Numerical Integration
The library provides an adaptive Runge–Kutta–Fehlberg (RKF45) integrator
that replaces Mathematica's NDSolve. After each step, the
state is projected back onto the simplex to correct numerical drift.
Adaptive step sizing handles the stiffness that logit dynamics exhibit
near the boundary.
const traj = integrate(replicator, F,
[0.5, 0.3, 0.2], // initial state
50, // end time
{
dt: 0.01, // initial step size
tol: 1e-8, // error tolerance
adaptive: true, // adaptive stepping
outputInterval: 0.1 // sample every 0.1 time units
}
);
// traj = [{t: 0, x: [0.5, 0.3, 0.2]}, {t: 0.1, x: [...]}, ...]
For velocity fields and quiver plots, use velocityField:
const field = velocityField(smith, F, 25, 3, 0.02);
// → [{x: [...], v: [...]}, ...] on a 25-subdivision grid
§7Lyapunov Functions
For stable games (games with a negative semi-definite payoff Jacobian $DF + DF^\top \preceq 0$, i.e., the class of games where all Nash equilibria are evolutionarily stable), Sandholm defines Lyapunov functions for each major dynamic whose decrease along trajectories proves global convergence.
| Dynamic | Lyapunov function $V(\mathbf{x})$ | Export |
|---|---|---|
| Replicator | $-\prod_j x_j^{x_j^*}$ (KL divergence) | replicatorLyapunov(x*) |
| BNN | $\sum_j [\hat{F}_j]^{+2}$ | bnnLyapunov(F) |
| Smith | $\sum_k x_k \sum_j [F_j - F_k]^{+2}$ | smithLyapunov(F) |
| Logit | $\eta \log\sum_j e^{F_j/\eta} - \mathbf{x}\cdot F(\mathbf{x}) + \eta H(\mathbf{x})$ | logitLyapunov(F, η) |
| Projection | $\|\mathbf{x} - \mathbf{x}^*\|^2$ | projectionLyapunov(x*) |
const V = bnnLyapunov(F);
for (const { t, x } of trajectory) {
console.log(t, V(x)); // should decrease in stable games
}
§8Simplex Utilities
Coordinate Conversions
For the 3-simplex, baryToCart maps barycentric coordinates
$(x_1, x_2, x_3)$ to Cartesian $(p_x, p_y)$ in an equilateral triangle
with vertices $e_1 = (0.5, \tfrac{\sqrt{3}}{2})$ (top),
$e_2 = (0, 0)$ (bottom-left), $e_3 = (1, 0)$ (bottom-right).
const [px, py] = baryToCart([0.4, 0.35, 0.25]);
const bary = cartToBary(px, py); // roundtrip
Grid Generation
simplexGrid(3, 10) // 66 points on boundary + interior
interiorSimplexGrid(3, 20) // interior only, margin = 0.01
Simplex Projection
After numerical integration steps, project back onto the simplex:
const xCorrected = projectToSimplex(xDrifted);
§9API Reference
Payoff Construction
Create a payoff function for a normal-form game with payoff matrix $A$. Returns $F(\mathbf{x}) = A\mathbf{x}$.
- A : number[][]
- $n \times n$ payoff matrix
- returns : function
- Payoff function $F: \Delta \to \mathbb{R}^n$
Average payoff $\bar{F}(\mathbf{x}) = \mathbf{x} \cdot F(\mathbf{x})$.
Excess payoff vector $\hat{F}(\mathbf{x}) = F(\mathbf{x}) - \bar{F}(\mathbf{x})$.
Positive part of excess payoff: $[\hat{F}_j]^{+} = \max(0, \hat{F}_j)$.
Payoff projected onto tangent space of simplex.
Dynamics
Replicator dynamic. $\dot{x}_j = x_j \hat{F}_j(\mathbf{x})$.
Maynard Smith replicator. Requires positive average payoff.
Brown–von Neumann–Nash dynamic.
Smith pairwise comparison dynamic.
Creates a logit dynamic with noise level $\eta$.
Creates an independent logit dynamic with noise level $\eta$.
Best response dynamic (low-temperature logit approximation, default $\eta = 0.001$).
Projection dynamic onto tangent cone.
General excess payoff dynamic with configurable growth function $\tilde\sigma$. Default: $v \mapsto [\max(0,v)]^2$.
General pairwise comparison dynamic with configurable switch rate $\rho$. Default: $d \mapsto \max(d, 0)$ (Smith).
Tempered logit dynamic with tempering function $Q$.
Convex combination of two dynamics.
Integration
Integrate a dynamic forward in time using adaptive RKF45.
- dynamic : function
- Dynamic function $(x, F) \to \dot{x}$
- F : function
- Payoff function
- x0 : number[]
- Initial state on the simplex
- T : number
- End time
- opts.dt : number
- Initial step size (default 0.01)
- opts.tol : number
- Error tolerance (default 1e-8)
- opts.adaptive : boolean
- Adaptive stepping (default true)
- opts.outputInterval : number
- Output sampling interval (default 0, every step)
- opts.maxSteps : number
- Maximum steps (default 50000)
Simplex Utilities
Barycentric to Cartesian for the 3-simplex.
Cartesian to barycentric for the 3-simplex.
Uniform grid on the $n$-simplex with $k$ subdivisions.
Interior grid excluding points within margin of the boundary.
Velocity field on a simplex grid.
Nearest-point projection onto the simplex.
Indices of strategies with positive mass.
Preset Games
Built-in payoff matrices.
Construct a $2 \times 2$ payoff matrix $[[a,b],[c,d]]$.