1use thiserror::Error;
4use torsh_core::error::TorshError;
5
6pub type ClusterResult<T> = Result<T, ClusterError>;
8
9#[derive(Error, Debug)]
11pub enum ClusterError {
12 #[error("Invalid number of clusters: {0}. Must be positive and less than number of samples")]
14 InvalidClusters(usize),
15
16 #[error("Invalid input data: {0}")]
18 InvalidInput(String),
19
20 #[error("Algorithm failed to converge after {max_iters} iterations")]
22 ConvergenceFailure { max_iters: usize },
23
24 #[error("Dataset is empty")]
26 EmptyDataset,
27
28 #[error("Insufficient data points: need at least {required}, got {actual}")]
30 InsufficientData { required: usize, actual: usize },
31
32 #[error("Invalid distance metric: {0}")]
34 InvalidDistanceMetric(String),
35
36 #[error("Invalid linkage criterion: {0}")]
38 InvalidLinkage(String),
39
40 #[error("Invalid epsilon parameter: {0}. Must be positive")]
42 InvalidEpsilon(f64),
43
44 #[error("Invalid minimum samples: {0}. Must be positive")]
46 InvalidMinSamples(usize),
47
48 #[error("Invalid covariance type: {0}")]
50 InvalidCovarianceType(String),
51
52 #[error("Singular matrix encountered during computation")]
54 SingularMatrix,
55
56 #[error("Tensor operation failed: {0}")]
58 TensorError(#[from] TorshError),
59
60 #[error("SciRS2 core error: {0}")]
62 SciRS2Error(String),
63
64 #[error("Invalid initialization method: {0}")]
66 InvalidInitialization(String),
67
68 #[error("Invalid affinity matrix: {0}")]
70 InvalidAffinityMatrix(String),
71
72 #[error("Memory allocation failed: {0}")]
74 MemoryError(String),
75
76 #[error("Invalid feature dimension: expected {expected}, got {actual}")]
78 InvalidFeatureDimension { expected: usize, actual: usize },
79
80 #[error("Invalid cluster assignment: {0}")]
82 InvalidAssignment(String),
83
84 #[error("Numerical instability detected: {0}")]
86 NumericalInstability(String),
87
88 #[error("Configuration error: {0}")]
90 ConfigError(String),
91
92 #[error("Algorithm not implemented: {0}")]
94 NotImplemented(String),
95}
96
97impl ClusterError {
98 pub fn scirs2_error(msg: impl Into<String>) -> Self {
100 Self::SciRS2Error(msg.into())
101 }
102
103 pub fn invalid_input(msg: impl Into<String>) -> Self {
105 Self::InvalidInput(msg.into())
106 }
107
108 pub fn config_error(msg: impl Into<String>) -> Self {
110 Self::ConfigError(msg.into())
111 }
112
113 pub fn numerical_instability(msg: impl Into<String>) -> Self {
115 Self::NumericalInstability(msg.into())
116 }
117
118 pub fn is_recoverable(&self) -> bool {
120 matches!(
121 self,
122 ClusterError::ConvergenceFailure { .. }
123 | ClusterError::NumericalInstability(_)
124 | ClusterError::ConfigError(_)
125 )
126 }
127
128 pub fn severity(&self) -> ErrorSeverity {
130 match self {
131 ClusterError::EmptyDataset
132 | ClusterError::SingularMatrix
133 | ClusterError::MemoryError(_) => ErrorSeverity::Critical,
134
135 ClusterError::InvalidClusters(_)
136 | ClusterError::InvalidInput(_)
137 | ClusterError::InvalidDistanceMetric(_)
138 | ClusterError::InvalidLinkage(_)
139 | ClusterError::InvalidEpsilon(_)
140 | ClusterError::InvalidMinSamples(_)
141 | ClusterError::InvalidCovarianceType(_)
142 | ClusterError::InvalidInitialization(_)
143 | ClusterError::InvalidAffinityMatrix(_)
144 | ClusterError::InvalidFeatureDimension { .. }
145 | ClusterError::InvalidAssignment(_)
146 | ClusterError::ConfigError(_) => ErrorSeverity::High,
147
148 ClusterError::ConvergenceFailure { .. } | ClusterError::NumericalInstability(_) => {
149 ErrorSeverity::Medium
150 }
151
152 ClusterError::InsufficientData { .. }
153 | ClusterError::TensorError(_)
154 | ClusterError::SciRS2Error(_)
155 | ClusterError::NotImplemented(_) => ErrorSeverity::Low,
156 }
157 }
158}
159
160#[derive(Debug, Clone, Copy, PartialEq, Eq)]
162pub enum ErrorSeverity {
163 Low,
164 Medium,
165 High,
166 Critical,
167}
168
169#[derive(Debug, Clone)]
171pub struct ClusterErrorContext {
172 pub algorithm: String,
173 pub data_shape: Option<Vec<usize>>,
174 pub n_clusters: Option<usize>,
175 pub iteration: Option<usize>,
176 pub additional_info: Option<String>,
177}
178
179impl ClusterErrorContext {
180 pub fn new(algorithm: impl Into<String>) -> Self {
182 Self {
183 algorithm: algorithm.into(),
184 data_shape: None,
185 n_clusters: None,
186 iteration: None,
187 additional_info: None,
188 }
189 }
190
191 pub fn with_data_shape(mut self, shape: Vec<usize>) -> Self {
193 self.data_shape = Some(shape);
194 self
195 }
196
197 pub fn with_n_clusters(mut self, n_clusters: usize) -> Self {
199 self.n_clusters = Some(n_clusters);
200 self
201 }
202
203 pub fn with_iteration(mut self, iteration: usize) -> Self {
205 self.iteration = Some(iteration);
206 self
207 }
208
209 pub fn with_info(mut self, info: impl Into<String>) -> Self {
211 self.additional_info = Some(info.into());
212 self
213 }
214}