diff --git a/docs/advanced.md b/docs/advanced.md index 89464f4..3e30cd5 100644 --- a/docs/advanced.md +++ b/docs/advanced.md @@ -20,7 +20,11 @@ from cge_core import CGE, PyCGE, example_data from cge_core.models import StdCGE ``` -The lower-level PyCGE lifecycle remains an escape hatch for debugging, validation, and engine development. New tutorials use the model façades instead. +For SimpleCGE, StandardCGE, and CamCGE, the generic `CGE → Equilibrium → Scenario → Result` workflow sits above `CoreEngine`, which in turn subclasses the retained `PyCGE` engine. These layers remain escape hatches for debugging, validation, and engine development. + +IFPRI has its own advanced API under `cge_core.models.ifpri`; it shares the Pyomo/solver boundary without being routed through `CoreEngine`. + +New tutorials use the model façades instead. ## Solver override @@ -40,4 +44,4 @@ Use `cge doctor` to inspect what CGE-Core detects. ## Why the public layer exists -The public API encodes recurring software decisions—model construction, canonical closure, scenario isolation, result snapshots and solver selection—so practitioner code can expose the economic decisions instead of repeating framework plumbing. +The public API encodes recurring software decisions—model construction, model-owned closure, counterfactual isolation, result inspection and solver selection—so practitioner code can expose the economic decisions instead of repeating framework plumbing. The implementation is allowed to differ by model family when those differences are economically meaningful. diff --git a/docs/api/public.md b/docs/api/public.md index a07250f..4974ca9 100644 --- a/docs/api/public.md +++ b/docs/api/public.md @@ -6,6 +6,10 @@ from cge_core import SimpleCGE, StandardCGE, CamCGE, IFPRICGE ``` +The four façades share a practitioner vocabulary—solve a benchmark, create a policy +counterfactual, solve again, and inspect results—but they do not all use the same internal +workflow classes. + ### SimpleCGE ```python @@ -43,9 +47,13 @@ base = IFPRICGE.synthetic().solve() result = base.scenario("TARCUT1").solve() ``` -## Equilibrium +IFPRI deliberately uses its own `IFPRIEquilibrium`, `IFPRIScenario`, and `IFPRIResult` +classes because its calibration, macro closure, and named policy experiments are +model-specific. + +## Generic Equilibrium -A solved benchmark supports: +For SimpleCGE, StandardCGE, and CamCGE, a solved benchmark supports: - `summary()` - `value(component, *index)` @@ -53,7 +61,7 @@ A solved benchmark supports: - `.raw` as an advanced escape hatch - `.closure` for model-owned closure information -## Scenario +## Generic Scenario The Hosoe/CAMCGE scenario object supports: @@ -64,11 +72,11 @@ The Hosoe/CAMCGE scenario object supports: - `endowment(factor, value=None, change=None)` where declared by the model - `solve()` -A scenario is an independent mutable counterfactual derived from a protected benchmark. +A generic scenario is an independent mutable clone derived from a protected benchmark. -## Result +## Generic Result -A solved result supports: +A generic solved result supports: - `summary()` - `value(component, *index)` @@ -78,14 +86,25 @@ A solved result supports: Ordinary numerical reads come from the result snapshot, not from later mutation of a live Pyomo object. -## Lower-level lifecycle +## IFPRI result surface + +`IFPRIEquilibrium` and `IFPRIResult` provide the corresponding `summary()`, `value()`, +`.raw`, and comparison operations appropriate to the IFPRI implementation. IFPRI scenarios +are named model-specific experiments rather than the generic semantic-shock object. -The lower-level public lifecycle remains available: +See {doc}`ifpri` for the advanced IFPRI API. + +## Lower-level generic lifecycle + +The lower-level generic lifecycle remains available: ```python from cge_core import CGE ``` `CGE → Equilibrium → Scenario → Result` is retained for downstream and advanced code. -The v0.8 façades configure this lifecycle for bundled models so ordinary users do not have -to supply closure and solver plumbing themselves. +`SimpleCGE`, `StandardCGE`, and `CamCGE` configure this lifecycle so ordinary users do not +have to supply closure and solver plumbing themselves. + +`IFPRICGE` intentionally uses its separate IFPRI adapter rather than being forced through +this generic lifecycle. diff --git a/docs/architecture.md b/docs/architecture.md index 5af4d49..6e5fcff 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -1,20 +1,23 @@ # Architecture -CGE-Core v0.8 separates five concerns: +CGE-Core v0.8 separates six concerns: 1. **economic model definitions**; 2. **benchmark data and calibration**; -3. **model-owned closure**; -4. **counterfactual workflow**; and -5. **numerical result snapshots**. +3. **model-owned closure and policy metadata**; +4. **counterfactual workflow**; +5. **numerical result interfaces**; and +6. **solver resolution**. -The public interface is deliberately smaller than the implementation. +The public interface is deliberately smaller than the implementation. The four bundled +model families present a similar modelling workflow, but v0.8 does **not** force them +through one internal implementation when their economics or validation requirements differ. ## Practitioner-first public architecture ```{mermaid} diagrams/cge-core-v080-public.mmd :name: cge-core-v080-public-architecture -:alt: CGE-Core v0.8 architecture showing practitioner model facades, benchmark and scenario workflow, model-specific economics, the lower-level engine, Pyomo, and the nonlinear solver. +:alt: CGE-Core v0.8 architecture showing the generic Simple, Standard, and CAMCGE workflow beside the IFPRI-specific workflow, converging on Pyomo and centralized nonlinear-solver resolution. ``` Use the mouse wheel or a trackpad pinch gesture to **zoom**, drag to **pan**, or select @@ -28,7 +31,8 @@ Use the mouse wheel or a trackpad pinch gesture to **zoom**, drag to **pan**, or ``` ```` -For ordinary work, the modeller sees: +For the Hosoe Simple, Hosoe Standard, and CAMCGE families, the modeller sees the generic +scientific lifecycle: ```text StandardCGE.example() @@ -45,21 +49,32 @@ Result A.compare(Equilibrium) Result A.compare(Result B) ``` -`SimpleCGE`, `StandardCGE`, and `CamCGE` supply their canonical closure automatically. -`IFPRICGE` retains the IFPRI model's own closure and named scenario machinery. +`SimpleCGE`, `StandardCGE`, and `CamCGE` configure this lifecycle with their own model +definition and `ModelSpec`. Their canonical closure is therefore model-owned even though +the surrounding workflow is shared. -A scenario owns one independent concrete model clone. The benchmark remains protected. -A solved result exposes numerical snapshots for ordinary inspection so that users do not -need to traverse mutable Pyomo objects. +`IFPRICGE` deliberately follows a parallel path. It exposes the same benchmark → scenario → +solve → inspect idea through `IFPRIEquilibrium`, `IFPRIScenario`, and `IFPRIResult`, while +retaining IFPRI-specific calibration, named policy scenarios, and macro-closure machinery. +It does not pass through the generic `CGE` / `CoreEngine` workflow merely for architectural +symmetry. -## Underlying PyCGE architecture +For the generic Simple/Standard/CAMCGE path, a scenario owns one independent clone of the +calibrated benchmark. The benchmark remains protected, and solved results expose immutable +numerical snapshots for ordinary inspection. IFPRI provides its own result objects over its +model-specific solved states. -The mature lower-level engine remains part of v0.8 and is useful for implementation, -validation, and advanced engine-level work. +## Generic workflow and the retained PyCGE engine + +The generic v0.8 path has three distinct software layers: + +1. `CGE → Equilibrium → Scenario → Result` is the **scientific workflow layer**; +2. `CoreEngine` is the **CGE-Core policy adapter**; and +3. `PyCGE` is the retained **lower-level engine mechanics**. ```{mermaid} diagrams/pycge-architecture.mmd :name: pycge-software-architecture -:alt: PyCGE software architecture showing data, model definition, workflow engine, solver, benchmark, simulation, and results layers. +:alt: CGE-Core v0.8 generic workflow architecture showing model definition and ModelSpec, the workflow layer, CoreEngine over PyCGE, Pyomo, centralized solver resolution, and a supported nonlinear backend. ``` ````{dropdown} Mermaid source @@ -70,20 +85,63 @@ validation, and advanced engine-level work. ``` ```` -The practitioner façades do **not** rewrite the validated economic algebra. They configure -and call the model-specific implementation while hiding routine framework plumbing. +### `ModelSpec` + +`ModelSpec` carries model-specific software policy that should not be guessed from component +names: default closure metadata, protected benchmark components, semantic policy shocks, and +required data declarations. Economic equations remain in the model-definition modules. + +### `CoreEngine` + +`CoreEngine` is intentionally small. It subclasses `PyCGE` and changes the protection policy +so benchmark/base protection comes from `ModelSpec` rather than historical naming rules such +as a trailing `0`. + +### `PyCGE` + +The retained lower-level engine owns the mature mechanics used by the generic workflow: +instance construction, mutation, undo/rollback, solver execution, and result bookkeeping. +Advanced users can still import it intentionally with: + +```python +from cge_core import PyCGE +``` + +The practitioner façades do **not** rewrite validated economic algebra. They configure and +call the appropriate model-specific implementation while hiding routine framework plumbing. + +## Solver boundary + +Ordinary code calls `.solve()` rather than managing solver installation or `PATH` state. +`cge_core.solver` resolves a supported nonlinear backend and is shared by the generic and +IFPRI paths. It prefers a usable system Ipopt, then a working `cyipopt`, and otherwise can +prepare the packaged COIN/Ipopt NL route used through Pyomo. + +The architecture therefore separates an economic modelling decision from the numerical +backend used to solve it. ## Model-family boundaries -| Family | v0.8 role | Closure | +| Family | v0.8 public path | Closure | | --- | --- | --- | -| Hosoe Simple | Teaching / closed-economy benchmark | Model-owned canonical closure | -| Hosoe Standard | Generic open-economy policy model | Model-owned canonical closure | -| CAMCGE | First-class installed historical replication | CAMCGE-specific closure | -| IFPRI Standard | Separate richer institutional model | IFPRI-specific closure and named scenarios | +| Hosoe Simple | Generic `CGE → Equilibrium → Scenario → Result` | Model-owned canonical closure | +| Hosoe Standard | Generic `CGE → Equilibrium → Scenario → Result` | Model-owned canonical closure | +| CAMCGE | Generic `CGE → Equilibrium → Scenario → Result` | CAMCGE-specific closure | +| IFPRI Standard | IFPRI-specific equilibrium/scenario/result adapter | IFPRI-specific closure and named scenarios | + +The project therefore shares practitioner semantics without pretending there is one +universal CGE equation template or one mandatory internal workflow. + +## Experimental authoring boundary + +The optional authoring tools live only under `cge_core.experimental`: + +- `cge_core.experimental.authoring` adapts functional Python models; and +- `cge_core.experimental.spec` implements the deterministic `.cge.md` specification. -The project therefore shares workflow semantics without pretending there is one universal -CGE equation template. +They are intentionally outside the main bundled-model pipeline. Experimental authoring can +evolve before 1.0 without forcing the validated Hosoe, CAMCGE, or IFPRI implementations to +be rewritten around it. ## The Standard CGE in economic blocks @@ -106,10 +164,11 @@ CGE equation template. | Production | {doc}`theory/production` | {doc}`MODEL` | `StandardCGE.example().solve()` | | Final demand | {doc}`theory/final-demand` | {doc}`MODEL` | result inspection | | Trade | {doc}`theory/trade` | {doc}`MODEL` | `scenario.tariff(...)` | -| Closure / Walras' law | {doc}`theory/closure` | {doc}`workflow` | automatic for bundled models | +| Closure / Walras' law | {doc}`theory/closure` | {doc}`workflow` | model-owned for bundled models | | SAM loading | {doc}`theory/sam` | {doc}`workflow` | `StandardCGE.from_sam(...)` | | Policy simulation | {doc}`getting-started/first-simulation` | {doc}`workflow` | benchmark → scenario → result | -| Advanced engine inspection | {doc}`api/engine` | {doc}`workflow` | `.raw` / lower-level API | +| IFPRI scenarios | {doc}`models/ifpri` | {doc}`IFPRI` | `IFPRICGE.synthetic().solve()` | +| Advanced engine inspection | {doc}`api/engine` | {doc}`workflow` | `.raw` / `PyCGE` | The intended reading path is: diff --git a/docs/bundled_models.md b/docs/bundled_models.md index d7b2e96..3d026e6 100644 --- a/docs/bundled_models.md +++ b/docs/bundled_models.md @@ -1,6 +1,6 @@ # Choose a bundled model -CGE-Core 0.8.0 gives four model families a similar lifecycle while preserving their different economics. +CGE-Core 0.8.0 gives four model families a similar practitioner workflow while preserving their different economics and, where appropriate, different internal execution paths. | Model | Best used for | Important boundary | |---|---|---| @@ -41,4 +41,4 @@ from cge_core import IFPRICGE base = IFPRICGE.synthetic().solve() ``` -Named scenarios include `TARCUT1`, `TARCUT2`, `FSAVINCR`, `PWMINCR`, and `DEVAL`. Their meaning depends on the IFPRI macro closure, so they are intentionally model-specific rather than generic policy toggles. +Named scenarios include `TARCUT1`, `TARCUT2`, `FSAVINCR`, `PWMINCR`, and `DEVAL`. Their meaning depends on the IFPRI macro closure, so they are intentionally model-specific rather than generic policy toggles. `IFPRICGE` therefore uses IFPRI-specific equilibrium/scenario/result classes rather than the generic `CGE → Equilibrium → Scenario → Result` implementation. diff --git a/docs/developer/overview.md b/docs/developer/overview.md index 4bbebc9..a03a11d 100644 --- a/docs/developer/overview.md +++ b/docs/developer/overview.md @@ -1,38 +1,88 @@ # Developer reference -CGE-Core's source tree is organized around the concepts a modeller actually -needs to understand. +CGE-Core v0.8 is organized around **two bundled execution paths** that share a numerical +boundary but do not pretend to be one implementation. ```text cge_core/ -├── workflow.py benchmark → scenario → result lifecycle -├── _engine.py model-declared engine policy -├── _pycge.py inherited lower-level PyCGE engine -├── solver.py numerical-backend resolution +├── workflow.py generic CGE → Equilibrium → Scenario → Result lifecycle +├── model_spec.py model-owned closure/protection/semantic metadata +├── _engine.py small ModelSpec-driven policy adapter over PyCGE +├── _pycge.py retained lower-level PyCGE engine mechanics +├── _shared.py shared comparison/index/rollback helpers +├── solver.py centralized numerical-backend resolution ├── sam.py social-accounting-matrix tools -├── models/ bundled economic model families -│ ├── simple/ -│ ├── standard/ -│ ├── camcge/ -│ └── ifpri/ -└── experimental/ optional authoring and .cge.md work +├── models/ +│ ├── _accounts.py shared model-account validation helpers +│ ├── simple/ Hosoe Simple CGE +│ ├── standard/ Hosoe Standard CGE +│ ├── camcge/ CAMCGE implementation and packaged data +│ └── ifpri/ IFPRI-specific calibration, closure, scenarios and reporting +└── experimental/ + ├── authoring/ functional Python model adapter + └── spec/ deterministic .cge.md parser/compiler ``` +## Two bundled execution paths + +### SimpleCGE / StandardCGE / CamCGE + +These façades configure the generic scientific lifecycle: + +```text +model definition + ModelSpec + ↓ +CGE → Equilibrium → Scenario → Result + ↓ + CoreEngine + ↓ + PyCGE + ↓ + Pyomo +``` + +`CoreEngine` deliberately contains little mechanics of its own. It subclasses `PyCGE` and +uses `ModelSpec` to replace historical name-based protection rules with explicit model-owned +policy. + +### IFPRICGE + +IFPRI retains its own calibrated execution path: + +```text +IFPRICGE + ↓ +IFPRI calibration / closure / named scenarios + ↓ +IFPRIEquilibrium → IFPRIScenario → IFPRIResult + ↓ +Pyomo +``` + +This is intentional. IFPRI shares practitioner semantics and solver policy with the other +families, but its richer institutional model and validation machinery do not need to be +forced through `CoreEngine` merely for file-level symmetry. + +Both paths use `cge_core.solver` to resolve a supported nonlinear backend. + ## Where to make a change -- **Economic equations or calibration:** `cge_core/models//`. -- **Benchmark/scenario/result behavior:** `cge_core/workflow.py`. -- **Solver detection and automatic setup:** `cge_core/solver.py`. +- **Hosoe/CAMCGE equations or calibration:** `cge_core/models//`. +- **Generic benchmark/scenario/result behavior:** `cge_core/workflow.py`. +- **Generic model policy and closure metadata:** `cge_core/model_spec.py`. +- **Model-protection policy adapter:** `cge_core/_engine.py`. +- **Lower-level PyCGE mechanics:** `cge_core/_pycge.py`. +- **IFPRI calibration, closure, scenarios, solve/reporting:** `cge_core/models/ifpri/`. +- **Solver detection and first-use setup:** `cge_core/solver.py`. - **SAM conversion and validation:** `cge_core/sam.py`. - **Function-based or `.cge.md` authoring:** `cge_core/experimental/`. -- **Lower-level PyCGE engine:** `cge_core/_pycge.py`. Historical redirect modules are intentionally absent in v0.8. Migration paths are -documented in `migration-v0.8.md` rather than duplicated as executable modules. +documented in {doc}`../migration-v0.8` rather than duplicated as executable modules. -The model families share a home and a public lifecycle, not ceremonial file -symmetry. Simple CGE stays simple; IFPRI keeps the extra modules its economics -and validation genuinely require. +The model families share a home and a practitioner vocabulary, not ceremonial file +symmetry. Simple CGE stays simple; IFPRI keeps the extra modules its economics and +validation genuinely require. The ordinary interface remains: @@ -46,5 +96,5 @@ result = scenario.solve() result.compare(base) ``` -This cleanup does not alter equations, calibration rules, closures, data, or -numerical validation targets. +The v0.8 architectural cleanup does not intentionally alter equations, calibration rules, +closures, data, or numerical validation targets. diff --git a/docs/diagrams/cge-core-v080-public.mmd b/docs/diagrams/cge-core-v080-public.mmd index 0c1ca03..1de1325 100644 --- a/docs/diagrams/cge-core-v080-public.mmd +++ b/docs/diagrams/cge-core-v080-public.mmd @@ -1,42 +1,50 @@ -flowchart LR +flowchart TB U["Practitioner"] - F["Model façade
SimpleCGE · StandardCGE
CamCGE · IFPRICGE"] - B["Solved benchmark
Equilibrium"] - S["Independent scenario
one concrete model clone"] - R["Immutable numerical result
summary · value · compare"] - - subgraph ECON["Model-specific economics"] - H["Hosoe Simple / Standard"] - C["CAMCGE"] - I["IFPRI Standard CGE"] + + subgraph PUBLIC["Practitioner entry points"] + G["SimpleCGE · StandardCGE · CamCGE"] + I["IFPRICGE"] + end + + subgraph GENERIC["Generic Simple / Standard / CAMCGE path"] + GW["Scientific workflow
CGE → Equilibrium → Scenario → Result"] + MD["Model definition + ModelSpec"] + CE["CoreEngine
model-declared policy"] + PY["PyCGE
instance · mutation · rollback · solve bookkeeping"] end - subgraph CORE["CGE-Core lower-level engine"] - A["CGE lifecycle"] - E["CoreEngine / PyCGE"] + subgraph IFPRI["IFPRI-specific path"] + IW["IFPRI workflow
IFPRIEquilibrium → IFPRIScenario → IFPRIResult"] + IM["IFPRI calibration · closure
named scenario builders"] end - P["Pyomo model"] - N["NLP solver"] + subgraph NUM["Shared numerical boundary"] + P["Pyomo model"] + SR["cge_core.solver
backend resolution"] + N["Supported NLP backend
Ipopt · cyipopt · COIN/ipoptnl"] + end + + U --> G + U --> I + + G --> GW + GW --> MD + MD --> CE + CE --> PY + PY --> P - U --> F - F --> B - B --> S - S --> R + I --> IW + IW --> IM + IM --> P - F --> H - F --> C - F --> I - H --> A - C --> A - A --> E - I --> P - E --> P - P --> N + P --> SR + SR --> N classDef public fill:#eef4ff,stroke:#4f6fad,stroke-width:2px,color:#172033; - classDef econ fill:#edf8f4,stroke:#3c806b,stroke-width:1.5px,color:#172033; - classDef internal fill:#f6f2ff,stroke:#7252b8,stroke-width:1.5px,color:#172033; - class U,F,B,S,R public; - class H,C,I econ; - class A,E,P,N internal; + classDef generic fill:#f6f2ff,stroke:#7252b8,stroke-width:1.5px,color:#172033; + classDef ifpri fill:#edf8f4,stroke:#3c806b,stroke-width:1.5px,color:#172033; + classDef numerical fill:#fff7e8,stroke:#9a6b22,stroke-width:1.5px,color:#172033; + class U,G,I public; + class GW,MD,CE,PY generic; + class IW,IM ifpri; + class P,SR,N numerical; diff --git a/docs/diagrams/pycge-architecture.mmd b/docs/diagrams/pycge-architecture.mmd index 9ca2e83..edbc874 100644 --- a/docs/diagrams/pycge-architecture.mmd +++ b/docs/diagrams/pycge-architecture.mmd @@ -1,61 +1,52 @@ flowchart TB + USER["Model façade / advanced user"] - USER["User script / notebook"] - - subgraph INPUT["Data layer"] - SAM["SAM / model CSV files"] - SAMTOOLS["sam.build_dataset()"] - EXAMPLE["example_data()"] - DATA["Pyomo DataPortal"] + subgraph DEF["Model-specific definition and policy"] + MODEL["Economic model definition
sets · parameters · variables · equations"] + SPEC["ModelSpec
closure metadata · protection
semantic shocks · required data"] + DATA["SAM / packaged model data"] end - subgraph DEF["Economic model definition"] - SPL["SplModelDef"] - STD["StdModelDef"] - ABSTRACT["Pyomo AbstractModel
sets · parameters · variables · equations"] + subgraph WF["Generic scientific workflow"] + CGE["CGE"] + EQ["Equilibrium
protected benchmark + snapshot"] + SC["Scenario
one independent model clone"] + RES["Result
immutable numerical snapshot"] end - subgraph ENG["PyCGE workflow engine"] - LOAD["model_data()"] - INSTANCE["model_instance()"] - CLOSURE["Closure
fix numeraire + drop redundant equation"] - CAL["model_calibrate()"] - CLONE["model_sim()"] - SHOCK["model_modify_sim()"] - SOLVE["model_solve()"] - COMPARE["model_compare() / model_postprocess()"] + subgraph ENG["Engine layers"] + CORE["CoreEngine
ModelSpec-driven policy"] + PYCGE["PyCGE
instance construction · mutation
undo/rollback · solve bookkeeping"] end - subgraph RUN["Runtime state"] - BASE["BASE
ConcreteModel"] - SIM["SIM
deep copy of BASE"] - SOLVER["Nonlinear solver
IPOPT / cyipopt"] - RESULTS["Results
DataFrame / files"] + subgraph RUN["Numerical runtime"] + PYOMO["Pyomo ConcreteModel"] + SOLVER["cge_core.solver
resolve supported backend"] + NLP["NLP backend
Ipopt · cyipopt · COIN/ipoptnl"] end - USER --> SAMTOOLS - USER --> EXAMPLE - USER --> SPL - USER --> STD - SAM --> SAMTOOLS - SAMTOOLS --> LOAD - EXAMPLE --> LOAD - LOAD --> DATA - SPL --> ABSTRACT - STD --> ABSTRACT - ABSTRACT --> INSTANCE - DATA --> INSTANCE - INSTANCE --> BASE - BASE --> CLOSURE - CLOSURE --> CAL - CAL --> SOLVER - SOLVER --> BASE - BASE --> CLONE - CLONE --> SIM - SHOCK --> SIM - SIM --> SOLVE - SOLVE --> SOLVER - SOLVER --> SIM - BASE --> COMPARE - SIM --> COMPARE - COMPARE --> RESULTS + USER --> CGE + MODEL --> CGE + SPEC --> CGE + DATA --> CGE + + CGE --> CORE + SPEC --> CORE + CORE --> PYCGE + PYCGE --> PYOMO + PYOMO --> SOLVER + SOLVER --> NLP + NLP --> EQ + + EQ --> SC + SC --> PYCGE + NLP --> RES + + classDef model fill:#edf8f4,stroke:#3c806b,stroke-width:1.5px,color:#172033; + classDef workflow fill:#eef4ff,stroke:#4f6fad,stroke-width:1.5px,color:#172033; + classDef engine fill:#f6f2ff,stroke:#7252b8,stroke-width:1.5px,color:#172033; + classDef runtime fill:#fff7e8,stroke:#9a6b22,stroke-width:1.5px,color:#172033; + class MODEL,SPEC,DATA model; + class CGE,EQ,SC,RES workflow; + class CORE,PYCGE engine; + class PYOMO,SOLVER,NLP runtime; diff --git a/docs/intro.md b/docs/intro.md index a9da2eb..6f9ddcb 100644 --- a/docs/intro.md +++ b/docs/intro.md @@ -55,10 +55,13 @@ The shared surface is a workflow, not a claim that these models have the same eq ## Advanced lower-level API -The `CGE → Equilibrium → Scenario → Result` lifecycle and lower-level `PyCGE` -engine remain available for advanced or downstream code. They are intentional advanced -inspection paths; new practitioner material should start with the four model-specific -entry points above. +For `SimpleCGE`, `StandardCGE`, and `CamCGE`, the `CGE → Equilibrium → Scenario → Result` +lifecycle sits above `CoreEngine` and the retained lower-level `PyCGE` engine. `IFPRICGE` +uses its own IFPRI-specific equilibrium/scenario/result adapter while sharing the same Pyomo +and solver boundary. + +These are intentional advanced implementation distinctions; new practitioner material +should still start with the four model-specific entry points above. ```{note} CGE-Core is an independent project. It is not affiliated with or endorsed by the diff --git a/docs/workflow.md b/docs/workflow.md index daf6a51..bc630b9 100644 --- a/docs/workflow.md +++ b/docs/workflow.md @@ -2,22 +2,33 @@ ## Practitioner workflow +All bundled families expose the same modelling idea: + ```text -model façade +choose a model ↓ solve benchmark ↓ -protected Equilibrium - ↓ -independent Scenario +create policy scenario ↓ -economic shock +apply economic shock / named scenario ↓ solve counterfactual ↓ -Result +inspect and compare results +``` + +For the Hosoe Simple, Hosoe Standard, and CAMCGE families this is implemented by the generic +v0.8 workflow: + +```text +model façade + ↓ +protected Equilibrium + ↓ +independent Scenario ↓ -compare with benchmark +Result snapshot ``` Example: @@ -34,29 +45,53 @@ result = scenario.solve() result.compare(base) ``` +IFPRI keeps the same practitioner sequence through model-specific types and named scenarios: + +```python +from cge_core import IFPRICGE + +base = IFPRICGE.synthetic().solve() +result = base.scenario("TARCUT1").solve() +``` + +Internally this path uses `IFPRIEquilibrium`, `IFPRIScenario`, and `IFPRIResult` plus the +IFPRI calibration and closure machinery. It is intentionally separate from the generic +`CGE` / `CoreEngine` path. + ## Why solve the benchmark first? Calibration reconstructs model parameters and verifies that the benchmark data are consistent with the model's equilibrium structure. The benchmark is therefore the reference state against which a counterfactual is interpreted. -## Why is a scenario a clone? +## Why is a generic scenario a clone? + +For SimpleCGE, StandardCGE, and CamCGE, a policy experiment must not mutate the benchmark or +another policy experiment. v0.8 therefore creates one independent concrete model clone per +generic scenario. Multiple counterfactuals can coexist. -A policy experiment must not mutate the benchmark or another policy experiment. v0.8 creates -one independent concrete model clone per scenario. Multiple counterfactuals can therefore -coexist. +IFPRI instead constructs its named scenario model through the IFPRI-specific scenario +builder using the benchmark dataset and calibration. The user-facing economic idea is the +same even though the internal mechanism differs. ## Closure -Bundled model façades own their canonical closure. This removes routine numeraire and -redundant-equation bookkeeping from ordinary user code without pretending closure is -economically unimportant. +Closure is model-owned, not a universal framework default. -Advanced users can still work through the lower-level API when the closure itself is the -object of research. +- SimpleCGE, StandardCGE, and CamCGE declare canonical closure metadata through their model + configuration and `ModelSpec`. +- IFPRICGE retains IFPRI-specific macro closure and named scenario machinery. + +This removes routine numeraire and redundant-equation bookkeeping from ordinary user code +without pretending closure is economically unimportant. + +Advanced users can still work through lower-level or model-specific APIs when closure itself +is the object of research. ## Results -`Result` is a numerical snapshot of a successful solve. Use `value()`, `summary()`, and -`compare()` for ordinary analysis. `.raw` is available when direct Pyomo access is genuinely -needed. +For the generic Simple/Standard/CAMCGE path, `Result` is an immutable numerical snapshot of +a successful solve. Use `value()`, `summary()`, and `compare()` for ordinary analysis. +`.raw` is available when direct Pyomo access is genuinely needed. + +IFPRI provides corresponding `IFPRIResult` methods over its model-specific solved state.