alice-analytics 0.6.0

High-performance telemetry & probabilistic statistical estimation — HyperLogLog++, DDSketch, Count-Min Sketch, local differential privacy, streaming anomaly detection
Documentation

ALICE-Analytics

日本語

Probabilistic data structures and streaming statistics with stated error bounds: HyperLogLog cardinality, DDSketch quantiles with a relative-error guarantee, Count-Min frequencies and heavy hitters, streaming moments / covariance / regression, windowed aggregates and anomaly detectors. The core is no_std with fixed-size, stack-allocated sketches. With the law feature it also summarises how a set of points deviates from a fitted alice_zip::law::SignalLaw.

It is not a time-series database, a metrics backend or a dashboard: it keeps summaries in memory and leaves storage and transport to the caller. Sketches answer approximate queries; when exact quantiles or exact distinct counts are needed, keep the raw values.

License: Apache-2.0 (0.3.0 and earlier: MIT OR Apache-2.0)

Contents

Installation

cargo add alice-analytics
# with the residual summary of alice-zip laws
cargo add alice-analytics --features law

Example

use alice_analytics::sketch::{DDSketch, HyperLogLog};

// quantiles with a relative-error guarantee of 1 %
let mut latency = DDSketch::new(0.01);
for ms in 1..=1000 {
    latency.insert(f64::from(ms));
}
let p99 = latency.quantile(0.99);
assert!((p99 - 990.0).abs() <= 0.01 * 990.0);

// distinct count in 16 KiB of registers
let mut users = HyperLogLog::new();
for id in 0..10_000_u64 {
    users.insert(&id);
}
let n = users.cardinality();
assert!((n - 10_000.0).abs() < 0.05 * 10_000.0);

Residual distribution of a law

law::residual_summary(&law, &points) (feature law) streams the residuals y − f(x) of points about an alice_zip::law::SignalLaw into a DDSketch2048 and returns a ResidualSummary:

Field Meaning
count points summarised
out_of_range points whose x is outside the law's valid range (or not finite); the law is not evaluated there, nothing is extrapolated
non_finite points in range whose y − f(x) is NaN or infinite; counted, not summarised
distribution None when nothing was summarised; otherwise mean, min, max, max_abs (exact) and p50, p90, p99 (sketched)
relative_accuracy α: each reported quantile q̂ of a residual q satisfies |q̂ − q| ≤ α·|q|
accurate_range residual magnitudes for which that bound holds; zero is always exact
outside_accuracy non-zero residuals outside accurate_range (0 means the bound holds for every quantile)

The quantile of rank ⌈q·n⌉ is reported, the same order statistic the sketch defines. Residuals are divided by a power-of-two scale before they are inserted (by default the largest power of two not above the law's own residual RMS), which moves accurate_range to the size of the residuals without rounding. law::ResidualSketch is the streaming form: push one point at a time, read quantile(q) for any q, choose α and the scale.

use alice_analytics::law::residual_summary;

let s = residual_summary(&law, &new_points);
println!("{} summarised, {} outside the valid range", s.count, s.out_of_range);
if let Some(d) = s.distribution {
    println!("p50 {:+e}  p99 {:+e}  max |r| {:e}", d.p50, d.p99, d.max_abs);
}

A complete program is in examples/residual_summary.rs (cargo run --example residual_summary --features law).

Features

Feature Default Description
std yes standard-library float functions, entropy-seeded privacy constructors, JSON / Prometheus export
law no law module: residual summary of an alice_zip::law::SignalLaw (adds alice-zip without its std feature, and alloc)
simd no no effect; kept so existing feature lists still build

Without std the privacy mechanisms take an explicit seed, and sqrt / ceil / floor / round / mul_add come from libm; IEEE 754 requires all five to be correctly rounded, so they return the same bits as the inherent methods. The transcendentals go through alice-det-math in both builds, so a no_std build and a std build agree bit for bit (see Determinism). CI builds the library for thumbv7em-none-eabihf with and without law.

Modules

Module Contents
sketch HyperLogLog10/12/14/16, DDSketch128 … DDSketch2048, CountMinSketch1024x5 / 2048x7 / 4096x5, HeavyHitters5/10/20, FnvHasher, Mergeable
stats StreamingStats (mean / variance / skewness / kurtosis), CovarianceMatrix, quantile_sorted, percentile_rank, iqr
window TumblingWindow, SlidingWindow, HierarchicalRollup
streaming_ops SimpleMovingAverage, ExponentialMovingAverage, ChangeRate, LinearRegression, LinearRegressionFull
anomaly StreamingMedian, MadDetector, ZScoreDetector, EwmaDetector, CompositeDetector
privacy deprecated, not differentially private (predictable noise source; its clock-seeded constructors are removed in 0.6.0, the module stays deprecated): use alice_crypto::dp
pipeline MetricPipeline, MetricRegistry, MetricSnapshot, RingBuffer
export JSON and Prometheus text for MetricSnapshot (std)
law residual_summary, ResidualSketch, ResidualSummary (feature law)

Error bounds

Structure Bound Checked in
DDSketch |q̂ − q| ≤ α·|q| for magnitudes inside accurate_range(), negative values included tests/analytic_oracle.rs
HyperLogLog standard error 1.04/√m (m registers) tests/analytic_oracle.rs
CountMinSketch estimate ≥ true count, overestimate ≤ ε·N, ε = e/w tests/analytic_oracle.rs
law::ResidualSketch the DDSketch bound on residuals, exact mean / min / max tests/law_residual.rs

The expected values in these tests are closed forms or two-pass references written in the test files, not outputs of the functions under test.

A non-finite sample (NaN, ±inf) is not a magnitude: DDSketch::insert counts it in non_finite() and leaves count, sum, min, max and every bin alone, so the ranks behind a quantile are the ranks of the values that reached a bin. This is the same classification the law module applies to a non-finite residual. A finite magnitude outside accurate_range() is counted and filed in the nearest edge bin: it keeps its rank, but not the α bound.

Determinism

The same inputs produce the same bits on every supported target. This matters because sketches are merged across machines, telemetry is replayed, and an audit may re-derive a quantile from the raw events: a last-ulp difference between two hosts turns an estimate into two estimates.

Tier What Why it is bit-exact
IEEE basic operations + − × ÷, sqrt, mul_add, ceil / floor / round IEEE 754 requires correct rounding, so every target agrees. mul_add is a fused multiply-add with a single rounding; a target without an FMA instruction uses the correctly rounded software fma
Transcendentals ln, exp, powf Routed through alice-det-math, whose kernels are built from the operations above in a fixed evaluation order. The platform libm is not used: its last ulp differs between macOS, glibc, MSVC and wasm
Integer powers γⁿ for the DDSketch bin layout Binary exponentiation with a fixed association order, rather than powi, whose multiplication tree is unspecified
Pseudo-random XorShift64 and everything seeded from it Integer state; a seeded constructor replays exactly. The entropy-seeded constructors (std only) are by design not reproducible

Enforcement is mechanical, in two layers:

  • clippy.toml lists the inherent f32 / f64 transcendentals under disallowed-methods, and CI runs cargo clippy --all-targets --all-features -- -D warnings, so reintroducing x.ln() is a compile error rather than a silent divergence. This is the layer that catches a regression on the machine that writes it.
  • tests/determinism_golden.rs serialises the outputs of seven scenarios (one per module with float arithmetic) bit for bit and compares a SHA-256 against a recorded constant. Each scenario also asserts that it serialised a non-zero number of bytes, so a scenario that stops exercising its module fails instead of passing on the hash of an empty buffer. CI runs the file on macOS aarch64, Linux x86_64, Linux aarch64 and Windows x86_64.

Naming the arithmetic

Bit-exactness across machines is only half of what a stored estimate needs. The other half is being able to say which arithmetic produced it, because a quantile computed under one implementation of ln and one computed under another are two numbers, however close they look.

alice_analytics::SEMANTICS_ID is that name: the 32-byte identifier alice-det-math publishes for its own numeric behaviour, re-exported here. Record it next to any estimate that is stored, transmitted or merged; two results are comparable as numbers only if the identifier agrees. It is pinned as hex by golden_semantics_id, so an arithmetic change cannot reach a release unnoticed.

With the law feature, law::ResidualSummary::law_id goes one step further and names both halves at once: it is the summarised law's own identifier taken under SEMANTICS_ID, so it changes when f(x) changes and when the arithmetic changes. It covers what f(x) reads (the domain and the coefficients) and not how the law was obtained, so two laws fitted from different measurements that evaluate identically share one identifier.

Determinism is not correctness. A golden hash pins whatever the code does today, including a mistake; the error bounds above are checked separately against closed forms. The two are independent and both are required.

Outside the guarantee: a target that does not honour IEEE 754 for the basic operations (32-bit x86 built for x87 without SSE2), any build with fast-math style flags, and the entropy-seeded privacy constructors. Results across a version change of this crate or of alice-det-math are also not pinned — only results across platforms at a given version. What SEMANTICS_ID adds is not a promise that the arithmetic never changes, but the ability to tell that it did.

Minimum supported Rust version

rust-version = "1.87" (measured: 1.86 does not build the library). CI checks the library on 1.87 with default and with all features; the development toolchain is pinned in rust-toolchain.toml.

Building and testing

cargo test --all-features
cargo test --all-features --test determinism_golden  # cross-platform bit equality
cargo test --all-features --test panic_contract      # degenerate-input contracts
cargo build --lib --no-default-features --features law --target thumbv7em-none-eabihf
scripts/preflight.sh          # every CI gate that runs locally
scripts/preflight.sh --quick  # static checks, clippy, builds, docs, cargo test --lib

Related crates

  • alice-det-math — the cross-platform bit-exact ln / exp / powf every float path here goes through
  • alice-zip — law::SignalLaw: a fitted law with its valid range, residual statistics, provenance and oracle cases, whose residual distribution the law feature summarises

ALICE-Law integration

Generated by ALICE-Law scripts/gen-law-readme-block.py from ALICE-Law 6a0a5587c152 and this repository's alice-law-conformance.toml; do not edit it by hand.

Release train alice-analytics 0.5.0 (published), law dependency: yes
Arithmetic alice-det-math 0.5.0 in the train, arithmetic identifier 135c407d4249c1c200e4d1be79a12e848ce5c355fc3a918da965849e697ef000; this crate requires alice-det-math = "0.5.1"
End to end check covered (alice-analytics = "0.5"), digest 966a78784bb4e3341ac6627d519db896f398176b71c2402b188294db6c8e3728, the same on aarch64 and x86_64

Conformance (G1-G14)

gate status note
G1 no-libm-direct pass every method the G1 list marks banned is in clippy.toml for f32 and f64, the pending powi is banned, every feature and the no_std target are linted with -D …
G2 ieee-max-min known unmet no Law helper for max/min and no default-deny manifest of raw_* exceptions exist. The library calls f64::max / f64::min / clamp directly at src/anomaly.rs:799, …
G3 no-outer-fneg-of-fma known unmet no checker for an outer negation of an fma result exists in this repository. mul_add is used in src/stats.rs, src/privacy.rs, src/anomaly.rs and …
G4 debug-release-arch-goldens known unmet the cross-platform golden file is tests/determinism_golden.rs (golden_sketch, golden_stats, golden_window, golden_anomaly, golden_privacy, …
G5 release-profile-test-leg known unmet no CI step runs the test suite under the release profile: ci.yml:40,43,50,52 are cargo test without --release, and the aarch64 legs (macos-latest, …
G6 semantics-id known unmet src/lib.rs:80 is pub use alice_det_math::SEMANTICS_ID;: the crate re-exports its arithmetic crate's identifier and folds none of its own pins. Its own …
G7 handshake-carries-id unverified audit: the crate has no decode / read / from_bytes / open entry point, no extern "C" or no_mangle item, no pyo3 or wasm-bindgen export, no dyn Any or …
G8 persisted-semantics-id unverified undecided: the crate writes no file or database row, but src/export.rs:24 snapshot_to_json, :58 snapshots_to_json, :86 snapshot_to_prometheus and :146 …
G9 persisted-meaning-change-is-major unverified depends on G8 (the persisted identifier whose history G9 audits), which is undecided. For the in-process identifier the history is: SEMANTICS_ID first appears …
G10 no-run-varying-artifacts pass the clock-seeded constructors of the deprecated privacy module (XorShift64::from_entropy, impl Default for XorShift64, LaplaceNoise::new, Rappor::new, …
G11 gate-self-tests known unmet the Law gates that are implemented here have no self-test: the G1 clippy lint has no canary that injects a banned call and confirms the lint still fires (a …
G12 public-repo-text-rules pass the CI job fetches ALICE-Law's own scripts/public_text_check.py at the revision named in .github/public-text-pin and refuses it unless its SHA-256 is the one …
G13 no-unspecified-order unverified audit: no HashMap or HashSet in src (the only one in the repository is tests/analytic_oracle.rs:434, in a test), no rayon, no thread spawn and no par_iter in …
G14 float-environment-invariants known unmet the build configuration sets no fast-math-style flag and no target-cpu=native: there is no .cargo directory and no build.rs, no RUSTFLAGS in any workflow, no …

Features and the guarantee

feature guarantee note
law not declared
simd not declared
std not declared

License

  • Apache License, Version 2.0, from 0.4.0
  • Redistributions, in source or binary form, carry LICENSE-APACHE and NOTICE (Apache-2.0 §4); the notice need not appear in an application's user interface
  • Earlier versions keep the licence they were released under: alice-analytics up to 0.3.0 on crates.io is MIT OR Apache-2.0
  • The "ALICE" names are covered by TRADEMARK_NOTICE, whatever the licence