dpctl icon indicating copy to clipboard operation
dpctl copied to clipboard

Bug in `asnumpy` copy_utils

Open abagusetty opened this issue 4 months ago • 1 comments

It looks like the order-C is being ignored and the C-contiguous checks fails when using dpnp.asnumpy(). Please refer to the below reproducer and output with dpnp+dpctl & cupy, more specifically 3c section of the output.

Upon a quick peek, much of the asnumpy is being forwarded to dpt.__copy_utils__ and so opened an issue here. Apologies if this is not the right repo and should move it to DPNP.

Reproducer:

import numpy as np
import dpnp as dp

print("dpnp version:", getattr(dp, "__version__", "unknown"))

# Build the same data as in the larger code
ctr_offsets_slice = [
    dp.array([0, 0, 0], dtype=np.int32),
    dp.array([3, 3, 3], dtype=np.int32),
    dp.array([5, 5, 5], dtype=np.int32),
    dp.array([7, 7, 7], dtype=np.int32),
    dp.array([10, 10, 10], dtype=np.int32),
    dp.array([11, 11, 11], dtype=np.int32),
    dp.array([12, 12, 12], dtype=np.int32),
]

# 3a: stack -> dpnp_array (should be C-contiguous)
temp1 = dp.stack(ctr_offsets_slice)

# 3b: transpose -> dpnp_array view (not C-contiguous)
temp2 = dp.stack(ctr_offsets_slice).T

# 3c: device->host with "order='C'"
# EXPECTED (per docstring "works exactly like numpy.asarray"): C-contiguous NumPy array
# ACTUAL: order is ignored for dpnp_array, result keeps non-C layout
temp3 = dp.asnumpy(temp2, order='C')

print("3a. Testing : temp1: ", bool(temp1.flags['C_CONTIGUOUS']), type(temp1), len(temp1), temp1)
print("3b. Testing : temp2: ", bool(temp2.flags['C_CONTIGUOUS']), type(temp2), len(temp2), temp2)
print("3c. Testing : temp3: ", bool(temp3.flags['C_CONTIGUOUS']), type(temp3), len(temp3), temp3)

# Programmatic check to make the failure obvious:
if not temp3.flags['C_CONTIGUOUS']:
    print("\nBUG: dpnp.asnumpy(dpnp_array, order='C') returned a non-C-contiguous NumPy array.")
    print("     strides:", temp3.strides, "| shape:", temp3.shape)

Output from DPNP+DPCTL:

dpnp version: 0.19.0dev3+8.gac795ab9.dirty
3a. Testing : temp1:  True <class 'dpnp.dpnp_array.dpnp_array'> 7 [[ 0  0  0]
 [ 3  3  3]
 [ 5  5  5]
 [ 7  7  7]
 [10 10 10]
 [11 11 11]
 [12 12 12]]
3b. Testing : temp2:  False <class 'dpnp.dpnp_array.dpnp_array'> 3 [[ 0  3  5  7 10 11 12]
 [ 0  3  5  7 10 11 12]
 [ 0  3  5  7 10 11 12]]
3c. Testing : temp3:  False <class 'numpy.ndarray'> 3 [[ 0  3  5  7 10 11 12]
 [ 0  3  5  7 10 11 12]
 [ 0  3  5  7 10 11 12]]

BUG: dpnp.asnumpy(dpnp_array, order='C') returned a non-C-contiguous NumPy array.
     strides: (4, 12) | shape: (3, 7)

Output from CUPY

cupy version: 13.4.1
3a. Testing : temp1:  True <class 'cupy.ndarray'> 7 [[ 0  0  0]
 [ 3  3  3]
 [ 5  5  5]
 [ 7  7  7]
 [10 10 10]
 [11 11 11]
 [12 12 12]]
3b. Testing : temp2:  False <class 'cupy.ndarray'> 3 [[ 0  3  5  7 10 11 12]
 [ 0  3  5  7 10 11 12]
 [ 0  3  5  7 10 11 12]]
3c. Testing : temp3:  True <class 'numpy.ndarray'> 3 [[ 0  3  5  7 10 11 12]
 [ 0  3  5  7 10 11 12]
 [ 0  3  5  7 10 11 12]]

abagusetty avatar Aug 25 '25 15:08 abagusetty

We don't expose an order keyword in dpt.asnumpy at the moment.

In [1]: import dpctl.tensor as dpt, dpctl, numpy as np

In [2]: ctr_offsets_slice = [
   ...:     dpt.asarray([0, 0, 0], dtype=dpt.int32),
   ...:     dpt.asarray([3, 3, 3], dtype=dpt.int32),
   ...:     dpt.asarray([5, 5, 5], dtype=dpt.int32),
   ...:     dpt.asarray([7, 7, 7], dtype=dpt.int32),
   ...:     dpt.asarray([10, 10, 10], dtype=dpt.int32),
   ...:     dpt.asarray([11, 11, 11], dtype=dpt.int32),
   ...:     dpt.asarray([12, 12, 12], dtype=dpt.int32),
   ...: ]

In [3]: temp1 = dpt.stack(ctr_offsets_slice)

In [4]: temp1.flags
Out[4]:
  C_CONTIGUOUS : True
  F_CONTIGUOUS : False
  WRITABLE : True

In [5]: temp2 = dpt.stack(ctr_offsets_slice).T

In [5]: temp2.flags
Out[9]:
  C_CONTIGUOUS : False
  F_CONTIGUOUS : True
  WRITABLE : True

In [6]: temp3 = dpt.asnumpy(temp2, order="C")
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[6], line 1
----> 1 temp3 = dpt.asnumpy(temp2, order="C")

TypeError: asnumpy() got an unexpected keyword argument 'order'

so this part of the bug falls on dpnp. But we have an open issue to add an order keyword—it wasn't a priority because no one had requested it, the issue was just for tracking.

I can add the order keyword to asnumpy here though. In the meantime, please file a bug with dpnp as well and cross-link it. When order is in dpt.asnumpy, dpnp can just leverage it.

ndgrigorian avatar Aug 26 '25 07:08 ndgrigorian

@ndgrigorian We’re hitting the dpt.asnumpy(order="C") gap when converting F-contiguous intermediate tensors coming out of transposed stacks, and it’s now on the critical path for our GPU4PySCF project on ALCF urora. Even a minimal order= keyword (matching NumPy semantics) would unblock us, and dpnp could then leverage it as you noted. Happy to test a PR on Aurora/PVC. Thanks again!

abagusetty avatar Nov 20 '25 16:11 abagusetty

@ndgrigorian We’re hitting the dpt.asnumpy(order="C") gap when converting F-contiguous intermediate tensors coming out of transposed stacks, and it’s now on the critical path for our GPU4PySCF project on ALCF urora. Even a minimal order= keyword (matching NumPy semantics) would unblock us, and dpnp could then leverage it as you noted. Happy to test a PR on Aurora/PVC. Thanks again!

As an update, there's been some work on it, but it will take a bit more time with the holidays and it will require new kernels in tensor

ndgrigorian avatar Nov 20 '25 17:11 ndgrigorian

Thanks for the update and expected timeline. Appreciate for prioritizing this

abagusetty avatar Nov 20 '25 17:11 abagusetty