🇬🇧 English · 🇪🇸 Español
Continuación de parámetros y arranques múltiples para equilibrios de política
puremacro.trade.continuation añade dos ayudas pequeñas sobre
solve_policy_equilibrium: una trayectoria auditada en un
parámetro del modelo con una política arancelaria completa y fija, y un
arranque múltiple en un objetivo exacto. Ambas aceptan una etapa solo después
de reauditar el estado devuelto con el evaluador de contabilidad consistente, y
ambas adjuntan la misma salvedad: un equilibrio alcanzado a lo largo de una
trayectoria o desde un arranque alternativo no queda establecido como
perteneciente a ninguna otra rama, y una búsqueda sin resolver no demuestra ni
un pliegue ni la inexistencia.
import numpy as np
from puremacro.trade import solve_policy_equilibrium
from puremacro.trade.continuation import sigma_path, try_starts
from puremacro.trade.data import generate_synthetic_mrio, package_mrio_to_calibration_result
from puremacro.trade._oecd_icio import condense_final_demand
from dataclasses import replace
# Three countries, three sectors, OECD final-use layout condensed to C/I/Cx.
raw = generate_synthetic_mrio("oecd", custom_c=3, custom_s=3, seed=2)
raw = replace(raw, taxes_less_subsidies_fd=np.zeros(raw.C*raw.K_F))
calibration = package_mrio_to_calibration_result(condense_final_demand(raw))
# A 10% import tariff by the first country on every foreign origin and use.
nc, ns, nfd = calibration.nc, calibration.ns, calibration.n_final_demand
tau, tau_fd = np.ones((ns*nc, ns, nc)), np.ones((ns*nc, nfd, nc))
tau[ns:, :, 0], tau_fd[ns:, :, 0] = 1.1, 1.1
# Follow the intermediate sourcing elasticity from Leontief (0) to 2 at that policy.
path = sigma_path(calibration, tau, tau_fd, 2., tol=1e-9)
print(path.summary())
print(path.to_dataframe()[["fraction", "sigma", "method", "iterations", "audited_max_residual"]])
# The endpoint is an ordinary consistent-accounting equilibrium.
direct = solve_policy_equilibrium(calibration, tau, tau_fd, sigma=2., tol=1e-9)
print(np.max(np.abs(path.final.x_sol - direct.x_sol))) # about 3e-11
# Several warm starts at one exact target; the first audited root wins.
starts = [{"name": "calibrated", "x0": None},
{"name": "from_path", "x0": path.final, "provenance": {"path": "sigma 0 -> 2"}}]
multi = try_starts(calibration, dict(tau=tau, tau_fd=tau_fd, sigma=2., tol=1e-9), starts)
print(multi.to_dataframe())
Se usa la semilla 2 porque la semilla sintética por defecto produce una
participación de gasto negativa tras la condensación, que la contabilidad
consistente rechaza. El ejemplo condensa los seis usos finales sintéticos con
condense_final_demand del módulo interno puremacro.trade._oecd_icio (no
forma parte de la API pública, así que puede cambiar sin aviso de
obsolescencia) y pasa impuestos nulos sobre el uso final porque esa ayuda no
acepta una tabla sin ellos.
Qué calculan las ayudas
continue_parameter(solve, path, start, ...) es el motor genérico. path(f)
devuelve los argumentos con nombre del solucionador en la fracción f de la
trayectoria, con f en [0, 1], por ejemplo {"tau": tau, "tau_fd": tau_fd,
"sigma": 2*f, "tol": 1e-9}. solve es o bien un TradeCalibrationResult, en
cuyo caso cada prueba llama a solve_policy_equilibrium con esos argumentos,
method="newton" y el estado aceptado anterior como arranque (véase la
sección siguiente), o bien un invocable solve(kwargs, x0) que devuelve un
TradeEquilibriumResult de contabilidad consistente (entonces calib= es
obligatorio para la auditoría). start debe resolver ya las ecuaciones en
path(start_fraction): se audita, nunca se vuelve a resolver, de modo que una
etapa guardada se reanuda en su fracción real.
sigma_path(calib, tau, tau_fd, sigma_target, sigma_start=0., ...) es el
envoltorio de conveniencia para la elasticidad de sustitución de insumos
intermedios. La trayectoria es
con los extremos exactos en f = 0 y f = 1 (f in [0, 1] significa f en
el intervalo [0, 1]). f nunca significa una fracción arancelaria: los
calendarios arancelarios intermedios y finales son completos y fijos a lo
largo de la trayectoria. Un start suministrado se audita en
sigma(start_fraction) sin volver a resolverlo (y entonces se rechaza x0).
Si se omite start, se resuelve en sigma(start_fraction) a partir de x0
con la escalera auditada completa de solve_policy_equilibrium
(method="auto" salvo que se indique un método): el arranque se audita como
uno suministrado, no se sigue, de modo que la regla de solo Newton descrita
más abajo se aplica únicamente a las pruebas. La primera etapa registra el
método, la semilla y el uso de la escalera de esa resolución, y
metadata["start_source"] indica "solved" o "supplied". Si la resolución
del arranque falla, se lanza PolicyEquilibriumError indicando la fracción,
antes de que exista ninguna etapa. Cuando sigma_start == sigma_target la
trayectoria solo audita el arranque.
try_starts(solve, target, starts, max_iter=35) prueba arranques con nombre
en los argumentos del objetivo, que no cambian. Cada arranque es un mapeo con
un name, un x0 (un vector de equilibrio, un TradeEquilibriumResult, o
None para el estado calibrado) y una provenance opcional, que se guarda
literalmente. Un arranque cuyo vector no es finito o no tiene la longitud de
estado de la calibración se registra como no resuelto sin resolver nada. La
búsqueda se detiene en la primera raíz auditada; un arranque rechazado nunca
sustituye a una raíz, y una búsqueda sin resolver devuelve final=None.
Arranques en caliente y allow_fallback
Por defecto cada prueba es una resolución de Newton
desde el estado aceptado anterior (en try_starts, desde el arranque con
nombre), y cualquier otro resultado es una prueba rechazada que reduce el
paso a la mitad. Con el valor por defecto allow_fallback=False:
- el adaptador de la calibración llama a
solve_policy_equilibriumconmethod="newton"y rechazamethod="auto","hybr"y"keller_pac"con unValueErrorantes de resolver nada, porque"auto"recorre la escalera de respaldo,"hybr"parte del estado calibrado y"keller_pac"parte del calendario sin aranceles, de modo que ninguno sigue la trayectoria; - un estado devuelto cuyo registro del solucionador muestra la escalera de
respaldo (
metadata["policy_solver_fallback_used"]), o cuyo último intento registrado partió de algo distinto del arranque suministrado, es una prueba rechazada. Esto también se aplica a los estados que devuelve un invocablesolvepropio.
allow_fallback=True activa la escalera: el adaptador usa entonces por
defecto method="auto", y una etapa aceptada puede ser una raíz auditada
alcanzada por hybr desde el estado calibrado o por la continuación
arancelaria de Keller desde el calendario sin aranceles. Esa etapa resuelve
las ecuaciones en los parámetros solicitados, pero no se alcanzó siguiendo la
trayectoria. Cada etapa y cada intento de try_starts registra seed
("warm" o "calibrated") y fallback_used, de modo que el registro deja
ver una raíz atribuida a un arranque con nombre cuyo vector nunca se usó.
Ley de pasos
Con fracción actual f y paso h, la ley de pasos es (prueba, fallo y éxito
en el bloque siguiente):
trial = min(1, f + h)
failure -> h <- h / 2; if h < min_step: stop, keep the last audited state
success -> f <- trial; if iterations <= fast_iterations: h <- min(max_step, growth h)
Es decir: tras un fallo el paso se reduce a la mitad y, si queda por debajo de
min_step, la trayectoria se detiene conservando el último estado auditado;
tras un éxito la fracción avanza y, si la prueba necesitó como mucho
fast_iterations iteraciones, el paso crece por growth hasta max_step.
Valores por defecto: initial_step = 0.25, min_step = 1/1024,
max_step = 0.5, growth = 1.5, fast_iterations = 6; puede pasarse un
calendario más prudente (tope de 0.4, cinco iteraciones rápidas,
min_step = 1/128). iterations es el recuento propio del solucionador:
iteraciones de Newton, evaluaciones de función de hybr de SciPy, o pasos de
continuación de Keller. Una prueba a menos de 1e-12 de la fracción uno se toma
como uno, lo que evita una etapa final duplicada por acumulación de coma
flotante. El tope de iteraciones por prueba es el max_iter del solucionador
(100 en solve_policy_equilibrium); pase un max_iter menor, por ejemplo 25,
para rechazar pruebas que convergen lentamente. Una parada lanza ParameterContinuationFailure, cuyo state es el
último equilibrio auditado (nunca la prueba rechazada) y cuyo result es el
ParameterContinuationResult parcial con status="unresolved", todas las
etapas aceptadas y todas las pruebas rechazadas.
Aceptación e invariantes
Una prueba se acepta solo cuando el estado devuelto
- declara convergencia,
- lleva contabilidad consistente, transferencias de suma fija y exactamente
el
sigmasolicitado y los calendarios arancelarios intermedios y finales solicitados (por igualdad de arrays), - supera la reevaluación de contabilidad consistente de todas las ecuaciones
de equilibrio y contables, incluidos el mercado de bienes omitido y los
saldos exteriores, en su tolerancia registrada, con todos los campos
reportados (precios, producciones, salarios, rentas del capital,
transferencias, índices de precios, demanda final, recaudación arancelaria
y PIB) iguales al equilibrio recalculado con
rtol = atol = 1e-8, - tiene un residuo absoluto máximo auditado no mayor que la
tolsolicitada (por defecto1e-8, la desolve_policy_equilibrium; absoluta en las unidades de la calibración), y - salvo con
allow_fallback=True, no se alcanzó mediante la escalera de respaldo del solucionador ni, según su último intento registrado, partió de una semilla distinta del arranque en caliente.
La auditoría es el propio evaluador del modelo, el mismo que usa
solve_policy_equilibrium; no es un oráculo derivado por separado. Un estado
que incumple el criterio 2 es un contrato roto y lanza ValueError de
inmediato; un estado que incumple cualquier otro criterio es una prueba
rechazada. Un ValueError o TypeError lanzado por el solucionador significa
entradas inválidas y se propaga; RuntimeError (por ejemplo
PolicyEquilibriumError), ArithmeticError y LinAlgError son fallos
numéricos que reducen el paso a la mitad. Los equilibrios heredan el
numerario y el cierre del saldo exterior de solve_policy_equilibrium (véase
la contabilidad consistente). En la tabla sintética
(observado, no fijado por una prueba), reescalar todos los
flujos por 1000 con tol escalada igual, o permutar países y sectores
manteniendo en su lugar el primer país y el primer sector, reprodujo la
trayectoria con una precisión de alrededor de 1e-14; poner otro país en
primer lugar, y con ello cambiar el numerario, cambió las producciones en
torno a un 1% y los salarios en 0.15.
Por defecto, todo argumento con nombre que path deja sin cambiar entre las
fracciones 0 y 1 se resume con una huella (calendarios arancelarios por sus
bytes, escalares por valor, estados de equilibrio por su resumen) y debe ser
idéntico en cada prueba; continued= nombra explícitamente los argumentos que
pueden cambiar e invariant= sustituye la huella. Una violación se lanza antes
de resolver la prueba. El resumen de la huella se guarda como invariant_id.
Una trayectoria identidad (nada continuado) audita igualmente el estado
suministrado y reporta la fracción uno.
Resultados
ParameterContinuationResult contiene fractions, parameter_values, las
stages y failures serializables en JSON (sin vectores de estado), final,
status, start_fraction, last_fraction, invariant_id, qualification y
metadata. Cada etapa registra el parámetro, el método, la semilla, si se usó
la escalera de respaldo, las iteraciones, el residuo del solucionador, el
residuo auditado, la tolerancia solicitada, los segundos, el paso usado, y el
mínimo de precio, de razón de producción, de salario y de gasto final. Cada
prueba rechazada registra el error y, cuando el solucionador lanzó
PolicyEquilibriumError, solver_attempts: el método, la semilla, el
indicador de convergencia, los residuos y el error de cada intento; el mensaje
de ParameterContinuationFailure termina con el último de ellos.
keep_states=True conserva cada TradeEquilibriumResult aceptado en
states; stage_callback(state, stage) recibe cada estado aceptado con una
copia independiente de su registro para que quien llama pueda persistirlo;
progress(event) recibe los eventos de prueba, aceptación y rechazo.
metadata= se fusiona con los metadatos del resultado, y las claves que el
resultado registra por sí mismo (method, solver, audit,
sigma_target, ...) se rechazan con un ValueError. to_dataframe(),
to_markdown(), to_latex(), to_typst() y summary() siguen las
convenciones del paquete, y plot(values, ax=...) dibuja un escalar por etapa
frente al parámetro: el residuo auditado por defecto, una clave de etapa, una
secuencia, o un invocable aplicado a los estados conservados (por ejemplo la
VE hicksiana de un país con compute_hicksian_welfare).
DirectTargetResult contiene attempts (nombre, procedencia, estado, error o
diagnósticos del solucionador, incluidos seed y fallback_used),
accepted_start, final, status, el target con huellas, qualification
y metadata, con los mismos exportadores de tablas.
final es un TradeEquilibriumResult ordinario de contabilidad consistente:
compute_hicksian_welfare y las funciones de
posprocesamiento lo aceptan sin cambios.
Qué está validado y qué no
Las pruebas de tests/test_trade_continuation.py comprueban, en la tabla
sintética 3 x 3 anterior (tol=1e-9): el extremo de la trayectoria en sigma
frente a un solve_policy_equilibrium(sigma=2) directo (diferencia máxima en
el vector de estado 3.1e-11, tolerancia 1e-9; fracciones aceptadas 0,
0.25, 0.625 y 1, cada prueba una resolución de Newton en caliente); la
reanudación desde una etapa guardada (1.2e-11); la conservación del
arranque tras un fallo, con los intentos del solucionador registrados; las
violaciones de invariantes y del contrato de parámetros lanzadas antes de
resolver o sin resolver; la trayectoria identidad; la validación de ajustes,
métodos y metadatos; el contrato de allow_fallback (estados de la escalera
de respaldo o de semilla calibrada rechazados por defecto y aceptados y
etiquetados con la opción); try_starts devolviendo solo raíces auditadas;
registros JSON, serialización con pickle, tablas y gráficos; y la ley de pasos
bajo resultados guionizados del solucionador..
En el fixture OECD congelado de 3 regiones x 3 sectores en millones de USD,
sigma_path(calib, tau, tau_fd, 0.5, tol=1e-5) con un arancel uniforme del
10% a las importaciones de EE. UU. coincide con
solve_policy_equilibrium(calib, tau, tau_fd, sigma=0.5, tol=1e-5) con una
precisión relativa de 6.6e-12 en precios y 6.7e-12 en PIB (tras
las correcciones del solucionador en modo consistente; la prueba exige
1e-6). Todas las etapas, incluido el arranque en sigma 0, son resoluciones
de Newton (el arranque requiere 6 iteraciones hasta 4.8e-8). Antes de esas
correcciones, Newton se detenía en sigma 0 con converged=False en un residuo
de 8.3e-6 porque su regla de parada no comprobaba las ecuaciones físicas
auditadas, y el arranque procedía del respaldo de Keller. La trayectoria de 0
a 2 queda sin resolver en este fixture: se detiene en la fracción 0.450
(sigma 0.900), y la trayectoria de 2 a 0 se detiene en la fracción 0.479
(sigma 1.041). Las llamadas directas a solve_policy_equilibrium en sigma
0.91, 0.95, 1.0, 1.01 y 1.04 fallan con Newton, híbrido y continuación de
Keller; en sigma 1, 1.01 y 1.04 el intento de Keller alcanza residuos de
4.9e-8, 9.9e-6 y 1.0e-7, pero en una raíz con gasto final negativo, que
la auditoría rechaza por demanda no factible. Sigma 0.9 se resuelve con Newton
y 1.05 mediante el respaldo de Keller. La herramienta reporta esa región como
etapas auditadas más un ParameterContinuationFailure; no la resuelve.
Es una observación sobre la pila de solucionadores existente cerca de la
elasticidad unitaria en esta tabla y tolerancia, no una propiedad del
algoritmo de trayectoria; una prueba marcada como lenta fija el contrato
(etapas auditadas, estado conservado auditado) y no el fallo.
Todo resultado lleva una salvedad en result.qualification, adaptada a
trayectorias de parámetros (original en inglés):
An admissible exact-target equilibrium found from an alternative start or production-elasticity path is not established as belonging to the forward low-elasticity tariff branch. Local stability is reported separately; neither convergence nor unsuccessful searches prove global uniqueness or nonexistence.
No hay guardia de rechazo temprano para pruebas lentas: una prueba se rechaza solo cuando el solucionador lanza una excepción, el registro del solucionador muestra una semilla distinta del arranque en caliente, o la auditoría falla. Este módulo no evalúa la estabilidad local de los estados aceptados (véase estabilidad local reducida).
Documentación relacionada
- Política arancelaria hicksiana y recuperación auditada del equilibrio:
el solucionador auditado que llama cada etapa (Newton desde el arranque en
caliente por defecto; la escalera completa Newton, híbrida y de Keller con
allow_fallback=True). - Contabilidad de comercio consistente: las ecuaciones que reevalúa la auditoría de cada etapa, y el cierre del numerario y del saldo exterior.
- Bienestar hicksiano de consumo: bienestar a lo largo de una trayectoria a partir de los estados conservados.
- Estado de validación estructural.