agent_urban_planning.SimulationEngine¶
- class SimulationEngine(scenario, agent_config, engine=None, seed=None, verbose=False, clustering=None, llm_provider=None, llm_model=None, llm_temperature=None, llm_concurrency=None, price_elasticity=None, initial_damping=None, market_convergence_threshold=None, max_market_iterations=None)[source]¶
Bases:
objectOrchestrate one end-to-end simulation: config to environment to market to metrics.
Top-level entry point of the library. Wires together a scenario, an agent population, a
DecisionEngine, and a market clearer (HousingMarketfor Singapore-style scenarios,AhlfeldtMarketfor Berlin Cobb-Douglas + Fréchet spatial-equilibrium scenarios). Callingrun()produces aSimulationResultsobject containing welfare metrics, per-agent allocations, the price-history trajectory, and run metadata.- Parameters:
scenario (
ScenarioConfig) – A loadedScenarioConfigdescribing zones, transport network, and (for Berlin) Ahlfeldt structural parameters.agent_config (
AgentDistributionalConfig) – AAgentDistributionalConfigdescribing the agent population (per-zone Census distributions or explicit agent records).engine (
Optional[DecisionEngine]) – An optionalDecisionEngine. WhenNonethe engine is auto-selected based onscenario(Berlin scenarios →AhlfeldtUtilityEngine; everything else → the legacy SingaporeUtilityEngine).seed (
Optional[int]) – Optional integer seed for the agent-sampling RNG. Defaults toscenario.simulation.random_seed.verbose (
bool) – WhenTrue, prints progress and intermediate results.clustering (
Optional[ClusteringConfig]) – Optional clustering configuration for wrapping the inner engine in aClusterizedDecisionEngine(used with full-LLM mode to amortize calls across archetypes).llm_provider (
Optional[str]) – Provider name to record in run metadata ("codex-cli","claude-code","anthropic", …).llm_model (
Optional[str]) – Model name to record (e.g."haiku").llm_temperature (
Optional[float]) – Sampling temperature to record.llm_concurrency (
Optional[int]) – Concurrency setting for the async LLM client.price_elasticity (
Optional[float]) – Override of the floor-price elasticity used by the tatonnement step. Falls back to engine default.initial_damping (
Optional[float]) – Initial Walrasian dampinglambda.market_convergence_threshold (
Optional[float]) – Maximum absolute excess demand at which clearing is declared converged.max_market_iterations (
Optional[int]) – Iteration cap on the tatonnement loop.
Examples
>>> import agent_urban_planning as aup >>> scenario = aup.data.builtin.load("singapore_real_v2") >>> agents = aup.data.builtin.load_agents("singapore_real_v2") >>> sim = aup.SimulationEngine(scenario=scenario, agent_config=agents) >>> results = sim.run(policy=None) >>> sorted(results.metrics.zone_populations.values()) [0.05, 0.07, 0.13, ...]
See also
agent_urban_planning.UtilityEngine— the closed-form decision engine used by default for Berlin scenarios.agent_urban_planning.AhlfeldtMarket— the two-market tatonnement clearer used for Berlin scenarios.References
Ahlfeldt, G. M., Redding, S. J., Sturm, D. M., Wolf, N. (2015). The economics of density: Evidence from the Berlin Wall. Econometrica, 83(6), 2127-2189.
- classmethod from_paths(scenario_path, agents_path, engine=None, seed=None)[source]¶
Build a
SimulationEnginedirectly from YAML file paths.Convenience constructor that loads both the scenario and agent configurations from disk before delegating to the regular constructor. Useful for command-line scripts and notebooks.
- Parameters:
scenario_path (
str) – Path to a scenario YAML file (passed toload_scenario()).agents_path (
str) – Path to an agent-population YAML file (passed toload_agents()).engine (
Optional[DecisionEngine]) – OptionalDecisionEngineinstance.Noneselects the default engine for the scenario.
- Return type:
- Returns:
A configured
SimulationEngineready to run.
Examples
>>> import agent_urban_planning as aup >>> sim = aup.SimulationEngine.from_paths( ... "config/scenarios/singapore_real_v2.yaml", ... "config/agents/singapore_real_v2.yaml", ... seed=42, ... )
- run(policy=None, baseline=None, market_resume=None, market_checkpoint_callback=None, llm_cache_path=None)[source]¶
Run one simulation under the given policy and return full results.
Applies the policy to the base environment, runs the scenario-appropriate market clearer (HDB/private tatonnement for Singapore, Q + w joint tatonnement for Berlin), assembles per-agent results, and computes welfare metrics. When
policyisNone, the scenario’s built-in environment is used unchanged — no transit or facility investments are applied. This supports both Ahlfeldt replication runs (which have no notion of government investment) and baseline observational runs that report the pre-intervention equilibrium.- Parameters:
policy (
Optional[PolicyConfig]) – OptionalPolicyConfigdescribing transit and facility investments to apply before clearing.Noneruns the scenario as observed.baseline (
Optional[SimulationResults]) – Optional pre-computedSimulationResultsfrom a prior run. When provided, each agent’sutility_vs_baselinefield is filled in with the difference between their realized utility here and inbaseline.market_resume (
Optional[dict]) – Optional checkpoint state from a prior interrupted run, returned bymarket_checkpoint_callbackin a previous invocation. Lets long LLM runs survive process restarts.market_checkpoint_callback – Optional callable invoked with a JSON-serializable checkpoint dict at every market iteration. Receives a
dictpayload that can be passed back asmarket_resumeto resume.llm_cache_path (
Optional[str]) – Optional path to a disk-backed LLM cache. Reuses cached LLM completions across invocations.
- Return type:
- Returns:
A
SimulationResultsobject aggregating welfare metrics, per-agent allocations, market price history, and run metadata.
Examples
>>> import agent_urban_planning as aup >>> sim = aup.SimulationEngine(scenario, agent_config) >>> baseline = sim.run() >>> with_policy = sim.run(policy, baseline=baseline) >>> with_policy.metrics.avg_utility - baseline.metrics.avg_utility 0.04
- compare_policies(policies)[source]¶
Run one simulation per policy and return results keyed by policy name.
Iterates over a list of policies, running
run()for each and threading the first policy’s result through as the baseline forutility_vs_baselinecomputations on subsequent policies. Useful for cross-policy welfare comparisons in a single call.- Parameters:
policies (
list[PolicyConfig]) – List ofPolicyConfigobjects. The first policy in the list is treated as the comparison baseline.- Return type:
- Returns:
A dict mapping each
policy.nameto itsSimulationResults.
Examples
>>> import agent_urban_planning as aup >>> sim = aup.SimulationEngine(scenario, agent_config) >>> results = sim.compare_policies([baseline_policy, alt_policy]) >>> results["alt"].metrics.avg_utility 2.13
- budget_sweep(base_policy, transit_shares)[source]¶
Sweep over transit budget share and return results for each share level.
Generates a series of policies that interpolate between facility-only (
share=0) and transit-only (share=1) allocations of the policy’s total budget, and runs the simulation for each. At every share level transit investments scale travel time improvements proportionally (50% budget yields halfway between old and new time), while facility investments scale capacity and quality proportionally.- Parameters:
base_policy (
PolicyConfig) – A baselinePolicyConfigwhosetransit_investmentsandfacility_investmentsare used as the upper-budget reference. The policy’stotal_budgetdefines the budget envelope swept.transit_shares (
list[float]) – Iterable of fractions in[0, 1]indicating what share of the total budget goes to transit at each point of the sweep. The complementary1 - sharegoes to facilities.
- Return type:
- Returns:
List of
(share, SimulationResults)tuples in the order oftransit_shares. Use these to build budget-allocation tradeoff plots.
Examples
>>> import agent_urban_planning as aup >>> # sim = aup.SimulationEngine(scenario, agent_config) >>> # sweep = sim.budget_sweep(policy, [0.0, 0.5, 1.0]) >>> # shares = [s for s, _ in sweep] >>> # avg_utilities = [r.metrics.avg_utility for _, r in sweep]