From d3421e6e7a001dacc52fe23adf887e4dd4cda4dc Mon Sep 17 00:00:00 2001 From: rohan Date: Tue, 3 Mar 2026 15:15:10 +0530 Subject: [PATCH 1/4] docs: add new examples to nav and clean up tutorials section --- docs/examples/examples/evolution-of-trust.md | 90 ++++++++++++++++++++ docs/examples/feature-matrix.md | 37 ++++---- docs/examples/index.md | 5 +- docs/examples/learning-path.md | 6 ++ docs/tutorials/getting-started.md | 41 +++++++-- mkdocs.yml | 3 +- 6 files changed, 156 insertions(+), 26 deletions(-) create mode 100644 docs/examples/examples/evolution-of-trust.md diff --git a/docs/examples/examples/evolution-of-trust.md b/docs/examples/examples/evolution-of-trust.md new file mode 100644 index 0000000..df425bd --- /dev/null +++ b/docs/examples/examples/evolution-of-trust.md @@ -0,0 +1,90 @@ +# Evolution of Trust + +**Iterated Prisoner's Dilemma** — OGS game structure with tournament simulation and evolutionary dynamics. + +Based on Nicky Case's [The Evolution of Trust](https://ncase.me/trust/). Demonstrates how a single OGS specification can serve as the source of truth for both structural analysis and computational simulation. + +## OGS Decomposition + +``` +Players: Alice, Bob +Actions: {Cooperate, Defect} +Payoff Matrix: (R, T, S, P) = (2, 3, -1, 0) +Composition: (alice | bob) >> payoff .feedback([payoff -> decisions]) +``` + +## Composition + +```python +pipeline = (alice_decision | bob_decision) >> payoff_computation +system = pipeline.feedback([payoff -> decisions]) +``` + +```mermaid +flowchart TD + subgraph Simultaneous Decisions + Alice[Alice Decision] + Bob[Bob Decision] + end + Simultaneous Decisions --> Payoff[[Payoff Computation]] + Payoff -.Alice Payoff.-> Alice + Payoff -.Bob Payoff.-> Bob +``` + +## What You'll Learn + +- Building a 2-player normal-form game from OGS primitives (`DecisionGame`, `CovariantFunction`) +- Feedback composition for iterated play (payoffs fed back to decision nodes) +- Non-zero-sum payoff matrices with negative values (Sucker payoff S = -1) +- **Interoperability pattern**: same OGS specification consumed by multiple tools (visualization, simulation, equilibrium analysis) +- Strategy protocol design for agent-based simulation on top of GDS specifications + +## Key Concepts + +### OGS Game Structure + +| Block | OGS Type | Purpose | +|---|---|---| +| Alice Decision | DecisionGame | Chooses Cooperate or Defect based on observation | +| Bob Decision | DecisionGame | Symmetric to Alice | +| Payoff Computation | CovariantFunction | Maps action pairs to payoffs via the matrix | + +### Payoff Matrix + +| | Bob: Cooperate | Bob: Defect | +|---|:---:|:---:| +| **Alice: Cooperate** | (2, 2) | (-1, 3) | +| **Alice: Defect** | (3, -1) | (0, 0) | + +T > R > P > S and 2R > T + S (satisfies the Prisoner's Dilemma conditions). + +### Simulation Stack + +The tournament code builds three layers on top of the OGS specification: + +1. **Strategies** — 8 implementations (Tit for Tat, Grim Trigger, Detective, Pavlov, etc.) following a common `Strategy` protocol +2. **Tournament** — `play_match()` for iterated rounds, `play_round_robin()` for all-pairs competition +3. **Evolutionary dynamics** — `run_evolution()` for generational population selection + +Each layer consumes only `get_payoff()` from the specification — no GDS internals needed. + +### Terminal Conditions + +| Outcome | Actions | Payoffs | Character | +|---|---|---|---| +| Mutual Cooperation | (C, C) | (2, 2) | Pareto optimal | +| Mutual Defection | (D, D) | (0, 0) | Nash equilibrium | +| Alice Exploits | (D, C) | (3, -1) | Temptation vs Sucker | +| Bob Exploits | (C, D) | (-1, 3) | Sucker vs Temptation | + +## Files + +- [model.py](https://github.com/BlockScience/gds-core/blob/main/packages/gds-examples/games/evolution_of_trust/model.py) +- [strategies.py](https://github.com/BlockScience/gds-core/blob/main/packages/gds-examples/games/evolution_of_trust/strategies.py) +- [tournament.py](https://github.com/BlockScience/gds-core/blob/main/packages/gds-examples/games/evolution_of_trust/tournament.py) +- [test_model.py](https://github.com/BlockScience/gds-core/blob/main/packages/gds-examples/games/evolution_of_trust/test_model.py) + +## Related + +- [Interoperability Guide](../../guides/interoperability.md) — detailed explanation of the specification-as-interoperability-layer pattern +- [Prisoner's Dilemma](prisoners-dilemma.md) — the base GDS framework version (without OGS or simulation) diff --git a/docs/examples/feature-matrix.md b/docs/examples/feature-matrix.md index d8cd7db..f747eed 100644 --- a/docs/examples/feature-matrix.md +++ b/docs/examples/feature-matrix.md @@ -1,20 +1,23 @@ # Feature Coverage Matrix -| Feature | SIR | 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 (Θ) | ✓ | ✓ | ✓ | | ✓ | ✓ | +| Feature | SIR | Thermostat | Lotka-V | Prisoner's D | Insurance | Crosswalk | Evol. of Trust | +|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:| +| BoundaryAction | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | +| Policy | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | +| Mechanism | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | +| ControlAction | | ✓ | | | ✓ | ✓ | | +| `>>` (sequential) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | +| `\|` (parallel) | ✓ | | ✓ | ✓ | | | | +| `.feedback()` | | ✓ | | | | | | +| `.loop()` | | | ✓ | ✓ | | | | +| CONTRAVARIANT wiring | | ✓ | | | | | | +| Temporal wiring | | | ✓ | ✓ | | | | +| Multi-variable Entity | | ✓ | | ✓ | ✓ | | | +| Multiple entities | ✓ | | ✓ | ✓ | ✓ | | | +| Parameters (Θ) | ✓ | ✓ | ✓ | | ✓ | ✓ | | +| OGS DSL | | | | | | | ✓ | +| OGS Feedback | | | | | | | ✓ | +| Simulation layer | | | | | | | ✓ | ## Complexity Progression @@ -26,5 +29,9 @@ | Lotka-Volterra | BA, P, M | `>>`, `\|`, `.loop()` | COVARIANT temporal loops | | Prisoner's Dilemma | BA, P, M | `\|`, `>>`, `.loop()` | Nested parallel, multi-entity | | Crosswalk | BA, P, CA, M | `>>` | Mechanism design, governance | +| Evolution of Trust | OGS games | OGS feedback | Iterated PD, strategies, simulation | **Roles:** BA = BoundaryAction, P = Policy, CA = ControlAction, M = Mechanism + +!!! note "OGS examples" + The Evolution of Trust uses the gds-games (OGS) DSL rather than gds-framework roles directly. OGS games (DecisionGame, CovariantFunction) compile to GDS blocks via the canonical bridge. diff --git a/docs/examples/index.md b/docs/examples/index.md index 0c84f02..59afcba 100644 --- a/docs/examples/index.md +++ b/docs/examples/index.md @@ -4,7 +4,7 @@ [![Python](https://img.shields.io/pypi/pyversions/gds-examples)](https://pypi.org/project/gds-examples/) [![License](https://img.shields.io/github/license/BlockScience/gds-examples)](https://github.com/BlockScience/gds-examples/blob/main/LICENSE) -**Six complete domain models** demonstrating every [gds-framework](https://blockscience.github.io/gds-framework) feature. Each `model.py` is written as a tutorial chapter with inline GDS theory commentary — read them in order. +**Seven example models** demonstrating every [gds-framework](https://blockscience.github.io/gds-framework) feature. The first six are GDS framework tutorials with inline theory commentary. The seventh uses the OGS game theory DSL with tournament simulation. ## Quick Start @@ -29,8 +29,9 @@ uv run python examples/sir_epidemic/generate_views.py --save | 4 | [Prisoner's Dilemma](examples/prisoners-dilemma.md) | Nested `\|`, multi-entity X, complex trees | `\|` `>>` `.loop()` | | 5 | [Insurance Contract](examples/insurance.md) | ControlAction role, complete 4-role taxonomy | `>>` | | 6 | [Crosswalk Problem](examples/crosswalk.md) | Mechanism design, discrete Markov transitions | `>>` | +| 7 | [Evolution of Trust](examples/evolution-of-trust.md) | OGS iterated PD, strategies, tournament simulation | OGS feedback | -Start with SIR Epidemic and work down — each introduces one new concept. +Start with SIR Epidemic and work down — each introduces one new concept. The Evolution of Trust example uses gds-games (OGS) rather than gds-framework directly. ## File Structure diff --git a/docs/examples/learning-path.md b/docs/examples/learning-path.md index 22fcf35..c658da5 100644 --- a/docs/examples/learning-path.md +++ b/docs/examples/learning-path.md @@ -40,6 +40,12 @@ The canonical GDS example from BlockScience. Demonstrates mechanism design with **New:** mechanism design, governance parameters, discrete state +### 7. [Evolution of Trust](examples/evolution-of-trust.md) — OGS + Simulation + +Bridges specification and computation. Uses the OGS game theory DSL to define an iterated Prisoner's Dilemma, then projects the specification into a tournament simulator with 8 strategies and evolutionary dynamics. + +**New:** OGS DSL, `DecisionGame`/`CovariantFunction`, `Strategy` protocol, interoperability pattern + ## Prerequisites - Python 3.12+ diff --git a/docs/tutorials/getting-started.md b/docs/tutorials/getting-started.md index 6de88f3..fdb86f4 100644 --- a/docs/tutorials/getting-started.md +++ b/docs/tutorials/getting-started.md @@ -1,15 +1,40 @@ -# Getting Started +# Start Here -This page has moved. The getting started tutorial is now at: +New to GDS? Choose the path that matches your goal. -**[Getting Started Guide](../guides/getting-started.md)** +## Build Your First Model -The guide walks you through building a thermostat model in 5 progressive stages, from raw blocks to DSL to verification. +The **Getting Started Guide** walks you through building a thermostat model in 5 progressive stages, from raw blocks to DSL to verification. -## Other Resources +[Getting Started Guide](../guides/getting-started.md){ .md-button .md-button--primary } + +## Learn by Example + +Work through the **example models** in order. Each introduces one new concept while reinforcing previous ones. + +| # | Example | What You'll Learn | +|:-:|---------|-------------------| +| 1 | [SIR Epidemic](../examples/examples/sir-epidemic.md) | Fundamentals — types, entities, spaces, blocks, composition | +| 2 | [Thermostat PID](../examples/examples/thermostat.md) | Feedback and backward flow | +| 3 | [Lotka-Volterra](../examples/examples/lotka-volterra.md) | Temporal loops and cross-timestep iteration | +| 4 | [Prisoner's Dilemma](../examples/examples/prisoners-dilemma.md) | Nested composition and multi-entity state | +| 5 | [Insurance Contract](../examples/examples/insurance.md) | Complete 4-role taxonomy | +| 6 | [Crosswalk Problem](../examples/examples/crosswalk.md) | Mechanism design and governance parameters | +| 7 | [Evolution of Trust](../examples/examples/evolution-of-trust.md) | OGS game theory DSL and simulation | + +[Full Learning Path](../examples/learning-path.md){ .md-button } + +## Compare DSLs + +The **Rosetta Stone** guide models the same problem (thermostat) using three different DSLs — stockflow, control, and game theory — to show how GDS unifies them. + +[Rosetta Stone](../guides/rosetta-stone.md){ .md-button } + +## Other Guides | Guide | Description | |---|---| -| [Rosetta Stone](../guides/rosetta-stone.md) | Same problem modeled with stockflow, control, and game theory DSLs | -| [Verification](../guides/verification.md) | All 3 verification layers demonstrated with deliberately broken models | -| [Visualization](../guides/visualization.md) | 6 view types, 5 themes, and cross-DSL rendering with gds-viz | +| [Real-World Patterns](../guides/real-world-patterns.md) | Common modeling patterns and anti-patterns | +| [Verification](../guides/verification.md) | All 3 verification layers with deliberately broken models | +| [Visualization](../guides/visualization.md) | 6 view types, 5 themes, cross-DSL rendering | +| [Interoperability](../guides/interoperability.md) | Using GDS specs as input to external tools (Nash solvers, simulators) | diff --git a/mkdocs.yml b/mkdocs.yml index ba2789e..5802a03 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -165,10 +165,11 @@ nav: - Prisoner's Dilemma: examples/examples/prisoners-dilemma.md - Insurance Contract: examples/examples/insurance.md - Crosswalk Problem: examples/examples/crosswalk.md + - Evolution of Trust: examples/examples/evolution-of-trust.md - Building Models: examples/building-models.md - Feature Matrix: examples/feature-matrix.md - Tutorials: - - Getting Started: tutorials/getting-started.md + - Start Here: tutorials/getting-started.md - Guides: - Getting Started: guides/getting-started.md - Real-World Patterns: guides/real-world-patterns.md From 60e146a7b52b199bf50eb19c1b6efd8ae890ff8f Mon Sep 17 00:00:00 2001 From: rohan Date: Tue, 3 Mar 2026 15:16:25 +0530 Subject: [PATCH 2/4] docs: add best practices, DSL comparison, and troubleshooting guides --- docs/guides/best-practices.md | 482 +++++++++++++++++++++++++++++++++ docs/guides/choosing-a-dsl.md | 303 +++++++++++++++++++++ docs/guides/troubleshooting.md | 422 +++++++++++++++++++++++++++++ mkdocs.yml | 3 + 4 files changed, 1210 insertions(+) create mode 100644 docs/guides/best-practices.md create mode 100644 docs/guides/choosing-a-dsl.md create mode 100644 docs/guides/troubleshooting.md diff --git a/docs/guides/best-practices.md b/docs/guides/best-practices.md new file mode 100644 index 0000000..8875546 --- /dev/null +++ b/docs/guides/best-practices.md @@ -0,0 +1,482 @@ +# Best Practices: Composition Patterns & Anti-Patterns + +Practical guidance for building clean, verifiable GDS specifications. Covers naming, composition patterns, type system tips, verification workflow, and common mistakes to avoid. + +--- + +## Naming Conventions + +### Port Names and Token-Based Auto-Wiring + +The `>>` operator auto-wires blocks by **token overlap**. Port names are tokenized by splitting on ` + ` (space-plus-space) and `, ` (comma-space), then lowercasing each part. Plain spaces are **not** delimiters. + +```python +from gds import interface + +# "Heater Command" is ONE token: "heater command" +interface(forward_out=["Heater Command"]) + +# "Temperature + Setpoint" is TWO tokens: "temperature", "setpoint" +interface(forward_out=["Temperature + Setpoint"]) + +# This auto-wires to "Temperature" because they share the "temperature" token +interface(forward_in=["Temperature"]) +``` + +!!! tip "Naming rules for auto-wiring" + - Use **plain spaces** for multi-word names that should stay as one token: `"Heat Signal"`, `"Order Status"` + - Use **` + `** to combine independent signals into a compound port: `"Temperature + Pressure"` + - Use **`, `** as an alternative compound delimiter: `"Agent 1, Agent 2"` + - Token matching is **case-insensitive**: `"Heat Signal"` matches `"heat signal"` + +### Block Names + +Choose block names that read well in verification reports and diagrams: + +```python +# Good: descriptive, verb-noun for actions +BoundaryAction(name="Data Ingest", ...) +Policy(name="Validate Transform", ...) +Mechanism(name="Update Temperature", ...) + +# Bad: generic, unclear role +AtomicBlock(name="Block1", ...) +Policy(name="Process", ...) +``` + +!!! note + Block names appear in verification findings, Mermaid diagrams, and `SpecQuery` results. Clear names make debugging significantly easier. + +--- + +## Composition Patterns + +### The Three-Tier Pipeline + +The canonical GDS composition follows a tiered structure that maps directly to the `h = f . g` decomposition: + +```python +from gds import BoundaryAction, Mechanism, Policy, interface + +# Tier 1: Exogenous inputs (boundary) and observers +ingest = BoundaryAction( + name="Data Ingest", + interface=interface(forward_out=["Raw Signal"]), +) + +sensor = Policy( + name="Sensor", + interface=interface( + forward_in=["State Reading"], + forward_out=["Observation"], + ), +) + +# Tier 2: Decision logic (policies) +controller = Policy( + name="Controller", + interface=interface( + forward_in=["Raw Signal + Observation"], + forward_out=["Command"], + ), +) + +# Tier 3: State dynamics (mechanisms) +update = Mechanism( + name="Update State", + interface=interface( + forward_in=["Command"], + forward_out=["State Reading"], + ), + updates=[("Plant", "value")], +) + +# Compose the tiers +input_tier = ingest | sensor # parallel: independent inputs +forward = input_tier >> controller >> update # sequential: data flows forward +system = forward.loop(...) # temporal: state feeds back to observers +``` + +This pattern recurs across all five DSLs: + +``` +(exogenous inputs | observers) >> (decision logic) >> (state dynamics) + .loop(state dynamics -> observers) +``` + +### When to Use Auto-Wiring vs Explicit Wiring + +**Auto-wiring** (`>>`) works when output and input ports share tokens: + +```python +# Auto-wires because "Heat Signal" tokens overlap +heater = BoundaryAction( + name="Heater", + interface=interface(forward_out=["Heat Signal"]), +) +update = Mechanism( + name="Update Temperature", + interface=interface(forward_in=["Heat Signal"]), + updates=[("Room", "temperature")], +) +pipeline = heater >> update # auto-wired via token overlap +``` + +**Explicit wiring** is needed when port names do not share tokens, or when you need precise control: + +```python +from gds.blocks.composition import StackComposition, Wiring +from gds.ir.models import FlowDirection + +# Ports don't share tokens -- explicit wiring required +tier_transition = StackComposition( + name="Cross-Tier", + left=policy_tier, + right=mechanism_tier, + wiring=[ + Wiring( + source_block="Controller", + source_port="Control Output", + target_block="Plant Dynamics", + target_port="Actuator Input", + direction=FlowDirection.COVARIANT, + ), + ], +) +``` + +!!! tip + Start with auto-wiring and only switch to explicit wiring when the compiler raises a token overlap error. This keeps compositions readable. + +### Feedback vs Temporal Loop + +Two loop operators serve different purposes: + +| Operator | Direction | Timing | Use Case | +|----------|-----------|--------|----------| +| `.feedback()` | CONTRAVARIANT | Within timestep | Backward utility/reward signals | +| `.loop()` | COVARIANT | Across timesteps | State fed back to observers | + +```python +from gds.blocks.composition import Wiring +from gds.ir.models import FlowDirection + +# Temporal loop: state at time t feeds into observer at time t+1 +system_with_loop = forward_pipeline.loop( + [ + Wiring( + source_block="Update State", + source_port="State Reading", + target_block="Sensor", + target_port="State Reading", + direction=FlowDirection.COVARIANT, + ) + ], +) + +# Feedback loop: backward signal within a single timestep +# Used in game theory for utility/payoff channels +system_with_feedback = game_pipeline.feedback( + [ + Wiring( + source_block="Payoff", + source_port="Agent Utility", + target_block="Decision", + target_port="Agent Utility", + direction=FlowDirection.CONTRAVARIANT, + ) + ], +) +``` + +!!! warning + `.feedback()` is **contravariant** -- it flows backward. `.loop()` is **covariant** -- it flows forward across time. Mixing these up will cause G-003 direction consistency failures. + +### Parallel Composition for Independent Subsystems + +Use `|` to compose blocks that operate independently at the same tier: + +```python +# Two boundary actions providing independent inputs +heater_input = BoundaryAction( + name="Heater", + interface=interface(forward_out=["Heat Signal"]), +) +setpoint_input = BoundaryAction( + name="Setpoint", + interface=interface(forward_out=["Target Temperature"]), +) + +# Parallel: no validation needed, ports are independent +input_tier = heater_input | setpoint_input +``` + +Parallel composition does not validate any port relationships -- it simply places blocks side by side. The downstream `>>` composition handles the wiring. + +--- + +## Anti-Patterns + +### Don't Use ControlAction + +`ControlAction` exists in the type system but is **unused across all five DSLs**. Every DSL maps observation and decision logic to `Policy` instead. + +```python +# Bad: ControlAction is unused and will confuse readers +from gds import ControlAction +controller = ControlAction(name="Controller", ...) + +# Good: Use Policy for all decision/observation logic +from gds import Policy +controller = Policy(name="Controller", ...) +``` + +### Don't Put State Updates in Policy + +Policy blocks compute decisions. Only Mechanism blocks write state. + +```python +# Bad: Policy should not claim to update state +controller = Policy( + name="Controller", + interface=interface(forward_in=["Signal"], forward_out=["Command"]), + # Don't try to work around this -- Mechanism is the only writer +) + +# Good: Separate decision from state mutation +controller = Policy( + name="Controller", + interface=interface(forward_in=["Signal"], forward_out=["Command"]), +) +update = Mechanism( + name="Apply Command", + interface=interface(forward_in=["Command"]), + updates=[("Plant", "value")], # only Mechanism has updates +) +``` + +### Don't Skip Verification + +Even models that compile successfully benefit from verification. The checks catch subtle structural issues that compilation alone does not. + +```python +from gds import compile_system, verify + +system_ir = compile_system("My Model", root=pipeline) + +# Always verify -- even for "simple" models +report = verify(system_ir) +for finding in report.findings: + if not finding.passed: + print(f"[{finding.check_id}] {finding.message}") +``` + +### Don't Create Circular Sequential Composition + +The `>>` operator builds a DAG. Cycles in covariant flow are caught by G-006: + +```python +# Bad: creates a cycle in the covariant flow graph +a >> b >> c >> a # G-006 will flag this + +# Good: use .loop() for cross-timestep feedback +forward = a >> b >> c +system = forward.loop([...]) # temporal loop, not a cycle +``` + +### Don't Mix Domain Concerns in a Single Block + +Each block should have a single responsibility aligned with its GDS role: + +```python +# Bad: one block doing both validation and state update +mega_block = AtomicBlock( + name="Do Everything", + interface=interface( + forward_in=["Raw Data"], + forward_out=["Clean Data"], + ), +) + +# Good: separate concerns by role +validate = Policy( + name="Validate Data", + interface=interface(forward_in=["Raw Data"], forward_out=["Clean Data"]), +) +persist = Mechanism( + name="Persist Data", + interface=interface(forward_in=["Clean Data"]), + updates=[("Dataset", "count")], +) +``` + +--- + +## Type System Tips + +### Token Overlap for Auto-Wiring + +Understanding token splitting is essential for `>>` composition: + +```python +from gds.types.tokens import tokenize + +# Plain spaces are NOT delimiters +tokenize("Heater Command") # -> {"heater command"} + +# " + " splits into separate tokens +tokenize("Temperature + Setpoint") # -> {"temperature", "setpoint"} + +# ", " also splits +tokenize("Agent 1, Agent 2") # -> {"agent 1", "agent 2"} +``` + +Two ports auto-wire when their token sets **overlap** (share at least one token): + +```python +from gds.types.tokens import tokens_overlap + +# These overlap on "temperature" +tokens_overlap("Temperature + Setpoint", "Temperature") # True + +# These do NOT overlap +tokens_overlap("Heat Signal", "Temperature Reading") # False +``` + +### TypeDef Constraints Are Runtime Only + +TypeDef constraints validate data values, not compilation structure. They are never called during `>>` composition or `compile_system()`: + +```python +from gds import typedef + +# The constraint is checked only when you call check_value() +Temperature = typedef("Temperature", float, constraint=lambda x: -273.15 <= x <= 1000) + +Temperature.check_value(20.0) # True +Temperature.check_value(-300.0) # False -- below absolute zero + +# This does NOT affect compilation or wiring +``` + +### Use Spaces to Define Valid Domains + +Spaces define the shape of data flowing between blocks. Use them to document the semantic contract: + +```python +from gds import space, typedef + +Voltage = typedef("Voltage", float, units="V") +Current = typedef("Current", float, units="A") + +# The space documents what flows through the wire +electrical_signal = space("ElectricalSignal", voltage=Voltage, current=Current) +``` + +--- + +## Verification Workflow + +Run checks in order from fastest/cheapest to most comprehensive: + +### Step 1: Domain Checks (DSL-Level) + +If using a DSL, run its domain-specific checks first. These are the fastest and catch DSL-level errors in domain-native terms: + +```python +from stockflow.verification.engine import verify as sf_verify + +report = sf_verify(model) # runs SF-001..SF-005 +``` + +### Step 2: Generic Checks on SystemIR + +After compilation, run the six structural topology checks: + +```python +from gds import compile_system, verify + +system_ir = compile_system("My Model", root=pipeline) +report = verify(system_ir) # runs G-001..G-006 +``` + +### Step 3: Semantic Checks on GDSSpec + +For full domain property validation: + +```python +from gds import ( + check_canonical_wellformedness, + check_completeness, + check_determinism, + check_parameter_references, + check_type_safety, +) + +for check in [ + check_completeness, + check_determinism, + check_type_safety, + check_parameter_references, + check_canonical_wellformedness, +]: + findings = check(spec) + for f in findings: + if not f.passed: + print(f"[{f.check_id}] {f.message}") +``` + +!!! note "G-002 and BoundaryAction" + G-002 (signature completeness) requires every block to have both inputs and outputs. BoundaryAction blocks have no inputs by design -- they are exogenous. G-002 failures on BoundaryAction blocks are **expected and not a bug**. When running `include_gds_checks=True` in DSL verification, filter G-002 findings for BoundaryAction blocks. + +--- + +## Parameters + +Parameters (Theta) are **structural metadata**. GDS never assigns values or binds parameters to concrete data. They document what is tunable: + +```python +from gds import GDSSpec, Policy, interface, typedef + +spec = GDSSpec(name="Thermostat") + +# Declare the parameter +Setpoint = typedef("Setpoint", float, units="celsius") +spec.register_parameter("setpoint", Setpoint) + +# Reference it from a block +controller = Policy( + name="Controller", + interface=interface(forward_in=["Temperature"], forward_out=["Command"]), + params_used=["setpoint"], # structural reference, not a binding +) +spec.register_block(controller) +``` + +Use `SpecQuery.param_to_blocks()` to trace which blocks depend on which parameters: + +```python +from gds import SpecQuery + +query = SpecQuery(spec) +query.param_to_blocks() +# -> {"setpoint": ["Controller"]} +``` + +!!! tip + Parameters are for documenting tunable constants (learning rate, setpoint, threshold). Don't use them for runtime configuration -- GDS has no execution engine. Parameters exist so that structural queries like "which blocks are affected by this parameter?" can be answered without simulation. + +--- + +## Summary + +| Do | Don't | +|----|-------| +| Use the three-tier pattern: boundary >> policy >> mechanism | Create circular sequential compositions | +| Name ports for clear token overlap | Use generic names like "Signal" everywhere | +| Start with auto-wiring, fall back to explicit | Use explicit wiring when auto-wiring works | +| Use `.loop()` for cross-timestep state feedback | Use `.feedback()` for temporal state (it is contravariant) | +| Use Policy for all decision/observation logic | Use ControlAction (unused across all DSLs) | +| Run verification even on passing models | Skip verification -- subtle issues hide in structure | +| Separate state mutation (Mechanism) from decisions (Policy) | Put state-updating logic in Policy blocks | +| Use parameters for tunable constants | Use parameters for runtime configuration | diff --git a/docs/guides/choosing-a-dsl.md b/docs/guides/choosing-a-dsl.md new file mode 100644 index 0000000..3c98a1d --- /dev/null +++ b/docs/guides/choosing-a-dsl.md @@ -0,0 +1,303 @@ +# Choosing a DSL + +Five domain-specific languages compile to the same GDS core. This guide helps you pick the right one for your problem -- or decide when to use the framework directly. + +--- + +## Decision Matrix + +| If your system has... | Use | Package | Why | +|---|---|---|---| +| Stocks accumulating over time | **gds-stockflow** | `stockflow` | Native stock/flow/auxiliary semantics with accumulation dynamics | +| State-space dynamics (A, B, C, D matrices) | **gds-control** | `gds_control` | Control theory mapping with sensors, controllers, plant states | +| Strategic agents making decisions | **gds-games** | `ogs` | Game-theoretic composition with utility/payoff channels | +| Software architecture to formalize | **gds-software** | `gds_software` | Six diagram types: DFD, SM, Component, C4, ERD, Dependency | +| Business processes or supply chains | **gds-business** | `gds_business` | Causal loop, supply chain network, value stream mapping | +| None of the above | **gds-framework** | `gds` | Build your own vocabulary on the composition algebra | + +--- + +## DSL Profiles + +### gds-stockflow + +**Domain:** System dynamics -- stocks, flows, auxiliaries, converters. + +**Best for:** Accumulation dynamics, resource pools, population models, anything modeled with stock-flow diagrams. + +**Example:** SIR epidemic model, Lotka-Volterra predator-prey, inventory management. + +```python +from stockflow.dsl.elements import Auxiliary, Converter, Flow, Stock +from stockflow.dsl.model import StockFlowModel + +model = StockFlowModel( + name="Population", + stocks=[Stock(name="Population", initial=1000.0, non_negative=True)], + flows=[ + Flow(name="births", target="Population"), + Flow(name="deaths", source="Population"), + ], + converters=[Converter(name="birth_rate"), Converter(name="death_rate")], + auxiliaries=[ + Auxiliary(name="net_growth", inputs=["birth_rate", "death_rate"]), + ], +) +``` + +**Canonical form:** `h = f . g` with |X| = number of stocks, |f| = number of accumulation mechanisms. + +**Domain checks:** SF-001 (orphan stocks), SF-003 (auxiliary cycles), SF-004 (unused converters), plus 2 more. + +--- + +### gds-control + +**Domain:** Feedback control systems -- states, inputs, sensors, controllers. + +**Best for:** Thermostat-like control loops, PID controllers, any system with plant state, measurement, and actuation. + +**Example:** Temperature regulation, resource level tracking, robotic control. + +```python +from gds_control.dsl.elements import Controller, Input, Sensor, State +from gds_control.dsl.model import ControlModel + +model = ControlModel( + name="Thermostat", + states=[State(name="temperature", initial=20.0)], + inputs=[Input(name="heater")], + sensors=[Sensor(name="temp_sensor", observes=["temperature"])], + controllers=[ + Controller(name="pid", reads=["temp_sensor", "heater"], drives=["temperature"]), + ], +) +``` + +**Canonical form:** `h = f . g` with |X| = number of states, full dynamical character. + +**DSL element mapping:** State -> Mechanism + Entity, Input -> BoundaryAction, Sensor -> Policy, Controller -> Policy. + +--- + +### gds-games (OGS) + +**Domain:** Game theory -- strategic interactions, decision games, payoff computation. + +**Best for:** Multi-agent decision problems, mechanism design, auction theory, commons dilemmas. + +**Example:** Prisoner's dilemma, resource extraction games, insurance contracts. + +```python +from ogs.dsl.games import CovariantFunction, DecisionGame +from ogs.dsl.pattern import Pattern, PatternInput +from ogs.dsl.types import InputType, Signature, port + +agent = DecisionGame( + name="Player", + signature=Signature( + x=(port("Resource Signal"),), + y=(port("Extraction Decision"),), + r=(port("Player Payoff"),), + ), + logic="Choose extraction amount", +) +``` + +**Canonical form:** `h = g` (stateless -- no mechanisms, no state). All game blocks map to Policy. + +**Key difference:** OGS uses `OpenGame` (a subclass of `AtomicBlock`) with its own `PatternIR`, which projects back to `SystemIR` via `PatternIR.to_system_ir()`. It also has backward (contravariant) channels for utility signals. + +--- + +### gds-software + +**Domain:** Software architecture -- six diagram types for formalizing system structure. + +**Best for:** Documenting and verifying software architectures, data flows, state machines, component interactions. + +**Diagram types:** + +| Type | What it models | +|------|----------------| +| DFD (Data Flow Diagram) | Processes, data stores, external entities | +| SM (State Machine) | States and transitions | +| Component | Provided/required interfaces | +| C4 | Context, container, component views | +| ERD (Entity-Relationship) | Data model relationships | +| Dependency | Module dependencies | + +```python +from gds_software.dsl.elements import DFDProcess, DFDDataStore, DFDExternalEntity +from gds_software.dsl.model import SoftwareModel + +model = SoftwareModel( + name="Order System", + diagram_type="dfd", + processes=[DFDProcess(name="Process Order")], + data_stores=[DFDDataStore(name="Order DB")], + external_entities=[DFDExternalEntity(name="Customer")], +) +``` + +**Canonical form:** Varies by diagram type. DFD with data stores has state (|X| > 0). ERD and Dependency are typically stateless. + +**Domain checks:** 27 checks across all six diagram types. + +--- + +### gds-business + +**Domain:** Business dynamics -- causal loops, supply chains, value streams. + +**Best for:** Business process modeling, supply chain optimization, value stream analysis. + +**Diagram types:** + +| Type | What it models | +|------|----------------| +| CLD (Causal Loop Diagram) | Reinforcing/balancing feedback loops | +| SCN (Supply Chain Network) | Nodes, links, inventory accumulation | +| VSM (Value Stream Map) | Process steps, buffers, cycle times | + +```python +from gds_business.cld.elements import Variable, CausalLink +from gds_business.cld.model import CLDModel + +model = CLDModel( + name="Market Dynamics", + variables=[Variable(name="Demand"), Variable(name="Price"), Variable(name="Supply")], + links=[ + CausalLink(source="Demand", target="Price", polarity="+"), + CausalLink(source="Price", target="Supply", polarity="+"), + CausalLink(source="Supply", target="Demand", polarity="-"), + ], +) +``` + +**Canonical form:** CLD is stateless (`h = g`). SCN has full dynamics (`h = f . g`). VSM is stateful only with buffers. + +**Domain checks:** 11 checks across all three diagram types. + +--- + +## Feature Comparison + +### State and Canonical Form + +| DSL | Has State? | Canonical Form | Character | +|-----|-----------|----------------|-----------| +| gds-stockflow | Yes (stocks) | `h = f . g` | Dynamical -- state-dominant accumulation | +| gds-control | Yes (plant states) | `h = f . g` | Dynamical -- full feedback control | +| gds-games | No | `h = g` | Strategic -- pure policy computation | +| gds-software | Varies by diagram | Varies | Diagram-dependent | +| gds-business CLD | No | `h = g` | Stateless -- pure signal relay | +| gds-business SCN | Yes (inventory) | `h = f . g` | Dynamical -- inventory accumulation | +| gds-business VSM | Optional (buffers) | Varies | Stateful only with buffers | + +!!! note "The canonical spectrum" + All DSLs compile to the same `h = f . g` decomposition with varying dimensionality of the state space X. When |X| = 0, the system is stateless and `h = g`. When both |f| > 0 and |g| > 0, the system is fully dynamical. GDS is a **unified transition calculus** -- not just a dynamical systems framework. + +### GDS Role Mapping + +Every DSL maps its elements to the same four GDS roles: + +| GDS Role | stockflow | control | games | software | business | +|----------|-----------|---------|-------|----------|----------| +| BoundaryAction | Converter | Input | PatternInput | External entity | External source | +| Policy | Flow, Auxiliary | Sensor, Controller | All game types | Process, Transform | Variable, Link | +| Mechanism | Accumulation | State Dynamics | (none) | Data store update | Inventory update | +| ControlAction | (unused) | (unused) | (unused) | (unused) | (unused) | + +!!! warning + `ControlAction` is unused across all five DSLs. Use `Policy` for all decision, observation, and control logic. + +### Verification Depth + +| DSL | Domain Checks | Generic (G-series) | Semantic (SC-series) | +|-----|---------------|---------------------|---------------------| +| gds-stockflow | 5 (SF-001..SF-005) | 6 (via SystemIR) | 7 (via GDSSpec) | +| gds-control | Domain validation | 6 (via SystemIR) | 7 (via GDSSpec) | +| gds-games | OGS-specific | 6 (via SystemIR) | 7 (via GDSSpec) | +| gds-software | 27 across 6 diagrams | 6 (via SystemIR) | 7 (via GDSSpec) | +| gds-business | 11 across 3 diagrams | 6 (via SystemIR) | 7 (via GDSSpec) | + +--- + +## When to Use Raw gds-framework + +Use the framework directly when: + +1. **No DSL vocabulary fits.** Your domain does not map cleanly to stocks/flows, control loops, games, software diagrams, or business processes. + +2. **You need custom block roles.** The existing roles (BoundaryAction, Policy, Mechanism) work, but you want domain-specific naming or constraints. + +3. **You are exploring a new domain.** Build a prototype with raw blocks and composition, then decide if a DSL would help. + +```python +from gds import ( + BoundaryAction, + GDSSpec, + Mechanism, + Policy, + compile_system, + interface, + verify, +) + +# Build directly with the composition algebra +sensor = BoundaryAction(name="Sensor", interface=interface(forward_out=["Reading"])) +logic = Policy(name="Logic", interface=interface(forward_in=["Reading"], forward_out=["Command"])) +actuator = Mechanism( + name="Actuator", + interface=interface(forward_in=["Command"]), + updates=[("Plant", "value")], +) + +system = sensor >> logic >> actuator +system_ir = compile_system("Custom System", root=system) +report = verify(system_ir) +``` + +!!! tip + If you find yourself repeating the same pattern across multiple raw-framework models, that is a signal to consider creating a DSL. All five existing DSLs started as repeated patterns that were factored into a compiler. + +--- + +## Cross-DSL Interoperability + +All DSLs compile to `GDSSpec`, which means you can: + +1. **Compare models across DSLs** -- the canonical `h = f . g` decomposition works on any spec, regardless of which DSL produced it. + +2. **Use the same verification pipeline** -- generic checks (G-001..G-006) and semantic checks (SC-001..SC-007) work on any compiled system or spec. + +3. **Query any spec with SpecQuery** -- parameter influence, entity update maps, and dependency graphs work uniformly. + +```python +from gds import SpecQuery, project_canonical + +# Same analysis works regardless of which DSL compiled the spec +canonical = project_canonical(spec) +query = SpecQuery(spec) + +print(f"State dimension: {len(canonical.state_space)}") +print(f"Mechanism count: {len(canonical.mechanisms)}") +print(f"Policy count: {len(canonical.policies)}") +``` + +For a concrete example of the same problem modeled through three different DSL lenses, see the [Rosetta Stone](rosetta-stone.md) guide. + +--- + +## Summary + +| Question | Answer | +|----------|--------| +| "I have accumulating stocks" | Use **gds-stockflow** | +| "I have feedback control loops" | Use **gds-control** | +| "I have strategic agents" | Use **gds-games** | +| "I have software to document" | Use **gds-software** | +| "I have business processes" | Use **gds-business** | +| "None of these fit" | Use **gds-framework** directly | +| "I want to compare across domains" | All compile to **GDSSpec** -- use the canonical decomposition | diff --git a/docs/guides/troubleshooting.md b/docs/guides/troubleshooting.md new file mode 100644 index 0000000..b679c28 --- /dev/null +++ b/docs/guides/troubleshooting.md @@ -0,0 +1,422 @@ +# Troubleshooting + +Common errors, verification failures, and debugging strategies. Organized by where you encounter the problem: compilation, verification, or runtime. + +--- + +## Compilation Errors + +### Token Overlap Required + +**Error:** Sequential composition `>>` fails because output and input ports share no tokens. + +**Cause:** The `>>` operator auto-wires by token overlap. Port names are tokenized by splitting on ` + ` and `, `, then lowercasing. If no tokens overlap between the left block's `forward_out` and the right block's `forward_in`, composition fails. + +```python +# This FAILS: "Temperature" and "Pressure" share no tokens +sensor = BoundaryAction( + name="Sensor", + interface=interface(forward_out=["Temperature"]), +) +actuator = Mechanism( + name="Actuator", + interface=interface(forward_in=["Pressure"]), + updates=[("Plant", "value")], +) +pipeline = sensor >> actuator # ERROR: no token overlap +``` + +**Fix options:** + +1. **Rename ports** so they share at least one token: + + ```python + sensor = BoundaryAction( + name="Sensor", + interface=interface(forward_out=["Pressure Reading"]), + ) + actuator = Mechanism( + name="Actuator", + interface=interface(forward_in=["Pressure Reading"]), + updates=[("Plant", "value")], + ) + pipeline = sensor >> actuator # OK: tokens overlap on "pressure reading" + ``` + +2. **Use explicit wiring** when renaming is not appropriate: + + ```python + from gds.blocks.composition import StackComposition, Wiring + from gds.ir.models import FlowDirection + + pipeline = StackComposition( + name="Sensor to Actuator", + left=sensor, + right=actuator, + wiring=[ + Wiring( + source_block="Sensor", + source_port="Temperature", + target_block="Actuator", + target_port="Pressure", + direction=FlowDirection.COVARIANT, + ), + ], + ) + ``` + +### Port Not Found + +**Error:** A wiring references a port name that does not exist on the specified block. + +**Cause:** Typo in the port name, or the port was defined on a different block than expected. + +**Fix:** Check the exact port names on both blocks. Port names in `Wiring` must match the strings used in `interface()`: + +```python +# Check what ports a block actually has +print(sensor.interface.forward_out) # inspect the actual port names +``` + +### Duplicate Block Name + +**Error:** Two blocks in the same composition tree have the same name. + +**Cause:** Block names must be unique within a composition. The compiler flattens the tree and uses names as identifiers. + +**Fix:** Give each block a unique, descriptive name: + +```python +# Bad: duplicate names +sensor_a = Policy(name="Sensor", ...) +sensor_b = Policy(name="Sensor", ...) # name collision + +# Good: unique names +sensor_a = Policy(name="Temperature Sensor", ...) +sensor_b = Policy(name="Pressure Sensor", ...) +``` + +--- + +## Generic Check Failures (G-Series) + +Generic checks operate on the compiled `SystemIR` and verify structural topology. + +### G-001: Domain/Codomain Matching + +**What it checks:** For every covariant wiring, the wiring label must be consistent with the source block's `forward_out` or the target block's `forward_in` (token subset). + +**When it fails:** A wiring label does not match either the source output ports or the target input ports. + +**Fix:** Ensure the wiring label shares tokens with the connected ports. If using auto-wiring, this is handled automatically. If using explicit wiring, check that your `Wiring.label` (or the port names it derives from) match the block interfaces. + +### G-002: Signature Completeness + +**What it checks:** Every block must have at least one non-empty input slot and at least one non-empty output slot. + +**When it fails:** A block has no inputs, no outputs, or neither. + +!!! warning "BoundaryAction blocks will always fail G-002" + BoundaryAction has no `forward_in` ports by design -- it represents exogenous input. This is **expected behavior**, not a bug. When running verification with `include_gds_checks=True` in DSL engines, filter G-002 findings for BoundaryAction blocks: + + ```python + # Filter out expected G-002 failures on BoundaryAction blocks + real_failures = [ + f for f in report.findings + if not f.passed and not ( + f.check_id == "G-002" + and any("BoundaryAction" in elem or "no inputs" in f.message + for elem in f.source_elements) + ) + ] + ``` + +### G-003: Direction Consistency + +**What it checks:** Two validations: + +- Flag consistency: COVARIANT + `is_feedback` is a contradiction (feedback implies contravariant). CONTRAVARIANT + `is_temporal` is also a contradiction (temporal implies covariant). +- Contravariant port-slot matching: for contravariant wirings, the label must match backward ports. + +**When it fails:** Direction flags contradict each other, or contravariant wiring labels do not match backward ports. + +**Fix:** Ensure `.feedback()` wirings use `FlowDirection.CONTRAVARIANT` and `.loop()` wirings use `FlowDirection.COVARIANT`. + +### G-004: Dangling Wirings + +**What it checks:** Every wiring's source and target must reference a block that exists in the system. + +**When it fails:** A wiring references a block name that is not in the compiled system -- typically a typo or a block that was removed from the composition. + +```python +# This will fail G-004: "Ghost" does not exist +WiringIR(source="Ghost", target="B", label="signal", ...) +# -> G-004 FAIL: source 'Ghost' unknown +``` + +**Fix:** Check that all block names in wirings match actual block names in the composition tree. + +### G-005: Sequential Type Compatibility + +**What it checks:** In stack composition (non-temporal, covariant wirings), the wiring label must be a token subset of **both** the source's `forward_out` and the target's `forward_in`. + +**When it fails:** A wiring connects blocks with incompatible port types in sequential composition. + +**Fix:** Rename ports so they share tokens, or use explicit wiring with correct labels. + +### G-006: Covariant Acyclicity + +**What it checks:** The covariant (non-temporal, non-contravariant) flow graph must be a DAG -- no cycles within a single timestep. + +**When it fails:** Three or more blocks form a cycle via covariant wirings, creating an algebraic loop that cannot be resolved within one timestep. + +``` +A -> B -> C -> A # cycle detected! +``` + +**Fix:** Break the cycle by using `.loop()` (temporal, across timesteps) for one of the edges instead of `>>` (sequential, within timestep). + +--- + +## Semantic Check Failures (SC-Series) + +Semantic checks operate on `GDSSpec` and verify domain properties. + +### SC-001: Completeness (Orphan State Variables) + +**What it checks:** Every entity variable is updated by at least one mechanism. + +**When it fails:** An entity has a state variable but no mechanism's `updates` list references it. The variable can never change -- likely a specification error. + +**Fix:** Add a Mechanism that updates the orphan variable, or remove the variable if it is not needed. + +### SC-002: Determinism (Write Conflicts) + +**What it checks:** Within each wiring, no two mechanisms update the same entity variable. + +**When it fails:** Two mechanisms both claim to update `Counter.value` -- non-deterministic state transition. + +**Fix:** Consolidate the updates into a single mechanism, or separate them into different wirings that execute at different times. + +### SC-003: Reachability + +**What it checks:** Can signals reach from one block to another through wiring connections? + +**When it fails:** A block is isolated -- no path connects it to the rest of the system. + +**Fix:** Add wiring connections or check that the block is included in the correct composition. + +### SC-004: Type Safety + +**What it checks:** Wire spaces match source and target block expectations. Space references on wires correspond to registered spaces. + +**When it fails:** A wire references a space that is not registered, or source/target blocks are connected to incompatible spaces. + +**Fix:** Register all spaces with `spec.register_space()` or `spec.collect()` before referencing them in wirings. + +### SC-005: Parameter References + +**What it checks:** Every `params_used` entry on blocks corresponds to a registered parameter in the spec's `parameter_schema`. + +**When it fails:** A block claims to use parameter `"learning_rate"` but no such parameter is registered. + +**Fix:** Register the parameter: + +```python +LearningRate = typedef("LearningRate", float, constraint=lambda x: 0 < x < 1) +spec.register_parameter("learning_rate", LearningRate) +``` + +### SC-006: Canonical f (No Mechanisms) + +**What it checks:** At least one mechanism exists in the spec, so the state transition function f is non-empty. + +**When it fails:** The spec has no Mechanism blocks. The canonical `h = f . g` degenerates to `h = g`. + +!!! note + This is a **warning**, not necessarily an error. Game-theoretic models (OGS) are intentionally stateless -- `h = g` is their correct canonical form. If you expect state dynamics, add Mechanism blocks. + +### SC-007: Canonical X (No State Space) + +**What it checks:** The state space X is non-empty -- at least one entity with variables exists. + +**When it fails:** No entities are registered, so there is no state to transition. + +**Fix:** Register entities with state variables if your model has state. If the model is intentionally stateless, this warning can be ignored. + +--- + +## Common Gotchas + +### Token Matching Rules + +Token splitting only happens on ` + ` (space-plus-space) and `, ` (comma-space). Plain spaces within a name are **not** delimiters: + +```python +from gds.types.tokens import tokenize + +tokenize("Heater Command") # {"heater command"} -- ONE token +tokenize("Heater + Command") # {"heater", "command"} -- TWO tokens +tokenize("Temperature + Setpoint") # {"temperature", "setpoint"} +tokenize("Agent 1, Agent 2") # {"agent 1", "agent 2"} +``` + +### Feedback is Contravariant, Loop is Covariant + +These are not interchangeable: + +| Operator | Direction | Timing | Purpose | +|----------|-----------|--------|---------| +| `.feedback()` | CONTRAVARIANT | Within timestep | Backward utility/reward signals | +| `.loop()` | COVARIANT | Across timesteps | State feedback to observers | + +Using `.feedback()` for temporal state feedback will cause G-003 failures. + +### Frozen Pydantic Models + +DSL elements (Stock, Flow, Sensor, etc.) and GDS value objects (TypeDef, Space, Entity, StateVariable) are **frozen** Pydantic models. You cannot mutate them after creation: + +```python +from gds import typedef + +t = typedef("Temperature", float) +t.name = "Pressure" # ERROR: frozen model, cannot assign + +# Instead, create a new instance +p = typedef("Pressure", float) +``` + +### collect() vs register_wiring() + +`GDSSpec.collect()` type-dispatches objects by their Python type: TypeDef, Space, Entity, Block, ParameterDef. It does **not** handle SpecWiring: + +```python +spec = GDSSpec(name="My Spec") + +# These go through collect() +spec.collect(Temperature, HeaterCommand, room, sensor, controller) + +# SpecWiring must use register_wiring() explicitly +spec.register_wiring(SpecWiring( + name="Main Pipeline", + block_names=["Sensor", "Controller"], + wires=[Wire(source="Sensor", target="Controller", space="SignalSpace")], +)) +``` + +### Spec Validation vs System Verification + +These are different operations on different objects: + +```python +# Spec validation: checks registration consistency (missing types, blocks, etc.) +errors = spec.validate_spec() # returns list[str] + +# System verification: runs G-001..G-006 on compiled IR +report = verify(system_ir) # returns VerificationReport + +# Semantic checks: runs SC-001..SC-007 on the spec +findings = check_completeness(spec) # returns list[Finding] +``` + +--- + +## Debug Workflow + +### Step 1: Inspect the Compiled IR + +After compilation, print the blocks and wirings to see what the compiler produced: + +```python +from gds import compile_system + +system_ir = compile_system("Debug Model", root=pipeline) + +print("=== Blocks ===") +for block in system_ir.blocks: + print(f" {block.name}: {block.signature}") + +print("\n=== Wirings ===") +for wiring in system_ir.wirings: + print(f" {wiring.source} --{wiring.label}--> {wiring.target} ({wiring.direction})") +``` + +### Step 2: Visualize with gds-viz + +Generate Mermaid diagrams for visual inspection: + +```python +from gds_viz.mermaid import system_to_mermaid + +mermaid_str = system_to_mermaid(system_ir) +print(mermaid_str) +# Paste into any Mermaid renderer (mkdocs, GitHub, mermaid.live) +``` + +### Step 3: Run Verification with Individual Checks + +Run checks one at a time to isolate the issue: + +```python +from gds.verification.engine import verify +from gds.verification.generic_checks import ( + check_g001_domain_codomain_matching, + check_g004_dangling_wirings, + check_g006_covariant_acyclicity, +) + +# Run one check at a time +for check in [ + check_g001_domain_codomain_matching, + check_g004_dangling_wirings, + check_g006_covariant_acyclicity, +]: + report = verify(system_ir, checks=[check]) + failures = [f for f in report.findings if not f.passed] + if failures: + print(f"\n{check.__name__}:") + for f in failures: + print(f" [{f.check_id}] {f.message}") +``` + +### Step 4: Use SpecQuery for Structural Analysis + +`SpecQuery` answers questions about information flow without running the model: + +```python +from gds import SpecQuery + +query = SpecQuery(spec) + +# What blocks affect a specific entity variable? +query.blocks_affecting("Room", "temperature") +# -> ['Update Temperature', 'Controller', 'Sensor'] + +# What parameters influence which blocks? +query.param_to_blocks() +# -> {'setpoint': ['Controller']} + +# Full block dependency graph +query.dependency_graph() +``` + +--- + +## Quick Reference: Error to Fix + +| Error | Likely Cause | Fix | +|-------|-------------|-----| +| Token overlap required | `>>` ports share no tokens | Rename ports or use explicit wiring | +| Port not found | Typo in wiring port name | Check `block.interface` for exact names | +| Duplicate block name | Two blocks with same name | Use unique descriptive names | +| G-001 FAIL | Wiring label mismatches ports | Align wiring labels with port tokens | +| G-002 FAIL on BoundaryAction | Expected -- no inputs by design | Filter or ignore for boundary blocks | +| G-003 FAIL | Direction flag contradiction | Match `.feedback()` with CONTRAVARIANT, `.loop()` with COVARIANT | +| G-004 FAIL | Wiring references missing block | Fix block name typo | +| G-005 FAIL | Sequential port type mismatch | Ensure `>>` ports share tokens on both sides | +| G-006 FAIL | Cycle in covariant flow | Break cycle with `.loop()` for temporal edge | +| SC-001 WARNING | Orphan state variable | Add a Mechanism that updates it | +| SC-002 ERROR | Two mechanisms update same variable | Consolidate into one Mechanism | +| SC-005 FAIL | Unregistered parameter | Call `spec.register_parameter()` | +| SC-006/007 WARNING | No mechanisms or entities | Add state if expected, or ignore for stateless models | +| Cannot mutate frozen model | Pydantic frozen=True | Create a new instance instead | diff --git a/mkdocs.yml b/mkdocs.yml index ba2789e..288f8d1 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -171,10 +171,13 @@ nav: - Getting Started: tutorials/getting-started.md - Guides: - Getting Started: guides/getting-started.md + - Choosing a DSL: guides/choosing-a-dsl.md + - Best Practices: guides/best-practices.md - Real-World Patterns: guides/real-world-patterns.md - Rosetta Stone: guides/rosetta-stone.md - Verification: guides/verification.md - Visualization: guides/visualization.md + - Troubleshooting: guides/troubleshooting.md - Interoperability: guides/interoperability.md - Design Notes: - Layer 0 Milestone: guides/architecture-milestone-layer0.md From 6e05f042e58602364adba75f3a88ac08fa8995a9 Mon Sep 17 00:00:00 2001 From: rohan Date: Tue, 3 Mar 2026 15:19:11 +0530 Subject: [PATCH 3/4] docs: integrate stockflow, control, and software into mkdocs site --- docs/control/api/checks.md | 15 ++ docs/control/api/compile.md | 27 +++ docs/control/api/elements.md | 11 + docs/control/api/init.md | 8 + docs/control/api/model.md | 5 + docs/control/api/verification.md | 5 + docs/control/getting-started.md | 105 +++++++++ docs/control/guide/elements.md | 120 ++++++++++ docs/control/guide/verification.md | 97 ++++++++ docs/control/index.md | 95 ++++++++ docs/software/api/c4-checks.md | 11 + docs/software/api/c4-compile.md | 9 + docs/software/api/c4-elements.md | 13 ++ docs/software/api/c4-model.md | 5 + docs/software/api/common.md | 23 ++ docs/software/api/cp-checks.md | 11 + docs/software/api/cp-compile.md | 9 + docs/software/api/cp-elements.md | 9 + docs/software/api/cp-model.md | 5 + docs/software/api/dep-checks.md | 11 + docs/software/api/dep-compile.md | 9 + docs/software/api/dep-elements.md | 9 + docs/software/api/dep-model.md | 5 + docs/software/api/dfd-checks.md | 13 ++ docs/software/api/dfd-compile.md | 23 ++ docs/software/api/dfd-elements.md | 11 + docs/software/api/dfd-model.md | 5 + docs/software/api/erd-checks.md | 11 + docs/software/api/erd-compile.md | 9 + docs/software/api/erd-elements.md | 11 + docs/software/api/erd-model.md | 5 + docs/software/api/index.md | 70 ++++++ docs/software/api/init.md | 8 + docs/software/api/sm-checks.md | 15 ++ docs/software/api/sm-compile.md | 19 ++ docs/software/api/sm-elements.md | 13 ++ docs/software/api/sm-model.md | 5 + docs/software/api/verification.md | 5 + docs/software/getting-started.md | 127 +++++++++++ docs/software/guide/diagram-types.md | 328 +++++++++++++++++++++++++++ docs/software/guide/verification.md | 95 ++++++++ docs/software/index.md | 69 ++++++ docs/stockflow/api/checks.md | 13 ++ docs/stockflow/api/compile.md | 27 +++ docs/stockflow/api/elements.md | 11 + docs/stockflow/api/init.md | 8 + docs/stockflow/api/model.md | 5 + docs/stockflow/api/verification.md | 5 + docs/stockflow/getting-started.md | 94 ++++++++ docs/stockflow/guide/elements.md | 116 ++++++++++ docs/stockflow/guide/verification.md | 88 +++++++ docs/stockflow/index.md | 84 +++++++ mkdocs.yml | 61 +++++ 53 files changed, 1971 insertions(+) create mode 100644 docs/control/api/checks.md create mode 100644 docs/control/api/compile.md create mode 100644 docs/control/api/elements.md create mode 100644 docs/control/api/init.md create mode 100644 docs/control/api/model.md create mode 100644 docs/control/api/verification.md create mode 100644 docs/control/getting-started.md create mode 100644 docs/control/guide/elements.md create mode 100644 docs/control/guide/verification.md create mode 100644 docs/control/index.md create mode 100644 docs/software/api/c4-checks.md create mode 100644 docs/software/api/c4-compile.md create mode 100644 docs/software/api/c4-elements.md create mode 100644 docs/software/api/c4-model.md create mode 100644 docs/software/api/common.md create mode 100644 docs/software/api/cp-checks.md create mode 100644 docs/software/api/cp-compile.md create mode 100644 docs/software/api/cp-elements.md create mode 100644 docs/software/api/cp-model.md create mode 100644 docs/software/api/dep-checks.md create mode 100644 docs/software/api/dep-compile.md create mode 100644 docs/software/api/dep-elements.md create mode 100644 docs/software/api/dep-model.md create mode 100644 docs/software/api/dfd-checks.md create mode 100644 docs/software/api/dfd-compile.md create mode 100644 docs/software/api/dfd-elements.md create mode 100644 docs/software/api/dfd-model.md create mode 100644 docs/software/api/erd-checks.md create mode 100644 docs/software/api/erd-compile.md create mode 100644 docs/software/api/erd-elements.md create mode 100644 docs/software/api/erd-model.md create mode 100644 docs/software/api/index.md create mode 100644 docs/software/api/init.md create mode 100644 docs/software/api/sm-checks.md create mode 100644 docs/software/api/sm-compile.md create mode 100644 docs/software/api/sm-elements.md create mode 100644 docs/software/api/sm-model.md create mode 100644 docs/software/api/verification.md create mode 100644 docs/software/getting-started.md create mode 100644 docs/software/guide/diagram-types.md create mode 100644 docs/software/guide/verification.md create mode 100644 docs/software/index.md create mode 100644 docs/stockflow/api/checks.md create mode 100644 docs/stockflow/api/compile.md create mode 100644 docs/stockflow/api/elements.md create mode 100644 docs/stockflow/api/init.md create mode 100644 docs/stockflow/api/model.md create mode 100644 docs/stockflow/api/verification.md create mode 100644 docs/stockflow/getting-started.md create mode 100644 docs/stockflow/guide/elements.md create mode 100644 docs/stockflow/guide/verification.md create mode 100644 docs/stockflow/index.md diff --git a/docs/control/api/checks.md b/docs/control/api/checks.md new file mode 100644 index 0000000..3d04b46 --- /dev/null +++ b/docs/control/api/checks.md @@ -0,0 +1,15 @@ +# gds_control.verification.checks + +Control system verification checks (CS-001..CS-006). + +::: gds_control.verification.checks.check_cs001_undriven_states + +::: gds_control.verification.checks.check_cs002_unobserved_states + +::: gds_control.verification.checks.check_cs003_unused_inputs + +::: gds_control.verification.checks.check_cs004_controller_read_validity + +::: gds_control.verification.checks.check_cs005_controller_drive_validity + +::: gds_control.verification.checks.check_cs006_sensor_observe_validity diff --git a/docs/control/api/compile.md b/docs/control/api/compile.md new file mode 100644 index 0000000..0187583 --- /dev/null +++ b/docs/control/api/compile.md @@ -0,0 +1,27 @@ +# gds_control.dsl.compile + +Compiler: ControlModel -> GDSSpec / SystemIR. + +## Semantic Types + +::: gds_control.dsl.compile.StateType + +::: gds_control.dsl.compile.StateSpace + +::: gds_control.dsl.compile.ReferenceType + +::: gds_control.dsl.compile.ReferenceSpace + +::: gds_control.dsl.compile.MeasurementType + +::: gds_control.dsl.compile.MeasurementSpace + +::: gds_control.dsl.compile.ControlType + +::: gds_control.dsl.compile.ControlSpace + +## Public Functions + +::: gds_control.dsl.compile.compile_model + +::: gds_control.dsl.compile.compile_to_system diff --git a/docs/control/api/elements.md b/docs/control/api/elements.md new file mode 100644 index 0000000..301cfa1 --- /dev/null +++ b/docs/control/api/elements.md @@ -0,0 +1,11 @@ +# gds_control.dsl.elements + +Control system element declarations -- frozen Pydantic models for user-facing declarations. + +::: gds_control.dsl.elements.State + +::: gds_control.dsl.elements.Input + +::: gds_control.dsl.elements.Sensor + +::: gds_control.dsl.elements.Controller diff --git a/docs/control/api/init.md b/docs/control/api/init.md new file mode 100644 index 0000000..63c35c6 --- /dev/null +++ b/docs/control/api/init.md @@ -0,0 +1,8 @@ +# gds_control + +Public API -- top-level exports. + +::: gds_control + options: + show_submodules: false + members: false diff --git a/docs/control/api/model.md b/docs/control/api/model.md new file mode 100644 index 0000000..8787267 --- /dev/null +++ b/docs/control/api/model.md @@ -0,0 +1,5 @@ +# gds_control.dsl.model + +ControlModel -- declarative container for control system specifications. + +::: gds_control.dsl.model.ControlModel diff --git a/docs/control/api/verification.md b/docs/control/api/verification.md new file mode 100644 index 0000000..7c5a662 --- /dev/null +++ b/docs/control/api/verification.md @@ -0,0 +1,5 @@ +# gds_control.verification + +Verification engine -- runs domain checks with optional GDS structural checks. + +::: gds_control.verification.engine.verify diff --git a/docs/control/getting-started.md b/docs/control/getting-started.md new file mode 100644 index 0000000..db84a4d --- /dev/null +++ b/docs/control/getting-started.md @@ -0,0 +1,105 @@ +# Getting Started + +## Installation + +```bash +uv add gds-control +# or: pip install gds-control +``` + +For development (monorepo): + +```bash +git clone https://github.com/BlockScience/gds-core.git +cd gds-core +uv sync --all-packages +``` + +## Your First Control Model + +A control model describes a feedback control system: states represent the plant, inputs provide reference signals, sensors observe state, and controllers compute control actions. + +```python +from gds_control import ( + State, Input, Sensor, Controller, + ControlModel, compile_model, compile_to_system, verify, +) + +# Declare a thermostat control system +model = ControlModel( + name="Thermostat", + states=[State(name="temperature", initial=20.0)], + inputs=[Input(name="setpoint")], + sensors=[Sensor(name="thermometer", observes=["temperature"])], + controllers=[ + Controller( + name="PID", + reads=["thermometer", "setpoint"], + drives=["temperature"], + ) + ], +) + +# Compile to GDS +spec = compile_model(model) +print(f"Blocks: {len(spec.blocks)}") # 4 blocks +print(f"Entities: {len(spec.entities)}") # 1 (temperature state) + +# Compile to SystemIR for verification +ir = compile_to_system(model) +print(f"{len(ir.blocks)} blocks, {len(ir.wirings)} wirings") + +# Verify — domain checks + GDS structural checks +report = verify(model, include_gds_checks=True) +print(f"{report.checks_passed}/{report.checks_total} checks passed") +``` + +## A Multi-State Model + +Control models support multiple states with multiple sensors and controllers: + +```python +from gds_control import ( + State, Input, Sensor, Controller, + ControlModel, verify, +) + +model = ControlModel( + name="HVAC System", + states=[ + State(name="temperature", initial=22.0), + State(name="humidity", initial=45.0), + ], + inputs=[ + Input(name="temp_setpoint"), + Input(name="humidity_setpoint"), + ], + sensors=[ + Sensor(name="temp_sensor", observes=["temperature"]), + Sensor(name="humidity_sensor", observes=["humidity"]), + ], + controllers=[ + Controller( + name="heater", + reads=["temp_sensor", "temp_setpoint"], + drives=["temperature"], + ), + Controller( + name="humidifier", + reads=["humidity_sensor", "humidity_setpoint"], + drives=["humidity"], + ), + ], +) + +# Verify +report = verify(model, include_gds_checks=False) +for f in report.findings: + print(f" [{f.check_id}] {'PASS' if f.passed else 'FAIL'} {f.message}") +``` + +## Next Steps + +- [Elements & GDS Mapping](guide/elements.md) -- detailed element reference and how each maps to GDS +- [Verification Guide](guide/verification.md) -- all 6 domain checks explained +- [API Reference](api/init.md) -- complete auto-generated API docs diff --git a/docs/control/guide/elements.md b/docs/control/guide/elements.md new file mode 100644 index 0000000..e623ce8 --- /dev/null +++ b/docs/control/guide/elements.md @@ -0,0 +1,120 @@ +# Elements & GDS Mapping + +`gds-control` provides four element types, each mapping to a specific GDS role and corresponding to the standard state-space representation. + +## State + +A state variable in the plant. Each state becomes a GDS entity with a `value` state variable, and a dynamics block that applies incoming control signals. + +```python +State(name="temperature", initial=20.0) +``` + +**GDS mapping:** `Mechanism` (state update *f*) + `Entity` (state *X*) + +**State-space:** x (state vector) + +| Field | Type | Default | Description | +|-------|------|---------|-------------| +| `name` | str | required | State name (becomes entity name) | +| `initial` | float \| None | None | Initial value | + +### Port Convention + +- Output: `"{Name} State"` (temporal feedback to sensors) +- Input: `"{ControllerName} Control"` (incoming control signals) + +--- + +## Input + +An exogenous reference signal or disturbance entering the system from outside. Inputs have no internal sources -- they represent the boundary between the system and its environment. + +```python +Input(name="setpoint") +``` + +**GDS mapping:** `BoundaryAction` (exogenous input *U*) + +**State-space:** r (reference signal) + +| Field | Type | Default | Description | +|-------|------|---------|-------------| +| `name` | str | required | Input name | + +### Port Convention + +- Output: `"{Name} Reference"` + +--- + +## Sensor + +A sensor reads state variables and emits a measurement signal. The `observes` list declares which states the sensor can read -- validated at model construction time. + +```python +Sensor(name="thermometer", observes=["temperature"]) +``` + +**GDS mapping:** `Policy` (observation *g*) + +**State-space:** y = Cx + Du (sensor output) + +| Field | Type | Default | Description | +|-------|------|---------|-------------| +| `name` | str | required | Sensor name | +| `observes` | list[str] | [] | Names of states this sensor reads | + +### Port Convention + +- Input: `"{StateName} State"` +- Output: `"{Name} Measurement"` + +--- + +## Controller + +A controller reads sensor measurements and/or reference inputs, then emits control signals to drive state variables. + +```python +Controller(name="PID", reads=["thermometer", "setpoint"], drives=["temperature"]) +``` + +**GDS mapping:** `Policy` (decision logic *g*) + +**State-space:** u = K(y, r) (control law) + +| Field | Type | Default | Description | +|-------|------|---------|-------------| +| `name` | str | required | Controller name | +| `reads` | list[str] | [] | Names of sensors/inputs this controller reads | +| `drives` | list[str] | [] | Names of states this controller drives | + +### Port Convention + +- Input: `"{ReadName} Measurement"` or `"{ReadName} Reference"` +- Output: `"{Name} Control"` + +--- + +## Semantic Type System + +Four distinct semantic spaces, all `float`-backed but structurally separate -- this prevents accidentally wiring a measurement where a control signal is expected: + +| Type | Space | Used By | Description | +|------|-------|---------|-------------| +| `StateType` | `StateSpace` | States | Plant state variables | +| `ReferenceType` | `ReferenceSpace` | Inputs | Exogenous reference/disturbance signals | +| `MeasurementType` | `MeasurementSpace` | Sensors | Sensor output measurements | +| `ControlType` | `ControlSpace` | Controllers | Controller output signals | + +## Composition Structure + +The compiler builds a tiered composition tree: + +``` +(inputs | sensors) >> (controllers) >> (state dynamics) + .loop([state dynamics forward_out -> sensor forward_in]) +``` + +This maps to the GDS canonical form `h = f . g` where states carry state (X), dynamics provide f, and sensors + controllers contribute to g. diff --git a/docs/control/guide/verification.md b/docs/control/guide/verification.md new file mode 100644 index 0000000..ecc9f15 --- /dev/null +++ b/docs/control/guide/verification.md @@ -0,0 +1,97 @@ +# Verification + +`gds-control` provides 6 domain-specific verification checks, plus access to the 6 GDS generic checks (G-001..G-006) via the unified `verify()` function. + +## Using verify() + +The `verify()` function runs domain checks on the control model: + +```python +from gds_control import verify + +report = verify(model) # Domain checks only +report = verify(model, include_gds_checks=True) # Domain + GDS checks +``` + +The returned `VerificationReport` contains a list of `Finding` objects with: + +- `check_id` -- e.g., "CS-001", "G-003" +- `severity` -- ERROR, WARNING, or INFO +- `message` -- human-readable description +- `passed` -- whether the check passed +- `source_elements` -- elements involved + +## Domain Checks + +| ID | Name | Severity | What it checks | +|----|------|----------|----------------| +| CS-001 | Undriven states | WARNING | Every state driven by >= 1 controller | +| CS-002 | Unobserved states | WARNING | Every state observed by >= 1 sensor | +| CS-003 | Unused inputs | WARNING | Every input read by >= 1 controller | +| CS-004 | Controller read validity | ERROR | Controller `reads` reference declared sensors/inputs | +| CS-005 | Controller drive validity | ERROR | Controller `drives` reference declared states | +| CS-006 | Sensor observe validity | ERROR | Sensor `observes` reference declared states | + +### CS-001: Undriven States + +States not driven by any controller cannot be actuated: + +``` +[CS-001] WARNING: State 'pressure' is NOT driven by any controller +``` + +### CS-002: Unobserved States + +States not observed by any sensor are invisible to the control loop: + +``` +[CS-002] WARNING: State 'pressure' is NOT observed by any sensor +``` + +### CS-003: Unused Inputs + +Inputs not read by any controller have no effect on the system: + +``` +[CS-003] WARNING: Input 'disturbance' is NOT read by any controller +``` + +### CS-004: Controller Read Validity + +Controllers must reference declared sensors or inputs in their `reads` list: + +``` +[CS-004] ERROR: Controller 'PID' reads 'missing_sensor' which is NOT a declared sensor or input +``` + +### CS-005: Controller Drive Validity + +Controllers must reference declared states in their `drives` list: + +``` +[CS-005] ERROR: Controller 'PID' drives 'missing_state' which is NOT a declared state +``` + +### CS-006: Sensor Observe Validity + +Sensors must reference declared states in their `observes` list: + +``` +[CS-006] ERROR: Sensor 'probe' observes 'missing_state' which is NOT a declared state +``` + +## GDS Generic Checks + +When `include_gds_checks=True`, the model is compiled to `SystemIR` and the 6 GDS generic checks run: + +| ID | Name | What it checks | +|----|------|----------------| +| G-001 | Domain/codomain compatibility | Wiring type tokens match | +| G-002 | Signature completeness | Every block has inputs and outputs | +| G-003 | Unique block naming | No duplicate block names | +| G-004 | Wiring source existence | Wired blocks exist | +| G-005 | Wiring target existence | Wired blocks exist | +| G-006 | Hierarchy consistency | Block tree is well-formed | + +!!! note + G-002 will flag `BoundaryAction` blocks (Inputs) as having "no inputs" -- this is expected since they are exogenous sources by design. diff --git a/docs/control/index.md b/docs/control/index.md new file mode 100644 index 0000000..e5f4c23 --- /dev/null +++ b/docs/control/index.md @@ -0,0 +1,95 @@ +# gds-control + +[![PyPI](https://img.shields.io/pypi/v/gds-control)](https://pypi.org/project/gds-control/) +[![Python](https://img.shields.io/pypi/pyversions/gds-control)](https://pypi.org/project/gds-control/) +[![License](https://img.shields.io/github/license/BlockScience/gds-core)](https://github.com/BlockScience/gds-core/blob/main/LICENSE) + +**State-space control DSL over GDS semantics** -- control theory with formal verification. + +## What is this? + +`gds-control` extends the GDS framework with control systems vocabulary -- states, inputs, sensors, and controllers. It provides: + +- **4 element types** -- State, Input, Sensor, Controller +- **Typed compilation** -- Each element compiles to GDS role blocks, entities, and composition trees +- **6 verification checks** -- Domain-specific structural validation (CS-001..CS-006) +- **Canonical decomposition** -- Validated h = f ∘ g projection mapping directly to state-space representation +- **Full GDS integration** -- All downstream tooling works immediately (canonical projection, semantic checks, gds-viz) + +## Architecture + +``` +gds-framework (pip install gds-framework) +| +| Domain-neutral composition algebra, typed spaces, +| state model, verification engine, flat IR compiler. +| ++-- gds-control (pip install gds-control) + | + | Control DSL: State, Input, Sensor, Controller elements, + | compile_model(), domain verification, verify() dispatch. + | + +-- Your application + | + | Concrete control models, analysis notebooks, + | verification runners. +``` + +## GDS Mapping + +The DSL maps directly to the standard state-space representation: + +``` +x' = Ax + Bu (state dynamics -> Mechanism) +y = Cx + Du (sensor output -> Policy) +u = K(y, r) (control law -> Policy) +r (reference -> BoundaryAction) +``` + +``` +Your declaration What the compiler produces +---------------- ------------------------- +State("temperature") -> Mechanism + Entity (state update f + state X) +Input("setpoint") -> BoundaryAction (exogenous input U) +Sensor("thermometer") -> Policy (observation g) +Controller("PID") -> Policy (decision logic g) +ControlModel(...) -> GDSSpec + SystemIR (full GDS specification) +``` + +## Composition Tree + +The compiler builds a tiered composition tree: + +``` +(inputs | sensors) >> (controllers) >> (state dynamics) + .loop([state dynamics forward_out -> sensor forward_in]) +``` + +- **Within each tier:** parallel composition (`|`) -- independent inputs and sensors run side-by-side +- **Across tiers:** sequential composition (`>>`) -- sensors feed controllers, controllers feed state dynamics +- **Temporal recurrence:** `.loop()` -- state outputs at timestep *t* feed back to sensors at timestep *t+1* + +**Design decision:** All non-state-updating blocks use `Policy`. `ControlAction` is deliberately not used -- it sits outside the canonical *g* partition, which would break the clean `(A, B, C, D) <-> (X, U, g, f)` mapping. + +## Canonical Form + +Control models produce the full dynamical form: + +| |X| | |f| | Form | Character | +|-----|-----|------|-----------| +| n | n | h = f ∘ g | Full dynamical system | + +States carry state (X), dynamics blocks provide f, and sensors + controllers contribute to g. + +## Quick Start + +```bash +uv add gds-control +# or: pip install gds-control +``` + +See [Getting Started](getting-started.md) for a full walkthrough. + +## Credits + +Built on [gds-framework](../framework/index.md) by [BlockScience](https://block.science). diff --git a/docs/software/api/c4-checks.md b/docs/software/api/c4-checks.md new file mode 100644 index 0000000..05441f7 --- /dev/null +++ b/docs/software/api/c4-checks.md @@ -0,0 +1,11 @@ +# gds_software.c4.checks + +C4 model verification checks (C4-001..C4-004). + +::: gds_software.c4.checks.check_c4001_relationship_validity + +::: gds_software.c4.checks.check_c4002_container_hierarchy + +::: gds_software.c4.checks.check_c4003_external_connectivity + +::: gds_software.c4.checks.check_c4004_level_consistency diff --git a/docs/software/api/c4-compile.md b/docs/software/api/c4-compile.md new file mode 100644 index 0000000..a4b011a --- /dev/null +++ b/docs/software/api/c4-compile.md @@ -0,0 +1,9 @@ +# gds_software.c4.compile + +Compiler: C4Model -> GDSSpec / SystemIR. + +## Public Functions + +::: gds_software.c4.compile.compile_c4 + +::: gds_software.c4.compile.compile_c4_to_system diff --git a/docs/software/api/c4-elements.md b/docs/software/api/c4-elements.md new file mode 100644 index 0000000..00a0ead --- /dev/null +++ b/docs/software/api/c4-elements.md @@ -0,0 +1,13 @@ +# gds_software.c4.elements + +C4 model element declarations -- frozen Pydantic models for user-facing declarations. + +::: gds_software.c4.elements.Person + +::: gds_software.c4.elements.ExternalSystem + +::: gds_software.c4.elements.Container + +::: gds_software.c4.elements.C4Component + +::: gds_software.c4.elements.C4Relationship diff --git a/docs/software/api/c4-model.md b/docs/software/api/c4-model.md new file mode 100644 index 0000000..dd79ac7 --- /dev/null +++ b/docs/software/api/c4-model.md @@ -0,0 +1,5 @@ +# gds_software.c4.model + +C4Model -- declarative container for C4 architecture models. + +::: gds_software.c4.model.C4Model diff --git a/docs/software/api/common.md b/docs/software/api/common.md new file mode 100644 index 0000000..ed38bfd --- /dev/null +++ b/docs/software/api/common.md @@ -0,0 +1,23 @@ +# gds_software.common + +Shared types, errors, and compilation utilities. + +## Diagram Kinds + +::: gds_software.common.types.DiagramKind + +## Errors + +::: gds_software.common.errors.SWError + +::: gds_software.common.errors.SWValidationError + +::: gds_software.common.errors.SWCompilationError + +## Compilation Utilities + +::: gds_software.common.compile_utils.parallel_tier + +::: gds_software.common.compile_utils.build_inter_tier_wirings + +::: gds_software.common.compile_utils.sequential_with_explicit_wiring diff --git a/docs/software/api/cp-checks.md b/docs/software/api/cp-checks.md new file mode 100644 index 0000000..0d725de --- /dev/null +++ b/docs/software/api/cp-checks.md @@ -0,0 +1,11 @@ +# gds_software.component.checks + +Component diagram verification checks (CP-001..CP-004). + +::: gds_software.component.checks.check_cp001_interface_satisfaction + +::: gds_software.component.checks.check_cp002_connector_validity + +::: gds_software.component.checks.check_cp003_dangling_interfaces + +::: gds_software.component.checks.check_cp004_component_naming diff --git a/docs/software/api/cp-compile.md b/docs/software/api/cp-compile.md new file mode 100644 index 0000000..e94ed13 --- /dev/null +++ b/docs/software/api/cp-compile.md @@ -0,0 +1,9 @@ +# gds_software.component.compile + +Compiler: ComponentModel -> GDSSpec / SystemIR. + +## Public Functions + +::: gds_software.component.compile.compile_component + +::: gds_software.component.compile.compile_component_to_system diff --git a/docs/software/api/cp-elements.md b/docs/software/api/cp-elements.md new file mode 100644 index 0000000..4e15415 --- /dev/null +++ b/docs/software/api/cp-elements.md @@ -0,0 +1,9 @@ +# gds_software.component.elements + +Component diagram element declarations -- frozen Pydantic models for user-facing declarations. + +::: gds_software.component.elements.Component + +::: gds_software.component.elements.InterfaceDef + +::: gds_software.component.elements.Connector diff --git a/docs/software/api/cp-model.md b/docs/software/api/cp-model.md new file mode 100644 index 0000000..2d46b7f --- /dev/null +++ b/docs/software/api/cp-model.md @@ -0,0 +1,5 @@ +# gds_software.component.model + +ComponentModel -- declarative container for component diagrams. + +::: gds_software.component.model.ComponentModel diff --git a/docs/software/api/dep-checks.md b/docs/software/api/dep-checks.md new file mode 100644 index 0000000..bfbb2f7 --- /dev/null +++ b/docs/software/api/dep-checks.md @@ -0,0 +1,11 @@ +# gds_software.dependency.checks + +Dependency graph verification checks (DG-001..DG-004). + +::: gds_software.dependency.checks.check_dg001_dep_validity + +::: gds_software.dependency.checks.check_dg002_acyclicity + +::: gds_software.dependency.checks.check_dg003_layer_ordering + +::: gds_software.dependency.checks.check_dg004_module_connectivity diff --git a/docs/software/api/dep-compile.md b/docs/software/api/dep-compile.md new file mode 100644 index 0000000..84a4d55 --- /dev/null +++ b/docs/software/api/dep-compile.md @@ -0,0 +1,9 @@ +# gds_software.dependency.compile + +Compiler: DependencyModel -> GDSSpec / SystemIR. + +## Public Functions + +::: gds_software.dependency.compile.compile_dep + +::: gds_software.dependency.compile.compile_dep_to_system diff --git a/docs/software/api/dep-elements.md b/docs/software/api/dep-elements.md new file mode 100644 index 0000000..3f308dc --- /dev/null +++ b/docs/software/api/dep-elements.md @@ -0,0 +1,9 @@ +# gds_software.dependency.elements + +Dependency graph element declarations -- frozen Pydantic models for user-facing declarations. + +::: gds_software.dependency.elements.Module + +::: gds_software.dependency.elements.Dep + +::: gds_software.dependency.elements.Layer diff --git a/docs/software/api/dep-model.md b/docs/software/api/dep-model.md new file mode 100644 index 0000000..b6759ce --- /dev/null +++ b/docs/software/api/dep-model.md @@ -0,0 +1,5 @@ +# gds_software.dependency.model + +DependencyModel -- declarative container for dependency graphs. + +::: gds_software.dependency.model.DependencyModel diff --git a/docs/software/api/dfd-checks.md b/docs/software/api/dfd-checks.md new file mode 100644 index 0000000..fe30f45 --- /dev/null +++ b/docs/software/api/dfd-checks.md @@ -0,0 +1,13 @@ +# gds_software.dfd.checks + +DFD verification checks (DFD-001..DFD-005). + +::: gds_software.dfd.checks.check_dfd001_process_connectivity + +::: gds_software.dfd.checks.check_dfd002_flow_validity + +::: gds_software.dfd.checks.check_dfd003_no_ext_to_ext + +::: gds_software.dfd.checks.check_dfd004_store_connectivity + +::: gds_software.dfd.checks.check_dfd005_process_output diff --git a/docs/software/api/dfd-compile.md b/docs/software/api/dfd-compile.md new file mode 100644 index 0000000..dc71ff8 --- /dev/null +++ b/docs/software/api/dfd-compile.md @@ -0,0 +1,23 @@ +# gds_software.dfd.compile + +Compiler: DFDModel -> GDSSpec / SystemIR. + +## Semantic Types + +::: gds_software.dfd.compile.ContentType + +::: gds_software.dfd.compile.ContentSpace + +::: gds_software.dfd.compile.DataType + +::: gds_software.dfd.compile.DataSpace + +::: gds_software.dfd.compile.SignalType + +::: gds_software.dfd.compile.SignalSpace + +## Public Functions + +::: gds_software.dfd.compile.compile_dfd + +::: gds_software.dfd.compile.compile_dfd_to_system diff --git a/docs/software/api/dfd-elements.md b/docs/software/api/dfd-elements.md new file mode 100644 index 0000000..1b3fb41 --- /dev/null +++ b/docs/software/api/dfd-elements.md @@ -0,0 +1,11 @@ +# gds_software.dfd.elements + +DFD element declarations -- frozen Pydantic models for user-facing declarations. + +::: gds_software.dfd.elements.ExternalEntity + +::: gds_software.dfd.elements.Process + +::: gds_software.dfd.elements.DataStore + +::: gds_software.dfd.elements.DataFlow diff --git a/docs/software/api/dfd-model.md b/docs/software/api/dfd-model.md new file mode 100644 index 0000000..5fb79ed --- /dev/null +++ b/docs/software/api/dfd-model.md @@ -0,0 +1,5 @@ +# gds_software.dfd.model + +DFDModel -- declarative container for data flow diagrams. + +::: gds_software.dfd.model.DFDModel diff --git a/docs/software/api/erd-checks.md b/docs/software/api/erd-checks.md new file mode 100644 index 0000000..ac21a70 --- /dev/null +++ b/docs/software/api/erd-checks.md @@ -0,0 +1,11 @@ +# gds_software.erd.checks + +ERD verification checks (ER-001..ER-004). + +::: gds_software.erd.checks.check_er001_relationship_validity + +::: gds_software.erd.checks.check_er002_pk_existence + +::: gds_software.erd.checks.check_er003_attribute_uniqueness + +::: gds_software.erd.checks.check_er004_relationship_naming diff --git a/docs/software/api/erd-compile.md b/docs/software/api/erd-compile.md new file mode 100644 index 0000000..7cb0e11 --- /dev/null +++ b/docs/software/api/erd-compile.md @@ -0,0 +1,9 @@ +# gds_software.erd.compile + +Compiler: ERDModel -> GDSSpec / SystemIR. + +## Public Functions + +::: gds_software.erd.compile.compile_erd + +::: gds_software.erd.compile.compile_erd_to_system diff --git a/docs/software/api/erd-elements.md b/docs/software/api/erd-elements.md new file mode 100644 index 0000000..ca9777e --- /dev/null +++ b/docs/software/api/erd-elements.md @@ -0,0 +1,11 @@ +# gds_software.erd.elements + +ERD element declarations -- frozen Pydantic models for user-facing declarations. + +::: gds_software.erd.elements.ERDEntity + +::: gds_software.erd.elements.Attribute + +::: gds_software.erd.elements.ERDRelationship + +::: gds_software.erd.elements.Cardinality diff --git a/docs/software/api/erd-model.md b/docs/software/api/erd-model.md new file mode 100644 index 0000000..eacbb31 --- /dev/null +++ b/docs/software/api/erd-model.md @@ -0,0 +1,5 @@ +# gds_software.erd.model + +ERDModel -- declarative container for entity-relationship diagrams. + +::: gds_software.erd.model.ERDModel diff --git a/docs/software/api/index.md b/docs/software/api/index.md new file mode 100644 index 0000000..f113ef3 --- /dev/null +++ b/docs/software/api/index.md @@ -0,0 +1,70 @@ +# API Reference + +Complete API documentation for `gds-software`, auto-generated from source docstrings. + +## Common + +| Module | Description | +|--------|-------------| +| [gds_software](init.md) | Package root -- version, top-level imports | +| [gds_software.common](common.md) | Shared types, errors, compilation utilities | + +## Data Flow Diagrams + +| Module | Description | +|--------|-------------| +| [gds_software.dfd.elements](dfd-elements.md) | ExternalEntity, Process, DataStore, DataFlow declarations | +| [gds_software.dfd.model](dfd-model.md) | DFDModel container | +| [gds_software.dfd.compile](dfd-compile.md) | DFD -> GDSSpec / SystemIR compiler | +| [gds_software.dfd.checks](dfd-checks.md) | DFD-001..DFD-005 verification checks | + +## State Machines + +| Module | Description | +|--------|-------------| +| [gds_software.statemachine.elements](sm-elements.md) | State, Event, Transition, Guard, Region | +| [gds_software.statemachine.model](sm-model.md) | StateMachineModel container | +| [gds_software.statemachine.compile](sm-compile.md) | SM -> GDSSpec / SystemIR compiler | +| [gds_software.statemachine.checks](sm-checks.md) | SM-001..SM-006 verification checks | + +## Component Diagrams + +| Module | Description | +|--------|-------------| +| [gds_software.component.elements](cp-elements.md) | Component, InterfaceDef, Connector | +| [gds_software.component.model](cp-model.md) | ComponentModel container | +| [gds_software.component.compile](cp-compile.md) | Component -> GDSSpec / SystemIR compiler | +| [gds_software.component.checks](cp-checks.md) | CP-001..CP-004 verification checks | + +## C4 Models + +| Module | Description | +|--------|-------------| +| [gds_software.c4.elements](c4-elements.md) | Person, ExternalSystem, Container, C4Component, C4Relationship | +| [gds_software.c4.model](c4-model.md) | C4Model container | +| [gds_software.c4.compile](c4-compile.md) | C4 -> GDSSpec / SystemIR compiler | +| [gds_software.c4.checks](c4-checks.md) | C4-001..C4-004 verification checks | + +## Entity-Relationship Diagrams + +| Module | Description | +|--------|-------------| +| [gds_software.erd.elements](erd-elements.md) | ERDEntity, Attribute, ERDRelationship, Cardinality | +| [gds_software.erd.model](erd-model.md) | ERDModel container | +| [gds_software.erd.compile](erd-compile.md) | ERD -> GDSSpec / SystemIR compiler | +| [gds_software.erd.checks](erd-checks.md) | ER-001..ER-004 verification checks | + +## Dependency Graphs + +| Module | Description | +|--------|-------------| +| [gds_software.dependency.elements](dep-elements.md) | Module, Dep, Layer | +| [gds_software.dependency.model](dep-model.md) | DependencyModel container | +| [gds_software.dependency.compile](dep-compile.md) | Dependency -> GDSSpec / SystemIR compiler | +| [gds_software.dependency.checks](dep-checks.md) | DG-001..DG-004 verification checks | + +## Verification + +| Module | Description | +|--------|-------------| +| [gds_software.verification](verification.md) | Union dispatch verify() engine | diff --git a/docs/software/api/init.md b/docs/software/api/init.md new file mode 100644 index 0000000..befacd2 --- /dev/null +++ b/docs/software/api/init.md @@ -0,0 +1,8 @@ +# gds_software + +Public API -- top-level exports. + +::: gds_software + options: + show_submodules: false + members: false diff --git a/docs/software/api/sm-checks.md b/docs/software/api/sm-checks.md new file mode 100644 index 0000000..755943d --- /dev/null +++ b/docs/software/api/sm-checks.md @@ -0,0 +1,15 @@ +# gds_software.statemachine.checks + +State machine verification checks (SM-001..SM-006). + +::: gds_software.statemachine.checks.check_sm001_initial_state + +::: gds_software.statemachine.checks.check_sm002_reachability + +::: gds_software.statemachine.checks.check_sm003_determinism + +::: gds_software.statemachine.checks.check_sm004_guard_completeness + +::: gds_software.statemachine.checks.check_sm005_region_partition + +::: gds_software.statemachine.checks.check_sm006_transition_validity diff --git a/docs/software/api/sm-compile.md b/docs/software/api/sm-compile.md new file mode 100644 index 0000000..b4b6368 --- /dev/null +++ b/docs/software/api/sm-compile.md @@ -0,0 +1,19 @@ +# gds_software.statemachine.compile + +Compiler: StateMachineModel -> GDSSpec / SystemIR. + +## Semantic Types + +::: gds_software.statemachine.compile.EventType + +::: gds_software.statemachine.compile.EventSpace + +::: gds_software.statemachine.compile.StateType + +::: gds_software.statemachine.compile.StateSpace + +## Public Functions + +::: gds_software.statemachine.compile.compile_sm + +::: gds_software.statemachine.compile.compile_sm_to_system diff --git a/docs/software/api/sm-elements.md b/docs/software/api/sm-elements.md new file mode 100644 index 0000000..b779017 --- /dev/null +++ b/docs/software/api/sm-elements.md @@ -0,0 +1,13 @@ +# gds_software.statemachine.elements + +State machine element declarations -- frozen Pydantic models for user-facing declarations. + +::: gds_software.statemachine.elements.State + +::: gds_software.statemachine.elements.Event + +::: gds_software.statemachine.elements.Transition + +::: gds_software.statemachine.elements.Guard + +::: gds_software.statemachine.elements.Region diff --git a/docs/software/api/sm-model.md b/docs/software/api/sm-model.md new file mode 100644 index 0000000..f83bef3 --- /dev/null +++ b/docs/software/api/sm-model.md @@ -0,0 +1,5 @@ +# gds_software.statemachine.model + +StateMachineModel -- declarative container for state machine diagrams. + +::: gds_software.statemachine.model.StateMachineModel diff --git a/docs/software/api/verification.md b/docs/software/api/verification.md new file mode 100644 index 0000000..1952bda --- /dev/null +++ b/docs/software/api/verification.md @@ -0,0 +1,5 @@ +# gds_software.verification + +Verification engine -- union dispatch across all software diagram types. + +::: gds_software.verification.engine.verify diff --git a/docs/software/getting-started.md b/docs/software/getting-started.md new file mode 100644 index 0000000..283b7aa --- /dev/null +++ b/docs/software/getting-started.md @@ -0,0 +1,127 @@ +# Getting Started + +## Installation + +```bash +uv add gds-software +# or: pip install gds-software +``` + +For development (monorepo): + +```bash +git clone https://github.com/BlockScience/gds-core.git +cd gds-core +uv sync --all-packages +``` + +## Your First DFD + +A Data Flow Diagram models processes, external entities, data stores, and the flows between them: + +```python +from gds_software import ( + ExternalEntity, Process, DataStore, DataFlow, + DFDModel, compile_dfd, compile_dfd_to_system, verify, +) + +model = DFDModel( + name="Order System", + entities=[ExternalEntity(name="Customer")], + processes=[ + Process(name="Validate Order"), + Process(name="Process Payment"), + ], + stores=[DataStore(name="Order DB")], + flows=[ + DataFlow(name="Order", source="Customer", target="Validate Order"), + DataFlow(name="Valid Order", source="Validate Order", target="Process Payment"), + DataFlow(name="Record", source="Process Payment", target="Order DB"), + DataFlow(name="Confirmation", source="Process Payment", target="Customer"), + ], +) + +# Compile to GDS +spec = compile_dfd(model) +ir = compile_dfd_to_system(model) +print(f"{len(ir.blocks)} blocks, {len(ir.wirings)} wirings") + +# Verify +report = verify(model, include_gds_checks=False) +for f in report.findings: + print(f" [{f.check_id}] {'PASS' if f.passed else 'FAIL'} {f.message}") +``` + +## Your First State Machine + +A State Machine models states, events, and transitions: + +```python +from gds_software import ( + State, Event, Transition, StateMachineModel, + compile_sm, verify, +) + +model = StateMachineModel( + name="Traffic Light", + states=[ + State(name="Red", is_initial=True), + State(name="Green"), + State(name="Yellow"), + ], + events=[ + Event(name="timer"), + ], + transitions=[ + Transition(source="Red", target="Green", event="timer"), + Transition(source="Green", target="Yellow", event="timer"), + Transition(source="Yellow", target="Red", event="timer"), + ], +) + +# Compile and verify +spec = compile_sm(model) +report = verify(model, include_gds_checks=False) +for f in report.findings: + print(f" [{f.check_id}] {'PASS' if f.passed else 'FAIL'} {f.message}") +``` + +## Your First Component Diagram + +A Component Diagram models software components with provided and required interfaces: + +```python +from gds_software import ( + Component, InterfaceDef, Connector, + ComponentModel, compile_component, verify, +) + +model = ComponentModel( + name="Web App", + components=[ + Component(name="Frontend", provides=["UI"], requires=["API"]), + Component(name="Backend", provides=["API"], requires=["DB"]), + Component(name="Database", provides=["DB"]), + ], + interfaces=[ + InterfaceDef(name="UI"), + InterfaceDef(name="API"), + InterfaceDef(name="DB"), + ], + connectors=[ + Connector(name="F->B", source="Frontend", target="Backend", interface="API"), + Connector(name="B->D", source="Backend", target="Database", interface="DB"), + ], +) + +spec = compile_component(model) +report = verify(model, include_gds_checks=False) +for f in report.findings: + print(f" [{f.check_id}] {'PASS' if f.passed else 'FAIL'} {f.message}") +``` + +## Next Steps + +- [Diagram Types Guide](guide/diagram-types.md) -- all 6 diagram types with elements and GDS mapping +- [Verification Guide](guide/verification.md) -- all 27 domain checks explained +- [API Reference](api/init.md) -- complete auto-generated API docs diff --git a/docs/software/guide/diagram-types.md b/docs/software/guide/diagram-types.md new file mode 100644 index 0000000..79ca7c0 --- /dev/null +++ b/docs/software/guide/diagram-types.md @@ -0,0 +1,328 @@ +# Diagram Types + +`gds-software` supports six software architecture diagram types, each with its own element vocabulary, GDS mapping, and composition structure. + +## Data Flow Diagram (DFD) + +Data flow diagrams model **data movement** through a system -- processes transform data, external entities provide/consume it, and data stores persist it. + +### Elements + +| Element | Description | GDS Role | +|---------|-------------|----------| +| `ExternalEntity` | Actor outside the system boundary | BoundaryAction | +| `Process` | Data transformation step | Policy | +| `DataStore` | Persistent data repository | Mechanism + Entity | +| `DataFlow` | Directed data movement | Wiring | + +### GDS Mapping + +Three-tier composition with optional temporal loop: + +``` +Composition: (entities |) >> (processes |) >> (stores |) + .loop([stores -> processes]) # if stores exist +Canonical: h = g (no stores) or h = f . g (with stores) +``` + +### Checks + +DFD-001..DFD-005 (5 checks). See [Verification](verification.md#dfd-checks). + +### Example + +```python +from gds_software import ( + ExternalEntity, Process, DataStore, DataFlow, DFDModel, +) + +model = DFDModel( + name="Order Processing", + entities=[ExternalEntity(name="Customer")], + processes=[Process(name="Validate"), Process(name="Ship")], + stores=[DataStore(name="Orders")], + flows=[ + DataFlow(name="Request", source="Customer", target="Validate"), + DataFlow(name="Save", source="Validate", target="Orders"), + DataFlow(name="Lookup", source="Orders", target="Ship"), + DataFlow(name="Notify", source="Ship", target="Customer"), + ], +) +``` + +--- + +## State Machine (SM) + +State machines model **behavioral transitions** -- states connected by event-triggered transitions with optional guards. + +### Elements + +| Element | Description | GDS Role | +|---------|-------------|----------| +| `State` | A discrete system state | Mechanism + Entity | +| `Event` | External trigger | BoundaryAction | +| `Transition` | State change on event | Policy | +| `Guard` | Boolean condition on transition | (embedded in Transition) | +| `Region` | Orthogonal concurrent partition | ParallelComposition | + +### GDS Mapping + +Three-tier composition: + +``` +Composition: (events |) >> (transitions |) >> (states |) + .loop([states -> transitions]) +Canonical: h = f . g (stateful) +``` + +### Checks + +SM-001..SM-006 (6 checks). See [Verification](verification.md#state-machine-checks). + +### Example + +```python +from gds_software import ( + State, Event, Transition, StateMachineModel, +) + +model = StateMachineModel( + name="Door", + states=[ + State(name="Closed", is_initial=True), + State(name="Open"), + State(name="Locked"), + ], + events=[Event(name="open"), Event(name="close"), Event(name="lock"), Event(name="unlock")], + transitions=[ + Transition(source="Closed", target="Open", event="open"), + Transition(source="Open", target="Closed", event="close"), + Transition(source="Closed", target="Locked", event="lock"), + Transition(source="Locked", target="Closed", event="unlock"), + ], +) +``` + +--- + +## Component Diagram (CP) + +Component diagrams model **software structure** -- components with provided/required interfaces connected by connectors. + +### Elements + +| Element | Description | GDS Role | +|---------|-------------|----------| +| `Component` | Software module with interfaces | Policy | +| `InterfaceDef` | Named interface contract | (metadata) | +| `Connector` | Wiring between components via interface | Wiring | + +### GDS Mapping + +Single-tier parallel composition: + +``` +Composition: (components |) +Canonical: h = g (stateless) +``` + +### Port Naming + +Uses ` + ` delimiter for token overlap: `"{Interface} + Provided"`, `"{Interface} + Required"`. + +### Checks + +CP-001..CP-004 (4 checks). See [Verification](verification.md#component-checks). + +### Example + +```python +from gds_software import ( + Component, InterfaceDef, Connector, ComponentModel, +) + +model = ComponentModel( + name="Microservices", + components=[ + Component(name="AuthService", provides=["Auth"], requires=["UserDB"]), + Component(name="UserStore", provides=["UserDB"]), + ], + interfaces=[InterfaceDef(name="Auth"), InterfaceDef(name="UserDB")], + connectors=[ + Connector(name="Auth->Users", source="AuthService", target="UserStore", interface="UserDB"), + ], +) +``` + +--- + +## C4 Model + +C4 models describe **system context and containers** -- people, systems, containers, and components with relationships. + +### Elements + +| Element | Description | GDS Role | +|---------|-------------|----------| +| `Person` | Human user/actor | BoundaryAction | +| `ExternalSystem` | System outside the boundary | BoundaryAction | +| `Container` | Deployable unit (app, database, etc.) | Policy | +| `C4Component` | Component within a container | Policy | +| `C4Relationship` | Directed dependency | Wiring | + +### GDS Mapping + +Two-tier composition: + +``` +Composition: (persons | externals) >> (containers | components) +Canonical: h = g (stateless) +``` + +### Checks + +C4-001..C4-004 (4 checks). See [Verification](verification.md#c4-checks). + +### Example + +```python +from gds_software import ( + Person, ExternalSystem, Container, C4Relationship, C4Model, +) + +model = C4Model( + name="E-Commerce", + persons=[Person(name="Shopper")], + external_systems=[ExternalSystem(name="Payment Gateway")], + containers=[ + Container(name="Web App", technology="React"), + Container(name="API", technology="FastAPI"), + Container(name="Database", technology="PostgreSQL"), + ], + relationships=[ + C4Relationship(source="Shopper", target="Web App", description="Browses"), + C4Relationship(source="Web App", target="API", description="API calls"), + C4Relationship(source="API", target="Database", description="Reads/writes"), + C4Relationship(source="API", target="Payment Gateway", description="Charges"), + ], +) +``` + +--- + +## Entity-Relationship Diagram (ERD) + +ERDs model **data structure** -- entities with attributes connected by relationships with cardinality. + +### Elements + +| Element | Description | GDS Role | +|---------|-------------|----------| +| `ERDEntity` | Data entity with attributes | Policy | +| `Attribute` | Entity field (name, type, PK/FK flags) | (embedded in ERDEntity) | +| `ERDRelationship` | Association between entities | Wiring | +| `Cardinality` | Relationship multiplicity (ONE, MANY) | (embedded in ERDRelationship) | + +### GDS Mapping + +Single-tier parallel composition: + +``` +Composition: (entities |) +Canonical: h = g (stateless) +``` + +### Checks + +ER-001..ER-004 (4 checks). See [Verification](verification.md#erd-checks). + +### Example + +```python +from gds_software import ( + ERDEntity, Attribute, ERDRelationship, Cardinality, ERDModel, +) + +model = ERDModel( + name="Blog", + entities=[ + ERDEntity( + name="User", + attributes=[ + Attribute(name="id", type="int", is_pk=True), + Attribute(name="email", type="str"), + ], + ), + ERDEntity( + name="Post", + attributes=[ + Attribute(name="id", type="int", is_pk=True), + Attribute(name="author_id", type="int", is_fk=True), + Attribute(name="title", type="str"), + ], + ), + ], + relationships=[ + ERDRelationship( + name="writes", + source="User", target="Post", + source_cardinality=Cardinality.ONE, + target_cardinality=Cardinality.MANY, + ), + ], +) +``` + +--- + +## Dependency Graph (DG) + +Dependency graphs model **module dependencies** with optional layered architecture constraints. + +### Elements + +| Element | Description | GDS Role | +|---------|-------------|----------| +| `Module` | Software module/package | Policy | +| `Dep` | Directed dependency between modules | Wiring | +| `Layer` | Architectural layer (for ordering constraints) | (metadata) | + +### GDS Mapping + +Single-tier parallel composition: + +``` +Composition: (modules |) +Canonical: h = g (stateless) +``` + +### Checks + +DG-001..DG-004 (4 checks). See [Verification](verification.md#dependency-checks). + +### Example + +```python +from gds_software import ( + Module, Dep, Layer, DependencyModel, +) + +model = DependencyModel( + name="Clean Architecture", + modules=[ + Module(name="handlers", layer="application"), + Module(name="services", layer="domain"), + Module(name="repository", layer="infrastructure"), + ], + deps=[ + Dep(source="handlers", target="services"), + Dep(source="repository", target="services"), + ], + layers=[ + Layer(name="application", order=0), + Layer(name="domain", order=1), + Layer(name="infrastructure", order=2), + ], +) +``` diff --git a/docs/software/guide/verification.md b/docs/software/guide/verification.md new file mode 100644 index 0000000..87d3646 --- /dev/null +++ b/docs/software/guide/verification.md @@ -0,0 +1,95 @@ +# Verification + +`gds-software` provides 27 domain-specific verification checks across six diagram types, plus access to the 6 GDS generic checks (G-001..G-006) via the unified `verify()` function. + +## Using verify() + +The `verify()` function auto-dispatches to the correct domain checks based on model type: + +```python +from gds_software import verify + +report = verify(model) # Domain + GDS checks +report = verify(model, include_gds_checks=False) # Domain checks only +``` + +The returned `VerificationReport` contains a list of `Finding` objects with: + +- `check_id` -- e.g., "DFD-001", "SM-003", "G-003" +- `severity` -- ERROR, WARNING, or INFO +- `message` -- human-readable description +- `passed` -- whether the check passed +- `source_elements` -- elements involved + +## DFD Checks + +| ID | Name | Severity | What it checks | +|----|------|----------|----------------| +| DFD-001 | Process connectivity | WARNING | Every process has >= 1 incoming and >= 1 outgoing flow | +| DFD-002 | Flow validity | ERROR | Flow source/target reference declared elements | +| DFD-003 | No external-to-external | ERROR | No direct flow between two external entities | +| DFD-004 | Store connectivity | WARNING | Every data store has >= 1 connected flow | +| DFD-005 | Process output | ERROR | Every process has at least one outgoing flow | + +## State Machine Checks + +| ID | Name | Severity | What it checks | +|----|------|----------|----------------| +| SM-001 | Initial state | ERROR | Exactly one initial state exists (per region) | +| SM-002 | Reachability | WARNING | All states reachable from initial state via transitions | +| SM-003 | Determinism | WARNING | No two transitions from the same state on the same event (without distinct guards) | +| SM-004 | Guard completeness | INFO | For guarded transitions, checks if guards cover all cases | +| SM-005 | Region partition | ERROR | If regions declared, every state belongs to exactly one region | +| SM-006 | Transition validity | ERROR | Transition source/target/event reference declared elements | + +## Component Checks + +| ID | Name | Severity | What it checks | +|----|------|----------|----------------| +| CP-001 | Interface satisfaction | WARNING | Every required interface is satisfied by some component's provided interface | +| CP-002 | Connector validity | ERROR | Connector source/target reference declared components | +| CP-003 | Dangling interfaces | WARNING | Every declared interface is referenced by at least one component | +| CP-004 | Component naming | ERROR | No duplicate component names | + +## C4 Checks + +| ID | Name | Severity | What it checks | +|----|------|----------|----------------| +| C4-001 | Relationship validity | ERROR | Relationship source/target reference declared elements | +| C4-002 | Container hierarchy | WARNING | Components reference valid parent containers | +| C4-003 | External connectivity | WARNING | Every external system has >= 1 relationship | +| C4-004 | Level consistency | WARNING | Relationships connect elements at appropriate C4 levels | + +## ERD Checks + +| ID | Name | Severity | What it checks | +|----|------|----------|----------------| +| ER-001 | Relationship validity | ERROR | Relationship source/target reference declared entities | +| ER-002 | Primary key existence | WARNING | Every entity has at least one PK attribute | +| ER-003 | Attribute uniqueness | ERROR | No duplicate attribute names within an entity | +| ER-004 | Relationship naming | ERROR | No duplicate relationship names | + +## Dependency Checks + +| ID | Name | Severity | What it checks | +|----|------|----------|----------------| +| DG-001 | Dependency validity | ERROR | Dep source/target reference declared modules | +| DG-002 | Acyclicity | ERROR | No circular dependencies in the module graph | +| DG-003 | Layer ordering | WARNING | Dependencies respect layer ordering (higher layers depend on lower) | +| DG-004 | Module connectivity | WARNING | Every module is part of at least one dependency | + +## GDS Generic Checks + +When `include_gds_checks=True` (default), the model is compiled to `SystemIR` and the 6 GDS generic checks run: + +| ID | Name | What it checks | +|----|------|----------------| +| G-001 | Domain/codomain compatibility | Wiring type tokens match | +| G-002 | Signature completeness | Every block has inputs and outputs | +| G-003 | Unique block naming | No duplicate block names | +| G-004 | Wiring source existence | Wired blocks exist | +| G-005 | Wiring target existence | Wired blocks exist | +| G-006 | Hierarchy consistency | Block tree is well-formed | + +!!! note + G-002 will flag `BoundaryAction` blocks (ExternalEntity, Person, Event) as having "no inputs" -- this is expected since they are exogenous sources by design. diff --git a/docs/software/index.md b/docs/software/index.md new file mode 100644 index 0000000..98ae121 --- /dev/null +++ b/docs/software/index.md @@ -0,0 +1,69 @@ +# gds-software + +[![PyPI](https://img.shields.io/pypi/v/gds-software)](https://pypi.org/project/gds-software/) +[![Python](https://img.shields.io/pypi/pyversions/gds-software)](https://pypi.org/project/gds-software/) +[![License](https://img.shields.io/github/license/BlockScience/gds-core)](https://github.com/BlockScience/gds-core/blob/main/LICENSE) + +**Software architecture DSL over GDS semantics** -- DFDs, state machines, component diagrams, C4 models, ERDs, and dependency graphs with formal verification. + +## What is this? + +`gds-software` extends the GDS framework with software architecture vocabulary -- six diagram types commonly used in software engineering, each compiled to GDS specifications with structural verification. It provides: + +- **6 diagram types** -- Data Flow Diagram (DFD), State Machine (SM), Component Diagram (CP), C4 Model, Entity-Relationship Diagram (ERD), Dependency Graph (DG) +- **Typed compilation** -- Each diagram compiles to GDS role blocks, entities, and composition trees +- **27 verification checks** -- Domain-specific structural validation across all diagram types +- **Canonical decomposition** -- Validated h = f ∘ g projection for all diagram types +- **Full GDS integration** -- All downstream tooling works immediately (canonical projection, semantic checks, gds-viz) + +## Architecture + +``` +gds-framework (pip install gds-framework) +| +| Domain-neutral composition algebra, typed spaces, +| state model, verification engine, flat IR compiler. +| ++-- gds-software (pip install gds-software) + | + | Software architecture DSL: 6 diagram types, + | compile_*(), domain verification, verify() dispatch. + | + +-- Your application + | + | Concrete architecture models, analysis notebooks, + | verification runners. +``` + +## Diagram Types at a Glance + +| Diagram | Elements | Checks | Canonical Form | +|---------|----------|--------|----------------| +| **DFD** | ExternalEntity, Process, DataStore, DataFlow | DFD-001..005 | Varies (stateful with data stores) | +| **State Machine** | State, Event, Transition, Guard, Region | SM-001..006 | Varies (stateful with states) | +| **Component** | Component, InterfaceDef, Connector | CP-001..004 | h = g (stateless) | +| **C4** | Person, ExternalSystem, Container, C4Component, C4Relationship | C4-001..004 | h = g (stateless) | +| **ERD** | ERDEntity, Attribute, ERDRelationship, Cardinality | ER-001..004 | h = g (stateless) | +| **Dependency** | Module, Dep, Layer | DG-001..004 | h = g (stateless) | + +## GDS Role Mappings + +All six diagram types follow a shared mapping pattern: + +- Exogenous inputs (ExternalEntity, Person, Event) --> `BoundaryAction` +- Decision/observation logic (Process, Transition, Module, Component) --> `Policy` +- State updates (DataStore, State, stateful containers) --> `Mechanism` + `Entity` +- Connections (DataFlow, Connector, Dep, Relationship) --> `Wiring` + +## Quick Start + +```bash +uv add gds-software +# or: pip install gds-software +``` + +See [Getting Started](getting-started.md) for a full walkthrough. + +## Credits + +Built on [gds-framework](../framework/index.md) by [BlockScience](https://block.science). diff --git a/docs/stockflow/api/checks.md b/docs/stockflow/api/checks.md new file mode 100644 index 0000000..bb84873 --- /dev/null +++ b/docs/stockflow/api/checks.md @@ -0,0 +1,13 @@ +# stockflow.verification.checks + +Stock-flow verification checks (SF-001..SF-005). + +::: stockflow.verification.checks.check_sf001_orphan_stocks + +::: stockflow.verification.checks.check_sf002_flow_stock_validity + +::: stockflow.verification.checks.check_sf003_auxiliary_acyclicity + +::: stockflow.verification.checks.check_sf004_converter_connectivity + +::: stockflow.verification.checks.check_sf005_flow_completeness diff --git a/docs/stockflow/api/compile.md b/docs/stockflow/api/compile.md new file mode 100644 index 0000000..b6727b9 --- /dev/null +++ b/docs/stockflow/api/compile.md @@ -0,0 +1,27 @@ +# stockflow.dsl.compile + +Compiler: StockFlowModel -> GDSSpec / SystemIR. + +## Semantic Types + +::: stockflow.dsl.compile.LevelType + +::: stockflow.dsl.compile.LevelSpace + +::: stockflow.dsl.compile.UnconstrainedLevelType + +::: stockflow.dsl.compile.UnconstrainedLevelSpace + +::: stockflow.dsl.compile.RateType + +::: stockflow.dsl.compile.RateSpace + +::: stockflow.dsl.compile.SignalType + +::: stockflow.dsl.compile.SignalSpace + +## Public Functions + +::: stockflow.dsl.compile.compile_model + +::: stockflow.dsl.compile.compile_to_system diff --git a/docs/stockflow/api/elements.md b/docs/stockflow/api/elements.md new file mode 100644 index 0000000..b9c2bb1 --- /dev/null +++ b/docs/stockflow/api/elements.md @@ -0,0 +1,11 @@ +# stockflow.dsl.elements + +Stock-flow element declarations -- frozen Pydantic models for user-facing declarations. + +::: stockflow.dsl.elements.Stock + +::: stockflow.dsl.elements.Flow + +::: stockflow.dsl.elements.Auxiliary + +::: stockflow.dsl.elements.Converter diff --git a/docs/stockflow/api/init.md b/docs/stockflow/api/init.md new file mode 100644 index 0000000..4493f74 --- /dev/null +++ b/docs/stockflow/api/init.md @@ -0,0 +1,8 @@ +# stockflow + +Public API -- top-level exports. + +::: stockflow + options: + show_submodules: false + members: false diff --git a/docs/stockflow/api/model.md b/docs/stockflow/api/model.md new file mode 100644 index 0000000..014b99c --- /dev/null +++ b/docs/stockflow/api/model.md @@ -0,0 +1,5 @@ +# stockflow.dsl.model + +StockFlowModel -- declarative container for stock-flow diagrams. + +::: stockflow.dsl.model.StockFlowModel diff --git a/docs/stockflow/api/verification.md b/docs/stockflow/api/verification.md new file mode 100644 index 0000000..f891b8d --- /dev/null +++ b/docs/stockflow/api/verification.md @@ -0,0 +1,5 @@ +# stockflow.verification + +Verification engine -- runs domain checks with optional GDS structural checks. + +::: stockflow.verification.engine.verify diff --git a/docs/stockflow/getting-started.md b/docs/stockflow/getting-started.md new file mode 100644 index 0000000..d98ba6b --- /dev/null +++ b/docs/stockflow/getting-started.md @@ -0,0 +1,94 @@ +# Getting Started + +## Installation + +```bash +uv add gds-stockflow +# or: pip install gds-stockflow +``` + +For development (monorepo): + +```bash +git clone https://github.com/BlockScience/gds-core.git +cd gds-core +uv sync --all-packages +``` + +## Your First Stock-Flow Model + +A stock-flow model describes accumulation dynamics: stocks hold value, flows transfer it, auxiliaries compute intermediate values, and converters inject exogenous inputs. + +```python +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 +spec = compile_model(model) +print(f"Blocks: {len(spec.blocks)}") # 6 blocks +print(f"Entities: {len(spec.entities)}") # 1 (Population stock) + +# Compile to SystemIR for verification +ir = compile_to_system(model) +print(f"{len(ir.blocks)} blocks, {len(ir.wirings)} wirings") + +# Verify — domain checks + GDS structural checks +report = verify(model, include_gds_checks=True) +print(f"{report.checks_passed}/{report.checks_total} checks passed") +``` + +## A Multi-Stock Model + +Stock-flow models shine with multiple interacting stocks: + +```python +from stockflow import ( + Stock, Flow, Auxiliary, StockFlowModel, verify, +) + +model = StockFlowModel( + name="SIR Epidemic", + stocks=[ + Stock(name="Susceptible", initial=990.0), + Stock(name="Infected", initial=10.0), + Stock(name="Recovered", initial=0.0), + ], + flows=[ + Flow(name="Infection", source="Susceptible", target="Infected"), + Flow(name="Recovery", source="Infected", target="Recovered"), + ], + auxiliaries=[ + Auxiliary(name="Infection Rate", inputs=["Susceptible", "Infected"]), + Auxiliary(name="Recovery Rate", inputs=["Infected"]), + ], +) + +# Compile and verify +spec = model.compile() if hasattr(model, 'compile') else compile_model(model) +report = verify(model, include_gds_checks=False) +for f in report.findings: + print(f" [{f.check_id}] {'PASS' if f.passed else 'FAIL'} {f.message}") +``` + +## Next Steps + +- [Elements & GDS Mapping](guide/elements.md) -- detailed element reference and how each maps to GDS +- [Verification Guide](guide/verification.md) -- all 5 domain checks explained +- [API Reference](api/init.md) -- complete auto-generated API docs diff --git a/docs/stockflow/guide/elements.md b/docs/stockflow/guide/elements.md new file mode 100644 index 0000000..6ddc30e --- /dev/null +++ b/docs/stockflow/guide/elements.md @@ -0,0 +1,116 @@ +# Elements & GDS Mapping + +`gds-stockflow` provides four element types, each mapping to a specific GDS role. + +## Stock + +Stocks accumulate value over time. Each stock becomes a GDS entity with a `level` state variable, and a mechanism block that applies incoming flow rates. + +```python +Stock(name="Population", initial=1000.0, non_negative=True) +``` + +**GDS mapping:** `Mechanism` (state update *f*) + `Entity` (state *X*) + +| 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 | + +### Port Convention + +- Output: `"{Name} Level"` (temporal feedback to auxiliaries) +- Input: `"{FlowName} Rate"` (incoming flow rates) + +--- + +## Flow + +Flows transfer value between stocks (or from/to "clouds" -- external sources/sinks). + +```python +Flow(name="Births", target="Population") # inflow from cloud +Flow(name="Deaths", source="Population") # outflow to cloud +Flow(name="Migration", source="A", target="B") # transfer between stocks +``` + +**GDS mapping:** `Policy` (rate computation *g*) + +| Field | Type | Default | Description | +|-------|------|---------|-------------| +| `name` | str | required | Flow name | +| `source` | str | "" | Source stock (empty = cloud inflow) | +| `target` | str | "" | Target stock (empty = cloud outflow) | + +### Port Convention + +- Output: `"{Name} Rate"` + +--- + +## Auxiliary + +Auxiliaries compute intermediate values from stocks, converters, or other auxiliaries. They form an acyclic dependency graph -- the compiler validates this at construction time. + +```python +Auxiliary(name="Birth Rate", inputs=["Population", "Fertility"]) +``` + +**GDS mapping:** `Policy` (decision logic *g*) + +| Field | Type | Default | Description | +|-------|------|---------|-------------| +| `name` | str | required | Auxiliary name | +| `inputs` | list[str] | [] | Names of stocks, converters, or auxiliaries this depends on | + +### Port Convention + +- Input: `"{InputName} Level"` or `"{InputName} Signal"` +- Output: `"{Name} Signal"` + +--- + +## Converter + +Converters represent exogenous constants or parameters -- values that enter the system from outside. Converters have no internal inputs. + +```python +Converter(name="Fertility", units="births/person/year") +``` + +**GDS mapping:** `BoundaryAction` (exogenous input *U*) + +| Field | Type | Default | Description | +|-------|------|---------|-------------| +| `name` | str | required | Converter name | +| `units` | str | "" | Unit label | + +### Port Convention + +- Output: `"{Name} Signal"` + +--- + +## 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 | + +## Composition Structure + +The compiler builds a tiered composition tree: + +``` +(converters |) >> (auxiliaries |) >> (flows |) >> (stock mechanisms |) + .loop([stock forward_out -> auxiliary forward_in]) +``` + +This maps to the GDS canonical form `h = f . g` where stocks carry state (X), mechanisms provide f, and all other elements contribute to g. diff --git a/docs/stockflow/guide/verification.md b/docs/stockflow/guide/verification.md new file mode 100644 index 0000000..e1a2b5a --- /dev/null +++ b/docs/stockflow/guide/verification.md @@ -0,0 +1,88 @@ +# Verification + +`gds-stockflow` provides 5 domain-specific verification checks, plus access to the 6 GDS generic checks (G-001..G-006) via the unified `verify()` function. + +## Using verify() + +The `verify()` function runs domain checks on the stock-flow model: + +```python +from stockflow import verify + +report = verify(model) # Domain checks only +report = verify(model, include_gds_checks=True) # Domain + GDS checks +``` + +The returned `VerificationReport` contains a list of `Finding` objects with: + +- `check_id` -- e.g., "SF-001", "G-003" +- `severity` -- ERROR, WARNING, or INFO +- `message` -- human-readable description +- `passed` -- whether the check passed +- `source_elements` -- elements involved + +## Domain Checks + +| 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 | + +### SF-001: Orphan Stocks + +Stocks not connected to any flow are flagged -- they accumulate nothing: + +``` +[SF-001] WARNING: Stock 'Unused' has no connected flows +``` + +### SF-002: Flow-Stock Validity + +Flow source and target must reference declared stock names: + +``` +[SF-002] ERROR: Flow 'Transfer' references undeclared stock 'Missing' +``` + +### SF-003: Auxiliary Acyclicity + +Auxiliaries form a dependency graph. Cycles would create infinite recursion: + +``` +[SF-003] ERROR: Cycle detected in auxiliary dependencies: A -> B -> A +``` + +### SF-004: Converter Connectivity + +Converters not referenced by any auxiliary are flagged as unused: + +``` +[SF-004] WARNING: Converter 'Unused Param' is not referenced by any auxiliary +``` + +### SF-005: Flow Completeness + +Every flow must have at least one of `source` or `target`: + +``` +[SF-005] ERROR: Flow 'Broken' has neither source nor target +``` + +## GDS Generic Checks + +When `include_gds_checks=True`, the model is compiled to `SystemIR` and the 6 GDS generic checks run: + +| ID | Name | What it checks | +|----|------|----------------| +| G-001 | Domain/codomain compatibility | Wiring type tokens match | +| G-002 | Signature completeness | Every block has inputs and outputs | +| G-003 | Unique block naming | No duplicate block names | +| G-004 | Wiring source existence | Wired blocks exist | +| G-005 | Wiring target existence | Wired blocks exist | +| G-006 | Hierarchy consistency | Block tree is well-formed | + +!!! note + G-002 will flag `BoundaryAction` blocks (Converters) as having "no inputs" -- this is expected since they are exogenous sources by design. diff --git a/docs/stockflow/index.md b/docs/stockflow/index.md new file mode 100644 index 0000000..54248f2 --- /dev/null +++ b/docs/stockflow/index.md @@ -0,0 +1,84 @@ +# gds-stockflow + +[![PyPI](https://img.shields.io/pypi/v/gds-stockflow)](https://pypi.org/project/gds-stockflow/) +[![Python](https://img.shields.io/pypi/pyversions/gds-stockflow)](https://pypi.org/project/gds-stockflow/) +[![License](https://img.shields.io/github/license/BlockScience/gds-core)](https://github.com/BlockScience/gds-core/blob/main/LICENSE) + +**Declarative stock-flow DSL over GDS semantics** — system dynamics with formal verification. + +## What is this? + +`gds-stockflow` extends the GDS framework with system dynamics vocabulary — stocks, flows, auxiliaries, and converters. It provides: + +- **4 element types** — Stock, Flow, Auxiliary, Converter +- **Typed compilation** — Each element compiles to GDS role blocks, entities, and composition trees +- **5 verification checks** — Domain-specific structural validation (SF-001..SF-005) +- **Canonical decomposition** — Validated h = f ∘ g projection with state-dominant accumulation +- **Full GDS integration** — All downstream tooling works immediately (canonical projection, semantic checks, gds-viz) + +## Architecture + +``` +gds-framework (pip install gds-framework) +| +| Domain-neutral composition algebra, typed spaces, +| state model, verification engine, flat IR compiler. +| ++-- gds-stockflow (pip install gds-stockflow) + | + | Stock-flow DSL: Stock, Flow, Auxiliary, Converter elements, + | compile_model(), domain verification, verify() dispatch. + | + +-- Your application + | + | Concrete stock-flow models, analysis notebooks, + | verification runners. +``` + +## GDS Mapping + +``` +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) +``` + +## 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* + +## Canonical Form + +Stock-flow models produce the full dynamical form: + +| |X| | |f| | Form | Character | +|-----|-----|------|-----------| +| n | n | h = f ∘ g | State-dominant accumulation | + +Stocks carry state (X), mechanisms provide f, and all other elements contribute to g. + +## Quick Start + +```bash +uv add gds-stockflow +# or: pip install gds-stockflow +``` + +See [Getting Started](getting-started.md) for a full walkthrough. + +## Credits + +Built on [gds-framework](../framework/index.md) by [BlockScience](https://block.science). diff --git a/mkdocs.yml b/mkdocs.yml index ba2789e..6576d2e 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -155,6 +155,67 @@ nav: - gds_business.vsm.compile: business/api/vsm-compile.md - gds_business.vsm.checks: business/api/vsm-checks.md - gds_business.verification: business/api/verification.md + - Stock-Flow: + - Overview: stockflow/index.md + - Getting Started: stockflow/getting-started.md + - User Guide: + - Elements & GDS Mapping: stockflow/guide/elements.md + - Verification: stockflow/guide/verification.md + - API Reference: + - stockflow: stockflow/api/init.md + - stockflow.dsl.elements: stockflow/api/elements.md + - stockflow.dsl.model: stockflow/api/model.md + - stockflow.dsl.compile: stockflow/api/compile.md + - stockflow.verification.checks: stockflow/api/checks.md + - stockflow.verification: stockflow/api/verification.md + - Control: + - Overview: control/index.md + - Getting Started: control/getting-started.md + - User Guide: + - Elements & GDS Mapping: control/guide/elements.md + - Verification: control/guide/verification.md + - API Reference: + - gds_control: control/api/init.md + - gds_control.dsl.elements: control/api/elements.md + - gds_control.dsl.model: control/api/model.md + - gds_control.dsl.compile: control/api/compile.md + - gds_control.verification.checks: control/api/checks.md + - gds_control.verification: control/api/verification.md + - Software: + - Overview: software/index.md + - Getting Started: software/getting-started.md + - User Guide: + - Diagram Types: software/guide/diagram-types.md + - Verification: software/guide/verification.md + - API Reference: + - Overview: software/api/index.md + - gds_software: software/api/init.md + - gds_software.common: software/api/common.md + - gds_software.dfd.elements: software/api/dfd-elements.md + - gds_software.dfd.model: software/api/dfd-model.md + - gds_software.dfd.compile: software/api/dfd-compile.md + - gds_software.dfd.checks: software/api/dfd-checks.md + - gds_software.statemachine.elements: software/api/sm-elements.md + - gds_software.statemachine.model: software/api/sm-model.md + - gds_software.statemachine.compile: software/api/sm-compile.md + - gds_software.statemachine.checks: software/api/sm-checks.md + - gds_software.component.elements: software/api/cp-elements.md + - gds_software.component.model: software/api/cp-model.md + - gds_software.component.compile: software/api/cp-compile.md + - gds_software.component.checks: software/api/cp-checks.md + - gds_software.c4.elements: software/api/c4-elements.md + - gds_software.c4.model: software/api/c4-model.md + - gds_software.c4.compile: software/api/c4-compile.md + - gds_software.c4.checks: software/api/c4-checks.md + - gds_software.erd.elements: software/api/erd-elements.md + - gds_software.erd.model: software/api/erd-model.md + - gds_software.erd.compile: software/api/erd-compile.md + - gds_software.erd.checks: software/api/erd-checks.md + - gds_software.dependency.elements: software/api/dep-elements.md + - gds_software.dependency.model: software/api/dep-model.md + - gds_software.dependency.compile: software/api/dep-compile.md + - gds_software.dependency.checks: software/api/dep-checks.md + - gds_software.verification: software/api/verification.md - Examples: - Overview: examples/index.md - Learning Path: examples/learning-path.md From af1a4a1bee434decc29801d1b01d34056cd27723 Mon Sep 17 00:00:00 2001 From: rohan Date: Tue, 3 Mar 2026 15:30:35 +0530 Subject: [PATCH 4/4] docs: add llms.txt generation via mkdocs-llmstxt plugin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Generates /llms.txt (index) and /llms-full.txt (full content) from the existing nav structure on every mkdocs build. All 162 doc pages across 11 sections are included. No external services or API keys required — the plugin converts rendered HTML back to Markdown at build time. --- mkdocs.yml | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 1 + 2 files changed, 59 insertions(+) diff --git a/mkdocs.yml b/mkdocs.yml index 283e262..5301440 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -35,6 +35,64 @@ plugins: show_bases: true members_order: source docstring_style: google + - llmstxt: + markdown_description: | + GDS (Generalized Dynamical Systems) is a typed compositional specification + framework for complex systems. It provides a composition algebra (stack, + parallel, feedback, loop), a 3-stage compiler to flat IR, and a verification + engine. Five domain DSLs (games, control, stock-flow, software, business) + compile to the same GDS IR, validating it as a universal transition calculus. + full_output: llms-full.txt + sections: + Framework: + - index.md + - framework/index.md + - framework/quick-reference.md + - framework/getting-started/install.md + - framework/getting-started/quickstart.md + - framework/guide/*.md + - framework/design/*.md + - framework/api/*.md + Visualization: + - viz/index.md + - viz/getting-started.md + - viz/guide/*.md + - viz/api/*.md + Games: + - games/index.md + - games/getting-started.md + - games/guide/*.md + - games/design/*.md + - games/api/*.md + Business: + - business/index.md + - business/getting-started.md + - business/guide/*.md + - business/api/*.md + Stock-Flow: + - stockflow/index.md + - stockflow/getting-started.md + - stockflow/guide/*.md + - stockflow/api/*.md + Control: + - control/index.md + - control/getting-started.md + - control/guide/*.md + - control/api/*.md + Software: + - software/index.md + - software/getting-started.md + - software/guide/*.md + - software/api/*.md + Examples: + - examples/*.md + - examples/examples/*.md + Tutorials: + - tutorials/*.md + Guides: + - guides/*.md + Ecosystem: + - framework/ecosystem.md markdown_extensions: - admonition diff --git a/pyproject.toml b/pyproject.toml index efb2ab5..273eeab 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -79,4 +79,5 @@ docs = [ "mkdocs>=1.6", "mkdocs-material>=9.5", "mkdocstrings[python]>=0.27", + "mkdocs-llmstxt>=0.5", ]