Skip to content

refactor(stage3): constraint (K), accuracy at each query root (#1207 PR 8) - #1238

Draft
zzylol wants to merge 8 commits into
refactor/1207-contextsfrom
refactor/1207-accuracy
Draft

zzylol wants to merge 8 commits into
refactor/1207-contextsfrom
refactor/1207-accuracy

Conversation

@zzylol

@zzylol zzylol commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

Stacked on #1237 (PR 7, execution contexts). Part of #1207 (PR 8 of the order), #1204 item 3, #1203. Draft; not to be merged on its own (one merge unit, PRs 0–9).

Why

Until this PR nothing checked a query's composed accuracy: Stage 3 selected the cheapest sizes the edges' parameter floors allowed, and a floor is only a pruning cap (Pass 2 takes the largest cap over the queries, and a target whose metric the family does not state leaves the input unbounded). PR 6 showed the result (handoff H1): quantile_over_time(0.99, latency_ms[60m]) with a rank target of 0.01 selected six DDSketch windows at alpha 0.1, a relative error of 10 % that says nothing about rank. The concepts doc §4 and #1204 item 3 define constraint (K): per-candidate error bounds $E_c$, $D_c$ composed over the selected edges with big-M, checked at each query root.

What

  • Constraint (K) in the selection MILP: $E_{\kappa,c,m}$ per execution and error metric, $D_{\kappa,c}$, $F$/$H$ per input, big-M only on selected edges ($y_{\kappa,u,c,i}$), root checks $E_v \le \varepsilon_q$, $D_v \le \delta_q / n$ when $z_{q,v} = 1$.
  • The metric rule on the selected path: an input error in a metric the consumer's contract does not carry must be zero on a selected edge, and a root accepts only the metrics its target accepts (a rank target rejects a DDSketch readout; a relative-error ratio rejects a KLL operand). Validity caps $b_{c,i}$ (a ratio's denominator) are enforced on selected edges.
  • Own guarantees $\varepsilon^{\mathrm{loc}}$, $\delta^{\mathrm{loc}}$ from each candidate's $\boldsymbol\theta$ (asap_accuracy::estimators::local_guarantee), with the conditions PR 4 removed with node-tree propagation brought back: a DDSketch interior quantile and a Count-Min count hold only over values proven non-negative; a windowed or merged state keeps a guarantee only when its family's merged error does; a distinct count over a source's own rows takes its failure probability at the source's declared distinct bound (classic HLL, distinct-count UnivMon: the PR 4 dead code, now used).
  • Root check after the solve: each query's composed guarantee in the selected DAG is checked again with the deployment's accuracy model and evidence; an output none accepts is ErrorSelectingPlan::SelectedPlanFailedCheck (the PR 0 variant, now constructed).
  • Accuracy policy: (K) applies to a query unless the deployment's model accepts its output regardless of its guarantee (IgnoreAccuracyGuarantees), asked on the root the front ends lowered (AccuracyCheckInputs::query_roots, new).
  • Every todo!("#1207 PR 8 …") is implemented (accuracy_constraints); the dead_code allowances for PR 8 are gone (AccuracyVariables, accuracy_constraints, HLL classic_guarantee, UnivMon distinct_guarantee, incomplete_top_layer_statistic).

Not here: tools and snapshots (PR 9); latency (L) and resource budgets (B) are out of scope (#1204 items 5, 6).

Representation, rules, enumeration (#1207 (a)/(b)/(c))

  • (a) Representation: no memo change. MILP variables for accuracy: $E_{\kappa,c,m}$, $F_{\kappa,c,i,m}$, $D_{\kappa,c}$, $H_{\kappa,c,i}$, and $U_{\kappa,c}$ (part of the output without a guarantee).
  • (b) Rules: none.
  • (c) Enumeration: the MILP's feasible set shrinks to plans meeting (K). The reference enumerator (Appendix A) now also filters DAGs by each root's composed error (CandidateErrorModel::composed_errors) and agrees with the MILP on Tracking: implement the optimization formulation (memo candidates, properties, edges y_{u,c,i}, MILP) as named code per stage #1207's problem example under 12 cost/target sets (targets that admit a plan and targets that admit none).

Notation → code

Notation Code
$\varepsilon^{\mathrm{loc}}_c$, $\delta^{\mathrm{loc}}_c$ OwnGuarantee::{error_bound, failure_probability} (per metric), from CandidateErrorModel::new (own_guarantee, read_guarantee)
$\beta_{c,i}$, $b_{c,i}$ CandidateErrorModel::{factor, validity_cap} (from LogicalRequirements::error_propagation, per metric)
$M_{c,i}$, $M_q$ CandidateErrorModel::{error_bound_ceiling, failure_probability_ceiling} (the producer's / output's ceiling)
$E_{\kappa,c}$, $F_{\kappa,c,i}$, $D_{\kappa,c}$, $H_{\kappa,c,i}$ AccuracyVariables::{error_bound, error_bound_of_input, failure_probability, failure_probability_of_input} (keys: Execution, input, metric index)
(K) accuracy_constraints (model.rs)
$\varepsilon_q$, $\delta_q$ at the root RootAccuracyLimits (root_accuracy_limits, per query; None where (K) does not apply)
$G_q$ (values a whole-result δ covers) values_per_result of AccuracyCheckInputs::query_roots[q]
metric rule accuracy_constraints: $E_{u,m} \le M(1-y)$ when $c$'s contract lacks $m$; RootAccuracyLimits::error_bound_limit[m] = 0 for a metric the target does not accept

How

  1. select_plan builds CandidateErrorModel::new(physical, data): per physical candidate, its own guarantee, its error contracts in the five metrics, and ceilings (rounds of $\bar E_{c,m} = \varepsilon^{\mathrm{loc}}{c,m} + \sum_i \beta{c,i,m} \max_{u \in P_{c,i}} \bar E_{u,m}$ over admissible edges until stable, at most one round per candidate; capped at $10^6$).
  2. root_accuracy_limits(entries, query_roots, data, model): per query, None when the model accepts its output regardless of its guarantee; otherwise $\varepsilon_q$ in the metrics its target accepts, $\delta_q / n$ with $n$ = max_values_covered(scope, G_q). A scalar root that combines outputs (not scalar(v)) requires exact references.
  3. solve_selection: when some query has limits, accuracy_variables and accuracy_constraints add (K) per execution and $y_{\kappa,u,c,i}$; a constraint whose big-M side holds at its ceiling is left out.
  4. After realization, accepted_accuracy composes each query's error over the selected DAG (composed_errors, no big-M), states it over the target's scope, and checks it with the model, then the evidence, then the accept-regardless rule.

Major algorithm changes

Stage 3: constraint (K)

  • Problem. Sizes were constrained only by per-edge floors. quantile_over_time(0.99, latency_ms[60m]), rank 0.01, selected DDSketch alpha 0.1 windows (relative error, not rank); the ratio p99/p50 at relative 0.03 could take alpha 0.02 for both operands (each within its floor, composed $(0.02+0.02)/0.9 = 0.044 > 0.03$).
  • Before / after. Before: (A)–(I), (R); accuracy recorded, never checked. After: How 1–4.
  • Correctness. Soundness: for a selected acyclic DAG, $E$ at each selected execution is at least $\varepsilon^{\mathrm{loc}}$ plus $\beta$ times its selected producers' $E$ (the big-M is slack exactly on unselected edges since $M \ge$ the producer's largest $E$); the root check bounds it by the target; the post-check recomputes it without big-M and rejects anything over the target. Conservative only (never accepts a plan missing its target): a producer reached by two paths and its failure event count twice; a state's own guarantee is the largest of the statistics read from its group. Tests: the_problem_example_meets_each_query_target_at_its_root, a_ddsketch_interior_quantile_needs_non_negative_values, failure_probabilities_add_along_the_selected_edges, a_producer_s_error_counts_only_on_its_selected_edge, the_milp_agrees_with_the_reference_enumeration_under_accuracy, end to end each_query_meets_its_target_at_its_root.
  • Complexity. Variables: $\le 5$ $E$ per execution that may carry an error, one $D$, one $U$; $F$/$H$ per (execution, input) with an approximate producer. Constraints: $O(5 \cdot |y|)$ plus one root check per (output, metric). Measured (After, below): 117/181 vs 78/110 variables/constraints for the p99, 583/958 vs 462/579 for Tracking: implement the optimization formulation (memo candidates, properties, edges y_{u,c,i}, MILP) as named code per stage #1207's example; solve 21–160 ms. Ceilings: $O(|\mathcal C| \cdot |\mathcal E| \cdot 5)$ worst case, a few rounds in practice.
  • Assumptions and limits. Cost model 1 only (as PR 6/7). The MILP encodes the built-in target rule (metric_can_meet, no cross-metric conversion); a model with conversions is only consulted after the solve. Evidence (output_guarantee) cannot make (K) feasible. Latency (L) and budgets (B) out of scope.

Deleted (replaced in this PR)

Deleted Replaced by
todo!("#1207 PR 8: constraint (K)") and the dead_code allowances on AccuracyVariables, accuracy_constraints implemented
#[allow(dead_code)] on HLL classic_guarantee, UnivMon distinct_guarantee, incomplete_top_layer_statistic read by distinct_count_guarantee_over_source
accepted_accuracy that accepted every sketch output in its target's metric without a check accepted_accuracy over the composed guarantee (model, evidence, accept-regardless), composed_guarantees
AccuracyVariables keyed by PhysicalCandidateId (Vec) keyed by execution and metric (HashMap)
accuracy_constraints(physical, selection, accuracy) accuracy_constraints(inputs, selection, variables, accuracy)

ErrorSelectingPlan::SelectedPlanFailedCheck (PR 0) is now constructed; its doc no longer mentions budget shares.

New abstractions not in #1207 or the doc (pending approval)

  • AccuracyCheckInputs::query_roots: &[QueryRoot] (pub; PlanningModels::accuracy_check_inputs now takes the roots): Stage 3 reads $G_q$ (values_per_result) and asks the model whether it accepts the query's output regardless of its guarantee.
  • asap_accuracy::estimators::distinct_count_guarantee_over_source(parameters, source, max_distinct) (pub): classic HLL / distinct-count UnivMon guarantee with δ bound at the source's declared distinct bound.
  • Stage 3 private (milp/error_bounds.rs): CandidateErrorModel (own guarantee, contracts per metric, ceilings, composed_errors), OwnGuarantee, RootAccuracyLimits (+ EXACT), root_accuracy_limits, returns_its_output_unchanged, METRICS/METRIC_COUNT; private helpers own_guarantee, statistics_read, read_guarantee, proven_non_negative, raw_data_samples_of_metric, source_distinct_bound, state_family.
  • AccuracyVariables beyond the doc's $E_c, F_{c,i}, D_c, H_{c,i}$: a metric index on $E$/$F$ (metric rule on selected paths) and $U_{\kappa,c}$ (without_guarantee, part of the output without a guarantee), plus per-execution keys (doc §4: $E_{\kappa,c}$ under contexts).
  • accuracy_variables (creates the family; PR 0 had only the struct and accuracy_constraints).
  • Rules Stage 3 states: (K) applies to a query unless the model accepts its output regardless of its guarantee, asked on the root the front ends lowered; a scalar root other than scalar(v) / a scalar subquery requires exact references; unknown δ counts as 1.

Decisions taken conservatively (please confirm)

  1. IgnoreAccuracyGuarantees (PR 4 question). Ignore mode turns off only (K) (root limits None for every query). Pass 2's $\alpha^{\log}$ stays: parameter floors from the query targets, heap-size floors (heap ≥ k, a correctness need, not accuracy) and the metric rule's dropped edges ($\alpha^{\log}=0$) still apply, since Pass 2 and Stage 2 do not know the policy. So in ignore mode a plan meets every per-edge floor and only the composed root check is skipped.
  2. Accept-regardless per query, before the solve is asked on the root the front ends lowered (the realized output does not exist yet); after the solve it is asked again on the realized output, as before.
  3. DDSketch interior quantile / Count-Min (the PR 4 handoff's sign condition): a read has a guarantee only when the update's weight domain, confirmed by the data workload, is non-negative, or its raw samples' metric is declared non-negative (counter or values_non_negative); otherwise no guarantee ($U = 1$).
  4. A state's own guarantee is the largest error per metric (and largest δ) over every statistic read from its group, directly or through merges — conservative when one state serves reads with different bounds (e.g. a DDSketch read at the maximum and at an interior quantile without a sign proof: both lose the guarantee).

Spec questions

  1. Whole-result failure probabilities are unreachable with the lists (PR 1). Every listed θ fails with probability 0.01 per value, so a whole-result target δ = 0.01 over 1,000 series allows $10^{-5}$ per value: no KLL/CMS meets it. Before Tracking: implement the optimization formulation (memo candidates, properties, edges y_{u,c,i}, MILP) as named code per stage #1207, Pass 1 sized δ per value (with_failure_probability). Options: (a) add δ variants to the lists (KLL failure_probability, CMS depth); (b) keep as is (such queries are exact or infeasible). 4 planner tests now fail for this (below); I did not change their workloads.
  2. Readout error placement. The doc puts $\varepsilon^{\mathrm{loc}}$ on the candidate; a readout has no θ, so the error of reading a statistic is on the state candidate (its θ) as the largest over the statistics read from its group. Alternative: a per-(state, statistic) variable family (exact, larger).
  3. Error metrics in $E$. The doc's $E_c$ is one number; a value-preserving consumer (projection, Concat) can carry a rank error from one producer and a relative error from another, so $E$ is per metric here. OK?
  4. Evidence (AccuracyEvidenceProvider::output_guarantee) is per candidate output and cannot enter the MILP; it is read only in the post-solve check, so evidence can no longer make an output without a theoretical guarantee selectable. Acceptable, or should the accept-regardless pre-check also consult evidence on the lowered root?
  5. Sizes tie in the objective. Above the floor, sizes differ in cost only through memory held during an evaluation, which the objective leaves out (PR 7 spec question 2): e.g. p99+p50 selects KLL k 1404 and Tracking: implement the optimization formulation (memo candidates, properties, edges y_{u,c,i}, MILP) as named code per stage #1207's example DDSketch alpha 0.002 where k 269 / alpha 0.01 meet the targets at the same objective.
  6. Mixed workloads. When (K) applies to some queries, the metric-rule and validity-cap constraints apply on every selected edge, including paths serving only ignored queries.

Lower-PR items (not changed)

  • RejectionCause::CountMinWeightsNotProvenNonNegative is never constructed: PR 6 deleted the executability check (nodes_with_proven_non_negative_weights); a Count-Min over unproven weights is now only "no guarantee" under (K), and in ignore mode it can be selected and the executor refuses it (PR 5/6).
  • RejectionCause::{LatencyBoundExceeded, LatencyOverRepeatInterval, OverClusterCapacity} never constructed ((L), (B) out of scope; PR 6).
  • raw_data_samples_of_metric is duplicated: Pass 1's (realization.rs) is crate-private; Stage 3 needs the same walk and cannot depend on Pass 1. Moving it to asap-types changes PR 3.
  • docs/design_docs/proposals/planner-layering.md lines ~277, 313, 372–421 (memo concepts table and the ratio-of-quantiles example) still describe root_node, input_groups, accuracy_budget_parts and pre-Tracking: implement the optimization formulation (memo candidates, properties, edges y_{u,c,i}, MILP) as named code per stage #1207 counts (PR 0/PR 3 doc; numbers come from PR 9's re-baselined stage_pipeline). crates/devtools/src/bin/asap_planner.rs:90 comment names accuracy_budget_parts (PR 9 tool).
  • PR 4: local_guarantee gives a Count-Min TopK read a frequency bound (old code had none for top-k membership); unchanged.

Verification

In progress: independent review and the test table follow in the next body update.

…ailure probability at the source's distinct bound (#1207 PR 8)

distinct_count_guarantee_over_source reads classic HLL's and a distinct-count
UnivMon's guarantees (kept dead since PR 4 for this) and binds their failure
probability at SourceFacts::max_distinct_per_evaluation, for Stage 3's (K).
CandidateErrorModel (milp/error_bounds.rs): each physical candidate's own
guarantee (a sketch state's: the largest error per metric and failure
probability of the statistics read from its group; none for a read whose
bound needs values not proven non-negative, a windowed or merged state that
does not keep the guarantee, or a family without an error model), its error
contract per metric (error_propagation), and ceilings of E and D used as M.
AccuracyVariables E/F per execution and metric, D/H, U (no guarantee);
accuracy_constraints: E >= eps_loc + sum beta F, F >= E_u - M(1-y), the
metric rule and validity caps on selected edges, D by a union bound, and the
root checks E_v <= eps_q, D_v <= delta_q / n, U_v = 0 when z = 1, for every
query the accuracy model does not accept regardless of its guarantee.
After the solve, each query's composed guarantee is checked with the model
and evidence; an output none accepts is SelectedPlanFailedCheck.
AccuracyCheckInputs carries the front ends' query roots (G_q and the
accept-regardless policy). Tests: #1207's problem example, the DDSketch sign
condition, failure probabilities adding, big-M only on selected edges, and
the reference enumeration under (K).
…ee per metric; an exact target's limits are exact (#1207 PR 8)
…ignore mode; a whole-result failure probability over 1,000 series (#1207 PR 8)
@zzylol zzylol added accuracy Accuracy requirements, guarantees, and propagation optimizer Workload optimization and plan selection refactor labels Nov 7, 2026
…its edge; (K) applies unless the model or evidence may accept outputs regardless; one G per query for (K) and the check; no ceiling cap; tighter HiGHS tolerances under (K) (#1207 PR 8)

- The error of reading a state is a constant per edge (u, c) for the
  consumer's statistic(s) on the producer's theta (E_c >= sum eps_uc y + ...),
  so one query's read no longer changes another's (a DDSketch read at the
  maximum keeps its guarantee when a p50 read of the same state has none).
- root_accuracy_limits follows the AccuracyModel/AccuracyEvidenceProvider
  contract (may_accept_regardless_of_guarantee, may_provide_evidence): (K)
  applies to every query or none; the per-query pre-ASAP accept-regardless
  call is gone.
- values_per_result is computed once from the lowered roots and used by
  both (K) and the check after the solve; a short query_roots slice is an
  EntryCount error.
- Ceilings stop after one round per execution (a valid M for every acyclic
  selection) with no cap, which could make unrelated plans infeasible.
- OwnGuarantee renamed ErrorBounds (read errors and composed errors).
- Tests: the regression test above; the DDSketch sign test checks the
  declared case too; the big-M test's description matches it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

accuracy Accuracy requirements, guarantees, and propagation optimizer Workload optimization and plan selection refactor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant