Expand description

miri-checked crates.io docs.rs GitHub


This crate provides a safe idiomatic Rust binding to DLPack which is the standard in-memory, (mostly) hardware agnosnic data exchange interface and is recognized by major Deep Learning frameworks such as PyTorch, Tensorflow, MXNet, TVM and major array processing frameworks such as NumPy and CuPy. An important feature of this standard is to provide zero-cost tensor conversion across frameworks on a particular supported hardware.

The Minimum Supported Rust Version (MSRV) is the stable toolchain 1.57.0.

Usage

There are two usages related to where the owner of the underlying data / storage of a tensor resides. The main distinction is when the underlying data gets dropped and which framework is responsible for it.

Rust as the owner

In this case, ManagedTensor is built from ManagedTensorProxy which is the same proxy for ffi::DLManagedTensor.

Non-Rust owner

In this case, either ffi::DLTensor or its (invariant) Rust wrapper Tensor can be used.


Example

For when ownership is not concerned, one can use Tensor. Here is an example on how the conversion

ndarray::ArrayD <--> Tensor

can happen at zero-cost.

impl<'tensor> From<&'tensor mut ArrayD<f32>> for Tensor<'tensor> {
    fn from(arr: &'tensor mut ArrayD<f32>) -> Self {
        let inner = DLTensor::new(
            arr.as_mut_ptr() as *mut c_void,
            Device::default(),
            arr.ndim() as i32,
            DataType::f32(),
            arr.shape().as_ptr() as *const _ as *mut i64,
            arr.strides().as_ptr() as *const _ as *mut i64,
            0,
        );
        Tensor(inner)
    }
}

impl<'tensor> From<&'tensor mut Tensor<'tensor>> for ArrayD<f32> {
   fn from(t: &'tensor mut Tensor<'tensor>) -> Self {
        unsafe {
            let arr = RawArrayViewMut::from_shape_ptr(t.0.shape().unwrap(), t.0.data() as *mut f32);
            arr.deref_into_view_mut().into_dyn().to_owned()
        }
    }
}

And when ownership is concerned, one can use the ManagedTensor. Here is an example on how the conversion

ndarray::ArrayD <--> ManagedTensor

can happen at zero-cost.

impl<'tensor, 'ctx> From<&'tensor mut ArrayD<f32>> for ManagedTensor<'tensor, 'ctx> {
    fn from(t: &'tensor mut ArrayD<f32>) -> Self {
        let dlt: Tensor<'tensor> = Tensor::from(t);
        let inner = DLManagedTensor::new(dlt.0, ptr::null_mut());
        ManagedTensor(inner)
    }
}

impl<'tensor, 'ctx> From<&mut ManagedTensor<'tensor, 'ctx>> for ArrayD<f32> {
    fn from(mt: &mut ManagedTensor<'tensor, 'ctx>) -> Self {
        let dlt: DLTensor = mt.0.inner.dl_tensor.into();
        unsafe {
            let arr = RawArrayViewMut::from_shape_ptr(dlt.shape().unwrap(), dlt.data() as *mut f32);
            arr.deref_into_view_mut().into_dyn().to_owned()
        }
    }
}

See the complete examples/sample where the above cases have been simulated for the Rust ndarray conversion.

Re-exports

pub use datatype::DataType;
pub use datatype::DataTypeCode;
pub use device::Device;
pub use device::DeviceType;
pub use tensor::ManagedTensor;
pub use tensor::ManagedTensorProxy;
pub use tensor::Tensor;

Modules

Functions