Skip to content

refactor(stage1): summary readouts and inputs as one operator over groups; Memo::add_candidate; one node equality in Pass 1 (#1203 2a-2) - #1218

Closed
zzylol wants to merge 4 commits into
refactor/1203-2a-intern-pass1from
refactor/1203-2a-2-summary-results-inputs
Closed

zzylol wants to merge 4 commits into
refactor/1203-2a-intern-pass1from
refactor/1203-2a-2-summary-results-inputs

Conversation

@zzylol

@zzylol zzylol commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #1215; part of #1203, tracked in #1207 (PR 2a-2).

Merge unit. This PR merges into main only together with the rest of #1207's merge unit (steps 1a–6, #1207 "Order"): interim items below are deleted by a later PR in the unit.

Why

#1203 decided one operator per candidate, every intermediate result its own memo group. #1215 did this for Pass 1; the Pass 2 rules still add private operator chains. This PR switches the first two rules, summary inputs (rule 3) and summary results (rule 8), and adds the shared function the other rules will switch to.

What

  • Rules 3 and 8 add each candidate as one operator over groups: every node beneath the rewritten root is found or added as a group with Stage 0's equality (over_child_groups, the walk refactor(stage1): Pass 1 adds a memo group for every intermediate result (#1203 PR 2a-1) #1215 added).
    • Rule 8 (summary_results): a candidate with two or more private builds and a readout of one of them gets a copy whose readouts, builds and inputs are all groups (origin SummaryResult). It no longer builds a dedicated readout producer per readout and a consumer over them.
    • Rule 3 (summary_inputs): the private Project / Filter inputs below a build are found or added as groups (origin SummaryInput, sized for the whole budget), and the consumer copy over them is one operator over groups.
  • Pass 1 and both rules go through one function, operator_over_groups (moved out of Pass 1): remove exact guarantees below the root, find or add the child groups, raise the parts of the added groups read through.
  • One candidate equality for the memo: the doc's Memo::add_candidate (section 3 of the concepts doc) adds a candidate unless an equal one is there (same input groups, fused groups, budget parts, and Stage 0's node equality, children by pointer first), and returns its CandidateForMemoGroupId either way. Rule 9 uses it too, in place of its own graph-signature check (SummaryStates::known). Memo::candidate(id) reads a candidate by id; CandidateForMemoGroupId moves to candidates.rs, where the doc puts it.
  • Test fix: sketch_rollup::quantiles_rolled_up_keep_the_sketch_bound failed once in CI (run 42071683865, attempt 1: web: rank 0.9395, error 0.0505 > 0.05). Not this PR's code: the test composes its candidates outside the memo, and the executor's KLL seeds its coin per run. Composed alone the KLL is sized for the calibrated failure probability 0.01 (k = 52), and the test checks eight answers. It now sizes each candidate for its target's failure probability as Pass 1 does, with the target at 1e-6 (k = 100).
  • One output-contract check: reuse::same_output_contract (result kind, fields, time index, unique keys, closed label set). check_output_contract, scan widening and rule 9 call it; scan widening's copy and rule 9's inline check are deleted.
  • boundary_groups looks groups up with Memo::group_whose_node_is and a predicate, not a pointer map each caller builds (review item E4 of refactor(stage1): Pass 1 adds a memo group for every intermediate result (#1203 PR 2a-1) #1215).
  • Plans unchanged: no corpus_snapshots / sketch_plan_snapshots file changes; Stage 2 candidate counts identical on every snapshot input.

New abstractions not in #1207 or the doc: decisions (2026-11-05)

Per #1207's interface policy, these names are not defined by #1207, #1203 or section 3 of memo-groups-properties-requirements.md. #1207's steps 1a–6 merge into main as one merge unit (#1207, Order), so an interim abstraction may stay in this PR only when a later PR of the unit deletes it; none reaches main. Rows marked Pending approval are not deleted by any PR in the unit and need the user's decision.

Abstraction What it is for Decision
operator_over_groups (crate fn) a rule's node tree as its root operator over found/added groups Interim, deleted by 2b (CandidateExpression)
without_exact_guarantees (one function since #1222) strip exact guarantees so a composed node equals the Stage 0 group's node Interim, deleted by 2b (guarantee out of the key)
reuse::add_candidate_over_groups, reuse::find_or_add_producer a rule's rewrite as one operator over groups; a producer's group with 1 budget part Interim, deleted by 2b / step 6
push_candidate_if_new candidate dedup Deleted in #1218: the doc's Memo::add_candidate
NodeToStructuralIdCache Pass 1's own structural identity Deleted in #1218: Pass 1 uses Memo::add_candidate and hash_consing::equal_nodes
hash_consing::equal_nodes Stage 0's equality without the sharing check, for Memo::add_candidate and Pass 1 Pending approval. Recommendation: keep (part of hash_consing, the one node equality)
reuse::same_output_contract (one copy, moved from scan widening) a rewrite keeps the candidate's output Pending approval. Recommendation: keep (the rules' only output check; the doc is silent on it)

How

  1. operator_over_groups(memo, root, queries, origin, parts): without_exact_guarantees_below(root) → over_child_groups(memo, ...) → size_own_candidate_for on every added group read through (groups_read_through_added_groups). Pass 1's target_candidates now calls it instead of the three steps inline.
  2. reuse::add_candidate_over_groups(memo, owner, original, node, origin): the output-contract check shared with append_reader (check_output_contract), then operator_over_groups with the owner's queries and original's parts, then Memo::add_candidate (added when the returned index is a new one). A rewrite whose groups include the owner itself is not added.
  3. reuse::find_or_add_producer(memo, node, queries, origin): exact guarantees removed (root included), then find_or_add_group with 1 part (every caller passed 1, which had no effect). Rule 3's canonical inputs use it; equal TimeRange inputs are already one group by Stage 0's equality, so the rule's same_range_input rebinding is deleted.

Representation / rules / enumeration

  • (a) Representation. Unchanged types. Candidates added by rules 3 and 8 now have a root_node that is one operator whose children are their input_groups' nodes (by child position), like Pass 1's. Rules 1, 2, 4–7, 9 and step 10 still add chains.
  • (b) Rules. Rules 3 and 8 extend the memo through find_or_add_group (via operator_over_groups / find_or_add_producer). Their matching still reads private chains (nodes_within, direct_input_builds), which only the unconverted rules still make.
  • (c) Enumeration. Unchanged (enumeration.rs, Stage 2, Stage 3 untouched).

Key code interfaces added / modified

crates/types/src/stages/hash_consing.rs

/// Whether two nodes are equal by Stage 0's equality, legal to share or not;
/// children compared by pointer first. Used by `Memo::add_candidate` only. // new, private to the types crate
pub(super) fn equal_nodes(left: &OperatorNode, right: &OperatorNode) -> bool;

crates/types/src/stages/candidates.rs

/// One candidate of a memo group: index `index` in group `group`'s candidates.
/// The doc's type; moved here from `stages/physical/memo_candidates.rs`. // moved, unchanged
pub struct CandidateForMemoGroupId {
    /// The group. // unchanged
    pub group: MemoGroupId,
    /// The candidate's index in the group's candidates. // unchanged
    pub index: usize,
}

impl Memo {
    /// Adds `candidate` to `group` and returns its id, or returns the id of
    /// the equal candidate there already (same input groups, fused groups,
    /// budget parts and an equal node): a candidate is in a group once.
    /// The doc's interface; replaces `push_candidate_if_new`. // new
    pub fn add_candidate(&mut self, group: MemoGroupId, candidate: CandidateForMemoGroup) -> CandidateForMemoGroupId;
    /// The candidate `id` names. The doc's interface; used by the planner's
    /// memo-choice check (`planner/src/run.rs`). // new
    pub fn candidate(&self, id: CandidateForMemoGroupId) -> &CandidateForMemoGroup;
}

crates/logical-asap-optimization/src/operator_over_groups.rs (new, crate-internal)

/// A composed or rewritten candidate root as one operator over found or added
/// groups, the added groups it reads through raised to its parts. Used by
/// Pass 1 and by the converted Pass 2 rules. // new (was inline in Pass 1)
pub(crate) fn operator_over_groups(memo: &mut Memo, root: &Rc<OperatorNode>, queries: &[usize],
    origin: &OriginOfCandidateForMemoGroup, accuracy_budget_parts: usize)
    -> (Rc<OperatorNode>, Vec<MemoGroupId>);
/// The root with exact guarantees removed below it. // moved from pass1/add_pass1_candidates.rs
pub(crate) fn without_exact_guarantees_below(root: &Rc<OperatorNode>) -> Rc<OperatorNode>;
// push_candidate_if_new: removed, now `Memo::add_candidate`

crates/logical-asap-optimization/src/pass2/memo/rules/reuse.rs (crate-internal)

/// Adds `original` rewritten by `origin` to `node` to group `owner`, as one
/// operator over groups; false when an equal candidate is there. // new
pub(in crate::pass2::memo) fn add_candidate_over_groups(memo: &mut Memo, owner: MemoGroupId,
    original: &CandidateForMemoGroup, node: Rc<OperatorNode>,
    origin: OriginOfCandidateForMemoGroup) -> Result<bool, Pass2RuleError>;
/// A producer's group, found or added with its exact guarantees removed. // new
pub(in crate::pass2::memo) fn find_or_add_producer(memo: &mut Memo, node: &Rc<OperatorNode>,
    queries: &[usize], origin: &OriginOfCandidateForMemoGroup) -> MemoGroupId;
/// Whether a rewrite keeps the original's output (result kind, fields, time
/// index, unique keys, closed label set). The one output-contract check. // new (moved from scan_labels.rs)
pub(in crate::pass2::memo) fn same_output_contract(expected: &OperatorNode, actual: &OperatorNode) -> bool;
/// Adds a chain candidate; still used by rollup (6, 7). // unchanged
pub(in crate::pass2::memo) fn append_reader(/* unchanged */) -> Result<bool, Pass2RuleError>;
/// Adds a dedicated producer group; still used by top-k (4) and rollup (6). // unchanged
pub(in crate::pass2::memo) fn append_producer(/* unchanged */) -> Result<MemoGroupId, Pass2RuleError>;

crates/logical-asap-optimization/src/pass2/memo/state_groups.rs (crate-internal)

/// The groups a node reads: each node beneath it that is the own node of a
/// group `is_boundary` accepts. // was (node, &HashMap<*const OperatorNode, MemoGroupId>)
pub(super) fn boundary_groups(memo: &Memo, node: &Rc<OperatorNode>,
    is_boundary: impl Fn(MemoGroupId) -> bool) -> Vec<MemoGroupId>;

Usage. Neither rule fires on the KLL example or on the scan-widening example: after #1215 no Pass 1 or Pass 2 candidate there has a private Project / Filter below a build or two private builds. asap_planner --workload kll.json --run-until stage1_pass2 --format text (and the scan-widening workload) print byte-identical output before and after this PR, so there is no memo excerpt to compare. The rules fire on the chains the unit tests inject into rule 1's tumbling-window candidates (state_groups::tests::check_private_inputs): for quantile_over_time(0.95, latency[5m]) and quantile_over_time(0.99, latency[5m]) with a private identity Project (or Filter true) under each private SummaryTumblingWindowChain:

# before: rule 3's reader copy keeps the chain private down to the new input group
G2   [k]  TumblingWindows  Project ← SummaryEstimate ← SummaryMerge ← SummaryTumblingWindowChain  inputs=[Gp]
Gp   [0]  SummaryInput     Project  inputs=[G0]
# after (printed from the test's memo): the reader copy is one operator; the estimate,
# merge and chain are groups, and both queries' copies share the chain and merge groups
G2   [k]  TumblingWindows  Project                     inputs=[G13]
G10  [0]  SummaryInput     Project                     inputs=[G0]   queries [0, 1]
G11  [0]  TumblingWindows  SummaryTumblingWindowChain  inputs=[G10]  queries [0, 1]
G12  [0]  TumblingWindows  SummaryMerge                inputs=[G11]  queries [0, 1]
G13  [0]  TumblingWindows  SummaryEstimate (q 0.95)    inputs=[G12]  queries [0]
G17  [0]  TumblingWindows  SummaryEstimate (q 0.99)    inputs=[G12]  queries [1]
# G14, G15, G16, G18: the same for the second tumbling-window width

Major algorithm changes

None. The rules match the same candidates; only the shape of what they add changes.

Before this PR

Rules 3 and 8 added dedicated producer groups (append_producer, reused only by origin and parts) and a consumer chain over them (append_reader, deduplicated by a deep ==).

After this PR

Rules 3 and 8 add one operator over groups, with groups found by Stage 0's equality; Pass 1 and both rules share operator_over_groups.

Deleted

  • summary_results.rs: its per-readout producer groups (append_producer), the replacement map and replace of the consumer.
  • summary_inputs.rs: the TimeRange rebinding through state_groups::same_range_input (Stage 0's equality finds the group), and canonical_preprocessing's Result (nothing fails any more).
  • pass1/add_pass1_candidates.rs: the inline strip / child groups / parts raise, now operator_over_groups; without_exact_guarantees_below moved out.
  • The pointer maps every boundary_groups caller built (reuse.rs ×2, scan_labels.rs, state_groups.rs ×2).
  • Second review: scan_labels::same_output_contract and rule 9's inline output check (now reuse::same_output_contract); rule 9's SummaryStates::known map (now Memo::add_candidate; graph_signature stays for one test); find_or_add_producer's budget-parts argument.
  • Interface alignment (b8402c7): push_candidate_if_new (now the doc's Memo::add_candidate); the hand-indexed memo.candidates[group][index] in the planner's memo-choice check (now Memo::candidate); CandidateForMemoGroupId in stages/physical/memo_candidates.rs (moved to candidates.rs).

Kept because unconverted rules still use them: append_reader (rollup), append_producer (top-k, rollup), nodes_within / boundary_ptrs / direct_input_builds (top-k, rules 3 and 8's matching, step 10), same_range_input (rule 9), reuse::same_node / restore_inputs (rollup).

Plan cost, Stage 2 candidates and planning time

UPDATE_SNAPSHOTS=1 left every corpus_snapshots and sketch_plan_snapshots file unchanged. Stage 2 physical candidates (Cn: lines of --run-until stage2) and --run-until stage3 wall time per snapshot input, #1215 (b080c96) vs this PR, release builds on one host:

snapshot Stage 2 candidates, before after seconds, before after
o11y_bench/q02 248 248 0.12 0.12
o11y_bench/q05 498 498 0.24 0.24
o11y_bench/q06 251 251 0.11 0.12
o11y_bench/q11 7 7 0.02 0.02
o11y_bench/q12 2 2 0.01 0.01
o11y_bench/q18 737 737 0.29 0.29
o11y_bench/q19 495 495 0.26 0.26
o11y_bench/q20 4 4 0.02 0.01
o11y_bench/q21 368 368 0.11 0.11
o11y_bench/q22 247 247 0.10 0.10
o11y_bench/q23 247 247 0.11 0.10
o11y_bench/q25 495 495 0.26 0.26
o11y_bench/q26 247 247 0.11 0.11
o11y_bench/q27 4 4 0.01 0.01
sketch/shared-quantiles-1h-mvp 1801 1801 0.90 0.89
sketch/nested-windows-15m-1h-mvp 2281 2281 1.24 1.23
sketch/ratio-of-quantiles-1h-mvp 1802 1802 2.57 2.54
sketch/quantile-single-mvp 201 201 0.08 0.09
sketch/quantile-grouped-mvp 108 108 0.06 0.06
sketch/topk-rate-guarantees-ignored-mvp 87 87 0.05 0.06
sketch/fuzz-issue903-raw-retained 135 135 0.07 0.08
sketch/random-mixed-seed-862-w02-q2-q7-mvp 136 136 0.09 0.09
sketch/random-mixed-seed-862-w01-q3-q8-mvp 226 226 0.11 0.11
sketch/random-mixed-seed-862-long-w01-q3-mvp 72 72 0.09 0.09
total 7.02 6.99

Second review fixes

Folded into the rebased commit (f27b38f):

  • One output-contract check. reuse::same_output_contract replaces three copies: scan_labels::same_output_contract, rule 9's inline check (shared_configuration_readers.rs, which also skipped the result kind), and the body of check_output_contract, which now only wraps it.
  • Rule 9 deduplicates with push_candidate_if_new, keeping the guard against a rewrite that reads its own group. Its own known map of graph signatures is deleted.
  • find_or_add_producer drops accuracy_budget_parts. Every caller passed 1, so the argument changed nothing. A producer group is found or added with 1 part, and the summary_inputs comment now says so.
  • add_candidate_over_groups(memo, owner: MemoGroupId, ...): one id type, was usize.
  • push_candidate_if_new is a crate-private Stage 1 function (operator_over_groups.rs), not a Memo method.
  • Plans unchanged: snapshots and both memo texts byte-identical to b3c7b5a (below).

Notation (formulation → code)

Formulation (#1203, #1201, #1207) Code name in this PR
memo group $G$ MemoGroup, id MemoGroupId (also the owner of an added candidate)
candidate $v = (e_v, p_v)$ added to $G$ Memo::add_candidate (doc), through add_candidate_over_groups
candidate id $(G, \text{index})$ CandidateForMemoGroupId (doc), read with Memo::candidate (doc)
expression $e_v = \operatorname{op}v(G{v,1}, \dots, G_{v,n})$ operator_over_groups (returns the operator over the groups' nodes and $G_{v,1..n}$)
equality of two expressions hash_consing::equal_nodes (Stage 0's equality)
a consumer's output must stay unchanged by a rewrite same_output_contract

Concepts not in the formulation

Interim names with no formulation term, for the user to name or drop:

  • Producer (find_or_add_producer): a group a rule factors out of its consumers, e.g. a summary input or a top-k ranking. In the formulation it is just a memo group.
  • Output contract (same_output_contract, check_output_contract): a rewritten candidate must produce its group's output.
  • Summary result / summary input (origins SummaryResult, SummaryInput): rules 3 and 8. Both go once no rule makes private chains.
  • Accuracy budget parts, added groups, Stage 0 groups: see refactor(stage1): Pass 1 adds a memo group for every intermediate result (#1203 PR 2a-1) #1215.
  • Equal candidate (Memo::add_candidate's dedup): same input groups, fused groups, budget parts and an equal node.

Second review verification (f27b38f)

  • cargo test -p asap-types --lib: 146 passed. cargo test -p asap-logical-asap-optimization --lib: 198 passed.
  • cargo test -p asap-integration-tests --test integration -- memo_enumeration reuse_rules operator_sharing sharing_issue_regressions finer_aggregate_reuse planner_layering_example having_shared_aggregate stage1_logical_asap_optimization joint_search_equivalence sketch_rollup rollup_controlled production_rollup_workloads: 154 passed, 23 ignored (pre-existing).
  • cargo test -p asap-devtools --lib: 18 passed. --test devtools -- asap_planner:: asap_planner_acceptance:: memo_pricing::: 56 passed, 1 ignored.
  • UPDATE_SNAPSHOTS=1 cargo test --release -p asap-devtools --test corpus_snapshots -- --include-ignored: 9 passed, no file changed.
  • KLL and scan-widening --run-until stage1_pass2 --format text: byte-identical to b3c7b5a. Rule 9 runs on both, so its new deduplication adds the same candidates.
  • asap_planner --run-until stage1_pass2 --format text: byte-identical to the pre-review head b3c7b5a on 35 workloads besides the two examples: the reviewer's 20 workloads, six rollup / scan-widening / avg workloads, five earlier finer-aggregate, rollup, top-k and scan-KLL examples, and random-mixed-seed-862 workloads 01–04.
  • cargo fmt --all; cargo clippy --workspace --all-targets -- -D warnings: clean.

One node equality in Pass 1 (2026-11-05)

NodeToStructuralIdCache, Pass 1's own structural identity beside Stage 0's equality, is deleted. Pass 1 adds its candidates through Memo::add_candidate; the exact composition of a group's own node is recognized with hash_consing::equal_nodes once the composed node takes the group's schema and drops its exact guarantee; PerComputationCandidates::walked compares groups with equal_nodes. One behaviour change: two literals that are both NaN are no longer one candidate (equal_nodes compares floats by value); no snapshot has one. Verification: UPDATE_SNAPSHOTS=1 cargo test --release -p asap-devtools --test corpus_snapshots -- --include-ignored: 9 passed, no file changed; cargo test -p asap-logical-asap-optimization --lib: 197 passed (the deleted structural_keys_preserve_nonfinite_and_signed_zero_literals tested the deleted cache); cargo test -p asap-devtools --lib: 18 passed; cargo test -p asap-integration-tests --test integration (12 filters as above): 154 passed, 23 ignored.

Interface alignment and CI fix verification (b8402c7)

  • sketch_rollup::quantiles_rolled_up_keep_the_sketch_bound: the composed KLL now has k = 100, failure_probability: Some(1e-6) (was k = 52, None), printed from the test. 300 local runs of the new test: 0 failures (the old test also passed 300 local runs; its CI failure is a rare draw).
  • cargo test -p asap-types -p asap-logical-asap-optimization -p asap-planner -p asap-physical-asap-optimization --lib: 146, 198, 11, 29 passed.
  • cargo test -p asap-integration-tests --test integration -- memo_enumeration reuse_rules operator_sharing sharing_issue_regressions finer_aggregate_reuse planner_layering_example having_shared_aggregate stage1_logical_asap_optimization joint_search_equivalence sketch_rollup rollup_controlled production_rollup_workloads: 154 passed, 23 ignored (pre-existing), the same count as before.
  • cargo fmt --all -- --check; cargo clippy --workspace --all-targets -- -D warnings: clean.
  • Memo::add_candidate keeps push_candidate_if_new's equality, so the rules add the same candidates; snapshots were not rerun for this commit.

Viewer

Viewer tests are not updated for the group shape (#1203 decision 6); viewer_contract passes.

Verification

  • cargo test -p asap-logical-asap-optimization --lib: 198 passed. state_groups::tests::check_private_inputs now looks for a build group both queries reach over a SummaryInput group among all groups (the build is now a group of the rule, not a SharedBuild group of step 10).
  • cargo test -p asap-physical-asap-optimization: 29 passed.
  • cargo test -p asap-plan-selection --lib: 142 passed.
  • cargo test -p asap-devtools --lib: 18 passed.
  • cargo test -p asap-integration-tests --test integration -- memo_enumeration reuse_rules operator_sharing sharing_issue_regressions finer_aggregate_reuse planner_layering_example having_shared_aggregate: 117 passed, 23 ignored (pre-existing).
  • cargo test -p asap-devtools --test devtools -- asap_planner:: asap_planner_acceptance:: memo_pricing:: viewer_contract::: 58 passed, 1 ignored (pre-existing).
  • UPDATE_SNAPSHOTS=1 cargo test --release -p asap-devtools --test corpus_snapshots -- --include-ignored: 9 passed, no file changed.
  • cargo fmt --all; cargo clippy --workspace --all-targets -- -D warnings: clean.
  • KLL and scan-widening --run-until stage1_pass2 --format text: byte-identical to refactor(stage1): Pass 1 adds a memo group for every intermediate result (#1203 PR 2a-1) #1215's head.

Left for 2a-3..k

@zzylol zzylol added CSE optimizer Workload optimization and plan selection refactor labels Nov 5, 2026
@zzylol
zzylol force-pushed the refactor/1203-2a-2-summary-results-inputs branch from b3c7b5a to ef701a7 Compare November 5, 2026 15:44
zzylol added a commit that referenced this pull request Nov 5, 2026
…s candidates when found

`exact_input_group` replaced the candidates of the group `find_or_add_group`
returned, also when it found an existing group. Now the exact candidates
replace the own candidate only of a group it added; a found group keeps its
candidates and gets the new ones through `push_candidate_if_new`. Top-k and
finer-aggregate reads follow #1218's interface changes (`MemoGroupId` owner,
`find_or_add_producer` without budget parts).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K1M6br9PL4SDeVS1XpezKJ
@zzylol
zzylol force-pushed the refactor/1203-2a-intern-pass1 branch from 9133835 to 8182edf Compare November 5, 2026 16:59
@zzylol
zzylol force-pushed the refactor/1203-2a-2-summary-results-inputs branch from ef701a7 to f27b38f Compare November 5, 2026 17:08
zzylol added a commit that referenced this pull request Nov 5, 2026
…s candidates when found

`exact_input_group` replaced the candidates of the group `find_or_add_group`
returned, also when it found an existing group. Now the exact candidates
replace the own candidate only of a group it added; a found group keeps its
candidates and gets the new ones through `push_candidate_if_new`. Top-k and
finer-aggregate reads follow #1218's interface changes (`MemoGroupId` owner,
`find_or_add_producer` without budget parts).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K1M6br9PL4SDeVS1XpezKJ
@zzylol

zzylol commented Nov 5, 2026

Copy link
Copy Markdown
Contributor Author

CI note: run 42071683865 failed sketch_rollup::quantiles_rolled_up_keep_the_sketch_bound with web: rank 0.9394904458598726 of 43.32363620279617. The bound is 0.99 ± 0.05, so the answer missed by 0.0005.

This is a flaky test, not this PR:

  • asap_sketchlib seeds KLL's compaction coin from the thread RNG (Coin::new → rng().random()), so the rolled-up KLL answer changes from run to run.
  • The test asserts the rank bound for every job, but the target allows a failure probability of 0.01 per answer.
  • The same code passed CI on refactor(stage1): top-k reuse and finer-aggregate reads as one operator over groups (#1203 2a-3) #1220's head, which contains this PR unchanged.
  • It passed 6 of 6 local reruns at f27b38f, and in every full integration run of this series.

I re-ran the failed job. Making the test deterministic (a seeded coin, or a looser per-job check) is CI/test work outside this PR.

@zzylol
zzylol force-pushed the refactor/1203-2a-2-summary-results-inputs branch from e0a775f to b8402c7 Compare November 5, 2026 20:15
zzylol added a commit that referenced this pull request Nov 5, 2026
…s candidates when found

`exact_input_group` replaced the candidates of the group `find_or_add_group`
returned, also when it found an existing group. Now the exact candidates
replace the own candidate only of a group it added; a found group keeps its
candidates and gets the new ones through `push_candidate_if_new`. Top-k and
finer-aggregate reads follow #1218's interface changes (`MemoGroupId` owner,
`find_or_add_producer` without budget parts).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K1M6br9PL4SDeVS1XpezKJ
@zzylol
zzylol force-pushed the refactor/1203-2a-2-summary-results-inputs branch from b8402c7 to 45c8d37 Compare November 5, 2026 21:13
zzylol added a commit that referenced this pull request Nov 5, 2026
…s candidates when found

`exact_input_group` replaced the candidates of the group `find_or_add_group`
returned, also when it found an existing group. Now the exact candidates
replace the own candidate only of a group it added; a found group keeps its
candidates and gets the new ones through `push_candidate_if_new`. Top-k and
finer-aggregate reads follow #1218's interface changes (`MemoGroupId` owner,
`find_or_add_producer` without budget parts).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K1M6br9PL4SDeVS1XpezKJ
@zzylol zzylol changed the title refactor(stage1): summary readouts and summary inputs as one operator over groups (#1203 2a-2) refactor(stage1): summary readouts and inputs as one operator over groups; Memo::add_candidate; one node equality in Pass 1 (#1203 2a-2) Nov 5, 2026
zzylol and others added 4 commits November 5, 2026 21:24
… over groups

Rules 3 (summary inputs) and 8 (summary results) add their candidates as
one operator over groups (`add_candidate_over_groups`, through the shared
`operator_over_groups` Pass 1 now uses too), so every node beneath them is
a memo group. Summary inputs find or add their canonical inputs through
`find_or_add_producer` (Stage 0's equality), which replaces the TimeRange
rebinding. Equal candidates are found by `push_candidate_if_new`
(hash-consing equality), which rule 9 now uses too, in place of its own
graph-signature check. `boundary_groups` looks groups up with
`Memo::group_whose_node_is`.

Second review fixes, folded in on the rebase onto #1215's fixes: one
`same_output_contract` in reuse.rs (scan widening, rule 9 and
`check_output_contract` use it); `add_candidate_over_groups` takes a
`MemoGroupId` owner; `find_or_add_producer` drops its always-1
budget-parts argument; `push_candidate_if_new` is a crate-private Stage 1
function, not a `Memo` method.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K1M6br9PL4SDeVS1XpezKJ
…he test can rely on

quantiles_rolled_up_keep_the_sketch_bound checks eight randomized KLL
answers against their rank bound. Composed alone, the KLL is sized for the
calibrated failure probability 0.01 (k = 52), so an answer may miss the
bound (CI: rank 0.9395 for p99, error 0.0505 > 0.05). The test now sizes
each candidate for its target's failure probability as Pass 1 does, with
the target at 1e-6 (k = 100).
…h_candidate_if_new

The concepts doc (section 3) defines Memo::add_candidate, Memo::candidate
and CandidateForMemoGroupId in candidates.rs. add_candidate takes
push_candidate_if_new's place: it returns the id of the candidate, the
equal one already in the group when there is one. CandidateForMemoGroupId
moves from the physical boundary types to candidates.rs; the planner's
memo-choice check reads a candidate through Memo::candidate.
hash_consing::equal_nodes is now private to the types crate.
NodeToStructuralIdCache, Pass 1's own structural identity next to
Stage 0's equality, is deleted. Pass 1 adds its candidates through
Memo::add_candidate, and recognizes the exact composition of a group's
own node with hash_consing::equal_nodes after the composed node takes the
group's schema and loses its exact guarantee. PerComputationCandidates::walked
compares groups with equal_nodes. Snapshots unchanged.
@zzylol
zzylol force-pushed the refactor/1203-2a-2-summary-results-inputs branch from 45c8d37 to 80bec5a Compare November 5, 2026 21:24
zzylol added a commit that referenced this pull request Nov 5, 2026
…s candidates when found

`exact_input_group` replaced the candidates of the group `find_or_add_group`
returned, also when it found an existing group. Now the exact candidates
replace the own candidate only of a group it added; a found group keeps its
candidates and gets the new ones through `push_candidate_if_new`. Top-k and
finer-aggregate reads follow #1218's interface changes (`MemoGroupId` owner,
`find_or_add_producer` without budget parts).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K1M6br9PL4SDeVS1XpezKJ
@zzylol

zzylol commented Nov 6, 2026

Copy link
Copy Markdown
Contributor Author

Closed: replaced by #1207's new order (interfaces first, then one PR per stage), starting with PR 0 #1226. The code on this branch is reused in PRs 1–8; the branch is kept.

@zzylol zzylol closed this Nov 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CSE optimizer Workload optimization and plan selection refactor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant