Complete domain models demonstrating every gds-framework feature. Each model.py is written as a tutorial chapter with inline GDS theory commentary — read them in order.
Examples are organized by domain. Some are built using GDS framework primitives directly, while others use higher-level domain DSLs that compile to GDS automatically. See the individual DSL packages:
| Domain | DSL Package | Raw GDS Examples | DSL Examples |
|---|---|---|---|
| System dynamics | gds-stockflow | SIR Epidemic, Lotka-Volterra | SIR Epidemic (DSL) |
| Control theory | gds-control | Thermostat PID | Double Integrator |
| Game theory | gds-games | Prisoner's Dilemma, Insurance, Crosswalk | — |
- Learning Path
- Quick Start
- Examples
- Visualization Guide
- Visualization Views
- Feature Coverage Matrix
- Building New Examples
- Credits & Attribution
Start with SIR Epidemic and work down. Each example introduces one new concept.
| # | Example | Domain | New Concept | Composition | Roles |
|---|---|---|---|---|---|
| 1 | SIR Epidemic | Stock-flow | Fundamentals — TypeDef, Entity, Space, blocks | >> | |
BA, P, M |
| 1b | SIR Epidemic (DSL) | Stock-flow | gds-stockflow DSL — same model, declarative style | auto | BA, P, M |
| 2 | Thermostat PID | Control | .feedback(), CONTRAVARIANT backward flow |
>> .feedback() |
BA, P, CA, M |
| 3 | Lotka-Volterra | Stock-flow | .loop(), COVARIANT temporal iteration |
>> | .loop() |
BA, P, M |
| 4 | Prisoner's Dilemma | Games | Nested |, multi-entity X, complex trees |
| >> .loop() |
BA, P, M |
| 5 | Insurance Contract | Games | ControlAction role, complete 4-role taxonomy | >> |
BA, P, CA, M |
| 6 | Crosswalk Problem | Games | Mechanism design, discrete Markov transitions | >> |
BA, P, CA, M |
Roles: BA = BoundaryAction, P = Policy, CA = ControlAction, M = Mechanism
# Run all example tests (168 tests)
uv run --package gds-examples pytest packages/gds-examples -v
# Run a specific example
uv run --package gds-examples pytest packages/gds-examples/stockflow/sir_epidemic/ -v
# Generate all structural diagrams
uv run python packages/gds-examples/visualize_examples.py
# Generate all 6 views for one example
uv run python packages/gds-examples/stockflow/sir_epidemic/generate_views.py # print to stdout
uv run python packages/gds-examples/stockflow/sir_epidemic/generate_views.py --save # write VIEWS.mdExamples are organized by domain, each following the same layout:
packages/gds-examples/
├── stockflow/
│ ├── sir_epidemic/ # Epidemiology — basic roles + composition (raw GDS)
│ ├── sir_epidemic_dsl/ # Epidemiology — same model via gds-stockflow DSL
│ └── lotka_volterra/ # Population dynamics — temporal loops
├── control/
│ ├── thermostat/ # Control theory — feedback composition
│ └── double_integrator/ # Control DSL — state-space (A,B,C,D) mapping
├── games/
│ ├── prisoners_dilemma/ # Game theory — nested parallel + loops
│ ├── prisoners_dilemma_dsl/ # Game theory — OGS DSL version
│ ├── insurance/ # Finance — 4-role taxonomy
│ └── crosswalk/ # Mechanism design — Markov transitions
├── guides/
│ ├── visualization/ # Dedicated gds-viz guide (see below)
│ └── verification/ # Verification and analysis showcase
└── visualize_examples.py # Generate structural diagrams for all
Each example contains:
sir_epidemic/
├── __init__.py # empty
├── model.py # types, entities, spaces, blocks, build_spec(), build_system()
├── test_model.py # comprehensive tests for every layer
├── generate_views.py # generates all 6 visualization views with commentary
└── VIEWS.md # generated output — 6 Mermaid diagrams with explanations
Start here. 3 compartments (Susceptible, Infected, Recovered) with contact-driven infection dynamics. Models the core stock-flow pattern: accumulation levels change via rate flows.
X = (S, I, R) U = contact_rate g = infection_policy f = (update_s, update_i, update_r) Θ = {beta, gamma, contact_rate}
contact >> infection_policy >> (update_s | update_i | update_r)What you'll learn
- TypeDef with runtime constraints (non-negative counts, positive rates)
- Entity and StateVariable for defining state space X
- Space for typed inter-block communication channels
- BoundaryAction (exogenous input), Policy (decision logic), Mechanism (state update)
>>sequential composition with token-based auto-wiring|parallel composition for independent mechanisms- GDSSpec registration and SpecWiring
- compile_system() to produce SystemIR
Domain: Stock-flow — see gds-stockflow for the declarative DSL
Files: model.py · tests · views
Same model, declarative style. Reimplements the SIR epidemic using the gds-stockflow DSL. Instead of manually constructing TypeDefs, Entities, Spaces, Blocks, and wiring, you declare Stocks, Flows, Auxiliaries, and Converters — the compiler handles all GDS mapping automatically.
Stocks: Susceptible, Infected, Recovered
Flows: Infection (S→I), Recovery (I→R)
Auxiliaries: Infection Rate, Recovery Rate
Converters: Contact Rate, Recovery Time
# The entire model in ~20 lines of declarations
model = StockFlowModel(name="SIR Epidemic", stocks=[...], flows=[...], auxiliaries=[...], converters=[...])
spec = compile_model(model) # → GDSSpec with 9 blocks, 3 entities
system = compile_to_system(model) # → SystemIR with temporal loopsWhat you'll learn
- StockFlowModel declarative API (Stock, Flow, Auxiliary, Converter)
- compile_model() for automatic GDSSpec generation (types, spaces, entities, blocks, wirings)
- compile_to_system() for automatic composition with temporal loops
- SF-001..SF-005 domain verification checks
- Comparison: ~200 lines of manual GDS vs ~20 lines of DSL declarations
- How DSL elements map to GDS roles (Converter → BoundaryAction, Auxiliary/Flow → Policy, Stock → Mechanism + Entity)
Domain: Stock-flow — built with gds-stockflow DSL
Files: model.py · tests · views
Adds feedback — backward information flow within a single timestep. Models a classic sensor → controller → plant control loop with energy cost feedback.
X = (T, E) U = measured_temp g = pid_controller f = update_room Θ = {setpoint, Kp, Ki, Kd}
(sensor >> controller >> plant >> update).feedback([Energy Cost: plant -> controller CONTRAVARIANT])What you'll learn
.feedback()composition for within-timestep backward flow- CONTRAVARIANT flow direction (backward_out → backward_in)
- ControlAction role — reads state and emits control signals (vs Mechanism which writes state)
- backward_in / backward_out ports on block interfaces
- Multi-variable Entity (Room has both temperature and energy_consumed)
Key distinction: Room Plant is ControlAction (not Mechanism) because it has backward_out. Mechanisms cannot have backward ports.
Domain: Control — see gds-control for the declarative DSL
Files: model.py · tests · views
Adds temporal loops — forward iteration across timesteps. Predator-prey dynamics where population levels at timestep t feed rate computations at t+1.
X = (x, y) U = population_signal g = compute_rates f = (update_prey, update_predator) Θ = {prey_birth_rate, ...}
(observe >> compute >> (update_prey | update_pred)).loop([Population Signal -> Compute Rates COVARIANT])What you'll learn
.loop()composition for cross-timestep temporal feedback- COVARIANT flow direction — mandatory for
.loop()(CONTRAVARIANT raises GDSTypeError) - Mechanism with forward_out — emitting signals after state update
- exit_condition parameter for loop termination
- Contrast with
.feedback(): within-timestep (thermostat) vs across-timestep (here)
Key distinction: Temporal wirings must be COVARIANT — .loop() enforces this at construction time.
Domain: Stock-flow — see gds-stockflow for the declarative DSL
Files: model.py · tests · views
Most complex composition — nested parallel + sequential + temporal loop. Two agents independently choose strategies, receive payoffs, and update their world models across rounds.
X = (s_A, U_A, s_B, U_B, t) U = game_config g = (alice, bob) f = (payoff, world_models) Θ = {}
pipeline = (payoff_setting | (alice | bob)) >> payoff_realization >> (alice_world | bob_world)
system = pipeline.loop([world models -> decisions])What you'll learn
- Nested parallel composition:
(A | B) | Cfor logical grouping - Multi-entity state space X with 3 entities (5 state variables total)
- Mechanism with forward_out for temporal feedback
- Complex composition tree combining all operators except
.feedback() - Design choice: parameter vs exogenous input (payoff matrix is U, not Θ)
Domain: Game theory — see gds-games for the OGS DSL with compositional game patterns
Files: model.py · tests · views · architecture viz
Same game, different approach -- reimplements the Prisoner's Dilemma using the gds-games (OGS) typed DSL instead of hand-wiring GDS blocks. Demonstrates how the OGS DSL expresses the same game-theoretic structure more concisely.
decisions = alice_decision | bob_decision
game_round = decisions >> payoff_computation
system = game_round.feedback([payoff -> decisions])What you'll learn
- OGS DecisionGame and CovariantFunction atomic game types
- OGS composition operators (
>>,|,.feedback()) - OGS Pattern with TerminalCondition and ActionSpace metadata
compile_to_ir()for OGS PatternIR generationcompile_pattern_to_spec()for GDS projection- OGS verification checks via
verify() - OGS report generation via
generate_reports() - Contrast: manual GDS blocks vs OGS DSL for the same domain
Domain: Game theory -- see gds-games for the OGS DSL
Files: model.py · tests · views script
Completes the role taxonomy — the only example using all 4 block roles. Models claim events flowing through risk assessment, premium calculation, and reserve updates.
X = (R, P, C, H) U = claim_event g = risk_assessment d = premium_calculation f = (claim_payout, reserve_update) Θ = {base_premium_rate, deductible, coverage_limit}
claim >> risk >> premium >> payout >> reserve_updateWhat you'll learn
- ControlAction role — the 4th block role, for admissibility/control decisions
- Complete 4-role taxonomy: BoundaryAction → Policy → ControlAction → Mechanism
- ControlAction vs Policy: Policy is core decision logic (g), ControlAction constrains the action space (d)
- params_used on ControlAction — parameterized admissibility rules
Key distinction: Premium Calculation is ControlAction because it enforces admissibility constraints — it decides what's allowed, not what to do.
Domain: Game theory / finance — see gds-games for the OGS DSL
Files: model.py · tests · views
Mechanism design — the canonical GDS example from BlockScience. A pedestrian decides whether to cross a one-way street while traffic evolves as a discrete Markov chain. A governance body chooses crosswalk placement to minimize accident probability.
X = traffic_state ∈ {-1, 0, +1} U = (luck, crossing_position) g = pedestrian_decision d = safety_check f = traffic_transition Θ = {crosswalk_location}
observe >> decide >> check >> transitionWhat you'll learn
- Discrete Markov state transitions as GDS
- Mechanism design: governance parameter (crosswalk location) constraining agent behavior
- ControlAction for admissibility enforcement (safety check)
- Complete 4-role taxonomy in a minimal model
- Design parameter Θ as a governance lever
Domain: Game theory / mechanism design — see gds-games for the OGS DSL
Files: model.py · tests · views · README
A dedicated guide at guides/visualization/ makes gds-viz a first-class citizen with focused, runnable demos:
| Script | What It Demonstrates |
|---|---|
all_views_demo.py |
All 6 view types from a single model (SIR Epidemic), with docstrings explaining when to use each view |
theme_customization.py |
All 5 built-in Mermaid themes (neutral, default, dark, forest, base) applied to the same model |
cross_dsl_views.py |
Same viz API across different DSLs (hand-built SIR vs gds-control Double Integrator) |
test_visualization_guide.py |
Tests verifying all scripts produce valid Mermaid output |
# Launch the interactive marimo notebook (recommended)
uv run marimo edit packages/gds-examples/guides/visualization/notebook.py
# Run as a read-only app
uv run marimo run packages/gds-examples/guides/visualization/notebook.py
# Run the visualization guide tests
uv run --package gds-examples pytest packages/gds-examples/guides/visualization/ -vThe guides/verification/ directory demonstrates GDS verification as a feature using deliberately broken models. It covers all three verification layers:
| Layer | Checks | Operates On | What It Catches |
|---|---|---|---|
| Generic | G-001..G-006 | SystemIR | Structural topology: dangling wirings, type mismatches, cycles |
| Semantic | SC-001..SC-007 | GDSSpec | Domain properties: orphan state, write conflicts, empty canonical form |
| Domain | SF-001..SF-005 | StockFlowModel | Stock-flow semantics: orphan stocks, auxiliary cycles, unused converters |
| File | Purpose |
|---|---|
broken_models.py |
Collection of deliberately broken models, each triggering specific checks |
verification_demo.py |
Step-by-step demo of generic and semantic checks, fix-and-re-verify workflow |
domain_checks_demo.py |
Domain-specific checks using the stockflow DSL |
test_verification_guide.py |
Tests verifying that each broken model triggers expected findings |
# Run the verification showcase tests
uv run --package gds-examples pytest packages/gds-examples/guides/verification/ -vEach example includes a generate_views.py script that produces 6 complementary views via gds-viz:
| View | Input | What It Shows |
|---|---|---|
| 1. Structural | SystemIR | Compiled block graph — role shapes, wiring arrows |
| 2. Canonical GDS | CanonicalGDS | Mathematical decomposition: X_t → U → g → f → X_{t+1} |
| 3. Architecture by Role | GDSSpec | Blocks grouped by GDS role |
| 4. Architecture by Domain | GDSSpec | Blocks grouped by domain tag |
| 5. Parameter Influence | GDSSpec | Θ → blocks → entities causal map |
| 6. Traceability | GDSSpec | Backwards trace from one state variable to all influencing blocks |
Sample diagrams
Architecture by domain (Thermostat PID) — blocks grouped by physical subsystem:
%%{init:{"theme":"neutral"}}%%
flowchart TD
classDef boundary fill:#93c5fd,stroke:#2563eb,stroke-width:2px,color:#1e3a5f
classDef policy fill:#fcd34d,stroke:#d97706,stroke-width:2px,color:#78350f
classDef mechanism fill:#86efac,stroke:#16a34a,stroke-width:2px,color:#14532d
classDef control fill:#d8b4fe,stroke:#9333ea,stroke-width:2px,color:#3b0764
classDef generic fill:#cbd5e1,stroke:#64748b,stroke-width:1px,color:#1e293b
classDef entity fill:#e2e8f0,stroke:#475569,stroke-width:2px,color:#0f172a
classDef param fill:#fdba74,stroke:#ea580c,stroke-width:2px,color:#7c2d12
classDef state fill:#5eead4,stroke:#0d9488,stroke-width:2px,color:#134e4a
classDef target fill:#fca5a5,stroke:#dc2626,stroke-width:2px,color:#7f1d1d
classDef empty fill:#e2e8f0,stroke:#94a3b8,stroke-width:1px,color:#475569
subgraph Sensor ["Sensor"]
Temperature_Sensor([Temperature Sensor]):::boundary
end
subgraph Controller ["Controller"]
PID_Controller[PID Controller]:::policy
end
subgraph Plant ["Plant"]
Room_Plant[Room Plant]:::control
Update_Room[[Update Room]]:::mechanism
end
entity_Room[("Room<br/>temperature: T, energy_consumed: E")]:::entity
Update_Room -.-> entity_Room
Temperature_Sensor --TemperatureSpace--> PID_Controller
PID_Controller --CommandSpace--> Room_Plant
Room_Plant --EnergyCostSpace--> PID_Controller
Room_Plant --RoomStateSpace--> Update_Room
Structural view (Thermostat PID) — thick feedback arrow (==>) shows CONTRAVARIANT flow:
%%{init:{"theme":"neutral"}}%%
flowchart TD
classDef boundary fill:#93c5fd,stroke:#2563eb,stroke-width:2px,color:#1e3a5f
classDef policy fill:#fcd34d,stroke:#d97706,stroke-width:2px,color:#78350f
classDef mechanism fill:#86efac,stroke:#16a34a,stroke-width:2px,color:#14532d
classDef control fill:#d8b4fe,stroke:#9333ea,stroke-width:2px,color:#3b0764
classDef generic fill:#cbd5e1,stroke:#64748b,stroke-width:1px,color:#1e293b
Temperature_Sensor([Temperature Sensor]):::boundary
PID_Controller[PID Controller]:::generic
Room_Plant[Room Plant]:::generic
Update_Room[[Update Room]]:::mechanism
Temperature_Sensor --Measured Temperature--> PID_Controller
PID_Controller --Heater Command--> Room_Plant
Room_Plant --Room State--> Update_Room
Room_Plant ==Energy Cost==> PID_Controller
Parameter influence (SIR Epidemic) — Θ → blocks → entities causal map:
%%{init:{"theme":"neutral"}}%%
flowchart LR
classDef boundary fill:#93c5fd,stroke:#2563eb,stroke-width:2px,color:#1e3a5f
classDef policy fill:#fcd34d,stroke:#d97706,stroke-width:2px,color:#78350f
classDef mechanism fill:#86efac,stroke:#16a34a,stroke-width:2px,color:#14532d
classDef control fill:#d8b4fe,stroke:#9333ea,stroke-width:2px,color:#3b0764
classDef generic fill:#cbd5e1,stroke:#64748b,stroke-width:1px,color:#1e293b
classDef entity fill:#e2e8f0,stroke:#475569,stroke-width:2px,color:#0f172a
classDef param fill:#fdba74,stroke:#ea580c,stroke-width:2px,color:#7c2d12
classDef state fill:#5eead4,stroke:#0d9488,stroke-width:2px,color:#134e4a
classDef target fill:#fca5a5,stroke:#dc2626,stroke-width:2px,color:#7f1d1d
classDef empty fill:#e2e8f0,stroke:#94a3b8,stroke-width:1px,color:#475569
param_beta{{"beta"}}:::param
param_contact_rate{{"contact_rate"}}:::param
param_gamma{{"gamma"}}:::param
Contact_Process[Contact Process]
Infection_Policy[Infection Policy]
entity_Infected[("Infected<br/>I")]:::entity
entity_Recovered[("Recovered<br/>R")]:::entity
entity_Susceptible[("Susceptible<br/>S")]:::entity
param_beta -.-> Infection_Policy
param_contact_rate -.-> Contact_Process
param_gamma -.-> Infection_Policy
Update_Recovered -.-> entity_Recovered
Update_Susceptible -.-> entity_Susceptible
Update_Infected -.-> entity_Infected
Contact_Process --> Infection_Policy
Infection_Policy --> Update_Infected
Infection_Policy --> Update_Recovered
Infection_Policy --> Update_Susceptible
Each example's VIEWS.md contains all 6 views with commentary. Output is Mermaid markdown — renders in GitHub, GitLab, VS Code, Obsidian, and mermaid.live.
# Generate views for one example
uv run python packages/gds-examples/stockflow/sir_epidemic/generate_views.py --save
# Generate views for all examples
for d in stockflow/sir_epidemic stockflow/sir_epidemic_dsl stockflow/lotka_volterra control/thermostat games/prisoners_dilemma games/insurance games/crosswalk; do
uv run python packages/gds-examples/$d/generate_views.py --save
done| Feature | SIR | SIR DSL | Thermostat | Lotka-V | Prisoner's D | Insurance | Crosswalk |
|---|---|---|---|---|---|---|---|
| BoundaryAction | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Policy | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Mechanism | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| ControlAction | ✓ | ✓ | ✓ | ||||
>> (sequential) |
✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| (parallel) |
✓ | ✓ | ✓ | ✓ | |||
.feedback() |
✓ | ||||||
.loop() |
✓ | ✓ | ✓ | ||||
| CONTRAVARIANT wiring | ✓ | ||||||
| Temporal wiring | ✓ | ✓ | ✓ | ||||
| Multi-variable Entity | ✓ | ✓ | ✓ | ||||
| Multiple entities | ✓ | ✓ | ✓ | ✓ | ✓ | ||
| Parameters (Θ) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | |
| Domain DSL | ✓ |
See CLAUDE.md for a detailed guide covering:
- Step-by-step model creation (types → entities → spaces → blocks → spec → system)
- Role constraint rules (what each role enforces on its interface)
- Composition operator reference with pitfalls
- Common mistakes at construction, registration, and validation time
- Test patterns to follow
- Design decisions (state vs signal, parameter vs exogenous input, ControlAction vs Policy)
Apache-2.0
Built with Claude Code. All code is test-driven and human-reviewed.
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.