Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 0 additions & 139 deletions crates/asap-aware-mapping/src/accuracy/allocation.rs

This file was deleted.

93 changes: 93 additions & 0 deletions crates/asap-aware-mapping/src/accuracy/budget.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
//! Split an end-to-end accuracy target equally among the approximate inputs
//! of one composition (#669, part b).
//!
//! When `n` approximate values compose into one output, each is sized for an
//! equal share of the output's target, so the composed bound meets the
//! target with no slack: `ε_i = ε / n` for an additively composed metric,
//! `ε_i = (1 + ε)^{1/n} − 1` for a relative one (relative errors compose as
//! `Π(1 + ε_i) − 1`), and `δ_i = δ / n` (the union bound). Only equal
//! splits are made.
use super::*;

/// `shares` of `of` equal shares of `target`, in `metric`'s composition:
/// the target of one approximate input (`shares = 1`), or of a nested
/// sub-DAG holding `shares` of the `of` inputs. `None` for an exact target
/// or an ε that is not positive and finite: no approximate input meets it.
/// `of = 1` (or 0) is `target` itself.
pub fn equal_shares(
target: &AccuracyTarget,
metric: ErrorMetric,
of: usize,
shares: usize,
) -> Option<AccuracyTarget> {
if target.is_exact() || !(target.error.is_finite() && target.error > 0.0) {
return None;
}
let fraction = shares.min(of.max(1)) as f64 / of.max(1) as f64;
let error = match metric {
ErrorMetric::Relative => (1.0 + target.error).powf(fraction) - 1.0,
_ => target.error * fraction,
};
Some(AccuracyTarget {
error,
failure_probability: target.failure_probability.map(|delta| delta * fraction),
..target.clone()
})
}

/// One of `approximate_inputs` equal shares of `target` ([`equal_shares`]).
pub fn equal_share(
target: &AccuracyTarget,
metric: ErrorMetric,
approximate_inputs: usize,
) -> Option<AccuracyTarget> {
equal_shares(target, metric, approximate_inputs, 1)
}

#[cfg(test)]
mod tests {
use super::*;

/// Additive metrics split ε and δ by `n`; the `n` shares add up to the
/// target.
#[test]
fn an_additive_target_splits_epsilon_and_delta_equally() {
let target = AccuracyTarget::epsilon_delta(0.1, 0.02);
let share = equal_share(&target, ErrorMetric::ABSOLUTE, 2).unwrap();
assert_eq!(share, AccuracyTarget::epsilon_delta(0.05, 0.01));
let rest = equal_shares(&target, ErrorMetric::ABSOLUTE, 3, 2).unwrap();
assert!((rest.error - 0.1 * 2.0 / 3.0).abs() < 1e-12);
assert!((rest.failure_probability.unwrap() - 0.02 * 2.0 / 3.0).abs() < 1e-12);
assert_eq!(
equal_share(&target, ErrorMetric::Rank, 1),
Some(target.clone())
);
assert_eq!(
equal_share(&AccuracyTarget::epsilon(0.1), ErrorMetric::Rank, 2)
.unwrap()
.failure_probability,
None
);
}

/// A relative target splits multiplicatively: `(1 + ε_i)^n = 1 + ε`,
/// not `n ε_i = ε`.
#[test]
fn a_relative_target_splits_multiplicatively() {
let share = equal_share(&AccuracyTarget::epsilon(0.21), ErrorMetric::Relative, 2).unwrap();
assert!((share.error - 0.1).abs() < 1e-12);
}

/// No approximate input meets an exact target.
#[test]
fn an_exact_target_has_no_share() {
assert_eq!(
equal_share(&AccuracyTarget::exact(), ErrorMetric::ABSOLUTE, 2),
None
);
assert_eq!(
equal_share(&AccuracyTarget::epsilon(0.0), ErrorMetric::ABSOLUTE, 2),
None
);
}
}
1 change: 1 addition & 0 deletions crates/asap-aware-mapping/src/accuracy/estimators/cms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ mod tests {
&g,
&AccuracyTarget::epsilon_delta(0.01, 0.001)
.with_metric(ErrorMetric::ABSOLUTE_STREAM_L1)
.with_scope(asap_types::types::AccuracyScope::PerValue)
));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,9 @@ mod tests {
assert_eq!(guarantee.spec.metric, ErrorMetric::ABSOLUTE_STREAM_L2);
assert!(DefaultAccuracyModel.satisfies(
&guarantee,
&AccuracyTarget::epsilon_delta(0.01, 0.01).with_metric(ErrorMetric::ABSOLUTE_STREAM_L2)
&AccuracyTarget::epsilon_delta(0.01, 0.01)
.with_metric(ErrorMetric::ABSOLUTE_STREAM_L2)
.with_scope(asap_types::types::AccuracyScope::PerValue)
));
}

Expand Down
9 changes: 9 additions & 0 deletions crates/asap-aware-mapping/src/accuracy/estimators/hll.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,15 @@ impl ClassicHllConfidence {
}
}

/// The smallest precision from `precision` up whose classic failure
/// probability over a source of at most `max_distinct` distinct items is at
/// most `delta` (#669b); `precision` when none up to 18 is.
pub(crate) fn precision_for(precision: u8, max_distinct: u64, delta: f64) -> u8 {
(precision..=18)
.find(|&p| classic_failure_probability(p, max_distinct) <= delta)
.unwrap_or(precision)
}

/// HLL RSE-magnitude inversion: sized from ε alone. Its confidence comes
/// from the source's distinct bound ([`classic_guarantee`]).
pub(crate) fn hll_precision(eps: f64) -> u8 {
Expand Down
38 changes: 36 additions & 2 deletions crates/asap-aware-mapping/src/accuracy/estimators/kll.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,36 @@ pub(crate) fn size_params(eps: f64, delta: f64) -> SketchParams {
}
}

/// `params` resized to fail with probability at most `delta` at no larger
/// rank error: `k` for its current error at `delta`'s scale, never smaller.
/// Unchanged when it already fails no more often.
pub(crate) fn with_failure_probability(params: &SketchParams, delta: f64) -> SketchParams {
let SketchParams::Kll {
k,
failure_probability,
} = params
else {
return params.clone();
};
let current = failure_probability.unwrap_or(KLL_CALIBRATED_FAILURE_PROBABILITY);
let Some(scale) = delta_scale(current) else {
return params.clone();
};
if delta >= current {
return params.clone();
}
match size_params(kll_rank_error_99(*k) * scale, delta) {
SketchParams::Kll {
k: resized,
failure_probability,
} => SketchParams::Kll {
k: resized.max(*k),
failure_probability,
},
other => other,
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -178,7 +208,9 @@ mod tests {
assert_eq!(g.spec.failure_probability.evaluate(), Some(0.01));
assert!(DefaultAccuracyModel.satisfies(
&g,
&AccuracyTarget::epsilon_delta(0.01, 0.01).with_metric(ErrorMetric::Rank)
&AccuracyTarget::epsilon_delta(0.01, 0.01)
.with_metric(ErrorMetric::Rank)
.with_scope(asap_types::types::AccuracyScope::PerValue)
));
assert_eq!(g.approximate_layer_count(), 1);
assert!(g.provenance.iter().any(|source| matches!(
Expand Down Expand Up @@ -209,7 +241,9 @@ mod tests {
.unwrap();
assert!(DefaultAccuracyModel.satisfies(
&g,
&AccuracyTarget::epsilon_delta(0.01, 0.001).with_metric(ErrorMetric::Rank)
&AccuracyTarget::epsilon_delta(0.01, 0.001)
.with_metric(ErrorMetric::Rank)
.with_scope(asap_types::types::AccuracyScope::PerValue)
));
assert_eq!(g.spec.failure_probability.evaluate(), Some(0.001));
}
Expand Down
Loading
Loading