pub struct Layout { /* private fields */ }Expand description
How a tensor’s logical shape maps onto its flat backing buffer.
A Layout bundles the shape, the per-axis stride (how many buffer
elements to step to advance one index along that axis), an offset into the
buffer, and a cached total len. Views, slices, transposes, and broadcasts
are all just new layouts over the same buffer, which is what makes those
operations zero-copy. See the layout docs for the full
model, including the adj_stride iteration trick.
You rarely build one by hand: a tensor’s layout fields are reachable directly
through the Dimension trait (t.shape(), t.stride(),
t.is_contiguous(), …), and t.layout() hands back the whole Layout.
Constructing one explicitly is mostly useful for shaping a skeleton slot.
§Examples
use candela::{Dimension, Layout, Tensor};
// Shape/stride are available straight off the tensor via `Dimension`.
let t = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], &[2, 3]);
assert_eq!(t.shape(), &[2, 3]);
assert_eq!(t.layout(), &Layout::new(&[2, 3]));
// Or build one directly.
let l = Layout::new(&[2, 3]);
assert_eq!(l.stride(), &[3, 1]);
assert!(l.is_contiguous());Implementations§
Source§impl Layout
impl Layout
Sourcepub fn empty() -> Self
pub fn empty() -> Self
Build the empty layout: shape [0], length 0.
§Examples
use candela::Layout;
assert!(Layout::empty().is_empty());Sourcepub fn from_raw_parts(
shape: Box<[usize]>,
stride: Box<[i32]>,
adj_stride: Box<[i32]>,
offset: usize,
len: usize,
) -> Self
pub fn from_raw_parts( shape: Box<[usize]>, stride: Box<[i32]>, adj_stride: Box<[i32]>, offset: usize, len: usize, ) -> Self
Assemble a layout from already-computed fields, without validation.
An escape hatch for callers that have already worked out every field,
including the adj_stride iteration helper. Prefer new or
from_strided, which derive those for you.
§Examples
use candela::Layout;
// A hand-built 2x3 contiguous layout, equivalent to `Layout::new(&[2, 3])`.
let l = Layout::from_raw_parts(
Box::from([2, 3]),
Box::from([3, 1]),
Box::from([1, 1]),
0,
6,
);
assert_eq!(l.shape(), &[2, 3]);
assert_eq!(l.len(), 6);Sourcepub fn from_strided(shape: &[usize], stride: &[i32], offset: usize) -> Self
pub fn from_strided(shape: &[usize], stride: &[i32], offset: usize) -> Self
Build a layout for shape with an explicit stride and offset.
The adj_stride iteration helper is derived for you. Use this to describe
non-contiguous data - a stride of 0 on an axis, for instance, repeats
that axis (broadcasting).
§Examples
use candela::Layout;
// Column-major 2x3: advancing a column steps 1, advancing a row steps 2.
let l = Layout::from_strided(&[2, 3], &[1, 2], 0);
assert_eq!(l.shape(), &[2, 3]);
assert_eq!(l.stride(), &[1, 2]);Sourcepub fn with_offset(self, offset: usize) -> Self
pub fn with_offset(self, offset: usize) -> Self
Sourcepub fn view(&self, shape: &[usize]) -> Result<Self, OpError>
pub fn view(&self, shape: &[usize]) -> Result<Self, OpError>
Derive a layout with a new shape but the same element count.
§Errors
Returns OpError::InvalidViewShape if shape’s element count differs
from this layout’s, or OpError::NonContiguousView if this layout is
not contiguous (viewing needs a contiguous source).
§Examples
use candela::Layout;
let l = Layout::new(&[2, 3]);
assert_eq!(l.view(&[3, 2])?.shape(), &[3, 2]);
assert!(l.view(&[4, 4]).is_err()); // 16 != 6 elementsSourcepub fn slice(&self, range: &[SliceRange]) -> Result<Self, OpError>
pub fn slice(&self, range: &[SliceRange]) -> Result<Self, OpError>
Derive the layout of a sub-region, one SliceRange per leading axis.
Axes without a range are taken in full. Build the range list with the
s! macro.
§Errors
Returns OpError::AxesOutOfBounds if range has more entries than the
layout has axes, or a slice error if a range is empty or runs past its axis.
§Examples
use candela::{s, Layout};
let l = Layout::new(&[2, 3]);
let sub = l.slice(s![0..1, 1..3])?;
assert_eq!(sub.shape(), &[1, 2]);Sourcepub fn transpose(&self) -> Self
pub fn transpose(&self) -> Self
Reverse the order of every axis (a full transpose), swapping both the shape and the stride end for end.
§Examples
use candela::Layout;
let t = Layout::new(&[2, 3]).transpose();
assert_eq!(t.shape(), &[3, 2]);
assert_eq!(t.stride(), &[1, 3]);Sourcepub fn transpose_axes(&self, axes: &[usize]) -> Result<Self, OpError>
pub fn transpose_axes(&self, axes: &[usize]) -> Result<Self, OpError>
Reorders the axes by an explicit permutation.
axes must list every axis index exactly once: transpose_axes(&[1, 0])
is the plain 2-D .transpose(), while &[0, 2, 1]
swaps only the last two axes of a rank-3 layout and leaves the first alone.
§Examples
use candela::Layout;
let l = Layout::new(&[1, 2, 3]);
let s = l.transpose_axes(&[0, 2, 1])?;
assert_eq!(s.shape(), &[1, 3, 2]);§Errors
Returns OpError::NotEnoughAxes if axes doesn’t have one entry per
axis, or OpError::AxesOutOfBounds if an index is out of range or
repeated (so the list isn’t a valid permutation).
Sourcepub fn broadcast(&self, shape: &[usize]) -> Result<Self, OpError>
pub fn broadcast(&self, shape: &[usize]) -> Result<Self, OpError>
Expands the layout to a larger shape along new or size-1 axes.
Broadcasting follows NumPy’s right-aligned rules: a target axis must either match the source or expand from size 1, and extra leading axes are added on the left. The repeated axes are faked with zero strides.
§Examples
use candela::Layout;
let row = Layout::new(&[1, 3]);
let b = row.broadcast(&[2, 3])?;
assert_eq!(b.shape(), &[2, 3]);§Errors
Returns OpError::CannotBroadcast if the target shape has fewer axes
than the source, or an axis is neither equal to the source nor expandable
from 1.
Sourcepub fn shape_as_3d(&self) -> [usize; 3]
pub fn shape_as_3d(&self) -> [usize; 3]
Collapses the shape into a canonical [batch, rows, cols] triple.
The last two axes become rows and cols; everything above them is
folded into a single batch count, and ranks below 3 are padded with
leading ones. The matmul kernel uses this to treat any rank uniformly.
§Examples
use candela::Layout;
assert_eq!(Layout::new(&[5]).shape_as_3d(), [1, 1, 5]);
assert_eq!(Layout::new(&[2, 3]).shape_as_3d(), [1, 2, 3]);
assert_eq!(Layout::new(&[2, 3, 4]).shape_as_3d(), [2, 3, 4]);
assert_eq!(Layout::new(&[6, 2, 3, 4]).shape_as_3d(), [12, 3, 4]);Sourcepub fn rotate_axis_innermost(&self, axis: usize) -> Result<Self, OpError>
pub fn rotate_axis_innermost(&self, axis: usize) -> Result<Self, OpError>
Cyclically rotates the axes so that iterating the layout walks along
axis: axis becomes the innermost (fastest-varying) dimension, and
every axis above it (0..=axis) is pulled inward as the surrounding
block, so a flat iterator steps through all of their index combinations
before advancing the trailing axes. The trailing axes (axis+1..) stay
outermost in their original order.
§Examples
use candela::Layout;
let r = Layout::new(&[2, 3, 4]).rotate_axis_innermost(0)?;
assert_eq!(r.shape(), &[3, 4, 2]);§Errors
Returns OpError::AxesOutOfBounds if axis is past the layout’s rank.
Sourcepub fn is_contiguous(&self) -> bool
pub fn is_contiguous(&self) -> bool
Returns true if a flat walk visits every element in row-major order with
no gaps - i.e. the layout has not been transposed, broadcast, or sliced
down the inner axes.
§Examples
use candela::Layout;
assert!(Layout::new(&[3, 4]).is_contiguous());
assert!(!Layout::new(&[3, 4]).transpose().is_contiguous());Sourcepub fn is_contiguous_at_axis(&self, axis: usize) -> bool
pub fn is_contiguous_at_axis(&self, axis: usize) -> bool
Like is_contiguous, but only checks the axes from
axis inward, ignoring how the outer axes are arranged.
An out-of-range axis returns false.
§Examples
use candela::Layout;
let l = Layout::new(&[2, 3]);
assert!(l.is_contiguous_at_axis(0));
assert!(!l.is_contiguous_at_axis(9)); // out of rangeSourcepub fn is_transposed(&self) -> bool
pub fn is_transposed(&self) -> bool
Returns true if any axis runs backwards relative to a contiguous layout -
the signature a transpose leaves behind. Broadcast axes (zero stride) are
not counted.
§Examples
use candela::Layout;
assert!(!Layout::new(&[3, 4]).is_transposed());
assert!(Layout::new(&[3, 4]).transpose().is_transposed());Sourcepub fn is_transposed_at_axis(&self, axis: usize) -> bool
pub fn is_transposed_at_axis(&self, axis: usize) -> bool
Like is_transposed, but tests a single axis.
An out-of-range axis returns false.
§Examples
use candela::Layout;
let l = Layout::new(&[3, 4]); // fresh: no axis is transposed
assert!(!l.is_transposed_at_axis(0));
assert!(!l.is_transposed_at_axis(9)); // out of rangeSourcepub fn is_last_axes_transposed(&self) -> bool
pub fn is_last_axes_transposed(&self) -> bool
Returns true for a 2-D layout whose two axes are transposed; always
false for any other rank.
§Examples
use candela::Layout;
assert!(Layout::new(&[3, 4]).transpose().is_last_axes_transposed());
assert!(!Layout::new(&[3, 4]).is_last_axes_transposed());Sourcepub fn shape(&self) -> &[usize]
pub fn shape(&self) -> &[usize]
The size of each axis.
§Examples
use candela::Layout;
assert_eq!(Layout::new(&[2, 3]).shape(), &[2, 3]);Sourcepub fn stride(&self) -> &[i32]
pub fn stride(&self) -> &[i32]
The per-axis stride: how many buffer elements to step to advance one index along that axis.
§Examples
use candela::Layout;
assert_eq!(Layout::new(&[2, 3]).stride(), &[3, 1]);Sourcepub fn adj_stride(&self) -> &[i32]
pub fn adj_stride(&self) -> &[i32]
The adjacent stride: the per-axis step a flat iterator applies when it rolls over into the next axis. See the layout docs.
§Examples
use candela::Layout;
// All ones for a freshly built contiguous layout.
assert_eq!(Layout::new(&[2, 3]).adj_stride(), &[1, 1]);Sourcepub fn offset(&self) -> usize
pub fn offset(&self) -> usize
The starting index into the backing buffer.
§Examples
use candela::Layout;
assert_eq!(Layout::new(&[4]).with_offset(3).offset(), 3);