Skip to main content

EagerRuntime

Struct EagerRuntime 

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

Shared eager execution context for tensors on a backend.

Reusing one context lets eager tensors share backend state, extension runtime caches, and gradient storage across a computation.

§Examples

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

let ctx = EagerRuntime::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.clone()).unwrap();
let z = ctx.with_eager_session(&mut tenferro_cpu::CpuEngine::serial(), |session| session.add(&x, &y)).unwrap();

assert_eq!(z.value().unwrap().as_slice::<f64>().unwrap(), &[3.0]);

Implementations§

Source§

impl EagerRuntime

Source

pub fn new() -> Result<Arc<EagerRuntime>, Error>

Create a shared eager execution context.

The context stores no engine: every operation runs on the engine the caller passes in.

§Examples
use tenferro_ad::EagerRuntime;

let ctx = EagerRuntime::new()?;
assert_eq!(std::sync::Arc::strong_count(&ctx), 1);
§Errors

Returns Error::RuntimeStateSource when the prepared-program runtime cannot be configured, preserving the underlying RuntimeConfigError.

Source

pub fn with_ad_context(ad: &AdContext) -> Result<Arc<EagerRuntime>, Error>

Create a shared eager execution context that uses the semantic extension rules and AD transform cache of ad.

§Examples
use tenferro_ad::{AdContext, EagerRuntime};

let ad = AdContext::builder().build().unwrap();
let ctx = EagerRuntime::with_ad_context(&ad)?;
assert_eq!(std::sync::Arc::strong_count(&ctx), 1);
§Errors

Returns Error::RuntimeStateSource when the prepared-program runtime cannot be configured.

Source

pub fn id(&self) -> ContextId

Return an opaque identifier for this context.

§Examples
use tenferro_ad::{EagerRuntime};

let ctx = EagerRuntime::new()?;
assert_ne!(ctx.id(), EagerRuntime::new()?.id());
Source

pub fn no_grad(&self) -> EagerNoGradGuard

Disable eager operation recording on the current thread until the guard is dropped.

This is useful for optimizer updates, metric calculations, and other eager computations that should not become part of the AD tape.

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

let ctx = EagerRuntime::new()?;
let x = EagerTensor::requires_grad_in(
    Tensor::from_vec_col_major(vec![1], vec![3.0_f64]).unwrap(),
    ctx.clone(),
)?;
let y = ctx.with_eager_session(&mut tenferro_cpu::CpuEngine::serial(), |s| {
    let _guard = ctx.no_grad();
    s.mul(&x, &x)
})?;
assert!(!y.tracks_grad());
Source

pub fn capture_trace(&self) -> EagerTraceCaptureGuard

Keep semantic-trace recording active for untracked intermediates.

See EagerTraceCaptureGuard for the full contract and an example.

Source

pub fn install_extension_module( &self, module: Arc<dyn ExtensionModule>, ) -> Result<RuntimeEpoch, Error>

Install or replace one extension module on this eager context’s runtime.

Eager extension wrappers call this as an idempotent “ensure installed” step. When the exact module instance (same module ID and allocation) is already installed, this is a read-only no-op that returns the current runtime epoch without acquiring the install lock or reconfiguring. The cold or replacement paths keep the transactional install-or-replace behavior, serialized so parallel first-use of the same extension family cannot publish over another thread’s base snapshot.

§Errors

Returns tenferro_runtime::Error::RuntimeState when runtime reconfiguration fails or the extension module transaction is invalid.

Source

pub fn clear_caches(&self) -> Result<(), Error>

Clear every cache owned by this eager context.

§Examples
use tenferro_ad::{EagerRuntime};

let ctx = EagerRuntime::new()?;
ctx.clear_caches()?;
assert_eq!(ctx.cache_stats()?.ad_transforms.entries, 0);
assert_eq!(ctx.cache_stats()?.prepared_derivatives.entries, 0);
§Errors

Returns tenferro_runtime::Error::RuntimeState when either the extension cache or AD-transform cache is poisoned.

Source

pub fn clear_prepared_derivative_cache(&self) -> Result<(), Error>

Clear prepared derivative program cache entries.

§Examples
use tenferro_ad::EagerRuntime;

let ctx = EagerRuntime::new()?;
ctx.clear_prepared_derivative_cache()?;
assert_eq!(ctx.cache_stats()?.prepared_derivatives.entries, 0);
§Errors

Returns tenferro_runtime::Error::RuntimeState if the prepared derivative cache lock is poisoned.

Source

pub fn cache_stats(&self) -> Result<EagerRuntimeCacheStats, Error>

Return eager runtime cache-entry and retained-byte stats.

§Examples
use tenferro_ad::{EagerRuntime};

let ctx = EagerRuntime::new()?;
let stats = ctx.cache_stats()?;
assert_eq!(stats.ad_transforms.entries, 0);
assert_eq!(stats.prepared_derivatives.entries, 0);
§Errors

Returns tenferro_runtime::Error::RuntimeState when a cache or AD-transform cache lock is poisoned.

Source

pub fn ad_transform_cache_limits(&self) -> Result<AdTransformCacheLimits, Error>

Return the AD transform cache retention limits.

§Examples
use tenferro_ad::EagerRuntime;

let ctx = EagerRuntime::new()?;
assert!(ctx.ad_transform_cache_limits()?.max_entries().get() > 0);
§Errors

Returns tenferro_runtime::Error::RuntimeState if the AD-transform cache lock is poisoned.

Source

pub fn set_ad_transform_cache_limits( &self, limits: AdTransformCacheLimits, ) -> Result<(), Error>

Replace AD transform cache retention limits.

§Examples
use std::num::NonZeroUsize;
use tenferro_ad::{AdTransformCacheLimits, EagerRuntime};

let ctx = EagerRuntime::new()?;
let limits = AdTransformCacheLimits::new(NonZeroUsize::new(1).unwrap());
ctx.set_ad_transform_cache_limits(limits)?;
assert_eq!(ctx.ad_transform_cache_limits()?, limits);
§Errors

Returns tenferro_runtime::Error::RuntimeState if the AD-transform cache lock is poisoned while updating limits.

Source

pub fn clear_ad_transform_caches(&self) -> Result<(), Error>

Clear AD transform cache entries visible through this eager runtime.

§Examples
use tenferro_ad::EagerRuntime;

let ctx = EagerRuntime::new()?;
ctx.clear_ad_transform_caches()?;
assert_eq!(ctx.cache_stats()?.ad_transforms.entries, 0);
§Errors

Returns tenferro_runtime::Error::RuntimeState if the AD-transform cache lock is poisoned while clearing entries.

Source

pub fn prepared_derivative_cache_limits( &self, ) -> Result<AdTransformCacheLimits, Error>

Return prepared derivative cache retention limits.

§Examples
use tenferro_ad::EagerRuntime;

let ctx = EagerRuntime::new()?;
assert!(ctx.prepared_derivative_cache_limits()?.max_entries().get() > 0);
§Errors

Returns tenferro_runtime::Error::RuntimeState if the prepared derivative cache lock is poisoned.

Source

pub fn set_prepared_derivative_cache_limits( &self, limits: AdTransformCacheLimits, ) -> Result<(), Error>

Replace prepared derivative cache retention limits.

§Examples
use std::num::NonZeroUsize;
use tenferro_ad::{AdTransformCacheLimits, EagerRuntime};

let ctx = EagerRuntime::new()?;
let limits = AdTransformCacheLimits::new(NonZeroUsize::new(1).unwrap());
ctx.set_prepared_derivative_cache_limits(limits)?;
assert_eq!(ctx.prepared_derivative_cache_limits()?, limits);
§Errors

Returns tenferro_runtime::Error::RuntimeState if the prepared derivative cache lock is poisoned.

Source

pub fn with_eager_session<T, E>( self: &Arc<EagerRuntime>, engine: &mut dyn ExecutionEngine, f: impl FnOnce(&mut EagerSession<'_>) -> Result<T, E>, ) -> Result<T, E>
where E: From<Error>,

Run eager operations on engine.

The callback receives an EagerSession that pairs this runtime with the borrowed engine; operations recorded through it are differentiable. Nothing is entered or locked: the engine is simply borrowed for the callback.

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

let ctx = EagerRuntime::new()?;
let mut engine = CpuEngine::serial();
let x = EagerTensor::from_tensor_in(Tensor::from_vec_col_major(vec![1], vec![1.0_f64])?, ctx.clone())?;
let y = EagerTensor::from_tensor_in(Tensor::from_vec_col_major(vec![1], vec![2.0_f64])?, ctx.clone())?;
let z = ctx.with_eager_session(&mut engine, |s| s.add(&x, &y))?;
assert_eq!(z.value()?.as_slice::<f64>()?, &[3.0]);
§Errors

Returns Error::Unsupported when the engine kind cannot be registered with this runtime or its context identity conflicts with the existing registration, Error::RuntimeStateSource when the runtime snapshot or engine registration state is unavailable, and the callback’s own error unchanged.

Source

pub fn clear_grads(&self) -> Result<(), Error>

Clear all live gradient slots tracked by this context.

This resets the stored gradients to None without unregistering the tensors, so future backward() calls can accumulate again.

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

let ctx = EagerRuntime::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.clone()).unwrap();
let loss = ctx.with_eager_session(&mut tenferro_cpu::CpuEngine::serial(), |s| {
    let product = s.mul(&x, &y)?;
    s.reduce_sum(&product, Some(&[0]))
})?;
let _ = loss.backward(&mut tenferro_cpu::CpuEngine::serial()).unwrap();

ctx.clear_grads()?;

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

Returns tenferro_runtime::Error::RuntimeState if a gradient-slot lock is poisoned while clearing live gradients.

Source

pub fn constant_from( self: &Arc<EagerRuntime>, tensor: Tensor, ) -> Result<EagerTensor, Error>

Import a concrete tensor into this context as an untracked constant.

The returned tensor does not participate in gradient tracking. Use this for fixed masks, quadrature weights, physical constants, and other data that should not receive gradients. Like EagerSession::constant_from, it performs no host/device transfer; use EagerSession::constant_from_host to upload host data into a device runtime.

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

let ctx = EagerRuntime::new()?;
let c = ctx.constant_from(Tensor::from_vec_col_major(vec![2], vec![1.0_f64, 2.0]).unwrap())?;
let x = EagerTensor::requires_grad_in(Tensor::from_vec_col_major(vec![2], vec![3.0_f64, 4.0]).unwrap(), ctx.clone())?;
let z = ctx.with_eager_session(&mut tenferro_cpu::CpuEngine::serial(), |s| s.add(&x, &c))?;

assert_eq!(z.value()?.as_slice::<f64>().unwrap(), &[4.0, 6.0]);
§Errors

Returns tenferro_runtime::Error::RuntimeState when metadata cannot be registered or the backend lock is poisoned.

Source

pub fn variable_from( self: &Arc<EagerRuntime>, tensor: Tensor, ) -> Result<EagerTensor, Error>

Import a concrete tensor into this context as a trainable variable.

The returned tensor participates in gradient tracking; its gradient slot is registered in this context.

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

let ctx = EagerRuntime::new()?;
let p = ctx.variable_from(Tensor::from_vec_col_major(vec![2], vec![1.0_f64, 2.0]).unwrap())?;
let loss = ctx.with_eager_session(&mut tenferro_cpu::CpuEngine::serial(), |s| {
    let y = s.exp(&p)?;
    s.reduce_sum(&y, Some(&[0]))
})?;
let _ = loss.backward(&mut tenferro_cpu::CpuEngine::serial()).unwrap();

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

Returns tenferro_runtime::Error::RuntimeState when gradient metadata or the eager backend state cannot be registered.

Source

pub fn grad( self: &Arc<EagerRuntime>, engine: &mut dyn ExecutionEngine, output: &EagerTensor, wrt: &EagerTensor, ) -> Result<EagerTensor, Error>

Gradient of a scalar eager output with respect to an eager tensor.

Functional eager gradients return ordinary eager tensors and do not write into grad() slots. The returned tensor keeps a trace when the derivative computation depends on tracked eager values.

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

let ctx = EagerRuntime::new()?;
let x = EagerTensor::requires_grad_in(
    Tensor::from_vec_col_major(vec![], vec![3.0_f64]).unwrap(),
    ctx.clone(),
)?;
let loss = ctx.with_eager_session(&mut tenferro_cpu::CpuEngine::serial(), |s| s.mul(&x, &x))?;
let dx = ctx.grad(&mut tenferro_cpu::CpuEngine::serial(), &loss, &x)?;
assert_eq!(dx.value()?.as_slice::<f64>().unwrap(), &[6.0]);
§Errors

Returns tenferro_runtime::Error::NonScalarGrad for a non-scalar output, Error::ContextMismatch for tensors from another runtime, Error::UnsupportedAdRule when an AD rule is unavailable, or a typed validation/backend error from eager execution. An inactive wrt returns Error::Validation with argument: "wrt"; use grad_optional to observe that state.

Source

pub fn grad_optional( self: &Arc<EagerRuntime>, engine: &mut dyn ExecutionEngine, output: &EagerTensor, wrt: &EagerTensor, ) -> Result<Option<EagerTensor>, Error>

Gradient that returns None when wrt is inactive.

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

let ctx = EagerRuntime::new()?;
let x = EagerTensor::requires_grad_in(
    Tensor::from_vec_col_major(vec![], vec![3.0_f64]).unwrap(),
    ctx.clone(),
)?;
let y = EagerTensor::requires_grad_in(
    Tensor::from_vec_col_major(vec![], vec![4.0_f64]).unwrap(),
    ctx.clone(),
)?;
let loss = ctx.with_eager_session(&mut tenferro_cpu::CpuEngine::serial(), |s| s.mul(&y, &y))?;
assert!(ctx.grad_optional(&mut tenferro_cpu::CpuEngine::serial(), &loss, &x)?.is_none());
§Errors

Returns tenferro_runtime::Error::NonScalarGrad for a non-scalar output, Error::ContextMismatch for a foreign runtime, or a typed validation/backend/runtime-state error from eager execution.

Source

pub fn vjp( self: &Arc<EagerRuntime>, engine: &mut dyn ExecutionEngine, output: &EagerTensor, wrt: &EagerTensor, cotangent: &EagerTensor, ) -> Result<EagerTensor, Error>

Reverse-mode vector-Jacobian product for eager tensors.

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

let ctx = EagerRuntime::new()?;
let x = EagerTensor::requires_grad_in(
    Tensor::from_vec_col_major(vec![2], vec![2.0_f64, 3.0]).unwrap(),
    ctx.clone(),
)?;
let y = ctx.with_eager_session(&mut tenferro_cpu::CpuEngine::serial(), |s| s.mul(&x, &x))?;
let seed = EagerTensor::from_tensor_in(
    Tensor::from_vec_col_major(vec![2], vec![1.0_f64, 1.0]).unwrap(),
    ctx.clone(),
)?;
let dx = ctx.vjp(&mut tenferro_cpu::CpuEngine::serial(), &y, &x, &seed)?;
assert_eq!(dx.value()?.as_slice::<f64>().unwrap(), &[4.0, 6.0]);
§Errors

Returns Error::ContextMismatch for tensors from different eager runtimes, Error::Validation when the cotangent shape or dtype does not match the output, Error::UnsupportedAdRule when a rule is not registered, or a typed backend/runtime-state error. An inactive wrt returns Error::Validation with argument: "wrt"; use vjp_optional to observe that state.

Source

pub fn vjp_optional( self: &Arc<EagerRuntime>, engine: &mut dyn ExecutionEngine, output: &EagerTensor, wrt: &EagerTensor, cotangent: &EagerTensor, ) -> Result<Option<EagerTensor>, Error>

Reverse-mode vector-Jacobian product that returns None for inactive inputs.

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

let ctx = EagerRuntime::new()?;
let x = EagerTensor::requires_grad_in(
    Tensor::from_vec_col_major(vec![1], vec![2.0_f64]).unwrap(),
    ctx.clone(),
)?;
let y = EagerTensor::requires_grad_in(
    Tensor::from_vec_col_major(vec![1], vec![4.0_f64]).unwrap(),
    ctx.clone(),
)?;
let seed = EagerTensor::from_tensor_in(
    Tensor::from_vec_col_major(vec![1], vec![1.0_f64]).unwrap(),
    ctx.clone(),
)?;
let loss = ctx.with_eager_session(&mut tenferro_cpu::CpuEngine::serial(), |s| s.mul(&y, &y))?;
assert!(ctx.vjp_optional(&mut tenferro_cpu::CpuEngine::serial(), &loss, &x, &seed)?.is_none());
§Errors

Returns Error::ContextMismatch for tensors from different eager runtimes, Error::Validation when the cotangent shape or dtype does not match the output, Error::UnsupportedAdRule when a rule is not registered, or a typed backend/runtime-state error.

Source

pub fn jvp( self: &Arc<EagerRuntime>, engine: &mut dyn ExecutionEngine, output: &EagerTensor, wrt: &EagerTensor, tangent: &EagerTensor, ) -> Result<EagerTensor, Error>

Forward-mode Jacobian-vector product for eager tensors.

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

let ctx = EagerRuntime::new()?;
let x = EagerTensor::requires_grad_in(
    Tensor::from_vec_col_major(vec![1], vec![3.0_f64]).unwrap(),
    ctx.clone(),
)?;
let tangent = EagerTensor::from_tensor_in(
    Tensor::from_vec_col_major(vec![1], vec![1.0_f64]).unwrap(),
    ctx.clone(),
)?;
let y = ctx.with_eager_session(&mut tenferro_cpu::CpuEngine::serial(), |s| s.mul(&x, &x))?;
let dy = ctx.jvp(&mut tenferro_cpu::CpuEngine::serial(), &y, &x, &tangent)?;
assert_eq!(dy.value()?.as_slice::<f64>().unwrap(), &[6.0]);
§Errors

Returns Error::ContextMismatch for tensors from different eager runtimes, Error::Validation when the tangent shape or dtype does not match wrt, Error::UnsupportedAdRule when a rule is unavailable, or a typed backend/runtime-state error. An inactive wrt returns Error::Validation with argument: "wrt"; use jvp_optional to observe that state.

Source

pub fn jvp_optional( self: &Arc<EagerRuntime>, engine: &mut dyn ExecutionEngine, output: &EagerTensor, wrt: &EagerTensor, tangent: &EagerTensor, ) -> Result<Option<EagerTensor>, Error>

Forward-mode Jacobian-vector product that returns None for inactive outputs.

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

let ctx = EagerRuntime::new()?;
let x = EagerTensor::requires_grad_in(
    Tensor::from_vec_col_major(vec![1], vec![2.0_f64]).unwrap(),
    ctx.clone(),
)?;
let y = EagerTensor::requires_grad_in(
    Tensor::from_vec_col_major(vec![1], vec![4.0_f64]).unwrap(),
    ctx.clone(),
)?;
let tangent = EagerTensor::from_tensor_in(
    Tensor::from_vec_col_major(vec![1], vec![1.0_f64]).unwrap(),
    ctx.clone(),
)?;
let loss = ctx.with_eager_session(&mut tenferro_cpu::CpuEngine::serial(), |s| s.mul(&y, &y))?;
assert!(ctx.jvp_optional(&mut tenferro_cpu::CpuEngine::serial(), &loss, &x, &tangent)?.is_none());
§Errors

Returns Error::ContextMismatch for tensors from different eager runtimes, Error::Validation when the tangent shape or dtype does not match wrt, Error::UnsupportedAdRule when a rule is unavailable, or a typed backend/runtime-state error.

Trait Implementations§

Source§

impl Debug for EagerRuntime

Source§

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

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

§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

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

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

§

impl<T, S> SimdFrom<T, S> for T
where S: Simd,

§

fn simd_from(_simd: S, value: T) -> T

§

impl<F, T, S> SimdInto<T, S> for F
where T: SimdFrom<F, S>, S: Simd,

§

fn simd_into(self, simd: S) -> T

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

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V