Environments & Schelling

AMBER ships three environment types:

Agent attributes stay on the columnar table (model.agents / agents_df). The environment owns placement (where each agent sits).

Grid occupancy helpers (0.4.1)

On GridEnvironment:

Helper

Role

add_agent_from_id(id, pos) / add_agent(agent, pos)

Place or move an agent onto a cell

remove_agent_from_pos(pos)

Clear a cell; returns the agent id that left

get_agent_at_pos(pos)

Occupant id or None

get_random_empty_cell()

Uniform empty cell (uses model.rng when present)

get_empty_cells_in_radius(pos, radius)

Empty Moore neighbourhood

get_neighbors(pos, radius=1)

Moore ring of cells (alias of distance-based neighbours)

empty_positions()

All unoccupied cells

Positions are stored on the frame as grid_position (object column of tuples). Prefer these helpers over hand-rolled occupancy dicts.

Canonical Schelling pattern

Full runnable code: examples/schelling_vectorized.py.

  1. Create the grid and population

    self.env = am.GridEnvironment(self, size=self.p.grid_size)
    self.agents = am.AgentList(self, self.p.n, SchellingAgent)
    self.agents.set(group=self.rng.integers(0, 2, size=self.p.n))
    
  2. Place agents once (random unique cells)

    cells = list(self.env.positions)
    self.rng.shuffle(cells)
    for agent, pos in zip(self.agents, cells[: self.p.n]):
        self.env.add_agent_from_id(agent.id, pos)
    
  3. Each step: happiness (per-agent) then filter + relocate

    Neighbourhood inspection is irregular → agent methods are fine. Selecting who moves is vectorized:

    for a in self.agents:
        a.update_happiness(self.p.want_similar)
    for aid in self.agents.where(~self.agents.happy).ids.to_list():
        self.agents.by_id(aid).relocate()  # uses get_random_empty_cell
    
  4. Report with model_reporters

    model_reporters = {
        "happy_frac": lambda m: float(m.agents.happy.sum()) / max(len(m.agents), 1),
    }
    

When not to force full vectorization

Moore-neighbourhood Schelling happiness is a classic “per-agent is OK” workload: each agent’s local view is small and sparse. AMBER still wins on metrics, placement bookkeeping, and filtered selection (where / set / scatter_add). Dense continuous interactions are a better fit for the tensor lane (borrow / commit) or ArrayKernelModel — see Going faster (lanes).