|
4 | 4 | //! The following conversions are supported: |
5 | 5 | //! |
6 | 6 | //! - `DLPackTensor` => `ndarray::Array` (makes a copy of the data) |
| 7 | +//! - `DLPackTensor` => `ndarray::ArcArray` (makes a copy of the data) |
7 | 8 | //! - `DLPackTensorRef` => `ndarray::ArrayView` |
8 | 9 | //! - `DLPackTensorRefMut` => `ndarray::ArrayViewMut` |
9 | 10 | //! - `ndarray::Array` => `DLPackTensor` |
10 | 11 | //! - `&ndarray::Array` => `DLPackTensorRef` |
11 | 12 | //! - `&mut ndarray::Array` => `DLPackTensorRefMut` |
12 | 13 | //! - `ndarray::ArrayView` => `DLPackTensorRef` |
13 | 14 | //! - `ndarray::ArrayViewMut` => `DLPackTensorRefMut` |
| 15 | +//! - `ndarray::ArcArray` => `DLPackTensor` (share data) |
| 16 | +//! - `&ndarray::ArcArray` => `DLPackTensorRef` |
14 | 17 | //! |
15 | 18 | //! # Examples |
16 | 19 | //! |
|
35 | 38 | //! let tensor_ref: DLPackTensorRef = (&array).try_into().unwrap(); |
36 | 39 | //! ``` |
37 | 40 |
|
38 | | -use ndarray::{Array, Dimension, ShapeBuilder}; |
| 41 | +use std::ffi::c_void; |
| 42 | +use ndarray::{Array, ArcArray, Dimension, ShapeBuilder}; |
39 | 43 |
|
40 | 44 | use crate::data_types::{CastError, DLPackPointerCast, GetDLPackDataType}; |
41 | 45 | use crate::sys; |
@@ -181,6 +185,22 @@ where |
181 | 185 | } |
182 | 186 | } |
183 | 187 |
|
| 188 | +/// This implementation provides a conversion from a DLPack `DLPackTensor` to an |
| 189 | +/// `ndarray::ArcArray`. |
| 190 | +/// |
| 191 | +/// **Note:** This conversion makes a copy of the underlying tensor data. |
| 192 | +impl<T, D> TryFrom<DLPackTensor> for ArcArray<T, D> |
| 193 | +where |
| 194 | + D: Dimension + DimFromVec + 'static, |
| 195 | + T: DLPackPointerCast + Clone + 'static, |
| 196 | +{ |
| 197 | + type Error = DLPackNDarrayError; |
| 198 | + |
| 199 | + fn try_from(tensor: DLPackTensor) -> Result<Self, Self::Error> { |
| 200 | + let array: Array<T, D> = tensor.try_into()?; |
| 201 | + Ok(array.into()) |
| 202 | + } |
| 203 | +} |
184 | 204 |
|
185 | 205 | /*****************************************************************************/ |
186 | 206 | /* ndarray => DLPack */ |
@@ -277,6 +297,22 @@ impl<'a, T, D> TryFrom<&'a ndarray::Array<T, D>> for DLPackTensorRef<'a> where |
277 | 297 | } |
278 | 298 | } |
279 | 299 |
|
| 300 | +impl<'a, T, D> TryFrom<&'a ArcArray<T, D>> for DLPackTensorRef<'a> where |
| 301 | + D: ndarray::Dimension, |
| 302 | + T: GetDLPackDataType, |
| 303 | +{ |
| 304 | + type Error = DLPackNDarrayError; |
| 305 | + |
| 306 | + fn try_from(array: &'a ArcArray<T, D>) -> Result<Self, Self::Error> { |
| 307 | + let tensor = array_to_tensor_view(array)?; |
| 308 | + |
| 309 | + return Ok(unsafe { |
| 310 | + // SAFETY: we are constraining the lifetime of the return value |
| 311 | + DLPackTensorRef::from_raw(tensor) |
| 312 | + }); |
| 313 | + } |
| 314 | +} |
| 315 | + |
280 | 316 | impl<'a, T, D> TryFrom<&'a mut ndarray::Array<T, D>> for DLPackTensorRefMut<'a> where |
281 | 317 | D: ndarray::Dimension, |
282 | 318 | T: GetDLPackDataType, |
@@ -388,11 +424,65 @@ where |
388 | 424 | } |
389 | 425 | } |
390 | 426 |
|
| 427 | +/// Convert a shared `ArcArray` into a `DLPackTensor`. |
| 428 | +/// This is ZERO-COPY: it increments the reference count of the data. |
| 429 | +impl<'a, T, D> TryFrom<&'a ArcArray<T, D>> for DLPackTensor |
| 430 | +where |
| 431 | + D: Dimension, |
| 432 | + T: GetDLPackDataType + 'static + Clone, |
| 433 | +{ |
| 434 | + type Error = DLPackNDarrayError; |
| 435 | + |
| 436 | + fn try_from(array: &'a ArcArray<T, D>) -> Result<Self, Self::Error> { |
| 437 | + let shared_view = array.clone(); |
| 438 | + |
| 439 | + let shape: Vec<i64> = shared_view.shape().iter().map(|&s| s as i64).collect(); |
| 440 | + let strides: Vec<i64> = shared_view.strides().iter().map(|&s| s as i64).collect(); |
| 441 | + let ndim = shape.len() as i32; |
| 442 | + |
| 443 | + let mut ctx = Box::new(ManagerContext { |
| 444 | + _array: shared_view, |
| 445 | + shape, |
| 446 | + strides, |
| 447 | + }); |
| 448 | + |
| 449 | + let data_ptr = ctx._array.as_ptr() as *mut c_void; |
| 450 | + let shape_ptr = ctx.shape.as_mut_ptr(); |
| 451 | + let strides_ptr = ctx.strides.as_mut_ptr(); |
| 452 | + |
| 453 | + let dl_tensor = sys::DLTensor { |
| 454 | + data: data_ptr, |
| 455 | + device: sys::DLDevice { |
| 456 | + device_type: sys::DLDeviceType::kDLCPU, |
| 457 | + device_id: 0, |
| 458 | + }, |
| 459 | + ndim, |
| 460 | + dtype: T::get_dlpack_data_type(), |
| 461 | + shape: shape_ptr, |
| 462 | + strides: strides_ptr, |
| 463 | + byte_offset: 0, |
| 464 | + }; |
| 465 | + |
| 466 | + let managed_tensor = sys::DLManagedTensorVersioned { |
| 467 | + version: sys::DLPackVersion::current(), |
| 468 | + manager_ctx: Box::into_raw(ctx) as *mut _, |
| 469 | + deleter: Some(deleter_fn::<ArcArray<T, D>>), |
| 470 | + flags: 0, |
| 471 | + dl_tensor, |
| 472 | + }; |
| 473 | + |
| 474 | + unsafe { |
| 475 | + Ok(DLPackTensor::from_raw(managed_tensor)) |
| 476 | + } |
| 477 | + } |
| 478 | +} |
| 479 | + |
391 | 480 | #[cfg(test)] |
392 | 481 | mod tests { |
393 | 482 | use super::*; |
394 | 483 | use crate::sys::{DLDevice, DLDeviceType, DLTensor}; |
395 | 484 | use ndarray::prelude::*; |
| 485 | + use ndarray::ArcArray2; |
396 | 486 |
|
397 | 487 | #[test] |
398 | 488 | fn test_dlpack_to_ndarray() { |
@@ -554,4 +644,41 @@ mod tests { |
554 | 644 |
|
555 | 645 | assert_eq!(original_array, final_array); |
556 | 646 | } |
| 647 | + |
| 648 | + #[test] |
| 649 | + fn test_arc_array_to_dlpack_share() { |
| 650 | + let array = ArcArray2::from_shape_vec((2, 3), vec![1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0]).unwrap(); |
| 651 | + let ptr = array.as_ptr(); |
| 652 | + |
| 653 | + // Conversion to DLPackTensor (Owned) should share data |
| 654 | + let tensor: DLPackTensor = (&array).try_into().unwrap(); |
| 655 | + let raw = unsafe { &tensor.raw.as_ref().dl_tensor }; |
| 656 | + |
| 657 | + assert_eq!(raw.data as *const f32, ptr); |
| 658 | + |
| 659 | + // Convert back to Array (Copy) |
| 660 | + let array_copy: Array<f32, _> = tensor.try_into().unwrap(); |
| 661 | + assert_eq!(array, array_copy); |
| 662 | + // Pointers should differ due to copy |
| 663 | + assert_ne!(array_copy.as_ptr(), ptr); |
| 664 | + } |
| 665 | + |
| 666 | + #[test] |
| 667 | + fn test_dlpack_to_arc_array() { |
| 668 | + let array = arr2(&[[10.0f32, 11.0], [12.0, 13.0]]); |
| 669 | + let tensor: DLPackTensor = array.clone().try_into().unwrap(); |
| 670 | + |
| 671 | + let arc_array: ArcArray<f32, _> = tensor.try_into().unwrap(); |
| 672 | + assert_eq!(arc_array, array); |
| 673 | + } |
| 674 | + |
| 675 | + #[test] |
| 676 | + fn test_arc_array_to_dlpack_ref() { |
| 677 | + let array = ArcArray2::from_shape_vec((2, 2), vec![1, 2, 3, 4]).unwrap(); |
| 678 | + let tensor_ref: DLPackTensorRef = (&array).try_into().unwrap(); |
| 679 | + |
| 680 | + assert_eq!(tensor_ref.n_dims(), 2); |
| 681 | + let shape = tensor_ref.shape(); |
| 682 | + assert_eq!(shape, &[2, 2]); |
| 683 | + } |
557 | 684 | } |
0 commit comments