Skip to content

refactor(planner): name the planner pipeline Stage 1 to Stage 3 and its fields by stage - #675

Merged
zzylol merged 1 commit into
mainfrom
stack/g1b-stage1-to-stage3-names
Oct 13, 2026
Merged

zzylol merged 1 commit into
mainfrom
stack/g1b-stage1-to-stage3-names

Conversation

@zzylol

@zzylol zzylol commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Why

User review on #674: the pipeline's names did not say which stages they cover ("this covers which stage(s)?", "What does label mean?", "What does owner mean?").

What

Pure rename plus docs; behavior is identical.

  • pass/stage_pipeline.rs → pass/stage1_to_stage3.rs; plan_stages → run_stage1_to_stage3; StagePipelineRun → Stage1ToStage3Run; StagePipeline → Stage1ToStage3Pass, registry name stage-pipeline → stage1-to-stage3. The module doc now opens with Stage 1 → Stage 2 → Stage 3 from the query-language frontends' roots, with Stage 0 run before it in the facade.
  • Run fields named by stage: stage1_pass1_per_computation, stage1_logical_asap_optimization, stage2_physical_asap_optimization, stage2_pruned, stage3_plan_selection. combinations stays, documented as the number of Stage 1 whole-workload candidates. Every field doc names its stage.
  • label → display_name (the fn and the LogicalCandidate / PhysicalCandidate / PrunedCandidate fields), documented as a human-readable name used only for display. The stage_pipeline JSON key changes to match; the dag-viewer on main does not read it. Fixture regenerated: the only changes are the key rename and the resulting key order.
  • target_owners / owner → query_of_target, the index of the query root that first reads each target.
  • Callers, re-exports (crate root, asap_planner), tests, devtools, and the pluggable-optimization doc are updated.

Key code interfaces added / modified

Renames only; signatures and behavior are otherwise unchanged.

crates/asap-aware-mapping/src/pass/stage1_to_stage3.rs (renamed from pass/stage_pipeline.rs), pass/mod.rs, lib.rs

/// The output of Stage 1, Stage 2 and Stage 3 for one workload, returned by
/// `run_stage1_to_stage3`; callers read it to show or test each stage.
pub struct Stage1ToStage3Run<Id> {                                         // was StagePipelineRun
    /// Stage 1, Pass 1: the per-computation candidates, with the roots they read.
    pub stage1_pass1_per_computation: PerComputationCandidates<Id>,       // was per_computation
    /// Number of whole-workload candidates (Cartesian product), saturating at `usize::MAX`.
    pub combinations: usize,
    /// Stage 1: every whole-workload logical candidate, in enumeration order.
    pub stage1_logical_asap_optimization: Vec<LogicalCandidate>,          // was logical
    /// Stage 2: one physical candidate (`P<n>` from `L<n>`) per Stage 1 candidate that composed and was implemented.
    pub stage2_physical_asap_optimization: Vec<PhysicalCandidate>,        // was physical
    /// Stage 2: the Stage 1 candidates Stage 2 could not implement, with reasons.
    pub stage2_pruned: Vec<PrunedCandidate>,                              // was pruned
    /// Stage 3: the selection over the Stage 2 candidates, or why it failed.
    pub stage3_plan_selection: Result<Selection, SelectionError>,         // was selection
}

/// One whole-workload logical candidate: one per-computation choice per Pass 1 target.
pub struct LogicalCandidate {
    /// `L<n>`, numbered in enumeration order.
    pub id: String,
    /// Human-readable name, e.g. "Q1 exact · Q2 CMS+heap".
    pub display_name: String,  // was label; display only
    /// `choice[t]` indexes `targets[t].choices.candidates` of the Pass 1 inventory.
    pub choice: Vec<usize>,
    /// The composed roots in input order, or why this choice did not compose.
    pub roots: Result<Vec<QueryRoot>, PerComputationCandidateError>,
}
/// A composed logical candidate that Stage 2 could not implement.
pub struct PrunedCandidate {
    /// Id of the logical candidate it came from.
    pub from_logical: String,
    /// Human-readable name, as on the logical candidate.
    pub display_name: String,  // was label; display only
    /// Why Stage 2 could not implement it.
    pub reason: String,
}

/// Runs Stage 1 (Pass 1 and composition), Stage 2 and Stage 3 over the
/// frontends' roots; `targets[i]` is `roots[i]`'s accuracy requirement. Only a
/// Pass 1 failure is an `Err`; other failures are reported inside the run.
// renamed from plan_stages; re-exported from the crate root and asap_planner
pub fn run_stage1_to_stage3<Id: Clone + std::fmt::Debug>(
    roots: Vec<(Id, QueryRoot)>,
    targets: &[Option<AccuracyTarget>],
    data: &DataWorkload,
    models: PlanningModels<'_>,
) -> Result<Stage1ToStage3Run<Id>, PerComputationCandidateError>;

/// The planner's default optimization pass: runs `run_stage1_to_stage3` over
/// the workload and returns the selected physical candidate's roots.
pub struct Stage1ToStage3Pass;  // was StagePipeline
impl OptimizationPass for Stage1ToStage3Pass {
    /// The pass's registry key.
    fn name(&self) -> &'static str;  // "stage1-to-stage3", was "stage-pipeline" (also the registry key)
}

crates/asap-aware-mapping/src/stage2_physical_asap_optimization.rs

/// One CandidatePhysicalASAPDAG (Stage 2 output), derived from exactly one
/// Stage 1 logical candidate; Stage 3 prices and selects among these.
pub struct PhysicalCandidate {
    /// `P<n>`, matching the `L<n>` it came from.
    pub id: String,
    /// Id of the logical candidate it implements.
    pub from_logical: String,
    /// Human-readable name of the candidate.
    pub display_name: String,  // was label; display only
    /// The timed query roots, one per query in workload order, that `dag` exports.
    pub roots: Vec<QueryRoot>,
    /// The physical DAG of this candidate.
    pub dag: PhysicalASAPDAG,
}

crates/devtools/src/bin/stage_pipeline.rs

// One candidate object: its id, its human-readable name, and its exported DAG.
{ "id": "...", "display_name": "...", "dag": { /* ... */ } }

Candidate objects in the stage_pipeline JSON document: key "label" renamed to "display_name".

crates/integration-tests/src/lib.rs

/// Test helpers that run the planner pipeline on one query with the built-in models.
pub mod planning {
    /// Every stage's output for the single query `root`; panics if Pass 1 rejects it.
    // renamed from plan_stages(root, accuracy) -> StagePipelineRun<usize>
    pub fn run_stage1_to_stage3(
        root: Rc<OperatorNode>,
        accuracy: AccuracyTarget,
    ) -> asap_aware_mapping::Stage1ToStage3Run<usize>;
}

Usage:

let run = run_stage1_to_stage3(
    roots.into_iter().enumerate().collect(),
    &[Some(target)],
    &data_workload,
    PlanningModels::builtin(),
)?;
let logical = &run.stage1_logical_asap_optimization;
let selected = run.selected();  // from run.stage3_plan_selection

Verification

  • cargo fmt --all -- --check: clean
  • cargo clippy --workspace --all-targets -- -D warnings: clean
  • cargo nextest run --workspace: 1632 passed, 50 skipped
  • python3 tools/dag-viewer/test_render.py: 29 tests OK
  • tools/dag-viewer/examples/planner-layering-example1.json regenerated: 6 label → display_name keys changed, nothing else

Human review — do not complete with an agent

  • The MVP boundary is correct.
  • New conceptual layers or public interfaces are necessary.
  • The before/after description matches the intended product behavior.
  • Human reviewer:
  • Decision and rationale:

…ts fields by stage

Follow-up to the review on #674. Pure rename plus docs; behavior unchanged.

- pass/stage_pipeline.rs -> pass/stage1_to_stage3.rs; plan_stages ->
  run_stage1_to_stage3; StagePipelineRun -> Stage1ToStage3Run;
  StagePipeline -> Stage1ToStage3Pass, registered as "stage1-to-stage3".
- Run fields named by stage: stage1_pass1_per_computation,
  stage1_logical_asap_optimization, stage2_physical_asap_optimization,
  stage2_pruned, stage3_plan_selection; combinations documented as Stage 1.
- Candidate label -> display_name (Logical/Physical/PrunedCandidate and the
  stage_pipeline JSON key); target_owners -> query_of_target.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W7qG9aFyPij5uWsyAJCxDW
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant