Skip to main content

Generalized Dynamical Systems — typed compositional specifications for complex systems

Project description

gds-framework

Typed compositional specifications for complex systems, grounded in Generalized Dynamical Systems theory (Zargham & Shorish, 2022).

Glossary

Term Definition In the framework
State (x) The current configuration of the system — a point in the state space A value held by StateVariables inside an Entity
State Space (X) All possible configurations; can be any data structure, not just ℝⁿ Product of all Entity variables, each typed by TypeDef
Input (u) An external or agent-chosen action that influences the next state A signal flowing through Ports on a block's Interface
Admissible Input Space (U_x) The set of inputs available given the current state x Constraints encoded in ControlAction blocks
Input Map (g) Selects an input u from the admissible set — may be a decision-maker or stochastic process BoundaryAction (exogenous) or Policy (endogenous)
State Update Map (f) Takes current state and chosen input, produces the next state: f(x, u) → x⁺ Mechanism blocks — the only blocks that write to state
State Transition Map (h) The composed pipeline h = f|_x ∘ g — one full step of the system The wiring produced by >> composition
Trajectory (x₀, x₁, ...) A sequence of states produced by repeatedly applying h Temporal iteration via .loop()
Reachability Can the system reach state y from state x through some sequence of inputs? check_reachability() in the verification engine
Controllability Can the system be steered to a target state from any nearby initial condition? Formal property checked at the spec level
Configuration Space The subset of X where every point is reachable from some initial condition Characterized by transitive closure over the wiring graph

What is this?

gds-framework is a foundation layer for specifying dynamical systems as compositions of typed blocks. It provides the domain-neutral primitives — you bring the domain knowledge.

gds-framework                          Your domain package
─────────────────                       ──────────────────
Block, Interface, Port                  PredatorBlock, PreyBlock
>> | .feedback() .loop()                predator >> prey >> environment
TypeDef, Space, Entity                  Population(int, ≥0), EcosystemState
GDSSpec, verify()                       check_conservation(), check_stability()
compile_system() → SystemIR             visualize(), simulate()

The idea: a Generalized Dynamical System is a pair {h, X} where X is a state space (any data structure) and h: X → X is a state transition map. The GDS canonical form decomposes h into a pipeline of typed blocks — observations, decisions, and state updates — that compose via wiring:

GDS concept Paper notation gds-framework
State Space X Entity with StateVariables
Exogenous observation g(·) BoundaryAction
Decision / policy g: X → U_x Policy
State update f: X × U_x → X Mechanism
Admissible input constraint U: X → ℘(U) ControlAction
Transition map h = f|_x ∘ g Composed wiring (>>)
Trajectory x₀, x₁, ... Temporal loop (.loop())

This decomposition is the same regardless of whether you're modeling a biological ecosystem, a control system, a financial market, or a game-theoretic interaction. gds-framework provides the decomposition machinery; domain packages provide the semantics.

Architecture: foundation + domain packages

gds-framework (pip install gds-framework)
│
│  Domain-neutral composition algebra, typed spaces,
│  state model, verification engine, flat IR compiler.
│  No domain-specific concepts. No simulation. No rendering.
│
├── Domain: Ecology
│   └── Predator-prey dynamics, population models, SIR epidemiology
│
├── Domain: Control Systems
│   └── Controllers, plants, sensors, stability/controllability checks
│
├── Domain: Financial Systems
│   └── Insurance contracts, market mechanisms, conservation of flows
│
├── Domain: Game Theory
│   └── Iterated games, strategy adaptation, equilibrium analysis
│
└── Domain: Multi-Agent Systems
    └── Agent policies, environment dynamics, coordination protocols

Each domain package is a thin layer. The heavy lifting — composition, compilation, verification, querying — lives in gds-framework.

Example: what lives where

Consider modeling a Lotka-Volterra predator-prey system as a GDS. The state space is (prey_population, predator_population). Each timestep: the environment is observed, growth/predation rates are computed, populations are updated.

gds-framework provides (domain-neutral):

  • TypeDef(name="Population", python_type=int, constraint=lambda x: x >= 0) — constrained types
  • Entity(name="Prey", variables={"population": ...}) — state containers
  • BoundaryAction, Policy, Mechanism — block roles with interface constraints
  • >> composition with type checking, .loop() for temporal iteration
  • verify() — are all state variables updated? any write conflicts? all blocks reachable?

A domain package would add (ecology-specific):

  • Concrete block implementations with actual dynamics (Lotka-Volterra equations)
  • Domain-specific verification (population conservation, extinction checks)
  • Simulation execution (running trajectories from initial conditions)
  • Visualization (phase plots, time series)

The same split applies to any domain. An iterated prisoner's dilemma model would use BoundaryAction for observing the opponent's last move, Policy for strategy selection (tit-for-tat, always-defect, etc.), Mechanism for payoff calculation and score update, and .loop() for repeated rounds — all composed from the same primitives.

A thermostat control system would use BoundaryAction for the temperature sensor, Policy for the PID controller, Mechanism for the room's thermal dynamics, and .feedback() for the energy cost signal flowing backward.

Examples

Five tutorial examples in gds-examples demonstrate every framework feature. Each model.py reads like a tutorial chapter with inline GDS theory commentary.

# Example What It Teaches Composition
1 SIR Epidemic Fundamentals — TypeDef, Entity, Space, 3 block roles >> |
2 Thermostat PID .feedback(), CONTRAVARIANT, backward ports >> .feedback()
3 Lotka-Volterra .loop(), COVARIANT temporal iteration >> | .loop()
4 Prisoner's Dilemma Nested |, multi-entity state, complex trees | >> .loop()
5 Insurance Contract ControlAction role, complete 4-role taxonomy >>

Start with SIR Epidemic (no prerequisites) and work down — each introduces one new concept. Every example includes a test suite, a generate_views.py script producing 6 visualization views, and a pre-generated VIEWS.md.

Each model generates 6 views automatically. Here are two for the SIR Epidemic:

Structural view — compiled block graph with role-based shapes (stadium = boundary, double-bracket = terminal mechanism) and typed wiring labels:

%%{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
    Contact_Process([Contact Process]):::boundary
    Infection_Policy[Infection Policy]:::generic
    Update_Susceptible[[Update Susceptible]]:::mechanism
    Update_Infected[[Update Infected]]:::mechanism
    Update_Recovered[[Update Recovered]]:::mechanism
    Contact_Process --Contact Signal--> Infection_Policy
    Infection_Policy --Susceptible Delta--> Update_Susceptible
    Infection_Policy --Infected Delta--> Update_Infected
    Infection_Policy --Recovered Delta--> Update_Recovered

Canonical GDS view — mathematical decomposition showing state (X), input (U), policy (g), mechanism (f), and parameter space (Θ):

%%{init:{"theme":"neutral"}}%%
flowchart LR
    classDef boundary fill:#93c5fd,stroke:#2563eb,stroke-width:2px,color:#1e3a5f
    classDef policy fill:#fcd34d,stroke:#d97706,stroke-width:2px,color:#78350f
    classDef mechanism fill:#86efac,stroke:#16a34a,stroke-width:2px,color:#14532d
    classDef control fill:#d8b4fe,stroke:#9333ea,stroke-width:2px,color:#3b0764
    classDef generic fill:#cbd5e1,stroke:#64748b,stroke-width:1px,color:#1e293b
    classDef entity fill:#e2e8f0,stroke:#475569,stroke-width:2px,color:#0f172a
    classDef param fill:#fdba74,stroke:#ea580c,stroke-width:2px,color:#7c2d12
    classDef state fill:#5eead4,stroke:#0d9488,stroke-width:2px,color:#134e4a
    classDef target fill:#fca5a5,stroke:#dc2626,stroke-width:2px,color:#7f1d1d
    classDef empty fill:#e2e8f0,stroke:#94a3b8,stroke-width:1px,color:#475569
    X_t(["X_t<br/>Susceptible.count, Infected.count, Recovered.count"]):::state
    X_next(["X_{t+1}<br/>Susceptible.count, Infected.count, Recovered.count"]):::state
    Theta{{"Θ<br/>contact_rate, beta, gamma"}}:::param
    subgraph U ["Boundary (U)"]
        Contact_Process[Contact Process]:::boundary
    end
    subgraph g ["Policy (g)"]
        Infection_Policy[Infection Policy]:::policy
    end
    subgraph f ["Mechanism (f)"]
        Update_Susceptible[Update Susceptible]:::mechanism
        Update_Infected[Update Infected]:::mechanism
        Update_Recovered[Update Recovered]:::mechanism
    end
    X_t --> U
    U --> g
    g --> f
    Update_Susceptible -.-> |Susceptible.count| X_next
    Update_Infected -.-> |Infected.count| X_next
    Update_Recovered -.-> |Recovered.count| X_next
    Theta -.-> g
    Theta -.-> f
    style U fill:#dbeafe,stroke:#60a5fa,stroke-width:1px,color:#1e40af
    style g fill:#fef3c7,stroke:#fbbf24,stroke-width:1px,color:#92400e
    style f fill:#dcfce7,stroke:#4ade80,stroke-width:1px,color:#166534

Architecture by role view — blocks grouped by GDS role (boundary, policy, mechanism), with entity cylinders showing which state variables each mechanism writes:

%%{init:{"theme":"neutral"}}%%
flowchart TD
    classDef boundary fill:#93c5fd,stroke:#2563eb,stroke-width:2px,color:#1e3a5f
    classDef policy fill:#fcd34d,stroke:#d97706,stroke-width:2px,color:#78350f
    classDef mechanism fill:#86efac,stroke:#16a34a,stroke-width:2px,color:#14532d
    classDef control fill:#d8b4fe,stroke:#9333ea,stroke-width:2px,color:#3b0764
    classDef generic fill:#cbd5e1,stroke:#64748b,stroke-width:1px,color:#1e293b
    classDef entity fill:#e2e8f0,stroke:#475569,stroke-width:2px,color:#0f172a
    classDef param fill:#fdba74,stroke:#ea580c,stroke-width:2px,color:#7c2d12
    classDef state fill:#5eead4,stroke:#0d9488,stroke-width:2px,color:#134e4a
    classDef target fill:#fca5a5,stroke:#dc2626,stroke-width:2px,color:#7f1d1d
    classDef empty fill:#e2e8f0,stroke:#94a3b8,stroke-width:1px,color:#475569
    subgraph boundary ["Boundary (U)"]
        Contact_Process([Contact Process]):::boundary
    end
    subgraph policy ["Policy (g)"]
        Infection_Policy[Infection Policy]:::policy
    end
    subgraph mechanism ["Mechanism (f)"]
        Update_Susceptible[[Update Susceptible]]:::mechanism
        Update_Infected[[Update Infected]]:::mechanism
        Update_Recovered[[Update Recovered]]:::mechanism
    end
    entity_Susceptible[("Susceptible<br/>count: S")]:::entity
    entity_Infected[("Infected<br/>count: I")]:::entity
    entity_Recovered[("Recovered<br/>count: R")]:::entity
    Update_Susceptible -.-> entity_Susceptible
    Update_Infected -.-> entity_Infected
    Update_Recovered -.-> entity_Recovered
    Contact_Process --ContactSignalSpace--> Infection_Policy
    Infection_Policy --DeltaSpace--> Update_Infected
    Infection_Policy --DeltaSpace--> Update_Recovered
    Infection_Policy --DeltaSpace--> Update_Susceptible
    style boundary fill:#dbeafe,stroke:#60a5fa,stroke-width:1px,color:#1e40af
    style policy fill:#fef3c7,stroke:#fbbf24,stroke-width:1px,color:#92400e
    style mechanism fill:#dcfce7,stroke:#4ade80,stroke-width:1px,color:#166534

Parameter influence view — Θ → blocks → entities causal map. Shows which parameters affect which blocks, and through them which state variables. For the Thermostat PID, all 4 PID gains flow through a single control point:

%%{init:{"theme":"neutral"}}%%
flowchart LR
    classDef boundary fill:#93c5fd,stroke:#2563eb,stroke-width:2px,color:#1e3a5f
    classDef policy fill:#fcd34d,stroke:#d97706,stroke-width:2px,color:#78350f
    classDef mechanism fill:#86efac,stroke:#16a34a,stroke-width:2px,color:#14532d
    classDef control fill:#d8b4fe,stroke:#9333ea,stroke-width:2px,color:#3b0764
    classDef generic fill:#cbd5e1,stroke:#64748b,stroke-width:1px,color:#1e293b
    classDef entity fill:#e2e8f0,stroke:#475569,stroke-width:2px,color:#0f172a
    classDef param fill:#fdba74,stroke:#ea580c,stroke-width:2px,color:#7c2d12
    classDef state fill:#5eead4,stroke:#0d9488,stroke-width:2px,color:#134e4a
    classDef target fill:#fca5a5,stroke:#dc2626,stroke-width:2px,color:#7f1d1d
    classDef empty fill:#e2e8f0,stroke:#94a3b8,stroke-width:1px,color:#475569
    param_Kd{{"Kd"}}:::param
    param_Ki{{"Ki"}}:::param
    param_Kp{{"Kp"}}:::param
    param_setpoint{{"setpoint"}}:::param
    PID_Controller[PID Controller]
    entity_Room[("Room<br/>T, E")]:::entity
    param_Kd -.-> PID_Controller
    param_Ki -.-> PID_Controller
    param_Kp -.-> PID_Controller
    param_setpoint -.-> PID_Controller
    Update_Room -.-> entity_Room
    PID_Controller --> Room_Plant
    Room_Plant --> PID_Controller
    Room_Plant --> Update_Room

The remaining 2 views (architecture by domain, traceability) are in each example's VIEWS.md. Models with feedback show thick arrows (==>), temporal loops show dashed arrows (-.->).

See the gds-examples repo for the full guide and instructions on building new models from scratch.

Quick start

pip install gds-framework
from gds import (
    BoundaryAction, Policy, ControlAction,
    interface, Wiring,
    compile_system, verify,
)
from gds.ir.models import FlowDirection

# Define blocks with GDS roles and typed interfaces
sensor = BoundaryAction(
    name="Temperature Sensor",
    interface=interface(forward_out=["Temperature"]),
)
controller = Policy(
    name="PID Controller",
    interface=interface(
        forward_in=["Temperature", "Setpoint"],
        forward_out=["Heater Command"],
        backward_in=["Energy Cost"],
    ),
)
plant = ControlAction(
    name="Room",
    interface=interface(
        forward_in=["Heater Command"],
        forward_out=["Temperature"],
        backward_out=["Energy Cost"],
    ),
)

# Compose with operators — types checked at construction time
system = (sensor >> controller >> plant).feedback([
    Wiring(
        source_block="Room", source_port="Energy Cost",
        target_block="PID Controller", target_port="Energy Cost",
        direction=FlowDirection.CONTRAVARIANT,
    )
])

# Compile to flat IR and verify
ir = compile_system("Thermostat", system)
report = verify(ir)
print(f"{len(ir.blocks)} blocks, {len(ir.wirings)} wirings")
# 3 blocks, 3 wirings
print(f"{report.checks_passed}/{report.checks_total} checks passed")
# 13/14 checks passed (G-002 flags BoundaryAction for having no inputs — expected)

What's included

Layer 1 — Composition Algebra: Blocks with bidirectional typed interfaces, composed via four operators (>>, |, .feedback(), .loop()). A 3-stage compiler flattens composition trees into flat IR. Six generic verification checks validate structural properties.

Layer 2 — Specification Layer: TypeDef with runtime constraints, typed Spaces, Entity with StateVariables, block roles (BoundaryAction, Policy, Mechanism, ControlAction), GDSSpec registry, ParameterSchema for configuration space Θ, CanonicalGDS projection deriving the formal h = f ∘ g decomposition, Tagged mixin for inert semantic annotations, semantic verification (completeness, determinism, reachability, type safety, parameter references, canonical wellformedness), SpecQuery for dependency analysis, and JSON serialization.

Intellectual lineage

  • GDS formalism (Roxin 1960s; Zargham & Shorish 2022) — state transitions composed over arbitrary data structures, with formal notions of reachability, controllability, and admissibility
  • MSML (BlockScience) — block roles, parameter tracking, typed transmission channels
  • BDP-lib (Block Diagram Protocol) — abstract/concrete separation, structural validation
  • Categorical cybernetics (Ghani, Hedges et al.) — bidirectional composition with contravariant feedback

See docs/gds_deepdive.md for the full analysis and docs/gds_improvement_plan.md for the roadmap.

Status

v0.2.0 — Alpha. Both layers are implemented and tested (460 tests, 99% coverage). v0.2 adds parameter typing (Θ), canonical projection (h = f ∘ g derivation), tagged metadata, and 6 Mermaid visualization views via gds-viz. The composition algebra and specification layer are stable. Domain packages and simulation execution are not yet built — gds-framework is the foundation they will build on.

License

Apache-2.0

Credits & Attribution

Development & Implementation

Theoretical Foundation

This codebase is a direct implementation of the research and mathematical frameworks developed by:

Architectural Inspiration

The design patterns and structural approach of this library are heavily influenced by the prior work of Sean McOwen (@SeanMcOwen), specifically:

  • MSML: For system specification logic.
  • bdp-lib: For block-data processing architecture.

Contributors

  • Peter Hacker (@phacker3) — Code auditing and review (BlockScience).

Intellectual Lineage

This project exists within the broader ecosystem of:

  • cadCAD: For foundational philosophy in Complex Adaptive Dynamics.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

gds_framework-0.2.0.tar.gz (109.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

gds_framework-0.2.0-py3-none-any.whl (45.7 kB view details)

Uploaded Python 3

File details

Details for the file gds_framework-0.2.0.tar.gz.

File metadata

  • Download URL: gds_framework-0.2.0.tar.gz
  • Upload date:
  • Size: 109.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.0 {"installer":{"name":"uv","version":"0.10.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for gds_framework-0.2.0.tar.gz
Algorithm Hash digest
SHA256 9058e5509c9b5bfad27fea6dfdcd3c0c23536aa46a00e3b679d10e281e27f5ef
MD5 eb4e759583aa174125f3ed565f1c92c6
BLAKE2b-256 eda6632668276525f6c13fd072660a4aff6252b94b04fe9d751e386e2657e9a9

See more details on using hashes here.

File details

Details for the file gds_framework-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: gds_framework-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 45.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.0 {"installer":{"name":"uv","version":"0.10.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for gds_framework-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4fe985dc367e56d45483b03fc2d5e14df3792e5969aefb031791435613316bed
MD5 d8bb777314032edf486f3943f2aadb13
BLAKE2b-256 ceb224f22a04008002ad5c72f6f387930a90e5ae620defe6308aa36c5355768f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page