State-space control DSL over GDS semantics — control theory with formal guarantees.
- Quick Start
- What is this?
- Architecture
- Elements
- Semantic Type System
- Verification
- Examples
- Status
- Credits & Attribution
pip install gds-controlfrom 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 — produces a real GDSSpec with role blocks, entities, wirings
spec = compile_model(model)
ir = compile_to_system(model)
print(f"{len(ir.blocks)} blocks, {len(ir.wirings)} wirings")
# Verify — domain checks + optional GDS structural checks
report = verify(model, include_gds_checks=True)
print(f"{report.checks_passed}/{report.checks_total} checks passed")gds-control is a domain DSL that compiles state-space control systems to GDS specifications. You declare states, inputs, sensors, and controllers as plain data models — the compiler handles the mapping to GDS role blocks, entities, composition trees, and wirings.
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)
This maps directly to the standard state-space representation:
ẋ = Ax + Bu (state dynamics → Mechanism)
y = Cx + Du (sensor output → Policy)
u = K(y, r) (control law → Policy)
r (reference → BoundaryAction)
Once compiled, all downstream GDS tooling works immediately — canonical projection (h = f ∘ g), semantic checks, SpecQuery dependency analysis, JSON serialization, and gds-viz diagram generation.
ControlModel (user-facing declarations)
│
▼ compile_model()
GDSSpec (entities, blocks, wirings, parameters)
│
▼ compile_to_system()
SystemIR (flat IR for verification + visualization)
No parallel IR stack. The compiler produces a real GDSSpec with real GDS role blocks. This means control models are first-class GDS citizens — they compose with other GDS models, share the same verification engine, and render with the same visualization tools.
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.
Four declaration types, each mapping to a specific GDS role:
State(name="temperature", initial=20.0)GDS mapping: Mechanism (state update f) + Entity (state X)
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. States emit a State port for temporal feedback.
| Field | Type | Default | Description |
|---|---|---|---|
name |
str | required | State name (becomes entity name) |
initial |
float | None | None | Initial value |
Input(name="setpoint")GDS mapping: BoundaryAction (exogenous input U)
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.
| Field | Type | Default | Description |
|---|---|---|---|
name |
str | required | Input name |
Sensor(name="thermometer", observes=["temperature"])GDS mapping: Policy (observation g)
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.
| Field | Type | Default | Description |
|---|---|---|---|
name |
str | required | Sensor name |
observes |
list[str] | [] | Names of states this sensor reads |
Controller(name="PID", reads=["thermometer", "setpoint"], drives=["temperature"])GDS mapping: Policy (decision logic g)
A controller reads sensor measurements and/or reference inputs, then emits control signals to drive state variables. The reads list references sensors or inputs; the drives list references states.
| 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 |
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 |
Six domain-specific checks validate the control model structure before compilation:
| 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 |
from gds_control import verify
# Domain checks only
report = verify(model)
# Domain checks + GDS structural checks (G-001..G-006)
report = verify(model, include_gds_checks=True)One tutorial example in gds-examples demonstrates control system modeling using the GDS framework primitives:
| Example | Domain | What It Teaches |
|---|---|---|
| Thermostat PID | Control theory | .feedback() composition, CONTRAVARIANT backward flow, ControlAction role, multi-variable entities |
Thermostat structural view — feedback arrow shows CONTRAVARIANT energy cost flow
%%{init:{"theme":"neutral"}}%%
flowchart TD
classDef boundary fill:#93c5fd,stroke:#2563eb,stroke-width:2px,color:#1e3a5f
classDef policy fill:#fcd34d,stroke:#d97706,stroke-width:2px,color:#78350f
classDef mechanism fill:#86efac,stroke:#16a34a,stroke-width:2px,color:#14532d
classDef control fill:#d8b4fe,stroke:#9333ea,stroke-width:2px,color:#3b0764
classDef generic fill:#cbd5e1,stroke:#64748b,stroke-width:1px,color:#1e293b
Temperature_Sensor([Temperature Sensor]):::boundary
PID_Controller[PID Controller]:::generic
Room_Plant[Room Plant]:::generic
Update_Room[[Update Room]]:::mechanism
Temperature_Sensor --Measured Temperature--> PID_Controller
PID_Controller --Heater Command--> Room_Plant
Room_Plant --Room State--> Update_Room
Room_Plant ==Energy Cost==> PID_Controller
v0.1.0 — Alpha. Complete DSL with 6 verification checks and full GDS compilation. 117 tests.
Apache-2.0
Built with Claude Code. All code is test-driven and human-reviewed.
Author: Rohan Mehta — BlockScience
Theoretical foundation: Dr. Michael Zargham and Dr. Jamsheed Shorish — Generalized Dynamical Systems, Part I: Foundations (2021).
Architectural inspiration: Sean McOwen — MSML and bdp-lib.
Contributors:
- Michael Zargham — Project direction, GDS theory guidance, and technical review (BlockScience).
- Peter Hacker — Code auditing and review (BlockScience).
Lineage: Part of the cadCAD ecosystem for Complex Adaptive Dynamics.