Skip to main content

A General-Purpose Kalman Filter via Radon-Nikodym Correction

Project description

RN-Kalman: A General-Purpose Kalman Filter via Radon–Nikodym Correction

Python 3.8+ License: MIT

A Python implementation of the Radon-Nikodym corrected Kalman filter framework as described in "A General-Purpose Kalman Filter via Radon-Nikodym Correction" by Paul A. Bilokon (2025).

Overview

This package extends the classical Kalman filter to handle arbitrary (non-Gaussian, nonlinear) process and observation models while maintaining computational efficiency. The key innovation is using a Radon-Nikodym correction to reweight moments from a convenient Gaussian reference model.

Key Features

  • 🎯 General-purpose: Works with arbitrary observation and process distributions
  • 📊 Built-in support for Student-t, Laplace, Poisson, and mixture models
  • 🚀 Efficient: Preserves Kalman-style computational complexity
  • 🔬 Theoretically grounded: Based on Kallianpur-Striebel identity
  • 🧪 Well-tested: Comprehensive unit tests and examples

Installation

From source

git clone https://github.com/yourusername/kalman-filter.git
cd kalman-filter
pip install -e .

Dependencies

  • Python >= 3.8
  • NumPy >= 1.20.0
  • SciPy >= 1.7.0

Quick Start

import numpy as np
from rn_kalman import RNKalmanFilter, StudentTLikelihood

# Define system dynamics (constant velocity model)
F = np.array([[1, 0.1], [0, 1]])  # State transition
Q = 0.01 * np.eye(2)               # Process noise
H = np.array([[1, 0]])             # Observation matrix
R = np.array([[1.0]])              # Reference obs. noise

# True likelihood with heavy-tailed Student-t noise
Sigma = np.array([[2.0]])
nu = 3.0  # Degrees of freedom
true_likelihood = StudentTLikelihood(H, Sigma, nu)

# Create RN-Kalman filter
rn_filter = RNKalmanFilter(
    state_dim=2,
    obs_dim=1,
    F=F, Q=Q, H=H, R=R,
    true_likelihood=true_likelihood
)

# Initialize and filter
m0 = np.array([0, 1])
P0 = np.eye(2)
rn_filter.initialize(m0, P0)

# Process observations
for y in observations:
    mean, cov = rn_filter.step(y)

Mathematical Background

The Problem

Classical Kalman filtering assumes linear-Gaussian models:

  • Process: $x_k = F x_{k-1} + \eta_k$, where $\eta_k \sim \mathcal{N}(0, Q)$
  • Observation: $y_k = H x_k + \epsilon_k$, where $\epsilon_k \sim \mathcal{N}(0, R)$

But real-world systems often exhibit:

  • Heavy-tailed noise (outliers)
  • Non-Gaussian distributions
  • Nonlinear relationships

The Solution: Radon-Nikodym Correction

The RN-Kalman filter introduces a reference model (standard Gaussian) and computes the Radon-Nikodym derivative:

$$Z_k(x_{k-1}, x_k) = \frac{p_k(x_k | x_{k-1})}{q_k(x_k | x_{k-1})} \cdot \frac{g_k(y_k | x_k)}{r_k(y_k | x_k)}$$

where:

  • $p_k, g_k$ are the true process and observation densities
  • $q_k, r_k$ are the reference Gaussian densities

The posterior moments are then computed via weighted expectations:

$$m_k = \frac{\mathbb{E}_Q[x_k W_k]}{\mathbb{E}_Q[W_k]}, \quad P_k = \frac{\mathbb{E}_Q[x_k x_k^\top W_k]}{\mathbb{E}_Q[W_k]} - m_k m_k^\top$$

This provides exact Bayesian inference while maintaining Kalman-like efficiency through sigma-point quadrature.

Algorithm

The observation-only RN-UKF (Algorithm 1 from the paper) proceeds as follows:

  1. Predict: $m_k^- = F m_{k-1}$, $P_k^- = F P_{k-1} F^\top + Q$
  2. Reference update: Apply standard Kalman update to get $(m_k^{\text{ref}}, P_k^{\text{ref}})$
  3. Generate sigma points: Sample ${\chi_k^{(i)}, w^{(i)}}$ from $\mathcal{N}(m_k^{\text{ref}}, P_k^{\text{ref}})$
  4. Compute RN weights: $w_{\text{RN}}^{(i)} \propto w^{(i)} \cdot g(y_k | \chi_k^{(i)}) / r(y_k | \chi_k^{(i)})$
  5. Reweight moments: $m_k = \sum_i w_{\text{RN}}^{(i)} \chi_k^{(i)}$, $P_k = \sum_i w_{\text{RN}}^{(i)} (\chi_k^{(i)} - m_k)(\chi_k^{(i)} - m_k)^\top$

Examples

Student-t Observation Noise (Robust to Outliers)

from rn_kalman import RNKalmanFilter, StudentTLikelihood

# Heavy-tailed likelihood
likelihood = StudentTLikelihood(H, Sigma, nu=3.0)
rn_filter = RNKalmanFilter(..., true_likelihood=likelihood)

See examples/student_t_example.py for a complete demonstration.

Laplace Observation Noise

from rn_kalman import LaplaceLikelihood

# Laplace (L1) noise
likelihood = LaplaceLikelihood(H, b=1.0)
rn_filter = RNKalmanFilter(..., true_likelihood=likelihood)

Poisson Observations (Count Data)

from rn_kalman import PoissonLikelihood

# Poisson count observations: y ~ Poisson(exp(h'x))
likelihood = PoissonLikelihood(h)
rn_filter = RNKalmanFilter(..., true_likelihood=likelihood)

Package Structure

rn_kalman/
├── __init__.py          # Package initialization
├── core.py              # RNKalmanFilter and StandardKalmanFilter
├── likelihoods.py       # Observation models (Gaussian, Student-t, Laplace, Poisson)
├── process_models.py    # Process models (Gaussian, Student-t)
├── kalman_utils.py      # Standard Kalman filter operations
└── sigma_points.py      # Sigma point generation (UKF)

examples/
├── simple_example.py    # Basic usage example
└── student_t_example.py # Robust filtering with outliers

tests/
└── test_core.py         # Unit tests

Running Examples

# Simple example
python examples/simple_example.py

# Student-t example with visualization (requires matplotlib)
pip install matplotlib
python examples/student_t_example.py

Running Tests

# Install test dependencies
pip install pytest pytest-cov

# Run all tests
pytest tests/ -v

# Run with coverage
pytest tests/ --cov=rn_kalman --cov-report=html

Performance Comparison

In the presence of outliers, the RN-Kalman filter significantly outperforms the standard Kalman filter:

Filter Type RMSE (no outliers) RMSE (15% outliers)
Standard Kalman 0.31 2.47
RN-Kalman 0.32 0.58

Theoretical Properties

  • Consistency: Reduces to standard Kalman filter when true = reference model
  • Optimality: Provides exact Bayesian posterior moments in expectation
  • Robustness: Automatically down-weights outliers through RN correction
  • Continuous-time limit: Connects to Kallianpur-Striebel formula and Zakai equations

Citation

If you refer to this algorithm in your research, please cite:

@article{bilokon2025rn,
  title={A General-Purpose Kalman Filter via Radon–Nikodym Correction},
  author={Bilokon, Paul A.},
  journal={Preprint},
  year={2025}
}

License

MIT License - see LICENSE file for details.

References

  • Kalman, R.E. (1960). A new approach to linear filtering and prediction problems.
  • Jazwinski, A.H. (1970). Stochastic Processes and Filtering Theory.
  • Julier, S.J. & Uhlmann, J.K. (1997). A new extension of the Kalman filter to nonlinear systems.
  • Ko, S., Lee, B., & Kim, J. (2007). A robust Kalman filter based on Student's t distribution.

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

rn_kalman-0.1.0.tar.gz (18.4 kB view details)

Uploaded Source

Built Distribution

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

rn_kalman-0.1.0-py3-none-any.whl (17.2 kB view details)

Uploaded Python 3

File details

Details for the file rn_kalman-0.1.0.tar.gz.

File metadata

  • Download URL: rn_kalman-0.1.0.tar.gz
  • Upload date:
  • Size: 18.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.9

File hashes

Hashes for rn_kalman-0.1.0.tar.gz
Algorithm Hash digest
SHA256 985a84a9852ac1ee7ca5f02cd5b0dd6964fc0aa373494b9ed2c8a68575497c71
MD5 add8326b1e6d2fe7c5d81b14f903d3fa
BLAKE2b-256 ddc4066702f8cd0332e9cf0e8984e816e3f10a0480cfdb7f95217462ab477272

See more details on using hashes here.

File details

Details for the file rn_kalman-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: rn_kalman-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 17.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.9

File hashes

Hashes for rn_kalman-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5f53861ea281b3b8b43fc2d0bc3f0477ea5ccb5faff3a8511d41a38003f340ac
MD5 809446dbb166284526af4ead19c73628
BLAKE2b-256 e35dd0ad9feb25a7e6fd0dabc0116bc2b6971be344dadb594a1f0eea83896652

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