use smallvec::SmallVec;
const INLINE_RANK: usize = 8;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Shape {
dims: smallvec::SmallVec<[usize; INLINE_RANK]>,
}
impl Shape {
#[must_use]
pub fn from_dims(dims: &[usize]) -> Self {
Self {
dims: dims.iter().copied().collect(),
}
}
#[must_use]
pub fn rank(&self) -> usize {
self.dims.len()
}
#[must_use]
pub fn numel(&self) -> usize {
self.dims.iter().product()
}
#[must_use]
pub fn dims(&self) -> &[usize] {
&self.dims
}
#[must_use]
#[allow(clippy::unnecessary_cast)]
pub fn row_major_strides(&self) -> Strides {
let rank = self.dims.len();
if rank == 0 {
return Strides {
strides: smallvec::SmallVec::new(),
};
}
let mut actual_strides = smallvec::smallvec![0usize; rank];
actual_strides[rank - 1] = 1; for i in (0..rank - 1).rev() {
let (result, overflow) =
(actual_strides[i + 1] as usize).overflowing_mul(self.dims[i + 1]);
actual_strides[i] = if overflow { usize::MAX } else { result };
}
let mut final_strides = actual_strides;
for (i, &d) in self.dims.iter().enumerate() {
if d == 1 {
final_strides[i] = 0;
}
}
Strides {
strides: final_strides,
}
}
#[must_use]
pub fn broadcast_compatible(&self, other: &Self) -> bool {
let a = self.dims();
let b = other.dims();
let len = a.len().max(b.len());
for i in 0..len {
let dim_idx_from_right = len - 1 - i;
let da = if dim_idx_from_right < a.len() {
a[a.len() - 1 - dim_idx_from_right]
} else {
1
};
let db = if dim_idx_from_right < b.len() {
b[b.len() - 1 - dim_idx_from_right]
} else {
1
};
if da != db && da != 1 && db != 1 {
return false;
}
}
true
}
#[must_use]
pub fn broadcast_output(&self, other: &Self) -> Option<Self> {
let a = self.dims();
let b = other.dims();
let len = a.len().max(b.len());
let mut out = smallvec::SmallVec::<[usize; INLINE_RANK]>::with_capacity(len);
for i in 0..len {
let da = if i + a.len() < len {
1
} else {
a[i + a.len() - len]
};
let db = if i + b.len() < len {
1
} else {
b[i + b.len() - len]
};
if da != db && da != 1 && db != 1 {
return None; }
out.push(da.max(db));
}
Some(Self { dims: out })
}
#[must_use]
pub fn scalar() -> Self {
Self {
dims: smallvec::SmallVec::new(),
}
}
#[must_use]
pub fn vector(n: usize) -> Self {
Self::from_dims(&[n])
}
#[must_use]
pub fn matrix(rows: usize, cols: usize) -> Self {
Self::from_dims(&[rows, cols])
}
}
impl core::fmt::Display for Shape {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "[")?;
for (i, d) in self.dims.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{d}")?;
}
write!(f, "]")
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Strides {
strides: smallvec::SmallVec<[usize; INLINE_RANK]>,
}
impl Strides {
#[must_use]
pub fn as_slice(&self) -> &[usize] {
&self.strides
}
#[must_use]
pub fn get(&self, dim: usize) -> Option<usize> {
self.strides.get(dim).copied()
}
#[must_use]
pub fn checked_flat_index(&self, idx: &[usize]) -> Option<usize> {
debug_assert_eq!(
idx.len(),
self.strides.len(),
"Rank mismatch for checked_flat_index"
);
let mut flat = 0usize;
for (&i, &s) in idx.iter().zip(self.strides.iter()) {
let term = i.checked_mul(s)?; flat = flat.checked_add(term)?; }
Some(flat)
}
#[must_use]
pub fn flat_index(&self, idx: &[usize]) -> usize {
assert_eq!(idx.len(), self.strides.len(), "Index rank mismatch");
self.checked_flat_index(idx)
.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.")
}
#[must_use]
pub fn max_flat_index(&self, shape: &Shape) -> usize {
assert_eq!(
self.strides.len(),
shape.rank(),
"Rank mismatch for Strides::max_flat_index"
);
let max_idx_components: SmallVec<[usize; INLINE_RANK]> =
shape.dims().iter().map(|&d| d.saturating_sub(1)).collect();
self.checked_flat_index(&max_idx_components)
.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.")
}
} #[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_row_major_strides_2d() {
let s = Shape::matrix(3, 4);
let strides = s.row_major_strides();
assert_eq!(strides.as_slice(), &[4, 1]);
}
#[test]
fn test_broadcast_strides() {
let s = Shape::from_dims(&[1, 4]);
let strides = s.row_major_strides();
assert_eq!(strides.as_slice()[0], 0);
assert_eq!(strides.as_slice()[1], 1);
}
#[test]
fn test_broadcast_output() {
let a = Shape::from_dims(&[3, 1]);
let b = Shape::from_dims(&[1, 4]);
let out = a.broadcast_output(&b).unwrap();
assert_eq!(out.dims(), &[3, 4]);
}
#[test]
fn test_numel() {
let s = Shape::matrix(3, 4);
assert_eq!(s.numel(), 12);
}
#[test]
fn test_flat_index() {
let s = Shape::matrix(3, 4);
let strides = s.row_major_strides();
assert_eq!(strides.flat_index(&[1, 2]), 6);
}
}