Getting started

Installation

cd pyLEAFS
pip install -e .

pyLEAFS requires Python 3.8+; numpy and matplotlib are installed automatically.

A first simulation

The forager() factory builds the v1 world — one resource field, one greedy-forager population — with the parameters of the forager applet:

from pyLEAFS import Simulation

sim = Simulation.forager(seed=0)
sim.run(1000)
print(sim.populations[0].count, "agents alive")
print(sim.fields[0].total(), "resources")

run stops early if the population goes extinct; pass stop_on_extinction=False to advance a fixed number of steps regardless.

Watching it live

The Viewer opens an interactive matplotlib window:

from pyLEAFS import Simulation, Viewer

Viewer(Simulation.forager(seed=0)).play()

Controls:

spacebar

Pause / resume.

click empty space

Add a new agent at the cursor (works paused or running).

click on an agent

Select it: a ring appears, the side panel shows its state (fuel, age, harvested count, offspring, heading), and its recent trajectory is drawn as a trail.

Clicking empty space while an agent is selected deselects it and adds an agent there. The viewer targets two-dimensional worlds.

The homogeneity knob

The environment is controlled by a single dimensionless parameter, the homogeneity \(\Xi\). It sets how patchy or uniform the resource field is by fixing the energy per resource:

sparse = Simulation.forager(seed=0, Xi=0.3)   # patchy: few, rich resources
dense  = Simulation.forager(seed=0, Xi=1.0)   # uniform: many, lean resources

print(sparse.fields[0].N_eq)   # equilibrium resources per region
print(dense.fields[0].N_eq)

Larger \(\Xi\) means a more homogeneous world with more, lower-energy resources. See The model for the definition and its role in the model.

Two or three dimensions

The core is dimension-agnostic. The length of the grid shape selects the dimension:

flat = Simulation.forager(seed=0, shape=(10, 10))      # 2d
solid = Simulation.forager(seed=0, shape=(10, 10, 10))  # 3d

solid.run(500)

The Viewer renders 2d worlds; 3d runs are headless for now.

Reproducibility

Every stochastic part of the simulation draws from a single numpy.random.Generator threaded through from the seed. Two runs built with the same seed produce identical histories:

a = Simulation.forager(seed=7); a.run(300, stop_on_extinction=False)
b = Simulation.forager(seed=7); b.run(300, stop_on_extinction=False)
assert a.populations[0].count == b.populations[0].count

Tests

pip install pytest
pytest