Skip to content

Rosetta Stone: Cross-Domain Comparison

Three views of the same resource pool problem, each compiled to a GDS canonical form. This guide demonstrates the central insight of GDS: the same composition algebra underlies stock-flow dynamics, feedback control, and strategic game theory.

The Resource Pool Scenario

A shared resource pool (water reservoir, inventory, commons) that agents interact with through supply, consumption, or extraction. The same real-world system is modeled through three different DSL lenses.

Canonical Spectrum

All three views compile to GDSSpec and project to the canonical h = f . g decomposition:

View             |X|  |U|  |g|  |f|  Form                 Character
----------------------------------------------------------------------
Stock-Flow         1    2    3    1  h_theta = f . g      Dynamical
Control            1    1    2    1  h_theta = f . g      Dynamical
Game Theory        0    1    3    0  h = g                Strategic

Key insight: each DSL decomposes the problem differently:

  • Stock-Flow: State X is the resource level, updated by net flow rates. Two exogenous parameters (supply rate, consumption rate) drive the dynamics.
  • Control: State X is the resource level, regulated by a feedback controller that tracks an exogenous reference setpoint.
  • Game Theory: No state -- pure strategic interaction. Two agents simultaneously choose extraction amounts; a payoff function determines the outcome.

Stock-Flow View

Models the resource pool as a stock that accumulates via a supply inflow and depletes via a consumption outflow. An auxiliary computes the net rate from supply and consumption parameters.

Canonical facts: |X|=1, |U|=2, |g|=3, |f|=1, character = Dynamical

StockFlowModel Declaration

from stockflow.dsl.compile import compile_model, compile_to_system
from stockflow.dsl.elements import Auxiliary, Converter, Flow, Stock
from stockflow.dsl.model import StockFlowModel

model = StockFlowModel(
    name="Resource Pool (Stock-Flow)",
    stocks=[
        Stock(name="ResourceLevel", initial=100.0, non_negative=True),
    ],
    flows=[
        Flow(name="supply", target="ResourceLevel"),
        Flow(name="consumption", source="ResourceLevel"),
    ],
    auxiliaries=[
        Auxiliary(name="net_rate", inputs=["supply_rate", "consumption_rate"]),
    ],
    converters=[
        Converter(name="supply_rate"),
        Converter(name="consumption_rate"),
    ],
)

Structural Diagram

%%{init:{"theme":"neutral"}}%%
flowchart TD
    classDef boundary fill:#93c5fd,stroke:#2563eb,stroke-width:2px,color:#1e3a5f
    classDef generic fill:#cbd5e1,stroke:#64748b,stroke-width:1px,color:#1e293b
    supply_rate([supply_rate]):::boundary
    consumption_rate([consumption_rate]):::boundary
    net_rate[net_rate]:::generic
    supply([supply]):::boundary
    consumption([consumption]):::boundary
    ResourceLevel_Accumulation[ResourceLevel Accumulation]:::generic
    supply_rate --supply_rate Signal--> net_rate
    consumption_rate --consumption_rate Signal--> net_rate
    supply --supply Rate--> ResourceLevel_Accumulation
    consumption --consumption Rate--> ResourceLevel_Accumulation

Canonical Diagram

%%{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 param fill:#fdba74,stroke:#ea580c,stroke-width:2px,color:#7c2d12
    classDef state fill:#5eead4,stroke:#0d9488,stroke-width:2px,color:#134e4a
    X_t(["X_t<br/>level"]):::state
    X_next(["X_{t+1}<br/>level"]):::state
    Theta{{"Θ<br/>supply_rate, consumption_rate"}}:::param
    subgraph U ["Boundary (U)"]
        supply_rate[supply_rate]:::boundary
        consumption_rate[consumption_rate]:::boundary
    end
    subgraph g ["Policy (g)"]
        net_rate[net_rate]:::policy
        supply[supply]:::policy
        consumption[consumption]:::policy
    end
    subgraph f ["Mechanism (f)"]
        ResourceLevel_Accumulation[ResourceLevel Accumulation]:::mechanism
    end
    X_t --> U
    U --> g
    g --> f
    ResourceLevel_Accumulation -.-> |ResourceLevel.level| 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

Control View

Models the same resource pool as a feedback control system. The resource level is a plant state, a target reference level is an exogenous input, and a controller adjusts the supply rate to track the target.

Canonical facts: |X|=1, |U|=1, |g|=2, |f|=1, character = Dynamical

ControlModel Declaration

from gds_control.dsl.compile import compile_model, compile_to_system
from gds_control.dsl.elements import Controller, Input, Sensor, State
from gds_control.dsl.model import ControlModel

model = ControlModel(
    name="Resource Pool (Control)",
    states=[
        State(name="resource_level", initial=100.0),
    ],
    inputs=[
        Input(name="target_level"),
    ],
    sensors=[
        Sensor(name="level_sensor", observes=["resource_level"]),
    ],
    controllers=[
        Controller(
            name="supply_controller",
            reads=["level_sensor", "target_level"],
            drives=["resource_level"],
        ),
    ],
)

Structural Diagram

%%{init:{"theme":"neutral"}}%%
flowchart TD
    classDef boundary fill:#93c5fd,stroke:#2563eb,stroke-width:2px,color:#1e3a5f
    classDef generic fill:#cbd5e1,stroke:#64748b,stroke-width:1px,color:#1e293b
    target_level([target_level]):::boundary
    level_sensor[level_sensor]:::generic
    supply_controller[supply_controller]:::generic
    resource_level_Dynamics[resource_level Dynamics]:::generic
    target_level --target_level Reference--> supply_controller
    level_sensor --level_sensor Measurement--> supply_controller
    supply_controller --supply_controller Control--> resource_level_Dynamics
    resource_level_Dynamics -.resource_level State..-> level_sensor

Canonical Diagram

%%{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 param fill:#fdba74,stroke:#ea580c,stroke-width:2px,color:#7c2d12
    classDef state fill:#5eead4,stroke:#0d9488,stroke-width:2px,color:#134e4a
    X_t(["X_t<br/>value"]):::state
    X_next(["X_{t+1}<br/>value"]):::state
    Theta{{"Θ<br/>target_level"}}:::param
    subgraph U ["Boundary (U)"]
        target_level[target_level]:::boundary
    end
    subgraph g ["Policy (g)"]
        level_sensor[level_sensor]:::policy
        supply_controller[supply_controller]:::policy
    end
    subgraph f ["Mechanism (f)"]
        resource_level_Dynamics[resource_level Dynamics]:::mechanism
    end
    X_t --> U
    U --> g
    g --> f
    resource_level_Dynamics -.-> |resource_level.value| 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

Game Theory View

Models the resource pool as a two-player extraction game using the OGS (Open Games) DSL. Two agents simultaneously decide how much to extract from a shared resource. Each agent's payoff depends on how much resource remains after both extractions -- a classic common-pool resource dilemma.

This is a stateless strategic interaction: no persistent state updates, pure policy computation.

Canonical facts: |X|=0, |U|=1, |g|=3, |f|=0, character = Strategic

OGS Pattern Declaration

from ogs.dsl.games import CovariantFunction, DecisionGame
from ogs.dsl.pattern import Pattern, PatternInput
from ogs.dsl.types import InputType, Signature, port

resource_input = PatternInput(
    name="Resource Availability",
    input_type=InputType.RESOURCE,
    schema_hint="float >= 0",
    target_game="Agent 1 Extraction",
    flow_label="Resource Signal",
)

agent1 = DecisionGame(
    name="Agent 1 Extraction",
    signature=Signature(
        x=(port("Resource Signal"),),
        y=(port("Agent 1 Decision"),),
        r=(port("Agent 1 Payoff"),),
    ),
    logic="Choose extraction amount based on resource availability",
)

agent2 = DecisionGame(
    name="Agent 2 Extraction",
    signature=Signature(
        x=(port("Resource Signal"),),
        y=(port("Agent 2 Decision"),),
        r=(port("Agent 2 Payoff"),),
    ),
    logic="Choose extraction amount based on resource availability",
)

payoff = CovariantFunction(
    name="Payoff Computation",
    signature=Signature(
        x=(port("Agent 1 Decision"), port("Agent 2 Decision")),
        y=(port("Allocation Result"),),
    ),
    logic="Compute payoffs based on total extraction vs available resource",
)

# Compose: agents decide in parallel, then payoff computation
game_tree = (agent1 | agent2) >> payoff

pattern = Pattern(
    name="Resource Pool (Game)",
    game=game_tree,
    inputs=[resource_input],
)

Canonical Diagram

Since there are no mechanisms (|f|=0), the canonical form reduces to h = g -- pure policy.

%%{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 state fill:#5eead4,stroke:#0d9488,stroke-width:2px,color:#134e4a
    X_t(["X_t"]):::state
    X_next(["X_{t+1}"]):::state
    subgraph U ["Boundary (U)"]
        Resource_Availability[Resource Availability]:::boundary
    end
    subgraph g ["Policy (g)"]
        Agent_1_Extraction[Agent 1 Extraction]:::policy
        Agent_2_Extraction[Agent 2 Extraction]:::policy
        Payoff_Computation[Payoff Computation]:::policy
    end
    X_t --> U
    U --> g
    g --> X_next
    style U fill:#dbeafe,stroke:#60a5fa,stroke-width:1px,color:#1e40af
    style g fill:#fef3c7,stroke:#fbbf24,stroke-width:1px,color:#92400e

Cross-Domain Comparison

The comparison table built programmatically by comparison.py reveals the unified transition calculus:

h_theta : X -> X    where h = f . g
  • When |f| > 0 and |g| > 0: Dynamical system (stock-flow, control)
  • When |f| = 0 and |g| > 0: Strategic system (game theory)
  • When |g| = 0 and |f| > 0: Autonomous system (no policy)

This is the "Rosetta Stone" -- the same mathematical structure expressed in different domain languages, all grounded in GDS theory.

from gds_examples.rosetta.comparison import canonical_spectrum_table

print(canonical_spectrum_table())

Interactive Notebook

Source code for packages/gds-examples/notebooks/rosetta.py

Tip: paste this code into an empty cell, and the marimo editor will create cells for you

"""Cross-Domain Rosetta Stone — interactive marimo notebook.

Compares the same resource-pool scenario across three DSL views
(Stock-Flow, Control, Game Theory), showing how they all map to
the GDS canonical form.

Run with: marimo run notebooks/rosetta.py
"""
# /// script
# requires-python = ">=3.12"
# dependencies = [
#     "gds-examples",
#     "marimo>=0.20.0",
# ]
# ///

import marimo

__generated_with = "0.10.0"
app = marimo.App(width="medium", app_title="GDS Rosetta Stone Guide")


@app.cell
def _(mo):
    mo.md(
        r"""
        # Cross-Domain Rosetta Stone

        The same **resource pool** scenario modeled in three domain-specific
        languages, all mapping to the GDS canonical form $h = f \circ g$.

        | DSL | Perspective | Key Idea |
        |-----|------------|----------|
        | **Stock-Flow** | Accumulation | Rates change resource level |
        | **Control** | Regulation | Controller tracks reference |
        | **Game Theory** | Strategic | Agents extract from pool |

        Each DSL compiles to a `GDSSpec`, from which `project_canonical()` extracts
        the formal decomposition. The table below summarises the **canonical spectrum**
        across all three views.
        """
    )
    return


@app.cell
def _(mo, comparison):
    _table = comparison.canonical_spectrum_table()
    mo.md(
        f"""
## Canonical Spectrum

```
{_table}
```

The spectrum reveals how the same real-world concept decomposes differently:

- **Stock-Flow** and **Control** are *dynamical* ($f \\neq \\emptyset$) — state
  evolves over time.
- **Game Theory** is *strategic* ($f = \\emptyset$) — pure policy, no persistent
  state.
"""
    )
    return


@app.cell
def _(mo):
    mo.md(
        r"""
        ---
        ## Stock-Flow View

        Models the resource pool as a stock that accumulates via supply inflow
        and depletes via consumption outflow. An auxiliary computes the net rate.

        **Key facts:** $|X|=1$, $|U|=2$, $|g|=3$, $|f|=1$, character = *Dynamical*
        """
    )
    return


@app.cell
def _(mo, gds_viz, sf_view):
    _model = sf_view.build_model()
    _spec = sf_view.build_spec()
    _system = sf_view.build_system()
    _canonical = sf_view.build_canonical()

    _structural_mermaid = gds_viz.system_to_mermaid(_system)
    _canonical_mermaid = gds_viz.canonical_to_mermaid(_canonical)

    mo.vstack(
        [
            mo.md(f"**Model:** {_model.name}"),
            mo.ui.tabs(
                {
                    "Structural Diagram": mo.mermaid(_structural_mermaid),
                    "Canonical Diagram": mo.mermaid(_canonical_mermaid),
                    "Formula": mo.md(
                        f"$$\n{_canonical.formula()}\n$$\n\n"
                        f"- State X: `{list(_canonical.state_variables)}`\n"
                        f"- Inputs U: `{list(_canonical.input_ports)}`\n"
                        f"- Policy g: `{list(_canonical.policy_blocks)}`\n"
                        f"- Mechanism f: `{list(_canonical.mechanism_blocks)}`"
                    ),
                }
            ),
        ]
    )
    return


@app.cell
def _(mo):
    mo.md(
        r"""
        ---
        ## Control View

        Models the same resource pool as a feedback control system.
        A controller adjusts supply to track an exogenous target reference level.

        **Key facts:** $|X|=1$, $|U|=1$, $|g|=2$, $|f|=1$, character = *Dynamical*
        """
    )
    return


@app.cell
def _(mo, gds_viz, ctrl_view):
    _model = ctrl_view.build_model()
    _spec = ctrl_view.build_spec()
    _system = ctrl_view.build_system()
    _canonical = ctrl_view.build_canonical()

    _structural_mermaid = gds_viz.system_to_mermaid(_system)
    _canonical_mermaid = gds_viz.canonical_to_mermaid(_canonical)

    mo.vstack(
        [
            mo.md(f"**Model:** {_model.name}"),
            mo.ui.tabs(
                {
                    "Structural Diagram": mo.mermaid(_structural_mermaid),
                    "Canonical Diagram": mo.mermaid(_canonical_mermaid),
                    "Formula": mo.md(
                        f"$$\n{_canonical.formula()}\n$$\n\n"
                        f"- State X: `{list(_canonical.state_variables)}`\n"
                        f"- Inputs U: `{list(_canonical.input_ports)}`\n"
                        f"- Policy g: `{list(_canonical.policy_blocks)}`\n"
                        f"- Mechanism f: `{list(_canonical.mechanism_blocks)}`"
                    ),
                }
            ),
        ]
    )
    return


@app.cell
def _(mo):
    mo.md(
        r"""
        ---
        ## Game Theory View

        Models the resource pool as a two-player extraction game. Two agents
        simultaneously choose extraction amounts; a payoff function computes
        allocations. No persistent state — pure strategic interaction.

        **Key facts:** $|X|=0$, $|U|=1$, $|g|=3$, $|f|=0$, character = *Strategic*
        """
    )
    return


@app.cell
def _(mo, gds_viz, game_view):
    _pattern = game_view.build_pattern()
    _canonical = game_view.build_canonical()

    _canonical_mermaid = gds_viz.canonical_to_mermaid(_canonical)

    mo.vstack(
        [
            mo.md(f"**Pattern:** {_pattern.name}"),
            mo.ui.tabs(
                {
                    "Canonical Diagram": mo.mermaid(_canonical_mermaid),
                    "Formula": mo.md(
                        "Since there are no mechanisms ($|f|=0$), the canonical "
                        "form reduces to $h = g$ — pure policy.\n\n"
                        f"- State X: `{list(_canonical.state_variables)}`\n"
                        f"- Inputs U: `{list(_canonical.input_ports)}`\n"
                        f"- Policy g: `{list(_canonical.policy_blocks)}`\n"
                        f"- Mechanism f: `{list(_canonical.mechanism_blocks)}`"
                    ),
                }
            ),
        ]
    )
    return


@app.cell
def _(mo):
    mo.md(
        r"""
        ---
        ## Cross-Domain Comparison

        Select a view below to explore in detail, or browse the tabs for a
        side-by-side comparison of all three canonical diagrams.
        """
    )
    return


@app.cell
def _(mo, gds_viz, sf_view, ctrl_view, game_view, comparison):
    _canonicals = comparison.build_all_canonicals()

    _sf_system = sf_view.build_system()
    _ctrl_system = ctrl_view.build_system()

    _sf_canonical_mermaid = gds_viz.canonical_to_mermaid(_canonicals["Stock-Flow"])
    _ctrl_canonical_mermaid = gds_viz.canonical_to_mermaid(_canonicals["Control"])
    _game_canonical_mermaid = gds_viz.canonical_to_mermaid(_canonicals["Game Theory"])

    view_dropdown = mo.ui.dropdown(
        options=["Stock-Flow", "Control", "Game Theory"],
        value="Stock-Flow",
        label="Select view:",
    )

    _comparison_tabs = mo.ui.tabs(
        {
            "Stock-Flow Canonical": mo.mermaid(_sf_canonical_mermaid),
            "Control Canonical": mo.mermaid(_ctrl_canonical_mermaid),
            "Game Theory Canonical": mo.mermaid(_game_canonical_mermaid),
        }
    )

    # Build comparison markdown from canonical data
    _rows = []
    for _name, _c in _canonicals.items():
        _x = len(_c.state_variables)
        _u = len(_c.input_ports)
        _g = len(_c.policy_blocks)
        _f = len(_c.mechanism_blocks)
        _char = "Dynamical" if _f > 0 else "Strategic"
        _form = _c.formula()
        _rows.append(f"| {_name} | {_x} | {_u} | {_g} | {_f} | {_form} | {_char} |")

    _comparison_md = mo.md(
        "### Comparison Data\n\n"
        "| View | |X| | |U| | |g| | |f| | Formula | Character |\n"
        "|------|-----|-----|-----|-----|---------|----------|\n" + "\n".join(_rows)
    )

    mo.vstack(
        [
            view_dropdown,
            _comparison_tabs,
            _comparison_md,
        ]
    )
    return (view_dropdown,)


@app.cell
def _(mo, gds_viz, view_dropdown, sf_view, ctrl_view, game_view):
    _selected = view_dropdown.value

    if _selected == "Stock-Flow":
        _canonical = sf_view.build_canonical()
        _system = sf_view.build_system()
        _detail_structural = mo.mermaid(gds_viz.system_to_mermaid(_system))
        _detail_canonical = mo.mermaid(gds_viz.canonical_to_mermaid(_canonical))
        _detail_content = mo.vstack(
            [
                mo.md(f"### {_selected} — Detail View"),
                mo.ui.tabs(
                    {
                        "Structural": _detail_structural,
                        "Canonical": _detail_canonical,
                    }
                ),
            ]
        )
    elif _selected == "Control":
        _canonical = ctrl_view.build_canonical()
        _system = ctrl_view.build_system()
        _detail_structural = mo.mermaid(gds_viz.system_to_mermaid(_system))
        _detail_canonical = mo.mermaid(gds_viz.canonical_to_mermaid(_canonical))
        _detail_content = mo.vstack(
            [
                mo.md(f"### {_selected} — Detail View"),
                mo.ui.tabs(
                    {
                        "Structural": _detail_structural,
                        "Canonical": _detail_canonical,
                    }
                ),
            ]
        )
    else:
        _canonical = game_view.build_canonical()
        _detail_canonical = mo.mermaid(gds_viz.canonical_to_mermaid(_canonical))
        _detail_content = mo.vstack(
            [
                mo.md(f"### {_selected} — Detail View"),
                mo.md("*(Game theory has no SystemIR structural diagram.)*"),
                _detail_canonical,
            ]
        )

    return (_detail_content,)


@app.cell
def _():
    import marimo as mo

    return (mo,)


@app.cell
def _():
    from gds_examples.rosetta import comparison, game_view
    from gds_examples.rosetta import control_view as ctrl_view
    from gds_examples.rosetta import stockflow_view as sf_view

    import gds_viz

    return comparison, ctrl_view, game_view, gds_viz, sf_view


if __name__ == "__main__":
    app.run()

To run the notebook locally:

uv run marimo run packages/gds-examples/notebooks/rosetta.py

Run the test suite:

uv run --package gds-examples pytest packages/gds-examples/tests/test_rosetta.py -v

Source Files

File Purpose
stockflow_view.py Stock-flow DSL model
control_view.py Control DSL model
game_view.py Game theory DSL model
comparison.py Cross-domain canonical comparison
rosetta.py Interactive marimo notebook