Skip to main content

axonml_core/
error.rs

1//! Error types for the AxonML core crate.
2//!
3//! `Error` enum covers ShapeMismatch, DTypeMismatch, DeviceMismatch,
4//! InvalidDimension, IndexOutOfBounds, AllocationFailed, DeviceNotAvailable,
5//! InvalidOperation, BroadcastError, EmptyTensor, NotContiguous,
6//! GradientError, SerializationError, and InternalError. `Result<T>` alias
7//! is the standard return type for fallible tensor/device operations.
8//!
9//! # File
10//! `crates/axonml-core/src/error.rs`
11//!
12//! # Author
13//! Andrew Jewell Sr. — AutomataNexus LLC
14//! ORCID: 0009-0005-2158-7060
15//!
16//! # Updated
17//! April 14, 2026 11:15 PM EST
18//!
19//! # Disclaimer
20//! Use at own risk. This software is provided "as is", without warranty of any
21//! kind, express or implied. The author and AutomataNexus shall not be held
22//! liable for any damages arising from the use of this software.
23
24use thiserror::Error;
25
26use crate::device::Device;
27use crate::dtype::DType;
28
29// =============================================================================
30// Error Types
31// =============================================================================
32
33/// The main error type for Axonml operations.
34#[derive(Error, Debug, Clone, PartialEq)]
35pub enum Error {
36    /// Shape mismatch between tensors.
37    #[error("Shape mismatch: expected {expected:?}, got {actual:?}")]
38    ShapeMismatch {
39        /// The expected shape.
40        expected: Vec<usize>,
41        /// The actual shape.
42        actual: Vec<usize>,
43    },
44
45    /// Data type mismatch between tensors.
46    #[error("DType mismatch: expected {expected:?}, got {actual:?}")]
47    DTypeMismatch {
48        /// The expected data type.
49        expected: DType,
50        /// The actual data type.
51        actual: DType,
52    },
53
54    /// Device mismatch between tensors.
55    #[error("Device mismatch: expected {expected:?}, got {actual:?}")]
56    DeviceMismatch {
57        /// The expected device.
58        expected: Device,
59        /// The actual device.
60        actual: Device,
61    },
62
63    /// Invalid dimension index.
64    #[error("Invalid dimension: index {index} for tensor with {ndim} dimensions")]
65    InvalidDimension {
66        /// The invalid dimension index.
67        index: i64,
68        /// Number of dimensions in the tensor.
69        ndim: usize,
70    },
71
72    /// Index out of bounds.
73    #[error("Index out of bounds: index {index} for dimension of size {size}")]
74    IndexOutOfBounds {
75        /// The invalid index.
76        index: usize,
77        /// The size of the dimension.
78        size: usize,
79    },
80
81    /// Memory allocation failed.
82    #[error("Memory allocation failed: requested {size} bytes on {device:?}")]
83    AllocationFailed {
84        /// The requested size in bytes.
85        size: usize,
86        /// The device on which allocation failed.
87        device: Device,
88    },
89
90    /// Device not available.
91    #[error("Device not available: {device:?}")]
92    DeviceNotAvailable {
93        /// The unavailable device.
94        device: Device,
95    },
96
97    /// Invalid operation for the given tensor.
98    #[error("Invalid operation: {message}")]
99    InvalidOperation {
100        /// Description of why the operation is invalid.
101        message: String,
102    },
103
104    /// Broadcasting failed between shapes.
105    #[error("Cannot broadcast shapes {shape1:?} and {shape2:?}")]
106    BroadcastError {
107        /// The first shape.
108        shape1: Vec<usize>,
109        /// The second shape.
110        shape2: Vec<usize>,
111    },
112
113    /// Empty tensor error.
114    #[error("Operation not supported on empty tensor")]
115    EmptyTensor,
116
117    /// Contiguous tensor required.
118    #[error("Operation requires contiguous tensor")]
119    NotContiguous,
120
121    /// Gradient computation error.
122    #[error("Gradient error: {message}")]
123    GradientError {
124        /// Description of the gradient error.
125        message: String,
126    },
127
128    /// Serialization/deserialization error.
129    #[error("Serialization error: {message}")]
130    SerializationError {
131        /// Description of the serialization error.
132        message: String,
133    },
134
135    /// Internal error (should not happen).
136    #[error("Internal error: {message}")]
137    InternalError {
138        /// Description of the internal error.
139        message: String,
140    },
141}
142
143// =============================================================================
144// Result Type
145// =============================================================================
146
147/// A specialized Result type for Axonml operations.
148pub type Result<T> = core::result::Result<T, Error>;
149
150// =============================================================================
151// Helper Functions
152// =============================================================================
153
154impl Error {
155    /// Creates a new shape mismatch error.
156    #[must_use]
157    pub fn shape_mismatch(expected: &[usize], actual: &[usize]) -> Self {
158        Self::ShapeMismatch {
159            expected: expected.to_vec(),
160            actual: actual.to_vec(),
161        }
162    }
163
164    /// Creates a new invalid operation error.
165    #[must_use]
166    pub fn invalid_operation(message: impl Into<String>) -> Self {
167        Self::InvalidOperation {
168            message: message.into(),
169        }
170    }
171
172    /// Creates a new internal error.
173    #[must_use]
174    pub fn internal(message: impl Into<String>) -> Self {
175        Self::InternalError {
176            message: message.into(),
177        }
178    }
179}
180
181// =============================================================================
182// Tests
183// =============================================================================
184
185#[cfg(test)]
186mod tests {
187    use super::*;
188
189    #[test]
190    fn test_error_display() {
191        let err = Error::shape_mismatch(&[2, 3], &[2, 4]);
192        assert!(err.to_string().contains("Shape mismatch"));
193    }
194
195    #[test]
196    fn test_error_equality() {
197        let err1 = Error::EmptyTensor;
198        let err2 = Error::EmptyTensor;
199        assert_eq!(err1, err2);
200    }
201}