Predator-Prey (Lotka–Volterra, SciPy)
A predator-prey simulation using the Lotka-Volterra equations.
Level:Intermediate
Dynamic Behavior Patterns
Explore common system behaviors: exponential growth, goal-seeking decay, overshoot-and-collapse, and S-curve saturation.
Explore Dynamic Behavior Patternssimulation.py
Predator and prey in the Lotka–Volterra model
Here we let SciPy's solve_ivp
integrate the predator–prey equations for us. It's a different angle on the classic chase between hungry predators and their food supply.
from tys import probe, progress
Integrate the Lotka–Volterra equations over time.
def simulate(cfg: dict):
import numpy as np
from scipy.integrate import solve_ivp
Parameters
x0 = float(cfg["initial_prey"])
y0 = float(cfg["initial_predator"])
alpha = cfg["alpha"]
beta = cfg["beta"]
delta = cfg["delta"]
gamma = cfg["gamma"]
sim_time = float(cfg["sim_time"])
dt = float(cfg.get("time_step", 0.01))
assert all(p > 0 for p in (alpha, beta, delta, gamma)), "Rates must be positive"
assert dt > 0 and sim_time > 0, "Time parameters must be positive"
Right-hand side of the ODE system
def lv(_, z):
x, y = z
return [
alpha * x - beta * x * y, # dx/dt
delta * x * y - gamma * y # dy/dt
]
Build a t_eval that is inside [0, sim_time]
n_steps = int(round(sim_time / dt))
t_eval = np.linspace(0.0, sim_time, n_steps + 1)
sol = solve_ivp(
lv, (0.0, sim_time), [x0, y0],
t_eval=t_eval, method="RK45", max_step=dt
)
prey_series = np.maximum(sol.y[0], 0.0) # clip tiny negatives
predator_series = np.maximum(sol.y[1], 0.0)
for t, x, y in zip(sol.t, prey_series, predator_series):
probe("prey", t, x)
probe("predator", t, y)
progress(100)
return {
"final_prey": float(prey_series[-1]),
"final_predator": float(predator_series[-1]),
}
def requirements():
SciPy comes with Pyodide, so only declare it as builtin
return {
"builtin": ["micropip", "scipy", "pyyaml"]
}
Default.yaml
initial_prey: 10
initial_predator: 5
alpha: 1.5
beta: 1.0
delta: 1.0
gamma: 3.0
sim_time: 20
time_step: 0.01
Charts (Default)
Final Results (Default)
Metric | Value |
---|---|
final_prey | 1.99 |
final_predator | 0.02 |
FAQ
- How are the predator-prey equations solved?
- SciPy's solve_ivp integrates the differential system using RK45 and returns values at each evaluation step.
- Why clamp negative populations?
- The integrator can introduce tiny negatives, so numpy.maximum ensures prey and predator counts stay non-negative.
- How are evaluation times chosen?
- t_eval is a linspace from 0 to sim_time with step dt so charts align with the solver output.