Skip to content

Commit c9d2f5b

Browse files
committed
Add more tests to cover different use cases
1 parent a0e32c0 commit c9d2f5b

File tree

2 files changed

+194
-0
lines changed

2 files changed

+194
-0
lines changed

tests/test_dlpack.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import numpy
2+
import pytest
3+
from numpy.testing import assert_array_equal
4+
5+
import dpnp
6+
7+
from .helper import (
8+
get_all_dtypes,
9+
)
10+
11+
device_oneAPI = 14 # DLDeviceType.kDLOneAPI
12+
13+
14+
class TestDLPack:
15+
@pytest.mark.parametrize("stream", [None, 1])
16+
def test_stream(self, stream):
17+
x = dpnp.arange(5)
18+
x.__dlpack__(stream=stream)
19+
20+
@pytest.mark.parametrize("copy", [True, None, False])
21+
def test_copy(self, copy):
22+
x = dpnp.arange(5)
23+
x.__dlpack__(copy=copy)
24+
25+
def test_wrong_copy(self):
26+
x = dpnp.arange(5)
27+
x.__dlpack__(copy=dpnp.array([1, 2, 3]))
28+
29+
@pytest.mark.parametrize("xp", [dpnp, numpy])
30+
@pytest.mark.parametrize("dt", get_all_dtypes(no_none=True))
31+
def test_dtype_passthrough(self, xp, dt):
32+
x = xp.arange(5).astype(dt)
33+
y = xp.from_dlpack(x)
34+
35+
assert y.dtype == x.dtype
36+
assert_array_equal(x, y)
37+
38+
@pytest.mark.parametrize("xp", [dpnp, numpy])
39+
def test_non_contiguous(self, xp):
40+
x = xp.arange(25).reshape((5, 5))
41+
42+
y1 = x[0]
43+
assert_array_equal(y1, xp.from_dlpack(y1))
44+
45+
y2 = x[:, 0]
46+
assert_array_equal(y2, xp.from_dlpack(y2))
47+
48+
y3 = x[1, :]
49+
assert_array_equal(y3, xp.from_dlpack(y3))
50+
51+
y4 = x[1]
52+
assert_array_equal(y4, xp.from_dlpack(y4))
53+
54+
y5 = xp.diagonal(x).copy()
55+
assert_array_equal(y5, xp.from_dlpack(y5))
56+
57+
def test_device(self):
58+
x = dpnp.arange(5)
59+
assert x.__dlpack_device__()[0] == device_oneAPI
60+
y = dpnp.from_dlpack(x)
61+
assert y.__dlpack_device__()[0] == device_oneAPI
62+
z = y[::2]
63+
assert z.__dlpack_device__()[0] == device_oneAPI
64+
65+
def test_ndim0(self):
66+
x = dpnp.array(1.0)
67+
y = dpnp.from_dlpack(x)
68+
assert_array_equal(x, y)
69+
70+
def test_device(self):
71+
x = dpnp.arange(5)
72+
y = dpnp.from_dlpack(x, device=x.__dlpack_device__())
73+
assert x.device == y.device
74+
assert x.get_array()._pointer == y.get_array()._pointer
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import unittest
2+
3+
import dpctl
4+
import dpctl.tensor._dlpack as dlp
5+
import numpy
6+
import pytest
7+
8+
import dpnp as cupy
9+
from tests.third_party.cupy import testing
10+
11+
12+
def _gen_array(dtype, alloc_q=None):
13+
if cupy.issubdtype(dtype, numpy.unsignedinteger):
14+
array = cupy.random.randint(
15+
0, 10, size=(2, 3), sycl_queue=alloc_q
16+
).astype(dtype)
17+
elif cupy.issubdtype(dtype, cupy.integer):
18+
array = cupy.random.randint(
19+
-10, 10, size=(2, 3), sycl_queue=alloc_q
20+
).astype(dtype)
21+
elif cupy.issubdtype(dtype, cupy.floating):
22+
array = cupy.random.rand(2, 3, sycl_queue=alloc_q).astype(dtype)
23+
elif cupy.issubdtype(dtype, cupy.complexfloating):
24+
array = cupy.random.random((2, 3), sycl_queue=alloc_q).astype(dtype)
25+
elif dtype == cupy.bool_:
26+
array = cupy.random.randint(
27+
0, 2, size=(2, 3), sycl_queue=alloc_q
28+
).astype(cupy.bool_)
29+
else:
30+
assert False, f"unrecognized dtype: {dtype}"
31+
return array
32+
33+
34+
class TestDLPackConversion(unittest.TestCase):
35+
@testing.for_all_dtypes(no_bool=False)
36+
def test_conversion(self, dtype):
37+
orig_array = _gen_array(dtype)
38+
tensor = orig_array.__dlpack__()
39+
out_array = dlp.from_dlpack_capsule(tensor)
40+
testing.assert_array_equal(orig_array, out_array)
41+
assert orig_array.get_array()._pointer == out_array._pointer
42+
43+
44+
@testing.parameterize(*testing.product({"memory": ("device", "managed")}))
45+
class TestNewDLPackConversion(unittest.TestCase):
46+
def _get_stream(self, stream_name):
47+
if stream_name == "null":
48+
return dpctl.SyclQueue()
49+
return dpctl.SyclQueue()
50+
51+
@testing.for_all_dtypes(no_bool=False)
52+
def test_conversion(self, dtype):
53+
orig_array = _gen_array(dtype)
54+
out_array = cupy.from_dlpack(orig_array)
55+
testing.assert_array_equal(orig_array, out_array)
56+
assert orig_array.get_array()._pointer == out_array.get_array()._pointer
57+
58+
def test_stream(self):
59+
allowed_streams = ["null", True]
60+
61+
# stream order is automatically established via DLPack protocol
62+
for src_s in [self._get_stream(s) for s in allowed_streams]:
63+
for dst_s in [self._get_stream(s) for s in allowed_streams]:
64+
orig_array = _gen_array(cupy.float32, alloc_q=src_s)
65+
dltensor = orig_array.__dlpack__(stream=orig_array)
66+
67+
out_array = dlp.from_dlpack_capsule(dltensor)
68+
out_array = cupy.from_dlpack(out_array, device=dst_s)
69+
testing.assert_array_equal(orig_array, out_array)
70+
assert (
71+
orig_array.get_array()._pointer
72+
== out_array.get_array()._pointer
73+
)
74+
75+
76+
class TestDLTensorMemory(unittest.TestCase):
77+
# def setUp(self):
78+
# self.old_pool = cupy.get_default_memory_pool()
79+
# self.pool = cupy.cuda.MemoryPool()
80+
# cupy.cuda.set_allocator(self.pool.malloc)
81+
82+
# def tearDown(self):
83+
# self.pool.free_all_blocks()
84+
# cupy.cuda.set_allocator(self.old_pool.malloc)
85+
86+
def test_deleter(self):
87+
# memory is freed when tensor is deleted, as it's not consumed
88+
array = cupy.empty(10)
89+
tensor = array.__dlpack__()
90+
# str(tensor): <capsule object "dltensor" at 0x7f7c4c835330>
91+
assert '"dltensor"' in str(tensor)
92+
# assert self.pool.n_free_blocks() == 0
93+
# del array
94+
# assert self.pool.n_free_blocks() == 0
95+
# del tensor
96+
# assert self.pool.n_free_blocks() == 1
97+
98+
def test_deleter2(self):
99+
# memory is freed when array2 is deleted, as tensor is consumed
100+
array = cupy.empty(10)
101+
tensor = array.__dlpack__()
102+
assert '"dltensor"' in str(tensor)
103+
array2 = dlp.from_dlpack_capsule(tensor)
104+
assert '"used_dltensor"' in str(tensor)
105+
# assert self.pool.n_free_blocks() == 0
106+
# del array
107+
# assert self.pool.n_free_blocks() == 0
108+
# del array2
109+
# assert self.pool.n_free_blocks() == 1
110+
# del tensor
111+
# assert self.pool.n_free_blocks() == 1
112+
113+
def test_multiple_consumption_error(self):
114+
# Prevent segfault, see #3611
115+
array = cupy.empty(10)
116+
tensor = array.__dlpack__()
117+
array2 = dlp.from_dlpack_capsule(tensor)
118+
with pytest.raises(ValueError) as e:
119+
array3 = dlp.from_dlpack_capsule(tensor)
120+
assert "consumed multiple times" in str(e.value)

0 commit comments

Comments
 (0)