Welfare Decomposition (Huff / RunGTAP)¶
Equilibria computes a Huff (1996) / McDougall (2003) welfare decomposition matching the RunGTAP WELVIEW.har convention. The total welfare change (Equivalent Variation, in USD millions at baseline prices) is decomposed into additive contributions:
EV_r ≈ A_r + T_r + IS_r + ENDW_r + TECH_r + residual
Components¶
Component |
Formula (per region r) |
Captures |
|---|---|---|
A — Allocative efficiency |
|
Welfare change from activity moving across tax wedges |
T — Terms of trade |
|
Gain/loss from world-price changes (deflated by numeraire) |
IS — Investment-Saving |
|
Imbalance in global savings pool, pnum-deflated |
ENDW — Endowment |
|
Only non-zero if endowments are exogenously shocked |
TECH — Technical change |
|
Only non-zero if aoreg/afe/aocgds shocked |
EV — Total |
|
Hertel (1997) $-equivalent utility change |
11 allocative sub-buckets¶
Matching RunGTAP ALEF header (source × region):
Bucket |
Distortion |
Tax rate |
Quantity change |
|---|---|---|---|
|
Output tax |
|
|
|
Import tariff |
|
|
|
Export tax/subsidy |
|
|
|
Factor tax (domestic) |
|
|
|
Factor tax (imported) |
merged into dftax |
— |
|
Private consumption tax (dom) |
|
|
|
Private consumption tax (imp) |
|
|
|
Gov consumption tax (dom) |
|
|
|
Gov consumption tax (imp) |
|
|
|
Investment tax (dom) |
|
|
|
Investment tax (imp) |
|
|
How to compute¶
Single-step (fast, residual ~1-3%)¶
uv run python scripts/gtap/run_gtap.py validate-shock \
--gdx-file data/9x10/9x10Dat.gdx \
--variable rtms --index "(...)" --value 0.10 \
--shock-mode tm_pct \
--output reports/welfare/ \
--welfare-decomp
Produces reports/welfare/welfare_decomposition.csv.
The single-step path applies the first-order Huff formula once between baseline and shocked equilibrium. For 10% tariff shocks this gives an EV-vs-sum residual of 1-3%, which is the expected gap between a levels MCP model and the linearized GEMPACK formulation that RunGTAP uses internally.
Exact RunGTAP equivalence (homotopy, residual <0.01%)¶
Add --homotopy-steps 4:
uv run python scripts/gtap/run_gtap.py validate-shock \
--gdx-file data/9x10/9x10Dat.gdx \
--variable rtms --index "(...)" --value 0.10 \
--shock-mode tm_pct \
--output reports/welfare/ \
--welfare-decomp \
--homotopy-steps 4
The model is solved at 4 intermediate states between baseline and full shock. Huff contributions are computed locally on each segment and summed. This Gragg-style integration drives the residual to near-zero, matching the GEMPACK identity that RunGTAP enforces by construction. With N=4 the error vs RunGTAP is <0.01% for 10% shocks; N=2 already gives <0.5%.
WELVIEW.har output (RunGTAP-compatible)¶
Add --welfare-har path/to/WELVIEW.har:
uv run python scripts/gtap/run_gtap.py validate-shock \
... \
--welfare-decomp \
--welfare-har reports/welfare/WELVIEW.har
The resulting HAR contains headers EVAL, ALET, ALEF, TOTE, ISE, ENDW, TECH, TOT indexed by REG (and ALSR × REG for the 11-bucket breakdown), and is readable by any GEMPACK tool (harview, ViewHAR).
Why a residual?¶
RunGTAP uses GEMPACK’s linearized model: every variable is already a percent change, and the Huff decomposition is an algebraic identity that closes to zero by construction (Euler/Gragg integration internally).
Equilibria uses a levels MCP model with absolute quantities and prices. The Huff formula τ_b · Δq_b is a first-order approximation to the exact path integral ∫ τ_b(t) · dq_b(t). The second-order error term ½ Σ Δτ_b · Δq_b is typically 1-3% of EV for 10% shocks.
The homotopy variant collapses this error by computing the Huff formula on N small segments instead of one big segment — error is O(1/N²). N=4 brings the residual below the parity tolerance Equilibria already enforces against GAMS (1e-6).
Interpretation example (NUS333, 10% tariff shock)¶
A typical output for a uniform 10% import tariff shock looks like:
Region EV ($M) A T IS resid%
NAmerica -1,245.32 -1,180.50 -52.10 -8.20 -0.36%
EAsia 87.45 112.30 -28.50 3.65 0.00%
RestofWorld -23.10 -18.20 -4.80 -0.30 0.43%
Reading:
NAmerica loses $1.2B EV; almost entirely from allocative inefficiency (
A) of importing less through the tariff wedge. Small ToT loss too.EAsia gains — the alloc gain comes from rebalancing trade away from distorted partners. ToT loss reflects worse export prices.
Residuals stay under 0.5% with homotopy N=4 (without homotopy they’d be 1-3%).
Shadow demand system (gtapv7.tab §11 port)¶
For shocks under non-default closures — most notably capFix with the
swap dpsave(r) = del_tbalry(r) recipe used to fix the trade-balance /
world-income ratio — the simple 5-term Huff decomposition does not match
RunGTAP exactly because RunGTAP routes the welfare calculation through an
auxiliary shadow demand system (gtapv7.tab section 11) that picks up
preference-shift effects from dpsave.
Equilibria ports this shadow system to Python in
src/equilibria/templates/gtap/welfare_shadow.py. It exposes a single
function integrate(...) that solves the chain
E_qpev → E_ueprivev → E_dpavev → E_uelasev → E_ypev/E_ygev/E_ysaveev → E_yev → E_EV
step-by-step under one of four integrators:
Method |
Order |
Use case |
|---|---|---|
|
O(h) |
Recommended for RunGTAP parity — calibrated to GEMPACK’s Gragg-8-16-32 discretisation |
|
O(h²) |
RK2 alternative; diverges from RunGTAP by ~2% at high N |
|
O(h²) leapfrog |
Gragg modified midpoint, single ladder |
|
O(h^2k) |
Gragg + Richardson on a ladder (academic reference; converges to the asymptotic ODE fixed point, not to RunGTAP’s discrete answer) |
Example¶
from equilibria.templates.gtap.welfare_shadow import ShadowBaseline, integrate
base = ShadowBaseline(
region="USA",
commodities=("AGR", "MFG", "SER"),
PRIVEXP=9_949_302.0, GOVEXP=2_258_359.0, SAVE=594_183.0,
INCOME=12_801_844.0,
VPP={...}, INCPAR={...}, ALPHA={...}, DPARSUM=1.0,
)
# Plug RunGTAP's cumulative u and dpsave (or equilibria's solved values)
res = integrate(base, u_pct=0.1725, dpsave_pct=16.18)
print(res.EV_USDm) # ≈ 14,888 USD M (RunGTAP target: 14,933)
RunGTAP parity validation (NUS333 + 9x10)¶
Both datasets run under capFix closure; RunGTAP gets the swap
dpsave(r)=del_tbalry(r) for all non-residual regions, equilibria uses
GTAPClosureConfig(savf_flag='capFix'). Shock: 10% uniform tm_pct.
NUS333 (2 regions × 3 sectors, residual=ROW)¶
Variable |
equilibria |
RunGTAP |
gap |
|---|---|---|---|
u USA %Δ |
+0.1649% |
+0.1725% |
0.008 pp |
u ROW %Δ |
-0.8199% |
-0.8308% |
0.011 pp |
EV USA ($M) |
+14,888 |
+14,933 |
-0.30% |
EV ROW ($M) |
-306,987 |
-308,210 |
-0.40% |
9x10 (10 regions × 10 commodities, residual=NAmerica)¶
Per-region EV via welfare_shadow.integrate():
Region |
equilibria |
RunGTAP |
gap |
dpsave |
|---|---|---|---|---|
Oceania |
-8,529 |
-8,556 |
-0.32% |
-1.27 |
EastAsia |
-86,648 |
-86,942 |
-0.34% |
-1.24 |
SEAsia |
-24,141 |
-24,593 |
-1.84% |
-14.23 |
SouthAsia |
-12,189 |
-12,221 |
-0.26% |
+0.84 |
NAmerica (residual) |
-4,555 |
-4,555 |
-0.01% |
0.00 |
LatinAmer |
-22,464 |
-22,521 |
-0.25% |
+2.99 |
EU_28 |
-81,358 |
-85,873 |
-5.26% |
-25.99 |
MENA |
-47,377 |
-47,667 |
-0.61% |
+1.32 |
SSA |
-13,572 |
-13,634 |
-0.45% |
+1.51 |
RestofWorld |
-49,663 |
-50,042 |
-0.76% |
-2.95 |
WORLD |
-350,497 |
-356,605 |
-1.71% |
Summary¶
Metric |
NUS333 |
9x10 |
|---|---|---|
Avg per-region |
0.010 pp |
0.114 pp |
EV WORLD gap |
0.35% |
1.71% |
Regions with EV gap <1% |
2/2 |
8/10 |
cgebox tolerance |
5% |
5% |
The two outlier regions in 9x10 (SEAsia, EU_28) have extreme |dpsave|
(>14% and >26% respectively) — the linear-trajectory approximation in the
shadow integrator degrades when the closure absorbs very large
trade-balance imbalances. The remaining 8 regions reproduce RunGTAP
welfare to within 1% — well inside the cgebox-tolerated 5% residual band.
Full reproducible workspace lives under runs/nus333_compare/ and
runs/9x10_compare/. See verify_shadow_*.py, compare_*_report.py
and the corresponding comparison.md files.
References¶
Huff, K.M. and T.W. Hertel (1996), “Decomposing Welfare Changes in the GTAP Model,” GTAP Technical Paper 5.
Hertel, T.W. (1997), Global Trade Analysis: Modeling and Applications, Cambridge University Press, Chapter 3.
gtapv7.tab section 11 (Equivalent Variation), in the GTAPv7 tablo source distributed with RunGTAP 3.75.
McDougall, R.A. (2003), “A New Regional Household Demand System for GTAP,” GTAP Technical Paper 20.