Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions docs/advanced.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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.
39 changes: 29 additions & 10 deletions docs/api/public.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -43,17 +47,21 @@ 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)`
- `scenario(name)`
- `.raw` as an advanced escape hatch
- `.closure` for model-owned closure information

## Scenario
## Generic Scenario

The Hosoe/CAMCGE scenario object supports:

Expand All @@ -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)`
Expand All @@ -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.
113 changes: 86 additions & 27 deletions docs/architecture.md
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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()
Expand All @@ -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
Expand All @@ -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

Expand All @@ -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:

Expand Down
4 changes: 2 additions & 2 deletions docs/bundled_models.md
Original file line number Diff line number Diff line change
@@ -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 |
|---|---|---|
Expand Down Expand Up @@ -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.
94 changes: 72 additions & 22 deletions docs/developer/overview.md
Original file line number Diff line number Diff line change
@@ -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/<family>/`.
- **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/<family>/`.
- **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:

Expand All @@ -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.
Loading