tenferro_tensor_core/rank.rs
1use crate::{IntoShapeVec, Result, ShapeVec, StrideVec, ValidationError};
2use std::fmt::Debug;
3
4/// Convert a shape container into the representation required by a tensor rank.
5///
6/// Dynamic rank accepts any [`IntoShapeVec`] input. For a static [`Rank<N>`],
7/// arrays and array references of exactly `N` elements convert without a
8/// runtime rank check; vectors, slices, and [`ShapeVec`] values are checked by
9/// [`TensorRank::shape_from_vec`].
10///
11/// # Examples
12///
13/// ```rust
14/// use tenferro_tensor_core::{IntoRankShape, Rank};
15///
16/// let shape = <[usize; 2] as IntoRankShape<Rank<2>>>::into_rank_shape([2, 3])?;
17/// assert_eq!(shape, [2, 3]);
18/// # Ok::<(), tenferro_tensor_core::ValidationError>(())
19/// ```
20///
21/// ```compile_fail
22/// use tenferro_tensor_core::{IntoRankShape, Rank};
23///
24/// let _ = <[usize; 2] as IntoRankShape<Rank<3>>>::into_rank_shape([2, 3]);
25/// ```
26pub trait IntoRankShape<R: TensorRank> {
27 /// Convert this shape container into `R`'s shape representation.
28 ///
29 /// # Examples
30 ///
31 /// ```
32 /// use tenferro_tensor_core::{IntoRankShape, Rank};
33 /// let shape = <Vec<usize> as IntoRankShape<Rank<2>>>::into_rank_shape(vec![2, 3])?;
34 /// assert_eq!(shape, [2, 3]);
35 /// # Ok::<(), tenferro_tensor_core::ValidationError>(())
36 /// ```
37 ///
38 /// # Errors
39 ///
40 /// Returns [`ValidationError::RankMismatch`] when a vector or slice has
41 /// a length different from the static rank.
42 fn into_rank_shape(self) -> Result<R::Shape>;
43}
44
45/// Rank contract for tensor metadata shapes and strides.
46///
47/// # Examples
48///
49/// ```rust
50/// use tenferro_tensor_core::{Rank, TensorRank};
51/// let shape = <Rank<2> as TensorRank>::shape_from_vec(vec![2, 3].into())?;
52/// assert_eq!(shape.as_ref(), &[2, 3]);
53/// # Ok::<(), tenferro_tensor_core::ValidationError>(())
54/// ```
55pub trait TensorRank: private::Sealed + Clone + Copy + Debug + Eq + Send + Sync + 'static {
56 /// Static rank when known at compile time.
57 ///
58 /// # Examples
59 ///
60 /// ```rust
61 /// use tenferro_tensor_core::{DynRank, Rank, TensorRank};
62 ///
63 /// assert_eq!(DynRank::RANK, None);
64 /// assert_eq!(Rank::<2>::RANK, Some(2));
65 /// ```
66 const RANK: Option<usize>;
67
68 /// Shape representation for this rank.
69 ///
70 /// # Examples
71 ///
72 /// ```rust
73 /// use tenferro_tensor_core::{DynRank, TensorRank};
74 ///
75 /// let shape: <DynRank as TensorRank>::Shape = vec![2, 3].into();
76 /// assert_eq!(shape.as_ref(), &[2, 3]);
77 /// ```
78 type Shape: Clone + Debug + PartialEq + Eq + AsRef<[usize]> + IntoRankShape<Self>;
79
80 /// Stride representation for this rank.
81 ///
82 /// # Examples
83 ///
84 /// ```rust
85 /// use tenferro_tensor_core::{DynRank, TensorRank};
86 ///
87 /// let strides: <DynRank as TensorRank>::Strides = vec![1, 2].into();
88 /// assert_eq!(strides.as_ref(), &[1, 2]);
89 /// ```
90 type Strides: Clone + Debug + PartialEq + Eq + AsRef<[isize]>;
91
92 /// Convert a dynamic shape vector into this rank's shape representation.
93 ///
94 /// # Examples
95 ///
96 /// ```rust
97 /// use tenferro_tensor_core::{Rank, TensorRank};
98 ///
99 /// let shape = <Rank<1> as TensorRank>::shape_from_vec(vec![4].into())?;
100 /// assert_eq!(shape.as_ref(), &[4]);
101 /// # Ok::<(), tenferro_tensor_core::ValidationError>(())
102 /// ```
103 ///
104 /// # Errors
105 ///
106 /// Returns [`ValidationError::RankMismatch`] when the shape length does
107 /// not equal the compile-time rank.
108 fn shape_from_vec(shape: ShapeVec) -> Result<Self::Shape>;
109
110 /// Convert this rank's shape representation into a dynamic shape vector.
111 ///
112 /// # Examples
113 ///
114 /// ```rust
115 /// use tenferro_tensor_core::{Rank, TensorRank};
116 ///
117 /// let shape = <Rank<2> as TensorRank>::shape_from_vec(vec![2, 3].into())?;
118 /// assert_eq!(<Rank<2> as TensorRank>::shape_into_vec(shape).as_slice(), &[2, 3]);
119 /// # Ok::<(), tenferro_tensor_core::ValidationError>(())
120 /// ```
121 fn shape_into_vec(shape: Self::Shape) -> ShapeVec;
122
123 /// Convert a dynamic stride vector into this rank's stride representation.
124 ///
125 /// # Examples
126 ///
127 /// ```rust
128 /// use tenferro_tensor_core::{Rank, TensorRank};
129 ///
130 /// let strides = <Rank<2> as TensorRank>::strides_from_vec(vec![1, 2].into())?;
131 /// assert_eq!(strides.as_ref(), &[1, 2]);
132 /// # Ok::<(), tenferro_tensor_core::ValidationError>(())
133 /// ```
134 ///
135 /// # Errors
136 ///
137 /// Returns [`ValidationError::RankMismatch`] when the stride length does
138 /// not equal the compile-time rank.
139 fn strides_from_vec(strides: StrideVec) -> Result<Self::Strides>;
140
141 /// Convert this rank's stride representation into a dynamic stride vector.
142 ///
143 /// # Examples
144 ///
145 /// ```rust
146 /// use tenferro_tensor_core::{Rank, TensorRank};
147 ///
148 /// let strides = <Rank<2> as TensorRank>::strides_from_vec(vec![1, 2].into())?;
149 /// assert_eq!(<Rank<2> as TensorRank>::strides_into_vec(strides).as_slice(), &[1, 2]);
150 /// # Ok::<(), tenferro_tensor_core::ValidationError>(())
151 /// ```
152 fn strides_into_vec(strides: Self::Strides) -> StrideVec;
153}
154
155/// Dynamic tensor rank marker.
156///
157/// # Examples
158///
159/// ```rust
160/// use tenferro_tensor_core::{DynRank, TensorRank};
161///
162/// assert_eq!(DynRank::RANK, None);
163/// ```
164#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
165pub struct DynRank;
166
167/// Static tensor rank marker.
168///
169/// # Examples
170///
171/// ```rust
172/// use tenferro_tensor_core::{Rank, TensorRank};
173///
174/// assert_eq!(Rank::<3>::RANK, Some(3));
175/// ```
176#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
177pub struct Rank<const N: usize>;
178
179impl<S> IntoRankShape<DynRank> for S
180where
181 S: IntoShapeVec,
182{
183 fn into_rank_shape(self) -> Result<ShapeVec> {
184 Ok(self.into_shape_vec())
185 }
186}
187
188impl<const N: usize> IntoRankShape<Rank<N>> for [usize; N] {
189 fn into_rank_shape(self) -> Result<[usize; N]> {
190 Ok(self)
191 }
192}
193
194impl<const N: usize> IntoRankShape<Rank<N>> for &[usize; N] {
195 fn into_rank_shape(self) -> Result<[usize; N]> {
196 Ok(*self)
197 }
198}
199
200impl<const N: usize> IntoRankShape<Rank<N>> for Vec<usize> {
201 fn into_rank_shape(self) -> Result<[usize; N]> {
202 Rank::<N>::shape_from_vec(self.into())
203 }
204}
205
206impl<const N: usize> IntoRankShape<Rank<N>> for &[usize] {
207 fn into_rank_shape(self) -> Result<[usize; N]> {
208 Rank::<N>::shape_from_vec(self.into_shape_vec())
209 }
210}
211
212impl<const N: usize> IntoRankShape<Rank<N>> for ShapeVec {
213 fn into_rank_shape(self) -> Result<[usize; N]> {
214 Rank::<N>::shape_from_vec(self)
215 }
216}
217
218impl TensorRank for DynRank {
219 const RANK: Option<usize> = None;
220
221 type Shape = ShapeVec;
222 type Strides = StrideVec;
223
224 fn shape_from_vec(shape: ShapeVec) -> Result<Self::Shape> {
225 Ok(shape)
226 }
227
228 fn shape_into_vec(shape: Self::Shape) -> ShapeVec {
229 shape
230 }
231
232 fn strides_from_vec(strides: StrideVec) -> Result<Self::Strides> {
233 Ok(strides)
234 }
235
236 fn strides_into_vec(strides: Self::Strides) -> StrideVec {
237 strides
238 }
239}
240
241impl<const N: usize> TensorRank for Rank<N> {
242 const RANK: Option<usize> = Some(N);
243
244 type Shape = [usize; N];
245 type Strides = [isize; N];
246
247 fn shape_from_vec(shape: ShapeVec) -> Result<Self::Shape> {
248 let actual = shape.len();
249 shape
250 .into_vec()
251 .try_into()
252 .map_err(|_| ValidationError::RankMismatch {
253 expected: N,
254 actual,
255 })
256 }
257
258 fn shape_into_vec(shape: Self::Shape) -> ShapeVec {
259 ShapeVec::from_iter(shape)
260 }
261
262 fn strides_from_vec(strides: StrideVec) -> Result<Self::Strides> {
263 let actual = strides.len();
264 strides
265 .into_vec()
266 .try_into()
267 .map_err(|_| ValidationError::RankMismatch {
268 expected: N,
269 actual,
270 })
271 }
272
273 fn strides_into_vec(strides: Self::Strides) -> StrideVec {
274 StrideVec::from_iter(strides)
275 }
276}
277
278mod private {
279 pub trait Sealed {}
280
281 impl Sealed for super::DynRank {}
282 impl<const N: usize> Sealed for super::Rank<N> {}
283}