Skip to main content

candela/tensor/
errors.rs

1/// The error returned by fallible tensor operations.
2///
3/// Operations that can fail at runtime — `view`, `slice`, `matmul`, the axis
4/// reductions, and so on — return `Result<_, OpError>`. The error is produced
5/// when the operation is built, not at `.materialize()`, so a bad shape is
6/// caught at the call site rather than deep inside execution. `OpError`
7/// implements [`Error`](std::error::Error) and [`Display`](std::fmt::Display),
8/// so it composes with `?` and `Box<dyn Error>`.
9///
10/// # Examples
11///
12/// ```
13/// use candela::{OpError, Tensor};
14///
15/// let t = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], &[2, 2]);
16/// // A view must preserve the element count; 3 * 3 = 9 != 4.
17/// assert!(matches!(t.view(&[3, 3]), Err(OpError::InvalidViewShape)));
18/// ```
19#[derive(Debug)]
20#[non_exhaustive]
21pub enum OpError {
22    /// A `view` was requested with a shape whose element count differs from the original.
23    InvalidViewShape,
24    /// A `view` was requested on a non-contiguous tensor; use `reshape` instead.
25    NonContiguousView,
26    /// A slice resolved to more elements than the tensor holds. Carries `(tensor_len, slice_len)`.
27    InvalidSliceShape(usize, usize),
28    /// A slice range is empty — its end is not past its start.
29    SliceOutOfBounds,
30    /// An index passed to `get` is past the end of its axis.
31    IndexOutOfBounds,
32    /// An axis index is out of range, repeated, or there are more axes than the tensor has.
33    AxesOutOfBounds,
34    /// The inner dimensions of a `matmul` don't agree. Carries the two mismatched sizes.
35    CannotMatMul(usize, usize),
36    /// The shapes (or a `broadcast` target) aren't broadcast-compatible.
37    CannotBroadcast,
38    /// An operation received the wrong number of axes or indices. Carries `(expected, got)`.
39    NotEnoughAxes(usize, usize),
40    /// Two tensors in an elementwise op have incompatible shapes. Carries both shapes.
41    NotSameShape(Box<[usize]>, Box<[usize]>),
42    /// Batched `matmul` operands have incompatible batch dimensions. Carries the two batch sizes.
43    NotSameBatch(usize, usize),
44    /// A 0-D shape (`&[]`) was given; tensors must have rank >= 1.
45    ZeroRankShape,
46    // A declared slot was not the same used during construction of the skeleton
47    NotSameSlot(usize),
48    // The amount of slots provided to the skeleton was different than used
49    IncorrectSlotAmount(usize, usize),
50    // The layout of the idx is not the same declared slot layout
51    NotSameLayoutAtSlot(usize),
52}
53
54impl std::fmt::Display for OpError {
55    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56        match self {
57            OpError::InvalidViewShape => write!(
58                f,
59                "the view shape does not have the same size as the original shape"
60            ),
61            OpError::NonContiguousView => write!(
62                f,
63                "the view is non-contiguous. you probably want a reshape instead"
64            ),
65            OpError::SliceOutOfBounds => write!(
66                f,
67                "you cannot reference a slice that access out of bounds memory"
68            ),
69            OpError::IndexOutOfBounds => write!(f, "you cannot reference out of bounds memory",),
70            OpError::InvalidSliceShape(expected, got) => write!(
71                f,
72                "the slice shape is bigger than the original tensor it is slicing. expected {} found {}",
73                expected, got
74            ),
75            OpError::AxesOutOfBounds => {
76                write!(f, "cannot reference out of bounds axes")
77            }
78            OpError::CannotMatMul(expected, got) => {
79                write!(
80                    f,
81                    "cannot matmul. expected the row of the second tensor to be {} found {}",
82                    expected, got
83                )
84            }
85            OpError::CannotBroadcast => {
86                write!(f, "cannot broadcast to that shape")
87            }
88            OpError::NotEnoughAxes(expected, got) => {
89                write!(
90                    f,
91                    "there's not enough axes for this operation. expected {} found {}",
92                    expected, got
93                )
94            }
95            OpError::NotSameShape(expected, got) => {
96                write!(f, "expected {:?}, but got {:?}", *expected, *got)
97            }
98            OpError::NotSameBatch(expected, got) => {
99                write!(
100                    f,
101                    "tensors do not have the same batch dimension. expected {} found {}. use broadcasting if necessary",
102                    expected, got
103                )
104            }
105            OpError::ZeroRankShape => {
106                write!(
107                    f,
108                    "tensor shape must have rank >= 1 (empty shape `&[]` / 0-D tensors are not supported)"
109                )
110            }
111            OpError::NotSameSlot(slot_idx) => {
112                write!(
113                    f,
114                    "slot at idx {} was not used in the skeleton construction",
115                    slot_idx
116                )
117            }
118            OpError::IncorrectSlotAmount(expected, got) => {
119                write!(
120                    f,
121                    "got {} slots binded to the skeleton but expected {}",
122                    got, expected
123                )
124            }
125            OpError::NotSameLayoutAtSlot(slot_idx) => {
126                write!(
127                    f,
128                    "slot at idx {} did not have a compatible layout",
129                    slot_idx
130                )
131            }
132        }
133    }
134}
135
136impl std::error::Error for OpError {}