From b517fbf256a17621d48fa3f8508c5d5ca1017bad Mon Sep 17 00:00:00 2001 From: zzylol <50204836+zzylol@users.noreply.github.com> Date: Mon, 5 Oct 2026 13:11:05 +0000 Subject: [PATCH] Remove the unread accuracy-evidence API After the legacy search was removed, nothing reads planning-time accuracy evidence. Delete AccuracyEvidenceProvider, NoAccuracyEvidence, WorkloadAccuracyEvidence, EstimatorContract, QuantileInputDomain, PlanningModels.evidence / with_evidence, and ClassicHllConfidence, which only an EstimatorContract could reach. PropagationStats keeps the two Hydra shared-grid fields hydra_guarantee reads and moves next to it. The generic HLL estimator Stage 3 uses is unchanged. Co-Authored-By: Claude Opus 5.5 --- .../src/accuracy/estimators/hll.rs | 200 +----------------- .../src/accuracy/estimators/mod.rs | 13 +- .../src/accuracy/evidence.rs | 175 --------------- crates/logical-optimizer/src/accuracy/mod.rs | 12 +- crates/logical-optimizer/src/lib.rs | 3 - crates/plan-selection/src/lib.rs | 15 +- .../architecture/input-output-workflow.md | 2 +- docs/design_docs/concepts/accuracy-models.md | 30 +-- .../end-to-end-accuracy-guarantees.md | 12 +- docs/develop_docs/library-api.md | 1 - 10 files changed, 30 insertions(+), 433 deletions(-) delete mode 100644 crates/logical-optimizer/src/accuracy/evidence.rs diff --git a/crates/logical-optimizer/src/accuracy/estimators/hll.rs b/crates/logical-optimizer/src/accuracy/estimators/hll.rs index db57063a..ee7730b2 100644 --- a/crates/logical-optimizer/src/accuracy/estimators/hll.rs +++ b/crates/logical-optimizer/src/accuracy/estimators/hll.rs @@ -1,8 +1,5 @@ -//! Estimator-specific confidence for classic HLL's linear-counting branch. -//! -//! This is conditional on independent uniform bucket hashes and an enforced -//! upper bound on distinct items in the complete evaluation population (including -//! all merged panes). It is not an RSE-to-normal conversion or an ERP fit. +//! HLL's relative standard error. Generic HLL has no modeled confidence: +//! its failure probability stays unknown. use super::*; @@ -26,96 +23,6 @@ pub(super) fn generic_guarantee( "generic_hll_rse_only_no_confidence_v1", )) } -/// A finite-population contract for `m * ln(m / zero_registers)` with the -/// classic HLL small-range switch. Hashing is assumed independent and uniform. -/// The deployment must establish the population bound; observations alone do -/// not establish it. Unsupported precisions/populations return no certificate. -#[derive(Debug, Clone, Copy)] -pub struct ClassicHllConfidence { - max_distinct: u32, - relative_error: f64, -} - -impl ClassicHllConfidence { - pub fn new(max_distinct: u32, relative_error: f64) -> Option { - (max_distinct > 0 - && max_distinct <= 4096 - && relative_error.is_finite() - && (1e-6..1.0).contains(&relative_error)) - .then_some(Self { - max_distinct, - relative_error, - }) - } - - pub fn guarantee(&self, precision: u8) -> Option { - let delta = self.failure_probability(precision)?; - Some(ResultGuarantee { - metric: ErrorMetric::Cardinality, - bound: BoundExpr::Constant { - value: self.relative_error, - }, - failure_probability: ProbabilityExpr::Constant { value: delta }, - provenance: vec![GuaranteeSource::SketchEvaluation { - algorithm: "Hll".into(), - contract: "classic_hll_linear_counting_collision_bound_v1".into(), - params: serde_json::json!({"precision": precision, - "max_distinct": self.max_distinct, "relative_error": self.relative_error, - "hash_assumption": "independent_uniform_buckets", - "population_scope": "complete_evaluation_including_merged_panes"}), - query: "Cardinality".into(), - }], - }) - } - - pub fn precision(&self, delta: f64) -> Option { - if !delta.is_finite() || !(0.0..1.0).contains(&delta) || delta == 0.0 { - return None; - } - (4..=18).find(|&p| self.failure_probability(p).is_some_and(|d| d <= delta)) - } - - /// Finite bound, not an asymptotic RSE fit. With N distinct hashes and K - /// occupied buckets, C=N-K collision arrivals satisfy - /// P(C>=t) <= lambda^t/t!, lambda=N(N-1)/(2m): each arrival's conditional - /// collision probability is at most (i-1)/m, and a union bound over t - /// arrivals is bounded by the t-th power of their sum divided by t!. - /// - /// N<=m/2 makes the classic raw estimate <=2*alpha_m*m<2.5m, - /// so the small-range switch always uses L=-m*ln(1-K/m). Then - /// K<=L<=N + N^2/(2(m-N)). The latter bounds overestimation - /// deterministically; underestimation implies C>epsilon*N. - /// We maximize the collision bound over EVERY integer N in the contract, - /// not just its upper endpoint (small-cardinality tails matter). - fn failure_probability(&self, precision: u8) -> Option { - if !(4..=18).contains(&precision) { - return None; - } - let m = f64::from(1u32 << precision); - let max_n = f64::from(self.max_distinct); - // Reserve numerical slack; do not certify sub-floating-point error. - let eps = self.relative_error - 1e-8; - if max_n > m / 2.0 || max_n / (2.0 * (m - max_n)) > eps { - return None; - } - let mut log_factorial = vec![0.0; self.max_distinct as usize + 1]; - for i in 1..log_factorial.len() { - log_factorial[i] = log_factorial[i - 1] + (i as f64).ln(); - } - let mut worst = 0.0_f64; - for n in 2..=self.max_distinct { - let nf = f64::from(n); - // Including a boundary collision event is conservative. - let t = ((eps * nf).floor() as usize + 1).min(n as usize); - let lambda = nf * (nf - 1.0) / (2.0 * m); - let log_tail = (t as f64) * lambda.ln() - log_factorial[t]; - worst = worst.max(log_tail.min(0.0).exp()); - } - // Never return a spurious zero from underflow or numeric cancellation. - Some((worst * (1.0 + 1e-10) + 1e-12).min(1.0)) - } -} - /// HLL RSE-magnitude inversion. Generic HLL has no modeled confidence target. pub(crate) fn hll_precision(eps: f64) -> u8 { saturating_ceil((1.04 / eps).powi(2).log2(), 4, 18) as u8 @@ -125,109 +32,6 @@ pub(crate) fn hll_precision(eps: f64) -> u8 { mod tests { use super::*; - /// A supported estimator contract supplies a probability, unlike generic HLL RSE. - #[test] - fn bounded_classic_hll_has_a_feasible_confidence_target() { - let model = ClassicHllConfidence::new(128, 0.05).unwrap(); - let precision = model.precision(0.01).expect("finite confidence-sized HLL"); - let guarantee = model.guarantee(precision).unwrap(); - assert!(!guarantee.has_unknown()); - assert!(guarantee.failure_probability.evaluate().unwrap() <= 0.01); - assert_eq!(guarantee.bound.evaluate(), Some(0.05)); - } - /// Tighter confidence must increase precision or explicitly become unavailable. - #[test] - fn sizing_and_domain_limits_are_consistent() { - let model = ClassicHllConfidence::new(128, 0.05).unwrap(); - assert!(model.precision(0.001).unwrap() > model.precision(0.01).unwrap()); - assert!(model.precision(1e-12).is_none()); - assert!(model.precision(0.0).is_none()); - assert!(model.precision(f64::NAN).is_none()); - assert!(model.guarantee(3).is_none()); - assert!(model.guarantee(19).is_none()); - assert!(model.guarantee(7).is_none()); - for (n, e) in [(0, 0.05), (4097, 0.05), (128, 0.0), (128, f64::NAN)] { - assert!(ClassicHllConfidence::new(n, e).is_none()); - } - } - - /// Exact occupancy probabilities independently check both tails for every N. - #[test] - fn probability_bound_dominates_exact_occupancy_distribution() { - for precision in 4..=10 { - let m = 1usize << precision; - let max_n = 64.min(m / 2); - for eps in [0.05, 0.2, 0.6] { - let model = ClassicHllConfidence::new(max_n as u32, eps).unwrap(); - let Some(bound) = model.failure_probability(precision) else { - continue; - }; - let mut occupancy = vec![0.0; max_n + 1]; - occupancy[0] = 1.0; - for n in 1..=max_n { - let mut next = vec![0.0; max_n + 1]; - for k in 0..n { - next[k] += occupancy[k] * k as f64 / m as f64; - next[k + 1] += occupancy[k] * (m - k) as f64 / m as f64; - } - occupancy = next; - let actual: f64 = occupancy - .iter() - .enumerate() - .filter_map(|(k, &prob)| { - let estimate = -(m as f64) * (-(k as f64) / (m as f64)).ln_1p(); - ((estimate - n as f64).abs() > eps * n as f64).then_some(prob) - }) - .sum(); - assert!( - actual <= bound + 1e-12, - "p={precision} n={n} eps={eps}: {actual}>{bound}" - ); - } - } - } - } - /// The model's evaluation formula matches the actual classic estimator after merge. - #[test] - fn native_classic_estimator_and_merged_registers_use_the_same_contract() { - use asap_sketchlib::sketches::hll::{Classic, HyperLogLogP16}; - let model = ClassicHllConfidence::new(128, 0.05).unwrap(); - assert!( - model - .guarantee(16) - .unwrap() - .failure_probability - .evaluate() - .unwrap() - < 0.01 - ); - let mut single = HyperLogLogP16::::new(); - let mut left = HyperLogLogP16::::new(); - let mut right = HyperLogLogP16::::new(); - for n in 0..128u64 { - // SplitMix64 supplies deterministic test hashes, not a proof of randomness. - let mut h = n.wrapping_add(0x9e3779b97f4a7c15); - h = (h ^ (h >> 30)).wrapping_mul(0xbf58476d1ce4e5b9); - h = (h ^ (h >> 27)).wrapping_mul(0x94d049bb133111eb); - h ^= h >> 31; - single.insert_with_hash(h); - if n % 2 == 0 { - left.insert_with_hash(h); - } else { - right.insert_with_hash(h); - } - } - left.merge(&right); - assert_eq!(single.registers_as_slice(), left.registers_as_slice()); - let zeroes = left - .registers_as_slice() - .iter() - .filter(|&&r| r == 0) - .count(); - let expected = (65536.0 * (65536.0 / zeroes as f64).ln()) as usize; - assert_eq!(left.estimate(), expected); - assert!((expected as f64 - 128.0).abs() / 128.0 <= 0.05); - } #[test] fn generic_rse_sizing_does_not_certify_confidence() { use crate::pass1::realization::default_size_params; diff --git a/crates/logical-optimizer/src/accuracy/estimators/mod.rs b/crates/logical-optimizer/src/accuracy/estimators/mod.rs index 1afe89f4..b8cc48f2 100644 --- a/crates/logical-optimizer/src/accuracy/estimators/mod.rs +++ b/crates/logical-optimizer/src/accuracy/estimators/mod.rs @@ -89,12 +89,11 @@ pub fn local_guarantee(family: &FieldDataType, query: &SketchStatistic) -> Optio // it with probability e^-shared_rows. N is the whole input's // weight, not one group's, so this bounds error relative to it. let inner = sketch_guarantee(kind.algorithm(), kind.params(), query)?; - let stats = crate::accuracy::PropagationStats { + let stats = PropagationStats { hydra_shared_grid_collision_bound: Some( std::f64::consts::E / f64::from(*shared_columns), ), hydra_shared_grid_failure_probability: Some((-f64::from(*shared_rows)).exp()), - ..Default::default() }; Some(hydra_guarantee(&inner, &stats)) } @@ -177,6 +176,15 @@ pub(crate) fn saturating_ceil(x: f64, lo: u32, hi: u32) -> u32 { } (x.ceil() as u32).clamp(lo, hi) } +/// Hydra's shared-grid statistics; a missing one stays a symbolic leaf. +#[derive(Debug, Clone, Default, PartialEq)] +pub(crate) struct PropagationStats { + /// Hydra shared-grid collision error in the inner guarantee's metric. + pub hydra_shared_grid_collision_bound: Option, + /// Failure probability assigned to the Hydra shared-grid term. + pub hydra_shared_grid_failure_probability: Option, +} + /// Compose the inner per-subpopulation guarantee with Hydra's outer shared /// grid. The paper's collision term depends on deployment/data statistics; /// keeping those leaves symbolic makes the formula explicit while ensuring @@ -186,7 +194,6 @@ pub(crate) fn hydra_guarantee( stats: &PropagationStats, ) -> ResultGuarantee { let mut provenance = inner.provenance.clone(); - provenance.extend(stats.evidence_provenance.clone()); provenance.push(GuaranteeSource::ChildGuarantee { input_index: 0, guarantee: Box::new(inner.clone()), diff --git a/crates/logical-optimizer/src/accuracy/evidence.rs b/crates/logical-optimizer/src/accuracy/evidence.rs deleted file mode 100644 index 84365ac7..00000000 --- a/crates/logical-optimizer/src/accuracy/evidence.rs +++ /dev/null @@ -1,175 +0,0 @@ -//! Scoped source contracts and evidence required by accuracy rules. -use super::*; - -/// A trusted source assertion scoped by `AccuracyEvidenceProvider` to one -/// complete evaluation. Choosing this variant asserts the estimator and hash -/// assumptions; it must not be inferred from sampled population statistics. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum EstimatorContract { - /// Classic HLL with independent uniform bucket hashing, including merged panes. - ClassicHll { max_distinct_per_evaluation: u32 }, -} - -/// An enforced domain for every sample of a direct quantile operand, in every -/// evaluation window. The provider promises a nonempty population containing -/// only finite values in this interval. Sampled min/max statistics are not a -/// proof: the contract must be enforced by the source or execution layer. -#[derive(Debug, Clone, PartialEq)] -pub struct QuantileInputDomain { - pub lower: f64, - pub upper: f64, - /// Upper bound on samples per evaluation, matching the pinned evaluation's - /// exact Float64 rank limit. The population must also be nonempty. - pub max_samples: u64, - pub contract: String, -} - -/// Statistics a propagation rule may consult. Every field is optional and -/// defaults to "unknown": a rule that needs a missing statistic emits a -/// [`BoundExpr::Unknown`] leaf (or rejects) rather than guessing. -#[derive(Debug, Clone, Default, PartialEq)] -pub struct PropagationStats { - /// Certified finite ranges for the true numerator and denominator values. - /// Quantile ratios obtain these from their enforced input domains. - pub division_operand_domains: Option<[QuantileInputDomain; 2]>, - /// Provenance for supplied evidence (source, observation identity, etc.). - pub evidence_provenance: Vec, - /// Whether every input value is known to be non-negative — required by - /// the multiplicative relative-error rule, which is unsound across a - /// sign change. - pub values_non_negative: Option, - /// Number of input rows an exact aggregation consumes (e.g. the number - /// of groups a function folds), for exact aggregate union bounds - /// bound over per-input failures. - pub input_row_count: Option, - /// Fresh key-frequency distribution evidence from the data workload. - /// Built-in rules preserve it for deployment-specific accuracy models; - /// they do not assume a favorable distribution when it is absent. - pub data_distribution: Option, - /// Lower confidence bound of the kth selected TopK item, after widening - /// the interval by the sketch's own estimation error. - pub topk_selected_lower_bound: Option, - /// Greatest upper confidence bound among excluded TopK items, after - /// widening the interval by the sketch's own estimation error. - pub topk_excluded_upper_bound: Option, - /// Union-bound failure probability of all intervals used by the margin - /// certificate. - pub topk_interval_failure_probability: Option, - /// Hydra shared-grid collision error in the inner guarantee's metric. - pub hydra_shared_grid_collision_bound: Option, - /// Failure probability assigned to the Hydra shared-grid term. - pub hydra_shared_grid_failure_probability: Option, -} - -/// Supplies typed planning-time evidence required by propagation rules. -pub trait AccuracyEvidenceProvider { - /// Trusted estimator contract for this complete aggregate expression, - /// including source, filters, grouping and all panes in each evaluation. - /// An observed cardinality is not an enforced population bound. - fn estimator_contract(&self, _expression: &OperatorNode) -> Option { - None - } - - /// Enforced upper bound on distinct (partition, item) identities across a - /// complete TopK evaluation. Used to union-bound score errors for adaptively - /// selected candidates. Observed cardinality is not sufficient evidence. - fn topk_max_distinct_items(&self, _expression: &OperatorNode) -> Option { - None - } - - /// Proof scoped to this complete quantile expression, including its source, - /// filters, grouping and window. `None` means unknown, including emptiness. - fn quantile_input_domain(&self, _operand: &OperatorNode) -> Option { - None - } - - fn propagation_stats( - &self, - _op: &CompositionOperator, - _family: &FieldDataType, - _query: Option<&SketchStatistic>, - ) -> PropagationStats { - PropagationStats::default() - } -} - -#[derive(Debug, Default, Clone, Copy)] -pub struct NoAccuracyEvidence; - -impl AccuracyEvidenceProvider for NoAccuracyEvidence {} - -/// Accuracy evidence backed by the normalized data workload. Freshness is -/// checked at the planning time before values reach any accuracy rule. -#[derive(Debug, Clone, Copy)] -pub struct WorkloadAccuracyEvidence<'a> { - pub data: &'a asap_types::workload::DataWorkload, - pub now_ms: u64, -} - -impl AccuracyEvidenceProvider for WorkloadAccuracyEvidence<'_> { - fn propagation_stats( - &self, - _op: &CompositionOperator, - _family: &FieldDataType, - _query: Option<&SketchStatistic>, - ) -> PropagationStats { - PropagationStats { - input_row_count: self.data.input_cardinality.value_at(self.now_ms).copied(), - data_distribution: self.data.distribution.value_at(self.now_ms).cloned(), - ..PropagationStats::default() - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use asap_types::workload::{DataDistribution, DataWorkload, Evidence, EvidenceSource}; - #[test] - fn workload_accuracy_evidence_uses_only_fresh_data_characteristics() { - let data = DataWorkload { - input_cardinality: Evidence { - value: Some(42), - source: EvidenceSource::Observed, - observed_at_ms: Some(1_000), - valid_for_ms: Some(500), - }, - distribution: Evidence { - value: Some(DataDistribution::Bursty), - source: EvidenceSource::Observed, - observed_at_ms: Some(1_000), - valid_for_ms: Some(500), - }, - ..Default::default() - }; - let provider = WorkloadAccuracyEvidence { - data: &data, - now_ms: 1_500, - }; - let fresh = provider.propagation_stats( - &CompositionOperator::ExactSum, - &FieldDataType::ExactAggregate( - asap_types::ir::schema::ExactKind::Sum, - asap_types::ir::schema::ExactParams::Sum, - ), - None, - ); - assert_eq!(fresh.input_row_count, Some(42)); - assert_eq!(fresh.data_distribution, Some(DataDistribution::Bursty)); - - let stale = WorkloadAccuracyEvidence { - data: &data, - now_ms: 1_501, - } - .propagation_stats( - &CompositionOperator::ExactSum, - &FieldDataType::ExactAggregate( - asap_types::ir::schema::ExactKind::Sum, - asap_types::ir::schema::ExactParams::Sum, - ), - None, - ); - assert_eq!(stale.input_row_count, None); - assert_eq!(stale.data_distribution, None); - } -} diff --git a/crates/logical-optimizer/src/accuracy/mod.rs b/crates/logical-optimizer/src/accuracy/mod.rs index 150623d2..9f858314 100644 --- a/crates/logical-optimizer/src/accuracy/mod.rs +++ b/crates/logical-optimizer/src/accuracy/mod.rs @@ -2,24 +2,16 @@ //! //! Estimator models derive local guarantees ([`local_guarantee`]) and the //! conservative target check ([`satisfies`]); Stage 3's accuracy model -//! (`asap_plan_selection::DefaultAccuracyModel`) delegates to them. Evidence -//! supplies scoped contracts. Unknown evidence may retain a candidate but does -//! not authorize selection. See `docs/design_docs/concepts/accuracy-models.md` +//! (`asap_plan_selection::DefaultAccuracyModel`) delegates to them. An unknown +//! bound may retain a candidate but does not authorize selection. See `docs/design_docs/concepts/accuracy-models.md` //! for the design. pub mod estimators; -pub mod evidence; - -pub use evidence::{ - AccuracyEvidenceProvider, EstimatorContract, NoAccuracyEvidence, PropagationStats, - QuantileInputDomain, WorkloadAccuracyEvidence, -}; use asap_types::ir::properties::{ BoundExpr, CompositionOperator, ErrorMetric, GuaranteeSource, ProbabilityExpr, ResultGuarantee, }; use asap_types::ir::schema::{FieldDataType, SketchAlgorithm, SketchParams, SketchStatistic}; -use asap_types::ir::OperatorNode; use asap_types::types::AccuracyTarget; pub use estimators::{local_guarantee, sketch_guarantee}; diff --git a/crates/logical-optimizer/src/lib.rs b/crates/logical-optimizer/src/lib.rs index 2991f401..619982d3 100644 --- a/crates/logical-optimizer/src/lib.rs +++ b/crates/logical-optimizer/src/lib.rs @@ -28,7 +28,4 @@ pub mod pass2; #[cfg(test)] mod test_support; -pub use accuracy::{ - AccuracyEvidenceProvider, NoAccuracyEvidence, PropagationStats, WorkloadAccuracyEvidence, -}; pub use pass1::realization::{has_subpopulations, summary_candidates, Realization}; diff --git a/crates/plan-selection/src/lib.rs b/crates/plan-selection/src/lib.rs index e78b7621..4543b9a2 100644 --- a/crates/plan-selection/src/lib.rs +++ b/crates/plan-selection/src/lib.rs @@ -69,7 +69,6 @@ use crate::cost::analytical_cost::{ use crate::cost::physical_operator_statistics::{ EdgeStatistics, OperatorStatistics, PartitionStatistics, UnaryEdgeStatistics, }; -use asap_logical_optimizer::accuracy::{AccuracyEvidenceProvider, NoAccuracyEvidence}; use asap_logical_optimizer::pass1::logical_candidates::{ choice_index, combination_count, compose_logical_candidate, enumerate_choices, nested_targets, read_targets, LocalLogicalCandidates, LogicalCandidateError, @@ -103,7 +102,6 @@ const DEFAULT_LOOKBACK_MS: u64 = 60_000; pub const MAX_ENUMERATED_CANDIDATES: usize = 64; static DEFAULT_ACCURACY_MODEL: DefaultAccuracyModel = DefaultAccuracyModel; -static NO_ACCURACY_EVIDENCE: NoAccuracyEvidence = NoAccuracyEvidence; static UNRESTRICTED: DeploymentCapabilities = DeploymentCapabilities::UNRESTRICTED; /// Stage 3's price coefficients and amortization horizon. Values are @@ -168,7 +166,6 @@ impl Stage3Calibration { #[non_exhaustive] pub struct PlanningModels<'a> { pub accuracy: &'a dyn AccuracyModel, - pub evidence: &'a dyn AccuracyEvidenceProvider, pub calibration: Stage3Calibration, /// What the deployment can build, read out and keep; unrestricted by /// default. @@ -176,13 +173,9 @@ pub struct PlanningModels<'a> { } impl<'a> PlanningModels<'a> { - pub fn new( - accuracy: &'a dyn AccuracyModel, - evidence: &'a dyn AccuracyEvidenceProvider, - ) -> Self { + pub fn new(accuracy: &'a dyn AccuracyModel) -> Self { Self { accuracy, - evidence, calibration: Stage3Calibration::ILLUSTRATIVE, capabilities: &UNRESTRICTED, } @@ -193,7 +186,6 @@ impl<'a> PlanningModels<'a> { pub fn builtin() -> PlanningModels<'static> { PlanningModels { accuracy: &DEFAULT_ACCURACY_MODEL, - evidence: &NO_ACCURACY_EVIDENCE, calibration: Stage3Calibration::ILLUSTRATIVE, capabilities: &UNRESTRICTED, } @@ -204,11 +196,6 @@ impl<'a> PlanningModels<'a> { self } - pub fn with_evidence(mut self, evidence: &'a dyn AccuracyEvidenceProvider) -> Self { - self.evidence = evidence; - self - } - pub fn with_calibration(mut self, calibration: Stage3Calibration) -> Self { self.calibration = calibration; self diff --git a/docs/design_docs/architecture/input-output-workflow.md b/docs/design_docs/architecture/input-output-workflow.md index c6e2f757..66cde9f2 100644 --- a/docs/design_docs/architecture/input-output-workflow.md +++ b/docs/design_docs/architecture/input-output-workflow.md @@ -290,7 +290,7 @@ latter cannot be fabricated by one. |---|---|---| | Accuracy model | `PlanningModels.accuracy`; `DefaultAccuracyModel` by default. Stage 3 checks each summary estimate with it. | Derives each estimate's guarantee and checks it against the requested accuracy. The model does not itself provide missing data-domain facts. | | Cost calibration | `PlanningModels.calibration`; `Stage3Calibration::ILLUSTRATIVE` by default. | Stage 3 prices candidates analytically; the built-in calibration is not a measured deployment cost. | -| Accuracy/domain evidence | `AccuracyEvidenceProvider`; default strategies use `NoAccuracyEvidence` when no provider is supplied. | Input ranges, nonempty populations, Top-K intervals, and similar facts can certify or rule out particular approximations. Missing facts remain unknown. | +| Accuracy/domain evidence | No planner input yet; the accuracy-evidence provider was removed with the legacy search. | Input ranges, nonempty populations, Top-K intervals, and similar facts can certify or rule out particular approximations. Missing facts remain unknown. | | Measured cost evidence | Supplied through a deployment-specific cost model or physical-evidence provider when cost-based physical comparison is needed. | CPU, memory, and I/O estimates must be comparable before claiming a summary beats raw recomputation. | | Runtime capabilities | Checked by deployment-specific providers. | Prevents choosing a maintenance/window operation the intended executor cannot implement. | diff --git a/docs/design_docs/concepts/accuracy-models.md b/docs/design_docs/concepts/accuracy-models.md index 00fc1bb9..75487ed2 100644 --- a/docs/design_docs/concepts/accuracy-models.md +++ b/docs/design_docs/concepts/accuracy-models.md @@ -188,12 +188,12 @@ configuration or candidate is needed. ## Evidence and trust boundaries -`AccuracyEvidenceProvider` supplies estimator contracts, quantile input domains -and propagation evidence. The evidence must cover the population to which the +The planner has no accuracy-evidence input today: the provider that supplied +estimator contracts and quantile input domains was removed with the legacy +search, its only reader. Any future evidence must cover the population to which the claimed guarantee applies: sources, filters, grouping, evaluation windows and all merged panes. Evidence for a narrower population cannot silently certify -a wider one. The workload-backed provider checks freshness before exposing -its supported data characteristics. +a wider one. Source contracts are assertions that the source or deployment must establish and enforce. They are not inferred from observed cardinality or sampled value @@ -207,24 +207,12 @@ certification. If a model uses an empirical accuracy calibration, the contract must identify its confidence level and scope rather than silently promoting an observation into a guarantee. -### Example: bounded Classic HLL +### HLL confidence -A deployment supplies `EstimatorContract::ClassicHll` for the complete aggregate -expression. It asserts the classic estimator, independent uniform bucket -hashing and an enforced maximum distinct population per evaluation, including -all merged panes. Planner combines this contract with the query or allocated -local target, selects a supported precision, derives the guarantee and uses -the normal propagation and selection checks. - -The current model supports maxima from 1 to 4096 and precisions from 4 to 18, -and certifies only configurations that remain in the linear-counting branch. -It bounds collisions across every integer cardinality in the declared domain -and bounds overestimation deterministically. It is not an RSE-to-normal -conversion, nor does it cover HIP/MLE or arbitrary unbounded populations. - -Missing evidence leaves generic HLL confidence unknown. Invalid or infeasible -contracts cannot authorize the result. The contract does not certify another -estimator, another expression or an unsupported shared-grid grouping. +Generic HLL has no modeled confidence: its failure probability is unknown, so +it can meet `Epsilon` but not `EpsilonDelta`. The bounded classic-HLL contract +that certified a confidence was reachable only through the removed evidence +provider and was removed with it. ## Sharing across consumers diff --git a/docs/develop_docs/end-to-end-accuracy-guarantees.md b/docs/develop_docs/end-to-end-accuracy-guarantees.md index b36b362c..e8e069be 100644 --- a/docs/develop_docs/end-to-end-accuracy-guarantees.md +++ b/docs/develop_docs/end-to-end-accuracy-guarantees.md @@ -232,16 +232,14 @@ with probability at most `1/3`. Zero or even depth has no modeled guarantee. ASAPPlanner contains the guarantee algebra and parameter-derived contracts. It imports `asap_sketchlib` DDSketch mapping bounds for ratio certification; see [DDSketch ratio certification](../design_docs/proposals/asap-aware-mapping/ddsketch-quantile-ratios.md). This dependency -does not make Planner a query executor. Data- or runtime-dependent evidence enters -through an `AccuracyEvidenceProvider` as typed `PropagationStats` and is -recorded in provenance; the stage pipeline does not read it yet. `NoAccuracyEvidence` is the -default: it supplies no missing facts. Guarantee derivation remains conservative; -the direct DDSketch ratio exception above retains a candidate without claiming -its accuracy is proven. +does not make Planner a query executor. The planner has no input for data- or +runtime-dependent accuracy evidence: the accuracy-evidence provider was removed +with the legacy search, which was its only reader. Guarantee derivation remains +conservative: a guarantee that needs a missing fact keeps it as an unknown leaf. ### TopK membership -`PropagationStats` supplies: +Not implemented in the stage pipeline. A certificate would need: - the lower confidence bound of the kth selected item; - the greatest upper confidence bound among excluded items; and diff --git a/docs/develop_docs/library-api.md b/docs/develop_docs/library-api.md index f5c04149..41e79c9c 100644 --- a/docs/develop_docs/library-api.md +++ b/docs/develop_docs/library-api.md @@ -247,7 +247,6 @@ unranked and carry no accuracy certificate; Stage 3 checks accuracy. | `accuracy` / `with_accuracy(&dyn AccuracyModel)` | `asap_plan_selection::DefaultAccuracyModel` | Each estimate's guarantee (`local_guarantee`) and whether it meets the query's target (`satisfies`); Stage 3 rejects an estimate whose family has no model | | `calibration` / `with_calibration(Stage3Calibration)` | `Stage3Calibration::ILLUSTRATIVE` | Weights that turn modeled resources into cost; illustrative, not measured | | `capabilities` / `with_capabilities(&DeploymentCapabilities)` | Unrestricted | What the deployment can build, read out and keep; candidates needing more are rejected | -| `evidence` / `with_evidence(&dyn AccuracyEvidenceProvider)` | `NoAccuracyEvidence` | Planning-time accuracy evidence; the stage pipeline does not read it yet | ## Workload inputs and defaults