Skip to main content

nabled_embeddings/
error.rs

1//! Error taxonomy for embedding retrieval compute.
2
3use nabled_core::errors::{IntoNabledError, NabledError, ShapeError};
4use nabled_linalg::matrix::MatrixError;
5use nabled_linalg::vector::VectorError;
6use nabled_ml::pca::PCAError;
7use thiserror::Error;
8
9/// Errors produced by `nabled-embeddings` retrieval compute.
10#[derive(Debug, Clone, PartialEq, Eq, Error)]
11pub enum EmbeddingError {
12    /// Input matrix or vector is empty.
13    #[error("input cannot be empty")]
14    EmptyInput,
15    /// Query and corpus feature dimensions are incompatible, or an output buffer is mis-sized.
16    #[error("input dimensions are incompatible")]
17    DimensionMismatch,
18    /// A metric that requires non-zero norms encountered a zero-norm row.
19    #[error("operation is undefined for zero-norm vectors")]
20    ZeroNorm,
21    /// Caller-provided argument is invalid (for example a non-positive component count).
22    #[error("invalid input: {0}")]
23    InvalidInput(String),
24    /// The underlying matrix decomposition failed to converge.
25    #[error("decomposition failed")]
26    DecompositionFailed,
27}
28
29impl From<VectorError> for EmbeddingError {
30    fn from(value: VectorError) -> Self {
31        match value {
32            VectorError::EmptyInput => EmbeddingError::EmptyInput,
33            VectorError::DimensionMismatch => EmbeddingError::DimensionMismatch,
34            VectorError::ZeroNorm => EmbeddingError::ZeroNorm,
35        }
36    }
37}
38
39impl From<MatrixError> for EmbeddingError {
40    fn from(value: MatrixError) -> Self {
41        match value {
42            MatrixError::EmptyInput => EmbeddingError::EmptyInput,
43            MatrixError::DimensionMismatch => EmbeddingError::DimensionMismatch,
44        }
45    }
46}
47
48impl From<PCAError> for EmbeddingError {
49    fn from(value: PCAError) -> Self {
50        match value {
51            PCAError::EmptyMatrix => EmbeddingError::EmptyInput,
52            PCAError::InvalidInput(message) => EmbeddingError::InvalidInput(message),
53            PCAError::DecompositionFailed => EmbeddingError::DecompositionFailed,
54        }
55    }
56}
57
58impl IntoNabledError for EmbeddingError {
59    fn into_nabled_error(self) -> NabledError {
60        match self {
61            EmbeddingError::EmptyInput => NabledError::Shape(ShapeError::EmptyInput),
62            EmbeddingError::DimensionMismatch => NabledError::Shape(ShapeError::DimensionMismatch),
63            EmbeddingError::ZeroNorm => NabledError::InvalidInput(
64                "operation is undefined for zero-norm vectors".to_string(),
65            ),
66            EmbeddingError::InvalidInput(message) => NabledError::InvalidInput(message),
67            EmbeddingError::DecompositionFailed => NabledError::ConvergenceFailed,
68        }
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75
76    #[test]
77    fn vector_error_maps_to_embedding_error() {
78        assert_eq!(EmbeddingError::from(VectorError::EmptyInput), EmbeddingError::EmptyInput);
79        assert_eq!(
80            EmbeddingError::from(VectorError::DimensionMismatch),
81            EmbeddingError::DimensionMismatch
82        );
83        assert_eq!(EmbeddingError::from(VectorError::ZeroNorm), EmbeddingError::ZeroNorm);
84    }
85
86    #[test]
87    fn matrix_error_maps_to_embedding_error() {
88        assert_eq!(EmbeddingError::from(MatrixError::EmptyInput), EmbeddingError::EmptyInput);
89        assert_eq!(
90            EmbeddingError::from(MatrixError::DimensionMismatch),
91            EmbeddingError::DimensionMismatch
92        );
93    }
94
95    #[test]
96    fn pca_error_maps_to_embedding_error() {
97        assert_eq!(EmbeddingError::from(PCAError::EmptyMatrix), EmbeddingError::EmptyInput);
98        assert_eq!(
99            EmbeddingError::from(PCAError::InvalidInput("x".to_string())),
100            EmbeddingError::InvalidInput("x".to_string())
101        );
102        assert_eq!(
103            EmbeddingError::from(PCAError::DecompositionFailed),
104            EmbeddingError::DecompositionFailed
105        );
106    }
107
108    #[test]
109    fn embedding_error_maps_to_shared_taxonomy() {
110        assert!(matches!(
111            EmbeddingError::EmptyInput.into_nabled_error(),
112            NabledError::Shape(ShapeError::EmptyInput)
113        ));
114        assert!(matches!(
115            EmbeddingError::DimensionMismatch.into_nabled_error(),
116            NabledError::Shape(ShapeError::DimensionMismatch)
117        ));
118        assert!(matches!(
119            EmbeddingError::ZeroNorm.into_nabled_error(),
120            NabledError::InvalidInput(_)
121        ));
122        assert!(matches!(
123            EmbeddingError::InvalidInput("x".to_string()).into_nabled_error(),
124            NabledError::InvalidInput(_)
125        ));
126        assert!(matches!(
127            EmbeddingError::DecompositionFailed.into_nabled_error(),
128            NabledError::ConvergenceFailed
129        ));
130    }
131
132    #[test]
133    fn embedding_error_displays_messages() {
134        assert_eq!(EmbeddingError::EmptyInput.to_string(), "input cannot be empty");
135        assert_eq!(
136            EmbeddingError::DimensionMismatch.to_string(),
137            "input dimensions are incompatible"
138        );
139        assert_eq!(
140            EmbeddingError::ZeroNorm.to_string(),
141            "operation is undefined for zero-norm vectors"
142        );
143        assert_eq!(
144            EmbeddingError::InvalidInput("oops".to_string()).to_string(),
145            "invalid input: oops"
146        );
147        assert_eq!(EmbeddingError::DecompositionFailed.to_string(), "decomposition failed");
148    }
149}