use thiserror::Error;
pub type KernelPcaResult<T> = std::result::Result<T, KernelPcaError>;
#[derive(Debug, Error)]
pub enum KernelPcaError {
#[error("Kernel PCA model has not been fitted yet: {0}")]
NotFitted(String),
#[error("Invalid input to Kernel PCA: {0}")]
InvalidInput(String),
#[error("Eigendecomposition of the centered Gram matrix failed: {0}")]
EigendecompositionFailed(String),
#[error(
"Dimension mismatch: expected feature dimension {expected}, got {got} (context: {context})"
)]
DimensionMismatch {
expected: usize,
got: usize,
context: String,
},
#[error(
"Requested {requested} components but only {available} positive eigenvalues are available"
)]
InsufficientComponents {
requested: usize,
available: usize,
},
}
impl KernelPcaError {
pub(crate) fn from_kernel(err: crate::error::KernelError) -> Self {
KernelPcaError::InvalidInput(format!("kernel evaluation failed: {}", err))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn not_fitted_display_contains_context() {
let err = KernelPcaError::NotFitted("transform".to_string());
let msg = err.to_string();
assert!(msg.contains("not been fitted"));
assert!(msg.contains("transform"));
}
#[test]
fn dimension_mismatch_display_mentions_both_sides() {
let err = KernelPcaError::DimensionMismatch {
expected: 3,
got: 5,
context: "transform input".to_string(),
};
let msg = err.to_string();
assert!(msg.contains("3"));
assert!(msg.contains("5"));
assert!(msg.contains("transform input"));
}
#[test]
fn insufficient_components_display_is_informative() {
let err = KernelPcaError::InsufficientComponents {
requested: 10,
available: 3,
};
let msg = err.to_string();
assert!(msg.contains("10"));
assert!(msg.contains("3"));
}
#[test]
fn from_kernel_wraps_message() {
let kernel_err = crate::error::KernelError::ComputationError("boom".to_string());
let pca_err = KernelPcaError::from_kernel(kernel_err);
let msg = pca_err.to_string();
assert!(msg.contains("boom"));
}
}