agent_urban_planning.AhlfeldtMarket

class AhlfeldtMarket(ahlfeldt_params, initial_damping=0.3, convergence_threshold=0.01, stall_threshold=1e-06, stall_window=10, max_iterations=1000, max_price_change_pct=0.5, max_price_change_pct_wage=None, verbose=False)[source]

Bases: HousingMarket

Two-market tatonnement clearing Q (residential floor) AND w (wages).

For Berlin / Ahlfeldt scenarios only. Selected by SimulationEngine when scenario.ahlfeldt_params is not None. Supersedes the HDB/private segmentation in HousingMarket.clear; Berlin has one unified floor market per zone with the commercial / residential split fixed from observed 2006 values (Decision: fixed θ_i).

Per-market elasticities are computed from the Ahlfeldt parameters as eta_floor = (1 - beta) * epsilon and eta_wage = 1 / (1 - alpha) + epsilon, and can be overridden via scenario-level eta_floor_override / eta_wage_override fields.

Parameters:
  • ahlfeldt_params – Structural parameters loaded from the scenario YAML. Drives elasticities, agglomeration toggle, and the chosen clearing method.

  • initial_damping (float) – Initial Walrasian step damping lambda. Adapts each iteration. Defaults to 0.3.

  • convergence_threshold (float) – Maximum absolute excess demand at which both markets are declared converged. Defaults to 0.01.

  • stall_threshold (float) – Minimum iter-to-iter change in residual to avoid being flagged as stalled. Defaults to 1e-6.

  • stall_window (int) – Consecutive stalled iterations before damping is boosted (and ultimately, the run terminates). Defaults to 10.

  • max_iterations (int) – Iteration budget. Defaults to 1000.

  • max_price_change_pct (float) – Per-iteration cap on relative price moves for floor prices. Defaults to 0.5 (50%).

  • max_price_change_pct_wage (float | None) – Per-iteration wage cap. Defaults to max_price_change_pct if None.

  • verbose (bool) – When True, prints per-iteration diagnostics.

Examples

>>> import agent_urban_planning as aup
>>> # Typically obtained from the SimulationEngine, not constructed
>>> # directly. See SimulationEngine.run() for end-to-end usage.
>>> # market = aup.AhlfeldtMarket(scenario.ahlfeldt_params)
>>> # result = market.clear(population, environment, engine)

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.

set_iteration_callback(fn)[source]

Install or clear a per-iteration progress callback.

Called after every tatonnement iteration with fn(iter_idx, max_floor_excess, max_labor_excess, elapsed_seconds). Exceptions raised by the callback are suppressed so progress rendering cannot break market clearing. Pass None to clear a previously installed callback.

Parameters:

fn (Optional[Callable[[int, float, float, float], None]]) – Callable accepting (iter_idx, max_floor_excess, max_labor_excess, elapsed_seconds), or None to disable.

Return type:

None

Returns:

None.

Examples

>>> import agent_urban_planning as aup
>>> # market = aup.AhlfeldtMarket(scenario.ahlfeldt_params)
>>> # market.set_iteration_callback(lambda i, f, l, t: print(i, f, l, t))
clear(population, environment, engine, resume_state=None, checkpoint_callback=None, cache_path=None)[source]

Run joint Q/w tatonnement and return an Ahlfeldt MarketResult.

Iterates: inject current wages into the engine, run decide_batch to get joint (residence, workplace) choices, compute floor and labor excess demand, optionally update endogenous productivity / amenity from current density, and update prices either via tatonnement or the closed-form FOC update (when clearing_method='foc_direct' and endogenous_land_use=True). Returns at convergence or after the iteration cap.

resume_state and checkpoint_callback are accepted for protocol compatibility with HousingMarket but ignored — Ahlfeldt runs are typically short enough (<= 200 iterations at Ortsteile resolution) that in-process execution is sufficient.

Parameters:
  • population (AgentPopulation) – The AgentPopulation to clear over.

  • environment (Environment) – The Environment carrying zones and their Ahlfeldt fundamentals.

  • engine (DecisionEngine) – A decision engine implementing decide_batch. Engines exposing set_current_wages / set_current_productivity / set_current_amenity receive per-iteration injection of those state vectors.

  • resume_state (Optional[dict]) – Ignored. Accepted for protocol compatibility.

  • checkpoint_callback (Optional[Callable[[dict], None]]) – Ignored. Accepted for protocol compatibility.

  • cache_path (Optional[str]) – Optional path to a disk-backed LLM cache passed to engines that accept caches via set_cache.

Return type:

MarketResult

Returns:

A MarketResult carrying equilibrium prices Q, wages, joint allocations, convergence flags for each market, and the full per-iteration history.

Examples

>>> import agent_urban_planning as aup
>>> # market = aup.AhlfeldtMarket(scenario.ahlfeldt_params)
>>> # result = market.clear(population, environment, engine)
>>> # max(result.prices.values())
7.42