Metadata-Version: 2.4
Name: agent-urban-planning
Version: 0.1.0
Summary: Agent-based urban planning simulator with closed-form, hybrid, and full-LLM decision engines
Author: TBD
License: MIT
Project-URL: Homepage, https://github.com/MASE-eLab/agent-urban-planning
Project-URL: Documentation, https://agent-urban-planning.readthedocs.io
Project-URL: Repository, https://github.com/MASE-eLab/agent-urban-planning
Project-URL: Issues, https://github.com/MASE-eLab/agent-urban-planning/issues
Keywords: agent-based-modeling,urban-planning,spatial-equilibrium,large-language-models,ahlfeldt-2015,berlin
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering
Classifier: Topic :: Scientific/Engineering :: Information Analysis
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.24
Requires-Dist: scipy>=1.10
Requires-Dist: pyyaml>=6.0
Requires-Dist: matplotlib>=3.7
Requires-Dist: python-dotenv>=1.0
Requires-Dist: requests>=2.28
Provides-Extra: llm
Requires-Dist: anthropic>=0.30; extra == "llm"
Requires-Dist: openai>=1.0; extra == "llm"
Requires-Dist: aiohttp>=3.9; extra == "llm"
Requires-Dist: scikit-learn>=1.3; extra == "llm"
Provides-Extra: plot
Requires-Dist: geopandas>=0.14; extra == "plot"
Provides-Extra: berlin
Requires-Dist: h5py>=3.8; extra == "berlin"
Requires-Dist: mat73>=0.60; extra == "berlin"
Provides-Extra: docs
Requires-Dist: sphinx>=7.0; extra == "docs"
Requires-Dist: furo>=2023.0; extra == "docs"
Requires-Dist: sphinx-autodoc-typehints>=1.24; extra == "docs"
Requires-Dist: myst-parser>=2.0; extra == "docs"
Requires-Dist: sphinx-design>=0.5; extra == "docs"
Requires-Dist: sphinx-copybutton>=0.5; extra == "docs"
Requires-Dist: sphinxcontrib-mermaid>=0.9; extra == "docs"
Requires-Dist: sphinx-codeautolink>=0.15; extra == "docs"
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0; extra == "dev"
Requires-Dist: ruff>=0.1; extra == "dev"
Requires-Dist: pandas>=2.0; extra == "dev"
Requires-Dist: build>=1.0; extra == "dev"
Requires-Dist: twine>=4.0; extra == "dev"
Provides-Extra: all
Requires-Dist: agent-urban-planning[berlin,dev,docs,llm,plot]; extra == "all"
Dynamic: license-file

# agent-urban-planning

[![PyPI](https://img.shields.io/pypi/v/agent-urban-planning.svg)](https://pypi.org/project/agent-urban-planning/)
[![Documentation Status](https://readthedocs.org/projects/agent-urban-planning/badge/?version=latest)](https://agent-urban-planning.readthedocs.io)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org)

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

![workflow](figures/workflow.png)

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

```bash
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.

```python
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

![Berlin Δlog Q](figures/berlin_dlogQ.png)

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

![Berlin Δlog w](figures/berlin_dlogW.png)

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`](figures/comparison_moments.csv).

**Three takeaways.**

1. **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.**
2. **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.
3. **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:

```bash
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`](examples/02_berlin_replication/README.md). The full reproducibility tier ladder lives at [`docs/reproducibility/berlin_v1_v5.md`](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`](CITATION.cff)).

## License

[MIT License](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.
