Agent-based urban planning simulator with closed-form, hybrid, and full-LLM decision engines
Project description
agent-urban-planning
Open-source Python library for agent-based urban planning simulation with closed-form, hybrid, and full-LLM decision engines. Companion to the NeurIPS Datasets & Benchmarks 2026 paper:
TBD — paper title
Workflow
The simulator is organized as a five-stage pipeline. (1) Environment — a city is described by zones, transportation graphs, land-use parameters, housing, amenities, and wages; an optional policy shock perturbs this state (e.g., a new rapid-transit line). (2) Agents — a population of heterogeneous household types (young professionals, families with kids, retirees, …) with demographic and preference attributes. (3) Decision — each agent picks a (residence, workplace) pair via one of three swappable engines: a closed-form UtilityEngine (V1–V3), a HybridDecisionEngine that elicits LLM-side preferences and resolves choice in closed form (V4), or a full LLMDecisionEngine that consults a language model end-to-end (V5 — the paper headline). (4) Market & Allocation — the macro layer aggregates choices, clears the housing/labor markets via tâtonnement (rents, wages, congestion), and allocates agents to zones. (5) Equilibrium — a fixed-point solver iterates 1→4 until prices and choices stop moving. Output is a bundle of welfare metrics, choropleth maps, and per-agent traces with a recorded seed for replay.
What it is
A modular ABM framework for simulating spatial-equilibrium urban policies. Five reference decision-engine variants ship as first-class API classes, configurable via kwargs:
| Paper variant | One-line description | API call |
|---|---|---|
| V1 Baseline-softmax | Closed-form Cobb-Douglas + Fréchet softmax | aup.UtilityEngine(mode="softmax") |
| V2 Baseline-ABM argmax | ABM with Fréchet idiosyncratic shocks | aup.UtilityEngine(mode="argmax", noise="frechet") |
| V3 Normal-ABM argmax | ABM with Gaussian shocks | aup.UtilityEngine(mode="argmax", noise="normal") |
| V4 Hybrid-ABM | LLM-elicited preferences + closed-form choice | aup.HybridDecisionEngine(elicitor=...) |
| V5 LLM-ABM (paper headline) | Full LLM-as-decision-maker, score-all-96 | aup.LLMDecisionEngine(response_format="score_all", rebalance_instruction=True, stage2_top_k_residences=10) |
Quickstart
git clone https://github.com/MASE-eLab/agent-urban-planning.git
cd agent-urban-planning
pip install -e ".[llm,plot,berlin]"
python examples/01_quickstart.py
The quickstart script walks through the five-stage pipeline (Environment → Agents → Decision → Market → Equilibrium) and runs a baseline simulation with V1 (closed-form softmax). Wall-clock <1 minute, no LLM credits required.
import agent_urban_planning as aup
from agent_urban_planning.data.loaders import load_scenario, load_agents
# Stage 1+2 — Environment + Agents
scenario = load_scenario("data/berlin/scenarios/berlin_2006_ortsteile.yaml")
agent_config = load_agents("data/berlin/agents/berlin_ortsteile_richer_10k.yaml")
# Stage 3 — Decision engine (V1; swap to V2/V3/V4/V5 with one constructor change)
engine = aup.UtilityEngine(scenario.ahlfeldt_params, mode="softmax")
# Stage 4 — SimulationEngine + market clearing (Stage 5 = result.metrics)
sim = aup.SimulationEngine(scenario=scenario, agent_config=agent_config,
engine=engine, seed=42)
result = sim.run(policy=None)
print(result.metrics.avg_utility, result.metrics.gini_coefficient)
The other 4 paper variants (V2, V3, V4, V5) swap only the decision engine — see the variant table below.
Berlin replication results
We benchmark all five variants on a 96-zone Berlin scenario (Ahlfeldt et al. 2015 calibration) under a hypothetical East–West Express rapid-transit shock (4 stations, 5-min between adjacent).
Choropleth: log-change in housing prices Δlog Q under the shock
The structural family (V1, V2, V4) produces a visibly focal response — price effects concentrate at the four station catchments. LLM-ABM (V5) instead spreads the response broadly across the city, picking up the gradient-flattening + agglomeration effects that exogenous-productivity models forbid.
Choropleth: log-change in wages Δlog w under the shock
The wage maps tell the same story at the workplace side: structural variants forbid the agglomeration channel by construction (productivity A_i is exogenous), while LLM-ABM produces a coherent compensating-differential pattern via its training-data priors on urban economics.
Cross-variant moments
Distribution-shape moments across the 96 zones (baseline → shock):
| Variant | μ ΔlogQ | σ ΔlogQ | p95 |ΔlogQ| | ΔY% | Δ Q̄ | Δ⟨U⟩ |
|---|---|---|---|---|---|---|
| V1 Baseline-softmax | +0.0004 | 0.0070 | 0.0083 | +0.0007 | +0.0008 | −0.0032 |
| V2 Baseline-ABM argmax | +0.0004 | 0.0071 | 0.0080 | −0.0000 | +0.0007 | −0.0031 |
| V3 Normal-ABM argmax | +0.0004 | 0.0042 | 0.0048 | +0.0132 | +0.0001 | −0.0027 |
| V4 Hybrid-ABM | +0.0004 | 0.0068 | 0.0077 | +0.0002 | +0.0007 | −0.0031 |
| V5 LLM-ABM | +0.0016 | 0.0083 | 0.0172 | +0.0299 | +0.0111 | −0.0056 |
Full table + interpretation: figures/comparison_moments.csv.
Three takeaways.
- The structural family agrees. V1, V2, V4 (and V3 modulo Gaussian-vs-Fréchet tail differences) cluster tightly on
μ ≈ +0.0004,σ ≈ 0.007,Δ⟨U⟩ ≈ −0.0031. They share the Cobb-Douglas + Fréchet architecture; the noise model and clustering wash out at zone-level moments. Architecturally distinct simulators give the same answer to a small policy shock — a useful sanity check for the structural family. - LLM-ABM is qualitatively different. Its mean log-change in housing price is 4× the structural consensus; spread (σ, p95) is 1.2–2× larger; mean rent change is 14× larger; aggregate productivity gain (+3%) is the only sizable positive value. Welfare drop is ~2× the structural family's.
- All five agree on welfare direction. Δ⟨U⟩ < 0 across the board under the Baseline-softmax welfare ruler — the shock is a (small) net welfare loss. The mechanisms differ: structural variants attribute the drop to commute/wage compensation; LLM-ABM picks up the same direction at ~2× magnitude because its equilibrium reroutes more agents to lower-utility configurations under the structural ruler's lens.
The full paper §6 discusses why LLM-ABM diverges (gradient-flattening + agglomeration, both forbidden in exogenous-productivity structural models).
Reproduce the paper results
The paper's V1–V5 Berlin runs (baseline + East-West Express shock) reproduce end-to-end from a git clone. Pick a path:
git clone https://github.com/MASE-eLab/agent-urban-planning.git
cd agent-urban-planning
pip install -e ".[llm,plot,berlin]"
# Tier 1+2 — quickstart pipeline walkthrough (<1 min, V1 baseline only)
python examples/01_quickstart.py
# Tier 3 — V1/V2/V3 closed-form + ABM variants (~3 hr each, no LLM credits)
python examples/02_berlin_replication/run_v1_softmax.py
python examples/02_berlin_replication/run_v2_argmax_frechet.py
python examples/02_berlin_replication/run_v3_argmax_normal.py
# Tier 3d — V4 hybrid (~3 hr, ~$5 in LLM credits)
python examples/02_berlin_replication/run_v4_hybrid.py --llm-provider codex-cli
# Tier 4 — V5 LLM-ABM, paper headline. Two paths:
# Path A: replay the bundled cache (~5–10 min, no credits, recommended for reviewers)
curl -L -o llm_cache_v5.tar.gz \
https://github.com/MASE-eLab/agent-urban-planning/releases/download/v0.1.0-data/llm_cache_v5.tar.gz
tar -xzf llm_cache_v5.tar.gz -C data/berlin/
python examples/02_berlin_replication/run_v5_score_all.py --no-llm
# Path B: rerun from scratch with live LLM (~10 hr, ~$30–50 credits)
python examples/02_berlin_replication/run_v5_score_all.py --llm-provider codex-cli
| Tier | What | Wall-clock | LLM credits |
|---|---|---|---|
| 1 | pip install + import agent_urban_planning |
<30 s | No |
| 2 | examples/01_quickstart.py |
<10 s | No |
| 3a-c | V1, V2, V3 Berlin baseline + shock | ~3 hr each | No |
| 3d | V4 Berlin baseline + shock (hybrid) | ~3 hr | ~$5 |
| 4 | V5 Berlin baseline + shock (cache replay) | ~5–10 min | No |
| 4 | V5 Berlin baseline + shock (live LLM) | ~10 hr | ~$30–50 |
Detailed instructions (real-run params, smoke testing, all 6 LLM provider options, cross-variant analysis snippets, numerical-reproducibility expectations) are in examples/02_berlin_replication/README.md. The full reproducibility tier ladder lives at docs/reproducibility/berlin_v1_v5.md (also rendered at https://agent-urban-planning.readthedocs.io/en/latest/reproducibility/berlin_v1_v5.html).
Documentation
Comprehensive API reference, tutorials, and concept guides:
→ https://agent-urban-planning.readthedocs.io
Repository layout
agent-urban-planning/
├── src/agent_urban_planning/ Library source
│ ├── core/ Environment / Agents / Market / Engine / Metrics
│ ├── decisions/ UtilityEngine / HybridDecisionEngine / LLMDecisionEngine
│ ├── data/ Loaders + builtin scenarios
│ ├── analysis/ Welfare + plotting
│ ├── llm/ LLM client wrappers
│ └── research/ Paper-specific helpers (Berlin)
├── examples/
│ ├── 01_quickstart.py Full V1 pipeline walkthrough (<1 min)
│ └── 02_berlin_replication/ End-to-end V1–V5 Berlin runs
├── data/ Bundled Berlin + Singapore data (git, not PyPI sdist)
├── docs/ Sphinx documentation source
├── figures/ README assets (workflow, choropleths, tables)
└── pyproject.toml
Citation
If you use this software in your research, please cite the accompanying paper (see CITATION.cff).
License
MIT License © 2026 The agent-urban-planning Authors
Acknowledgements
The Berlin replication is built on the Ahlfeldt, Redding, Sturm, Wolf (2015) "The Economics of Density: Evidence from the Berlin Wall" Econometrica 83(6) data and methodology. We are grateful for the authors' public release of the replication package; the data files in data/berlin/ and data/shapefiles/ derive from that release. See data/README.md for full attribution.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file agent_urban_planning-0.1.0.tar.gz.
File metadata
- Download URL: agent_urban_planning-0.1.0.tar.gz
- Upload date:
- Size: 179.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8ddf1b1e81d59f982fb1185435add5dd3f6f6131499e7abcc36e4510e1ffd804
|
|
| MD5 |
b8e041ee7016c11d0a15746c376547f5
|
|
| BLAKE2b-256 |
5330be0e5893c8831b20de83a060b24befbb8ad0fbed8c6875055372fd9e1b0b
|
File details
Details for the file agent_urban_planning-0.1.0-py3-none-any.whl.
File metadata
- Download URL: agent_urban_planning-0.1.0-py3-none-any.whl
- Upload date:
- Size: 204.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
38a14314fb24aec7dd2e4055d29117ed6a3dfe43bdccb5699f66f291df1761ec
|
|
| MD5 |
a97f1540b1dd1ae3bbf3552c0c7de35d
|
|
| BLAKE2b-256 |
d2fde195e45a60e5df4f03da8f98e5c8b0b0fdf48f7552a410bea7e051a27957
|