1use thiserror::Error;
8
9#[derive(Debug, Error)]
11pub enum DEError {
12 #[error("bounds mismatch: lower has {lower_len} elements, upper has {upper_len}")]
14 BoundsMismatch {
15 lower_len: usize,
17 upper_len: usize,
19 },
20
21 #[error("invalid bounds at index {index}: lower ({lower}) > upper ({upper})")]
23 InvalidBounds {
24 index: usize,
26 lower: f64,
28 upper: f64,
30 },
31
32 #[error("population size ({pop_size}) must be >= 4")]
34 PopulationTooSmall {
35 pop_size: usize,
37 },
38
39 #[error("invalid mutation factor: {factor} (must be in [0, 2])")]
41 InvalidMutationFactor {
42 factor: f64,
44 },
45
46 #[error("invalid crossover rate: {rate} (must be in [0, 1])")]
48 InvalidCrossoverRate {
49 rate: f64,
51 },
52
53 #[error("x0 dimension mismatch: expected {expected}, got {got}")]
55 X0DimensionMismatch {
56 expected: usize,
58 got: usize,
60 },
61
62 #[error("integrality mask dimension mismatch: expected {expected}, got {got}")]
64 IntegralityDimensionMismatch {
65 expected: usize,
67 got: usize,
69 },
70}
71
72pub type Result<T> = std::result::Result<T, DEError>;
74
75impl DEError {
76 pub fn is_bounds_error(&self) -> bool {
80 matches!(self, DEError::BoundsMismatch { .. } | DEError::InvalidBounds { .. })
81 }
82
83 pub fn is_config_error(&self) -> bool {
88 matches!(
89 self,
90 DEError::PopulationTooSmall { .. }
91 | DEError::InvalidMutationFactor { .. }
92 | DEError::InvalidCrossoverRate { .. }
93 )
94 }
95
96 pub fn is_dimension_error(&self) -> bool {
100 matches!(
101 self,
102 DEError::X0DimensionMismatch { .. } | DEError::IntegralityDimensionMismatch { .. }
103 )
104 }
105}
106
107#[cfg(test)]
108mod tests {
109 use super::*;
110
111 #[test]
112 fn test_error_display() {
113 let err = DEError::BoundsMismatch {
114 lower_len: 3,
115 upper_len: 5,
116 };
117 assert_eq!(
118 err.to_string(),
119 "bounds mismatch: lower has 3 elements, upper has 5"
120 );
121 }
122
123 #[test]
124 fn test_is_bounds_error() {
125 let bounds_err = DEError::BoundsMismatch {
126 lower_len: 1,
127 upper_len: 2,
128 };
129 let config_err = DEError::PopulationTooSmall { pop_size: 2 };
130
131 assert!(bounds_err.is_bounds_error());
132 assert!(!config_err.is_bounds_error());
133 }
134
135 #[test]
136 fn test_is_config_error() {
137 let config_err = DEError::InvalidCrossoverRate { rate: 1.5 };
138 let bounds_err = DEError::InvalidBounds {
139 index: 0,
140 lower: 5.0,
141 upper: 3.0,
142 };
143
144 assert!(config_err.is_config_error());
145 assert!(!bounds_err.is_config_error());
146 }
147
148 #[test]
149 fn test_is_dimension_error() {
150 let dim_err = DEError::X0DimensionMismatch {
151 expected: 10,
152 got: 5,
153 };
154 let bounds_err = DEError::BoundsMismatch {
155 lower_len: 1,
156 upper_len: 2,
157 };
158
159 assert!(dim_err.is_dimension_error());
160 assert!(!bounds_err.is_dimension_error());
161 }
162}