Skip to content

🇬🇧 English · 🇪🇸 Español

Auditorías de transiciones y refinamiento acotado

La auditoría de resultados puede inspeccionar trayectorias de previsión perfecta y del HANK no lineal de un activo incorporado. Evalúa la trayectoria suministrada sin resolver otra. Un experimento de refinamiento explícito puede ejecutar callbacks bajo un presupuesto de trabajo declarado.

También se admiten cierres editables HANKResult (closure='model'). Se vuelven a evaluar las ecuaciones agregadas originales y la respuesta lineal del hogar mediante sus Jacobianos sobre las trayectorias suministradas. En el cierre no lineal, la comparación con la transición final retenida del hogar exige que sus entradas sigan coincidiendo con ese experimento. Una entrada modificada falla su comprobación y deja la consistencia del hogar como not_checked; no se resuelve otro hogar. La masa estacionaria se comprueba por separado. La evidencia micro de Euler, presupuesto y transporte distributivo ausente sigue como not_checked.

Previsión perfecta

from puremacro.validation import audit_result

result = model.solve_perfect_foresight(periods=80)
report = audit_result(result)
print(report.to_markdown())
errors = report.maps["path_equations"]

Los nuevos PerfectForesightResult conservan las condiciones inicial y terminal resueltas, los choques, el orden de variables y la función de ecuaciones. La auditoría evalúa las ecuaciones originales en cada fecha, incluidas ambas fronteras. STEADY_STATE() usa la referencia fija de la solución. No vuelve a resolver endval ni reconstruye los choques desde un modelo modificado.

El mapa tiene ejes (periodo, ecuación), con periodos desde uno. Una trayectoria alterada falla aunque conserve converged=True y un residual registrado igual a cero. La distancia entre la última fecha simulada y la frontera terminal es un diagnóstico de horizonte sin calificación: la frontera se impone en T+1.

Para resultados antiguos o construidos manualmente:

from puremacro.validation import PathAuditContext

context = PathAuditContext(
    equations=equations,  # (adelanto, actual, rezago, choque) -> residuales
    y_init=initial_state, y_end=terminal_state,
    exogenous_path=shocks, variable_names=result.variable_names,
)
report = audit_result(result, model=context)

Los choques deben tener exactamente una fila por fecha. Si la función declara explícitamente steady_state, recibe el estado terminal fijo. El usuario vincula los parámetros de una función sin modelo. Sin contexto suficiente, la ecuación queda not_checked. Los choques sorpresivos secuenciales y MCP tienen contratos distintos y no están cubiertos por este adaptador; tampoco se atribuye una distribución a un modelo de agente representativo.

HANK no lineal

from puremacro.models.hank_sequence_space import solve_nonlinear_transition

transition = solve_nonlinear_transition(
    steady_state, shock_seq=monetary_shocks, horizon=80,
    retain_audit_paths=True,
)
report = audit_result(transition)

Se conservan los phi_pi, kappa y parámetros del hogar efectivos, incluidas las sustituciones de parámetros. Aun sin retener políticas por fecha, se recalculan las identidades de bienes, Phillips y Taylor/Fisher a partir de las trayectorias reportadas. La inflación terminal desviada es cero según el cierre incorporado; no se está verificando un bloque .mod arbitrario.

retain_audit_paths=True guarda el último recorrido del hogar ya calculado: consumo, ahorro, distribuciones de entrada y terminal, retornos realizados, salarios y consumo de continuación. No agrega otra solución EGM. Los arreglos principales requieren aproximadamente 8*(3*T+2)*n_activos*n_productividad bytes, además de los insumos. El valor predeterminado es False; la evidencia faltante queda not_checked. Los mapas de auditoría también ocupan memoria proporcional al tamaño de la trayectoria.

Con esos datos se comprueban:

  • Presupuesto por fecha y estado, consumo positivo, dominio de activos y correspondencia entre retorno ex ante y retorno realizado una fecha después.
  • Agregación independiente del consumo y equilibrio de bienes, incluido el gasto público y el impuesto que financia intereses de deuda.
  • Euler/KKT mediante interpolación de utilidad marginal en el ahorro continuo observado. La última fecha usa el consumo terminal conservado. Los percentiles ponderan fechas por igual y usan la masa suministrada en cada fecha.
  • Masa total y negativa, distribución inicial, transporte por lotería, conservación de activos medios y masa en la frontera superior artificial.

No se normaliza masa, recorta política ni resuelve otro hogar. El ejemplo sintético de 25 puntos y ocho fechas tiene residuales de bienes próximos a 2e-12, error Euler alrededor de 0.0023 y masa superior de 0.0056, que falla el umbral predeterminado 0.001. Son observaciones de esa calibración, no cotas universales del error.

Escaleras explícitas de refinamiento

RefinementCheck compara un candidato con la base. RefinementLadder compara resoluciones consecutivas y conserva cada intento:

from puremacro.validation import RefinementLadder, RefinementLevel

ladder = RefinementLadder(
    "horizonte",
    levels=[
        RefinementLevel("T160", solve_T160, settings={"horizon":160}, cost=2),
        RefinementLevel("T320", solve_T320, settings={"horizon":320}, cost=4),
        RefinementLevel("T640", solve_T640, settings={"horizon":640}, cost=8),
    ],
    metrics=extract_economic_metrics,
    max_solves=3, max_cost=14, consecutive_passes=2,
    rtol=0.005, atol=1e-8,
    validate_pair=check_same_economic_experiment,
)
report = audit_result(baseline, checks=("refinement",), refinement_ladders=[ladder])
print(report.metadata["refinement_ladders"]["horizonte"]["stop_reason"])

metrics(result) es obligatorio: devuelve un diccionario no vacío de escalares finitos, con las mismas claves en cada resolución. Elija consumo en una ventana económica fija, riqueza, bienestar, brecha terminal, errores Euler o entradas del jacobiano. Para unidades distintas, normalice explícitamente las métricas o use escaleras separadas. No se eligen automáticamente mallas ni horizontes.

Cada métrica debe satisfacer abs(nuevo-anterior) <= atol + rtol*abs(nuevo). Se exige el número indicado de comparaciones consecutivas aprobadas. Un cambio excesivo reinicia la cuenta; los intentos anteriores fallidos siguen visibles. Un callback fallido, no convergencia declarada, valores no finitos, claves distintas o rechazo de la pareja detienen el experimento sin certificar estabilidad. La estabilidad se refiere solamente a las métricas elegidas.

validate_pair(anterior,nuevo) puede rechazar cambios de especificación, fronteras o choques. Sin este callback, la equivalencia del experimento se registra como not_checked: settings es procedencia declarada por el usuario, no una inspección de la función que resolvió el modelo.

max_solves cuenta callbacks intentados, incluso fallidos. Cada intento consume su cost positivo declarado; no comienza si excedería max_cost. Estas unidades no son segundos ni memoria y no ofrecen un límite temporal duro: no se puede interrumpir un callback en curso. La parada distingue stable_metrics, solve_budget_exhausted, cost_budget_exhausted, levels_exhausted y errores nombrados. JSON, Markdown y los metadatos del DataFrame conservan presupuestos, intentos y métricas. Si no se selecciona refinement, no se ejecuta ninguno de los callbacks de métricas o solución.