mirror of
https://github.com/ruvnet/RuView
synced 2026-08-03 19:21:42 +00:00
feat: vendor midstream and sublinear-time-solver libraries (#109)
Add ruvnet/midstream (AIMDS real-time inference) and ruvnet/sublinear-time-solver (sublinear optimization algorithms) as vendored dependencies under vendor/.
This commit is contained in:
+404
@@ -0,0 +1,404 @@
|
||||
//! Error types and handling for the sublinear solver.
|
||||
//!
|
||||
//! This module defines all error conditions that can occur during matrix operations
|
||||
//! and solver execution, providing detailed error information for debugging and
|
||||
//! recovery strategies.
|
||||
|
||||
use core::fmt;
|
||||
use alloc::{string::String, vec::Vec};
|
||||
|
||||
/// Result type alias for solver operations.
|
||||
pub type Result<T> = core::result::Result<T, SolverError>;
|
||||
|
||||
/// Comprehensive error type for all solver operations.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum SolverError {
|
||||
/// Matrix is not diagonally dominant, which is required for convergence guarantees.
|
||||
MatrixNotDiagonallyDominant {
|
||||
/// The row where diagonal dominance fails
|
||||
row: usize,
|
||||
/// Diagonal element value
|
||||
diagonal: f64,
|
||||
/// Sum of off-diagonal absolute values
|
||||
off_diagonal_sum: f64,
|
||||
},
|
||||
|
||||
/// Numerical instability detected during computation.
|
||||
NumericalInstability {
|
||||
/// Description of the instability
|
||||
reason: String,
|
||||
/// Iteration where instability was detected
|
||||
iteration: usize,
|
||||
/// Current residual norm when instability occurred
|
||||
residual_norm: f64,
|
||||
},
|
||||
|
||||
/// Algorithm failed to converge within specified iterations.
|
||||
ConvergenceFailure {
|
||||
/// Number of iterations performed
|
||||
iterations: usize,
|
||||
/// Final residual norm achieved
|
||||
residual_norm: f64,
|
||||
/// Target tolerance that wasn't reached
|
||||
tolerance: f64,
|
||||
/// Algorithm that failed to converge
|
||||
algorithm: String,
|
||||
},
|
||||
|
||||
/// Invalid input parameters or data.
|
||||
InvalidInput {
|
||||
/// Description of the invalid input
|
||||
message: String,
|
||||
/// Optional parameter name that was invalid
|
||||
parameter: Option<String>,
|
||||
},
|
||||
|
||||
/// Dimension mismatch between matrix and vector operations.
|
||||
DimensionMismatch {
|
||||
/// Expected dimension
|
||||
expected: usize,
|
||||
/// Actual dimension found
|
||||
actual: usize,
|
||||
/// Context where mismatch occurred
|
||||
operation: String,
|
||||
},
|
||||
|
||||
/// Matrix format is not supported for the requested operation.
|
||||
UnsupportedMatrixFormat {
|
||||
/// Current matrix format
|
||||
current_format: String,
|
||||
/// Required format for the operation
|
||||
required_format: String,
|
||||
/// Operation that was attempted
|
||||
operation: String,
|
||||
},
|
||||
|
||||
/// Memory allocation failure.
|
||||
MemoryAllocationError {
|
||||
/// Requested allocation size in bytes
|
||||
requested_size: usize,
|
||||
/// Available memory at time of failure (if known)
|
||||
available_memory: Option<usize>,
|
||||
},
|
||||
|
||||
/// Index out of bounds for matrix or vector access.
|
||||
IndexOutOfBounds {
|
||||
/// The invalid index
|
||||
index: usize,
|
||||
/// Maximum valid index
|
||||
max_index: usize,
|
||||
/// Context where out-of-bounds access occurred
|
||||
context: String,
|
||||
},
|
||||
|
||||
/// Sparse matrix contains invalid data.
|
||||
InvalidSparseMatrix {
|
||||
/// Description of the invalid data
|
||||
reason: String,
|
||||
/// Position where invalid data was found
|
||||
position: Option<(usize, usize)>,
|
||||
},
|
||||
|
||||
/// Algorithm-specific error conditions.
|
||||
AlgorithmError {
|
||||
/// Name of the algorithm
|
||||
algorithm: String,
|
||||
/// Specific error message
|
||||
message: String,
|
||||
/// Additional context data
|
||||
context: Vec<(String, String)>,
|
||||
},
|
||||
|
||||
/// WebAssembly binding error (when WASM feature is enabled).
|
||||
#[cfg(feature = "wasm")]
|
||||
WasmBindingError {
|
||||
/// Error message from WASM binding
|
||||
message: String,
|
||||
/// JavaScript error if available
|
||||
js_error: Option<String>,
|
||||
},
|
||||
|
||||
/// I/O error for file operations (when std feature is enabled).
|
||||
#[cfg(feature = "std")]
|
||||
IoError {
|
||||
/// I/O error description
|
||||
#[cfg_attr(feature = "serde", serde(skip))]
|
||||
message: String,
|
||||
/// Context where I/O error occurred
|
||||
context: String,
|
||||
},
|
||||
|
||||
/// Serialization/deserialization error.
|
||||
#[cfg(feature = "serde")]
|
||||
SerializationError {
|
||||
/// Error message from serialization
|
||||
message: String,
|
||||
/// Data type being serialized
|
||||
data_type: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl SolverError {
|
||||
/// Check if this error indicates a recoverable condition.
|
||||
///
|
||||
/// Recoverable errors can potentially be resolved by adjusting
|
||||
/// algorithm parameters or switching to a different solver.
|
||||
pub fn is_recoverable(&self) -> bool {
|
||||
match self {
|
||||
SolverError::ConvergenceFailure { .. } => true,
|
||||
SolverError::NumericalInstability { .. } => true,
|
||||
SolverError::MatrixNotDiagonallyDominant { .. } => false, // Fundamental issue
|
||||
SolverError::InvalidInput { .. } => false, // User error
|
||||
SolverError::DimensionMismatch { .. } => false, // User error
|
||||
SolverError::MemoryAllocationError { .. } => false, // System limitation
|
||||
SolverError::IndexOutOfBounds { .. } => false, // Programming error
|
||||
SolverError::InvalidSparseMatrix { .. } => false, // Data corruption
|
||||
SolverError::UnsupportedMatrixFormat { .. } => true, // Can convert format
|
||||
SolverError::AlgorithmError { .. } => true, // Algorithm-specific, might recover
|
||||
#[cfg(feature = "wasm")]
|
||||
SolverError::WasmBindingError { .. } => false, // Runtime environment issue
|
||||
#[cfg(feature = "std")]
|
||||
SolverError::IoError { .. } => false, // External system issue
|
||||
#[cfg(feature = "serde")]
|
||||
SolverError::SerializationError { .. } => false, // Data format issue
|
||||
}
|
||||
}
|
||||
|
||||
/// Get suggested recovery strategy for recoverable errors.
|
||||
pub fn recovery_strategy(&self) -> Option<RecoveryStrategy> {
|
||||
match self {
|
||||
SolverError::ConvergenceFailure { algorithm, .. } => {
|
||||
// Suggest alternative algorithms
|
||||
Some(match algorithm.as_str() {
|
||||
"neumann" => RecoveryStrategy::SwitchAlgorithm("hybrid".to_string()),
|
||||
"forward_push" => RecoveryStrategy::SwitchAlgorithm("backward_push".to_string()),
|
||||
"backward_push" => RecoveryStrategy::SwitchAlgorithm("hybrid".to_string()),
|
||||
_ => RecoveryStrategy::RelaxTolerance(10.0),
|
||||
})
|
||||
},
|
||||
SolverError::NumericalInstability { .. } => {
|
||||
Some(RecoveryStrategy::IncreasePrecision)
|
||||
},
|
||||
SolverError::UnsupportedMatrixFormat { required_format, .. } => {
|
||||
Some(RecoveryStrategy::ConvertMatrixFormat(required_format.clone()))
|
||||
},
|
||||
SolverError::AlgorithmError { algorithm, .. } => {
|
||||
Some(RecoveryStrategy::SwitchAlgorithm("neumann".to_string()))
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the error severity level.
|
||||
pub fn severity(&self) -> ErrorSeverity {
|
||||
match self {
|
||||
SolverError::MemoryAllocationError { .. } => ErrorSeverity::Critical,
|
||||
SolverError::InvalidSparseMatrix { .. } => ErrorSeverity::Critical,
|
||||
SolverError::IndexOutOfBounds { .. } => ErrorSeverity::Critical,
|
||||
SolverError::MatrixNotDiagonallyDominant { .. } => ErrorSeverity::High,
|
||||
SolverError::ConvergenceFailure { .. } => ErrorSeverity::Medium,
|
||||
SolverError::NumericalInstability { .. } => ErrorSeverity::Medium,
|
||||
SolverError::InvalidInput { .. } => ErrorSeverity::Medium,
|
||||
SolverError::DimensionMismatch { .. } => ErrorSeverity::Medium,
|
||||
SolverError::UnsupportedMatrixFormat { .. } => ErrorSeverity::Low,
|
||||
SolverError::AlgorithmError { .. } => ErrorSeverity::Medium,
|
||||
#[cfg(feature = "wasm")]
|
||||
SolverError::WasmBindingError { .. } => ErrorSeverity::High,
|
||||
#[cfg(feature = "std")]
|
||||
SolverError::IoError { .. } => ErrorSeverity::Medium,
|
||||
#[cfg(feature = "serde")]
|
||||
SolverError::SerializationError { .. } => ErrorSeverity::Low,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Recovery strategies for recoverable errors.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum RecoveryStrategy {
|
||||
/// Switch to a different solver algorithm.
|
||||
SwitchAlgorithm(String),
|
||||
/// Increase numerical precision (f32 -> f64).
|
||||
IncreasePrecision,
|
||||
/// Relax convergence tolerance by the given factor.
|
||||
RelaxTolerance(f64),
|
||||
/// Restart with different random seed.
|
||||
RestartWithDifferentSeed,
|
||||
/// Convert matrix to a different storage format.
|
||||
ConvertMatrixFormat(String),
|
||||
/// Increase maximum iteration count.
|
||||
IncreaseIterations(usize),
|
||||
}
|
||||
|
||||
/// Error severity levels for logging and monitoring.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum ErrorSeverity {
|
||||
/// Low severity - algorithm can continue with degraded performance
|
||||
Low,
|
||||
/// Medium severity - operation failed but system remains stable
|
||||
Medium,
|
||||
/// High severity - significant failure requiring user intervention
|
||||
High,
|
||||
/// Critical severity - system integrity compromised
|
||||
Critical,
|
||||
}
|
||||
|
||||
impl fmt::Display for SolverError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
SolverError::MatrixNotDiagonallyDominant { row, diagonal, off_diagonal_sum } => {
|
||||
write!(f, "Matrix is not diagonally dominant at row {}: diagonal = {:.6}, off-diagonal sum = {:.6}",
|
||||
row, diagonal, off_diagonal_sum)
|
||||
},
|
||||
SolverError::NumericalInstability { reason, iteration, residual_norm } => {
|
||||
write!(f, "Numerical instability at iteration {}: {} (residual = {:.2e})",
|
||||
iteration, reason, residual_norm)
|
||||
},
|
||||
SolverError::ConvergenceFailure { iterations, residual_norm, tolerance, algorithm } => {
|
||||
write!(f, "Algorithm '{}' failed to converge after {} iterations: residual = {:.2e} > tolerance = {:.2e}",
|
||||
algorithm, iterations, residual_norm, tolerance)
|
||||
},
|
||||
SolverError::InvalidInput { message, parameter } => {
|
||||
match parameter {
|
||||
Some(param) => write!(f, "Invalid input for parameter '{}': {}", param, message),
|
||||
None => write!(f, "Invalid input: {}", message),
|
||||
}
|
||||
},
|
||||
SolverError::DimensionMismatch { expected, actual, operation } => {
|
||||
write!(f, "Dimension mismatch in {}: expected {}, got {}", operation, expected, actual)
|
||||
},
|
||||
SolverError::UnsupportedMatrixFormat { current_format, required_format, operation } => {
|
||||
write!(f, "Operation '{}' requires {} format, but matrix is in {} format",
|
||||
operation, required_format, current_format)
|
||||
},
|
||||
SolverError::MemoryAllocationError { requested_size, available_memory } => {
|
||||
match available_memory {
|
||||
Some(available) => write!(f, "Memory allocation failed: requested {} bytes, {} available",
|
||||
requested_size, available),
|
||||
None => write!(f, "Memory allocation failed: requested {} bytes", requested_size),
|
||||
}
|
||||
},
|
||||
SolverError::IndexOutOfBounds { index, max_index, context } => {
|
||||
write!(f, "Index {} out of bounds in {}: maximum valid index is {}",
|
||||
index, context, max_index)
|
||||
},
|
||||
SolverError::InvalidSparseMatrix { reason, position } => {
|
||||
match position {
|
||||
Some((row, col)) => write!(f, "Invalid sparse matrix at ({}, {}): {}", row, col, reason),
|
||||
None => write!(f, "Invalid sparse matrix: {}", reason),
|
||||
}
|
||||
},
|
||||
SolverError::AlgorithmError { algorithm, message, .. } => {
|
||||
write!(f, "Algorithm '{}' error: {}", algorithm, message)
|
||||
},
|
||||
#[cfg(feature = "wasm")]
|
||||
SolverError::WasmBindingError { message, js_error } => {
|
||||
match js_error {
|
||||
Some(js_err) => write!(f, "WASM binding error: {} (JS: {})", message, js_err),
|
||||
None => write!(f, "WASM binding error: {}", message),
|
||||
}
|
||||
},
|
||||
#[cfg(feature = "std")]
|
||||
SolverError::IoError { message, context } => {
|
||||
write!(f, "I/O error in {}: {}", context, message)
|
||||
},
|
||||
#[cfg(feature = "serde")]
|
||||
SolverError::SerializationError { message, data_type } => {
|
||||
write!(f, "Serialization error for {}: {}", data_type, message)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl std::error::Error for SolverError {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// Conversion from standard library errors
|
||||
#[cfg(feature = "std")]
|
||||
impl From<std::io::Error> for SolverError {
|
||||
fn from(err: std::io::Error) -> Self {
|
||||
SolverError::IoError {
|
||||
message: err.to_string(),
|
||||
context: "File operation".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Conversion for WASM environments
|
||||
#[cfg(feature = "wasm")]
|
||||
impl From<wasm_bindgen::JsValue> for SolverError {
|
||||
fn from(err: wasm_bindgen::JsValue) -> Self {
|
||||
let message = if let Some(string) = err.as_string() {
|
||||
string
|
||||
} else {
|
||||
"Unknown JavaScript error".to_string()
|
||||
};
|
||||
|
||||
SolverError::WasmBindingError {
|
||||
message,
|
||||
js_error: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(test, feature = "std"))]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_error_recoverability() {
|
||||
let convergence_error = SolverError::ConvergenceFailure {
|
||||
iterations: 100,
|
||||
residual_norm: 1e-3,
|
||||
tolerance: 1e-6,
|
||||
algorithm: "neumann".to_string(),
|
||||
};
|
||||
assert!(convergence_error.is_recoverable());
|
||||
|
||||
let dimension_error = SolverError::DimensionMismatch {
|
||||
expected: 100,
|
||||
actual: 50,
|
||||
operation: "matrix_vector_multiply".to_string(),
|
||||
};
|
||||
assert!(!dimension_error.is_recoverable());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_recovery_strategies() {
|
||||
let error = SolverError::ConvergenceFailure {
|
||||
iterations: 100,
|
||||
residual_norm: 1e-3,
|
||||
tolerance: 1e-6,
|
||||
algorithm: "neumann".to_string(),
|
||||
};
|
||||
|
||||
if let Some(RecoveryStrategy::SwitchAlgorithm(algo)) = error.recovery_strategy() {
|
||||
assert_eq!(algo, "hybrid");
|
||||
} else {
|
||||
panic!("Expected SwitchAlgorithm recovery strategy");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_severity() {
|
||||
let memory_error = SolverError::MemoryAllocationError {
|
||||
requested_size: 1000000,
|
||||
available_memory: None,
|
||||
};
|
||||
assert_eq!(memory_error.severity(), ErrorSeverity::Critical);
|
||||
|
||||
let convergence_error = SolverError::ConvergenceFailure {
|
||||
iterations: 100,
|
||||
residual_norm: 1e-3,
|
||||
tolerance: 1e-6,
|
||||
algorithm: "neumann".to_string(),
|
||||
};
|
||||
assert_eq!(convergence_error.severity(), ErrorSeverity::Medium);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user