A General-Purpose Kalman Filter via Radon-Nikodym Correction
Project description
RN-Kalman: A General-Purpose Kalman Filter via Radon–Nikodym Correction
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:
- Predict: $m_k^- = F m_{k-1}$, $P_k^- = F P_{k-1} F^\top + Q$
- Reference update: Apply standard Kalman update to get $(m_k^{\text{ref}}, P_k^{\text{ref}})$
- Generate sigma points: Sample ${\chi_k^{(i)}, w^{(i)}}$ from $\mathcal{N}(m_k^{\text{ref}}, P_k^{\text{ref}})$
- Compute RN weights: $w_{\text{RN}}^{(i)} \propto w^{(i)} \cdot g(y_k | \chi_k^{(i)}) / r(y_k | \chi_k^{(i)})$
- 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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
985a84a9852ac1ee7ca5f02cd5b0dd6964fc0aa373494b9ed2c8a68575497c71
|
|
| MD5 |
add8326b1e6d2fe7c5d81b14f903d3fa
|
|
| BLAKE2b-256 |
ddc4066702f8cd0332e9cf0e8984e816e3f10a0480cfdb7f95217462ab477272
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5f53861ea281b3b8b43fc2d0bc3f0477ea5ccb5faff3a8511d41a38003f340ac
|
|
| MD5 |
809446dbb166284526af4ead19c73628
|
|
| BLAKE2b-256 |
e35dd0ad9feb25a7e6fd0dabc0116bc2b6971be344dadb594a1f0eea83896652
|