Typed DSL for compositional game theory, built on gds-framework.
- Quick Start
- What is this?
- Architecture
- Game Types
- Composition
- Multi-Agent Helpers
- Pattern Registry
- IR Layers
- Verification
- CLI
- Examples
- Status
- Credits & Attribution
pip install gds-gamesfrom ogs.dsl.games import DecisionGame, CovariantFunction
from ogs.dsl.pattern import Pattern
from ogs import compile_to_ir, verify
# Define atomic games with typed signatures (x=input, y=output, r=utility, s=coutility)
sensor = CovariantFunction(name="Sensor", x="observation", y="signal")
agent = DecisionGame(name="Agent", x="signal", y="action", r="reward", s="experience")
# Compose sequentially (auto-wires by token matching)
game = sensor >> agent
# Wrap in a Pattern and compile to IR
pattern = Pattern(name="Simple Decision", game=game)
ir = compile_to_ir(pattern)
# Run verification checks
report = verify(ir)
print(f"{report.checks_passed}/{report.checks_total} checks passed")gds-games extends the GDS framework with game-theoretic vocabulary from compositional game theory (Ghani, Hedges et al.). It provides:
- 6 atomic game types with port-constraint validators
- Pattern composition — sequential, parallel, feedback, and corecursive operators
- Multi-agent helpers —
parallel(),multi_agent_composition(),reactive_decision_agent()with configurable flags - Pattern registry —
discover_patterns()auto-discovers patterns from a directory;Pattern.specialize()derives named variants - Dual IR —
PatternIRfor game-theoretic analysis + projection to GDSSystemIR - 13 verification checks — type matching (T-001..T-006) and structural validation (S-001..S-007)
- Canonical bridge —
compile_pattern_to_spec()maps games toGDSSpecforh = f ∘ gprojection - 7 Markdown report templates via Jinja2
- 6 Mermaid diagram generators for visualization
- CLI —
ogs compile,ogs verify,ogs report
Every open game has four directed information channels:
X → 𝒢 → Y (covariant / forward)
R ← 𝒢 ← S (contravariant / feedback)
| Channel | Name | Direction | Description |
|---|---|---|---|
| X | Observations | forward in | What the game observes |
| Y | Choices | forward out | What the game decides |
| R | Utilities | backward in | Outcomes the game receives |
| S | Coutilities | backward out | Valuations the game transmits upstream |
This bidirectional structure is the foundation of compositional game theory — games compose by wiring outputs to inputs, both forward and backward.
gds-framework (pip install gds-framework)
│
│ Domain-neutral composition algebra, typed spaces,
│ state model, verification engine, flat IR compiler.
│
└── gds-games (pip install gds-games)
│
│ Game-theoretic DSL: OpenGame types, Pattern composition,
│ PatternIR, GDS canonical bridge, verification, reports, CLI.
│
└── Your application
│
│ Concrete game patterns, analysis notebooks,
│ verification runners.
Pattern (game tree + flows + metadata)
│
├─── compile_to_ir() ──→ PatternIR (game-theoretic analysis, reports, viz)
│ │
│ └─── .to_system_ir() ──→ SystemIR (GDS generic checks)
│
└─── compile_pattern_to_spec() ──→ GDSSpec (canonical projection h = f ∘ g)
- PatternIR preserves game-theoretic structure — game types, flow types, corecursive composition. Used for reports and domain visualization.
- GDSSpec maps all games to
Policyand inputs toBoundaryAction. Canonical projection yieldsg = all games, f = ∅, X = ∅— the system is pure policy (h = g), which is semantically correct for games that compute equilibria, not state updates.
Six atomic game types, each enforcing structural constraints on its signature:
A strategic decision — a player who observes context and chooses an action.
DecisionGame(name="Player", x="state", y="action", r="payoff", s="experience")Has all four channels: X, Y, R, S. The core building block for strategic interactions.
A pure forward transformation: X → Y. No utility or coutility.
CovariantFunction(name="Sensor", x="raw data", y="processed signal")Constraint: R and S must be empty. The simplest game type — pure functional transformation without feedback.
A pure backward transformation: R → S. No observations or choices.
ContravariantFunction(name="Evaluator", r="outcome", s="valuation")Constraint: X and Y must be empty. The dual of CovariantFunction.
Discards an input channel: X → {}. Information is intentionally lost.
DeletionGame(name="Filter", x="noise signal")Constraint: Y must be empty. Represents intentional information filtering.
Copies an input to multiple outputs: X → X × X. Information is broadcast.
DuplicationGame(name="Broadcast", x="signal", y=("copy A", "copy B"))Constraint: Y must have 2+ ports. Represents information broadcasting to multiple consumers.
Future-conditioned observation: X → {}, with S = X. Technical game for temporal conditioning.
CounitGame(name="Future Context", x="future observation", s="context for present")Constraint: Y and R must be empty. Makes future observations available as coutility for upstream games.
Games compose using the same operators as GDS blocks, plus one additional:
| Operator | Syntax | Description |
|---|---|---|
| Sequential | a >> b |
Chain forward: Y of a feeds X of b |
| Parallel | a | b |
Side-by-side: independent games |
| Feedback | a.feedback(wirings) |
Backward within timestep (CONTRAVARIANT) |
| Corecursive | a.corecursive(wirings) |
Forward across timesteps (extends GDS .loop()) |
# Sequential: sensor feeds into agent
pipeline = sensor >> agent
# Parallel: two independent agents
pair = alice | bob
# Combined: observe, then both agents decide
system = observation >> (alice | bob) >> payoffA convenience subclass of Flow that defaults to CONTRAVARIANT direction — avoids repeating direction=FlowDirection.CONTRAVARIANT on every feedback wiring:
from ogs.dsl.composition import FeedbackFlow
FeedbackFlow(source_game="Outcome", source_port="Outcome",
target_game="Reactive Decision", target_port="Outcome")
# equivalent to: Flow(..., direction=FlowDirection.CONTRAVARIANT)Flow and FeedbackFlow accept OpenGame instances for source_game/target_game — they are coerced to name strings at construction time:
out = outcome()
rd = reactive_decision()
# Pass game objects instead of strings
FeedbackFlow(source_game=out, source_port="Outcome",
target_game=rd, target_port="Outcome")Builds a configurable single-agent decision loop from atomic games. Two boolean flags control which components are included:
from ogs.dsl.library import reactive_decision_agent
# Full 5-game loop with feedback (default)
agent = reactive_decision_agent("Agent 1")
# Open-loop chain without feedback — for multi-agent patterns
agent = reactive_decision_agent("Agent 1", include_outcome=False, include_feedback=False)include_outcome |
include_feedback |
Returns | Games |
|---|---|---|---|
True (default) |
True (default) |
FeedbackLoop |
CB → Hist → Pol → RD → Out + 3 feedback flows |
False |
True |
FeedbackLoop |
CB → Hist → Pol → RD + 2 feedback flows |
True |
False |
SequentialComposition |
CB → Hist → Pol → RD → Out (open chain) |
False |
False |
SequentialComposition |
CB → Hist → Pol → RD (open chain) |
Compose a dynamic list of games in parallel — enables N-agent patterns without manually enumerating the | chain:
from ogs.dsl.library import parallel
agents = [reactive_decision_agent(f"Agent {i}", include_outcome=False, include_feedback=False)
for i in range(1, 4)]
agents_parallel = parallel(agents)Also available as ParallelComposition.from_list(games).
Composes N open-loop agents in parallel, wires them into a shared router, and auto-generates all N × K contravariant feedback flows:
from ogs.dsl.library import multi_agent_composition
agent1 = reactive_decision_agent("Agent 1", include_outcome=False, include_feedback=False)
agent2 = reactive_decision_agent("Agent 2", include_outcome=False, include_feedback=False)
game = multi_agent_composition(
agents=[agent1, agent2],
router=my_decision_router(),
feedback_port_map={
"outcome": ("Outcome", "Outcome"),
"experience": ("Experience", "Experience"),
"history": ("History Update", "History Update"),
},
)
# Returns FeedbackLoop with 2 × 3 = 6 contravariant feedback flowsThe three-step structure: (1) parallel composition of agents, (2) sequential into router, (3) feedback loop with auto-generated flows.
Derive a named pattern variant from a base, inheriting the game tree and overriding only metadata:
base = Pattern(name="Base", game=game_tree, inputs=[...])
variant = base.specialize(
name="Resource Exchange",
terminal_conditions=[TerminalCondition(name="Agreement", ...)],
action_spaces=[ActionSpace(game="Agent 1 Reactive Decision", actions=["accept", "reject"])],
# inputs inherited from base automatically
)The game tree is shared (not deep-copied). All other fields (inputs, terminal_conditions, action_spaces, initializations, composition_type, source) inherit from the base unless explicitly overridden.
Auto-discover all Pattern objects from Python files in a directory:
from ogs.registry import discover_patterns
# Scan directory for modules with a `pattern` attribute
all_patterns = discover_patterns("./patterns")
for name, pattern in all_patterns.items():
ir = compile_to_ir(pattern)
report = verify(ir)Skips __init__.py and _-prefixed files. Silently skips modules that fail to import. Returns an ordered dict mapping module stem name to Pattern object.
Works well with pytest parametrize for batch verification:
ALL_PATTERNS = discover_patterns(PATTERNS_DIR)
@pytest.mark.parametrize("name,pattern", ALL_PATTERNS.items())
def test_compile_all(name, pattern):
ir = compile_to_ir(pattern)
report = verify(ir)
assert report.passedPreserves game-theoretic structure for analysis and visualization:
OpenGameIR— game name, type, signature, logic, tagsFlowIR— source/target game+port, flow type, directionHierarchyNodeIR— composition tree includingCORECURSIVEtypePatternIR— top-level container with.to_system_ir()projection
Flat IR compatible with all GDS tooling. Obtained via PatternIR.to_system_ir(). Used for GDS generic verification (G-001..G-006) and cross-domain interop.
from ogs import save_ir, load_ir
save_ir(ir_document, "game.json")
loaded = load_ir("game.json")13 domain-specific checks in two categories:
| ID | Name | What It Checks |
|---|---|---|
| T-001 | Sequential type match | Y tokens of left overlap X tokens of right in >> |
| T-002 | Feedback type match | Source and target ports share tokens in feedback |
| T-003 | Corecursive type match | Source and target ports share tokens across time |
| T-004 | Input type match | Pattern inputs wire to valid game ports |
| T-005 | Port uniqueness | No port appears in multiple flows as a target |
| T-006 | Signature completeness | Every game port is either wired or is an external boundary |
| ID | Name | What It Checks |
|---|---|---|
| S-001 | Game reachability | Every game is reachable from an input or boundary |
| S-002 | No isolated games | No games disconnected from all flows |
| S-003 | Hierarchy consistency | Composition tree matches flat game list |
| S-004 | Flow completeness | Every flow has valid source and target |
| S-005 | Acyclicity | No cycles in forward flow graph (within timestep) |
| S-006 | Terminal reachability | Every game can reach a terminal or feedback sink |
| S-007 | Composition balance | Parallel branches have compatible interfaces |
from ogs import verify
# Domain checks only
report = verify(ir_document)
# Domain checks + GDS structural checks (G-001..G-006)
report = verify(ir_document, include_gds_checks=True)# Compile a pattern to IR (JSON)
ogs compile pattern.py -o output.json
# Run verification
ogs verify output.json
# Generate Markdown reports
ogs report output.json -o reports/Three tutorial examples in gds-examples demonstrate game-theoretic modeling using the GDS framework primitives:
| Example | Domain | What It Teaches |
|---|---|---|
| Prisoner's Dilemma | Game theory | Nested parallel composition, multi-entity state, temporal loops |
| Insurance Contract | Finance | Complete 4-role taxonomy (ControlAction), pure sequential pipeline |
| Crosswalk Problem | Mechanism design | Discrete Markov transitions, governance parameters |
v0.3.0 — Alpha. Full DSL with 6 game types, 13 verification checks, 7 report templates, 6 diagram generators, canonical GDS bridge, CLI, multi-agent composition helpers, and pattern registry. 303 tests.
Apache-2.0
Built with Claude Code. All code is test-driven and human-reviewed.
Author: Rohan Mehta — Dynamical Systems Group
Theoretical foundation: Dr. Michael Zargham and Dr. Jamsheed Shorish — Generalized Dynamical Systems, Part I: Foundations (2021).
Game-theoretic foundation: Ghani, Hedges, Winschel, Zahn — Compositional Game Theory (2018). Bidirectional composition with contravariant feedback channels.
Architectural inspiration: Sean McOwen — MSML and bdp-lib.
Contributors:
- Michael Zargham — Project direction, GDS theory guidance, and technical review (Dynamical Systems Group).
- Peter Hacker — Code auditing and review (Dynamical Systems Group).
Lineage: Part of the cadCAD ecosystem for Complex Adaptive Dynamics.