IFPRI Standard CGE replication#
CGE-Core includes an independently written Python/Pyomo implementation of the IFPRI Standard CGE test economy. With the externally supplied IFPRI test data, it reproduces the BASE benchmark and five recorded policy simulations to numerical solver tolerance.
This is a precise, bounded claim. It does not mean that CGE-Core reproduces every country application, database, closure, or later variant built with the IFPRI Standard CGE framework.
What is and is not distributed#
The repository contains the clean-room Python equations, calibration,
closures, scenario definitions, tests, and full-precision validation targets.
It does not contain the official IFPRI source package or test.dat.
Point IFPRI_SOURCE_DIR to the folder containing your separately obtained
test.dat:
$env:IFPRI_SOURCE_DIR = "C:\path\to\ifpri-test-folder"
set IFPRI_SOURCE_DIR=C:\path\to\ifpri-test-folder
The directory is resolved only at runtime. The source file is parsed and validated but is neither copied into CGE-Core nor added to package artifacts.
If IFPRI_SOURCE_DIR is set but the directory does not contain test.dat,
the external replication tests fail deliberately rather than silently
skipping. Correct or unset the environment variable before rerunning them.
Load and calibrate the benchmark#
from cge_core.models.ifpri import (
calibrate_ifpri_benchmark,
load_ifpri_test_data,
validate_ifpri_calibration,
)
dataset = load_ifpri_test_data()
calibration = calibrate_ifpri_benchmark(dataset)
validate_ifpri_calibration(dataset, calibration)
The loader validates set relationships, SAM membership and balance, elasticity coverage and ranges, home-consumption shares, factor quantities, and tax mappings. Calibration is algebraic: it reconstructs the normalized benchmark prices and quantities before any nonlinear solve is attempted.
Solve BASE#
from cge_core.models.ifpri import (
build_ifpri_base_solve_model,
perturb_ifpri_start,
solve_ifpri_base,
)
base_model = build_ifpri_base_solve_model(dataset, calibration)
perturb_ifpri_start(base_model, 1.02)
base_report = solve_ifpri_base(base_model)
print(base_report.termination_condition)
print(base_report.max_abs_equation_residual)
The BASE closure fixes the CPI numeraire, foreign saving, investment scaling, and government-demand scaling; applies the recorded labor and activity-specific-capital closures; and minimizes the squared Walras residual. The solve is accepted only after an optimal or locally optimal termination.
Run the policy simulations#
from cge_core.models.ifpri import build_and_solve_ifpri_scenarios
results = build_and_solve_ifpri_scenarios(dataset)
results maps each scenario to (model, solve_report):
Scenario |
Shock and closure |
|---|---|
|
50% tariff cut; flexible government saving |
|
50% tariff cut; fixed government saving and uniform direct-tax adjustment |
|
10% increase in foreign saving |
|
10% increase in world import prices |
|
10% devaluation under a fixed-exchange-rate closure |
Extract and report results#
The reporting API returns pandas DataFrames rather than printing model internals:
from pathlib import Path
from cge_core.models.ifpri import (
compare_ifpri_scenarios,
extract_ifpri_solution,
summarize_ifpri_results,
)
base_values = extract_ifpri_solution(base_model)
summary = summarize_ifpri_results(results)
changes = compare_ifpri_scenarios(base_model, results)
output = Path("ifpri-results")
output.mkdir(exist_ok=True)
base_values.to_csv(output / "base_values.csv", index=False)
summary.to_csv(output / "solve_summary.csv", index=False)
changes.to_csv(output / "scenario_changes.csv", index=False)
extract_ifpri_solution produces one row per active variable element, with up
to three explicit index columns, the value, and whether it is fixed.
compare_ifpri_scenarios reports scenario minus BASE and the percentage
change. Percentage changes are left undefined where the BASE value is
numerically zero. The comparison includes variables common to both models;
scenario-only closure variables can still be extracted directly from the
scenario model.
A smaller report can be requested by component name:
changes = compare_ifpri_scenarios(
base_model,
results,
components=("EXR", "CPI", "QA", "QH", "QM", "QE"),
)
Validate against the GAMS reference#
Within a repository checkout, the full-precision target table can be used for an explicit comparison:
from pathlib import Path
from cge_core.models.ifpri import (
compare_ifpri_model_to_reference,
load_ifpri_reference_targets,
)
reference = Path(
"validation/gams/ifpri_standard/reference/full_precision_targets.csv"
)
targets = load_ifpri_reference_targets(reference, "NLP", "BASE")
comparison = compare_ifpri_model_to_reference(base_model, targets)
print(comparison.compared_values)
print(comparison.max_abs_difference)
print(comparison.max_relative_difference)
The external replication suite checks BASE and all five scenarios against the
full-precision targets. Public GitHub Actions cannot use the official
test.dat, so it instead exercises the same calibration, equation, closure,
shock, reporting, and IPOPT pathways with an independently authored,
redistributable synthetic economy. External-data tests and public tests are
marked separately so that unavailable official data cannot be mistaken for a
successful replication run.
Clean-room boundary#
The Python equations were implemented independently from public mathematical descriptions. Official GAMS source files remain outside the repository and are used only to produce or verify external reference results. The public synthetic fixture is independently authored and is not copied or derived from the official IFPRI test dataset.