Benchmarks

Variable-by-variable parity between the Python equilibria GTAP Standard 7 implementation and reference GAMS runs, plus wall-time benchmarks when GAMS can run locally. Numbers come from CSVs committed under docs/site/_data/benchmarks/ — Read the Docs renders this page from those files (it has no GAMS/PATH installed). Regenerate locally with:

make benchmarks           # all datasets (parity + MCP wall-time)
make benchmarks-nus333    # NUS333 only (also produces local parity + timing)
make benchmarks-nlp       # NLP wall-time: Python IPOPT vs GAMS local IPOPT

The default number of timing runs is BENCH_RUNS=5 (override on the make command line).

Each parity row reports, for one (dataset, phase, variable) triple, how many Pyomo Var cells match GAMS within tol_rel=1e-3 / tol_abs=1e-6 and the worst absolute / relative error observed. The __SUMMARY__ rows in the underlying CSV hold per-phase totals.

Hardware sensitivity: wall-time numbers depend on CPU, memory and filesystem. Parity (cell-level matching vs GAMS) is deterministic and identical across platforms, but solve times vary. Each section below is labelled with the host that produced it. Only compare ratios (Python vs GAMS-local) across machines.

Coverage matrix

The authoritative parity-coverage matrix (dataset × kind × ifSUB × phase, with per-row gap thresholds and CI status) is generated from scripts/gtap/coverage_matrix.py: see GTAP 7 Parity Coverage Matrix.

GTAP Standard 7 — 9 sectors × 10 regions

Reference: src/equilibria/templates/reference/gtap/output/COMP.gdx (rate-scaled 10% imptx shock, if_sub=False, rorflex=10).

Generated 2026-05-11T01:38:29Z from commit 1ba8d6d.

Parity vs GAMS NEOS reference

PhaseVars matchedCellsMatchDivergeMissingMatch rateResidualSolve time
base138/138599585995800
100.00%✓ match
2.22e-116.57s
shock138/138599785997800
100.00%✓ match
6.02e-137.59s

ℹ️ GAMS-local parity not available for 9x10. The model has ~10k equations and exceeds the GAMS community-license limit of 2500 rows/cols for nonlinear models. Only the NEOS reference run is used for 9x10.

GTAP Standard 7 — NUS333 (3 sectors × 3 regions × 3 factors)

Reference: output/nus333_neos/out.gdx (NEOS job 18744693, power-scaled 10% imptx shock, residual region ROW).

Generated 2026-05-12T00:09:18Z from commit e5b9385.

Parity vs GAMS NEOS reference

PhaseVars matchedCellsMatchDivergeMissingMatch rateResidualSolve time
base138/1381304130400
100.00%✓ match
1.98e-110.32s
shock138/1381310131000
100.00%✓ match
2.08e-070.32s

Parity vs GAMS local

PhaseVars matchedCellsMatchDivergeMissingMatch rateResidualSolve time
base138/1381304130400
100.00%✓ match
1.98e-110.32s
shock138/1381310131000
100.00%✓ match
2.08e-070.32s

Wall-time benchmark

Median / min / max / mean across the runs in nus333_timing.csv. The warm-up run is discarded — both sides solve from cold state then are re-run N times. Lower is better.

SolverNMedianMinMaxMean
Python equilibriaPATH C API, nonlinear full50.644s0.608s0.702s0.643s
GAMS localcomp_nus333.gms, PATH via GAMS 5350.848s0.769s0.917s0.831s
Median ratio Python / GAMS-local: 0.760×

NLP wall-time (Python IPOPT vs GAMS local IPOPT)

The MCP path uses PATH, which the GAMS community license caps at 1000 rows — so anything larger than ~NUS333 must go to NEOS and cannot be timed head-to-head locally. The NLP path uses IPOPT, an open-source solver GAMS does not license-cap, so both sides run on the same host up to gtap7_15x10. Python solves the full base→check→shock sequence with EQUILIBRIA_GTAP_SOLVE_NLP=1; GAMS runs the same bundle with ifMCP=0 + option nlp=ipopt. Regenerate with make benchmarks-nlp (from nlp_timing.csv).

Generated 2026-07-20T15:32:52Z from commit 8e55b9f. Warm-up run discarded; N timed runs per side. Lower is better.

DatasetModeifSUBNPython medianGAMS medianPython / GAMS
gtap7_3x3purepure050.331s0.809s0.41×
gtap7_3x3altertaxaltertax050.320s0.878s0.36×
gtap7_3x4altertaxaltertax050.461s1.015s0.45×
gtap7_5x5purepure051.078s1.244s0.87×
gtap7_5x5altertaxaltertax052.046s2.027s1.01×
gtap7_10x7purepure0513.050s7.113s1.83×
gtap7_10x7altertaxaltertax0529.293s21.352s1.37×
gtap7_15x10altertaxaltertax05494.149sno local ref
A ratio ≤ 1× means Python is at least as fast as GAMS-local on that row. Rows with no local ref are timed Python-only — they exceed what a local GAMS NLP reference was generated for, but IPOPT still solves them in-process (the historical large-model hang was PATH-specific, not IPOPT).