Repository navigation
refactor(planner)!: Pass 1 candidates are exact or a summary; fused computations become the outer target's FusedComputation - #729
Merged
Conversation
…sed computations become targets ComputationCandidate now says only how a target is computed: Exact, ExactAccumulator(Vec<ExactState>) or Summary(SketchAlgorithm). The whole-expression top-k and the nested-sum merge become fused targets (ComputationTarget::fused_target) that absorb the target beneath; avg becomes ExactAccumulator([Sum, Count]). WholeExpression, NestedSumMerge, AvgAsSumCount and ComputationCandidate::absorbs_inner are removed, and ComputationTarget::target is renamed aggregate. Enumeration order, candidate counts, plans, costs and labels are unchanged.
…ExactState ExactState.params only mirrored kind (no exact accumulator has parameters), so ExactAccumulator now holds Vec<ExactKind> and ExactState and ExactState::of are deleted. ExactParams stays: it is part of FieldDataType::ExactAggregate's serialized IR JSON, which the stage viewer reads.
…on_for
Replace ComputationTarget::fused_target (a boxed, cloned target) and
ComputationTarget::absorbs with fused: Option<FusedComputation { absorbs,
candidates }>, so a fused computation can no longer carry its own fused
computation or a missing absorbed index. ComputationTarget::chosen
becomes computation_for(index), returning the candidate and the target
it absorbs; composition and labels take aggregate, measure and samples
from the outer target.
Also refuse avg exact states other than [Sum, Count] in that order
(regression test avg_states_out_of_order_are_refused), document
ComputationTarget::choices and PerComputationCandidates::roots, and fix
compose_logical_candidate's doc on what choice[i] indexes.
This was referenced Oct 16, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
ComputationCandidatemixed two questions: which computation a Pass 1 target computes and how it is computed.WholeExpressionandNestedSumMergereally computed a different, fused computation (the outer aggregate together with the one beneath it), andAvgAsSumCountwas just two exact states. The design doc's Pass 1 table offers each target "exact or a summary"; the type should say the same.What
ComputationCandidatenow only says how a target is computed:Exact,ExactAccumulator(Vec<ExactKind>)orSummary(SketchAlgorithm).avgisExactAccumulator([Sum, Count]). Composition refuses any other order.fused: Option<FusedComputation { absorbs, candidates }>, which absorbs the target beneath.Summary(CmsWithHeap)andSummary(CountSketchWithHeap).ExactAccumulator([Sum]).WholeExpression,NestedSumMerge,AvgAsSumCountandComputationCandidate::absorbs_inner.ComputationTarget::absorbsis replaced byFusedComputation::absorbs.ComputationTarget::targetis renamedaggregate.ExactAccumulatorholds only the accumulator kinds, with no params;ExactParamsitself stays (see How).candidate_labelsreads the new shape. The design doc's Pass 1 section describes fused computations. No viewer change: the viewer shows onlydisplay_name, never candidate kinds.How
Fused-computation representation. A target with a fusion carries
fused: Some(FusedComputation { absorbs, candidates }).absorbsis the index intargetsof the inner target it replaces, andcandidatessays how the fused computation is computed. AFusedComputationholds no aggregate, measure, filter orHAVING; it is always the outer target's measure. So it cannot be in impossible states (a fused computation with its own fusion, or one with no absorbed target), and no target is cloned.A whole-workload choice keeps one digit per target. A digit ranges over the target's own candidates and then its fused computation's (
ComputationTarget::choice_count).ComputationTarget::computation_for(index)returns the chosen candidate together withSome(absorbed target)when it is the fused computation's.A fused group contributes the unfused pair (outer × inner) plus the fused candidates to the Cartesian product, so
combinations=n·m + f. The enumeration is unchanged:absorbed_choice_variesstill skips a fused pick with a non-first inner digit.Alternatives considered:
targets. It needs a "not computed" digit (Vec<Option<usize>>choices) everywhere, and a group-aware enumerator to keep the candidate order and ids.ComputationTargetas the fused alternative. This was the previous revision of this PR. It allowed impossible states and cloned the target.Composition.
compose_logical_candidategroups each target'scomputation_forresult bytarget.aggregate, as a privateChosenComputation { target, candidate, absorbs }. Insubstitute, the aggregate, measure andsamples_ofcome from the outer target. A candidate withabsorbs: Some(_)is fused:Summary(heap)builds the whole-expression heap over the absorbed input.ExactAccumulatorbuilds the nested Sum accumulator.Otherwise, a one-kind
ExactAccumulatoris finalized, and[Sum, Count]of anavgis the sum/count division, as before. Any other list isUnsupported.Why
ExactParamsstays. Composition buildsFieldDataType::ExactAggregate(kind, params)from the kind (a privateexact_family(kind)).ExactParamscarries no information: the kernels andcapability.rsonly check that it matcheskind. But it is part of a serialized format.FieldDataTypeisSerialize/Deserialize, so every exactSummaryAggin the IR JSON (thestage_pipelinedocument) is written as{"ExactAggregate": ["Sum", "Sum"]}. The stage viewer (tools/dag-viewer/stages.js, covered bytest_viewer.py) reads that array. Dropping the second element changes the wire format for the viewer and saved documents, so it belongs in its own format change.Where fused computations are created.
add_whole_expression_targetsmoves the top-k's heapSummarycandidates (its last candidates) into the fused computation, so the top-k keeps[Exact].add_nested_sum_targetsadds[ExactAccumulator([Sum])]. The eligibility checks are unchanged.Key code interfaces added / modified
crates/asap-aware-mapping/src/per_computation_candidates.rscrates/asap-aware-mapping/src/pass/stage1_to_stage3.rs: the signatures are unchanged.LogicalCandidate::choiceandStage1Candidate::choicenow indextargets[t].computation_for(choice[t]).next_choicecounts digits withchoice_count.crates/devtools/src/candidate_labels.rs:choice_labelkeeps its signature and its output. It labelsExactAccumulatoras "Sum acc" or "Sum/Count acc", and as "nested Sum acc" whencomputation_forreturns an absorbed target.Usage, end to end. This was run at the head commit as a test through the devtools pipeline that
stage_pipelineuses; every assert passed:Before this PR
For the panel above,
targets[outer].choices.candidateswas[ExactAccumulator { kind: Sum, params: Sum }, NestedSumMerge], withabsorbs: Some(1)on the outer target. The top-k intopk by (job) (10, sum_over_time(m[1m]))had[Exact, WholeExpression(CmsWithHeap), WholeExpression(CountSketchWithHeap)], andavghad[AvgAsSumCount]. The candidate enum carried both "which computation" and "how".After this PR
The outer sum has
[ExactAccumulator([Sum])]andfused: Some(FusedComputation { absorbs: 1, candidates: [ExactAccumulator([Sum])] }). The top-k has[Exact]and a fused computation with[Summary(CmsWithHeap), Summary(CountSketchWithHeap)].avghas[ExactAccumulator([Sum, Count])]. Choices, candidate ids and every plan are the same.Verification
cargo nextest run -p asap-aware-mapping -p asap-devtools -p asap-planner -p asap-frontend-sql -p asap-integration-testsgives 1117 passed, 29 skipped.cargo fmt --all -- --checkandcargo clippy --workspace --all-targets --all-features --locked -- -D warnings: clean.avg_states_out_of_order_are_refused. WithExactAccumulator([Count, Sum])on an avg target it failed before the order check (composition divided count by sum). It passes after.fused_computations_and_avg_states_have_their_own_labels: the nested sum is labeled apart from the separate sums, and an avg is labeled "Sum/Count acc".run_planning_stages. Examples 1, 2, 3a and 3b also throughstage_pipeline --max-displayed 100000. Logical candidates: 27, 82, 972, 5 and 12,500. Labels are distinct in each.stage_pipelineexact and ε=0.01/δ=0.001 runs of:topk by (job) (10, sum_over_time(m[1m])),topk by (job) (10, count_over_time(m[1m]))(whole-expression top-k),sum by (job) (sum_over_time(m[5m]))andsum by (job, __name__) (…)(nested sum),avg by (job) (m),avg_over_time(m[5m]),sum by (job) (rate(x[1m]))andtopk by (job) (10, rate(x[1m])). Also SQLAVG/SUM/COUNTover a non-null column, andAVGover a nullable one (pruned toExact).display_name.choice_count(), so they still cover the fused candidates.Limitations and follow-ups
ExactParamsstays because of the serialized IR format (see How). Removing it fromFieldDataType::ExactAggregateis a separate wire-format change that would touch the viewer and the saved example documents.