Skip to main content

cgelab

tests site license: MIT

A general toolkit for SAM-based computable general equilibrium models in Python. You supply a social accounting matrix. The package calibrates a standard nested CGE from it, solves each period, runs a recursive-dynamic path and measures welfare. The accounting checks that make any of it believable are built in rather than left to the user.

A model is assembled from three separable objects, and keeping them separable is the point:

Object Says
SAM what the base year was
ModelSpec what is assumed about substitution and functional form
Closure which balances are imposed and who adjusts

Results can turn on the last two, so both travel with the results instead of living in a branch inside a solver.

Install

pip install cgelab                 # from PyPI
pip install "cgelab[parquet]"      # adds pyarrow for .parquet SAMs
pip install "cgelab[dsge]"         # adds dsgelab, for forward-looking investment
pip install "cgelab @ git+https://github.com/GustavoHM/cgelab"   # the current main branch

For development:

git clone https://github.com/GustavoHM/cgelab && cd cgelab
pip install -e ".[test]" && pytest

Requires Python 3.11+, numpy, scipy and pandas. Try it with no data at all:

cgelab                    # runs the bundled toy economy and prints the diagnostics
cgelab my_sam.csv --sigma-armington 2.0 --sigma-va 0.5   # the same on your own SAM

Quick start

The bundled toy economy has every SAM entry written out, so the base year is known on paper and this runs as it is:

import cgelab as cge
from cgelab.examples import toy

sam, econ = toy.build()
print(cge.report(econ))           # base-year reproduction, Walras, homogeneity

base  = cge.solve_period(econ)
shock = cge.solve_period(econ, scenario=cge.Scenario(
    name="tax +50%", product_tax=econ.ptax * 1.5))
print(shock.summary())

A counterfactual over a horizon, and its welfare incidence:

growth = cge.Growth(labour=0.005, tfp=0.010, depreciation=0.05, discount=0.05)
years  = range(2021, 2046)

W   = cge.consumer_wedge(econ, {"c_ind": 0.03})
ref = cge.run_path(econ, years=years, growth=growth)
alt = cge.run_path(econ, years=years, growth=growth,
                   scenario=lambda y, t: cge.Scenario(consumer_wedge=W,
                                                      wedge_revenue_to="capital")
                                         if y >= 2026 else cge.Scenario())

ev = cge.equivalent_variation(ref, alt)
print(cge.ev_by_group(ev, {"hh_1": "rich", "hh_2": "poor"}))

With your own data, the first lines change (a .parquet SAM needs the parquet extra):

sam  = cge.SAM.read("my_sam.parquet")       # or SAM.from_long(df) / SAM.from_square(df)
sam.check_balance()                         # a hard gate, not a warning
econ = cge.calibrate(sam, cge.ModelSpec(sigma_armington=2.0, sigma_va=0.5))

Model structure

  • Production: Leontief between the intermediate bundle and value added, and within the intermediate bundle. A configurable nest (CES, Cobb–Douglas or Leontief) combines factors inside value added.
  • Make: commodity market shares are fixed at base-year proportions, so activity levels follow from commodity output, X = MSH @ Q.
  • Armington: CES between domestic and imported varieties.
  • Exports: fixed shares by default, or a CET frontier that answers to world export prices. The default is deliberate: the CET elasticity is the one parameter of a standard CGE that input–output sources do not supply.
  • Households: Cobb–Douglas or LES over commodities, out of income net of direct tax, saving and payments made straight to an activity. Income includes government transfers and net transfers from abroad.
  • Ownership: factor income is shared among households, government and the rest of the world. Returns to foreign-owned capital leave through the current account, and foreign saving is the current account, not the trade balance.
  • Government, investment, foreign: five closure combinations, each with its own adjusting variable (see Closure).
  • Dynamics: recursive, with one or several capital stocks accumulated from their service flows, investment shared by relative rent, FDI that builds foreign-owned capital, and an economy that may change year by year.

The numeraire factor's market is deliberately not solved. It is left to Walras' law, so its residual is an independent check on the accounting rather than an identity the solver was told to satisfy. It is recorded on every period.

What is in the box

Capability Entry point
SAM container, balance gate, sign-preserving RAS; GEMPACK/GTAP header array files SAM, read_har, write_har
Calibration: coefficients, nests, taxes (tariff and output tax inside prices), margins, LES subsistence, institutional flows, a list of cells the model does not read calibrate
Within-period equilibrium; investment, government (including deficit and revenue targets), foreign and labour closures solve_period, Closure
Trade: Armington, CET, export demand and import supply curves (large country) ModelSpec
Energy: a value-added-energy or capital-energy nest with interfuel substitution, emissions accounts, a carbon price, physical units ModelSpec.energy, add_emissions, add_physical_units
Counterfactuals: taxes, wedges, endowments, ownership, productivity, own-account levels, world prices, FDI, the investment basket Scenario
Recursive dynamics: several capital stocks, investment allocation, foreign-owned capital, a per-year economy run_path, Growth
Forward-looking investment (a Ramsey CGE solved by dsgelab) cgelab.bridge
Accounts: GDP from both sides, Laspeyres, Paasche and Fisher indices, terms of trade, the government account national_accounts, price_indices, government_account
Decompositions: subtotals by shock, Huff-Hertel welfare decomposition subtotals, welfare_decomposition
SAM multipliers, linkages, structural path analysis accounting_multipliers, linkages, structural_paths
Systematic sensitivity: Stroud quadrature and Monte Carlo over elasticities sensitivity, spec_sweep
Welfare: EV, CV and composition-immune group aggregation cgelab.welfare
Base-year reproduction, homogeneity, Walras cgelab.diagnostics
The same equilibrium as a JuMP model, for independent validation to_jump

Documentation

Verification

Every number the package produces is checked against something it did not compute itself:

  • Hand-built SAMs whose every entry is written out, so the base year is known on paper: base-year reproduction, zero-degree homogeneity and Walras' law in every closure, on every variant (margins, own-account activities, institutions, trade taxes, CET, energy nests, carbon).
  • Closed forms and published tables: Miller and Blair's Leontief inverse, the stochastic growth model (for the bridge to dsgelab), Stroud quadrature on cubics, CES first-order conditions, and the identities that hold in every equilibrium (GDP from both sides, the welfare decomposition, subtotals that add up).
  • A published model: the tariff abolition of Hosoe, Gasawa and Hashimoto's stdcge and lrgcge against a line-by-line JuMP transcription of the GAMS programs, in all 49 displayed variables, to 2.4e-12.
  • An independent replica: to_jump writes the same equilibrium as a JuMP model in which nothing is nested. validation/roundtrip_jump.py runs 34 cases; the nested solver and Ipopt agree to 1.2e-11.
pytest                                                     # the suite, without external tools
cd validation && julia --project=. -e 'using Pkg; Pkg.instantiate()' && cd ..
python validation/roundtrip_jump.py                        # the JuMP replica
python validation/replicate_hosoe.py                       # stdcge and lrgcge

Scope and known limitations

cgelab covers a single-country CGE at applied scale. It does not have the maturity of GAMS or GEMPACK, and docs/comparison.md says where it stands. Things to know before relying on it:

  • Several regions share one origin nest under the Armington. Factors do not migrate, and the regional slice refuses margins, CET, LES, trade taxes, energy nests and most closures by name. GTAP-style bilateral trade is 0.7.
  • No complementarity: activities cannot switch off, and there are no quotas, price floors or capacity limits. That is 0.8.
  • The solver uses finite-difference Jacobians on a few unknowns. It is fine up to a few hundred accounts; each commodity put on a trade curve adds an unknown.
  • Fixed proportions everywhere (sigma_va = 0 in every activity) can leave the model without a solution under full employment of every factor.
  • Under fixed export shares with a Cobb-Douglas Armington, the exchange rate is not determined, and solve_period refuses that combination; near an Armington elasticity of one it is badly conditioned.
  • Forward-looking investment goes through dsgelab (pip install "cgelab[dsge]"). run_path itself has no forward-looking agent.
  • GTAP data can be read, but not yet aggregated or turned into a SAM, and the welfare decomposition has not been compared with GTAP's own.
  • Platforms: an editable install (pip install -e) can break silently on Python 3.14 on macOS; install from the wheel if import cgelab fails.

Citing

If a published result went through this package, cite it. Software is a methodological input like any other, and the version matters: a number that only reproduces under one version is not reproducible without it. CITATION.cff carries the metadata, with the author's ORCID, in the format GitHub reads. A repository URL is not an archivable reference: each release will be archived on Zenodo, and the DOI it receives, which will be added to that file, is the reference to cite.

License

MIT. See LICENSE.

Metadata

Release files for cgelab 0.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for cgelab 0.5.0
File Size Uploaded
cgelab-0.5.0.tar.gz 199.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for cgelab 0.5.0
File Interpreter ABI Platform
cgelab-0.5.0-py3-none-any.whl Python 3 none any Details

Total release size: 323.6 kB

Release files / cgelab-0.5.0.tar.gz

Download URL cgelab-0.5.0.tar.gz
Size 199.1 kB
Tags Source
SHA-256 checksum
How to use checksums
39c33727e008dffb4a89d09d977d7c0ad769b15ccbfe9fd51210f78431aa2582
BLAKE2b-256 checksum
How to use checksums
b1b51882718bf692ac3a5b2ec1aa0f1f1ae6ed31d46f644c3bb60e466f6c6a05
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 18, 2026.

Transparency log

Release files / cgelab-0.5.0-py3-none-any.whl

Download URL cgelab-0.5.0-py3-none-any.whl
Size 124.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2d28f862a4eaf47542419b3c3157b0ac983c3b4f6d8fbde89f649cfde0ad7e54
BLAKE2b-256 checksum
How to use checksums
595c70ad08548144ced0ddec57b413f562cc43f2a37ccc3d314606fe616fb1f9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 18, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.5.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page