Typed DSL for Compositional Game Theory — define, verify, and report on open game patterns
Project description
gds-games
Typed DSL for compositional game theory, built on gds-framework.
Table of Contents
- Quick Start
- What is this?
- Architecture
- Game Types
- Composition
- Multi-Agent Helpers
- Pattern Registry
- IR Layers
- Verification
- CLI
- Examples
- Status
- Credits & Attribution
Quick Start
pip install gds-games
from 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")
What is this?
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
Information Flow Model
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.
Architecture
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.
Two Compilation Paths
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.
Game Types
Six atomic game types, each enforcing structural constraints on its signature:
DecisionGame
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.
CovariantFunction
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.
ContravariantFunction
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.
DeletionGame
Discards an input channel: X → {}. Information is intentionally lost.
DeletionGame(name="Filter", x="noise signal")
Constraint: Y must be empty. Represents intentional information filtering.
DuplicationGame
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.
CounitGame
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.
Composition
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) >> payoff
FeedbackFlow
A 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)
Object References in Flows
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")
Multi-Agent Helpers
reactive_decision_agent()
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) |
parallel()
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).
multi_agent_composition()
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 flows
The three-step structure: (1) parallel composition of agents, (2) sequential into router, (3) feedback loop with auto-generated flows.
Pattern Registry
Pattern.specialize()
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.
discover_patterns()
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.passed
IR Layers
PatternIR (domain-specific)
Preserves 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
SystemIR (GDS-generic)
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.
Serialization
from ogs import save_ir, load_ir
save_ir(ir_document, "game.json")
loaded = load_ir("game.json")
Verification
13 domain-specific checks in two categories:
Type Checks (T-001..T-006)
| 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 |
Structural Checks (S-001..S-007)
| 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)
CLI
# 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/
Examples
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 |
Status
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.
License
Apache-2.0
Built with Claude Code. All code is test-driven and human-reviewed.
Credits & Attribution
Author: Rohan Mehta — BlockScience
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 (BlockScience).
- Peter Hacker — Code auditing and review (BlockScience).
Lineage: Part of the cadCAD ecosystem for 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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file gds_games-0.99.0.tar.gz.
File metadata
- Download URL: gds_games-0.99.0.tar.gz
- Upload date:
- Size: 36.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","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":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
38ed6f5d0e4442959e0c4aeeecc1e67ceec93cb02d54b35e791b70ab9289fb3b
|
|
| MD5 |
30fd7c64a23e094295407272d7019046
|
|
| BLAKE2b-256 |
33754aac2a2b49c87e4b28c290941a869c13acbd9a03096cda2052b219218df8
|
File details
Details for the file gds_games-0.99.0-py3-none-any.whl.
File metadata
- Download URL: gds_games-0.99.0-py3-none-any.whl
- Upload date:
- Size: 12.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","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":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7a551a654cb645fc823f4c602fd542063a8ce364d3554cc10b0e191b3b763d23
|
|
| MD5 |
81e8cc462ae64a33970ca7e1b39714a3
|
|
| BLAKE2b-256 |
c1e678c2245d170f9fb5f00bbf3b0ac6d814a85e08526f500a9f089769aa2e47
|