perturbation-kernel¶
Reproducible perturbation-kernel estimators with SIMD and GPU backends.
A perturbation kernel answers one question: how much does a result move when you nudge the thing that produced it?
You supply four objects. A base state. A family of random perturbations with an intensity you control. A forward map to whatever you actually observe. A scalar functional of the resulting ensemble. The estimator returns that scalar and a non-asymptotic error bound.
The answer is a pure function of (family, n, seed). Same three inputs,
same bits, on any machine, any thread count, any CPU vector width.
import perturbation_kernel as pk
cfg = pk.Config(n=262_144, seed=20260610, invariance_lambda=1.0)
report = pk.Markov(k=5, theta_max=0.3).run(cfg)
report.value # 0.8802871704101562
report.functional # 'tail_survival'
report.execution # {'backend': 'auto', 'simd_path': 'neon', ...}
Why this exists¶
The usual way to ask whether a result is real is to compute a p-value against a null hypothesis nobody believes, using a threshold somebody picked in 1925. The perturbative alternative asks the direct question instead: perturb the inputs by the error you actually measured, re-run the pipeline, and see whether the result comes back.
That gives you a number with units. "The reported category survives up to input noise of 0.5" is a claim a reader can check against their own instrument. It does not depend on a convention, and it fails loudly when the effect is not there.
The mathematics is in A Measure-Theoretic Schema for Perturbation
Kernels; the wire formats are in SCHEMA.md v1.0.0. This repository is
the reference implementation, in Rust, with a Lean 4 formalisation of
the headline theorems alongside it.
What is here¶
| Rust crate | The engine, three worked families, a C ABI, and the SIMD and GPU backends |
| Python package | pip install perturbation-kernel, abi3 wheels for CPython 3.8+ |
| C++, Zig, Julia, TypeScript | Four more surfaces on the same engine; see Language bindings |
| Lean 4 library | The theorem statements, with the Gaussian-shift case fully proven |
Three guarantees¶
Bit-identical across host backends. scalar, simd and auto
compute the same reduction tree with the same IEEE-754 operations. Your
CPU's vector width and your machine's core count are not observable in
the output. Verified against golden values captured from v1.0.0, on
every length from 0 to 4096 and under property tests.
Bit-identical on the GPU too, or an error. backend="gpu" emulates
binary64 in u32 pairs and transcribes rand's integer sampler, so a
device run returns the same bits as the host. Families that need
transcendental functions WGSL does not specify exactly are refused
rather than silently approximated; backend="gpu_f32" runs those,
faster and approximately, and says so in every report it produces.
Refuses claims it cannot support. Assert an accuracy target below
the Theorem 7.3(c) sample floor and the run is rejected, with the
required n in the error message. The bound is not a decoration you can
quietly drop.