rssn_advanced/tensor/
shape.rs1use smallvec::SmallVec;
20
21const INLINE_RANK: usize = 8;
23
24#[derive(Debug, Clone, PartialEq, Eq, Hash)]
30pub struct Shape {
31 dims: smallvec::SmallVec<[usize; INLINE_RANK]>,
33}
34
35impl Shape {
36 #[must_use]
38 pub fn from_dims(dims: &[usize]) -> Self {
39 Self {
40 dims: dims.iter().copied().collect(),
41 }
42 }
43
44 #[must_use]
46 pub fn rank(&self) -> usize {
47 self.dims.len()
48 }
49
50 #[must_use]
54 pub fn numel(&self) -> usize {
55 self.dims.iter().product()
56 }
57
58 #[must_use]
60 pub fn dims(&self) -> &[usize] {
61 &self.dims
62 }
63
64 #[must_use]
72 #[allow(clippy::unnecessary_cast)]
73 pub fn row_major_strides(&self) -> Strides {
74 let rank = self.dims.len();
75 if rank == 0 {
76 return Strides {
77 strides: smallvec::SmallVec::new(),
78 };
79 }
80
81 let mut actual_strides = smallvec::smallvec![0usize; rank];
83 actual_strides[rank - 1] = 1; for i in (0..rank - 1).rev() {
85 let (result, overflow) =
87 (actual_strides[i + 1] as usize).overflowing_mul(self.dims[i + 1]);
88 actual_strides[i] = if overflow { usize::MAX } else { result };
89 }
90
91 let mut final_strides = actual_strides;
93 for (i, &d) in self.dims.iter().enumerate() {
94 if d == 1 {
95 final_strides[i] = 0;
96 }
97 }
98 Strides {
99 strides: final_strides,
100 }
101 }
102
103 #[must_use]
108 pub fn broadcast_compatible(&self, other: &Self) -> bool {
109 let a = self.dims();
110 let b = other.dims();
111 let len = a.len().max(b.len());
112 for i in 0..len {
113 let dim_idx_from_right = len - 1 - i;
114 let da = if dim_idx_from_right < a.len() {
115 a[a.len() - 1 - dim_idx_from_right]
116 } else {
117 1
118 };
119 let db = if dim_idx_from_right < b.len() {
120 b[b.len() - 1 - dim_idx_from_right]
121 } else {
122 1
123 };
124 if da != db && da != 1 && db != 1 {
125 return false;
126 }
127 }
128 true
129 }
130
131 #[must_use]
136 pub fn broadcast_output(&self, other: &Self) -> Option<Self> {
137 let a = self.dims();
138 let b = other.dims();
139 let len = a.len().max(b.len());
140 let mut out = smallvec::SmallVec::<[usize; INLINE_RANK]>::with_capacity(len);
141
142 for i in 0..len {
143 let da = if i + a.len() < len {
145 1
146 } else {
147 a[i + a.len() - len]
148 };
149 let db = if i + b.len() < len {
150 1
151 } else {
152 b[i + b.len() - len]
153 };
154
155 if da != db && da != 1 && db != 1 {
156 return None; }
158 out.push(da.max(db));
159 }
160 Some(Self { dims: out })
161 }
162
163 #[must_use]
165 pub fn scalar() -> Self {
166 Self {
167 dims: smallvec::SmallVec::new(),
168 }
169 }
170
171 #[must_use]
173 pub fn vector(n: usize) -> Self {
174 Self::from_dims(&[n])
175 }
176
177 #[must_use]
179 pub fn matrix(rows: usize, cols: usize) -> Self {
180 Self::from_dims(&[rows, cols])
181 }
182}
183
184impl core::fmt::Display for Shape {
185 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
186 write!(f, "[")?;
187 for (i, d) in self.dims.iter().enumerate() {
188 if i > 0 {
189 write!(f, ", ")?;
190 }
191 write!(f, "{d}")?;
192 }
193 write!(f, "]")
194 }
195}
196
197#[derive(Debug, Clone, PartialEq, Eq, Hash)]
203pub struct Strides {
204 strides: smallvec::SmallVec<[usize; INLINE_RANK]>,
205}
206
207impl Strides {
208 #[must_use]
210 pub fn as_slice(&self) -> &[usize] {
211 &self.strides
212 }
213
214 #[must_use]
216 pub fn get(&self, dim: usize) -> Option<usize> {
217 self.strides.get(dim).copied()
218 }
219
220 #[must_use]
224 pub fn checked_flat_index(&self, idx: &[usize]) -> Option<usize> {
225 debug_assert_eq!(
226 idx.len(),
227 self.strides.len(),
228 "Rank mismatch for checked_flat_index"
229 );
230 let mut flat = 0usize;
231 for (&i, &s) in idx.iter().zip(self.strides.iter()) {
232 let term = i.checked_mul(s)?; flat = flat.checked_add(term)?; }
235 Some(flat)
236 }
237
238 #[must_use]
243 pub fn flat_index(&self, idx: &[usize]) -> usize {
244 assert_eq!(idx.len(), self.strides.len(), "Index rank mismatch");
245 self.checked_flat_index(idx)
246 .expect("Flat index calculation overflowed. This indicates an invalid tensor configuration or an out-of-bounds access that would result in an extremely large flat index.")
247 }
248
249 #[must_use]
256 pub fn max_flat_index(&self, shape: &Shape) -> usize {
257 assert_eq!(
258 self.strides.len(),
259 shape.rank(),
260 "Rank mismatch for Strides::max_flat_index"
261 );
262 let max_idx_components: SmallVec<[usize; INLINE_RANK]> =
263 shape.dims().iter().map(|&d| d.saturating_sub(1)).collect();
264 self.checked_flat_index(&max_idx_components)
265 .expect("Maximum flat index calculation overflowed. This indicates an invalid strides/shape configuration that could lead to out-of-bounds access or silent data corruption.")
266 }
267} #[cfg(test)]
269mod tests {
270 use super::*;
271
272 #[test]
273 fn test_row_major_strides_2d() {
274 let s = Shape::matrix(3, 4);
275 let strides = s.row_major_strides();
276 assert_eq!(strides.as_slice(), &[4, 1]);
277 }
278
279 #[test]
280 fn test_broadcast_strides() {
281 let s = Shape::from_dims(&[1, 4]);
282 let strides = s.row_major_strides();
283 assert_eq!(strides.as_slice()[0], 0);
285 assert_eq!(strides.as_slice()[1], 1);
286 }
287
288 #[test]
289 fn test_broadcast_output() {
290 let a = Shape::from_dims(&[3, 1]);
291 let b = Shape::from_dims(&[1, 4]);
292 let out = a.broadcast_output(&b).unwrap();
293 assert_eq!(out.dims(), &[3, 4]);
294 }
295
296 #[test]
297 fn test_numel() {
298 let s = Shape::matrix(3, 4);
299 assert_eq!(s.numel(), 12);
300 }
301
302 #[test]
303 fn test_flat_index() {
304 let s = Shape::matrix(3, 4);
305 let strides = s.row_major_strides();
306 assert_eq!(strides.flat_index(&[1, 2]), 6);
308 }
309}