Skip to main content

nabled_control/
lib.rs

1//! Control theory for LTI systems.
2
3#![allow(clippy::missing_errors_doc, clippy::many_single_char_names)]
4
5use std::fmt;
6
7use nabled_core::errors::{IntoNabledError, NabledError, ShapeError};
8
9pub mod dare;
10pub mod gramian;
11pub mod lqr;
12pub mod observer;
13pub mod pole;
14
15#[derive(Debug, Clone, PartialEq)]
16pub enum ControlError {
17    EmptyMatrix,
18    DimensionMismatch,
19    InvalidInput(String),
20    ConvergenceFailed,
21    SingularSystem,
22}
23
24impl fmt::Display for ControlError {
25    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26        match self {
27            ControlError::EmptyMatrix => write!(f, "matrix cannot be empty"),
28            ControlError::DimensionMismatch => write!(f, "input dimensions are incompatible"),
29            ControlError::InvalidInput(message) => write!(f, "invalid input: {message}"),
30            ControlError::ConvergenceFailed => write!(f, "control iteration did not converge"),
31            ControlError::SingularSystem => write!(f, "singular control system"),
32        }
33    }
34}
35
36impl std::error::Error for ControlError {}
37
38impl IntoNabledError for ControlError {
39    fn into_nabled_error(self) -> NabledError {
40        match self {
41            ControlError::EmptyMatrix => NabledError::Shape(ShapeError::EmptyInput),
42            ControlError::DimensionMismatch => NabledError::Shape(ShapeError::DimensionMismatch),
43            ControlError::InvalidInput(message) => NabledError::InvalidInput(message),
44            ControlError::ConvergenceFailed => NabledError::ConvergenceFailed,
45            ControlError::SingularSystem => NabledError::SingularMatrix,
46        }
47    }
48}
49
50#[cfg(test)]
51mod tests {
52    use nabled_core::errors::{IntoNabledError, NabledError, ShapeError};
53
54    use super::*;
55
56    #[test]
57    fn control_errors_display_and_map_to_shared_taxonomy() {
58        assert_eq!(ControlError::EmptyMatrix.to_string(), "matrix cannot be empty");
59        assert_eq!(
60            ControlError::DimensionMismatch.to_string(),
61            "input dimensions are incompatible"
62        );
63        assert_eq!(
64            ControlError::InvalidInput("bad poles".to_string()).to_string(),
65            "invalid input: bad poles"
66        );
67        assert_eq!(
68            ControlError::ConvergenceFailed.to_string(),
69            "control iteration did not converge"
70        );
71        assert_eq!(ControlError::SingularSystem.to_string(), "singular control system");
72
73        assert!(matches!(
74            ControlError::EmptyMatrix.into_nabled_error(),
75            NabledError::Shape(ShapeError::EmptyInput)
76        ));
77        assert!(matches!(
78            ControlError::DimensionMismatch.into_nabled_error(),
79            NabledError::Shape(ShapeError::DimensionMismatch)
80        ));
81        assert!(matches!(
82            ControlError::InvalidInput("x".to_string()).into_nabled_error(),
83            NabledError::InvalidInput(_)
84        ));
85        assert!(matches!(
86            ControlError::ConvergenceFailed.into_nabled_error(),
87            NabledError::ConvergenceFailed
88        ));
89        assert!(matches!(
90            ControlError::SingularSystem.into_nabled_error(),
91            NabledError::SingularMatrix
92        ));
93    }
94}