Skip to main content

Reproducible perturbation-kernel estimators with SIMD and GPU backends

Project description

perturbation-kernel

Perturbative stability estimation with a reproducibility guarantee at the bit level.

Given a base state s, a parametrised Markov kernel P((s, θ), ·), an intensity law ρ over θ, a measurable forward map F: S → O, and a functional Φ: M₁(O) → R, the engine evaluates the plug-in estimator

Φ̂_N(s) = Φ( (1/N) Σᵢ δ_{F(Sᵢ)} ),    Sᵢ ~ P((s, θᵢ), ·),  θᵢ ~ ρ

and returns it with a non-asymptotic error bound. The estimate is a pure function of (family, N, seed): no ambient entropy, no thread-count dependence, no accumulation order that varies with hardware.

Reference implementation of SCHEMA.md v1.0.0.

Install

pip install perturbation-kernel

abi3 wheels for CPython 3.8+ on Linux, macOS and Windows, both architectures. No Rust toolchain required.

Use

import perturbation_kernel as pk

cfg = pk.Config(n=262_144, seed=20260610, invariance_lambda=1.0)
r = pk.Markov(k=5, theta_max=0.3).run(cfg)

r.value       # 0.8802871704101562
r.functional  # 'tail_survival'
r.execution   # {'backend': 'auto', 'simd_path': 'neon', 'precision': 'f64', ...}

Built-in families

Family Kernel P((s, θ), ·) Functional Φ Codomain
Gaussian(base, sigma_max) s + θ·N(0, I), θ ~ U[0, σ_max] negative summed coordinate variance (-∞, 0]
Bistable(x0, dt, theta_max) one Euler-Maruyama step in V(x) = (x²-1)² polarisation E[sgn X] [-1, 1]
Markov(k, theta_max, start, base_label) mix to U{0..k-1} w.p. θ survival P(X = base_label) [0, 1]

All three satisfy the C2 identity contract: θ₀ = 0 and P((s, θ₀), ·) = δ_s, so the null perturbation is exactly recoverable rather than approximately so.

pk.Markov(k=5, theta_max=0.0, start=2, base_label=2).run(cfg).value  # exactly 1.0
pk.Gaussian(base=[1.5, -2.0], sigma_max=0.0).run(cfg).value          # exactly 0.0

Determinism

Draw i consumes the substream ChaCha20(mix64(seed, i)), so the ensemble is index-addressable and order-free. Reduction is a fixed-shape pairwise tree keyed to the index order, not to the thread count, which is what keeps a non-associative float sum reproducible.

a = pk.Markov(k=5, theta_max=0.3).run(pk.Config(n=50_000, seed=7))
b = pk.Markov(k=5, theta_max=0.3).run(pk.Config(n=50_000, seed=7, backend="scalar"))
a.value.hex() == b.value.hex()   # True — identical mantissa, not "close"

CI verifies this against golden values captured from v1.0.0, across four native OS/architecture combinations and every feature configuration.

Backends

pk.available_backends()   # ['auto', 'scalar', 'simd', 'gpu', 'gpu_f32']
pk.simd_path()            # 'neon' | 'avx2' | 'scalar'
pk.gpu_device()           # 'Apple M4 Max (Metal, IntegratedGpu)' or None
Backend Execution Agreement
scalar single-threaded, portable reference
simd NEON vpaddq_f64 / AVX2 vhaddpd+vpermpd bit-identical
auto vectorised, rayon above 4096 draws bit-identical
gpu wgpu compute, emulated binary64 bit-identical
gpu_f32 wgpu compute, single precision statistically equivalent

Vectorisation is exact because each tree-level output is one IEEE-754 addition of one fixed operand pair; four lanes perform the same four additions. No lane-crossing accumulator, no reassociation, and the centring step is an explicit subtract-then-multiply so it cannot contract into an FMA (which rounds once where the reference rounds twice).

gpu is exact for Markov because that family's arithmetic reduces to a short, fully-specified list: f64.wgsl emulates binary64 multiplication in u32 pairs with round-to-nearest-even, rand's Lemire rejection sampler is transcribed rather than approximated, and the indicator observation makes the reduction integer addition — associative, hence scheduling-invariant. Families drawing normal deviates require ln/exp, whose accuracy WGSL leaves to the driver, so gpu rejects them rather than silently returning a different number:

pk.Gaussian(base=[0.5], sigma_max=0.3).run(pk.Config(n=1024, backend="gpu"))
# RuntimeError: ... use backend "gpu_f32" to accept a single-precision result

Error bounds

Declare Λ (Wasserstein-1 Lipschitz constant of Φ), the observation diameter D, and d_obs, and the report carries the Theorem 7.3 bound: a McDiarmid concentration term √(Λ²D²/2N · ln(2/η)) plus a Fournier-Guillin bias term for the empirical-measure convergence rate.

An accuracy claim below the Theorem 7.3(c) sample floor is rejected at run time rather than reported optimistically:

pk.sample_floor(invariance_lambda=1.0, observation_diameter=1.0,
                epsilon=0.05, eta=0.05, obs_dim=1)          # 262144

pk.Markov(k=5, theta_max=0.3).run(pk.Config(
    n=1000, seed=1, invariance_lambda=1.0,
    epsilon=0.05, eta=0.05, observation_diameter=1.0, obs_dim=1))
# ValueError: sample-complexity floor: requested (0.05,0.05) needs N >= 262144

The floor is dominated by the bias term, not the variance term: the Wasserstein rate converges more slowly than the concentration does, so tightening ε by 10× costs 256× the compute.

Other languages

The same engine, the same bits, through the C ABI (and wasm for TypeScript): Rust, C/C++, Zig, Julia, TypeScript. CI runs a cross-language agreement job that compares raw f64 bit patterns across all of them rather than each against a constant.

Documentation

https://godofecht.github.io/perturbation-kernel/

License

MIT.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

perturbation_kernel-2.1.0.tar.gz (135.7 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

perturbation_kernel-2.1.0-cp38-abi3-win_amd64.whl (2.8 MB view details)

Uploaded CPython 3.8+Windows x86-64

perturbation_kernel-2.1.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.5 MB view details)

Uploaded CPython 3.8+manylinux: glibc 2.17+ x86-64

perturbation_kernel-2.1.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (2.3 MB view details)

Uploaded CPython 3.8+manylinux: glibc 2.17+ ARM64

perturbation_kernel-2.1.0-cp38-abi3-macosx_11_0_arm64.whl (1.9 MB view details)

Uploaded CPython 3.8+macOS 11.0+ ARM64

perturbation_kernel-2.1.0-cp38-abi3-macosx_10_12_x86_64.whl (2.0 MB view details)

Uploaded CPython 3.8+macOS 10.12+ x86-64

File details

Details for the file perturbation_kernel-2.1.0.tar.gz.

File metadata

  • Download URL: perturbation_kernel-2.1.0.tar.gz
  • Upload date:
  • Size: 135.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for perturbation_kernel-2.1.0.tar.gz
Algorithm Hash digest
SHA256 8b31d935a75ba936102011784bbbdcabcc3d3bec00a5fd7173abcd62634b6c3d
MD5 6c561aba493ec6f6e90a0466aa8416a3
BLAKE2b-256 b68fe7098a1dd4afba418307a2a6f8aed33044ec33fad4529017510abf06a7bb

See more details on using hashes here.

File details

Details for the file perturbation_kernel-2.1.0-cp38-abi3-win_amd64.whl.

File metadata

File hashes

Hashes for perturbation_kernel-2.1.0-cp38-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 8fd8522d3d0f54df3f642904909018d4b02d7f76d299f0b61b0da78a5b919052
MD5 49ca5b4b4181801ef69d68082f5d30ef
BLAKE2b-256 fc6085714aa74a5c1c6610880a03579a844eff8918b632cce8fc3709099c6a20

See more details on using hashes here.

File details

Details for the file perturbation_kernel-2.1.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for perturbation_kernel-2.1.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 270ea6da27cb38bb463a9293b6b72b6f7a430a700239fec16b384d913a544476
MD5 c5cfb967e3be9ee97993d481fadfc28e
BLAKE2b-256 1b218c013c8f391589f9ff636e2c96855a844213e6cf1f9decd98b92f7fe7537

See more details on using hashes here.

File details

Details for the file perturbation_kernel-2.1.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for perturbation_kernel-2.1.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 2c0d58922e682d7864be7bd245fa7c2e9a161991fbe1461dc3b49aa5f082d2c3
MD5 86e7e62e37e3d8adf2c5c241b023e034
BLAKE2b-256 fb5843427b42339b39e94ce527fec1bebc062f2b8677041a2153d8cb6ae4b0de

See more details on using hashes here.

File details

Details for the file perturbation_kernel-2.1.0-cp38-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for perturbation_kernel-2.1.0-cp38-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 31b9fe3e1f6e2c8604dbff4575bb892199aeff40395f8158ca0175d783b48a4d
MD5 4fcbb5ea514a6afe4ce5f4e1c5d0a04b
BLAKE2b-256 393b3699d2e7da1ccb0c3c1718bb38482e119a1f4c5fdeeee8f6eb6e4f17a9b6

See more details on using hashes here.

File details

Details for the file perturbation_kernel-2.1.0-cp38-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for perturbation_kernel-2.1.0-cp38-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 53b7fbee4391d6e90a41d0a355a40fb17678cd4d43074e929ccd6693867aae29
MD5 42e7aecf127e57b84d505adb0a0655ea
BLAKE2b-256 ac090bdf9e8dcb31ededb420aecec645ad138c19ef7afde5dc45afe664bd7f86

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page