Custom decision engine¶
Write your own decision engine by subclassing
agent_urban_planning.DecisionEngine. Useful for research extensions
that don’t fit the V1-V5 templates.
The protocol¶
A decision engine implements a single method:
Given a batch of agents and current market state (zones, prices), return
a LocationChoice for each agent.
Minimal example¶
import agent_urban_planning as aup
from agent_urban_planning import DecisionEngine, LocationChoice
class RandomEngine(DecisionEngine):
"""Picks a random zone for each agent. Useful as a control baseline."""
def __init__(self, seed=None):
import numpy as np
self.rng = np.random.default_rng(seed)
def decide_batch(self, agents, environment, zone_options, prices):
choices = []
for agent in agents:
zone = self.rng.choice(zone_options)
choices.append(LocationChoice(
residence=zone,
workplace=zone, # toy: same zone for both
utility=0.0,
zone_utilities={},
))
return choices
# Use it just like any built-in engine
engine = RandomEngine(seed=42)
sim = aup.SimulationEngine(scenario=sc, agent_config=ag, engine=engine)
results = sim.run()
Tips¶
For LLM-based variants, prefer extending
LLMDecisionEngine(or its internal helpers) over subclassingDecisionEnginedirectly — the built-in async batching and caching layer saves significant work.For closed-form variants, look at the source of
agent_urban_planning.UtilityEnginefor reference patterns (Cobb-Douglas + Fréchet utility, softmax vs argmax dispatch).Custom engines can be passed to
SimulationEnginedirectly; no registration needed.
Next steps¶
Full LLM hierarchical engine (V5) — the V5 LLM-ABM pattern in depth.
Berlin V1-V5 replication — reproduce the paper’s results.