1use crate::{Error, Result, Shape};
4
5#[derive(Debug, PartialEq, Eq, Clone)]
6pub struct Layout {
7 shape: Shape,
8 stride: Vec<usize>,
10 start_offset: usize,
11}
12
13impl Layout {
14 pub fn new(shape: Shape, stride: Vec<usize>, start_offset: usize) -> Self {
15 Self {
16 shape,
17 stride,
18 start_offset,
19 }
20 }
21
22 pub fn contiguous_with_offset<S: Into<Shape>>(shape: S, start_offset: usize) -> Self {
23 let shape = shape.into();
24 let stride = shape.stride_contiguous();
25 Self {
26 shape,
27 stride,
28 start_offset,
29 }
30 }
31
32 pub fn contiguous<S: Into<Shape>>(shape: S) -> Self {
33 Self::contiguous_with_offset(shape, 0)
34 }
35
36 pub fn dims(&self) -> &[usize] {
37 self.shape.dims()
38 }
39
40 pub fn dim<D: crate::shape::Dim>(&self, dim: D) -> Result<usize> {
42 let dim = dim.to_index(&self.shape, "dim")?;
43 Ok(self.dims()[dim])
44 }
45
46 pub fn shape(&self) -> &Shape {
47 &self.shape
48 }
49
50 pub fn stride(&self) -> &[usize] {
51 &self.stride
52 }
53
54 pub fn start_offset(&self) -> usize {
55 self.start_offset
56 }
57
58 pub(crate) fn outer_stride_for_dim(&self, dim: usize) -> Option<usize> {
67 let dims = self.dims();
68 let strides = self.stride();
69
70 let mut expected = 1usize;
72 for i in (dim..dims.len()).rev() {
73 if strides[i] != expected {
74 return None;
75 }
76 expected *= dims[i];
77 }
78
79 if dim == 0 {
80 return Some(expected);
83 }
84
85 let outer_stride = strides[dim - 1];
87 let mut expected_outer = outer_stride;
88 for k in (0..dim - 1).rev() {
89 expected_outer *= dims[k + 1];
90 if strides[k] != expected_outer {
91 return None;
92 }
93 }
94
95 Some(outer_stride)
96 }
97
98 pub fn has_internal_overlap(&self) -> bool {
100 !self.range().is_some_and(|f| f.injective)
101 }
102
103 fn range(&self) -> Option<LayoutRange> {
106 let mut axes: Vec<(usize, usize)> = self
108 .dims()
109 .iter()
110 .zip(self.stride())
111 .filter(|(&d, _)| d > 1)
112 .map(|(&d, &s)| (d, s))
113 .collect();
114 axes.sort_unstable_by_key(|&(_, s)| s);
115
116 let mut span = 0usize;
117 let mut injective = true;
118 let mut dense = true;
119
120 for (d, s) in axes {
121 if s <= span {
122 injective = false;
124 dense = false;
125 } else if s - span != 1 {
126 dense = false;
128 }
129 span = span.checked_add((d - 1).checked_mul(s)?)?;
130 }
131
132 let lo = self.start_offset();
133 Some(LayoutRange {
134 lo,
135 hi: lo.checked_add(span)?,
136 injective,
137 dense,
138 })
139 }
140
141 pub fn relation(&self, other: &Self) -> LayoutRelation {
143 if self == other {
144 return LayoutRelation::Identical;
145 }
146
147 if self.shape().elem_count() == 0 || other.shape().elem_count() == 0 {
148 return LayoutRelation::Disjoint;
149 }
150
151 let (a, b) = match (self.range(), other.range()) {
153 (Some(a), Some(b)) => (a, b),
154 _ => return LayoutRelation::Unknown, };
156
157 if a.separated_from(&b) {
158 return LayoutRelation::Disjoint;
159 }
160
161 if a.densely_contains(&b) || b.densely_contains(&a) {
162 return LayoutRelation::Overlapping;
163 }
164
165 LayoutRelation::Unknown
171 }
172
173 pub fn contiguous_offsets(&self) -> Option<(usize, usize)> {
176 if self.is_contiguous() {
177 let start_o = self.start_offset;
178 Some((start_o, start_o + self.shape.elem_count()))
179 } else {
180 None
181 }
182 }
183
184 pub fn is_contiguous(&self) -> bool {
188 self.shape.is_contiguous(&self.stride)
189 }
190
191 pub fn is_fortran_contiguous(&self) -> bool {
193 self.shape.is_fortran_contiguous(&self.stride)
194 }
195
196 pub fn is_scalar(&self) -> bool {
197 let dims = self.dims();
198 dims.is_empty() || dims.iter().all(|d| *d == 1)
199 }
200
201 pub fn is_scalar_broadcast(&self) -> bool {
203 self.stride().iter().all(|s| *s == 0)
204 }
205
206 pub fn is_scalar_like(&self) -> bool {
207 self.is_scalar() || self.is_scalar_broadcast()
208 }
209
210 pub fn narrow(&self, dim: usize, start: usize, len: usize) -> Result<Self> {
211 let dims = self.shape().dims();
212 if dim >= dims.len() {
213 Err(Error::DimOutOfRange {
214 shape: self.shape().clone(),
215 dim: dim as i32,
216 op: "narrow",
217 }
218 .bt())?
219 }
220 if start + len > dims[dim] {
221 Err(Error::NarrowInvalidArgs {
222 shape: self.shape.clone(),
223 dim,
224 start,
225 len,
226 msg: "start + len > dim_len",
227 }
228 .bt())?
229 }
230 let mut dims = dims.to_vec();
231 dims[dim] = len;
232 Ok(Self {
233 shape: Shape::from(dims),
234 stride: self.stride.clone(),
235 start_offset: self.start_offset + self.stride[dim] * start,
236 })
237 }
238
239 pub fn transpose(&self, dim1: usize, dim2: usize) -> Result<Self> {
240 let rank = self.shape.rank();
241 if rank <= dim1 || rank <= dim2 {
242 Err(Error::UnexpectedNumberOfDims {
243 expected: usize::max(dim1, dim2),
244 got: rank,
245 shape: self.shape().clone(),
246 }
247 .bt())?
248 }
249 let mut stride = self.stride().to_vec();
250 let mut dims = self.shape().dims().to_vec();
251 dims.swap(dim1, dim2);
252 stride.swap(dim1, dim2);
253 Ok(Self {
254 shape: Shape::from(dims),
255 stride,
256 start_offset: self.start_offset,
257 })
258 }
259
260 pub fn permute(&self, idxs: &[usize]) -> Result<Self> {
261 let is_permutation =
262 idxs.len() == self.shape.rank() && (0..idxs.len()).all(|i| idxs.contains(&i));
263 if !is_permutation {
264 crate::bail!(
265 "dimension mismatch in permute, tensor {:?}, dims: {:?}",
266 self.dims(),
267 idxs
268 )
269 }
270 let stride = self.stride();
271 let dims = self.shape().dims();
272 let mut perm_stride = stride.to_vec();
273 let mut perm_dims = dims.to_vec();
274 for (i, &idx) in idxs.iter().enumerate() {
275 perm_stride[i] = stride[idx];
276 perm_dims[i] = dims[idx];
277 }
278 Ok(Self {
279 shape: Shape::from(perm_dims),
280 stride: perm_stride,
281 start_offset: self.start_offset,
282 })
283 }
284
285 pub fn broadcast_as<S: Into<Shape>>(&self, shape: S) -> Result<Self> {
286 let shape = shape.into();
287 if shape.rank() < self.shape().rank() {
288 return Err(Error::BroadcastIncompatibleShapes {
289 src_shape: self.shape().clone(),
290 dst_shape: shape,
291 }
292 .bt());
293 }
294 let added_dims = shape.rank() - self.shape().rank();
295 let mut stride = vec![0; added_dims];
296 for (&dst_dim, (&src_dim, &src_stride)) in shape.dims()[added_dims..]
297 .iter()
298 .zip(self.dims().iter().zip(self.stride()))
299 {
300 let s = if dst_dim == src_dim {
301 src_stride
302 } else if src_dim != 1 {
303 return Err(Error::BroadcastIncompatibleShapes {
304 src_shape: self.shape().clone(),
305 dst_shape: shape,
306 }
307 .bt());
308 } else {
309 0
310 };
311 stride.push(s)
312 }
313 Ok(Self {
314 shape,
315 stride,
316 start_offset: self.start_offset,
317 })
318 }
319
320 pub(crate) fn strided_index(&self) -> crate::StridedIndex<'_> {
321 crate::StridedIndex::from_layout(self)
322 }
323
324 pub(crate) fn strided_blocks(&self) -> crate::StridedBlocks<'_> {
325 let mut block_len = 1usize;
326 let mut contiguous_dims = 0usize; for (&stride, &dim) in self.stride().iter().zip(self.dims().iter()).rev() {
328 if dim == 1 {
330 contiguous_dims += 1;
331 continue;
332 }
333 if stride != block_len {
334 break;
335 }
336 block_len *= dim;
337 contiguous_dims += 1;
338 }
339 let index_dims = self.dims().len() - contiguous_dims;
340 match index_dims {
341 0 => crate::StridedBlocks::SingleBlock {
342 start_offset: self.start_offset,
343 len: block_len,
344 },
345 1 => crate::StridedBlocks::UniformBlocks {
346 start_offset: self.start_offset,
347 block_len,
348 count: self.dims()[0],
349 src_stride: self.stride[0],
350 },
351 _ => {
352 let block_start_index = crate::StridedIndex::new(
353 &self.dims()[..index_dims],
354 &self.stride[..index_dims],
355 self.start_offset,
356 );
357 crate::StridedBlocks::MultipleBlocks {
358 block_start_index,
359 block_len,
360 }
361 }
362 }
363 }
364}
365
366struct LayoutRange {
369 lo: usize,
371 hi: usize,
373 injective: bool,
375 dense: bool,
378}
379
380impl LayoutRange {
381 fn separated_from(&self, other: &Self) -> bool {
383 self.hi < other.lo || other.hi < self.lo
384 }
385
386 fn densely_contains(&self, other: &Self) -> bool {
388 self.dense && other.lo >= self.lo && other.lo <= self.hi
389 }
390}
391
392#[derive(Copy, Clone, PartialEq, Eq, Debug)]
394pub enum LayoutRelation {
395 Identical,
397 Disjoint,
399 Overlapping,
401 Unknown,
403}