gds-stockflow
Declarative stock-flow DSL over GDS semantics — system dynamics with formal guarantees.
Table of Contents
- Quick Start
- What is this?
- Architecture
- Elements
- Semantic Type System
- Verification
- Examples
- Status
- Credits & Attribution
Quick Start
pip install gds-stockflow
from stockflow import (
Stock, Flow, Auxiliary, Converter,
StockFlowModel, compile_model, compile_to_system, verify,
)
# Declare a simple population model
model = StockFlowModel(
name="Population",
stocks=[Stock(name="Population", initial=1000.0)],
flows=[
Flow(name="Births", target="Population"),
Flow(name="Deaths", source="Population"),
],
auxiliaries=[
Auxiliary(name="Birth Rate", inputs=["Population", "Fertility"]),
Auxiliary(name="Death Rate", inputs=["Population"]),
],
converters=[Converter(name="Fertility")],
)
# Compile to GDS — produces a real GDSSpec with role blocks, entities, wirings
spec = compile_model(model)
ir = compile_to_system(model)
print(f"{len(ir.blocks)} blocks, {len(ir.wirings)} wirings")
# Verify — domain checks + optional GDS structural checks
report = verify(model, include_gds_checks=True)
print(f"{report.checks_passed}/{report.checks_total} checks passed")
What is this?
gds-stockflow is a domain DSL that compiles stock-flow diagrams to GDS specifications. You declare stocks, flows, auxiliaries, and converters as plain data models — the compiler handles the mapping to GDS role blocks, entities, composition trees, and wirings.
Your declaration What the compiler produces
──────────────── ─────────────────────────
Stock("Population") → Mechanism + Entity (state update f + state X)
Flow("Births", target=...) → Policy (rate computation g)
Auxiliary("Birth Rate") → Policy (decision logic g)
Converter("Fertility") → BoundaryAction (exogenous input U)
StockFlowModel(...) → GDSSpec + SystemIR (full GDS specification)
Once compiled, all downstream GDS tooling works immediately — canonical projection (h = f ∘ g), semantic checks, SpecQuery dependency analysis, JSON serialization, and gds-viz diagram generation.
Architecture
DSL over GDS
StockFlowModel (user-facing declarations)
│
▼ compile_model()
GDSSpec (entities, blocks, wirings, parameters)
│
▼ compile_to_system()
SystemIR (flat IR for verification + visualization)
No parallel IR stack. The compiler produces a real GDSSpec with real GDS role blocks. This means stock-flow models are first-class GDS citizens — they compose with other GDS models, share the same verification engine, and render with the same visualization tools.
Composition Tree
The compiler builds a tiered composition tree:
(converters |) >> (auxiliaries |) >> (flows |) >> (stock mechanisms |)
.loop([stock forward_out → auxiliary forward_in])
- Within each tier: parallel composition (
|) — independent elements run side-by-side - Across tiers: sequential composition (
>>) — converters feed auxiliaries, auxiliaries feed flows, flows feed stock mechanisms - Temporal recurrence:
.loop()— stock levels at timestep t feed back to auxiliaries at timestep t+1
Elements
Four declaration types, each mapping to a specific GDS role:
Stock
Stock(name="Population", initial=1000.0, non_negative=True)
GDS mapping: Mechanism (state update f) + Entity (state X)
A stock accumulates value over time. Each stock becomes a GDS entity with a level state variable, and a mechanism block that applies incoming flow rates. Stocks emit a Level port for temporal feedback.
| Field | Type | Default | Description |
|---|---|---|---|
name |
str | required | Stock name (becomes entity name) |
initial |
float | None | None | Initial level |
units |
str | "" | Unit label |
non_negative |
bool | True | Constrain level ≥ 0 |
Flow
Flow(name="Births", target="Population")
Flow(name="Deaths", source="Population")
Flow(name="Migration", source="CityA", target="CityB")
GDS mapping: Policy (rate computation g)
A flow transfers value between stocks (or from/to "clouds" — external sources/sinks). Flows with only a target are inflows; flows with only a source are outflows; flows with both transfer between stocks.
| Field | Type | Default | Description |
|---|---|---|---|
name |
str | required | Flow name |
source |
str | "" | Source stock (empty = cloud inflow) |
target |
str | "" | Target stock (empty = cloud outflow) |
Auxiliary
Auxiliary(name="Birth Rate", inputs=["Population", "Fertility"])
GDS mapping: Policy (decision logic g)
An auxiliary computes intermediate values from stocks, converters, or other auxiliaries. Auxiliaries form an acyclic dependency graph — the compiler validates this at construction time.
| Field | Type | Default | Description |
|---|---|---|---|
name |
str | required | Auxiliary name |
inputs |
list[str] | [] | Names of stocks, converters, or auxiliaries this depends on |
Converter
Converter(name="Fertility", units="births/person/year")
GDS mapping: BoundaryAction (exogenous input U)
A converter represents an exogenous constant or parameter — a value that enters the system from outside. Converters have no internal inputs.
| Field | Type | Default | Description |
|---|---|---|---|
name |
str | required | Converter name |
units |
str | "" | Unit label |
Semantic Type System
Three distinct semantic spaces, all float-backed but structurally separate — this prevents accidentally wiring a rate where a level is expected:
| Type | Space | Used By | Constraint |
|---|---|---|---|
LevelType |
LevelSpace |
Stocks | ≥ 0 (by default) |
UnconstrainedLevelType |
UnconstrainedLevelSpace |
Stocks with non_negative=False |
None |
RateType |
RateSpace |
Flows | None (rates can be negative) |
SignalType |
SignalSpace |
Auxiliaries, Converters | None |
Verification
Five domain-specific checks validate the stock-flow model structure before compilation:
| ID | Name | Severity | What It Checks |
|---|---|---|---|
| SF-001 | Orphan stocks | WARNING | Every stock has ≥ 1 connected flow |
| SF-002 | Flow-stock validity | ERROR | Flow source/target reference declared stocks |
| SF-003 | Auxiliary acyclicity | ERROR | No cycles in auxiliary dependency graph |
| SF-004 | Converter connectivity | WARNING | Every converter referenced by ≥ 1 auxiliary |
| SF-005 | Flow completeness | ERROR | Every flow has at least one of source or target |
from stockflow import verify
# Domain checks only
report = verify(model)
# Domain checks + GDS structural checks (G-001..G-006)
report = verify(model, include_gds_checks=True)
Examples
Two tutorial examples in gds-examples demonstrate stock-flow modeling using the GDS framework primitives:
| Example | Domain | What It Teaches |
|---|---|---|
| SIR Epidemic | Epidemiology | 3-compartment accumulation, sequential + parallel composition |
| Lotka-Volterra | Population dynamics | Temporal loops (.loop()), predator-prey rate equations |
Status
v0.1.0 — Alpha. Complete DSL with 5 verification checks and full GDS compilation. 215 tests.
License
Apache-2.0
Built with Claude Code. All code is test-driven and human-reviewed.
Credits & Attribution
Author: Rohan Mehta — BlockScience
Theoretical foundation: Dr. Michael Zargham and Dr. Jamsheed Shorish — Generalized Dynamical Systems, Part I: Foundations (2021).
Architectural inspiration: Sean McOwen — MSML and bdp-lib.
Contributors:
- Michael Zargham — Project direction, GDS theory guidance, and technical review (BlockScience).
- Peter Hacker — Code auditing and review (BlockScience).
Lineage: Part of the cadCAD ecosystem for Complex Adaptive Dynamics.
Metadata
Release files for gds-stockflow 0.99.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| gds_stockflow-0.99.0.tar.gz | 28.2 kB | Without provenance | Details for gds_stockflow-0.99.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| gds_stockflow-0.99.0-py3-none-any.whl | Python 3 | none | any | Details for gds_stockflow-0.99.0-py3-none-any.whl |
Total release size: 33.7 kB
Release files / gds_stockflow-0.99.0.tar.gz
| Download URL | gds_stockflow-0.99.0.tar.gz |
|---|---|
| Size | 28.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
da5f28d93e4c3347a99d828e1df6c53f913e6d12d5e0643d3598e76fb60c1002
|
|
BLAKE2b-256 checksum How to use checksums |
88b367bc4b0e8ee3c98f5227c4737058761357edd66b3ab0895918c1d9e1c7b4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / gds_stockflow-0.99.0-py3-none-any.whl
| Download URL | gds_stockflow-0.99.0-py3-none-any.whl |
|---|---|
| Size | 5.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f9856cd8452ee928b824e4fbdf7447afeb8cc253be5c4801b31edefe55e84a75
|
|
BLAKE2b-256 checksum How to use checksums |
082e1041c19ef47d0fdfc453110ba3896bcbeae2ed467900ab0829e6b9c665f2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for gds-stockflow 0.99.0
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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.