🇬🇧 English · 🇪🇸 Español
Equilibrio general espacial cuantitativo
puremacro.spatial implementa el modelo continuo de equilibrio espacial geográfico con movilidad laboral, externalidades de aglomeración y fuerzas de congestión desarrollado por Allen y Arkolakis (2014, Quarterly Journal of Economics).
El modelo cumple de forma estricta el contrato de ejecución en Python puro / Pyodide (cero dependencias en C++, basado exclusivamente en NumPy/SciPy/Pandas/Matplotlib), permitiendo evaluaciones contrafactuales de política económica —tales como inversiones regionales en infraestructura de transporte y perturbaciones ambientales localizadas.
1. Marco teórico y algorítmico
1.1 Modelo gravitacional espacial geográfico con movilidad laboral de Allen-Arkolakis (2014)
Allen y Arkolakis (2014) formulan un modelo de equilibrio general espacial continuo sobre \(N\) regiones geográficas donde los trabajadores migran libremente hasta igualar el nivel de utilidad real en todas partes.
Flujos comerciales y acceso a mercados
El comercio bilateral entre el origen \(i\) y el destino \(j\) obedece a una estructura gravitacional:
donde \(\tau_{ij} \ge 1\) representan costos comerciales tipo iceberg (\(\tau_{ii} = 1\)), \(\theta = \sigma - 1\) es la elasticidad comercial y \(P_j\) es el índice de acceso a mercado de los consumidores (Consumer Market Access):
El acceso a mercado de las empresas (Firm Market Access, \(\text{FMA}_i\)) captura la demanda agregada de exportación:
El vaciado del mercado de bienes iguala el ingreso laboral regional con el valor total de las ventas hacia todos los destinos:
1.2 Aglomeración, congestión y equilibrio espacial
La utilidad de un trabajador representativo en la localización \(i\) depende de los salarios reales, amenidades locales \(a_i\) y la población endógena \(L_i\):
La productividad y las amenidades endógenas incorporan efectos espaciales de desbordamiento (spillovers): - Aglomeración de productividad: \(A_i = \bar{A}_i L_i^\alpha\), donde \(\alpha > 0\) refleja economías de escala externas marshallianas. - Congestión de amenidades: \(a_i = \bar{a}_i L_i^\beta\), donde \(\beta < 0\) captura la inelasticidad en la oferta de vivienda, tiempos de traslado y congestión urbana.
Ecualización espacial de utilidad
La libre movilidad laboral iguala la utilidad a lo largo de todas las regiones pobladas:
Teorema de unicidad
Allen y Arkolakis (2014, Teorema 2) demuestran que el equilibrio general espacial es único y estrictamente estable si y solo si las fuerzas de congestión dominan a las economías de aglomeración:
AllenArkolakisModel.is_unique evalúa analíticamente esta condición de unicidad durante la inicialización del modelo.
2. Opciones metodológicas y del modelo
| Dimensión | Allen-Arkolakis (2014) |
|---|---|
| Mecanismo económico central | Movilidad laboral espacial con aglomeración y congestión |
| Enfoque de resolución | Mapeo de contracción de punto fijo sobre \((w_i, L_i)\) |
| Márgenes endógenos | Distribución de población, salarios, acceso a mercados |
| Movilidad laboral | Libre movilidad espacial continua entre regiones |
| Casos de uso principales | Trenes de alta velocidad, corredores carreteros, choques climáticos |
| Tasa de convergencia | 20–50 iteraciones (\(< 0.005\) s) |
2.1 Backends de hardware (backend=, 3.4.0)
El solucionador de punto fijo acepta un argumento backend — "numpy"
(el valor por omisión), "mlx" (Apple Silicon) o "cupy" (NVIDIA):
aa_res = aa_model.solve_equilibrium(backend="mlx")
cf_res = aa_model.solve_counterfactual(trade_costs_new=tau_new, backend="mlx")
Conviene conocer tres propiedades antes de recurrir a ellos:
- NumPy es el oráculo. Toda ruta acelerada se contrasta contra la
implementación de referencia en NumPy, y nunca al revés. El backend
numpyestá siempre disponible y preserva intacto el contrato de Pyodide. tolsigue significandotol. Metal no admite float64, de modo que la contracción con MLX enAllenArkolakisModelse ejecuta en float32 hasta un piso de 1e-6; el resultado se refina después con la contracción float64 de NumPy, inicializada en la solución del dispositivo, de manera queconvergedse refiere a la toleranciatolsolicitada.- El fallo es explícito y no fatal. Un backend no instalado, un espacio de
nombres de arreglos que no puede importarse o una excepción en el dispositivo
emiten un
RuntimeWarningy recurren a NumPy. Un resultado acelerado que no convergió tampoco se devuelve nunca:AllenArkolakisModelremata la solución del dispositivo con la contracción float64 de NumPy descrita arriba — sin advertencia, porque entoncesconvergedes el veredicto de NumPy a la toleranciatolsolicitada.
from puremacro._backend import available_backends
available_backends() # ('numpy', 'numba', 'mlx') en esta máquina
3. Calibración canónica y especificaciones espaciales
1. Allen-Arkolakis: Geografía urbana de 4 ciudades en EE. UU.
- Ciudades: Nueva York, Los Ángeles, Chicago, Houston (con coordenadas geográficas reales de latitud y longitud).
- Elasticidad comercial: \(\theta = 4.0\).
- Elasticidad de aglomeración: \(\alpha = 0.08\); elasticidad de congestión: \(\beta = -0.35\).
- Costos de transporte iceberg: parametrizados mediante distancias ortodrómicas (Haversine): \(\tau_{ij} = 1.0 + \delta \cdot d_{ij}^\gamma\).
4. Ejemplos prácticos ejecutables
El siguiente script evalúa una inversión en infraestructura de transporte en Allen-Arkolakis:
import numpy as np
from puremacro.spatial import AllenArkolakisModel
# Modelo de equilibrio general espacial de 4 ciudades en Allen-Arkolakis
coords = np.array([
[40.7128, -74.0060], # NYC
[34.0522, -118.2437], # LA
[41.8781, -87.6298], # Chicago
[29.7604, -95.3698], # Houston
])
names = ["NYC", "LA", "CHI", "HOU"]
aa_model = AllenArkolakisModel.from_coordinates(
coords,
region_names=names,
theta=4.0,
alpha=0.08,
beta=-0.35,
total_population=100.0,
)
aa_res = aa_model.solve_equilibrium()
assert aa_res.converged
assert np.isclose(np.sum(aa_res.population), 100.0)
# Simular reducción del 20% en costos bilaterales de transporte entre NYC y Chicago
infra_res = aa_model.simulate_infrastructure_shock("NYC", "CHI", cost_reduction=0.20)
assert infra_res.converged
print(f"Allen-Arkolakis: Población en NYC: {aa_res.population[0]:.2f}, LA: {aa_res.population[1]:.2f}")
print(f"Impacto en bienestar por infraestructura: {infra_res.welfare_pct:+.2f}%")
5. Especificación completa de la API
AllenArkolakisModel(
trade_costs: np.ndarray,
fundamental_productivity: np.ndarray,
fundamental_amenity: np.ndarray,
theta: float = 4.0,
alpha: float = 0.10,
beta: float = -0.30,
total_population: float = 1.0,
region_names: Sequence[str] | None = None,
coordinates: np.ndarray | None = None,
)
AllenArkolakisModel.from_coordinates(
coords: Any,
fundamental_productivity: np.ndarray | None = None,
fundamental_amenity: np.ndarray | None = None,
distance_cost_param: float = 0.001,
distance_exponent: float = 0.5,
metric: str = "haversine",
theta: float = 4.0,
alpha: float = 0.10,
beta: float = -0.30,
total_population: float = 1.0,
region_names: Sequence[str] | None = None,
) -> AllenArkolakisModel
AllenArkolakisModel.solve_equilibrium(
tol: float = 1e-8,
max_iter: int = 2500,
damping: float = 0.35,
backend: str = "numpy", # 'numpy' | 'mlx' | 'cupy'
) -> AllenArkolakisResult
AllenArkolakisModel.solve_counterfactual(
trade_costs_new: np.ndarray | None = None,
productivity_new: np.ndarray | None = None,
amenity_new: np.ndarray | None = None,
tol: float = 1e-8,
max_iter: int = 2500,
damping: float = 0.35,
backend: str = "numpy",
) -> AllenArkolakisResult
AllenArkolakisModel.simulate_infrastructure_shock(
origin: str | int,
destination: str | int,
cost_reduction: float = 0.20,
**kwargs: Any,
) -> AllenArkolakisResult
AllenArkolakisModel.simulate_climate_shock(
productivity_shocks: Mapping[str | int, float] | np.ndarray | None = None,
amenity_shocks: Mapping[str | int, float] | np.ndarray | None = None,
**kwargs: Any,
) -> AllenArkolakisResult
6. Interfaz de resultados y contrafactuales de política
AllenArkolakisResult
res.wages: Salarios nominales regionales de equilibrio \(w_i\).res.population: Distribución espacial de la población \(L_i\), conservando estrictamente \(\sum L_i = \bar{L}\).res.price_index: Índice de precios CES espacial \(P_i\).res.real_wages: Salarios reales regionales \(w_i / P_i\).res.welfare: Nivel de utilidad espacial ecualizada \(\bar{u}\).res.consumer_market_access: Índice de acceso a mercado de los consumidores \(\text{CMA}_i\).res.firm_market_access: Índice de acceso a mercado de exportación de las empresas \(\text{FMA}_i\).res.welfare_pct: Cambio porcentual contrafactual en la utilidad agregada \((\bar{u}' / \bar{u} - 1) \times 100\).res.summary() -> pd.DataFrame: Tabla resumen con resultados regionales.res.plot(kind="spatial") -> matplotlib.figure.Figure: Diagrama de dispersión geográfico escalado por población y coloreado por salarios reales.
Referencias bibliográficas
- Allen, T., & Arkolakis, C. (2014). "Trade and the Topography of the Spatial Economy." The Quarterly Journal of Economics, 129(3), 1085–1140.