Skip to content

fan_out

fan_out

Fan-out expansion for parameterized sheet instantiation.

Expands stage-level fan_out declarations into concrete sheet numbers at config parse time. All downstream consumers (DAG, parallel executor, state, validation) see only expanded int sheet numbers — zero changes needed.

Expansion rules

Stage 1 (fan_out: 1) → Sheet 1 (stage=1, instance=1, fan_count=1) Stage 2 (fan_out: 3) → Sheet 2 (stage=2, instance=1, fan_count=3) → Sheet 3 (stage=2, instance=2, fan_count=3) → Sheet 4 (stage=2, instance=3, fan_count=3) Stage 3 (fan_out: 1) → Sheet 5 (stage=3, instance=1, fan_count=1)

Dependency expansion patterns

1→N (fan-out): Each instance depends on the single source N→1 (fan-in): Single target depends on ALL instances N→N (instance-match): Instance i depends on instance i N→M (cross-fan): All-to-all (conservative)

Classes

FanOutMetadata dataclass

FanOutMetadata(stage, instance, fan_count)

Per-sheet metadata tracking which logical stage and instance it represents.

Attributes:

Name Type Description
stage int

Logical stage number (1-indexed).

instance int

Instance within the fan-out group (1-indexed).

fan_count int

Total instances in this stage's fan-out group.

FanOutExpansion dataclass

FanOutExpansion(total_sheets, total_stages, sheet_metadata=dict(), expanded_dependencies=dict(), stage_sheets=dict())

Result of expanding fan_out declarations into concrete sheet numbers.

Attributes:

Name Type Description
total_sheets int

Total concrete sheet count after expansion.

total_stages int

Original stage count before expansion.

sheet_metadata dict[int, FanOutMetadata]

Map of sheet_num → FanOutMetadata.

expanded_dependencies dict[int, list[int]]

Map of sheet_num → list of prerequisite sheet nums.

stage_sheets dict[int, list[int]]

Map of stage → list of sheet nums belonging to that stage.

Functions

expand_fan_out

expand_fan_out(total_stages, fan_out, stage_dependencies)

Expand stage-level fan_out into concrete sheet-level assignments.

Pure function — no side effects, no imports beyond stdlib.

Parameters:

Name Type Description Default
total_stages int

Number of logical stages (1-indexed).

required
fan_out dict[int, int]

Map of stage → instance count. Stages not listed default to 1.

required
stage_dependencies dict[int, list[int]]

Map of stage → list of prerequisite stages.

required

Returns:

Type Description
FanOutExpansion

FanOutExpansion with concrete sheet assignments, metadata, and deps.

Raises:

Type Description
ValueError

If fan_out references invalid stages or has invalid counts.

Source code in src/marianne/core/fan_out.py
def expand_fan_out(
    total_stages: int,
    fan_out: dict[int, int],
    stage_dependencies: dict[int, list[int]],
) -> FanOutExpansion:
    """Expand stage-level fan_out into concrete sheet-level assignments.

    Pure function — no side effects, no imports beyond stdlib.

    Args:
        total_stages: Number of logical stages (1-indexed).
        fan_out: Map of stage → instance count. Stages not listed default to 1.
        stage_dependencies: Map of stage → list of prerequisite stages.

    Returns:
        FanOutExpansion with concrete sheet assignments, metadata, and deps.

    Raises:
        ValueError: If fan_out references invalid stages or has invalid counts.
    """
    _validate_inputs(total_stages, fan_out, stage_dependencies)

    # Phase 1: Assign concrete sheet numbers sequentially
    sheet_metadata: dict[int, FanOutMetadata] = {}
    stage_sheets: dict[int, list[int]] = {}
    current_sheet = 1

    for stage in range(1, total_stages + 1):
        count = fan_out.get(stage, 1)
        sheets_for_stage: list[int] = []

        for instance in range(1, count + 1):
            sheet_metadata[current_sheet] = FanOutMetadata(
                stage=stage,
                instance=instance,
                fan_count=count,
            )
            sheets_for_stage.append(current_sheet)
            current_sheet += 1

        stage_sheets[stage] = sheets_for_stage

    total_sheets = current_sheet - 1

    # Phase 2: Expand stage-level dependencies to sheet-level
    expanded_deps: dict[int, list[int]] = {}

    for stage, dep_stages in stage_dependencies.items():
        target_sheets = stage_sheets[stage]
        target_count = len(target_sheets)

        for dep_stage in dep_stages:
            source_sheets = stage_sheets[dep_stage]
            source_count = len(source_sheets)

            _expand_dependency_pair(
                expanded_deps,
                target_sheets,
                target_count,
                source_sheets,
                source_count,
            )

    return FanOutExpansion(
        total_sheets=total_sheets,
        total_stages=total_stages,
        sheet_metadata=sheet_metadata,
        expanded_dependencies=expanded_deps,
        stage_sheets=stage_sheets,
    )