Skip to main content

EagerTensor

Struct EagerTensor 

Source
pub struct EagerTensor { /* private fields */ }
Expand description

Eager tensor with reverse-mode autodiff over concrete tensor values.

This executes each primitive immediately and records a lightweight reverse DAG for backward(). Gradients accumulate across repeated backward() calls until they are cleared explicitly.

§Examples

use tenferro_cpu::CpuBackend;
use tenferro_ad::{EagerRuntime, EagerTensor, Tensor};

let ctx = EagerRuntime::with_cpu_backend(CpuBackend::new())?;
let x = EagerTensor::requires_grad_in(Tensor::from_vec_col_major(vec![3], vec![1.0_f64, 2.0, 3.0]).unwrap(), ctx)?;
for _ in 0..2 {
    let loss = x.runtime().with_eager_session(|s| {
        let squared = s.mul(&x, &x)?;
        s.reduce_sum(&squared, Some(&[0]))
    })?;
    loss.backward()?;
}

assert_eq!(x.grad()?.unwrap().as_slice::<f64>().unwrap(), &[4.0, 8.0, 12.0]);
x.clear_grad()?;

assert!(x.grad().unwrap().is_none());

Implementations§

Source§

impl EagerTensor

Source

pub fn from_tensor_in(tensor: Tensor, ctx: Arc<EagerRuntime>) -> Result<Self>

Create an untracked eager tensor inside an existing eager context.

§Examples
use tenferro_cpu::CpuBackend;
use tenferro_ad::{EagerRuntime, EagerTensor, Tensor};

let ctx = EagerRuntime::with_cpu_backend(CpuBackend::new())?;
let x = EagerTensor::from_tensor_in(Tensor::from_vec_col_major(vec![2], vec![1.0_f64, 2.0]).unwrap(), ctx)?;

assert_eq!(x.value()?.as_slice::<f64>().unwrap(), &[1.0, 2.0]);
§Errors

Returns tenferro_runtime::Error::RuntimeState when metadata cannot be registered in the target context, or a typed tensor/backend error while materializing the source value.

Source

pub fn from_vec_col_major_in<T: TensorScalar>( shape: impl IntoShapeVec, data: Vec<T>, ctx: Arc<EagerRuntime>, ) -> Result<Self>

Create an untracked eager tensor from compact column-major data inside an existing eager runtime.

§Errors

Returns Error::TensorRuntime with tenferro_tensor::ValidationError::ShapeMismatch when the shape and data length disagree, or with tenferro_tensor::ValidationError::IntegerOverflow when shape arithmetic overflows. Returns Error::RuntimeState when eager metadata cannot be registered.

Source

pub fn requires_grad_in(tensor: Tensor, ctx: Arc<EagerRuntime>) -> Result<Self>

Create a tracked eager leaf inside an existing eager context.

§Examples
use tenferro_cpu::CpuBackend;
use tenferro_ad::{EagerRuntime, EagerTensor, Tensor};

let ctx = EagerRuntime::with_cpu_backend(CpuBackend::new())?;
let x = EagerTensor::requires_grad_in(Tensor::from_vec_col_major(vec![2], vec![1.0_f64, 2.0]).unwrap(), ctx)?;

assert!(x.grad().unwrap().is_none());
§Errors

Returns tenferro_runtime::Error::RuntimeState when gradient metadata cannot be registered in the target context, or a typed tensor/backend error while creating the leaf.

Source

pub fn detach(&self) -> Self

Detach this tensor from the reverse graph.

The returned tensor keeps the concrete value but no longer contributes gradients to the original graph.

§Examples
use tenferro_cpu::CpuBackend;
use tenferro_ad::{EagerRuntime, EagerTensor, Tensor};

let ctx = EagerRuntime::with_cpu_backend(CpuBackend::new())?;
let x = EagerTensor::requires_grad_in(Tensor::from_vec_col_major(vec![2], vec![1.0_f64, 2.0]).unwrap(), ctx)?;
let y = x.detach();

assert_eq!(y.value()?.as_slice::<f64>().unwrap(), &[1.0, 2.0]);
assert!(y.grad().unwrap().is_none());
Source

pub fn detach_into(&self, ctx: &Arc<EagerRuntime>) -> Result<Self>

Detach this tensor from its graph and re-register it in a different context as an untracked leaf.

§Examples
use tenferro_cpu::CpuBackend;
use tenferro_ad::{EagerRuntime, EagerTensor, Tensor};

let ctx_a = EagerRuntime::with_cpu_backend(CpuBackend::new())?;
let ctx_b = EagerRuntime::with_cpu_backend(CpuBackend::new())?;
let x = EagerTensor::requires_grad_in(Tensor::from_vec_col_major(vec![2], vec![1.0_f64, 2.0]).unwrap(), ctx_a)?;
let d = x.detach_into(&ctx_b)?;

assert!(!d.tracks_grad());
assert_eq!(d.ctx_id(), ctx_b.id());
§Errors

Returns Error::RuntimeState if the source cannot be materialized or the target context cannot register its metadata.

Source

pub fn value(&self) -> Result<ValueGuard<'_>>

Borrow the retained value without creating an owner or copy.

§Errors

Returns Error::RuntimeState when the retained allocation-group descriptor is unavailable or invalid.

Source

pub fn duplicate_value(&self) -> Result<Tensor>

Explicitly duplicate this value into a fresh standalone allocation.

§Errors

Returns Error::RuntimeState when the retained value or execution session is unavailable, or a typed host/backend error when the value cannot be materialized as a contiguous tensor.

§Examples
use tenferro_ad::{EagerRuntime, EagerTensor, Tensor};
use tenferro_cpu::CpuBackend;

let ctx = EagerRuntime::with_cpu_backend(CpuBackend::new())?;
let value = EagerTensor::from_tensor_in(
    Tensor::from_vec_col_major(vec![2], vec![1.0_f64, 2.0])?,
    ctx,
)?;
let duplicate = value.duplicate_value()?;
assert_eq!(duplicate.as_slice::<f64>()?, &[1.0, 2.0]);
Source

pub fn into_value(self) -> Result<Tensor, IntoValueError<Self>>

Consume this handle and structurally extract its retained allocation.

A shared handle is returned unchanged as IntoValueError::NotUnique. Group extraction failures return the unchanged handle and typed group error; no copy or fallback materialization is attempted.

§Errors

Returns IntoValueError::NotUnique when another handle retains the value, or IntoValueError::Extract when structural group extraction fails because the allocation is aliased or its descriptor is invalid.

§Examples
use tenferro_ad::{EagerRuntime, EagerTensor, Tensor};
use tenferro_cpu::CpuBackend;

let ctx = EagerRuntime::with_cpu_backend(CpuBackend::new())?;
let value = EagerTensor::from_tensor_in(
    Tensor::from_vec_col_major(vec![1], vec![3.0_f64])?,
    ctx,
)?;
let owner = value
    .into_value()
    .expect("a uniquely owned value should be extractable");
assert_eq!(owner.as_slice::<f64>()?, &[3.0]);
Source

pub fn dtype(&self) -> DType

Return this tensor’s scalar dtype without materializing through value.

Source

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

Return this tensor’s logical shape without materializing through value.

Source

pub fn tensor_read(&self) -> TensorRead<'_>

Borrow this tensor value as a TensorRead.

This is the preferred borrowed input boundary for executor calls. It preserves the option to replace eager storage with non-contiguous views without forcing callers through value.

§Panics

Panics if a validated eager value record becomes unavailable, which indicates an internal invariant violation.

Source

pub fn to_tensor(&self) -> Result<Tensor>

Materialize this eager tensor as an owned Tensor.

This is the owned materialization boundary for callers that need a standalone compact tensor. The operation is fallible because eager values may be backed by lazy or backend-resident storage.

§Errors

Returns Error::RuntimeState if backend state is unavailable, or a typed tensor backend error when contiguous materialization fails.

Source

pub fn grad(&self) -> Result<Option<GradientValue>>

Return the accumulated gradient currently stored for this tensor.

The stored gradient accumulates across repeated backward() calls until it is cleared explicitly.

For complex scalar losses, stored gradients use tenferro’s Hermitian-adjoint cotangent convention. See https://tensor4all.org/tenferro-rs/guides/complex-ad.html.

§Examples
use tenferro_cpu::CpuBackend;
use tenferro_ad::{EagerRuntime, EagerTensor, Tensor};

let ctx = EagerRuntime::with_cpu_backend(CpuBackend::new())?;
let x = EagerTensor::requires_grad_in(Tensor::from_vec_col_major(vec![2], vec![1.0_f64, 2.0]).unwrap(), ctx.clone()).unwrap();
let loss = ctx.with_eager_session(|s| {
    let y = s.exp(&x)?;
    s.reduce_sum(&y, Some(&[0]))
})?;
let _cotangents = loss.backward().unwrap();

let grad = x.grad()?.unwrap();
assert_eq!(grad.shape(), &[2]);
§Errors

Returns Error::RuntimeState if the gradient slot is poisoned or no longer available.

Source

pub fn clear_grad(&self) -> Result<()>

Clear the accumulated gradient stored for this tensor.

This only affects this tensor’s gradient slot. Other tensors in the same context retain their gradients until they are cleared explicitly or overwritten by later accumulation.

§Examples
use tenferro_cpu::CpuBackend;
use tenferro_ad::{EagerRuntime, EagerTensor, Tensor};

let ctx = EagerRuntime::with_cpu_backend(CpuBackend::new())?;
let x = EagerTensor::requires_grad_in(Tensor::from_vec_col_major(vec![3], vec![1.0_f64, 2.0, 3.0]).unwrap(), ctx.clone()).unwrap();
let y = EagerTensor::requires_grad_in(Tensor::from_vec_col_major(vec![3], vec![4.0_f64, 5.0, 6.0]).unwrap(), ctx).unwrap();
let loss = x.runtime().with_eager_session(|s| {
    let product = s.mul(&x, &y)?;
    s.reduce_sum(&product, Some(&[0]))
})?;
let _ = loss.backward().unwrap();

x.clear_grad()?;

assert!(x.grad()?.is_none());
assert!(y.grad()?.is_some());
§Errors

Returns Error::RuntimeState if the gradient slot lock is poisoned.

Source

pub fn tracks_grad(&self) -> bool

Report whether this tensor participates in gradient tracking.

Tracked tensors keep a gradient slot in their eager context; untracked tensors and detached tensors do not.

§Examples
use tenferro_cpu::CpuBackend;
use tenferro_ad::{EagerRuntime, EagerTensor, Tensor};

let ctx = EagerRuntime::with_cpu_backend(CpuBackend::new())?;
let plain = EagerTensor::from_tensor_in(Tensor::from_vec_col_major(vec![2], vec![1.0_f64, 2.0]).unwrap(), ctx.clone()).unwrap();
let tracked = EagerTensor::requires_grad_in(Tensor::from_vec_col_major(vec![2], vec![3.0_f64, 4.0]).unwrap(), ctx.clone()).unwrap();
let detached = tracked.detach();

assert!(!plain.tracks_grad());
assert!(tracked.tracks_grad());
assert!(!detached.tracks_grad());
Source

pub fn ctx_id(&self) -> ContextId

Return the opaque identifier of the context this tensor belongs to.

§Examples
use tenferro_cpu::CpuBackend;
use tenferro_ad::{EagerRuntime, EagerTensor, Tensor};

let ctx = EagerRuntime::with_cpu_backend(CpuBackend::new())?;
let x = EagerTensor::from_tensor_in(Tensor::from_vec_col_major(vec![1], vec![1.0_f64]).unwrap(), ctx.clone()).unwrap();

assert_eq!(x.ctx_id(), ctx.id());
Source

pub fn runtime(&self) -> &Arc<EagerRuntime> ⓘ

Borrow the eager runtime context that owns this tensor.

Source

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

Check whether two tensors belong to the same eager context.

§Examples
use tenferro_cpu::CpuBackend;
use tenferro_ad::{EagerRuntime, EagerTensor, Tensor};

let ctx = EagerRuntime::with_cpu_backend(CpuBackend::new())?;
let x = EagerTensor::from_tensor_in(Tensor::from_vec_col_major(vec![1], vec![1.0_f64]).unwrap(), ctx.clone()).unwrap();
let y = EagerTensor::from_tensor_in(Tensor::from_vec_col_major(vec![1], vec![2.0_f64]).unwrap(), ctx).unwrap();

assert!(x.same_context(&y));
Source

pub fn backward(&self) -> Result<Gradients>

Run reverse-mode AD from this scalar output.

Returns the full cotangent map produced by the reverse pass and also accumulates into grad() for tracked eager tensors reachable from this output.

For complex scalar outputs, cotangents use tenferro’s Hermitian real-inner-product convention. See https://tensor4all.org/tenferro-rs/guides/complex-ad.html.

§Examples
use tenferro_cpu::CpuBackend;
use tenferro_ad::{EagerRuntime, EagerTensor, Tensor};

let ctx = EagerRuntime::with_cpu_backend(CpuBackend::new())?;
let x = EagerTensor::requires_grad_in(Tensor::from_vec_col_major(vec![3], vec![1.0_f64, 2.0, 3.0]).unwrap(), ctx).unwrap();
for _ in 0..2 {
    let loss = x.runtime().with_eager_session(|s| {
        let doubled = s.add(&x, &x)?;
        s.reduce_sum(&doubled, Some(&[0]))
    })?;
    loss.backward()?;
}

assert_eq!(x.grad().unwrap().unwrap().as_slice::<f64>().unwrap(), &[4.0, 4.0, 4.0]);
§Errors

Returns Error::NonScalarGrad when this output is not scalar, Error::UnsupportedAdRule when a graph operation lacks a reverse rule, or a typed validation/backend/runtime-state error during the reverse pass.

Source

pub fn backward_with(&self, cotangent: &EagerTensor) -> Result<Gradients>

Run reverse-mode AD from this output with an explicit cotangent seed.

This is the stateful eager VJP sugar: it returns the cotangent map and accumulates reachable tracked leaves into their grad() slots. Use EagerRuntime::vjp when the VJP result should be returned as a composable eager tensor without touching grad slots.

§Examples
use tenferro_ad::{EagerRuntime, EagerTensor, Tensor};
use tenferro_cpu::CpuBackend;

let ctx = EagerRuntime::with_cpu_backend(CpuBackend::new())?;
let x = EagerTensor::requires_grad_in(
    Tensor::from_vec_col_major(vec![2], vec![2.0_f64, 3.0]).unwrap(),
    ctx.clone(),
)?;
let seed = EagerTensor::from_tensor_in(
    Tensor::from_vec_col_major(vec![2], vec![1.0_f64, 2.0]).unwrap(),
    ctx,
)?;
let y = x.runtime().with_eager_session(|s| s.mul(&x, &x))?;
y.backward_with(&seed)?;
assert_eq!(x.grad()?.unwrap().as_slice::<f64>().unwrap(), &[4.0, 12.0]);
§Errors

Returns Error::ContextMismatch when cotangent belongs to another eager runtime, Error::Validation when its shape or dtype is not a valid seed, Error::UnsupportedAdRule for an unavailable reverse rule, or a typed backend/runtime-state error during execution.

Source§

impl EagerTensor

Source

pub fn slice_builder(&self) -> EagerSliceBuilder<'_>

Start a rank-preserving slicing builder for this tensor.

§Examples
use tenferro_ad::{EagerRuntime, EagerTensor, Tensor};

let ctx = EagerRuntime::new()?;
let x = EagerTensor::from_tensor_in(
    Tensor::from_vec_col_major(vec![3], vec![1.0_f64, 2.0, 3.0]).unwrap(),
    ctx,
).unwrap();
let y = x.runtime().with_eager_session(|s| x.slice_builder().axis(0, 0..2).apply(s))?;
assert_eq!(y.shape(), &[2]);

Trait Implementations§

Source§

impl Clone for EagerTensor

Source§

fn clone(&self) -> EagerTensor

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 EagerTensor

Source§

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

Formats the value using the given formatter. Read more

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
§

impl<T> ByRef<T> for T

§

fn by_ref(&self) -> &T

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.

§

impl<T, U> Imply<T> for U
where T: ?Sized, U: ?Sized,

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> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts 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
§

impl<T> MaybeSend for T
where T: Send,

§

impl<T> MaybeSendSync for T
where T: Send + Sync,

§

impl<T> MaybeSync for T
where T: Sync,

§

impl<T> Pointable for T

§

const ALIGN: usize

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
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, 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.