sklears_svm/
errors.rs

1//! Comprehensive Error Types for SVM Operations
2//!
3//! This module provides a unified error handling system for all SVM operations,
4//! including training, prediction, kernel computation, and various specialized algorithms.
5//! All errors provide detailed context, suggestions for resolution, and appropriate
6//! error codes for programmatic handling.
7
8use thiserror::Error;
9
10/// Main SVM Error type that encompasses all possible SVM operation failures
11#[derive(Error, Debug, Clone, PartialEq)]
12pub enum SVMError {
13    // === Data and Input Validation Errors ===
14    /// Invalid input data dimensions or format
15    #[error("Invalid input data: {message}")]
16    InvalidInput {
17        message: String,
18        suggestion: Option<String>,
19    },
20
21    /// Dimension mismatch between arrays
22    #[error("Dimension mismatch in {context}: expected {expected_str}, got {actual_str}")]
23    DimensionMismatch {
24        expected: Vec<usize>,
25        actual: Vec<usize>,
26        context: String,
27        expected_str: String,
28        actual_str: String,
29    },
30
31    /// Empty dataset provided
32    #[error("Empty dataset: cannot train on zero samples")]
33    EmptyDataset { context: String },
34
35    /// Invalid labels (e.g., non-finite, wrong format)
36    #[error("Invalid labels: {reason}")]
37    InvalidLabels { reason: String, suggestion: String },
38
39    /// Insufficient data for training
40    #[error("Insufficient data: need at least {required} samples, got {actual}")]
41    InsufficientData {
42        required: usize,
43        actual: usize,
44        context: String,
45    },
46
47    // === Hyperparameter and Configuration Errors ===
48    /// Invalid hyperparameters
49    #[error("Invalid hyperparameter '{parameter}': {reason}")]
50    InvalidHyperparameter {
51        parameter: String,
52        value: String,
53        reason: String,
54        valid_range: Option<String>,
55    },
56
57    /// Invalid kernel configuration
58    #[error("Invalid kernel configuration: {message}")]
59    InvalidKernel {
60        kernel_type: String,
61        message: String,
62        suggestion: String,
63    },
64
65    /// Invalid solver configuration
66    #[error("Invalid solver configuration for {solver}: {reason}")]
67    InvalidSolver {
68        solver: String,
69        reason: String,
70        compatible_solvers: Vec<String>,
71    },
72
73    // === Training and Optimization Errors ===
74    /// Training failed to converge
75    #[error("Training failed to converge after {iterations} iterations")]
76    ConvergenceFailure {
77        iterations: usize,
78        final_objective: Option<f64>,
79        tolerance: f64,
80        suggestions: Vec<String>,
81    },
82
83    /// Numerical instability during training
84    #[error("Numerical instability detected: {issue}")]
85    NumericalInstability {
86        issue: String,
87        context: String,
88        suggestions: Vec<String>,
89    },
90
91    /// Optimization algorithm failure
92    #[error("Optimization algorithm '{algorithm}' failed: {reason}")]
93    OptimizationFailure {
94        algorithm: String,
95        reason: String,
96        iteration: Option<usize>,
97        objective_value: Option<f64>,
98    },
99
100    /// Infeasible optimization problem
101    #[error("Optimization problem is infeasible: {reason}")]
102    InfeasibleProblem {
103        reason: String,
104        suggestions: Vec<String>,
105    },
106
107    // === Kernel Computation Errors ===
108    /// Kernel matrix computation failed
109    #[error("Kernel matrix computation failed: {reason}")]
110    KernelComputationError {
111        kernel_type: String,
112        reason: String,
113        sample_indices: Option<Vec<usize>>,
114    },
115
116    /// Kernel matrix is not positive semidefinite
117    #[error("Kernel matrix is not positive semidefinite")]
118    NonPositiveSemidefiniteKernel {
119        eigenvalue_info: Option<String>,
120        suggestions: Vec<String>,
121    },
122
123    /// Kernel cache errors
124    #[error("Kernel cache error: {operation} failed")]
125    KernelCacheError {
126        operation: String,
127        reason: String,
128        memory_usage: Option<usize>,
129    },
130
131    // === Memory and Resource Errors ===
132    /// Out of memory during computation
133    #[error("Out of memory during {operation}")]
134    OutOfMemory {
135        operation: String,
136        requested_bytes: Option<usize>,
137        available_bytes: Option<usize>,
138        suggestions: Vec<String>,
139    },
140
141    /// Insufficient computational resources
142    #[error("Insufficient computational resources: {resource}")]
143    InsufficientResources {
144        resource: String,
145        required: String,
146        available: String,
147    },
148
149    /// Memory allocation failed
150    #[error("Memory allocation failed for {purpose}: {reason}")]
151    AllocationError {
152        purpose: String,
153        reason: String,
154        size_bytes: Option<usize>,
155    },
156
157    // === Prediction and Model State Errors ===
158    /// Model not trained
159    #[error("Model not trained: call fit() before predict()")]
160    ModelNotTrained {
161        operation: String,
162        suggestions: Vec<String>,
163    },
164
165    /// Prediction failed
166    #[error("Prediction failed: {reason}")]
167    PredictionError {
168        reason: String,
169        sample_count: Option<usize>,
170        context: String,
171    },
172
173    /// Model state inconsistency
174    #[error("Model state inconsistency detected: {issue}")]
175    ModelStateInconsistency { issue: String, context: String },
176
177    // === Parallel Processing Errors ===
178    /// Parallel processing error
179    #[error("Parallel processing error in {operation}: {reason}")]
180    ParallelProcessingError {
181        operation: String,
182        reason: String,
183        thread_count: Option<usize>,
184    },
185
186    /// Thread synchronization error
187    #[error("Thread synchronization error: {details}")]
188    SynchronizationError { details: String, operation: String },
189
190    // === GPU and Hardware Acceleration Errors ===
191    /// GPU computation error
192    #[error("GPU computation error: {reason}")]
193    GpuError {
194        reason: String,
195        device_info: Option<String>,
196        fallback_available: bool,
197    },
198
199    /// SIMD operation error
200    #[error("SIMD operation error: {operation} failed")]
201    SimdError {
202        operation: String,
203        reason: String,
204        fallback_used: bool,
205    },
206
207    // === I/O and Serialization Errors ===
208    /// Model serialization/deserialization error
209    #[error("Model serialization error: {operation}")]
210    SerializationError {
211        operation: String,
212        reason: String,
213        format: String,
214    },
215
216    /// File I/O error during model operations
217    #[error("File I/O error: {operation}")]
218    IoError {
219        operation: String,
220        path: Option<String>,
221        reason: String,
222    },
223
224    // === Cross-validation and Model Selection Errors ===
225    /// Cross-validation error
226    #[error("Cross-validation error: {reason}")]
227    CrossValidationError {
228        fold: Option<usize>,
229        reason: String,
230        total_folds: Option<usize>,
231    },
232
233    /// Hyperparameter optimization error
234    #[error("Hyperparameter optimization failed: {method}")]
235    HyperparameterOptimizationError {
236        method: String,
237        reason: String,
238        iteration: Option<usize>,
239        best_score: Option<f64>,
240    },
241
242    // === Multi-class and Specialized SVM Errors ===
243    /// Multi-class strategy error
244    #[error("Multi-class strategy '{strategy}' error: {reason}")]
245    MultiClassError {
246        strategy: String,
247        reason: String,
248        class_count: Option<usize>,
249    },
250
251    /// Multi-label SVM error
252    #[error("Multi-label SVM error: {reason}")]
253    MultiLabelError {
254        reason: String,
255        label_indices: Option<Vec<usize>>,
256    },
257
258    /// Structured SVM error
259    #[error("Structured SVM error: {reason}")]
260    StructuredSVMError {
261        reason: String,
262        sequence_info: Option<String>,
263    },
264
265    // === External Integration Errors ===
266    /// Topic modeling integration error
267    #[error("Topic modeling error: {reason}")]
268    TopicModelingError {
269        model_type: String,
270        reason: String,
271        topic_count: Option<usize>,
272    },
273
274    /// Text processing error
275    #[error("Text processing error: {operation}")]
276    TextProcessingError {
277        operation: String,
278        reason: String,
279        document_index: Option<usize>,
280    },
281
282    /// Computer vision kernel error
283    #[error("Computer vision kernel error: {kernel_type}")]
284    ComputerVisionError {
285        kernel_type: String,
286        reason: String,
287        image_dimensions: Option<(usize, usize)>,
288    },
289
290    // === Generic and Unknown Errors ===
291    /// Internal error (should not normally occur)
292    #[error("Internal error: {message}")]
293    InternalError {
294        message: String,
295        location: String,
296        debug_info: Option<String>,
297    },
298
299    /// Unknown or unexpected error
300    #[error("Unknown error occurred: {message}")]
301    Unknown { message: String, context: String },
302}
303
304/// Result type for SVM operations
305pub type SVMResult<T> = Result<T, SVMError>;
306
307/// Error severity levels for logging and handling
308#[derive(Debug, Clone, Copy, PartialEq, Eq)]
309pub enum ErrorSeverity {
310    /// Low severity - operation can continue or has fallback
311    Low,
312    /// Medium severity - operation failed but recovery possible
313    Medium,
314    /// High severity - operation failed, no recovery possible
315    High,
316    /// Critical severity - system state may be compromised
317    Critical,
318}
319
320/// Error context for providing additional debugging information
321#[derive(Debug, Clone)]
322pub struct ErrorContext {
323    pub operation: String,
324    pub timestamp: std::time::SystemTime,
325    pub severity: ErrorSeverity,
326    pub metadata: std::collections::HashMap<String, String>,
327}
328
329impl SVMError {
330    /// Get the severity level of this error
331    pub fn severity(&self) -> ErrorSeverity {
332        match self {
333            // Data validation errors are usually medium severity
334            SVMError::InvalidInput { .. } => ErrorSeverity::Medium,
335            SVMError::DimensionMismatch { .. } => ErrorSeverity::Medium,
336            SVMError::EmptyDataset { .. } => ErrorSeverity::High,
337            SVMError::InvalidLabels { .. } => ErrorSeverity::Medium,
338            SVMError::InsufficientData { .. } => ErrorSeverity::High,
339
340            // Configuration errors are medium severity
341            SVMError::InvalidHyperparameter { .. } => ErrorSeverity::Medium,
342            SVMError::InvalidKernel { .. } => ErrorSeverity::Medium,
343            SVMError::InvalidSolver { .. } => ErrorSeverity::Medium,
344
345            // Training errors vary in severity
346            SVMError::ConvergenceFailure { .. } => ErrorSeverity::Medium,
347            SVMError::NumericalInstability { .. } => ErrorSeverity::High,
348            SVMError::OptimizationFailure { .. } => ErrorSeverity::High,
349            SVMError::InfeasibleProblem { .. } => ErrorSeverity::High,
350
351            // Kernel errors are usually high severity
352            SVMError::KernelComputationError { .. } => ErrorSeverity::High,
353            SVMError::NonPositiveSemidefiniteKernel { .. } => ErrorSeverity::High,
354            SVMError::KernelCacheError { .. } => ErrorSeverity::Medium,
355
356            // Memory errors are critical
357            SVMError::OutOfMemory { .. } => ErrorSeverity::Critical,
358            SVMError::InsufficientResources { .. } => ErrorSeverity::Critical,
359            SVMError::AllocationError { .. } => ErrorSeverity::Critical,
360
361            // Prediction errors vary
362            SVMError::ModelNotTrained { .. } => ErrorSeverity::High,
363            SVMError::PredictionError { .. } => ErrorSeverity::Medium,
364            SVMError::ModelStateInconsistency { .. } => ErrorSeverity::Critical,
365
366            // Parallel processing errors are medium
367            SVMError::ParallelProcessingError { .. } => ErrorSeverity::Medium,
368            SVMError::SynchronizationError { .. } => ErrorSeverity::High,
369
370            // Hardware errors have fallbacks so are low-medium
371            SVMError::GpuError {
372                fallback_available: true,
373                ..
374            } => ErrorSeverity::Low,
375            SVMError::GpuError {
376                fallback_available: false,
377                ..
378            } => ErrorSeverity::High,
379            SVMError::SimdError {
380                fallback_used: true,
381                ..
382            } => ErrorSeverity::Low,
383            SVMError::SimdError {
384                fallback_used: false,
385                ..
386            } => ErrorSeverity::Medium,
387
388            // I/O errors are medium-high
389            SVMError::SerializationError { .. } => ErrorSeverity::Medium,
390            SVMError::IoError { .. } => ErrorSeverity::Medium,
391
392            // Model selection errors are medium
393            SVMError::CrossValidationError { .. } => ErrorSeverity::Medium,
394            SVMError::HyperparameterOptimizationError { .. } => ErrorSeverity::Medium,
395
396            // Specialized SVM errors are medium-high
397            SVMError::MultiClassError { .. } => ErrorSeverity::Medium,
398            SVMError::MultiLabelError { .. } => ErrorSeverity::Medium,
399            SVMError::StructuredSVMError { .. } => ErrorSeverity::Medium,
400
401            // Integration errors are low-medium
402            SVMError::TopicModelingError { .. } => ErrorSeverity::Medium,
403            SVMError::TextProcessingError { .. } => ErrorSeverity::Medium,
404            SVMError::ComputerVisionError { .. } => ErrorSeverity::Medium,
405
406            // Internal and unknown errors are critical
407            SVMError::InternalError { .. } => ErrorSeverity::Critical,
408            SVMError::Unknown { .. } => ErrorSeverity::Critical,
409        }
410    }
411
412    /// Get error code for programmatic handling
413    pub fn error_code(&self) -> u32 {
414        match self {
415            SVMError::InvalidInput { .. } => 1001,
416            SVMError::DimensionMismatch { .. } => 1002,
417            SVMError::EmptyDataset { .. } => 1003,
418            SVMError::InvalidLabels { .. } => 1004,
419            SVMError::InsufficientData { .. } => 1005,
420
421            SVMError::InvalidHyperparameter { .. } => 2001,
422            SVMError::InvalidKernel { .. } => 2002,
423            SVMError::InvalidSolver { .. } => 2003,
424
425            SVMError::ConvergenceFailure { .. } => 3001,
426            SVMError::NumericalInstability { .. } => 3002,
427            SVMError::OptimizationFailure { .. } => 3003,
428            SVMError::InfeasibleProblem { .. } => 3004,
429
430            SVMError::KernelComputationError { .. } => 4001,
431            SVMError::NonPositiveSemidefiniteKernel { .. } => 4002,
432            SVMError::KernelCacheError { .. } => 4003,
433
434            SVMError::OutOfMemory { .. } => 5001,
435            SVMError::InsufficientResources { .. } => 5002,
436            SVMError::AllocationError { .. } => 5003,
437
438            SVMError::ModelNotTrained { .. } => 6001,
439            SVMError::PredictionError { .. } => 6002,
440            SVMError::ModelStateInconsistency { .. } => 6003,
441
442            SVMError::ParallelProcessingError { .. } => 7001,
443            SVMError::SynchronizationError { .. } => 7002,
444
445            SVMError::GpuError { .. } => 8001,
446            SVMError::SimdError { .. } => 8002,
447
448            SVMError::SerializationError { .. } => 9001,
449            SVMError::IoError { .. } => 9002,
450
451            SVMError::CrossValidationError { .. } => 10001,
452            SVMError::HyperparameterOptimizationError { .. } => 10002,
453
454            SVMError::MultiClassError { .. } => 11001,
455            SVMError::MultiLabelError { .. } => 11002,
456            SVMError::StructuredSVMError { .. } => 11003,
457
458            SVMError::TopicModelingError { .. } => 12001,
459            SVMError::TextProcessingError { .. } => 12002,
460            SVMError::ComputerVisionError { .. } => 12003,
461
462            SVMError::InternalError { .. } => 99001,
463            SVMError::Unknown { .. } => 99999,
464        }
465    }
466
467    /// Get suggestions for resolving this error
468    pub fn suggestions(&self) -> Vec<String> {
469        match self {
470            SVMError::ConvergenceFailure { suggestions, .. } => suggestions.clone(),
471            SVMError::NumericalInstability { suggestions, .. } => suggestions.clone(),
472            SVMError::InfeasibleProblem { suggestions, .. } => suggestions.clone(),
473            SVMError::NonPositiveSemidefiniteKernel { suggestions, .. } => suggestions.clone(),
474            SVMError::OutOfMemory { suggestions, .. } => suggestions.clone(),
475            SVMError::ModelNotTrained { suggestions, .. } => suggestions.clone(),
476
477            SVMError::InvalidInput {
478                suggestion: Some(s),
479                ..
480            } => vec![s.clone()],
481            SVMError::InvalidKernel { suggestion, .. } => vec![suggestion.clone()],
482            SVMError::InvalidLabels { suggestion, .. } => vec![suggestion.clone()],
483
484            // Default suggestions for common errors
485            SVMError::DimensionMismatch { .. } => vec![
486                "Check that input arrays have compatible dimensions".to_string(),
487                "Ensure training and test data have same number of features".to_string(),
488            ],
489
490            SVMError::EmptyDataset { .. } => vec![
491                "Provide at least one training sample".to_string(),
492                "Check data loading and preprocessing steps".to_string(),
493            ],
494
495            SVMError::InvalidHyperparameter {
496                valid_range: Some(range),
497                ..
498            } => vec![
499                format!("Use values in range: {}", range),
500                "Check hyperparameter documentation for valid ranges".to_string(),
501            ],
502
503            _ => vec!["Check documentation for this error type".to_string()],
504        }
505    }
506
507    /// Create a detailed error report
508    pub fn detailed_report(&self) -> String {
509        let mut report = format!("SVM Error [Code: {}]\n", self.error_code());
510        report.push_str(&format!("Severity: {:?}\n", self.severity()));
511        report.push_str(&format!("Message: {}\n", self));
512
513        let suggestions = self.suggestions();
514        if !suggestions.is_empty() {
515            report.push_str("\nSuggestions:\n");
516            for (i, suggestion) in suggestions.iter().enumerate() {
517                report.push_str(&format!("  {}. {}\n", i + 1, suggestion));
518            }
519        }
520
521        report
522    }
523}
524
525/// Convenience functions for creating common errors
526impl SVMError {
527    /// Create an invalid input error with suggestions
528    pub fn invalid_input(message: impl Into<String>) -> Self {
529        SVMError::InvalidInput {
530            message: message.into(),
531            suggestion: None,
532        }
533    }
534
535    /// Create an invalid input error with a suggestion
536    pub fn invalid_input_with_suggestion(
537        message: impl Into<String>,
538        suggestion: impl Into<String>,
539    ) -> Self {
540        SVMError::InvalidInput {
541            message: message.into(),
542            suggestion: Some(suggestion.into()),
543        }
544    }
545
546    /// Create a dimension mismatch error
547    pub fn dimension_mismatch(
548        expected: Vec<usize>,
549        actual: Vec<usize>,
550        context: impl Into<String>,
551    ) -> Self {
552        let expected_str = format!("{:?}", expected);
553        let actual_str = format!("{:?}", actual);
554        SVMError::DimensionMismatch {
555            expected,
556            actual,
557            context: context.into(),
558            expected_str,
559            actual_str,
560        }
561    }
562
563    /// Create a convergence failure error
564    pub fn convergence_failure(
565        iterations: usize,
566        tolerance: f64,
567        suggestions: Vec<String>,
568    ) -> Self {
569        SVMError::ConvergenceFailure {
570            iterations,
571            final_objective: None,
572            tolerance,
573            suggestions,
574        }
575    }
576
577    /// Create a model not trained error
578    pub fn model_not_trained(operation: impl Into<String>) -> Self {
579        SVMError::ModelNotTrained {
580            operation: operation.into(),
581            suggestions: vec![
582                "Call fit() method to train the model first".to_string(),
583                "Ensure training completed successfully".to_string(),
584            ],
585        }
586    }
587
588    /// Create an out of memory error with suggestions
589    pub fn out_of_memory(operation: impl Into<String>) -> Self {
590        SVMError::OutOfMemory {
591            operation: operation.into(),
592            requested_bytes: None,
593            available_bytes: None,
594            suggestions: vec![
595                "Reduce dataset size or use chunked processing".to_string(),
596                "Increase system memory or use out-of-core algorithms".to_string(),
597                "Consider using LinearSVC for large datasets".to_string(),
598            ],
599        }
600    }
601}
602
603/// Integration with other error types
604impl From<std::io::Error> for SVMError {
605    fn from(err: std::io::Error) -> Self {
606        SVMError::IoError {
607            operation: "file operation".to_string(),
608            path: None,
609            reason: err.to_string(),
610        }
611    }
612}
613
614/// Conversion functions for backward compatibility with existing error types
615impl From<crate::gpu_kernels::GpuKernelError> for SVMError {
616    fn from(err: crate::gpu_kernels::GpuKernelError) -> Self {
617        use crate::gpu_kernels::GpuKernelError::*;
618        match err {
619            DeviceNotAvailable => SVMError::GpuError {
620                reason: "GPU device not available".to_string(),
621                device_info: None,
622                fallback_available: true,
623            },
624            InsufficientMemory => SVMError::GpuError {
625                reason: "Insufficient GPU memory".to_string(),
626                device_info: None,
627                fallback_available: true,
628            },
629            ComputationFailed(msg) => SVMError::GpuError {
630                reason: format!("GPU computation failed: {}", msg),
631                device_info: None,
632                fallback_available: true,
633            },
634            ShaderCompilationFailed(msg) => SVMError::GpuError {
635                reason: format!("Shader compilation failed: {}", msg),
636                device_info: None,
637                fallback_available: false,
638            },
639            BufferCreationFailed => SVMError::GpuError {
640                reason: "Buffer creation failed".to_string(),
641                device_info: None,
642                fallback_available: true,
643            },
644            FeatureNotSupported(feature) => SVMError::GpuError {
645                reason: format!("Feature not supported: {}", feature),
646                device_info: None,
647                fallback_available: true,
648            },
649            DimensionMismatch => SVMError::DimensionMismatch {
650                expected: vec![],
651                actual: vec![],
652                context: "GPU kernel computation".to_string(),
653                expected_str: "[]".to_string(),
654                actual_str: "[]".to_string(),
655            },
656        }
657    }
658}
659
660#[allow(non_snake_case)]
661#[cfg(test)]
662mod tests {
663    use super::*;
664
665    #[test]
666    fn test_error_creation() {
667        let error = SVMError::invalid_input("test message");
668        assert_eq!(error.error_code(), 1001);
669        assert_eq!(error.severity(), ErrorSeverity::Medium);
670    }
671
672    #[test]
673    fn test_error_suggestions() {
674        let error = SVMError::model_not_trained("predict");
675        let suggestions = error.suggestions();
676        assert!(!suggestions.is_empty());
677        assert!(suggestions[0].contains("fit()"));
678    }
679
680    #[test]
681    fn test_detailed_report() {
682        let error = SVMError::convergence_failure(
683            1000,
684            1e-3,
685            vec![
686                "Increase max_iter".to_string(),
687                "Decrease tolerance".to_string(),
688            ],
689        );
690
691        let report = error.detailed_report();
692        assert!(report.contains("Code: 3001"));
693        assert!(report.contains("Severity:"));
694        assert!(report.contains("Suggestions:"));
695        assert!(report.contains("Increase max_iter"));
696    }
697
698    #[test]
699    fn test_dimension_mismatch() {
700        let error = SVMError::dimension_mismatch(vec![100, 50], vec![100, 40], "training data");
701
702        assert_eq!(error.error_code(), 1002);
703        let suggestions = error.suggestions();
704        assert!(suggestions
705            .iter()
706            .any(|s| s.contains("compatible dimensions")));
707    }
708}