Skip to main content

turbo-ckf

PyPI version Python versions CI License: MIT OR Apache-2.0

turbo-ckf is a Rust-backed Cubature Kalman Filter package for high-throughput prediction/update loops. Implemented here purely as an experiment after reading the paper.

What This Package Optimizes

  • Fast prediction with built-in linear models:
    • predict_standard_model(...)
    • predict_standard_model_ckf(...)
    • predict_linear_model(F)
    • predict_linear_model_ckf(F)
  • Fast AHRS update path:
    • update_paper_ahrs(...)

predict(...) and update(...) also run through Rust, but callback cost in Python can dominate if your models are heavy.

Square-Root Variant

TurboSRCKF is a separate estimator that propagates the lower-triangular Cholesky factor of P directly. The filter loop never calls stable_cholesky on P, so the silent diagonal-jitter accumulation TurboCKF surfaces via jitter_count is structurally impossible in steady state. Use it when P becomes ill-conditioned (e.g., long predict chains with tiny Q/R, or rank-deficient seed covariances). Mirrors predict(...) + update(...); for closed-form linear predicts or the AHRS update use TurboCKF.

from turbo_ckf import TurboSRCKF
kf = TurboSRCKF(dim_x=4, dim_z=2, dt=0.1, hx=hx_vectorized, fx=fx_vectorized)
kf.predict()
kf.update(z)
# kf.chol_P is the lower-triangular factor; kf.P = chol_P @ chol_P.T

Callback Contract

Custom fx and hx must be vectorized:

  • Input shape is (2 * dim_x, dim_x).
  • fx output shape must be (2 * dim_x, dim_x).
  • hx output shape must be (2 * dim_x, dim_z).

If you pass pointwise callbacks, TurboCKF raises immediately.

Install

pip install turbo-ckf

Prebuilt wheels (one abi3 wheel per platform, CPython 3.9 or newer; CI tests 3.9 to 3.13) are published for three platforms only:

  • Linux x86_64 (manylinux2014, glibc 2.17 or newer)
  • macOS arm64 (Apple silicon, macOS 11 or newer)
  • Windows x64

Everywhere else, including Linux aarch64 (for example Raspberry Pi), musl Linux (Alpine) and Intel Macs, pip builds from the sdist. That needs a Rust toolchain on PATH (install one from https://rustup.rs).

Install from source (development)

From turbo-ckf/:

bash turbo_ckf/setup_env.sh

Usage

from turbo_ckf import TurboCKF

kf = TurboCKF(dim_x=2, dim_z=1, dt=0.1, hx=hx_vectorized, fx=fx_vectorized)
kf.predict_standard_model("constant_velocity")
kf.update(z)

The built-in models, "constant_velocity" and "constant_acceleration", need to know how the state vector is ordered. The default, layout="blocked", is all positions, then all velocities, then all accelerations: [x, y, vx, vy] or [x, y, vx, vy, ax, ay]. FilterPy code usually orders the state per axis, [x, vx, y, vy] or [x, vx, ax, y, vy, ay]; pass layout="interleaved" for that. predict_standard_model_ckf takes the same argument. The layout cannot be inferred from the state, so a mismatch gives wrong predictions without an error. With dim_x=2 (or 3 for constant acceleration) the two layouts are the same.

# FilterPy-style 2-D state [x, vx, y, vy]
kf = TurboCKF(dim_x=4, dim_z=2, dt=0.1, hx=hx_vectorized, fx=fx_vectorized)
kf.predict_standard_model("constant_velocity", layout="interleaved")

State assignment is forgiving about spelling: x accepts lists, integer arrays, and column vectors; P, Q, R accept scalars (kf.R = 0.25 means 0.25 * I), 1-D diagonals (kf.Q = [1e-3, 1e-2]), or full matrices. Wrong sizes and non-finite values raise at assignment time with the attribute named in the error. Changing kf.dt after construction takes effect on the next predict, including the standard/linear-model paths.

A NaN or inf measurement raises instead of corrupting the state. For a missed measurement pass z=None, which runs the predict-only step and clears the innovation diagnostics. The same applies to batch_filter, which does not skip missing measurements yet (use run(..., nan_means_missing=True) for sequences with dropouts), and to callback outputs: an fx or hx that returns NaN or inf raises a ValueError naming it, and the state is left unchanged. In batch_parallel_step, a filter whose own x, P or z is non-finite gets status 3 and is skipped while the rest of the bank updates.

Filter state lives in Rust

The Rust backend holds x, P, Q, R and every diagnostic (K, y, S, x_prior, P_post, log_likelihood, nis, the jitter counters). An attribute is copied into NumPy only when you read it, and that copy is reused until the next predict/update, so a loop that reads nothing pays nothing for state sync.

In-place edits work as in FilterPy. The array returned by kf.x, kf.P, kf.Q or kf.R is the filter's current value, and writing into it (kf.x[0] = 1.0, kf.P[2:, 2:] *= 1000) takes effect on the next call. Once a predict/update or an assignment replaces the value, the array you held becomes a read-only snapshot, so a late write raises ValueError instead of being dropped silently, and history.append(kf.x) needs no .copy(). Assignment copies your array into the filter. Output arrays such as kf.K are snapshots that do not feed back into the filter, and TurboSRCKF's chol_P, chol_Q, chol_R are read-only. Reading never changes results; in TurboSRCKF it never re-factors P either.

copy(), to_dict()/from_dict() and pickle keep the diagnostic counters, and reset() zeroes them. Pickling stores fx and hx by reference, so use module-level functions (a lambda makes pickle raise). TurboSRCKF.to_dict() stores chol_P, so restoring does not re-factor P.

Per step, 4-state / 2-measurement constant-velocity model with a vectorized hx, Apple M4 Max, median of 7 runs:

Loop State copied every call State in Rust
predict_standard_model + update 12.6 us 4.1 us
same, reading kf.x and kf.P each step 12.5 us 4.7 us
predict + update with fx/hx callbacks 15.7 us 6.8 us
TurboSRCKF predict + update 18.9 us 6.6 us
run() over 10,000 steps (total) 182 ms 51 ms

Filtering a whole sequence

run(...) wraps the predict/update loop, stacks the per-step outputs, and handles measurement dropouts:

result = kf.run(zs)                # zs: (N, dim_z), or (N,) when dim_z == 1
result.xs, result.Ps               # posterior means / covariances, stacked
result.log_likelihoods, result.nis # per-step diagnostics

# Missed measurements: None entries (or all-NaN rows with
# nan_means_missing=True) skip the update for that step.
kf.run([z0, None, z2])
kf.run(zs, nan_means_missing=True)

# Per-step time steps and measurement noise:
kf.run(zs, dts=dt_array, Rs=r_stack)

run is available on both TurboCKF and TurboSRCKF. For linear models prefer the static TurboCKF.batch_filter, which runs the whole loop inside Rust in a single crossing.

AHRS path:

kf.predict_linear_model(Fk)
kf.update_paper_ahrs(z6, sigma_acc2=1e-2, sigma_mag2=1e-2)

z6 is [ax, ay, az, mx, my, mz] in any units, for example raw m/s^2 and uT. The paper's observation model predicts unit vectors, so the update normalizes the accelerometer and magnetometer 3-vectors to unit length before using them (a zero or non-finite norm raises ValueError). sigma_acc2 and sigma_mag2 are therefore variances of the unit-vector components, not of the raw readings. The filter needs dim_x=4 (quaternion) and dim_z=6, and the update overwrites R with diag(sigma_acc2, sigma_acc2, sigma_acc2, sigma_mag2, sigma_mag2, sigma_mag2).

Benchmarks

Paper-Reported Targets (Yamagishi and Jing, arXiv:2602.12283)

These are the KCKF-vs-CKF results reported in the paper:

  • MacBook Pro 2021 (M1 Pro): KCKF 0.110 ms vs CKF 0.135 ms (18.79% lower time, about 1.23x faster).
  • Raspberry Pi 4 Model B: KCKF 1.28 ms vs CKF 1.51 ms (15.15% lower time, about 1.18x faster).

Local Results In This Repo

Measured on February 21, 2026 on Apple M4 Max, Python 3.12.0, NumPy 2.4.2.

From:

.venv-turbo-ckf/bin/python turbo_ckf_tests/verify_before_after.py --steps 50000 --repeats 7 --warmup 1 --parity-steps 500

Median runtime per 50k predict+update steps:

  • FilterPy (filterpy_predict_update): 2.354375 s (baseline)
  • TurboCKF callback path (turbo_callback_predict_update): 0.662003 s (3.56x vs FilterPy)
  • TurboCKF standard model KCKF (turbo_standard_model_predict_update): 0.539281 s (4.37x vs FilterPy)
  • TurboCKF standard model CKF (turbo_standard_model_ckf_predict_update): 0.549905 s (4.28x vs FilterPy)
  • KCKF-vs-CKF full-step speedup in this setup: 1.02x (0.549905 / 0.539281)

From:

.venv-turbo-ckf/bin/python turbo_ckf_tests/benchmark_paper.py
  • KCKF-vs-CKF (prediction only, backend): 1.67x
  • KCKF-vs-CKF (full step, backend): 1.09x
  • KCKF-vs-CKF (full step, wrapper): 1.03x

Interpretation:

  • The largest gains here are from the Rust-backed implementation versus pure-Python FilterPy loops (~3.5x to ~4.4x in these runs).
  • The direct KCKF-vs-CKF algorithmic gain is present but smaller on full-step end-to-end runs, and larger when isolating prediction equations.

Research Basis

  • This repo is an implementation of the KCKF AHRS equations described in:
    • Yamagishi, S. and Jing, L., "A Lightweight Cubature Kalman Filter for Attitude and Heading Reference Systems Using Simplified Prediction Equations", IEEE Access, vol. 14, pp. 73686-73697, 2026, doi:10.1109/ACCESS.2026.3686031. Preprint: arXiv:2602.12283, https://arxiv.org/abs/2602.12283.
  • Credit for the method belongs to the paper authors.

License

Dual-licensed under either:

  • MIT (LICENSE-MIT)
  • Apache-2.0 (LICENSE-APACHE)

Metadata

Release files for turbo-ckf 0.9.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for turbo-ckf 0.9.0
File Size Uploaded
turbo_ckf-0.9.0.tar.gz 139.6 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for turbo-ckf 0.9.0
File Interpreter ABI Platform
turbo_ckf-0.9.0-cp39-abi3-win_amd64.whl CPython 3.9 abi3 Windows x86-64 Details
turbo_ckf-0.9.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.9 abi3 Linux glibc 2.17+ x86-64 Details
turbo_ckf-0.9.0-cp39-abi3-macosx_11_0_arm64.whl CPython 3.9 abi3 macOS 11.0+ ARM64 Details

Total release size: 1.6 MB

Release files / turbo_ckf-0.9.0.tar.gz

Download URL turbo_ckf-0.9.0.tar.gz
Size 139.6 kB
Tags Source
SHA-256 checksum
How to use checksums
1259937592d72b4af9c16082b6b3b71ce80540798bd582c84cf3ee9d793b882c
BLAKE2b-256 checksum
How to use checksums
cbd93b9d620e4cc3d0d91253ff83c51521d8cbbe732a55a7e54f896c8107840e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 24, 2026.

Transparency log

Release files / turbo_ckf-0.9.0-cp39-abi3-win_amd64.whl

Download URL turbo_ckf-0.9.0-cp39-abi3-win_amd64.whl
Size 405.0 kB
Tags CPython 3.9 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
68950e20955db730b5411d0f712b6dc750dfebf1c3bdb0c0262c2f7d5c1fa481
BLAKE2b-256 checksum
How to use checksums
58cf03fe7c0de5fab00d58ba383f91e472fbe9ef92ab05d475551129d1abd166
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 24, 2026.

Transparency log

Release files / turbo_ckf-0.9.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL turbo_ckf-0.9.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 576.2 kB
Tags CPython 3.9 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
5cf9dd4347cea24e8fe006e84b974b6df123150506b69e2736774b9e279e7fca
BLAKE2b-256 checksum
How to use checksums
f734895cb7fa45bbaba111f08278acbaa79b2892db747e9b9f2c06723c641fcc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 24, 2026.

Transparency log

Release files / turbo_ckf-0.9.0-cp39-abi3-macosx_11_0_arm64.whl

Download URL turbo_ckf-0.9.0-cp39-abi3-macosx_11_0_arm64.whl
Size 489.9 kB
Tags CPython 3.9 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
0645ca54eff32f1fd9932a7445255ba77437da648618dfb9a707c1bb35e46b7a
BLAKE2b-256 checksum
How to use checksums
74e167c5fa20d546c49fcf2743b5257471bd53001f53fe9e35ff79626e871235
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 24, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.9.0 This release

4 release files

0.8.0

4 release files

0.1.1

4 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page