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 {}