Skip to main content

torsh_cluster/
error.rs

1//! Error types for clustering operations
2
3use thiserror::Error;
4use torsh_core::error::TorshError;
5
6/// Result type for clustering operations
7pub type ClusterResult<T> = Result<T, ClusterError>;
8
9/// Errors that can occur during clustering operations
10#[derive(Error, Debug)]
11pub enum ClusterError {
12    /// Invalid number of clusters
13    #[error("Invalid number of clusters: {0}. Must be positive and less than number of samples")]
14    InvalidClusters(usize),
15
16    /// Invalid input data
17    #[error("Invalid input data: {0}")]
18    InvalidInput(String),
19
20    /// Convergence failure
21    #[error("Algorithm failed to converge after {max_iters} iterations")]
22    ConvergenceFailure { max_iters: usize },
23
24    /// Empty dataset
25    #[error("Dataset is empty")]
26    EmptyDataset,
27
28    /// Insufficient data points
29    #[error("Insufficient data points: need at least {required}, got {actual}")]
30    InsufficientData { required: usize, actual: usize },
31
32    /// Invalid distance metric
33    #[error("Invalid distance metric: {0}")]
34    InvalidDistanceMetric(String),
35
36    /// Invalid linkage criterion
37    #[error("Invalid linkage criterion: {0}")]
38    InvalidLinkage(String),
39
40    /// Invalid epsilon parameter for DBSCAN
41    #[error("Invalid epsilon parameter: {0}. Must be positive")]
42    InvalidEpsilon(f64),
43
44    /// Invalid minimum samples parameter
45    #[error("Invalid minimum samples: {0}. Must be positive")]
46    InvalidMinSamples(usize),
47
48    /// Invalid covariance type for Gaussian Mixture
49    #[error("Invalid covariance type: {0}")]
50    InvalidCovarianceType(String),
51
52    /// Singular matrix error
53    #[error("Singular matrix encountered during computation")]
54    SingularMatrix,
55
56    /// Tensor operation error
57    #[error("Tensor operation failed: {0}")]
58    TensorError(#[from] TorshError),
59
60    /// SciRS2 core error
61    #[error("SciRS2 core error: {0}")]
62    SciRS2Error(String),
63
64    /// Invalid initialization method
65    #[error("Invalid initialization method: {0}")]
66    InvalidInitialization(String),
67
68    /// Invalid affinity matrix
69    #[error("Invalid affinity matrix: {0}")]
70    InvalidAffinityMatrix(String),
71
72    /// Memory allocation error
73    #[error("Memory allocation failed: {0}")]
74    MemoryError(String),
75
76    /// Invalid feature dimension
77    #[error("Invalid feature dimension: expected {expected}, got {actual}")]
78    InvalidFeatureDimension { expected: usize, actual: usize },
79
80    /// Invalid cluster assignment
81    #[error("Invalid cluster assignment: {0}")]
82    InvalidAssignment(String),
83
84    /// Numerical instability
85    #[error("Numerical instability detected: {0}")]
86    NumericalInstability(String),
87
88    /// Configuration error
89    #[error("Configuration error: {0}")]
90    ConfigError(String),
91
92    /// Algorithm not implemented
93    #[error("Algorithm not implemented: {0}")]
94    NotImplemented(String),
95}
96
97impl ClusterError {
98    /// Create a new SciRS2 error
99    pub fn scirs2_error(msg: impl Into<String>) -> Self {
100        Self::SciRS2Error(msg.into())
101    }
102
103    /// Create a new invalid input error
104    pub fn invalid_input(msg: impl Into<String>) -> Self {
105        Self::InvalidInput(msg.into())
106    }
107
108    /// Create a new configuration error
109    pub fn config_error(msg: impl Into<String>) -> Self {
110        Self::ConfigError(msg.into())
111    }
112
113    /// Create a new numerical instability error
114    pub fn numerical_instability(msg: impl Into<String>) -> Self {
115        Self::NumericalInstability(msg.into())
116    }
117
118    /// Check if error is recoverable
119    pub fn is_recoverable(&self) -> bool {
120        matches!(
121            self,
122            ClusterError::ConvergenceFailure { .. }
123                | ClusterError::NumericalInstability(_)
124                | ClusterError::ConfigError(_)
125        )
126    }
127
128    /// Get error severity level
129    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/// Error severity levels
161#[derive(Debug, Clone, Copy, PartialEq, Eq)]
162pub enum ErrorSeverity {
163    Low,
164    Medium,
165    High,
166    Critical,
167}
168
169/// Context information for clustering errors
170#[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    /// Create a new error context
181    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    /// Set data shape
192    pub fn with_data_shape(mut self, shape: Vec<usize>) -> Self {
193        self.data_shape = Some(shape);
194        self
195    }
196
197    /// Set number of clusters
198    pub fn with_n_clusters(mut self, n_clusters: usize) -> Self {
199        self.n_clusters = Some(n_clusters);
200        self
201    }
202
203    /// Set iteration number
204    pub fn with_iteration(mut self, iteration: usize) -> Self {
205        self.iteration = Some(iteration);
206        self
207    }
208
209    /// Set additional information
210    pub fn with_info(mut self, info: impl Into<String>) -> Self {
211        self.additional_info = Some(info.into());
212        self
213    }
214}