apex_solver/linearizer/cpu/mod.rs
1//! CPU linearizer implementations.
2//!
3//! Provides sparse and dense Jacobian assembly for CPU computation.
4//! GPU equivalent lives in the sibling [`super::gpu`] module.
5//!
6//! This module also owns the [`LinearizationMode`] marker trait and its two
7//! implementations ([`SparseMode`], [`DenseMode`]), which define the matrix
8//! types used throughout the solver pipeline.
9
10pub mod dense;
11pub mod sparse;
12
13use faer::{Mat, sparse::SparseColMat};
14
15// ============================================================================
16// LinearizationMode — static dispatch between sparse and dense paths
17// ============================================================================
18
19/// Marker trait that defines the matrix types for a linear algebra path.
20///
21/// Enables zero-cost static dispatch between sparse and dense linear algebra
22/// backends. Optimizers are generic over `LinearizationMode`, so the compiler
23/// generates one specialization per concrete mode at no runtime cost.
24///
25/// Two implementations are provided:
26/// - [`SparseMode`]: Jacobian and Hessian are `SparseColMat<usize, f64>`
27/// - [`DenseMode`]: Jacobian and Hessian are `Mat<f64>`
28pub trait LinearizationMode: 'static {
29 /// The Jacobian matrix type (`SparseColMat` or `Mat`)
30 type Jacobian: Send + Sync;
31 /// The Hessian matrix type (`SparseColMat` or `Mat`)
32 type Hessian: Send + Sync;
33}
34
35/// Sparse linear algebra mode.
36///
37/// Uses `SparseColMat<usize, f64>` for Jacobians and Hessians.
38/// Optimal for large-scale problems with sparse structure (e.g., pose graphs).
39pub struct SparseMode;
40
41impl LinearizationMode for SparseMode {
42 type Jacobian = SparseColMat<usize, f64>;
43 type Hessian = SparseColMat<usize, f64>;
44}
45
46/// Dense linear algebra mode.
47///
48/// Uses `Mat<f64>` for Jacobians and Hessians.
49/// Optimal for small-to-medium problems (< 500 DOF) or dense Jacobians.
50pub struct DenseMode;
51
52impl LinearizationMode for DenseMode {
53 type Jacobian = Mat<f64>;
54 type Hessian = Mat<f64>;
55}
56
57#[cfg(test)]
58mod tests {
59 use super::*;
60 use faer::{Mat, sparse::SparseColMat};
61
62 type TestResult = Result<(), Box<dyn std::error::Error>>;
63
64 #[test]
65 fn test_sparse_mode_jacobian_is_sparse_col_mat() -> TestResult {
66 let j: <SparseMode as LinearizationMode>::Jacobian = SparseColMat::try_new_from_triplets(
67 1,
68 1,
69 &[faer::sparse::Triplet::new(0usize, 0usize, 1.0f64)],
70 )?;
71 assert_eq!(j.ncols(), 1);
72 Ok(())
73 }
74
75 #[test]
76 fn test_sparse_mode_hessian_is_sparse_col_mat() -> TestResult {
77 let h: <SparseMode as LinearizationMode>::Hessian = SparseColMat::try_new_from_triplets(
78 2,
79 2,
80 &[
81 faer::sparse::Triplet::new(0usize, 0usize, 1.0f64),
82 faer::sparse::Triplet::new(1usize, 1usize, 2.0f64),
83 ],
84 )?;
85 assert_eq!(h.nrows(), 2);
86 Ok(())
87 }
88
89 #[test]
90 fn test_dense_mode_jacobian_is_mat() {
91 let j: <DenseMode as LinearizationMode>::Jacobian = Mat::zeros(3, 4);
92 assert_eq!(j.nrows(), 3);
93 assert_eq!(j.ncols(), 4);
94 }
95
96 #[test]
97 fn test_dense_mode_hessian_is_mat() {
98 let h: <DenseMode as LinearizationMode>::Hessian =
99 Mat::from_fn(2, 2, |i, j| if i == j { 1.0 } else { 0.0 });
100 assert!((h[(0, 0)] - 1.0).abs() < 1e-12);
101 assert!(h[(0, 1)].abs() < 1e-12);
102 }
103}