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
impl EagerTensor
Sourcepub fn from_tensor_in(tensor: Tensor, ctx: Arc<EagerRuntime>) -> Result<Self>
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.
Sourcepub fn from_vec_col_major_in<T: TensorScalar>(
shape: impl IntoShapeVec,
data: Vec<T>,
ctx: Arc<EagerRuntime>,
) -> Result<Self>
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.
Sourcepub fn requires_grad_in(tensor: Tensor, ctx: Arc<EagerRuntime>) -> Result<Self>
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.
Sourcepub fn detach(&self) -> Self
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());Sourcepub fn detach_into(&self, ctx: &Arc<EagerRuntime>) -> Result<Self>
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.
Sourcepub fn value(&self) -> Result<ValueGuard<'_>>
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.
Sourcepub fn duplicate_value(&self) -> Result<Tensor>
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]);Sourcepub fn into_value(self) -> Result<Tensor, IntoValueError<Self>>
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]);Sourcepub fn dtype(&self) -> DType
pub fn dtype(&self) -> DType
Return this tensor’s scalar dtype without materializing through
value.
Sourcepub fn shape(&self) -> &[usize]
pub fn shape(&self) -> &[usize]
Return this tensor’s logical shape without materializing through
value.
Sourcepub fn tensor_read(&self) -> TensorRead<'_>
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.
Sourcepub fn to_tensor(&self) -> Result<Tensor>
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.
Sourcepub fn grad(&self) -> Result<Option<GradientValue>>
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.
Sourcepub fn clear_grad(&self) -> Result<()>
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.
Sourcepub fn tracks_grad(&self) -> bool
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());Sourcepub fn ctx_id(&self) -> ContextId
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());Sourcepub fn runtime(&self) -> &Arc<EagerRuntime> ⓘ
pub fn runtime(&self) -> &Arc<EagerRuntime> ⓘ
Borrow the eager runtime context that owns this tensor.
Sourcepub fn same_context(&self, other: &Self) -> bool
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));Sourcepub fn backward(&self) -> Result<Gradients>
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.
Sourcepub fn backward_with(&self, cotangent: &EagerTensor) -> Result<Gradients>
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
impl EagerTensor
Sourcepub fn slice_builder(&self) -> EagerSliceBuilder<'_>
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
impl Clone for EagerTensor
Source§fn clone(&self) -> EagerTensor
fn clone(&self) -> EagerTensor
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreAuto Trait Implementations§
impl !RefUnwindSafe for EagerTensor
impl !UnwindSafe for EagerTensor
impl Freeze for EagerTensor
impl Send for EagerTensor
impl Sync for EagerTensor
impl Unpin for EagerTensor
impl UnsafeUnpin for EagerTensor
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
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T, U> Imply<T> for U
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