Skip to main content

strided_view/
lib.rs

1//! Device-agnostic strided view types and metadata operations.
2//!
3//! This crate is a Rust port of Julia's [StridedViews.jl](https://github.com/Jutho/StridedViews.jl),
4//! providing strided multidimensional array view types with zero-copy metadata transformations.
5//!
6//! # Core Types
7//!
8//! - [`StridedView`] / [`StridedViewMut`]: Dynamic-rank strided views over existing data
9//! - [`StridedArray`]: Owned strided multidimensional array
10//! - [`ElementOp`] trait and implementations ([`Identity`], [`Conj`], [`Transpose`], [`Adjoint`]):
11//!   Type-level element operations applied lazily on access
12//!
13//! # Metadata Transformations
14//!
15//! These operate only on dims/strides/offset and never access the underlying data:
16//! - `permute`: Reorder dimensions
17//! - `transpose_2d`, `adjoint_2d`: 2D matrix transformations
18//! - `conj`: Compose conjugation operation
19//! - `broadcast`: Expand size-1 dimensions
20
21pub mod auxiliary;
22mod element_op;
23pub mod injectivity;
24mod raw;
25pub mod view;
26
27// ============================================================================
28// Inline metadata capacity
29// ============================================================================
30
31/// Maximum rank whose metadata is stored inline without a heap allocation.
32///
33/// [`StridedView`], [`StridedViewMut`] and [`StridedArray`] keep up to this
34/// many dimensions and strides inline in a fixed-size `Copy` buffer. Derived
35/// views,
36/// clones and array constructors are allocation-free at ranks 1 through
37/// `INLINE_RANK`; a higher rank spills to the heap gracefully (never an
38/// error).
39///
40/// # Examples
41///
42/// ```
43/// use strided_view::{StridedView, INLINE_RANK};
44///
45/// assert_eq!(INLINE_RANK, 8);
46/// let data = [0.0f64; 8];
47/// let view = StridedView::<f64>::new(&data, &[2, 2, 2], &[1, 2, 4], 0).unwrap();
48/// assert_eq!(view.ndim(), 3);
49/// ```
50pub const INLINE_RANK: usize = 8;
51
52/// Stride metadata with inline storage for up to [`INLINE_RANK`] dimensions.
53///
54/// Returned by [`view::col_major_strides`] and [`view::row_major_strides`],
55/// so a computed layout at rank `<= INLINE_RANK` does not allocate.
56///
57/// # Examples
58///
59/// ```
60/// use strided_view::StrideVec;
61///
62/// let strides: StrideVec = [1isize, 2, 4].into_iter().collect();
63/// assert_eq!(&strides[..], &[1, 2, 4]);
64/// ```
65pub type StrideVec = tinyvec::TinyVec<[isize; INLINE_RANK]>;
66
67/// Axis metadata with inline storage for up to [`INLINE_RANK`] dimensions.
68///
69/// Returned by [`auxiliary::index_order`].
70///
71/// # Examples
72///
73/// ```
74/// use strided_view::AxisVec;
75///
76/// let axes: AxisVec = [3usize, 1, 2].into_iter().collect();
77/// assert_eq!(&axes[..], &[3, 1, 2]);
78/// ```
79pub type AxisVec = tinyvec::TinyVec<[usize; INLINE_RANK]>;
80
81// ============================================================================
82// Element operations
83// ============================================================================
84pub use element_op::{
85    Adjoint, ComposableElementOp, Compose, Conj, ElementOp, ElementOpApply, Identity, Transpose,
86};
87
88// ============================================================================
89// Layout validation
90// ============================================================================
91pub use injectivity::is_injective_layout;
92#[doc(hidden)]
93pub use injectivity::is_injective_layout_without_alloc;
94
95// ============================================================================
96// View-based types
97// ============================================================================
98pub use raw::{
99    ErasedRawStridedMut, ErasedRawStridedPtr, ErasedRawStridedRef, ErasedRawStridedUninitMut,
100    KernelDType, KernelStorageElement, RawStridedMut, RawStridedRef,
101};
102pub use view::{col_major_strides, row_major_strides, StridedArray, StridedView, StridedViewMut};
103
104// ============================================================================
105// Error types
106// ============================================================================
107
108/// Errors that can occur during strided array operations.
109#[derive(Debug, thiserror::Error)]
110pub enum StridedError {
111    /// Array ranks do not match.
112    #[error("rank mismatch: {0} vs {1}")]
113    RankMismatch(usize, usize),
114
115    /// Array shapes are incompatible for the operation.
116    #[error("shape mismatch: {0:?} vs {1:?}")]
117    ShapeMismatch(Vec<usize>, Vec<usize>),
118
119    /// Invalid axis index for the given array rank.
120    #[error("invalid axis {axis} for rank {rank}")]
121    InvalidAxis { axis: usize, rank: usize },
122
123    /// Stride array length doesn't match dimensions.
124    #[error("stride and dims length mismatch")]
125    StrideLengthMismatch,
126
127    /// Integer overflow while computing array offset.
128    #[error("offset overflow while computing pointer")]
129    OffsetOverflow,
130
131    /// Failed to convert a scalar value for scaling operation.
132    #[error("failed to convert scalar for scaling")]
133    ScalarConversion,
134
135    /// Matrix is not square when a square matrix was required.
136    #[error("non-square matrix: rows={rows}, cols={cols}")]
137    NonSquare { rows: usize, cols: usize },
138
139    /// Mutable output layout maps multiple logical elements to the same memory offset.
140    #[error("mutable output layout is not injective")]
141    NonInjectiveOutputLayout,
142
143    /// Runtime view layout does not match the layout a prepared plan was compiled for.
144    #[error("view layout does not match the compiled plan")]
145    PlanLayoutMismatch,
146
147    /// Runtime dtype does not match the dtype a prepared plan was compiled for.
148    #[error("dtype mismatch: expected {expected}, got {actual}")]
149    DTypeMismatch {
150        expected: &'static str,
151        actual: &'static str,
152    },
153
154    /// A byte buffer length is not a whole number of dtype elements.
155    #[error(
156        "byte length {byte_len} is not a multiple of element size {element_size} for dtype {dtype}"
157    )]
158    ByteLengthMismatch {
159        dtype: &'static str,
160        byte_len: usize,
161        element_size: usize,
162    },
163
164    /// A byte buffer pointer does not satisfy the dtype alignment.
165    #[error("data pointer for dtype {dtype} is not aligned to {alignment} bytes")]
166    DataAlignmentMismatch {
167        dtype: &'static str,
168        alignment: usize,
169    },
170
171    /// A byte buffer for `bool` contains a value that is not a valid Rust bool.
172    #[error("invalid bool byte value {value}")]
173    InvalidBoolByte { value: u8 },
174
175    /// An execution context requested an invalid worker-thread budget.
176    #[error("invalid thread budget {max_threads}")]
177    InvalidThreadBudget { max_threads: usize },
178
179    /// The dtype is recognized but unsupported by the selected operation.
180    #[error("unsupported dtype {dtype}")]
181    UnsupportedDType { dtype: &'static str },
182
183    /// The dtype is recognized, but the selected op is not defined for it.
184    #[error("unsupported op {op} for dtype {dtype}")]
185    UnsupportedOp {
186        op: &'static str,
187        dtype: &'static str,
188    },
189
190    /// A mutable destination overlaps one of the operation inputs.
191    #[error("destination overlaps input {input}")]
192    OverlappingInputOutput { input: usize },
193
194    /// Integer division or remainder encountered a zero divisor.
195    #[error("integer {op} encountered a zero divisor")]
196    IntegerDivisionByZero { op: &'static str },
197
198    /// The operation arity is unsupported by this entry point.
199    #[error("unsupported arity {arity}; maximum supported arity is {max}")]
200    UnsupportedArity { arity: usize, max: usize },
201}
202
203/// Result type for strided array operations.
204pub type Result<T> = std::result::Result<T, StridedError>;