1#![allow(non_snake_case)]
2
3use crate::error::{IrisError, Result};
4
5pub struct KalmanFilter {
10 state_dim: usize,
12 measure_dim: usize,
14 F: Vec<Vec<f64>>,
16 H: Vec<Vec<f64>>,
18 Q: Vec<Vec<f64>>,
20 R: Vec<Vec<f64>>,
22 x: Vec<f64>,
24 P: Vec<Vec<f64>>,
26}
27
28impl KalmanFilter {
29 pub fn new(state_dim: usize, measure_dim: usize) -> Self {
38 let F = Self::eye(state_dim);
39 let H = Self::default_h(state_dim, measure_dim);
40 let Q = Self::scaled_eye(state_dim, 1e-2);
41 let R = Self::scaled_eye(measure_dim, 1e-1);
42 let x = vec![0.0; state_dim];
43 let P = Self::eye(state_dim);
44
45 Self {
46 state_dim,
47 measure_dim,
48 F,
49 H,
50 Q,
51 R,
52 x,
53 P,
54 }
55 }
56
57 pub fn set_transition(&mut self, f: Vec<Vec<f64>>) -> Result<()> {
59 if f.len() != self.state_dim || f.iter().any(|row| row.len() != self.state_dim) {
60 return Err(IrisError::InvalidParameter(format!(
61 "F must be [{0} x {0}]",
62 self.state_dim
63 )));
64 }
65 self.F = f;
66 Ok(())
67 }
68
69 pub fn set_measurement(&mut self, h: Vec<Vec<f64>>) -> Result<()> {
71 if h.len() != self.measure_dim || h.iter().any(|row| row.len() != self.state_dim) {
72 return Err(IrisError::InvalidParameter(format!(
73 "H must be [{0} x {1}]",
74 self.measure_dim, self.state_dim
75 )));
76 }
77 self.H = h;
78 Ok(())
79 }
80
81 pub fn set_process_noise(&mut self, q: Vec<Vec<f64>>) -> Result<()> {
83 if q.len() != self.state_dim || q.iter().any(|row| row.len() != self.state_dim) {
84 return Err(IrisError::InvalidParameter(format!(
85 "Q must be [{0} x {0}]",
86 self.state_dim
87 )));
88 }
89 self.Q = q;
90 Ok(())
91 }
92
93 pub fn set_measurement_noise(&mut self, r: Vec<Vec<f64>>) -> Result<()> {
95 if r.len() != self.measure_dim || r.iter().any(|row| row.len() != self.measure_dim) {
96 return Err(IrisError::InvalidParameter(format!(
97 "R must be [{0} x {0}]",
98 self.measure_dim
99 )));
100 }
101 self.R = r;
102 Ok(())
103 }
104
105 pub fn set_initial_state(&mut self, x: Vec<f64>) -> Result<()> {
107 if x.len() != self.state_dim {
108 return Err(IrisError::InvalidParameter(format!(
109 "State vector must have length {0}",
110 self.state_dim
111 )));
112 }
113 self.x = x;
114 Ok(())
115 }
116
117 pub fn predict(&mut self) {
119 self.x = mat_vec_mul(&self.F, &self.x);
121
122 let fp = mat_mul(&self.F, &self.P);
124 let f_t = transpose(&self.F);
125 let fpft = mat_mul(&fp, &f_t);
126 self.P = mat_add(&fpft, &self.Q);
127 }
128
129 pub fn update(&mut self, z: &[f64]) -> Result<()> {
138 if z.len() != self.measure_dim {
139 return Err(IrisError::InvalidParameter(format!(
140 "Measurement vector must have length {0}",
141 self.measure_dim
142 )));
143 }
144
145 let hx = mat_vec_mul(&self.H, &self.x);
147 let y: Vec<f64> = z.iter().zip(hx.iter()).map(|(zi, hxi)| zi - hxi).collect();
148
149 let hp = mat_mul(&self.H, &self.P);
151 let ht = transpose(&self.H);
152 let hpht = mat_mul(&hp, &ht);
153 let s = mat_add(&hpht, &self.R);
154
155 let s_inv = inverse(&s)?;
157 let pht = mat_mul(&self.P, &ht);
158 let k = mat_mul(&pht, &s_inv);
159
160 let ky = mat_vec_mul(&k, &y);
162 self.x = vec_add(&self.x, &ky);
163
164 let kh = mat_mul(&k, &self.H);
166 let i_kh = mat_sub(&Self::eye(self.state_dim), &kh);
167 self.P = mat_mul(&i_kh, &self.P);
168
169 Ok(())
170 }
171
172 pub fn state(&self) -> &[f64] {
174 &self.x
175 }
176
177 pub fn covariance_flat(&self) -> Vec<f64> {
179 self.P.iter().flat_map(|row| row.iter().copied()).collect()
180 }
181
182 pub fn covariance(&self) -> &[Vec<f64>] {
184 &self.P
185 }
186
187 fn eye(n: usize) -> Vec<Vec<f64>> {
190 (0..n)
191 .map(|i| (0..n).map(|j| if i == j { 1.0 } else { 0.0 }).collect())
192 .collect()
193 }
194
195 fn scaled_eye(n: usize, scale: f64) -> Vec<Vec<f64>> {
196 (0..n)
197 .map(|i| (0..n).map(|j| if i == j { scale } else { 0.0 }).collect())
198 .collect()
199 }
200
201 fn default_h(state_dim: usize, measure_dim: usize) -> Vec<Vec<f64>> {
202 let mut h = vec![vec![0.0; state_dim]; measure_dim];
203 let m = measure_dim.min(state_dim);
204 for i in 0..m {
205 h[i][i] = 1.0;
206 }
207 h
208 }
209}
210
211fn mat_mul(a: &[Vec<f64>], b: &[Vec<f64>]) -> Vec<Vec<f64>> {
214 let rows = a.len();
215 let cols = b[0].len();
216 let k = b.len();
217 let mut result = vec![vec![0.0; cols]; rows];
218 for i in 0..rows {
219 for j in 0..cols {
220 let mut sum = 0.0;
221 for p in 0..k {
222 sum += a[i][p] * b[p][j];
223 }
224 result[i][j] = sum;
225 }
226 }
227 result
228}
229
230fn mat_vec_mul(m: &[Vec<f64>], v: &[f64]) -> Vec<f64> {
231 m.iter()
232 .map(|row| row.iter().zip(v.iter()).map(|(a, b)| a * b).sum())
233 .collect()
234}
235
236fn mat_add(a: &[Vec<f64>], b: &[Vec<f64>]) -> Vec<Vec<f64>> {
237 a.iter()
238 .zip(b.iter())
239 .map(|(ra, rb)| ra.iter().zip(rb.iter()).map(|(ai, bi)| ai + bi).collect())
240 .collect()
241}
242
243fn mat_sub(a: &[Vec<f64>], b: &[Vec<f64>]) -> Vec<Vec<f64>> {
244 a.iter()
245 .zip(b.iter())
246 .map(|(ra, rb)| ra.iter().zip(rb.iter()).map(|(ai, bi)| ai - bi).collect())
247 .collect()
248}
249
250fn transpose(m: &[Vec<f64>]) -> Vec<Vec<f64>> {
251 if m.is_empty() {
252 return vec![];
253 }
254 let rows = m.len();
255 let cols = m[0].len();
256 (0..cols)
257 .map(|j| (0..rows).map(|i| m[i][j]).collect())
258 .collect()
259}
260
261fn vec_add(a: &[f64], b: &[f64]) -> Vec<f64> {
262 a.iter().zip(b.iter()).map(|(ai, bi)| ai + bi).collect()
263}
264
265fn inverse(m: &[Vec<f64>]) -> Result<Vec<Vec<f64>>> {
267 let n = m.len();
268 if n == 0 || m.iter().any(|row| row.len() != n) {
269 return Err(IrisError::InvalidParameter(
270 "Matrix must be square and non-empty".into(),
271 ));
272 }
273
274 let mut aug: Vec<Vec<f64>> = (0..n)
276 .map(|i| {
277 let mut row = m[i].clone();
278 row.extend((0..n).map(|j| if i == j { 1.0 } else { 0.0 }));
279 row
280 })
281 .collect();
282
283 for col in 0..n {
284 let mut max_val = aug[col][col].abs();
286 let mut max_row = col;
287 for row in (col + 1)..n {
288 if aug[row][col].abs() > max_val {
289 max_val = aug[row][col].abs();
290 max_row = row;
291 }
292 }
293 if max_val < 1e-12 {
294 return Err(IrisError::Tensor(
295 "Matrix is singular and cannot be inverted".into(),
296 ));
297 }
298 aug.swap(col, max_row);
299
300 let pivot = aug[col][col];
301 for j in 0..(2 * n) {
302 aug[col][j] /= pivot;
303 }
304
305 for row in 0..n {
306 if row == col {
307 continue;
308 }
309 let factor = aug[row][col];
310 for j in 0..(2 * n) {
311 aug[row][j] -= factor * aug[col][j];
312 }
313 }
314 }
315
316 let inv: Vec<Vec<f64>> = (0..n).map(|i| aug[i][n..(2 * n)].to_vec()).collect();
318
319 Ok(inv)
320}
321
322#[cfg(test)]
323mod tests {
324 use super::*;
325
326 #[test]
327 fn test_kalman_identity_tracking() {
328 let mut kf = KalmanFilter::new(2, 1);
330
331 kf.set_transition(vec![vec![1.0, 1.0], vec![0.0, 1.0]])
333 .unwrap();
334 kf.set_measurement(vec![vec![1.0, 0.0]]).unwrap();
335 kf.set_process_noise(vec![vec![1e-4, 0.0], vec![0.0, 1e-4]])
336 .unwrap();
337 kf.set_measurement_noise(vec![vec![0.1]]).unwrap();
338
339 kf.set_initial_state(vec![0.0, 0.0]).unwrap();
341
342 let mut last_pos = 0.0f64;
343 for step in 1..=10 {
344 kf.predict();
345
346 let true_pos = step as f64;
348 let measurement = [true_pos + 0.05]; kf.update(&measurement).unwrap();
350
351 let state = kf.state();
352 assert!(
354 (state[0] - true_pos).abs() < 2.0,
355 "Step {step}: expected ~{true_pos}, got {}",
356 state[0]
357 );
358 last_pos = state[0];
359 }
360 assert!((last_pos - 10.0).abs() < 2.0);
362 }
363
364 #[test]
365 fn test_kalman_predict_only() {
366 let mut kf = KalmanFilter::new(2, 1);
367 kf.set_transition(vec![vec![1.0, 1.0], vec![0.0, 1.0]])
368 .unwrap();
369 kf.set_initial_state(vec![0.0, 1.0]).unwrap();
370
371 kf.predict();
372 let s = kf.state();
373 assert!((s[0] - 1.0).abs() < 1e-10);
375 assert!((s[1] - 1.0).abs() < 1e-10);
376
377 kf.predict();
378 let s = kf.state();
379 assert!((s[0] - 2.0).abs() < 1e-10);
381 assert!((s[1] - 1.0).abs() < 1e-10);
382 }
383
384 #[test]
385 fn test_kalman_covariance_dims() {
386 let kf = KalmanFilter::new(3, 2);
387 let cov = kf.covariance_flat();
388 assert_eq!(cov.len(), 9); }
390
391 #[test]
392 fn test_inverse_2x2() {
393 let m = vec![vec![4.0, 7.0], vec![2.0, 6.0]];
394 let inv = inverse(&m).unwrap();
395 let product = mat_mul(&m, &inv);
397 for i in 0..2 {
398 for j in 0..2 {
399 let expected = if i == j { 1.0 } else { 0.0 };
400 assert!(
401 (product[i][j] - expected).abs() < 1e-10,
402 "({i},{j}): got {}",
403 product[i][j]
404 );
405 }
406 }
407 }
408
409 #[test]
410 fn test_inverse_singular() {
411 let m = vec![vec![1.0, 2.0], vec![2.0, 4.0]];
412 assert!(inverse(&m).is_err());
413 }
414
415 #[test]
416 fn test_measurement_mismatch() {
417 let mut kf = KalmanFilter::new(2, 1);
418 assert!(kf.update(&[1.0, 2.0]).is_err());
419 }
420}