🇬🇧 English · 🇪🇸 Español
Hicksian tariff policy and audited equilibrium recovery
Select metric="hicksian_ev" explicitly in the unilateral tariff search,
multilateral Nash search or welfare payoff matrix. This uses
consistent accounting, lump-sum fiscal rebates and the
consumption expenditure function. Historical defaults and
the aliases ev, equivalent_variation and consumption retain their historical
objectives. They do not select the new calculation. When evaluating real
absorption under metric="geary_khamis" via evaluate_national_welfare,
supplying base_equilibrium anchors valuation to fixed baseline Geary–Khamis
international reference prices \(\pi^{\text{base}}\), deflates net foreign lending
into real investment units via restore_capital_formation(..., p_fd=...), and
routes raw equilibrium state arrays (p_sol, y_sol, r_sol, w_sol, T_sol, XN_sol)
through the same consistent-accounting map as TradeEquilibriumResult, keeping
baseline-anchored Geary–Khamis welfare comparisons invariant to counterfactual
nominal price scaling.
from puremacro.trade import (
calibrate_trade_model, solve_policy_equilibrium, compute_unilateral_optimal_tariff,
solve_multilateral_nash_tariffs, compute_welfare_payoff_matrix,
)
from puremacro.trade.data import synthetic_codes, synthetic_icio_table
# A seeded synthetic table: two countries, three sectors, three final uses.
countries, sectors = synthetic_codes(2, 3)
calibration = calibrate_trade_model(synthetic_icio_table(2, 3, seed=1),
country_codes=countries, sector_codes=sectors)
home, partner = countries
# Choose an absolute tolerance appropriate for the calibration units.
ge_tol = 1e-5
base = solve_policy_equilibrium(calibration, sigma=2., tol=ge_tol)
options = dict(metric="hicksian_ev", base_equilibrium=base, sigma=2.,
consumption_categories=(0,), ge_tol=ge_tol)
opt = compute_unilateral_optimal_tariff(
calibration, country_idx=home, tariff_max=.30, num_grid=21, **options,
)
game = solve_multilateral_nash_tariffs(
calibration, player_countries=[home, partner], tariff_max=.30,
best_response_grid_size=13, tol=1e-4, regret_tol=1e-6, **options,
)
matrix = compute_welfare_payoff_matrix(
calibration, player_a=home, player_b=partner,
optimal_a=.10, optimal_b=.20, **options,
)
The named countries must exist in the calibration. sigma is intermediate
origin substitution; the final-use baskets remain Leontief. Consumption
categories default to (0,). Select additional non-investment categories only
when they belong in the stated utility function. This is conditional consumption
welfare, including government consumption when the input data aggregate it.
One comparison baseline
Each search holds the same zero-tariff equilibrium, preferences, numeraire,
foreign balances and consumption categories fixed for every candidate and
deviation. If base_equilibrium is omitted, the search solves it once. A supplied
baseline must pass the requested ge_tol, use the requested sigma and have
zero intermediate and final tariffs. Changes in domestic taxes are outside
this policy experiment.
The foreign-saving closure is part of that baseline. The unilateral, Nash and
payoff-matrix searches accept foreign_saving_units ("numeraire" by default,
or "world_income") and pass it to every equilibrium they solve: the baseline,
each candidate and each deviation. A supplied base_equilibrium must have been
solved with the same closure; otherwise the search raises ValueError. The
closure is recorded in metadata["foreign_saving_units"]. With nonzero
baseline foreign balances, the numeraire closure makes payoffs depend on which
country is listed first, and "world_income" does not (see
consistent accounting):
base = solve_policy_equilibrium(calibration, sigma=2., tol=ge_tol,
foreign_saving_units="world_income")
options.update(base_equilibrium=base, foreign_saving_units="world_income")
On notebook 63's two-country table (sigma=2, consumption categories (0, 2),
ge_tol=1e-9), country A's EV from its own 10% tariff is 2.540428% of baseline
consumption with "world_income", whichever country is listed first. With
"numeraire" it is 2.630850% when A is listed first and 2.495185% when B is.
In 4.3.0 the searches raised TypeError for this option.
Level-valued welfare fields and curves contain EV in calibration value units;
baseline EV is zero. Percentage gains use baseline selected consumption
expenditure, never baseline EV. Bloc payoffs sum their members' monetary EV;
strategic blocs must not overlap. World welfare includes all calibrated
countries, including countries that are not strategic players.
Terms-of-trade diagnostics are country-level indices; they are unavailable
(NaN) for a multi-country bloc because no external-trade bloc index is defined.
Nash metadata distinguishes player_regrets in value units from
relative_max_regret, a fraction of each player's fixed baseline consumption
expenditure. Thus regret_tol=1e-6 means one millionth of that expenditure,
not one millionth of EV and not a percentage point. These conventions are
recorded alongside the baseline and solver runs in result metadata.
What convergence establishes
Unilateral method="grid" evaluates the declared grid. method="bounded"
also refines every sampled local-maximum bracket, including endpoint brackets.
Nash method="best_response" uses this grid/refinement search for each player,
then evaluates simultaneous unilateral deviations at the final profile.
converged=True requires both the undamped best-response tariff gap and the
consumption-normalized regret to meet their tolerances. Small damped updates
alone cannot establish convergence. Reaching max_iter can return an audited GE
candidate with converged=False and finite final deviation diagnostics.
Read best_response_boundaries, the tariff ceiling and grid resolution. A
boundary solution is constrained by the imposed interval. Local refinement
can miss unsampled narrow peaks; these checks prove neither global optimality
nor existence or uniqueness of a continuous-game Nash equilibrium.
Boundary labels (metadata["boundary"] for the unilateral search,
metadata["best_response_boundaries"] for Nash) are "lower" or "upper"
when a rate lies within tol of 0 or of tariff_max, and "interior"
otherwise. When the two tolerance bands overlap (tariff_max <= 2 * tol,
recorded as metadata["boundary_bands_overlap"]), the nearer bound wins and
the exact midpoint is "interior". In 4.3.0 the lower bound was tested first,
so with tariff_max <= tol a reply at the ceiling was labelled "lower".
Ceilings above 2 * tol, including all the defaults, are unaffected. The
non-Hicksian Nash path applies the same rule to its
best_response_boundaries.
The payoff matrix uses the same two actions, zero and one fixed positive tariff,
for each player across all four cells. If omitted, the positive action is the
player's unilateral optimum against zero foreign tariffs. Supplied nash_a or
nash_b are accepted only as fixed actions, explicitly unverified as Nash rates.
Conflicting optimal_* and nash_* rates raise an error. Mutual positive tariffs
are not automatically a continuous-game Nash equilibrium or a Prisoner's Dilemma.
Recovery and failed deviations
solve_policy_equilibrium(method="auto") tries Newton, SciPy hybrid, then Keller
pseudo-arclength continuation. The first attempt may use a previously accepted
warm start; subsequent attempts restart from the calibrated state. Continuation
starts at zero tariffs and uses the complete target intermediate and final-use
schedules. All attempts preserve sigma, the fiscal closure and the requested
tolerance. Recovery does not relax the acceptance threshold.
Before any attempt, a Leontief schedule (sigma=0) is screened with
Collatz-Wielandt bounds on the spectral radius of the tariff-augmented cost
matrix B_tau = a * multiplier / (1 - t), using the exact production-tax wedge.
A lower bound of at least one certifies that no positive price system exists:
PolicyEquilibriumError is then raised with an empty attempts list and the
bounds in its viability attribute, and no solver runs. Every other outcome
lets the attempts run: viable, near_critical (bounds in [1 - 1e-6, 1); a
price system exists, with very large prices), unresolved (the bounds straddle
the threshold) and not_applicable for CES intermediates (sigma > 0), where
the Leontief bound certifies nothing. The screen is recorded in
equilibrium.metadata["policy_viability"]. foreign_saving_units
("numeraire" by default, or "world_income") is passed to every attempt; see
consistent accounting.
An accepted result must declare convergence, retain the requested schedules and
technology, and pass an independent re-evaluation of reported fields and all
equilibrium/accounting residuals, including the omitted goods market and foreign
balances. Attempt methods, seeds, warnings, errors and accepted residuals are
recorded in equilibrium.metadata["policy_solver_attempts"].
If all attempts fail, PolicyEquilibriumError exposes attempts; policy searches
also attach the failing profile. The search raises immediately. It produces
no payoff or best-response certificate for that deviation, caches no failed
state and never uses it as the next warm start. Scalar-refinement failure also
leaves optimization unresolved. Use ge_method, ge_max_iter, ge_max_steps
and ge_tol to control policy equilibrium solves independently of outer-game
iteration limits. Unknown options are rejected.
ge_tol bounds absolute residuals in the calibration's units. It must be
appropriate for their numerical scale: a table in million USD, such as the
bundled OECD table (load_icio_data()), needs an explicitly requested
ge_tol of about 1e-5. A request below floating-point
resolution can fail even at the baseline; recovery deliberately does not
silently loosen that request. Changing monetary units may require changing the
absolute GE tolerance, while percentage welfare and normalized regret remain
unit invariant.
Evidence and scope
A separately derived two-country CES relative-price equation, algebraic fiscal accounts and primal expenditure minimization supply the reference. All 1,681 profiles on a 41-by-41 tariff grid are evaluated independently. Unilateral choices, the constrained Nash candidate, deviations and fixed-action payoffs agree with that reference. Tests also exercise actual Newton failure with hybrid recovery, forced continuation, exhausted recovery, false convergence, failed deviation coverage, currency scaling and tiny relaxation.
See the reference code and
notebook 63. The benchmark
reaches its 40% ceiling; it does not identify an interior optimal tariff or
validate an empirical trade-war forecast. Supported instruments are universal,
final_only and intermediate_only. Gradient games, other fiscal closures and
flexible/GPU models are unavailable for this metric. Evaluate explicit tariff
schedules with the public solver and welfare evaluator.