Skip to content

gds_viz.mermaid

Core Mermaid syntax generation — flowchart and subgraph building utilities.

Lightweight visualization utilities for GDS systems.

Generates Mermaid flowchart diagrams from SystemIR or Block compositions. Mermaid diagrams can be rendered in: - GitHub markdown - GitLab markdown - VS Code markdown preview - mermaid.live - Any tool with Mermaid support

The module provides two visualization strategies:

  1. Flat diagrams (system_to_mermaid()) — show the compiled block structure with automatic shape/arrow styling based on block roles and wiring types.

  2. Architecture-aware diagrams — domain-specific visualizations that encode semantic information (agent/environment boundaries, private/public data flow, stateful vs stateless components). See examples/prisoners_dilemma/visualize.py for a reference implementation.

system_to_mermaid(system, show_hierarchy=False, *, theme=None)

Generate a Mermaid flowchart from a SystemIR.

Parameters:

Name Type Description Default
system SystemIR

The compiled system to visualize.

required
show_hierarchy bool

If True, uses the hierarchy tree to organize subgraphs. If False, renders a flat graph of all blocks.

False
theme MermaidTheme | None

Mermaid theme — one of 'default', 'neutral', 'dark', 'forest', 'base'. None uses the default ('neutral').

None

Returns:

Type Description
str

Mermaid flowchart diagram as a string.

Example
from examples.sir_epidemic.model import build_system
from gds_viz import system_to_mermaid

system = build_system()
mermaid = system_to_mermaid(system)
print(mermaid)
Source code in packages/gds-viz/gds_viz/mermaid.py
def system_to_mermaid(
    system: SystemIR,
    show_hierarchy: bool = False,
    *,
    theme: MermaidTheme | None = None,
) -> str:
    """Generate a Mermaid flowchart from a SystemIR.

    Args:
        system: The compiled system to visualize.
        show_hierarchy: If True, uses the hierarchy tree to organize subgraphs.
                       If False, renders a flat graph of all blocks.
        theme: Mermaid theme — one of 'default', 'neutral', 'dark', 'forest',
               'base'. None uses the default ('neutral').

    Returns:
        Mermaid flowchart diagram as a string.

    Example:
        ```python
        from examples.sir_epidemic.model import build_system
        from gds_viz import system_to_mermaid

        system = build_system()
        mermaid = system_to_mermaid(system)
        print(mermaid)
        ```
    """
    lines = [theme_directive(theme), "flowchart TD"]

    # Class definitions for role-based styling
    lines.extend(classdefs_for_roles(theme))

    if show_hierarchy and system.hierarchy:
        lines.append(_hierarchy_to_mermaid(system.hierarchy, indent=1))
    else:
        # Flat block diagram with role-based classes
        block_shapes = _get_block_shapes(system)
        block_roles = _get_block_roles(system)
        for block in system.blocks:
            shape_open, shape_close = block_shapes.get(block.name, ("[", "]"))
            safe_name = sanitize_id(block.name)
            role = block_roles.get(block.name, "generic")
            lines.append(
                f"    {safe_name}{shape_open}{block.name}{shape_close}:::{role}"
            )

    # Add wirings
    for wiring in system.wirings:
        src = sanitize_id(wiring.source)
        tgt = sanitize_id(wiring.target)
        label = wiring.label

        if wiring.is_temporal:
            # Temporal loop: dashed line with arrow back
            lines.append(f"    {src} -.{label}..-> {tgt}")
        elif wiring.is_feedback:
            # Feedback: thick arrow
            lines.append(f"    {src} =={label}==> {tgt}")
        elif wiring.direction == FlowDirection.CONTRAVARIANT:
            # Contravariant: backward arrow
            lines.append(f"    {tgt} <--{label}--- {src}")
        else:
            # Covariant forward: normal arrow
            lines.append(f"    {src} --{label}--> {tgt}")

    return "\n".join(lines)

block_to_mermaid(block, *, theme=None)

Generate a Mermaid flowchart from a Block composition tree.

This is a convenience wrapper that flattens the block and creates a minimal diagram showing the composition structure.

Parameters:

Name Type Description Default
block Block

The root block (atomic or composite).

required
theme MermaidTheme | None

Mermaid theme — one of 'default', 'neutral', 'dark', 'forest', 'base'. None uses the default ('neutral').

None

Returns:

Type Description
str

Mermaid flowchart diagram as a string.

Example
from gds.blocks.roles import BoundaryAction, Policy, Mechanism
from gds.types.interface import Interface, port
from gds_viz import block_to_mermaid

observe = BoundaryAction(
    name="Observe",
    interface=Interface(forward_out=(port("Signal"),))
)
decide = Policy(
    name="Decide",
    interface=Interface(
        forward_in=(port("Signal"),),
        forward_out=(port("Action"),)
    )
)
update = Mechanism(
    name="Update",
    interface=Interface(forward_in=(port("Action"),)),
    updates=[("Entity", "state")]
)

pipeline = observe >> decide >> update
print(block_to_mermaid(pipeline))
Source code in packages/gds-viz/gds_viz/mermaid.py
def block_to_mermaid(block: Block, *, theme: MermaidTheme | None = None) -> str:
    """Generate a Mermaid flowchart from a Block composition tree.

    This is a convenience wrapper that flattens the block and creates
    a minimal diagram showing the composition structure.

    Args:
        block: The root block (atomic or composite).
        theme: Mermaid theme — one of 'default', 'neutral', 'dark', 'forest',
               'base'. None uses the default ('neutral').

    Returns:
        Mermaid flowchart diagram as a string.

    Example:
        ```python
        from gds.blocks.roles import BoundaryAction, Policy, Mechanism
        from gds.types.interface import Interface, port
        from gds_viz import block_to_mermaid

        observe = BoundaryAction(
            name="Observe",
            interface=Interface(forward_out=(port("Signal"),))
        )
        decide = Policy(
            name="Decide",
            interface=Interface(
                forward_in=(port("Signal"),),
                forward_out=(port("Action"),)
            )
        )
        update = Mechanism(
            name="Update",
            interface=Interface(forward_in=(port("Action"),)),
            updates=[("Entity", "state")]
        )

        pipeline = observe >> decide >> update
        print(block_to_mermaid(pipeline))
        ```
    """
    from gds.compiler.compile import compile_system

    # Compile with default settings
    system = compile_system(name=block.name, root=block)
    return system_to_mermaid(system, show_hierarchy=False, theme=theme)