Skip to main content

Layout

Struct Layout 

Source
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

Source

pub fn new(shape: &[usize]) -> Self

Build a contiguous, row-major layout for shape.

§Panics

Panics if shape is empty (a tensor must have rank >= 1).

§Examples
use candela::Layout;
let l = Layout::new(&[2, 3]);
assert_eq!(l.shape(), &[2, 3]);
assert_eq!(l.stride(), &[3, 1]);
assert_eq!(l.len(), 6);
Source

pub fn empty() -> Self

Build the empty layout: shape [0], length 0.

§Examples
use candela::Layout;
assert!(Layout::empty().is_empty());
Source

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);
Source

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]);
Source

pub fn with_offset(self, offset: usize) -> Self

Return this layout with its offset into the backing buffer replaced.

Builder-style, usually chained onto new.

§Examples
use candela::Layout;
let l = Layout::new(&[4]).with_offset(3);
assert_eq!(l.offset(), 3);
Source

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 elements
Source

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]);
Source

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]);
Source

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).

Source

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.

Source

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]);
Source

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.

Source

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());
Source

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 range
Source

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());
Source

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 range
Source

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());
Source

pub fn shape(&self) -> &[usize]

The size of each axis.

§Examples
use candela::Layout;
assert_eq!(Layout::new(&[2, 3]).shape(), &[2, 3]);
Source

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]);
Source

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]);
Source

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);
Source

pub fn len(&self) -> usize

The total number of elements, i.e. the product of the shape.

§Examples
use candela::Layout;
assert_eq!(Layout::new(&[2, 3, 4]).len(), 24);
Source

pub fn is_empty(&self) -> bool

Returns true if the layout has no elements.

§Examples
use candela::Layout;
assert!(Layout::empty().is_empty());
assert!(!Layout::new(&[3]).is_empty());

Trait Implementations§

Source§

impl Clone for Layout

Source§

fn clone(&self) -> Layout

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Layout

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for Layout

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Eq for Layout

Source§

impl Hash for Layout

Source§

fn hash<H: Hasher>(&self, state: &mut H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl PartialEq for Layout

Source§

fn eq(&self, other: &Self) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.