Skip to main content

apex_solver/core/
mod.rs

1//! Core optimization components for the apex-solver library
2//!
3//! This module contains the fundamental building blocks for nonlinear least squares optimization:
4//! - Problem formulation and management
5//! - Residual blocks
6//! - Variables and manifold handling
7//! - Loss functions for robust estimation
8//! - Correctors for applying loss functions
9
10pub mod corrector;
11pub mod loss_functions;
12pub mod problem;
13pub mod residual_block;
14pub mod variable;
15
16use thiserror::Error;
17
18/// Core module error types for optimization problems and factors
19#[derive(Debug, Clone, Error)]
20pub enum CoreError {
21    /// Residual block operation failed
22    #[error("Residual block error: {0}")]
23    ResidualBlock(String),
24
25    /// Variable initialization or constraint error
26    #[error("Variable error: {0}")]
27    Variable(String),
28
29    /// Factor linearization failed
30    #[error("Factor linearization failed: {0}")]
31    FactorLinearization(String),
32
33    /// Symbolic structure construction failed
34    #[error("Symbolic structure error: {0}")]
35    SymbolicStructure(String),
36
37    /// Parallel computation error (thread/mutex failures)
38    #[error("Parallel computation error: {0}")]
39    ParallelComputation(String),
40
41    /// Dimension mismatch between residual/Jacobian/variables
42    #[error("Dimension mismatch: {0}")]
43    DimensionMismatch(String),
44
45    /// Invalid constraint specification (bounds, fixed indices)
46    #[error("Invalid constraint: {0}")]
47    InvalidConstraint(String),
48
49    /// Loss function error
50    #[error("Loss function error: {0}")]
51    LossFunction(String),
52
53    /// Invalid input parameter or configuration
54    #[error("Invalid input: {0}")]
55    InvalidInput(String),
56}
57
58/// Result type for core module operations
59pub type CoreResult<T> = Result<T, CoreError>;
60
61impl From<crate::linearizer::LinearizerError> for CoreError {
62    fn from(err: crate::linearizer::LinearizerError) -> Self {
63        match err {
64            crate::linearizer::LinearizerError::SymbolicStructure(msg) => {
65                CoreError::SymbolicStructure(msg)
66            }
67            crate::linearizer::LinearizerError::ParallelComputation(msg) => {
68                CoreError::ParallelComputation(msg)
69            }
70            crate::linearizer::LinearizerError::Variable(msg) => CoreError::Variable(msg),
71            crate::linearizer::LinearizerError::FactorLinearization(msg) => {
72                CoreError::FactorLinearization(msg)
73            }
74            crate::linearizer::LinearizerError::InvalidInput(msg) => CoreError::InvalidInput(msg),
75        }
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82    use crate::error::ErrorLogging;
83
84    // -------------------------------------------------------------------------
85    // CoreError Display — one test per variant
86    // -------------------------------------------------------------------------
87
88    #[test]
89    fn test_core_error_residual_block_display() {
90        let e = CoreError::ResidualBlock("bad block".into());
91        assert!(e.to_string().contains("bad block"));
92    }
93
94    #[test]
95    fn test_core_error_variable_display() {
96        let e = CoreError::Variable("bad var".into());
97        assert!(e.to_string().contains("bad var"));
98    }
99
100    #[test]
101    fn test_core_error_factor_linearization_display() {
102        let e = CoreError::FactorLinearization("jacobian fail".into());
103        assert!(e.to_string().contains("jacobian fail"));
104    }
105
106    #[test]
107    fn test_core_error_symbolic_structure_display() {
108        let e = CoreError::SymbolicStructure("sparse error".into());
109        assert!(e.to_string().contains("sparse error"));
110    }
111
112    #[test]
113    fn test_core_error_parallel_computation_display() {
114        let e = CoreError::ParallelComputation("thread panic".into());
115        assert!(e.to_string().contains("thread panic"));
116    }
117
118    #[test]
119    fn test_core_error_dimension_mismatch_display() {
120        let e = CoreError::DimensionMismatch("3 vs 6".into());
121        assert!(e.to_string().contains("3 vs 6"));
122    }
123
124    #[test]
125    fn test_core_error_invalid_constraint_display() {
126        let e = CoreError::InvalidConstraint("out of bounds".into());
127        assert!(e.to_string().contains("out of bounds"));
128    }
129
130    #[test]
131    fn test_core_error_loss_function_display() {
132        let e = CoreError::LossFunction("huber weight".into());
133        assert!(e.to_string().contains("huber weight"));
134    }
135
136    #[test]
137    fn test_core_error_invalid_input_display() {
138        let e = CoreError::InvalidInput("null pointer".into());
139        assert!(e.to_string().contains("null pointer"));
140    }
141
142    // -------------------------------------------------------------------------
143    // log() and log_with_source() return self (variant preserved)
144    // -------------------------------------------------------------------------
145
146    #[test]
147    fn test_core_error_log_returns_self() {
148        let e = CoreError::InvalidInput("test_log".into());
149        let returned = e.log();
150        assert!(returned.to_string().contains("test_log"));
151    }
152
153    #[test]
154    fn test_core_error_log_with_source_returns_self() {
155        let e = CoreError::DimensionMismatch("mismatch".into());
156        let source = std::io::Error::other("source error");
157        let returned = e.log_with_source(source);
158        assert!(returned.to_string().contains("mismatch"));
159    }
160
161    // -------------------------------------------------------------------------
162    // CoreResult type alias
163    // -------------------------------------------------------------------------
164
165    #[test]
166    fn test_core_result_ok() {
167        let r: CoreResult<i32> = Ok(42);
168        assert!(matches!(r, Ok(42)));
169    }
170
171    #[test]
172    fn test_core_result_err() {
173        let r: CoreResult<i32> = Err(CoreError::InvalidInput("oops".into()));
174        assert!(r.is_err());
175    }
176}