Skip to main content

rten_tensor/
tensor.rs

1use std::borrow::Cow;
2use std::fmt::Debug;
3use std::hash::{Hash, Hasher};
4use std::mem::MaybeUninit;
5use std::ops::{Index, IndexMut, Range};
6use std::sync::Arc;
7
8use crate::assume_init::AssumeInit;
9use crate::copy::{
10    copy_into, copy_into_slice, copy_into_uninit, copy_range_into_slice, map_into_slice,
11};
12use crate::errors::{DimensionError, ExpandError, FromDataError, ReshapeError, SliceError};
13use crate::iterators::{
14    AxisChunks, AxisChunksMut, AxisIter, AxisIterMut, InnerIter, InnerIterMut, Iter, IterMut,
15    Lanes, LanesMut, for_each_mut,
16};
17use crate::layout::{
18    AsIndex, AsShape, BroadcastLayout, DynLayout, FromShape, InsertDim, IntoLayout, Layout,
19    LayoutExt, MatrixLayout, MutLayout, NdLayout, OverlapPolicy, RemoveDim, ResizeLayout,
20    SizeArray, SliceWith, TrustedLayout,
21};
22use crate::overlap::may_have_internal_overlap;
23use crate::slice_range::{IntoSliceItems, SliceItem};
24use crate::storage::{
25    Alloc, CowData, GlobalAlloc, IntoStorage, Storage, StorageMut, ViewData, ViewMutData,
26};
27use crate::type_num::IndexCount;
28use crate::{Contiguous, RandomSource};
29
30/// The base type for multi-dimensional arrays. This consists of storage for
31/// elements, plus a _layout_ which maps from a multi-dimensional array index
32/// to a storage offset. This base type is not normally used directly but
33/// instead through a type alias which selects the storage type and layout.
34///
35/// The storage can be owned (like a `Vec<T>`), borrowed (like `&[T]`) or
36/// mutably borrowed (like `&mut [T]`). The layout can have a dimension count
37/// that is determined statically (ie. forms part of the tensor's type), see
38/// [`NdLayout`] or is only known at runtime, see [`DynLayout`].
39pub struct TensorBase<S: Storage, L: Layout> {
40    data: S,
41
42    // Layout mapping N-dimensional indices to offsets in `data`.
43    //
44    // Constructors must ensure:
45    //
46    // - Every index that is valid for `layout` must map to an offset that is
47    //   less than `data.len()`. The minimum length for a layout is given by
48    //   `Layout::min_data_len`.
49    // - If `S` is a mutable storage type, no two indices of `layout` can map to
50    //   the same offset. See the `may_have_internal_overlap` function.
51    layout: L,
52}
53
54/// Trait implemented by all variants of [`TensorBase`], which provides a
55/// `view` method to get an immutable view of the tensor, plus methods which
56/// forward to such a view.
57///
58/// The purpose of this trait is to allow methods to be specialized for
59/// immutable views by preserving the lifetime of the underlying data in
60/// return types (eg. `iter` returns `&[T]` in the trait, but `&'a [T]` in
61/// the view). This allows for chaining operations on views together (eg.
62/// `tensor.slice(...).transpose()`) without needing to separate each step
63/// into separate statements.
64///
65/// This trait is conceptually similar to the way [`std::ops::Deref`] in the Rust
66/// standard library allows a `Vec<T>` to have all the methods of an `&[T]`.
67///
68/// If stable Rust gains support for specialization or a `Deref` trait that can
69/// return non-references (see <https://github.com/rust-lang/rfcs/issues/997>)
70/// this will become unnecessary.
71pub trait AsView: Layout {
72    /// Type of element stored in this tensor.
73    type Elem;
74
75    /// The underlying layout of this tensor. It must have the same index
76    /// type (eg. `[usize; N]` or `&[usize]`) as this view.
77    type Layout: Clone + for<'a> Layout<Index<'a> = Self::Index<'a>>;
78
79    /// Return a borrowed view of this tensor.
80    fn view(&self) -> TensorBase<ViewData<'_, Self::Elem>, Self::Layout>;
81
82    /// Return the layout of this tensor.
83    fn layout(&self) -> &Self::Layout;
84
85    /// Return a view of this tensor using a borrowed [`CowData`] for storage.
86    ///
87    /// Together with [`into_cow`](TensorBase::into_cow), this is useful where
88    /// code needs to conditionally copy or create a new tensor, and get either
89    /// the borrowed or owned tensor into the same type.
90    fn as_cow(&self) -> TensorBase<CowData<'_, Self::Elem>, Self::Layout>
91    where
92        [Self::Elem]: ToOwned,
93    {
94        self.view().as_cow()
95    }
96
97    /// Return a view of this tensor with a dynamic rank.
98    fn as_dyn(&self) -> TensorBase<ViewData<'_, Self::Elem>, DynLayout> {
99        self.view().as_dyn()
100    }
101
102    /// Return an iterator over slices of this tensor along a given axis.
103    fn axis_chunks(&self, dim: usize, chunk_size: usize) -> AxisChunks<'_, Self::Elem, Self::Layout>
104    where
105        Self::Layout: MutLayout,
106    {
107        self.view().axis_chunks(dim, chunk_size)
108    }
109
110    /// Return an iterator over slices of this tensor along a given axis.
111    fn axis_iter(&self, dim: usize) -> AxisIter<'_, Self::Elem, Self::Layout>
112    where
113        Self::Layout: MutLayout + RemoveDim,
114    {
115        self.view().axis_iter(dim)
116    }
117
118    /// Broadcast this view to another shape.
119    ///
120    /// If `shape` is an array (`[usize; N]`), the result will have a
121    /// static-rank layout with `N` dims. If `shape` is a slice, the result will
122    /// have a dynamic-rank layout.
123    fn broadcast<S: IntoLayout>(&self, shape: S) -> TensorBase<ViewData<'_, Self::Elem>, S::Layout>
124    where
125        Self::Layout: BroadcastLayout<S::Layout>,
126    {
127        self.view().broadcast(shape)
128    }
129
130    /// Fallible variant of [`broadcast`](AsView::broadcast).
131    fn try_broadcast<S: IntoLayout>(
132        &self,
133        shape: S,
134    ) -> Result<TensorBase<ViewData<'_, Self::Elem>, S::Layout>, ExpandError>
135    where
136        Self::Layout: BroadcastLayout<S::Layout>,
137    {
138        self.view().try_broadcast(shape)
139    }
140
141    /// Copy elements from this tensor into `dest` in logical order.
142    ///
143    /// Returns the initialized slice. Panics if the length of `dest` does
144    /// not match the number of elements in `self`.
145    fn copy_into_slice<'a>(&self, dest: &'a mut [MaybeUninit<Self::Elem>]) -> &'a [Self::Elem]
146    where
147        Self::Elem: Copy;
148
149    /// Return the layout of this tensor as a slice, if it is contiguous.
150    fn data(&self) -> Option<&[Self::Elem]>;
151
152    /// Return a reference to the element at a given index, or `None` if the
153    /// index is invalid.
154    fn get<I: AsIndex<Self::Layout>>(&self, index: I) -> Option<&Self::Elem>
155    where
156        Self::Layout: TrustedLayout,
157    {
158        self.view().get(index)
159    }
160
161    /// Return a reference to the element at a given index, without performing
162    /// bounds checks.
163    ///
164    /// # Safety
165    ///
166    /// The caller must ensure that the index is valid for the tensor's shape.
167    unsafe fn get_unchecked<I: AsIndex<Self::Layout>>(&self, index: I) -> &Self::Elem {
168        let view = self.view();
169        unsafe { view.get_unchecked(index) }
170    }
171
172    /// Index the tensor along a given axis.
173    ///
174    /// Returns a view with one dimension removed.
175    ///
176    /// Panics if `axis >= self.ndim()` or `index >= self.size(axis)`.
177    fn index_axis(
178        &self,
179        axis: usize,
180        index: usize,
181    ) -> TensorBase<ViewData<'_, Self::Elem>, <Self::Layout as RemoveDim>::Output>
182    where
183        Self::Layout: MutLayout + RemoveDim,
184    {
185        self.view().index_axis(axis, index)
186    }
187
188    /// Return an iterator over the innermost N dimensions.
189    fn inner_iter<const N: usize>(&self) -> InnerIter<'_, Self::Elem, NdLayout<N>> {
190        self.view().inner_iter()
191    }
192
193    /// Return an iterator over the innermost `n` dimensions.
194    ///
195    /// Prefer [`inner_iter`](AsView::inner_iter) if `N` is known at compile time.
196    fn inner_iter_dyn(&self, n: usize) -> InnerIter<'_, Self::Elem, DynLayout> {
197        self.view().inner_iter_dyn(n)
198    }
199
200    /// Insert a size-1 axis at the given index.
201    fn insert_axis(&mut self, index: usize)
202    where
203        Self::Layout: ResizeLayout;
204
205    /// Remove a size-1 axis at the given index.
206    ///
207    /// This will panic if the index is out of bounds or the size of the index
208    /// is not 1.
209    fn remove_axis(&mut self, index: usize)
210    where
211        Self::Layout: ResizeLayout;
212
213    /// Return the scalar value in this tensor if it has 0 dimensions.
214    fn item(&self) -> Option<&Self::Elem> {
215        self.view().item()
216    }
217
218    /// Return an iterator over elements in this tensor in their logical order.
219    fn iter(&self) -> Iter<'_, Self::Elem>;
220
221    /// Return an iterator over 1D slices of this tensor along a given axis.
222    fn lanes(&self, dim: usize) -> Lanes<'_, Self::Elem>
223    where
224        Self::Layout: RemoveDim,
225    {
226        self.view().lanes(dim)
227    }
228
229    /// Return a new tensor with the same shape, formed by applying `f` to each
230    /// element in this tensor.
231    fn map<F, U>(&self, f: F) -> TensorBase<Vec<U>, Self::Layout>
232    where
233        F: Fn(&Self::Elem) -> U,
234        Self::Layout: FromShape,
235    {
236        self.view().map(f)
237    }
238
239    /// Variant of [`map`](AsView::map) which takes an allocator.
240    fn map_in<A: Alloc, F, U>(&self, alloc: A, f: F) -> TensorBase<Vec<U>, Self::Layout>
241    where
242        F: Fn(&Self::Elem) -> U,
243        Self::Layout: FromShape,
244    {
245        self.view().map_in(alloc, f)
246    }
247
248    /// Merge consecutive dimensions to the extent possible without copying
249    /// data or changing the iteration order.
250    ///
251    /// If the tensor is contiguous, this has the effect of flattening the
252    /// tensor into a vector.
253    fn merge_axes(&mut self)
254    where
255        Self::Layout: ResizeLayout;
256
257    /// Re-order the axes of this tensor to move the axis at index `from` to
258    /// `to`.
259    ///
260    /// Panics if `from` or `to` is >= `self.ndim()`.
261    fn move_axis(&mut self, from: usize, to: usize)
262    where
263        Self::Layout: MutLayout;
264
265    /// Convert this tensor to one with the same shape but a static dimension
266    /// count.
267    ///
268    /// Panics if `self.ndim() != N`.
269    fn nd_view<const N: usize>(&self) -> TensorBase<ViewData<'_, Self::Elem>, NdLayout<N>> {
270        self.view().nd_view()
271    }
272
273    /// Permute the dimensions of this tensor.
274    fn permute(&mut self, order: Self::Index<'_>)
275    where
276        Self::Layout: MutLayout;
277
278    /// Return a view with dimensions permuted in the order given by `dims`.
279    fn permuted(&self, order: Self::Index<'_>) -> TensorBase<ViewData<'_, Self::Elem>, Self::Layout>
280    where
281        Self::Layout: MutLayout,
282    {
283        self.view().permuted(order)
284    }
285
286    /// Return either a view or a copy of `self` with the given shape.
287    ///
288    /// The new shape must have the same number of elments as the current
289    /// shape. The result will have a static rank if `shape` is an array or
290    /// a dynamic rank if it is a slice.
291    ///
292    /// If `self` is contiguous this will return a view, as changing the shape
293    /// can be done without moving data. Otherwise it will copy elements into
294    /// a new tensor.
295    ///
296    /// # Panics
297    ///
298    /// Panics if the number of elements in the new shape does not match the
299    /// current shape.
300    fn reshaped<S: Copy + IntoLayout>(
301        &self,
302        shape: S,
303    ) -> TensorBase<CowData<'_, Self::Elem>, S::Layout>
304    where
305        Self::Elem: Clone,
306    {
307        self.view().reshaped(shape)
308    }
309
310    /// A variant of [`reshaped`](AsView::reshaped) that allows specifying the
311    /// allocator to use if a copy is needed.
312    fn reshaped_in<A: Alloc, S: Copy + IntoLayout>(
313        &self,
314        alloc: A,
315        shape: S,
316    ) -> TensorBase<CowData<'_, Self::Elem>, S::Layout>
317    where
318        Self::Elem: Clone,
319    {
320        self.view().reshaped_in(alloc, shape)
321    }
322
323    /// Reverse the order of dimensions in this tensor.
324    fn transpose(&mut self)
325    where
326        Self::Layout: MutLayout;
327
328    /// Return a view with the order of dimensions reversed.
329    fn transposed(&self) -> TensorBase<ViewData<'_, Self::Elem>, Self::Layout>
330    where
331        Self::Layout: MutLayout,
332    {
333        self.view().transposed()
334    }
335
336    /// Slice this tensor and return a view.
337    ///
338    /// If both this tensor's layout and the range have a statically-known
339    /// number of index terms, the result will have a static rank. Otherwise it
340    /// will have a dynamic rank.
341    ///
342    /// ```
343    /// use rten_tensor::prelude::*;
344    /// use rten_tensor::NdTensor;
345    ///
346    /// let x = NdTensor::from([[1, 2], [3, 4]]);
347    /// let col = x.slice((.., 1)); // `col` is an `NdTensorView<i32, 1>`
348    /// assert_eq!(col.shape(), [2usize]);
349    /// assert_eq!(col.to_vec(), [2, 4]);
350    /// ```
351    #[allow(clippy::type_complexity)]
352    fn slice<R: IntoSliceItems + IndexCount>(
353        &self,
354        range: R,
355    ) -> TensorBase<ViewData<'_, Self::Elem>, <Self::Layout as SliceWith<R, R::Count>>::Layout>
356    where
357        Self::Layout: SliceWith<R, R::Count>,
358    {
359        self.view().slice(range)
360    }
361
362    /// Slice this tensor along a given axis.
363    fn slice_axis(
364        &self,
365        axis: usize,
366        range: Range<usize>,
367    ) -> TensorBase<ViewData<'_, Self::Elem>, Self::Layout>
368    where
369        Self::Layout: MutLayout,
370    {
371        self.view().slice_axis(axis, range)
372    }
373
374    /// A variant of [`slice`](Self::slice) that returns a result
375    /// instead of panicking.
376    #[allow(clippy::type_complexity)]
377    fn try_slice<R: IntoSliceItems + IndexCount>(
378        &self,
379        range: R,
380    ) -> Result<
381        TensorBase<ViewData<'_, Self::Elem>, <Self::Layout as SliceWith<R, R::Count>>::Layout>,
382        SliceError,
383    >
384    where
385        Self::Layout: SliceWith<R, R::Count>,
386    {
387        self.view().try_slice(range)
388    }
389
390    /// Return a slice of this tensor as an owned tensor.
391    ///
392    /// This is more expensive than [`slice`](AsView::slice) as it copies the
393    /// data, but is more flexible as it supports ranges with negative steps.
394    #[allow(clippy::type_complexity)]
395    fn slice_copy<R: Clone + IntoSliceItems + IndexCount>(
396        &self,
397        range: R,
398    ) -> TensorBase<Vec<Self::Elem>, <Self::Layout as SliceWith<R, R::Count>>::Layout>
399    where
400        Self::Elem: Clone,
401        Self::Layout: SliceWith<
402                R,
403                R::Count,
404                Layout: FromShape + for<'a> Layout<Shape<'a>: TryFrom<&'a [usize], Error: Debug>>,
405            >,
406    {
407        self.slice_copy_in(GlobalAlloc::new(), range)
408    }
409
410    /// Variant of [`slice_copy`](AsView::slice_copy) which takes an allocator.
411    #[allow(clippy::type_complexity)]
412    fn slice_copy_in<A: Alloc, R: Clone + IntoSliceItems + IndexCount>(
413        &self,
414        pool: A,
415        range: R,
416    ) -> TensorBase<Vec<Self::Elem>, <Self::Layout as SliceWith<R, R::Count>>::Layout>
417    where
418        Self::Elem: Clone,
419        Self::Layout: SliceWith<
420                R,
421                R::Count,
422                Layout: FromShape + for<'a> Layout<Shape<'a>: TryFrom<&'a [usize], Error: Debug>>,
423            >,
424    {
425        // Fast path for slice ranges supported by `Tensor::slice`. This includes
426        // all ranges except those with a negative step. This benefits from
427        // optimizations that `Tensor::to_tensor` has for slices that are already
428        // contiguous or have a small number of dims.
429        if let Ok(slice_view) = self.try_slice(range.clone()) {
430            return slice_view.to_tensor_in(pool);
431        }
432
433        let items = range.into_slice_items();
434        let sliced_shape: Vec<_> = items
435            .as_ref()
436            .iter()
437            .copied()
438            .enumerate()
439            .filter_map(|(dim, item)| match item {
440                SliceItem::Index(_) => None,
441                SliceItem::Range(range) => Some(range.index_range(self.size(dim)).steps()),
442            })
443            .collect();
444        let sliced_len = sliced_shape.iter().product();
445        let mut sliced_data = pool.alloc(sliced_len);
446
447        copy_range_into_slice(
448            self.as_dyn(),
449            &mut sliced_data.spare_capacity_mut()[..sliced_len],
450            items.as_ref(),
451        );
452
453        // Safety: `copy_range_into_slice` initialized `sliced_len` elements.
454        unsafe {
455            sliced_data.set_len(sliced_len);
456        }
457
458        let sliced_shape = sliced_shape.as_slice().try_into().expect("slice failed");
459
460        TensorBase::from_data(sliced_shape, sliced_data)
461    }
462
463    /// Return a view of this tensor with all dimensions of size 1 removed.
464    fn squeezed(&self) -> TensorView<'_, Self::Elem>
465    where
466        Self::Layout: MutLayout,
467    {
468        self.view().squeezed()
469    }
470
471    /// Return a vector containing the elements of this tensor in their logical
472    /// order, ie. as if the tensor were flattened into one dimension.
473    fn to_vec(&self) -> Vec<Self::Elem>
474    where
475        Self::Elem: Clone;
476
477    /// Variant of [`to_vec`](AsView::to_vec) which takes an allocator.
478    fn to_vec_in<A: Alloc>(&self, alloc: A) -> Vec<Self::Elem>
479    where
480        Self::Elem: Clone;
481
482    /// Return a tensor with the same shape as this tensor/view but with the
483    /// data contiguous in memory and arranged in the same order as the
484    /// logical/iteration order (used by `iter`).
485    ///
486    /// This will return a view if the data is already contiguous or copy
487    /// data into a new buffer otherwise.
488    ///
489    /// Certain operations require or are faster with contiguous tensors.
490    fn to_contiguous(&self) -> Contiguous<TensorBase<CowData<'_, Self::Elem>, Self::Layout>>
491    where
492        Self::Elem: Clone,
493        Self::Layout: FromShape,
494    {
495        self.view().to_contiguous()
496    }
497
498    /// Variant of [`to_contiguous`](AsView::to_contiguous) which takes an
499    /// allocator.
500    fn to_contiguous_in<A: Alloc>(
501        &self,
502        alloc: A,
503    ) -> Contiguous<TensorBase<CowData<'_, Self::Elem>, Self::Layout>>
504    where
505        Self::Elem: Clone,
506        Self::Layout: FromShape,
507    {
508        self.view().to_contiguous_in(alloc)
509    }
510
511    /// Return a copy of this tensor with a given shape.
512    fn to_shape<S: IntoLayout>(&self, shape: S) -> TensorBase<Vec<Self::Elem>, S::Layout>
513    where
514        Self::Elem: Clone;
515
516    /// Return a slice containing the elements of this tensor in their logical
517    /// order, ie. as if the tensor were flattened into one dimension.
518    ///
519    /// Unlike [`data`](AsView::data) this will copy the elements if they are
520    /// not contiguous. Unlike [`to_vec`](AsView::to_vec) this will not copy
521    /// the elements if the tensor is already contiguous.
522    fn to_slice(&self) -> Cow<'_, [Self::Elem]>
523    where
524        Self::Elem: Clone,
525    {
526        self.view().to_slice()
527    }
528
529    /// Return a copy of this tensor/view which uniquely owns its elements.
530    fn to_tensor(&self) -> TensorBase<Vec<Self::Elem>, Self::Layout>
531    where
532        Self::Elem: Clone,
533        Self::Layout: FromShape,
534    {
535        self.to_tensor_in(GlobalAlloc::new())
536    }
537
538    /// Variant of [`to_tensor`](AsView::to_tensor) which takes an allocator.
539    fn to_tensor_in<A: Alloc>(&self, alloc: A) -> TensorBase<Vec<Self::Elem>, Self::Layout>
540    where
541        Self::Elem: Clone,
542        Self::Layout: FromShape,
543    {
544        TensorBase::from_data(self.layout().shape(), self.to_vec_in(alloc))
545    }
546
547    /// Return a view which performs "weak" checking when indexing via
548    /// `view[<index>]`. See [`WeaklyCheckedView`] for an explanation.
549    fn weakly_checked_view(&self) -> WeaklyCheckedView<ViewData<'_, Self::Elem>, Self::Layout> {
550        self.view().weakly_checked_view()
551    }
552}
553
554impl<S: Storage, L: Layout> TensorBase<S, L> {
555    /// Construct a new tensor from a given shape and storage.
556    ///
557    /// Panics if the data length does not match the product of `shape`.
558    #[track_caller]
559    pub fn from_data<D: IntoStorage<Output = S>>(shape: L::Shape<'_>, data: D) -> TensorBase<S, L>
560    where
561        L: FromShape,
562        for<'a> L::Shape<'a>: Clone,
563    {
564        let data = data.into_storage();
565        let len = data.len();
566        match Self::try_from_data(shape.clone(), data) {
567            Ok(data) => data,
568            Err(_) => panic!("data length {} does not match shape {:?}", len, shape),
569        }
570    }
571
572    /// Construct a new tensor from a given shape and storage.
573    ///
574    /// This will fail if the data length does not match the product of `shape`.
575    pub fn try_from_data<D: IntoStorage<Output = S>>(
576        shape: L::Shape<'_>,
577        data: D,
578    ) -> Result<TensorBase<S, L>, FromDataError>
579    where
580        L: FromShape,
581    {
582        let data = data.into_storage();
583        let layout = L::from_shape(shape);
584        if layout.min_data_len() != data.len() {
585            return Err(FromDataError::StorageLengthMismatch);
586        }
587        Ok(TensorBase { data, layout })
588    }
589
590    /// Create a tensor from a pre-created storage and layout.
591    ///
592    /// Panics if the storage length is too short for the layout, or the storage
593    /// is mutable and the layout may map multiple indices to the same offset.
594    pub fn from_storage_and_layout(data: S, layout: L) -> TensorBase<S, L> {
595        assert!(data.len() >= layout.min_data_len());
596        assert!(!S::MUTABLE || !may_have_internal_overlap(layout.shape(), layout.strides()));
597        TensorBase { data, layout }
598    }
599
600    /// Create a tensor from a pre-created storage and layout.
601    ///
602    /// # Safety
603    ///
604    /// Caller must ensure storage length is sufficient for the layout, and
605    /// that, if the storage is mutable, no two indices in the layout map to the
606    /// same offset.
607    pub(crate) unsafe fn from_storage_and_layout_unchecked(data: S, layout: L) -> TensorBase<S, L> {
608        debug_assert!(data.len() >= layout.min_data_len());
609        debug_assert!(!S::MUTABLE || !may_have_internal_overlap(layout.shape(), layout.strides()));
610        TensorBase { data, layout }
611    }
612
613    /// Construct a new tensor from a given shape and storage, and custom
614    /// strides.
615    ///
616    /// This will fail if the data length is incorrect for the shape and stride
617    /// combination, or if the strides lead to overlap (see [`OverlapPolicy`]).
618    /// See also [`TensorBase::from_slice_with_strides`] which is a similar method
619    /// for immutable views that does allow overlapping strides.
620    pub fn from_data_with_strides<D: IntoStorage<Output = S>>(
621        shape: L::Shape<'_>,
622        data: D,
623        strides: L::Strides<'_>,
624    ) -> Result<TensorBase<S, L>, FromDataError>
625    where
626        L: MutLayout,
627    {
628        let layout = L::from_shape_and_strides(shape, strides, OverlapPolicy::DisallowOverlap)?;
629        let data = data.into_storage();
630        if layout.min_data_len() > data.len() {
631            return Err(FromDataError::StorageTooShort);
632        }
633        Ok(TensorBase { data, layout })
634    }
635
636    /// Convert the current tensor into a dynamic rank tensor without copying
637    /// any data.
638    pub fn into_dyn(self) -> TensorBase<S, DynLayout>
639    where
640        L: Into<DynLayout>,
641    {
642        TensorBase {
643            data: self.data,
644            layout: self.layout.into(),
645        }
646    }
647
648    /// Return a tensor with a size-1 dimension inserted at `axis`.
649    ///
650    /// `axis` must be in the range `0..=self.ndim()`. Panics if `axis` is out
651    /// of bounds.
652    ///
653    /// This is a zero-copy operation that only changes the layout metadata.
654    #[track_caller]
655    pub fn with_new_axis(self, axis: usize) -> TensorBase<S, <L as InsertDim>::Output>
656    where
657        L: InsertDim,
658    {
659        assert!(
660            axis <= self.ndim(),
661            "axis {} is out of bounds for tensor with {} dims",
662            axis,
663            self.ndim()
664        );
665        let layout = self.layout.insert_dim(axis);
666        TensorBase {
667            data: self.data,
668            layout,
669        }
670    }
671
672    /// Return a tensor with a size-1 dimension at `axis` removed.
673    ///
674    /// `axis` must be in the range `0..self.ndim()` and `self.size(axis)` must
675    /// be 1. Panics if `axis` is out of bounds or the dimension size is not 1.
676    ///
677    /// This is a zero-copy operation that only changes the layout metadata.
678    #[track_caller]
679    pub fn with_axis_removed(self, axis: usize) -> TensorBase<S, <L as RemoveDim>::Output>
680    where
681        L: RemoveDim,
682    {
683        assert!(
684            axis < self.ndim(),
685            "axis {} is out of bounds for tensor with {} dims",
686            axis,
687            self.ndim()
688        );
689        assert!(
690            self.size(axis) == 1,
691            "cannot remove axis {} of size {}",
692            axis,
693            self.size(axis)
694        );
695        let layout = self.layout.remove_dim(axis);
696        TensorBase {
697            data: self.data,
698            layout,
699        }
700    }
701
702    /// Consume this tensor and return the underlying storage.
703    ///
704    /// Be aware that the underlying elements are not guaranteed to be contiguous.
705    pub(crate) fn into_storage(self) -> S {
706        self.data
707    }
708
709    /// Attempt to convert this tensor's layout to a static-rank layout with `N`
710    /// dimensions.
711    fn nd_layout<const N: usize>(&self) -> Result<NdLayout<N>, DimensionError> {
712        if self.ndim() != N {
713            return Err(DimensionError {
714                actual: self.ndim(),
715                expected: N,
716            });
717        }
718        let shape: [usize; N] = std::array::from_fn(|i| self.size(i));
719        let strides: [usize; N] = std::array::from_fn(|i| self.stride(i));
720        let layout = NdLayout::from_shape_and_strides(shape, strides, OverlapPolicy::AllowOverlap)
721            .expect("invalid layout");
722        Ok(layout)
723    }
724
725    /// Convert this tensor into a tensor with rank `N`.
726    pub fn into_rank<const N: usize>(self) -> Result<TensorBase<S, NdLayout<N>>, DimensionError> {
727        let layout = self.nd_layout()?;
728        Ok(TensorBase {
729            data: self.data,
730            layout,
731        })
732    }
733
734    /// Convert this tensor into one with dimensions re-ordered.
735    pub fn into_permuted(self, order: L::Index<'_>) -> TensorBase<S, L>
736    where
737        L: MutLayout,
738    {
739        TensorBase {
740            layout: self.layout.permuted(order),
741            data: self.data,
742        }
743    }
744
745    /// Return a raw pointer to the tensor's underlying data.
746    pub fn data_ptr(&self) -> *const S::Elem {
747        self.data.as_ptr()
748    }
749}
750
751impl<S: StorageMut, L: Clone + Layout> TensorBase<S, L> {
752    /// Return an iterator over mutable slices of this tensor along a given
753    /// axis. Each view yielded has one dimension fewer than the current layout.
754    pub fn axis_iter_mut(&mut self, dim: usize) -> AxisIterMut<'_, S::Elem, L>
755    where
756        L: RemoveDim,
757    {
758        AxisIterMut::new(self.view_mut(), dim)
759    }
760
761    /// Return an iterator over mutable slices of this tensor along a given
762    /// axis. Each view yielded has the same rank as this tensor, but the
763    /// dimension `dim` will only have `chunk_size` entries.
764    pub fn axis_chunks_mut(
765        &mut self,
766        dim: usize,
767        chunk_size: usize,
768    ) -> AxisChunksMut<'_, S::Elem, L>
769    where
770        L: MutLayout,
771    {
772        AxisChunksMut::new(self.view_mut(), dim, chunk_size)
773    }
774
775    /// Replace each element in this tensor with the result of applying `f` to
776    /// the element.
777    pub fn apply<F: Fn(&S::Elem) -> S::Elem>(&mut self, f: F) {
778        if let Some(data) = self.data_mut() {
779            // Fast path for contiguous tensors.
780            data.iter_mut().for_each(|x| *x = f(x));
781        } else {
782            for_each_mut(self.as_dyn_mut(), |x| *x = f(x));
783        }
784    }
785
786    /// Return a mutable view of this tensor with a dynamic dimension count.
787    pub fn as_dyn_mut(&mut self) -> TensorBase<ViewMutData<'_, S::Elem>, DynLayout> {
788        TensorBase {
789            layout: DynLayout::from(&self.layout),
790            data: self.data.view_mut(),
791        }
792    }
793
794    /// Copy elements from another tensor into this tensor.
795    ///
796    /// This tensor and `other` must have the same shape.
797    pub fn copy_from<S2: Storage<Elem = S::Elem>>(&mut self, other: &TensorBase<S2, L>)
798    where
799        S::Elem: Clone,
800        L: Clone,
801    {
802        assert!(
803            self.shape() == other.shape(),
804            "copy dest shape {:?} != src shape {:?}",
805            self.shape(),
806            other.shape()
807        );
808
809        if let Some(dest) = self.data_mut() {
810            if let Some(src) = other.data() {
811                dest.clone_from_slice(src);
812            } else {
813                // Drop all the existing values. This should be compiled away for
814                // `Copy` types.
815                let uninit_dest: &mut [MaybeUninit<S::Elem>] = unsafe { std::mem::transmute(dest) };
816                for x in &mut *uninit_dest {
817                    // Safety: All elements were initialized at the start of this
818                    // block, and we haven't written to the slice yet.
819                    unsafe { x.assume_init_drop() }
820                }
821
822                // Copy source into destination in contiguous order.
823                copy_into_slice(other.as_dyn(), uninit_dest);
824            }
825        } else {
826            copy_into(other.as_dyn(), self.as_dyn_mut());
827        }
828    }
829
830    /// Return the data in this tensor as a slice if it is contiguous.
831    pub fn data_mut(&mut self) -> Option<&mut [S::Elem]> {
832        // The length of `self.data` must be at least the minimum required by
833        // the layout, but it may be larger.
834        let len = self.layout.min_data_len();
835        let data = self.data.slice_mut(0..len);
836
837        self.layout.is_contiguous().then(|| unsafe {
838            // Safety: We verified the layout is contiguous.
839            data.to_slice_mut()
840        })
841    }
842
843    /// Index the tensor along a given axis.
844    ///
845    /// Returns a mutable view with one dimension removed.
846    ///
847    /// Panics if `axis >= self.ndim()` or `index >= self.size(axis)`.
848    pub fn index_axis_mut(
849        &mut self,
850        axis: usize,
851        index: usize,
852    ) -> TensorBase<ViewMutData<'_, S::Elem>, <L as RemoveDim>::Output>
853    where
854        L: MutLayout + RemoveDim,
855    {
856        let (offsets, layout) = self.layout.index_axis(axis, index);
857        TensorBase {
858            data: self.data.slice_mut(offsets),
859            layout,
860        }
861    }
862
863    /// Return a mutable view of the tensor's underlying storage.
864    pub fn storage_mut(&mut self) -> ViewMutData<'_, S::Elem> {
865        self.data.view_mut()
866    }
867
868    /// Replace all elements of this tensor with `value`.
869    pub fn fill(&mut self, value: S::Elem)
870    where
871        S::Elem: Clone,
872    {
873        self.apply(|_| value.clone())
874    }
875
876    /// Return a mutable reference to the element at `index`, or `None` if the
877    /// index is invalid.
878    pub fn get_mut<I: AsIndex<L>>(&mut self, index: I) -> Option<&mut S::Elem>
879    where
880        L: TrustedLayout,
881    {
882        self.offset(index.as_index()).map(|offset| unsafe {
883            // Safety: We verified the offset is in-bounds.
884            self.data.get_unchecked_mut(offset)
885        })
886    }
887
888    /// Return the element at a given index, without performing any bounds-
889    /// checking.
890    ///
891    /// # Safety
892    ///
893    /// The caller must ensure that the index is valid for the tensor's shape.
894    pub unsafe fn get_unchecked_mut<I: AsIndex<L>>(&mut self, index: I) -> &mut S::Elem {
895        let offset = self.layout.offset_unchecked(index.as_index());
896        unsafe { self.data.get_unchecked_mut(offset) }
897    }
898
899    pub(crate) fn mut_view_ref(&mut self) -> TensorBase<ViewMutData<'_, S::Elem>, &L> {
900        TensorBase {
901            data: self.data.view_mut(),
902            layout: &self.layout,
903        }
904    }
905
906    /// Return a mutable iterator over the N innermost dimensions of this tensor.
907    pub fn inner_iter_mut<const N: usize>(&mut self) -> InnerIterMut<'_, S::Elem, NdLayout<N>> {
908        InnerIterMut::new(self.view_mut())
909    }
910
911    /// Return a mutable iterator over the n innermost dimensions of this tensor.
912    ///
913    /// Prefer [`inner_iter_mut`](TensorBase::inner_iter_mut) if `N` is known
914    /// at compile time.
915    pub fn inner_iter_dyn_mut(&mut self, n: usize) -> InnerIterMut<'_, S::Elem, DynLayout> {
916        InnerIterMut::new_dyn(self.view_mut(), n)
917    }
918
919    /// Return a mutable iterator over the elements of this tensor, in their
920    /// logical order.
921    pub fn iter_mut(&mut self) -> IterMut<'_, S::Elem> {
922        IterMut::new(self.mut_view_ref())
923    }
924
925    /// Return an iterator over mutable 1D slices of this tensor along a given
926    /// dimension.
927    pub fn lanes_mut(&mut self, dim: usize) -> LanesMut<'_, S::Elem>
928    where
929        L: RemoveDim,
930    {
931        LanesMut::new(self.mut_view_ref(), dim)
932    }
933
934    /// Return a view of this tensor with a static dimension count.
935    ///
936    /// Panics if `self.ndim() != N`.
937    pub fn nd_view_mut<const N: usize>(
938        &mut self,
939    ) -> TensorBase<ViewMutData<'_, S::Elem>, NdLayout<N>> {
940        assert!(self.ndim() == N, "ndim {} != {}", self.ndim(), N);
941        TensorBase {
942            layout: self.nd_layout().unwrap(),
943            data: self.data.view_mut(),
944        }
945    }
946
947    /// Permute the order of dimensions according to the given order.
948    ///
949    /// See [`AsView::permuted`].
950    pub fn permuted_mut(&mut self, order: L::Index<'_>) -> TensorBase<ViewMutData<'_, S::Elem>, L>
951    where
952        L: MutLayout,
953    {
954        TensorBase {
955            layout: self.layout.permuted(order),
956            data: self.data.view_mut(),
957        }
958    }
959
960    /// Change the layout of the tensor without moving any data.
961    ///
962    /// This will return an error if the view is not contiguous.
963    ///
964    /// See also [`AsView::reshaped`].
965    pub fn reshaped_mut<SH: IntoLayout>(
966        &mut self,
967        shape: SH,
968    ) -> Result<TensorBase<ViewMutData<'_, S::Elem>, SH::Layout>, ReshapeError> {
969        let layout = self.layout.reshaped_for_view(shape)?;
970        Ok(TensorBase {
971            layout,
972            data: self.data.view_mut(),
973        })
974    }
975
976    /// Slice this tensor along a given axis.
977    pub fn slice_axis_mut(
978        &mut self,
979        axis: usize,
980        range: Range<usize>,
981    ) -> TensorBase<ViewMutData<'_, S::Elem>, L>
982    where
983        L: MutLayout,
984    {
985        let (offset_range, sliced_layout) = self.layout.slice_axis(axis, range.clone()).unwrap();
986        debug_assert_eq!(sliced_layout.size(axis), range.len());
987        TensorBase {
988            data: self.data.slice_mut(offset_range),
989            layout: sliced_layout,
990        }
991    }
992
993    /// Slice this tensor and return a mutable view.
994    ///
995    /// See [`slice`](AsView::slice) for notes on the layout of the returned
996    /// view.
997    pub fn slice_mut<R: IntoSliceItems + IndexCount>(
998        &mut self,
999        range: R,
1000    ) -> TensorBase<ViewMutData<'_, S::Elem>, <L as SliceWith<R, R::Count>>::Layout>
1001    where
1002        L: SliceWith<R, R::Count>,
1003    {
1004        self.try_slice_mut(range).expect("slice failed")
1005    }
1006
1007    /// A variant of [`slice_mut`](Self::slice_mut) that returns a
1008    /// result instead of panicking.
1009    #[allow(clippy::type_complexity)]
1010    pub fn try_slice_mut<R: IntoSliceItems + IndexCount>(
1011        &mut self,
1012        range: R,
1013    ) -> Result<
1014        TensorBase<ViewMutData<'_, S::Elem>, <L as SliceWith<R, R::Count>>::Layout>,
1015        SliceError,
1016    >
1017    where
1018        L: SliceWith<R, R::Count>,
1019    {
1020        let (offset_range, sliced_layout) = self.layout.slice_with(range)?;
1021        Ok(TensorBase {
1022            data: self.data.slice_mut(offset_range),
1023            layout: sliced_layout,
1024        })
1025    }
1026
1027    /// Return a mutable view of this tensor.
1028    pub fn view_mut(&mut self) -> TensorBase<ViewMutData<'_, S::Elem>, L>
1029    where
1030        L: Clone,
1031    {
1032        TensorBase {
1033            data: self.data.view_mut(),
1034            layout: self.layout.clone(),
1035        }
1036    }
1037
1038    /// Return a mutable view that performs only "weak" checking when indexing,
1039    /// this is faster but can hide bugs. See [`WeaklyCheckedView`].
1040    pub fn weakly_checked_view_mut(&mut self) -> WeaklyCheckedView<ViewMutData<'_, S::Elem>, L> {
1041        WeaklyCheckedView {
1042            base: self.view_mut(),
1043        }
1044    }
1045}
1046
1047impl<T, L: Clone + Layout> TensorBase<Vec<T>, L> {
1048    /// Create a new 1D tensor filled with an arithmetic sequence of values
1049    /// in the range `[start, end)` separated by `step`. If `step` is omitted,
1050    /// it defaults to 1.
1051    pub fn arange(start: T, end: T, step: Option<T>) -> TensorBase<Vec<T>, L>
1052    where
1053        T: Copy + PartialOrd + From<bool> + std::ops::Add<Output = T>,
1054        [usize; 1]: AsShape<L>,
1055        L: FromShape,
1056    {
1057        let step = step.unwrap_or((true).into());
1058        let mut data = Vec::new();
1059        let mut curr = start;
1060        while curr < end {
1061            data.push(curr);
1062            curr = curr + step;
1063        }
1064        TensorBase::from_data([data.len()].as_shape(), data)
1065    }
1066
1067    /// Append elements from `other` to this tensor along a given axis.
1068    ///
1069    /// This will fail if the shapes of `self` and `other` do not match along
1070    /// dimensions other than `axis`, or if the current tensor has
1071    /// insufficient capacity to expand without re-allocating.
1072    pub fn append<S2: Storage<Elem = T>>(
1073        &mut self,
1074        axis: usize,
1075        other: &TensorBase<S2, L>,
1076    ) -> Result<(), ExpandError>
1077    where
1078        T: Copy,
1079        L: MutLayout,
1080    {
1081        let shape_match = self.ndim() == other.ndim()
1082            && (0..self.ndim()).all(|d| d == axis || self.size(d) == other.size(d));
1083        if !shape_match {
1084            return Err(ExpandError::ShapeMismatch);
1085        }
1086
1087        let old_size = self.size(axis);
1088        let new_size = self.size(axis) + other.size(axis);
1089
1090        let Some(new_layout) = self.expanded_layout(axis, new_size) else {
1091            return Err(ExpandError::InsufficientCapacity);
1092        };
1093
1094        let new_data_len = new_layout.min_data_len();
1095        self.layout = new_layout;
1096
1097        // Fast path for the case where we can avoid initializing new elements
1098        // before copying the data.
1099        if self.layout.is_contiguous() && self.data.len() + other.len() == new_data_len {
1100            let added = new_data_len - self.data.len();
1101            other.copy_into_slice(&mut self.data.spare_capacity_mut()[..added]);
1102
1103            // Safety: `copy_into_slice` initialized every element between the
1104            // old and new lengths.
1105            unsafe {
1106                self.data.set_len(new_data_len);
1107            }
1108
1109            return Ok(());
1110        }
1111
1112        // Initialize new capacity if needed.
1113        if self.data.len() < new_data_len {
1114            // `other` must be non-empty here as otherwise the old/new layouts
1115            // would be the same and the `min_data_len` would not have grown.
1116            let fill = *other.iter().next().unwrap();
1117            self.data.resize(new_data_len, fill);
1118        }
1119
1120        self.slice_axis_mut(axis, old_size..new_size)
1121            .copy_from(other);
1122
1123        Ok(())
1124    }
1125
1126    /// Create a new 1D tensor from a `Vec<T>`.
1127    pub fn from_vec(vec: Vec<T>) -> TensorBase<Vec<T>, L>
1128    where
1129        [usize; 1]: AsShape<L>,
1130        L: FromShape,
1131    {
1132        TensorBase::from_data([vec.len()].as_shape(), vec)
1133    }
1134
1135    /// Clip dimension `dim` to `[range.start, range.end)`. The new size for
1136    /// the dimension must be <= the old size.
1137    ///
1138    /// This currently requires `T: Copy` to support efficiently moving data
1139    /// from the new start offset to the beginning of the element buffer.
1140    pub fn clip_dim(&mut self, dim: usize, range: Range<usize>)
1141    where
1142        T: Copy,
1143        L: MutLayout,
1144    {
1145        let (start, end) = (range.start, range.end);
1146
1147        assert!(start <= end, "start must be <= end");
1148        assert!(end <= self.size(dim), "end must be <= dim size");
1149
1150        self.layout.resize_dim(dim, end - start);
1151
1152        let range = if self.is_empty() {
1153            0..0
1154        } else {
1155            let start_offset = start * self.layout.stride(dim);
1156            let end_offset = start_offset + self.layout.min_data_len();
1157            start_offset..end_offset
1158        };
1159        self.data.copy_within(range.clone(), 0);
1160        self.data.truncate(range.end - range.start);
1161    }
1162
1163    /// Return true if this tensor can be expanded along a given axis to a
1164    /// new size without re-allocating.
1165    pub fn has_capacity(&self, axis: usize, new_size: usize) -> bool
1166    where
1167        L: MutLayout,
1168    {
1169        self.expanded_layout(axis, new_size).is_some()
1170    }
1171
1172    /// Return the layout this tensor would have if the size of `axis` were
1173    /// expanded to `new_size`.
1174    ///
1175    /// Returns `None` if the tensor does not have capacity for the new size.
1176    fn expanded_layout(&self, axis: usize, new_size: usize) -> Option<L>
1177    where
1178        L: MutLayout,
1179    {
1180        let mut new_layout = self.layout.clone();
1181        new_layout.resize_dim(axis, new_size);
1182        let new_data_len = new_layout.min_data_len();
1183
1184        let has_capacity = new_data_len <= self.data.capacity()
1185            && !may_have_internal_overlap(new_layout.shape(), new_layout.strides());
1186
1187        has_capacity.then_some(new_layout)
1188    }
1189
1190    /// Convert the storage of this tensor into an owned [`CowData`].
1191    ///
1192    /// This is useful in contexts where code needs to conditionally copy or
1193    /// create a new tensor. See [`AsView::as_cow`].
1194    pub fn into_cow(self) -> TensorBase<CowData<'static, T>, L> {
1195        let TensorBase { data, layout } = self;
1196        TensorBase {
1197            layout,
1198            data: CowData::Owned(data),
1199        }
1200    }
1201
1202    /// Convert the storage of this tensor to be reference counted.
1203    ///
1204    /// This is a (relatively) cheap operation that does not copy the tensor
1205    /// data.
1206    pub fn into_arc(self) -> TensorBase<Arc<Vec<T>>, L> {
1207        let TensorBase { data, layout } = self;
1208        TensorBase {
1209            layout,
1210            data: Arc::new(data),
1211        }
1212    }
1213
1214    /// Consume self and return the underlying data as a contiguous tensor.
1215    ///
1216    /// See also [`TensorBase::to_vec`].
1217    pub fn into_data(self) -> Vec<T>
1218    where
1219        T: Clone,
1220    {
1221        if self.is_contiguous() {
1222            self.into_non_contiguous_data()
1223        } else {
1224            self.to_vec()
1225        }
1226    }
1227
1228    /// Consume self and return the underlying data in whatever order the
1229    /// elements are currently stored.
1230    pub fn into_non_contiguous_data(mut self) -> Vec<T> {
1231        self.data.truncate(self.layout.min_data_len());
1232        self.data
1233    }
1234
1235    /// Consume self and return a new contiguous tensor with the given shape.
1236    ///
1237    /// This avoids copying the data if it is already contiguous.
1238    #[track_caller]
1239    pub fn into_shape<S: Copy + IntoLayout>(self, shape: S) -> TensorBase<Vec<T>, S::Layout>
1240    where
1241        T: Clone,
1242    {
1243        let Ok(layout) = self.layout.reshaped_for_copy(shape) else {
1244            panic!(
1245                "element count mismatch reshaping {:?} to {:?}",
1246                self.shape(),
1247                shape
1248            );
1249        };
1250        TensorBase {
1251            layout,
1252            data: self.into_data(),
1253        }
1254    }
1255
1256    /// Create a new tensor with a given shape and values generated by calling
1257    /// `f` repeatedly.
1258    ///
1259    /// Each call to `f` will receive an element index and should return the
1260    /// corresponding value. If the function does not need this index, use
1261    /// [`from_simple_fn`](TensorBase::from_simple_fn) instead, as it is faster.
1262    pub fn from_fn<F: FnMut(L::Index<'_>) -> T, Idx>(
1263        shape: L::Shape<'_>,
1264        f: F,
1265    ) -> TensorBase<Vec<T>, L>
1266    where
1267        L::Indices: Iterator<Item = Idx>,
1268        Idx: AsIndex<L>,
1269        L: FromShape,
1270    {
1271        Self::from_fn_in(GlobalAlloc::new(), shape, f)
1272    }
1273
1274    /// Variant of [`from_fn`](TensorBase::from_fn) that takes an allocator.
1275    pub fn from_fn_in<A: Alloc, F: FnMut(L::Index<'_>) -> T, Idx>(
1276        alloc: A,
1277        shape: L::Shape<'_>,
1278        mut f: F,
1279    ) -> TensorBase<Vec<T>, L>
1280    where
1281        L::Indices: Iterator<Item = Idx>,
1282        Idx: AsIndex<L>,
1283        L: FromShape,
1284    {
1285        let layout = L::from_shape(shape);
1286        let mut data = alloc.alloc(layout.len());
1287        data.extend(layout.indices().map(|idx| f(idx.as_index())));
1288        TensorBase { data, layout }
1289    }
1290
1291    /// Create a new tensor with a given shape and values generated by calling
1292    /// `f` repeatedly.
1293    pub fn from_simple_fn<F: FnMut() -> T>(shape: L::Shape<'_>, f: F) -> TensorBase<Vec<T>, L>
1294    where
1295        L: FromShape,
1296    {
1297        Self::from_simple_fn_in(GlobalAlloc::new(), shape, f)
1298    }
1299
1300    /// Variant of [`from_simple_fn`](TensorBase::from_simple_fn) that takes
1301    /// an allocator.
1302    pub fn from_simple_fn_in<A: Alloc, F: FnMut() -> T>(
1303        alloc: A,
1304        shape: L::Shape<'_>,
1305        mut f: F,
1306    ) -> TensorBase<Vec<T>, L>
1307    where
1308        L: FromShape,
1309    {
1310        let len = shape.iter().product();
1311        let mut data = alloc.alloc(len);
1312        data.extend(std::iter::from_fn(|| Some(f())).take(len));
1313        TensorBase::from_data(shape, data)
1314    }
1315
1316    /// Create a new 0D tensor from a scalar value.
1317    pub fn from_scalar(value: T) -> TensorBase<Vec<T>, L>
1318    where
1319        [usize; 0]: AsShape<L>,
1320        L: FromShape,
1321    {
1322        TensorBase::from_data([].as_shape(), vec![value])
1323    }
1324
1325    /// Create a new tensor with a given shape and all elements set to `value`.
1326    pub fn full(shape: L::Shape<'_>, value: T) -> TensorBase<Vec<T>, L>
1327    where
1328        T: Clone,
1329        L: FromShape,
1330    {
1331        Self::full_in(GlobalAlloc::new(), shape, value)
1332    }
1333
1334    /// Variant of [`full`](TensorBase::full) which takes an allocator.
1335    pub fn full_in<A: Alloc>(alloc: A, shape: L::Shape<'_>, value: T) -> TensorBase<Vec<T>, L>
1336    where
1337        T: Clone,
1338        L: FromShape,
1339    {
1340        let len = shape.iter().product();
1341        let mut data = alloc.alloc(len);
1342        data.resize(len, value);
1343        TensorBase::from_data(shape, data)
1344    }
1345
1346    /// Make the underlying data in this tensor contiguous.
1347    ///
1348    /// This means that after calling `make_contiguous`, the elements are
1349    /// guaranteed to be stored in the same order as the logical order in
1350    /// which `iter` yields elements. This method is cheap if the storage is
1351    /// already contiguous.
1352    pub fn make_contiguous(&mut self)
1353    where
1354        T: Clone,
1355        L: FromShape,
1356    {
1357        if self.is_contiguous() {
1358            return;
1359        }
1360        self.data = self.to_vec();
1361        self.layout = L::from_shape(self.layout.shape());
1362    }
1363
1364    /// Convert this tensor into a contiguous tensor.
1365    ///
1366    /// This is cheap if the tensor is already contiguous, otherwise the
1367    /// elements are copied into a new contiguous buffer.
1368    pub fn into_contiguous(self) -> Contiguous<Self>
1369    where
1370        T: Clone,
1371        L: FromShape,
1372    {
1373        Contiguous::from_owned(self)
1374    }
1375
1376    /// Create a new tensor with a given shape and elements populated using
1377    /// numbers generated by `rand_src`.
1378    ///
1379    /// A more general version of this method that generates values using any
1380    /// function is [`from_simple_fn`](Self::from_simple_fn).
1381    pub fn rand<R: RandomSource<T>>(shape: L::Shape<'_>, rand_src: &mut R) -> TensorBase<Vec<T>, L>
1382    where
1383        L: FromShape,
1384    {
1385        Self::from_simple_fn(shape, || rand_src.next())
1386    }
1387
1388    /// Create a new tensor with a given shape, with all elements set to their
1389    /// default value (ie. zero for numeric types).
1390    pub fn zeros(shape: L::Shape<'_>) -> TensorBase<Vec<T>, L>
1391    where
1392        T: Clone + Default,
1393        L: FromShape,
1394    {
1395        Self::zeros_in(GlobalAlloc::new(), shape)
1396    }
1397
1398    /// Variant of [`zeros`](TensorBase::zeros) which takes an allocator.
1399    pub fn zeros_in<A: Alloc>(alloc: A, shape: L::Shape<'_>) -> TensorBase<Vec<T>, L>
1400    where
1401        T: Clone + Default,
1402        L: FromShape,
1403    {
1404        // We delegate to `full_in` here and rely on compiler optimizations to
1405        // take advantage of the value being statically known to be zero.
1406        Self::full_in(alloc, shape, T::default())
1407    }
1408
1409    /// Return a new tensor containing uninitialized elements.
1410    ///
1411    /// The caller must initialize elements and then call
1412    /// [`assume_init`](TensorBase::assume_init) to convert to an initialized
1413    /// `Tensor<T>`.
1414    pub fn uninit(shape: L::Shape<'_>) -> TensorBase<Vec<MaybeUninit<T>>, L>
1415    where
1416        MaybeUninit<T>: Clone,
1417        L: FromShape,
1418    {
1419        Self::uninit_in(GlobalAlloc::new(), shape)
1420    }
1421
1422    /// Variant of [`uninit`](TensorBase::uninit) which takes an allocator.
1423    pub fn uninit_in<A: Alloc>(alloc: A, shape: L::Shape<'_>) -> TensorBase<Vec<MaybeUninit<T>>, L>
1424    where
1425        L: FromShape,
1426    {
1427        let len = shape.iter().product();
1428        let mut data = alloc.alloc(len);
1429
1430        // Safety: Since the contents of the `Vec` are `MaybeUninit`, we don't
1431        // need to initialize them.
1432        unsafe { data.set_len(len) }
1433
1434        TensorBase::from_data(shape, data)
1435    }
1436
1437    /// Concatenate a slice of tensors along a given dimension.
1438    ///
1439    /// All tensors must have the same shape, except along `dim` where the
1440    /// sizes may differ. Returns an error if `tensors` is empty or if shapes
1441    /// are incompatible.
1442    pub fn concat<S2: Storage<Elem = T>>(
1443        dim: usize,
1444        tensors: &[TensorBase<S2, L>],
1445    ) -> Result<TensorBase<Vec<T>, L>, ExpandError>
1446    where
1447        T: Copy,
1448        L: FromShape + MutLayout,
1449    {
1450        let first = tensors.first().ok_or(ExpandError::ShapeMismatch)?;
1451        let total_dim_size: usize = tensors.iter().map(|t| t.size(dim)).sum();
1452
1453        let mut target_layout = first.layout().clone();
1454        target_layout.resize_dim(dim, total_dim_size);
1455
1456        let mut result = Self::with_capacity(target_layout.shape(), dim);
1457        for tensor in tensors {
1458            result.append(dim, tensor)?;
1459        }
1460        Ok(result)
1461    }
1462
1463    /// Create a tensor which initially has zero elements, but can be expanded
1464    /// along a given dimension without reallocating.
1465    ///
1466    /// `shape` specifies the maximum shape that the tensor can be expanded to
1467    /// without reallocating. The initial shape will be the same, except for
1468    /// the dimension specified by `expand_dim`, which will be zero.
1469    pub fn with_capacity(shape: L::Shape<'_>, expand_dim: usize) -> TensorBase<Vec<T>, L>
1470    where
1471        T: Copy,
1472        L: FromShape + MutLayout,
1473    {
1474        Self::with_capacity_in(GlobalAlloc::new(), shape, expand_dim)
1475    }
1476
1477    /// Variant of [`with_capacity`](Self::with_capacity) which takes an allocator.
1478    pub fn with_capacity_in<A: Alloc>(
1479        alloc: A,
1480        shape: L::Shape<'_>,
1481        expand_dim: usize,
1482    ) -> TensorBase<Vec<T>, L>
1483    where
1484        T: Copy,
1485        L: FromShape + MutLayout,
1486    {
1487        let mut tensor = Self::uninit_in(alloc, shape);
1488        tensor.clip_dim(expand_dim, 0..0);
1489
1490        // Safety: Since at least one dimension has a size of zero, the tensor
1491        // has no elements and thus is fully initialized.
1492        unsafe { tensor.assume_init() }
1493    }
1494}
1495
1496impl<'a, T, L: Layout> TensorBase<CowData<'a, T>, L> {
1497    /// Consume self and return the underlying data in whatever order the
1498    /// elements are currently stored, if the storage is owned, or `None` if
1499    /// it is borrowed.
1500    pub fn into_non_contiguous_data(self) -> Option<Vec<T>> {
1501        match self.data {
1502            CowData::Owned(mut vec) => {
1503                vec.truncate(self.layout.min_data_len());
1504                Some(vec)
1505            }
1506            CowData::Borrowed(_) => None,
1507        }
1508    }
1509
1510    /// Convert this copy-on-write tensor into an owned tensor.
1511    ///
1512    /// This is cheap if the data is already owned or requires a copy otherwise.
1513    pub fn into_owned(self) -> TensorBase<Vec<T>, L>
1514    where
1515        L: Clone + FromShape,
1516        T: Clone,
1517    {
1518        self.into_owned_in(GlobalAlloc::new())
1519    }
1520
1521    /// Variant of [`into_owned`](Self) that takes an allocator.
1522    pub fn into_owned_in<A: Alloc>(self, alloc: A) -> TensorBase<Vec<T>, L>
1523    where
1524        L: Clone + FromShape,
1525        T: Clone,
1526    {
1527        match self.data {
1528            CowData::Owned(data) => TensorBase {
1529                data,
1530                layout: self.layout,
1531            },
1532            CowData::Borrowed(_) => {
1533                let data = self.to_vec_in(alloc);
1534                let layout = L::from_shape(self.shape());
1535                TensorBase { data, layout }
1536            }
1537        }
1538    }
1539
1540    /// Consume self and return a new contiguous tensor with the given shape.
1541    ///
1542    /// This avoids copying the data if it is already contiguous.
1543    pub fn into_shape<S: Clone + IntoLayout>(
1544        self,
1545        shape: S,
1546    ) -> TensorBase<CowData<'a, T>, S::Layout>
1547    where
1548        T: Clone,
1549        L: Clone,
1550    {
1551        self.into_shape_in(GlobalAlloc::new(), shape)
1552    }
1553
1554    /// Variant of [`into_shape`](Self::into_shape) which takes an allocator.
1555    pub fn into_shape_in<A: Alloc, S: Clone + IntoLayout>(
1556        self,
1557        alloc: A,
1558        shape: S,
1559    ) -> TensorBase<CowData<'a, T>, S::Layout>
1560    where
1561        T: Clone,
1562        L: Clone,
1563    {
1564        if let Ok(layout) = self.layout.reshaped_for_view(shape.clone()) {
1565            TensorBase {
1566                data: self.data,
1567                layout,
1568            }
1569        } else {
1570            let Ok(layout) = self.layout.reshaped_for_copy(shape.clone()) else {
1571                panic!(
1572                    "element count mismatch reshaping {:?} to {:?}",
1573                    self.shape(),
1574                    shape
1575                );
1576            };
1577
1578            TensorBase {
1579                data: CowData::Owned(self.to_vec_in(alloc)),
1580                layout,
1581            }
1582        }
1583    }
1584}
1585
1586/// Result of [`TensorBase::init_if_empty`].
1587pub enum InitEmpty<Init: Storage, Uninit: Storage, L: Layout> {
1588    /// Empty tensor with initialized storage.
1589    Empty(TensorBase<Init, L>),
1590    /// Non-empty tensor with uninitialized storage.
1591    NotEmpty(TensorBase<Uninit, L>),
1592}
1593
1594impl<T, S: Storage<Elem = MaybeUninit<T>> + AssumeInit, L: Layout + Clone> TensorBase<S, L>
1595where
1596    <S as AssumeInit>::Output: Storage<Elem = T>,
1597{
1598    /// Convert a tensor of potentially uninitialized elements to one of
1599    /// initialized elements.
1600    ///
1601    /// See also [`MaybeUninit::assume_init`].
1602    ///
1603    /// # Safety
1604    ///
1605    /// The caller must guarantee that all elements in this tensor have been
1606    /// initialized before calling `assume_init`.
1607    pub unsafe fn assume_init(self) -> TensorBase<<S as AssumeInit>::Output, L> {
1608        TensorBase {
1609            layout: self.layout,
1610            data: unsafe { self.data.assume_init() },
1611        }
1612    }
1613
1614    /// Convert `self` to an initialized tensor, if it has no elements.
1615    pub fn init_if_empty(self) -> InitEmpty<<S as AssumeInit>::Output, S, L> {
1616        if self.is_empty() {
1617            // The tensor has no elements, and thus is initialized.
1618            InitEmpty::Empty(unsafe { self.assume_init() })
1619        } else {
1620            InitEmpty::NotEmpty(self)
1621        }
1622    }
1623
1624    /// Initialize this tensor with data from another view.
1625    ///
1626    /// This tensor and `other` must have the same shape.
1627    pub fn init_from<S2: Storage<Elem = T>>(
1628        mut self,
1629        other: &TensorBase<S2, L>,
1630    ) -> TensorBase<<S as AssumeInit>::Output, L>
1631    where
1632        T: Copy,
1633        S: StorageMut<Elem = MaybeUninit<T>>,
1634    {
1635        assert_eq!(self.shape(), other.shape(), "shape mismatch");
1636
1637        match (self.data_mut(), other.data()) {
1638            // Source and dest are contiguous. Use a memcpy.
1639            (Some(self_data), Some(other_data)) => {
1640                let other_data: &[MaybeUninit<T>] = unsafe { std::mem::transmute(other_data) };
1641                self_data.clone_from_slice(other_data);
1642            }
1643            // Dest is contiguous.
1644            (Some(self_data), _) => {
1645                copy_into_slice(other.as_dyn(), self_data);
1646            }
1647            // Neither are contiguous.
1648            _ => {
1649                copy_into_uninit(other.as_dyn(), self.as_dyn_mut());
1650            }
1651        }
1652
1653        unsafe { self.assume_init() }
1654    }
1655}
1656
1657impl<'a, T, L: Clone + Layout> TensorBase<ViewData<'a, T>, L> {
1658    pub fn axis_iter(&self, dim: usize) -> AxisIter<'a, T, L>
1659    where
1660        L: MutLayout + RemoveDim,
1661    {
1662        AxisIter::new(self, dim)
1663    }
1664
1665    pub fn axis_chunks(&self, dim: usize, chunk_size: usize) -> AxisChunks<'a, T, L>
1666    where
1667        L: MutLayout,
1668    {
1669        AxisChunks::new(self, dim, chunk_size)
1670    }
1671
1672    /// Return a view of this tensor with a dynamic dimension count.
1673    ///
1674    /// See [`AsView::as_dyn`].
1675    pub fn as_dyn(&self) -> TensorBase<ViewData<'a, T>, DynLayout> {
1676        TensorBase {
1677            data: self.data,
1678            layout: DynLayout::from(&self.layout),
1679        }
1680    }
1681
1682    /// Convert the storage of this view to a borrowed [`CowData`].
1683    ///
1684    /// See [`AsView::as_cow`].
1685    pub fn as_cow(&self) -> TensorBase<CowData<'a, T>, L> {
1686        TensorBase {
1687            layout: self.layout.clone(),
1688            data: CowData::Borrowed(self.data),
1689        }
1690    }
1691
1692    /// Broadcast this view to another shape.
1693    ///
1694    /// See [`AsView::broadcast`].
1695    pub fn broadcast<S: IntoLayout>(&self, shape: S) -> TensorBase<ViewData<'a, T>, S::Layout>
1696    where
1697        L: BroadcastLayout<S::Layout>,
1698    {
1699        self.try_broadcast(shape).unwrap()
1700    }
1701
1702    /// Broadcast this view to another shape.
1703    ///
1704    /// See [`AsView::broadcast`].
1705    pub fn try_broadcast<S: IntoLayout>(
1706        &self,
1707        shape: S,
1708    ) -> Result<TensorBase<ViewData<'a, T>, S::Layout>, ExpandError>
1709    where
1710        L: BroadcastLayout<S::Layout>,
1711    {
1712        Ok(TensorBase {
1713            layout: self.layout.broadcast(shape)?,
1714            data: self.data,
1715        })
1716    }
1717
1718    /// Return the data in this tensor as a slice if it is contiguous, ie.
1719    /// the order of elements in the slice is the same as the logical order
1720    /// yielded by `iter`, and there are no gaps.
1721    pub fn data(&self) -> Option<&'a [T]> {
1722        // The length of `self.data` must be at least the minimum required by
1723        // the layout, but it may be larger.
1724        let len = self.layout.min_data_len();
1725        let data = self.data.slice(0..len);
1726
1727        self.layout.is_contiguous().then(|| unsafe {
1728            // Safety: Storage is contigous
1729            data.as_slice()
1730        })
1731    }
1732
1733    /// Return an immutable view of the tensor's underlying storage.
1734    pub fn storage(&self) -> ViewData<'a, T> {
1735        self.data.view()
1736    }
1737
1738    pub fn get<I: AsIndex<L>>(&self, index: I) -> Option<&'a T>
1739    where
1740        L: TrustedLayout,
1741    {
1742        self.offset(index.as_index()).map(|offset|
1743                // Safety:
1744                // - No logically overlapping mutable view exist.
1745                // - For trusted layouts, offset is promised to be less than
1746                //   the storage length
1747                unsafe {
1748                self.data.get_unchecked(offset)
1749            })
1750    }
1751
1752    /// Create a new view with a given shape and data slice, and custom strides.
1753    ///
1754    /// If you do not need to specify custom strides, use [`TensorBase::from_data`]
1755    /// instead. This method is similar to [`TensorBase::from_data_with_strides`],
1756    /// but allows strides that lead to internal overlap (see [`OverlapPolicy`]).
1757    pub fn from_slice_with_strides(
1758        shape: L::Shape<'_>,
1759        data: &'a [T],
1760        strides: L::Strides<'_>,
1761    ) -> Result<TensorBase<ViewData<'a, T>, L>, FromDataError>
1762    where
1763        L: MutLayout,
1764    {
1765        let layout = L::from_shape_and_strides(shape, strides, OverlapPolicy::AllowOverlap)?;
1766        if layout.min_data_len() > data.as_ref().len() {
1767            return Err(FromDataError::StorageTooShort);
1768        }
1769        Ok(TensorBase {
1770            data: data.into_storage(),
1771            layout,
1772        })
1773    }
1774
1775    /// Return the element at a given index, without performing any bounds-
1776    /// checking.
1777    ///
1778    /// # Safety
1779    ///
1780    /// The caller must ensure that the index is valid for the tensor's shape.
1781    pub unsafe fn get_unchecked<I: AsIndex<L>>(&self, index: I) -> &'a T {
1782        let offset = self.layout.offset_unchecked(index.as_index());
1783        unsafe { self.data.get_unchecked(offset) }
1784    }
1785
1786    /// Index the tensor along a given axis.
1787    ///
1788    /// Returns a view with one dimension removed.
1789    ///
1790    /// Panics if `axis >= self.ndim()` or `index >= self.size(axis)`.
1791    pub fn index_axis(
1792        &self,
1793        axis: usize,
1794        index: usize,
1795    ) -> TensorBase<ViewData<'a, T>, <L as RemoveDim>::Output>
1796    where
1797        L: MutLayout + RemoveDim,
1798    {
1799        let (offsets, layout) = self.layout.index_axis(axis, index);
1800        TensorBase {
1801            data: self.data.slice(offsets),
1802            layout,
1803        }
1804    }
1805
1806    /// Return an iterator over the inner `N` dimensions of this tensor.
1807    ///
1808    /// See [`AsView::inner_iter`].
1809    pub fn inner_iter<const N: usize>(&self) -> InnerIter<'a, T, NdLayout<N>> {
1810        InnerIter::new(self.view())
1811    }
1812
1813    /// Return an iterator over the inner `n` dimensions of this tensor.
1814    ///
1815    /// See [`AsView::inner_iter_dyn`].
1816    pub fn inner_iter_dyn(&self, n: usize) -> InnerIter<'a, T, DynLayout> {
1817        InnerIter::new_dyn(self.view(), n)
1818    }
1819
1820    /// Return the scalar value in this tensor if it has one element.
1821    pub fn item(&self) -> Option<&'a T> {
1822        match self.ndim() {
1823            0 => unsafe {
1824                // Safety: No logically overlapping mutable views exist.
1825                self.data.get(0)
1826            },
1827            _ if self.len() == 1 => self.iter().next(),
1828            _ => None,
1829        }
1830    }
1831
1832    /// Return an iterator over elements of this tensor in their logical order.
1833    ///
1834    /// See [`AsView::iter`].
1835    pub fn iter(&self) -> Iter<'a, T> {
1836        Iter::new(self.view_ref())
1837    }
1838
1839    /// Return an iterator over 1D slices of this tensor along a given dimension.
1840    ///
1841    /// See [`AsView::lanes`].
1842    pub fn lanes(&self, dim: usize) -> Lanes<'a, T>
1843    where
1844        L: RemoveDim,
1845    {
1846        assert!(dim < self.ndim());
1847        Lanes::new(self.view_ref(), dim)
1848    }
1849
1850    /// Return a view of this tensor with a static dimension count.
1851    ///
1852    /// Panics if `self.ndim() != N`.
1853    pub fn nd_view<const N: usize>(&self) -> TensorBase<ViewData<'a, T>, NdLayout<N>> {
1854        assert!(self.ndim() == N, "ndim {} != {}", self.ndim(), N);
1855        TensorBase {
1856            data: self.data,
1857            layout: self.nd_layout().unwrap(),
1858        }
1859    }
1860
1861    /// Permute the axes of this tensor according to `order`.
1862    ///
1863    /// See [`AsView::permuted`].
1864    pub fn permuted(&self, order: L::Index<'_>) -> TensorBase<ViewData<'a, T>, L>
1865    where
1866        L: MutLayout,
1867    {
1868        TensorBase {
1869            data: self.data,
1870            layout: self.layout.permuted(order),
1871        }
1872    }
1873
1874    /// Return a view or owned tensor that has the given shape.
1875    ///
1876    /// See [`AsView::reshaped`].
1877    pub fn reshaped<S: Copy + IntoLayout>(&self, shape: S) -> TensorBase<CowData<'a, T>, S::Layout>
1878    where
1879        T: Clone,
1880    {
1881        self.reshaped_in(GlobalAlloc::new(), shape)
1882    }
1883
1884    /// Variant of [`reshaped`](Self::reshaped) that takes an allocator.
1885    pub fn reshaped_in<A: Alloc, S: Copy + IntoLayout>(
1886        &self,
1887        alloc: A,
1888        shape: S,
1889    ) -> TensorBase<CowData<'a, T>, S::Layout>
1890    where
1891        T: Clone,
1892    {
1893        if let Ok(layout) = self.layout.reshaped_for_view(shape) {
1894            TensorBase {
1895                data: CowData::Borrowed(self.data),
1896                layout,
1897            }
1898        } else {
1899            let Ok(layout) = self.layout.reshaped_for_copy(shape) else {
1900                panic!(
1901                    "element count mismatch reshaping {:?} to {:?}",
1902                    self.shape(),
1903                    shape
1904                );
1905            };
1906
1907            TensorBase {
1908                data: CowData::Owned(self.to_vec_in(alloc)),
1909                layout,
1910            }
1911        }
1912    }
1913
1914    /// Slice this tensor and return a view. See [`AsView::slice`].
1915    pub fn slice<R: IntoSliceItems + IndexCount>(
1916        &self,
1917        range: R,
1918    ) -> TensorBase<ViewData<'a, T>, <L as SliceWith<R, R::Count>>::Layout>
1919    where
1920        L: SliceWith<R, R::Count>,
1921    {
1922        self.try_slice(range).expect("slice failed")
1923    }
1924
1925    /// Slice this tensor along a given axis.
1926    pub fn slice_axis(&self, axis: usize, range: Range<usize>) -> TensorBase<ViewData<'a, T>, L>
1927    where
1928        L: MutLayout,
1929    {
1930        let (offset_range, sliced_layout) = self.layout.slice_axis(axis, range.clone()).unwrap();
1931        debug_assert_eq!(sliced_layout.size(axis), range.len());
1932        TensorBase {
1933            data: self.data.slice(offset_range),
1934            layout: sliced_layout,
1935        }
1936    }
1937
1938    /// A variant of [`slice`](Self::slice) that returns a result
1939    /// instead of panicking.
1940    #[allow(clippy::type_complexity)]
1941    pub fn try_slice<R: IntoSliceItems + IndexCount>(
1942        &self,
1943        range: R,
1944    ) -> Result<TensorBase<ViewData<'a, T>, <L as SliceWith<R, R::Count>>::Layout>, SliceError>
1945    where
1946        L: SliceWith<R, R::Count>,
1947    {
1948        let (offset_range, sliced_layout) = self.layout.slice_with(range)?;
1949        Ok(TensorBase {
1950            data: self.data.slice(offset_range),
1951            layout: sliced_layout,
1952        })
1953    }
1954
1955    /// Remove all size-one dimensions from this tensor.
1956    ///
1957    /// See [`AsView::squeezed`].
1958    pub fn squeezed(&self) -> TensorView<'a, T>
1959    where
1960        L: MutLayout,
1961    {
1962        TensorBase {
1963            data: self.data.view(),
1964            layout: self.layout.squeezed(),
1965        }
1966    }
1967
1968    /// Divide this tensor into two views along a given axis.
1969    ///
1970    /// Returns a `(left, right)` tuple of views, where the `left` view
1971    /// contains the slice from `[0, mid)` along `axis` and the `right`
1972    /// view contains the slice from `[mid, end)` along `axis`.
1973    #[allow(clippy::type_complexity)]
1974    pub fn split_at(
1975        &self,
1976        axis: usize,
1977        mid: usize,
1978    ) -> (
1979        TensorBase<ViewData<'a, T>, L>,
1980        TensorBase<ViewData<'a, T>, L>,
1981    )
1982    where
1983        L: MutLayout,
1984    {
1985        let (left, right) = self.layout.split(axis, mid);
1986        let (left_offset_range, left_layout) = left;
1987        let (right_offset_range, right_layout) = right;
1988        let left_data = self.data.slice(left_offset_range.clone());
1989        let right_data = self.data.slice(right_offset_range.clone());
1990
1991        debug_assert_eq!(left_data.len(), left_layout.min_data_len());
1992        let left_view = TensorBase {
1993            data: left_data,
1994            layout: left_layout,
1995        };
1996
1997        debug_assert_eq!(right_data.len(), right_layout.min_data_len());
1998        let right_view = TensorBase {
1999            data: right_data,
2000            layout: right_layout,
2001        };
2002
2003        (left_view, right_view)
2004    }
2005
2006    /// Return a view of this tensor with elements stored in contiguous order.
2007    ///
2008    /// If the data is already contiguous, no copy is made, otherwise the
2009    /// elements are copied into a new buffer in contiguous order.
2010    pub fn to_contiguous(&self) -> Contiguous<TensorBase<CowData<'a, T>, L>>
2011    where
2012        T: Clone,
2013        L: FromShape,
2014    {
2015        self.to_contiguous_in(GlobalAlloc::new())
2016    }
2017
2018    /// Variant of [`to_contiguous`](TensorBase::to_contiguous) which takes
2019    /// an allocator.
2020    pub fn to_contiguous_in<A: Alloc>(&self, alloc: A) -> Contiguous<TensorBase<CowData<'a, T>, L>>
2021    where
2022        T: Clone,
2023        L: FromShape,
2024    {
2025        let tensor = if let Some(data) = self.data() {
2026            TensorBase {
2027                data: CowData::Borrowed(data.into_storage()),
2028                layout: self.layout.clone(),
2029            }
2030        } else {
2031            let data = self.to_vec_in(alloc);
2032            TensorBase {
2033                data: CowData::Owned(data),
2034                layout: L::from_shape(self.layout.shape()),
2035            }
2036        };
2037        Contiguous::new(tensor).unwrap()
2038    }
2039
2040    /// Return the underlying data as a flat slice if the tensor is contiguous,
2041    /// or a copy of the data as a flat slice otherwise.
2042    ///
2043    /// See [`AsView::to_slice`].
2044    pub fn to_slice(&self) -> Cow<'a, [T]>
2045    where
2046        T: Clone,
2047    {
2048        self.data()
2049            .map(Cow::Borrowed)
2050            .unwrap_or_else(|| Cow::Owned(self.to_vec()))
2051    }
2052
2053    /// Reverse the order of dimensions in this tensor. See [`AsView::transposed`].
2054    pub fn transposed(&self) -> TensorBase<ViewData<'a, T>, L>
2055    where
2056        L: MutLayout,
2057    {
2058        TensorBase {
2059            data: self.data,
2060            layout: self.layout.transposed(),
2061        }
2062    }
2063
2064    pub fn try_slice_dyn<R: IntoSliceItems>(
2065        &self,
2066        range: R,
2067    ) -> Result<TensorView<'a, T>, SliceError>
2068    where
2069        L: MutLayout,
2070    {
2071        let (offset_range, layout) = self.layout.slice_dyn(range.into_slice_items().as_ref())?;
2072        Ok(TensorBase {
2073            data: self.data.slice(offset_range),
2074            layout,
2075        })
2076    }
2077
2078    /// Return a read-only view of this tensor. See [`AsView::view`].
2079    pub fn view(&self) -> TensorBase<ViewData<'a, T>, L> {
2080        TensorBase {
2081            data: self.data,
2082            layout: self.layout.clone(),
2083        }
2084    }
2085
2086    pub(crate) fn view_ref(&self) -> TensorBase<ViewData<'a, T>, &L> {
2087        TensorBase {
2088            data: self.data,
2089            layout: &self.layout,
2090        }
2091    }
2092
2093    pub fn weakly_checked_view(&self) -> WeaklyCheckedView<ViewData<'a, T>, L> {
2094        WeaklyCheckedView { base: self.view() }
2095    }
2096}
2097
2098impl<S: Storage, L: Layout> Layout for TensorBase<S, L> {
2099    type Shape<'a>
2100        = L::Shape<'a>
2101    where
2102        Self: 'a;
2103    type Strides<'a>
2104        = L::Strides<'a>
2105    where
2106        Self: 'a;
2107    type Index<'a> = L::Index<'a>;
2108    type Indices = L::Indices;
2109
2110    fn ndim(&self) -> usize {
2111        self.layout.ndim()
2112    }
2113
2114    fn len(&self) -> usize {
2115        self.layout.len()
2116    }
2117
2118    fn is_empty(&self) -> bool {
2119        self.layout.is_empty()
2120    }
2121
2122    fn shape(&self) -> Self::Shape<'_> {
2123        self.layout.shape()
2124    }
2125
2126    fn size(&self, dim: usize) -> usize {
2127        self.layout.size(dim)
2128    }
2129
2130    fn strides(&self) -> Self::Strides<'_> {
2131        self.layout.strides()
2132    }
2133
2134    fn stride(&self, dim: usize) -> usize {
2135        self.layout.stride(dim)
2136    }
2137
2138    fn indices(&self) -> Self::Indices {
2139        self.layout.indices()
2140    }
2141
2142    fn offset(&self, index: Self::Index<'_>) -> Option<usize> {
2143        self.layout.offset(index)
2144    }
2145}
2146
2147impl<S: Storage, L: Layout + MatrixLayout> MatrixLayout for TensorBase<S, L> {
2148    fn rows(&self) -> usize {
2149        self.layout.rows()
2150    }
2151
2152    fn cols(&self) -> usize {
2153        self.layout.cols()
2154    }
2155
2156    fn row_stride(&self) -> usize {
2157        self.layout.row_stride()
2158    }
2159
2160    fn col_stride(&self) -> usize {
2161        self.layout.col_stride()
2162    }
2163}
2164
2165impl<T, S: Storage<Elem = T>, L: Layout + Clone> AsView for TensorBase<S, L> {
2166    type Elem = T;
2167    type Layout = L;
2168
2169    fn iter(&self) -> Iter<'_, T> {
2170        self.view().iter()
2171    }
2172
2173    fn copy_into_slice<'a>(&self, dest: &'a mut [MaybeUninit<T>]) -> &'a [T]
2174    where
2175        T: Copy,
2176    {
2177        if let Some(data) = self.data() {
2178            // Safety: `[T]` and `[MaybeUninit<T>]` have same layout.
2179            let src_uninit = unsafe { std::mem::transmute::<&[T], &[MaybeUninit<T>]>(data) };
2180            dest.copy_from_slice(src_uninit);
2181            // Safety: `copy_from_slice` initializes the whole slice or panics
2182            // if there is a length mismatch.
2183            unsafe { dest.assume_init() }
2184        } else {
2185            copy_into_slice(self.as_dyn(), dest)
2186        }
2187    }
2188
2189    fn data(&self) -> Option<&[Self::Elem]> {
2190        self.view().data()
2191    }
2192
2193    fn insert_axis(&mut self, index: usize)
2194    where
2195        L: ResizeLayout,
2196    {
2197        self.layout.insert_axis(index)
2198    }
2199
2200    #[track_caller]
2201    fn remove_axis(&mut self, index: usize)
2202    where
2203        L: ResizeLayout,
2204    {
2205        self.layout.remove_axis(index)
2206    }
2207
2208    fn merge_axes(&mut self)
2209    where
2210        L: ResizeLayout,
2211    {
2212        self.layout.merge_axes()
2213    }
2214
2215    fn layout(&self) -> &L {
2216        &self.layout
2217    }
2218
2219    fn map<F, U>(&self, f: F) -> TensorBase<Vec<U>, L>
2220    where
2221        F: Fn(&Self::Elem) -> U,
2222        L: FromShape,
2223    {
2224        self.map_in(GlobalAlloc::new(), f)
2225    }
2226
2227    fn map_in<A: Alloc, F, U>(&self, alloc: A, f: F) -> TensorBase<Vec<U>, L>
2228    where
2229        F: Fn(&Self::Elem) -> U,
2230        L: FromShape,
2231    {
2232        let len = self.len();
2233        let mut buf = alloc.alloc(len);
2234        if let Some(data) = self.data() {
2235            // Fast path for contiguous tensors.
2236            buf.extend(data.iter().map(f));
2237        } else {
2238            let dest = &mut buf.spare_capacity_mut()[..len];
2239            map_into_slice(self.as_dyn(), dest, f);
2240
2241            // Safety: `map_into` initialized all elements of `dest`.
2242            unsafe {
2243                buf.set_len(len);
2244            }
2245        };
2246        TensorBase::from_data(self.shape(), buf)
2247    }
2248
2249    fn move_axis(&mut self, from: usize, to: usize)
2250    where
2251        L: MutLayout,
2252    {
2253        self.layout.move_axis(from, to);
2254    }
2255
2256    fn view(&self) -> TensorBase<ViewData<'_, T>, L> {
2257        TensorBase {
2258            data: self.data.view(),
2259            layout: self.layout.clone(),
2260        }
2261    }
2262
2263    // For `get` and `get_unchecked` we override the default implementation in
2264    // the trait to skip view creation.
2265
2266    fn get<I: AsIndex<L>>(&self, index: I) -> Option<&Self::Elem> {
2267        self.offset(index.as_index()).map(|offset| unsafe {
2268            // Safety: We verified the offset is in-bounds
2269            self.data.get_unchecked(offset)
2270        })
2271    }
2272
2273    unsafe fn get_unchecked<I: AsIndex<L>>(&self, index: I) -> &T {
2274        let offset = self.layout.offset_unchecked(index.as_index());
2275        unsafe { self.data.get_unchecked(offset) }
2276    }
2277
2278    fn permute(&mut self, order: Self::Index<'_>)
2279    where
2280        L: MutLayout,
2281    {
2282        self.layout = self.layout.permuted(order);
2283    }
2284
2285    fn to_vec(&self) -> Vec<T>
2286    where
2287        T: Clone,
2288    {
2289        self.to_vec_in(GlobalAlloc::new())
2290    }
2291
2292    fn to_vec_in<A: Alloc>(&self, alloc: A) -> Vec<T>
2293    where
2294        T: Clone,
2295    {
2296        let len = self.len();
2297        let mut buf = alloc.alloc(len);
2298
2299        if let Some(data) = self.data() {
2300            buf.extend_from_slice(data);
2301        } else {
2302            copy_into_slice(self.as_dyn(), &mut buf.spare_capacity_mut()[..len]);
2303
2304            // Safety: We initialized `len` elements.
2305            unsafe { buf.set_len(len) }
2306        }
2307
2308        buf
2309    }
2310
2311    fn to_shape<SH: IntoLayout>(&self, shape: SH) -> TensorBase<Vec<Self::Elem>, SH::Layout>
2312    where
2313        T: Clone,
2314    {
2315        TensorBase {
2316            data: self.to_vec(),
2317            layout: self
2318                .layout
2319                .reshaped_for_copy(shape)
2320                .expect("reshape failed"),
2321        }
2322    }
2323
2324    fn transpose(&mut self)
2325    where
2326        L: MutLayout,
2327    {
2328        self.layout = self.layout.transposed();
2329    }
2330}
2331
2332impl<T, S: Storage<Elem = T>, const N: usize> TensorBase<S, NdLayout<N>> {
2333    /// Load an array of `M` elements from successive entries of a tensor along
2334    /// the `dim` axis.
2335    ///
2336    /// eg. If `base` is `[0, 1, 2]`, dim=0 and `M` = 4 this will return an
2337    /// array with values from indices `[0, 1, 2]`, `[1, 1, 2]` ... `[3, 1, 2]`.
2338    ///
2339    /// Panics if any of the array indices are out of bounds.
2340    #[inline]
2341    pub fn get_array<const M: usize>(&self, base: [usize; N], dim: usize) -> [T; M]
2342    where
2343        T: Copy + Default,
2344    {
2345        let offsets: [usize; M] = array_offsets(&self.layout, base, dim);
2346        let mut result = [T::default(); M];
2347        for i in 0..M {
2348            // Safety: `array_offsets` returns valid offsets
2349            result[i] = unsafe { *self.data.get_unchecked(offsets[i]) };
2350        }
2351        result
2352    }
2353}
2354
2355impl<T> TensorBase<Vec<T>, DynLayout> {
2356    /// Reshape this tensor in place. This is cheap if the tensor is contiguous,
2357    /// as only the layout will be changed, but requires copying data otherwise.
2358    #[track_caller]
2359    pub fn reshape(&mut self, shape: &[usize])
2360    where
2361        T: Clone,
2362    {
2363        self.reshape_in(GlobalAlloc::new(), shape)
2364    }
2365
2366    /// Variant of [`reshape`](TensorBase::reshape) which takes an allocator.
2367    #[track_caller]
2368    pub fn reshape_in<A: Alloc>(&mut self, alloc: A, shape: &[usize])
2369    where
2370        T: Clone,
2371    {
2372        if !self.is_contiguous() {
2373            self.data = self.to_vec_in(alloc);
2374        }
2375        let Ok(layout) = self.layout.reshaped_for_copy(shape) else {
2376            panic!(
2377                "element count mismatch reshaping {:?} to {:?}",
2378                self.shape(),
2379                shape
2380            );
2381        };
2382        self.layout = layout;
2383    }
2384}
2385
2386impl<'a, T, L: Layout> TensorBase<ViewMutData<'a, T>, L> {
2387    /// Divide this tensor into two mutable views along a given axis.
2388    ///
2389    /// Returns a `(left, right)` tuple of views, where the `left` view
2390    /// contains the slice from `[0, mid)` along `axis` and the `right`
2391    /// view contains the slice from `[mid, end)` along `axis`.
2392    #[allow(clippy::type_complexity)]
2393    pub fn split_at_mut(
2394        self,
2395        axis: usize,
2396        mid: usize,
2397    ) -> (
2398        TensorBase<ViewMutData<'a, T>, L>,
2399        TensorBase<ViewMutData<'a, T>, L>,
2400    )
2401    where
2402        L: MutLayout,
2403    {
2404        let (left, right) = self.layout.split(axis, mid);
2405        let (left_offset_range, left_layout) = left;
2406        let (right_offset_range, right_layout) = right;
2407        let (left_data, right_data) = self
2408            .data
2409            .split_mut(left_offset_range.clone(), right_offset_range.clone());
2410
2411        debug_assert_eq!(left_data.len(), left_layout.min_data_len());
2412        let left_view = TensorBase {
2413            data: left_data,
2414            layout: left_layout,
2415        };
2416
2417        debug_assert_eq!(right_data.len(), right_layout.min_data_len());
2418        let right_view = TensorBase {
2419            data: right_data,
2420            layout: right_layout,
2421        };
2422
2423        (left_view, right_view)
2424    }
2425
2426    /// Consume this view and return a mutable slice, if the tensor is
2427    /// contiguous.
2428    pub fn into_slice_mut(self) -> Option<&'a mut [T]> {
2429        let len = self.layout.min_data_len();
2430        self.is_contiguous().then(|| {
2431            // Safety: We verified that the slice is contiguous.
2432            let slice = unsafe { self.data.to_slice_mut() };
2433            &mut slice[..len]
2434        })
2435    }
2436}
2437
2438impl<T, L: FromShape> FromIterator<T> for TensorBase<Vec<T>, L>
2439where
2440    [usize; 1]: AsShape<L>,
2441{
2442    /// Create a new 1D tensor filled with an arithmetic sequence of values
2443    /// in the range `[start, end)` separated by `step`. If `step` is omitted,
2444    /// it defaults to 1.
2445    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> TensorBase<Vec<T>, L> {
2446        let data: Vec<T> = iter.into_iter().collect();
2447        TensorBase::from_data([data.len()].as_shape(), data)
2448    }
2449}
2450
2451impl<T, L: FromShape> From<Vec<T>> for TensorBase<Vec<T>, L>
2452where
2453    [usize; 1]: AsShape<L>,
2454{
2455    /// Create a 1D tensor from a vector.
2456    fn from(vec: Vec<T>) -> Self {
2457        Self::from_data([vec.len()].as_shape(), vec)
2458    }
2459}
2460
2461impl<'a, T, L: FromShape> From<&'a [T]> for TensorBase<ViewData<'a, T>, L>
2462where
2463    [usize; 1]: AsShape<L>,
2464{
2465    /// Create a 1D view from a slice.
2466    fn from(slice: &'a [T]) -> Self {
2467        Self::from_data([slice.len()].as_shape(), slice)
2468    }
2469}
2470
2471impl<'a, T, L: FromShape, const N: usize> From<&'a [T; N]> for TensorBase<ViewData<'a, T>, L>
2472where
2473    [usize; 1]: AsShape<L>,
2474{
2475    /// Create a 1D view from a slice of known length.
2476    fn from(slice: &'a [T; N]) -> Self {
2477        Self::from_data([slice.len()].as_shape(), slice.as_slice())
2478    }
2479}
2480
2481/// Return the offsets of `M` successive elements along the `dim` axis, starting
2482/// at index `base`.
2483///
2484/// Panics if any of the M element indices are out of bounds.
2485fn array_offsets<const N: usize, const M: usize>(
2486    layout: &NdLayout<N>,
2487    base: [usize; N],
2488    dim: usize,
2489) -> [usize; M] {
2490    assert!(
2491        base[dim] < usize::MAX - M && layout.size(dim) >= base[dim] + M,
2492        "array indices invalid"
2493    );
2494
2495    let offset = layout.must_offset(base);
2496    let stride = layout.stride(dim);
2497    let mut offsets = [0; M];
2498    for i in 0..M {
2499        offsets[i] = offset + i * stride;
2500    }
2501    offsets
2502}
2503
2504impl<T, S: StorageMut<Elem = T>, const N: usize> TensorBase<S, NdLayout<N>> {
2505    /// Store an array of `M` elements into successive entries of a tensor along
2506    /// the `dim` axis.
2507    ///
2508    /// See [`TensorBase::get_array`] for more details.
2509    #[inline]
2510    pub fn set_array<const M: usize>(&mut self, base: [usize; N], dim: usize, values: [T; M])
2511    where
2512        T: Copy,
2513    {
2514        let offsets: [usize; M] = array_offsets(&self.layout, base, dim);
2515
2516        for i in 0..M {
2517            // Safety: `array_offsets` returns valid offsets.
2518            unsafe { *self.data.get_unchecked_mut(offsets[i]) = values[i] };
2519        }
2520    }
2521}
2522
2523impl<T, S: Storage<Elem = T>> TensorBase<S, NdLayout<1>> {
2524    /// Convert this vector to a static array of length `M`.
2525    ///
2526    /// Panics if the length of this vector is not M.
2527    #[inline]
2528    pub fn to_array<const M: usize>(&self) -> [T; M]
2529    where
2530        T: Copy + Default,
2531    {
2532        self.get_array([0], 0)
2533    }
2534}
2535
2536impl<T, S: StorageMut<Elem = T>> TensorBase<S, NdLayout<1>> {
2537    /// Fill this vector with values from a static array of length `M`.
2538    ///
2539    /// Panics if the length of this vector is not M.
2540    #[inline]
2541    pub fn assign_array<const M: usize>(&mut self, values: [T; M])
2542    where
2543        T: Copy + Default,
2544    {
2545        self.set_array([0], 0, values)
2546    }
2547}
2548
2549/// View of a tensor with N dimensions.
2550pub type NdTensorView<'a, T, const N: usize> = TensorBase<ViewData<'a, T>, NdLayout<N>>;
2551
2552/// Owned tensor with N dimensions.
2553pub type NdTensor<T, const N: usize> = TensorBase<Vec<T>, NdLayout<N>>;
2554
2555/// Mutable view of a tensor with N dimensions.
2556pub type NdTensorViewMut<'a, T, const N: usize> = TensorBase<ViewMutData<'a, T>, NdLayout<N>>;
2557
2558/// Owned or borrowed tensor with N dimensions.
2559///
2560/// `CowNdTensor`s can be created using [`as_cow`](TensorBase::as_cow) (to
2561/// borrow) or [`into_cow`](TensorBase::into_cow).
2562///
2563/// The name comes from [`std::borrow::Cow`].
2564pub type CowNdTensor<'a, T, const N: usize> = TensorBase<CowData<'a, T>, NdLayout<N>>;
2565
2566/// View of a 2D tensor.
2567pub type Matrix<'a, T = f32> = NdTensorView<'a, T, 2>;
2568
2569/// Mutable view of a 2D tensor.
2570pub type MatrixMut<'a, T = f32> = NdTensorViewMut<'a, T, 2>;
2571
2572/// Owned tensor with a dynamic dimension count.
2573pub type Tensor<T = f32> = TensorBase<Vec<T>, DynLayout>;
2574
2575/// View of a tensor with a dynamic dimension count.
2576pub type TensorView<'a, T = f32> = TensorBase<ViewData<'a, T>, DynLayout>;
2577
2578/// Mutable view of a tensor with a dynamic dimension count.
2579pub type TensorViewMut<'a, T = f32> = TensorBase<ViewMutData<'a, T>, DynLayout>;
2580
2581/// Owned or borrowed tensor with a dynamic dimension count.
2582///
2583/// `CowTensor`s can be created using [`as_cow`](TensorBase::as_cow) (to
2584/// borrow) or [`into_cow`](TensorBase::into_cow).
2585///
2586/// The name comes from [`std::borrow::Cow`].
2587pub type CowTensor<'a, T> = TensorBase<CowData<'a, T>, DynLayout>;
2588
2589/// Reference-counted tensor with a dynamic dimension count.
2590///
2591/// This uses `Arc<Vec<T>>` rather than `Arc<[T]>` as the backing storage. This
2592/// adds an extra indirection when accessing the data, but it enables cheap
2593/// conversion between owned and reference-counted tensors.
2594pub type ArcTensor<T> = TensorBase<Arc<Vec<T>>, DynLayout>;
2595
2596/// Reference-counted tensor with N dimensions.
2597///
2598/// See also the notes for [`ArcTensor`].
2599pub type ArcNdTensor<T, const N: usize> = TensorBase<Arc<Vec<T>>, NdLayout<N>>;
2600
2601impl<T, S: Storage<Elem = T>, L: TrustedLayout, I: AsIndex<L>> Index<I> for TensorBase<S, L> {
2602    type Output = T;
2603
2604    /// Return the element at a given index.
2605    ///
2606    /// Panics if the index is out of bounds along any dimension.
2607    fn index(&self, index: I) -> &Self::Output {
2608        let offset = self.layout.must_offset(index.as_index());
2609
2610        // Safety: `TrustedLayout` guarantees offsets are < `min_data_len`.
2611        // TensorBase guarantees storage length is >= `min_data_len`.
2612        unsafe { self.data.get_unchecked(offset) }
2613    }
2614}
2615
2616impl<T, S: StorageMut<Elem = T>, L: TrustedLayout, I: AsIndex<L>> IndexMut<I> for TensorBase<S, L> {
2617    /// Return the element at a given index.
2618    ///
2619    /// Panics if the index is out of bounds along any dimension.
2620    fn index_mut(&mut self, index: I) -> &mut Self::Output {
2621        let index = index.as_index();
2622        let offset = self.layout.must_offset(index);
2623
2624        // Safety: `TrustedLayout` guarantees offsets are < `min_data_len`.
2625        // TensorBase guarantees storage length is >= `min_data_len`.
2626        unsafe { self.data.get_unchecked_mut(offset) }
2627    }
2628}
2629
2630impl<T, S: Storage<Elem = T> + Clone, L: Layout + Clone> Clone for TensorBase<S, L> {
2631    fn clone(&self) -> TensorBase<S, L> {
2632        let data = self.data.clone();
2633        TensorBase {
2634            data,
2635            layout: self.layout.clone(),
2636        }
2637    }
2638}
2639
2640impl<T, S: Storage<Elem = T> + Copy, L: Layout + Copy> Copy for TensorBase<S, L> {}
2641
2642impl<T: PartialEq, S: Storage<Elem = T>, L: Layout + Clone, V: AsView<Elem = T>> PartialEq<V>
2643    for TensorBase<S, L>
2644{
2645    fn eq(&self, other: &V) -> bool {
2646        self.shape().iter().eq(other.shape().iter()) && self.iter().eq(other.iter())
2647    }
2648}
2649
2650impl<T: Eq, S: Storage<Elem = T>, L: Layout + Clone> Eq for TensorBase<S, L> {}
2651
2652impl<T: Hash, S: Storage<Elem = T>, L: Layout + Clone> Hash for TensorBase<S, L> {
2653    fn hash<H: Hasher>(&self, state: &mut H) {
2654        for dim in self.shape().iter() {
2655            dim.hash(state);
2656        }
2657        for elem in self.iter() {
2658            elem.hash(state);
2659        }
2660    }
2661}
2662
2663impl<T, S: Storage<Elem = T>, const N: usize> From<TensorBase<S, NdLayout<N>>>
2664    for TensorBase<S, DynLayout>
2665{
2666    fn from(tensor: TensorBase<S, NdLayout<N>>) -> Self {
2667        Self {
2668            data: tensor.data,
2669            layout: tensor.layout.into(),
2670        }
2671    }
2672}
2673
2674impl<T, S1: Storage<Elem = T>, S2: Storage<Elem = T>, const N: usize>
2675    TryFrom<TensorBase<S1, DynLayout>> for TensorBase<S2, NdLayout<N>>
2676where
2677    S1: Into<S2>,
2678{
2679    type Error = DimensionError;
2680
2681    /// Convert a tensor or view with dynamic rank into a static rank one.
2682    ///
2683    /// Fails if `value` does not have `N` dimensions.
2684    fn try_from(value: TensorBase<S1, DynLayout>) -> Result<Self, Self::Error> {
2685        let layout: NdLayout<N> = value.layout().try_into()?;
2686        Ok(TensorBase {
2687            data: value.data.into(),
2688            layout,
2689        })
2690    }
2691}
2692
2693/// Trait for scalar (ie. non-array) values.
2694///
2695/// This is used to prevent generic types from being inferred as array types
2696/// in [`TensorBase::from`].
2697pub trait Scalar {}
2698
2699macro_rules! impl_scalar {
2700    ($ty:ty) => {
2701        impl Scalar for $ty {}
2702    };
2703}
2704impl_scalar!(bool);
2705impl_scalar!(u8);
2706impl_scalar!(i8);
2707impl_scalar!(u16);
2708impl_scalar!(i16);
2709impl_scalar!(u32);
2710impl_scalar!(i32);
2711impl_scalar!(u64);
2712impl_scalar!(i64);
2713impl_scalar!(usize);
2714impl_scalar!(isize);
2715impl_scalar!(f32);
2716impl_scalar!(f64);
2717impl_scalar!(String);
2718
2719// The `T: Scalar` bound avoids ambiguity when choosing a `Tensor::from`
2720// impl for a nested array literal, as it prevents `T` from matching an array
2721// type.
2722
2723impl<T: Clone + Scalar, L: Clone + FromShape> From<T> for TensorBase<Vec<T>, L>
2724where
2725    [usize; 0]: AsShape<L>,
2726{
2727    /// Construct a scalar tensor from a scalar value.
2728    fn from(value: T) -> Self {
2729        Self::from_scalar(value)
2730    }
2731}
2732
2733impl<T: Clone + Scalar, L: FromShape, const D0: usize> From<[T; D0]> for TensorBase<Vec<T>, L>
2734where
2735    [usize; 1]: AsShape<L>,
2736{
2737    /// Construct a 1D tensor from a 1D array.
2738    fn from(value: [T; D0]) -> Self {
2739        let data: Vec<T> = value.iter().cloned().collect();
2740        Self::from_data([D0].as_shape(), data)
2741    }
2742}
2743
2744impl<T: Clone + Scalar, L: FromShape, const D0: usize, const D1: usize> From<[[T; D1]; D0]>
2745    for TensorBase<Vec<T>, L>
2746where
2747    [usize; 2]: AsShape<L>,
2748{
2749    /// Construct a 2D tensor from a nested array.
2750    fn from(value: [[T; D1]; D0]) -> Self {
2751        let data: Vec<_> = value.iter().flat_map(|y| y.iter()).cloned().collect();
2752        Self::from_data([D0, D1].as_shape(), data)
2753    }
2754}
2755
2756impl<T: Clone + Scalar, L: FromShape, const D0: usize, const D1: usize, const D2: usize>
2757    From<[[[T; D2]; D1]; D0]> for TensorBase<Vec<T>, L>
2758where
2759    [usize; 3]: AsShape<L>,
2760{
2761    /// Construct a 3D tensor from a nested array.
2762    fn from(value: [[[T; D2]; D1]; D0]) -> Self {
2763        let data: Vec<_> = value
2764            .iter()
2765            .flat_map(|y| y.iter().flat_map(|z| z.iter()))
2766            .cloned()
2767            .collect();
2768        Self::from_data([D0, D1, D2].as_shape(), data)
2769    }
2770}
2771
2772/// A view of a tensor which does "weak" checking when indexing via
2773/// `view[<index>]`. This means that it does not bounds-check individual
2774/// dimensions, but does bounds-check the computed offset.
2775///
2776/// This offers a middle-ground between regular indexing, which bounds-checks
2777/// each index element, and unchecked indexing, which does no bounds-checking
2778/// at all and is thus unsafe.
2779pub struct WeaklyCheckedView<S: Storage, L: Layout> {
2780    base: TensorBase<S, L>,
2781}
2782
2783impl<T, S: Storage<Elem = T>, L: Layout> Layout for WeaklyCheckedView<S, L> {
2784    type Shape<'a>
2785        = L::Shape<'a>
2786    where
2787        Self: 'a;
2788    type Strides<'a>
2789        = L::Strides<'a>
2790    where
2791        Self: 'a;
2792    type Index<'a> = L::Index<'a>;
2793    type Indices = L::Indices;
2794
2795    fn ndim(&self) -> usize {
2796        self.base.ndim()
2797    }
2798
2799    fn offset(&self, index: Self::Index<'_>) -> Option<usize> {
2800        self.base.offset(index)
2801    }
2802
2803    fn len(&self) -> usize {
2804        self.base.len()
2805    }
2806
2807    fn shape(&self) -> Self::Shape<'_> {
2808        self.base.shape()
2809    }
2810
2811    fn strides(&self) -> Self::Strides<'_> {
2812        self.base.strides()
2813    }
2814
2815    fn indices(&self) -> Self::Indices {
2816        self.base.indices()
2817    }
2818}
2819
2820impl<T, S: Storage<Elem = T>, L: Layout, I: AsIndex<L>> Index<I> for WeaklyCheckedView<S, L> {
2821    type Output = T;
2822    fn index(&self, index: I) -> &Self::Output {
2823        let offset = self.base.layout.offset_unchecked(index.as_index());
2824        unsafe {
2825            // Safety: See comments in [Storage] trait.
2826            self.base.data.get(offset).expect("invalid offset")
2827        }
2828    }
2829}
2830
2831impl<T, S: StorageMut<Elem = T>, L: Layout, I: AsIndex<L>> IndexMut<I> for WeaklyCheckedView<S, L> {
2832    fn index_mut(&mut self, index: I) -> &mut Self::Output {
2833        let offset = self.base.layout.offset_unchecked(index.as_index());
2834        unsafe {
2835            // Safety: See comments in [Storage] trait.
2836            self.base.data.get_mut(offset).expect("invalid offset")
2837        }
2838    }
2839}
2840
2841#[cfg(test)]
2842mod tests;