pyLEAFS.fields module

Fields living on the shared grid.

A field is anything spatially distributed that the simulation steps in time and that agents can sample: the resource field here, and later a pheromone field, chemical gradients, and so on. The simulation holds a list of fields and steps each one, so new field types are add-ons rather than rewrites.

Field is the structural protocol every field follows. ResourceField is the v1 implementation: a replenishing Poisson point process, ported from the MATLAB Region/Environment classes but stored as one flat struct-of-arrays buffer instead of per-region objects.

class pyLEAFS.fields.Field(*args, **kwargs)[source]

Bases: Protocol

Structural protocol for a steppable, samplable field.

A field references a Grid and implements the three verbs the simulation and agents use. deposit is optional in spirit (the resource field does not accept deposits), but the signature is reserved so that pheromone-style fields slot in without changing call sites.

deposit(pos, amount) None[source]

Add amount to the field at positions pos (if supported).

sample(pos)[source]

Return whatever an agent senses of the field at positions pos.

step(rng) None[source]

Advance the field one timestep using random source rng.

class pyLEAFS.fields.ResourceField(grid, Gamma, gamma, epsilon, dt, capacity_factor=4.0)[source]

Bases: object

A replenishing Poisson resource field on the shared grid.

Resources are points scattered through the domain. Each timestep, within every region, new resources are born and existing ones die:

\[\begin{split}k_\mathrm{grow} &\sim \mathrm{Poisson}(\Gamma L^D\,dt / \epsilon) \\ k_\mathrm{decay} &\sim \mathrm{Binomial}(N,\, 1 - e^{-\gamma\,dt})\end{split}\]

giving an agent-free equilibrium of \(N_\mathrm{eq} = \Gamma L^D / (\epsilon\gamma)\) resources per region. Positions are stored in one flat (capacity, D) buffer; each region owns a contiguous slot range and an active count, so births append, deaths swap-delete, and harvest is O(1) — the struct-of-arrays equivalent of the MATLAB active-prefix Region.

Parameters:
  • grid (Grid) – Shared spatial grid.

  • Gamma (float) – Resource birth rate per unit volume, \(\Gamma\).

  • gamma (float) – Resource decay rate, \(\gamma\).

  • epsilon (float) – Energy yield per resource, \(\epsilon\).

  • dt (float) – Timestep.

  • capacity_factor (float, optional) – Per-region buffer is max(20, ceil(capacity_factor * N_eq)) slots (default 4.0, matching the MATLAB Region).

N_eq

Agent-free equilibrium count per region.

Type:

float

count

Current number of active resources in each region.

Type:

ndarray of int, shape (n_regions,)

positions

Physical positions of all active resources (rebuilt on demand by all_positions()).

Type:

ndarray, shape (n_active, D)

Examples

>>> g = Grid((5, 5), L=10.0)
>>> rf = ResourceField(g, Gamma=0.001, gamma=0.1, epsilon=0.1, dt=0.025)
>>> round(rf.N_eq)
10
all_positions()[source]

Return physical positions of every active resource, (M, D).

deposit(pos, amount)[source]

Resource fields do not accept deposits.

harvest(region_id, slot)[source]

Remove one resource by (region_id, slot). O(1) swap-delete.

Returns:

True if a resource was removed; False if the slot was already empty (e.g. another agent harvested it first this step).

Return type:

bool

neighbourhood(pos)[source]

Resources in the Moore neighbourhood of a single query position.

Parameters:

pos (ndarray, shape (D,)) – Physical query position.

Returns:

  • disp (ndarray, shape (m, D)) – Minimum-image displacement from pos to each nearby resource.

  • region_ids (ndarray of int, shape (m,)) – Owning region id for each resource (for harvest()).

  • slots (ndarray of int, shape (m,)) – Within-region active index for each resource (for harvest()).

sample(pos)[source]

Nearest-resource displacement for each query position.

Parameters:

pos (ndarray, shape (n, D)) – Physical query positions.

Returns:

  • disp (ndarray, shape (n, D)) – Displacement to the nearest resource (NaN row if none in range).

  • dist2 (ndarray, shape (n,)) – Squared distance to that resource (inf if none).

Notes

Searches the Moore neighbourhood of each query position. This is the sensing primitive the greedy population uses; it is a per-agent loop for now and is a natural @njit target once the core is validated.

seed(rng, n0=None)[source]

Populate every region near equilibrium.

Parameters:
  • rng (numpy.random.Generator) – Random source.

  • n0 (int, optional) – Exact initial count per region. If omitted, each region draws Poisson(N_eq).

step(rng)[source]

Advance resource dynamics one timestep.

Birth and death counts for all regions are drawn in two vectorized RNG calls; grow/decay order is randomised per region to avoid bias.

total()[source]

Total active resource count across the domain.