pub struct Tensor { /* private fields */ }Expand description
A tensor: shared storage viewed through a layout.
Cloning a tensor is cheap — it clones the layout and bumps the storage
refcount, never the data. View operations (reshape, permute,
narrow, …) produce new tensors over the same storage whenever the
layout arithmetic allows it.
Implementations§
Source§impl Tensor
impl Tensor
Sourcepub fn eye(n: usize) -> Tensor
pub fn eye(n: usize) -> Tensor
The n × n identity matrix on the CPU.
§Panics
Panics if n * n overflows usize. Unchecked, this wrapped to a
short allocation and surfaced as an index-out-of-bounds below.
Sourcepub fn diag(&self) -> Result<Tensor>
pub fn diag(&self) -> Result<Tensor>
The diagonal of every matrix in a [.., n, n] tensor, as [.., n].
Differentiable.
Sourcepub fn diag_embed(&self) -> Result<Tensor>
pub fn diag_embed(&self) -> Result<Tensor>
Matrices with the vectors of a [.., n] tensor on their diagonals,
as [.., n, n]. Differentiable.
Sourcepub fn trace(&self) -> Result<Tensor>
pub fn trace(&self) -> Result<Tensor>
The trace of every matrix in a [.., n, n] tensor, as [..].
Differentiable.
Sourcepub fn cholesky(&self) -> Result<Tensor>
pub fn cholesky(&self) -> Result<Tensor>
Lower-triangular Cholesky factor L of every symmetric
positive-definite matrix in a [.., n, n] tensor, L Lᵀ = A.
Reads the lower triangle. A matrix that is not positive definite
is a typed Error::InvalidArgument naming the batch index and
pivot. The backward pass runs on the host and returns to the input’s
device; its intermediates are f64, but it takes its inputs as
f32, so an f64 gradient carries f32 precision.
§Gradient convention
The gradient (Murray 2016) is taken with respect to symmetric
perturbations of the input: d logdet/dA = A⁻¹, which is the
standard result and what PyTorch returns. Because the forward reads
only the lower triangle, an elementwise finite difference — which
perturbs one entry and so breaks symmetry — does not agree with it,
and oxmera_autograd::gradcheck cannot be used on cholesky,
logdet or det directly. Check them through a symmetrizer
((X + Xᵀ)/2), as tests/gradcheck.rs does, or against A⁻¹.
The practical consequence: feed these ops a symmetric matrix. Given an asymmetric one the forward silently uses the lower triangle while the gradient describes a symmetric matrix, and the two disagree.
Sourcepub fn logdet(&self) -> Result<Tensor>
pub fn logdet(&self) -> Result<Tensor>
ln det A of every SPD matrix in a [.., n, n] tensor, as [..],
through the Cholesky factor: 2 Σ ln diag(L). Differentiable
(the gradient is A⁻¹, symmetrized).
Sourcepub fn det(&self) -> Result<Tensor>
pub fn det(&self) -> Result<Tensor>
det A of every SPD matrix in a [.., n, n] tensor, as [..],
through the Cholesky factor. Differentiable. For an indefinite
matrix use eigh — this is the SPD determinant.
Sourcepub fn eigh(&self) -> Result<(Tensor, Tensor)>
pub fn eigh(&self) -> Result<(Tensor, Tensor)>
Eigen-decomposition of every symmetric matrix in a [.., n, n]
tensor: eigenvalues ascending as [.., n] and orthonormal
eigenvectors as the columns of [.., n, n] (A V = V Λ). Not
differentiable.
The input must be symmetric to within EIGH_SYMMETRY_TOL
(relative); anything further is a typed Error::InvalidArgument
naming the batch index and the worst offending pair.
Before 0.4.0 the full matrix was read and silently symmetrized, so
a non-symmetric input returned the eigenpairs of (A + Aᵀ)/2 — a
different matrix — with no error: [[1, 2], [5, 1]] answered
[-2.5, 4.5] where the true eigenvalues are 1 ± √10, and the
returned pair did not satisfy A v = λ v for the A that was
passed. The tolerance keeps the case the check exists to permit —
a covariance or Gram matrix assembled as XᵀX / n in f32, which
is symmetric in intent and asymmetric in the last few bits — while
refusing a transpose that was actually missed.
Source§impl Tensor
impl Tensor
Sourcepub fn scalar_on(like: &Tensor, value: f32) -> Result<Tensor>
pub fn scalar_on(like: &Tensor, value: f32) -> Result<Tensor>
A scalar constant with the dtype and device of like (plumbing for
VJPs and scalar operator overloads).
Sourcepub fn to_dtype(&self, dtype: DType) -> Result<Tensor>
pub fn to_dtype(&self, dtype: DType) -> Result<Tensor>
This tensor’s elements converted to dtype (F32 ↔ F64, or
I64 → float). A no-op clone for the same dtype. CPU only for
F64; differentiable (the gradient converts back).
Sourcepub fn gt_mask(&self, rhs: &Tensor) -> Result<Tensor>
pub fn gt_mask(&self, rhs: &Tensor) -> Result<Tensor>
Elementwise a > b as a 0.0/1.0 mask. Not differentiable.
Sourcepub fn eq_mask(&self, rhs: &Tensor) -> Result<Tensor>
pub fn eq_mask(&self, rhs: &Tensor) -> Result<Tensor>
Elementwise a == b as a 0.0/1.0 mask. Not differentiable.
Sourcepub fn add_scalar(&self, s: f32) -> Result<Tensor>
pub fn add_scalar(&self, s: f32) -> Result<Tensor>
Add a scalar, broadcasting.
Sourcepub fn mul_scalar(&self, s: f32) -> Result<Tensor>
pub fn mul_scalar(&self, s: f32) -> Result<Tensor>
Multiply by a scalar, broadcasting.
Sourcepub fn matmul(&self, rhs: &Tensor) -> Result<Tensor>
pub fn matmul(&self, rhs: &Tensor) -> Result<Tensor>
Matrix product with NumPy/PyTorch batch semantics.
The last two dimensions are the matrix ([.., m, k] x [.., k, n]
→ [.., m, n]); every leading dimension is a batch dimension, and
batch dimensions broadcast against each other (1 against b, and
a missing leading dimension counts as 1). A rank-2 operand is one
matrix for every batch of the other. The result is rank 2 only
when both operands are: [2, 2, 3] x [1, 3, 2] is [2, 2, 2],
[m, k] x [b, k, n] is [b, m, n], [2, 1, 3, 4] x [5, 4, 6] is
[2, 5, 3, 6].
Backends implement the rank-2/rank-3 contract of
plan_matmul; higher ranks are
lowered here — the batch dimensions are broadcast (a zero-stride
view, materialized only when an operand’s batch really has to be
repeated), flattened to one batch axis, multiplied, and unflattened
— and every step is a recorded op, so the gradient needs no VJP of
its own.
Sourcepub fn sum(&self, axes: &[usize]) -> Result<Tensor>
pub fn sum(&self, axes: &[usize]) -> Result<Tensor>
Sum over axes (empty means all), removing them from the shape.
Sourcepub fn sum_keepdim(&self, axes: &[usize], keepdim: bool) -> Result<Tensor>
pub fn sum_keepdim(&self, axes: &[usize], keepdim: bool) -> Result<Tensor>
Sum over axes with explicit keepdim.
Sourcepub fn max_keepdim(&self, axes: &[usize], keepdim: bool) -> Result<Tensor>
pub fn max_keepdim(&self, axes: &[usize], keepdim: bool) -> Result<Tensor>
Maximum over axes with explicit keepdim.
Sourcepub fn mean(&self, axes: &[usize]) -> Result<Tensor>
pub fn mean(&self, axes: &[usize]) -> Result<Tensor>
Mean over axes (empty means all) — composite, so its gradient
flows through sum and scalar multiply.
Reducing over a zero-length extent is a typed error; see
Tensor::mean_keepdim.
Sourcepub fn mean_keepdim(&self, axes: &[usize], keepdim: bool) -> Result<Tensor>
pub fn mean_keepdim(&self, axes: &[usize], keepdim: bool) -> Result<Tensor>
Mean over axes with explicit keepdim.
§Errors
Reducing over a zero-length extent is an
Error::InvalidArgument: the mean of nothing is 0/0, and there
is no value that is the right answer.
Until 0.4.0 this returned NaN, which is not an answer either but
looks like one. A NaN from an empty last batch does not fail — it
flows into the loss, then into every gradient, and surfaces an
epoch later as a model that stopped learning for no visible reason.
argmax already refused the same input for the same reason; this
is the pair being made consistent.
sum and max still return their identities (0 and -inf) over
an empty extent, and deliberately: those compose correctly under
further reduction, and mean does not. The whole family is
tabulated in docs/LIMITATIONS.md.
Sourcepub fn argmax(&self, dim: usize, keepdim: bool) -> Result<Tensor>
pub fn argmax(&self, dim: usize, keepdim: bool) -> Result<Tensor>
Index of the maximum along dim, as an I64 tensor. Not
differentiable.
Sourcepub fn softmax(&self, dim: usize) -> Result<Tensor>
pub fn softmax(&self, dim: usize) -> Result<Tensor>
Numerically stable softmax along dim — composite.
Sourcepub fn log_softmax(&self, dim: usize) -> Result<Tensor>
pub fn log_softmax(&self, dim: usize) -> Result<Tensor>
Numerically stable log-softmax along dim — composite.
Sourcepub fn index_select(&self, dim: usize, indices: &Tensor) -> Result<Tensor>
pub fn index_select(&self, dim: usize, indices: &Tensor) -> Result<Tensor>
Rows of self along dim selected by indices (I64, on the CPU).
Source§impl Tensor
impl Tensor
Sourcepub fn from_storage(storage: Arc<Storage>, layout: Layout) -> Result<Self>
pub fn from_storage(storage: Arc<Storage>, layout: Layout) -> Result<Self>
A tensor over existing storage with an explicit layout.
Errors when the layout addresses elements outside the storage.
Sourcepub fn from_vec_f32(data: Vec<f32>, shape: impl Into<Shape>) -> Result<Self>
pub fn from_vec_f32(data: Vec<f32>, shape: impl Into<Shape>) -> Result<Self>
A contiguous CPU tensor holding data with shape shape.
Errors when data.len() does not equal shape.numel().
Sourcepub fn from_slice(data: &[f32], shape: impl Into<Shape>) -> Result<Self>
pub fn from_slice(data: &[f32], shape: impl Into<Shape>) -> Result<Self>
A contiguous CPU tensor copying data with shape shape.
Sourcepub fn from_vec_f64(data: Vec<f64>, shape: impl Into<Shape>) -> Result<Self>
pub fn from_vec_f64(data: Vec<f64>, shape: impl Into<Shape>) -> Result<Self>
A contiguous CPU F64 tensor holding data. f64 tensors live on
the CPU (the GPU backends carry f32); every op the CPU backend
implements accepts them, and Tensor::to_dtype converts.
Sourcepub fn from_vec_i64(data: Vec<i64>, shape: impl Into<Shape>) -> Result<Self>
pub fn from_vec_i64(data: Vec<i64>, shape: impl Into<Shape>) -> Result<Self>
A contiguous CPU I64 tensor holding data (indices, targets).
Sourcepub fn zeros(shape: impl Into<Shape>) -> Self
pub fn zeros(shape: impl Into<Shape>) -> Self
A CPU tensor of zeros.
§Panics
Panics if the shape’s element count overflows usize. Build the
shape from untrusted input through Tensor::try_zeros for a
typed error instead.
Sourcepub fn try_zeros(shape: impl Into<Shape>) -> Result<Self>
pub fn try_zeros(shape: impl Into<Shape>) -> Result<Self>
A CPU tensor of zeros, or Error::InvalidArgument when the
shape’s element count overflows usize.
Sourcepub fn ones(shape: impl Into<Shape>) -> Self
pub fn ones(shape: impl Into<Shape>) -> Self
A CPU tensor of ones.
§Panics
Panics if the shape’s element count overflows usize; see
Tensor::try_ones.
Sourcepub fn try_ones(shape: impl Into<Shape>) -> Result<Self>
pub fn try_ones(shape: impl Into<Shape>) -> Result<Self>
A CPU tensor of ones, or Error::InvalidArgument when the
shape’s element count overflows usize.
Sourcepub fn full(shape: impl Into<Shape>, value: f32) -> Self
pub fn full(shape: impl Into<Shape>, value: f32) -> Self
A CPU tensor filled with value.
§Panics
Panics if the shape’s element count overflows usize; see
Tensor::try_full.
Sourcepub fn try_full(shape: impl Into<Shape>, value: f32) -> Result<Self>
pub fn try_full(shape: impl Into<Shape>, value: f32) -> Result<Self>
A CPU tensor filled with value, or Error::InvalidArgument
when the shape’s element count overflows usize.
Sourcepub fn randn(shape: impl Into<Shape>) -> Self
pub fn randn(shape: impl Into<Shape>) -> Self
Standard-normal random CPU tensor, seeded from the OS.
Sourcepub fn randn_with_seed(shape: impl Into<Shape>, seed: u64) -> Self
pub fn randn_with_seed(shape: impl Into<Shape>, seed: u64) -> Self
Standard-normal random CPU tensor with a fixed seed, for reproducible tests and examples.
Sourcepub fn get_f32(&self, index: &[usize]) -> Result<f32>
pub fn get_f32(&self, index: &[usize]) -> Result<f32>
The element at a logical index, as f32.
Errors on rank mismatch, out-of-bounds, non-float dtype, or non-CPU storage.
Sourcepub fn get_f64(&self, index: &[usize]) -> Result<f64>
pub fn get_f64(&self, index: &[usize]) -> Result<f64>
The element at a logical index, as f64 (from an F64 tensor).
Sourcepub fn to_vec_f32(&self) -> Result<Vec<f32>>
pub fn to_vec_f32(&self) -> Result<Vec<f32>>
Every element in logical (row-major) order, as f32, from CPU
storage.
Sourcepub fn to_vec_f64(&self) -> Result<Vec<f64>>
pub fn to_vec_f64(&self) -> Result<Vec<f64>>
Every element in logical (row-major) order, as f64, from an F64
CPU tensor (use Tensor::to_dtype first for an f32 one).
Sourcepub fn to_vec_i64(&self) -> Result<Vec<i64>>
pub fn to_vec_i64(&self) -> Result<Vec<i64>>
Every element in logical (row-major) order, as i64.
Sourcepub fn reshape(&self, shape: impl Into<Shape>) -> Result<Self>
pub fn reshape(&self, shape: impl Into<Shape>) -> Result<Self>
A view (or copy, when this view is not contiguous) with the same elements in a new shape.
Sourcepub fn permute(&self, perm: &[usize]) -> Result<Self>
pub fn permute(&self, perm: &[usize]) -> Result<Self>
A view with dimensions reordered by perm (a permutation of
0..ndim).
Sourcepub fn transpose(&self, d0: usize, d1: usize) -> Result<Self>
pub fn transpose(&self, d0: usize, d1: usize) -> Result<Self>
A view with dimensions d0 and d1 swapped.
Sourcepub fn narrow(&self, dim: usize, start: usize, len: usize) -> Result<Self>
pub fn narrow(&self, dim: usize, start: usize, len: usize) -> Result<Self>
A view of len elements of dimension dim starting at start.
Sourcepub fn slice(&self, dim: usize, range: Range<usize>) -> Result<Self>
pub fn slice(&self, dim: usize, range: Range<usize>) -> Result<Self>
A view of range along dim — sugar over Tensor::narrow.
Sourcepub fn broadcast_to(&self, shape: impl Into<Shape>) -> Result<Self>
pub fn broadcast_to(&self, shape: impl Into<Shape>) -> Result<Self>
A zero-copy broadcast view to shape (stride 0 on expanded axes).
Sourcepub fn broadcast_view(&self, shape: &Shape) -> Result<Self>
pub fn broadcast_view(&self, shape: &Shape) -> Result<Self>
A broadcast view that records nothing on the tape — backend
plumbing; prefer Tensor::broadcast_to in user code.
Sourcepub fn unsqueeze(&self, dim: usize) -> Result<Self>
pub fn unsqueeze(&self, dim: usize) -> Result<Self>
A view with a new size-1 dimension inserted at dim.
Sourcepub fn contiguous(&self) -> Result<Self>
pub fn contiguous(&self) -> Result<Self>
This tensor’s elements, in logical order, in fresh contiguous storage on the same device. A no-op clone when already contiguous.
Sourcepub fn contiguous_untracked(&self) -> Result<Self>
pub fn contiguous_untracked(&self) -> Result<Self>
The contiguous copy without autograd recording — backend plumbing;
prefer Tensor::contiguous in user code.
Sourcepub fn requires_grad_(self, requires: bool) -> Self
pub fn requires_grad_(self, requires: bool) -> Self
Mark (or unmark) this tensor as a gradient-accumulating leaf, in place, returning it for chaining.
Sourcepub fn requires_grad(&self) -> bool
pub fn requires_grad(&self) -> bool
Whether gradients accumulate on this tensor during backward
— true for leaves marked with requires_grad_.
This answers a narrower question than PyTorch’s requires_grad:
a tensor computed from such a leaf is on the tape but does not
accumulate a gradient of its own, so it reports false here. Ask
is_tracked for “is this on the graph at all”.
Sourcepub fn is_tracked(&self) -> bool
pub fn is_tracked(&self) -> bool
Whether this tensor participates in the autograd tape at all —
true for a leaf that requires grad and for anything computed from
one while recording was enabled; false for constants and for
everything produced under no_grad.
This is the predicate that observes no_grad:
use oxmera_tensor::tensor::Tensor;
use oxmera_tensor::autograd::no_grad;
let a = Tensor::from_slice(&[1.0, 2.0], [2]).unwrap().requires_grad_(true);
assert!(a.mul_scalar(3.0).unwrap().is_tracked());
assert!(!no_grad(|| a.mul_scalar(3.0).unwrap()).is_tracked());Sourcepub fn grad(&self) -> Option<Tensor>
pub fn grad(&self) -> Option<Tensor>
The accumulated gradient, if a backward pass has produced one.
Sourcepub fn backward(&self) -> Result<()>
pub fn backward(&self) -> Result<()>
Propagate gradients from this scalar through the recorded tape.
Errors when the tensor is not a scalar; use
Tensor::backward_with to seed a non-scalar output.
Sourcepub fn backward_with(&self, seed: Tensor) -> Result<()>
pub fn backward_with(&self, seed: Tensor) -> Result<()>
Propagate gradients seeding this tensor’s gradient with seed.
Trait Implementations§
Auto Trait Implementations§
impl !RefUnwindSafe for Tensor
impl !UnwindSafe for Tensor
impl Freeze for Tensor
impl Send for Tensor
impl Sync for Tensor
impl Unpin for Tensor
impl UnsafeUnpin for Tensor
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more