Skip to main content

oxicuda_quant/
error.rs

1//! # Error types for `oxicuda-quant`
2
3use thiserror::Error;
4
5/// Alias for `Result<T, QuantError>`.
6pub type QuantResult<T> = Result<T, QuantError>;
7
8/// All errors that may be produced by `oxicuda-quant`.
9#[derive(Debug, Error, Clone, PartialEq)]
10pub enum QuantError {
11    /// Tensor dimension does not match the expected value.
12    #[error("dimension mismatch: expected {expected}, got {got}")]
13    DimensionMismatch { expected: usize, got: usize },
14
15    /// The input slice is empty when at least one element is required.
16    #[error("empty input: {0}")]
17    EmptyInput(&'static str),
18
19    /// A quantization scale is zero or negative (would produce NaN/Inf).
20    #[error("invalid scale {scale}: must be finite and positive")]
21    InvalidScale { scale: f32 },
22
23    /// A quantization bit-width is outside the valid range [1, 16].
24    #[error("invalid bit-width {bits}: must be in [1, 16]")]
25    InvalidBitWidth { bits: u32 },
26
27    /// The requested group size does not evenly divide the tensor length.
28    #[error("tensor length {len} is not divisible by group size {group}")]
29    GroupSizeMismatch { len: usize, group: usize },
30
31    /// A calibration dataset is required but was not provided.
32    #[error("calibration data required for {0}")]
33    CalibrationRequired(&'static str),
34
35    /// The Hessian matrix is singular or near-singular.
36    #[error("Hessian is near-singular (min diagonal = {min_diag:.3e})")]
37    SingularHessian { min_diag: f32 },
38
39    /// Distillation teacher and student outputs have incompatible shapes.
40    #[error("teacher output length {teacher} != student output length {student}")]
41    TeacherStudentMismatch { teacher: usize, student: usize },
42
43    /// A pruning threshold produced a 100% sparse tensor (no weights remain).
44    #[error("pruning threshold {threshold:.4} zeroed all {n} weights")]
45    AllZeroPruning { threshold: f32, n: usize },
46
47    /// FP8 format encoding received a NaN/Inf value.
48    #[error("FP8 encoding received non-finite value {0}")]
49    NonFiniteFp8(f32),
50
51    /// Mixed-precision policy could not satisfy the requested compression ratio.
52    #[error("cannot achieve target ratio {target:.2}× with given sensitivity")]
53    InfeasibleCompressionTarget { target: f32 },
54
55    /// Generic configuration error.
56    #[error("invalid configuration: {0}")]
57    InvalidConfig(String),
58}
59
60// ─── Tests ───────────────────────────────────────────────────────────────────
61
62#[cfg(test)]
63mod tests {
64    use super::*;
65
66    #[test]
67    fn error_display_dimension_mismatch() {
68        let e = QuantError::DimensionMismatch {
69            expected: 4,
70            got: 3,
71        };
72        assert!(e.to_string().contains("dimension mismatch"));
73    }
74
75    #[test]
76    fn error_display_invalid_scale() {
77        let e = QuantError::InvalidScale { scale: -0.5 };
78        assert!(e.to_string().contains("invalid scale"));
79    }
80
81    #[test]
82    fn error_display_group_size() {
83        let e = QuantError::GroupSizeMismatch { len: 10, group: 3 };
84        assert!(e.to_string().contains("not divisible"));
85    }
86
87    #[test]
88    fn result_alias_ok() {
89        let r: QuantResult<i32> = Ok(42);
90        assert!(r.is_ok());
91    }
92}