Skip to content

Resolve gh-2055 #2058

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Apr 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 59 additions & 28 deletions dpctl/tensor/_copy_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -349,36 +349,19 @@ def _copy_from_usm_ndarray_to_usm_ndarray(dst, src):
_copy_same_shape(dst, src_same_shape)


def _empty_like_orderK(X, dt, usm_type=None, dev=None):
"""Returns empty array like `x`, using order='K'

For an array `x` that was obtained by permutation of a contiguous
array the returned array will have the same shape and the same
strides as `x`.
def _make_empty_like_orderK(x, dt, usm_type, dev):
"""
if not isinstance(X, dpt.usm_ndarray):
raise TypeError(f"Expected usm_ndarray, got {type(X)}")
if usm_type is None:
usm_type = X.usm_type
if dev is None:
dev = X.device
fl = X.flags
if fl["C"] or X.size <= 1:
return dpt.empty_like(
X, dtype=dt, usm_type=usm_type, device=dev, order="C"
)
elif fl["F"]:
return dpt.empty_like(
X, dtype=dt, usm_type=usm_type, device=dev, order="F"
)
st = list(X.strides)
Returns empty array with shape and strides like `x`, with dtype `dt`,
USM type `usm_type`, on device `dev`.
"""
st = list(x.strides)
perm = sorted(
range(X.ndim),
key=lambda d: builtins.abs(st[d]) if X.shape[d] > 1 else 0,
range(x.ndim),
key=lambda d: builtins.abs(st[d]) if x.shape[d] > 1 else 0,
reverse=True,
)
inv_perm = sorted(range(X.ndim), key=lambda i: perm[i])
sh = X.shape
inv_perm = sorted(range(x.ndim), key=lambda i: perm[i])
sh = x.shape
sh_sorted = tuple(sh[i] for i in perm)
R = dpt.empty(sh_sorted, dtype=dt, usm_type=usm_type, device=dev, order="C")
if min(st) < 0:
Expand All @@ -389,12 +372,60 @@ def _empty_like_orderK(X, dt, usm_type=None, dev=None):
if st_sorted[i] < 0
else slice(None, None, None)
)
for i in range(X.ndim)
for i in range(x.ndim)
)
R = R[sl]
return dpt.permute_dims(R, inv_perm)


def _empty_like_orderK(x, dt, usm_type=None, dev=None):
"""
Returns empty array like `x`, using order='K'

For an array `x` that was obtained by permutation of a contiguous
array the returned array will have the same shape and the same
strides as `x`.
"""
if not isinstance(x, dpt.usm_ndarray):
raise TypeError(f"Expected usm_ndarray, got {type(x)}")
if usm_type is None:
usm_type = x.usm_type
if dev is None:
dev = x.device
fl = x.flags
if fl["C"] or x.size <= 1:
return dpt.empty_like(
x, dtype=dt, usm_type=usm_type, device=dev, order="C"
)
elif fl["F"]:
return dpt.empty_like(
x, dtype=dt, usm_type=usm_type, device=dev, order="F"
)
return _make_empty_like_orderK(x, dt, usm_type, dev)


def _from_numpy_empty_like_orderK(x, dt, usm_type, dev):
"""
Returns empty usm_ndarray like NumPy array `x`, using order='K'

For an array `x` that was obtained by permutation of a contiguous
array the returned array will have the same shape and the same
strides as `x`.
"""
if not isinstance(x, np.ndarray):
raise TypeError(f"Expected numpy.ndarray, got {type(x)}")
fl = x.flags
if fl["C"] or x.size <= 1:
return dpt.empty(
x.shape, dtype=dt, usm_type=usm_type, device=dev, order="C"
)
elif fl["F"]:
return dpt.empty(
x.shape, dtype=dt, usm_type=usm_type, device=dev, order="F"
)
return _make_empty_like_orderK(x, dt, usm_type, dev)


def _empty_like_pair_orderK(X1, X2, dt, res_shape, usm_type, dev):
if not isinstance(X1, dpt.usm_ndarray):
raise TypeError(f"Expected usm_ndarray, got {type(X1)}")
Expand Down Expand Up @@ -732,7 +763,7 @@ def _extract_impl(ary, ary_mask, axis=0):
if exec_q is None:
raise dpctl.utils.ExecutionPlacementError(
"arrays have different associated queues. "
"Use `Y.to_device(X.device)` to migrate."
"Use `y.to_device(x.device)` to migrate."
)
ary_nd = ary.ndim
pp = normalize_axis_index(operator.index(axis), ary_nd)
Expand Down
27 changes: 6 additions & 21 deletions dpctl/tensor/_ctors.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@
import dpctl.tensor as dpt
import dpctl.tensor._tensor_impl as ti
import dpctl.utils
from dpctl.tensor._copy_utils import _empty_like_orderK
from dpctl.tensor._copy_utils import (
_empty_like_orderK,
_from_numpy_empty_like_orderK,
)
from dpctl.tensor._data_types import _get_dtype
from dpctl.tensor._device import normalize_queue_device
from dpctl.tensor._usmarray import _is_object_with_buffer_protocol
Expand Down Expand Up @@ -233,6 +236,7 @@ def _asarray_from_numpy_ndarray(
if dtype is None:
# deduce device-representable output data type
dtype = _map_to_device_dtype(ary.dtype, copy_q)
_ensure_native_dtype_device_support(dtype, copy_q.sycl_device)
f_contig = ary.flags["F"]
c_contig = ary.flags["C"]
fc_contig = f_contig or c_contig
Expand All @@ -242,27 +246,8 @@ def _asarray_from_numpy_ndarray(
order = "C" if c_contig else "F"
if order == "K":
# new USM allocation
_ensure_native_dtype_device_support(dtype, copy_q.sycl_device)
res = dpt.usm_ndarray(
ary.shape,
dtype=dtype,
buffer=usm_type,
order="C",
buffer_ctor_kwargs={"queue": copy_q},
)
original_strides = ary.strides
ind = sorted(
range(ary.ndim),
key=lambda i: abs(original_strides[i]),
reverse=True,
)
new_strides = tuple(res.strides[ind[i]] for i in ind)
# reuse previously made USM allocation
res = dpt.usm_ndarray(
res.shape, dtype=res.dtype, buffer=res.usm_data, strides=new_strides
)
res = _from_numpy_empty_like_orderK(ary, dtype, usm_type, copy_q)
else:
_ensure_native_dtype_device_support(dtype, copy_q.sycl_device)
res = dpt.usm_ndarray(
ary.shape,
dtype=dtype,
Expand Down
45 changes: 0 additions & 45 deletions dpctl/tests/elementwise/test_type_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@

import dpctl
import dpctl.tensor as dpt
import dpctl.tensor._copy_utils as cu
import dpctl.tensor._type_utils as tu

from .utils import _all_dtypes, _map_to_device_dtype
Expand Down Expand Up @@ -70,50 +69,6 @@ def test_type_util_can_cast():
assert isinstance(r, bool)


def test_type_utils_empty_like_orderK():
try:
a = dpt.empty((10, 10), dtype=dpt.int32, order="F")
except dpctl.SyclDeviceCreationError:
pytest.skip("No SYCL devices available")
X = cu._empty_like_orderK(a, dpt.int32, a.usm_type, a.device)
assert X.flags["F"]


def test_type_utils_empty_like_orderK_invalid_args():
with pytest.raises(TypeError):
cu._empty_like_orderK([1, 2, 3], dpt.int32, "device", None)
with pytest.raises(TypeError):
cu._empty_like_pair_orderK(
[1, 2, 3],
(
1,
2,
3,
),
dpt.int32,
(3,),
"device",
None,
)
try:
a = dpt.empty(10, dtype=dpt.int32)
except dpctl.SyclDeviceCreationError:
pytest.skip("No SYCL devices available")
with pytest.raises(TypeError):
cu._empty_like_pair_orderK(
a,
(
1,
2,
3,
),
dpt.int32,
(10,),
"device",
None,
)


def test_type_utils_find_buf_dtype():
def _denier_fn(dt):
return False
Expand Down
100 changes: 100 additions & 0 deletions dpctl/tests/test_tensor_copy_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Data Parallel Control (dpctl)
#
# Copyright 2020-2025 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import numpy as np
import pytest

import dpctl.tensor as dpt
import dpctl.tensor._copy_utils as cu
from dpctl.tests.helper import get_queue_or_skip


def test_copy_utils_empty_like_orderK():
get_queue_or_skip()
a = dpt.empty((10, 10), dtype=dpt.int32, order="F")
X = cu._empty_like_orderK(a, dpt.int32, a.usm_type, a.device)
assert X.flags["F"]


def test_copy_utils_empty_like_orderK_invalid_args():
get_queue_or_skip()
with pytest.raises(TypeError):
cu._empty_like_orderK([1, 2, 3], dpt.int32, "device", None)
with pytest.raises(TypeError):
cu._empty_like_pair_orderK(
[1, 2, 3],
(
1,
2,
3,
),
dpt.int32,
(3,),
"device",
None,
)

a = dpt.empty(10, dtype=dpt.int32)
with pytest.raises(TypeError):
cu._empty_like_pair_orderK(
a,
(
1,
2,
3,
),
dpt.int32,
(10,),
"device",
None,
)


def test_copy_utils_from_numpy_empty_like_orderK():
q = get_queue_or_skip()

a = np.empty((10, 10), dtype=np.int32, order="C")
r0 = cu._from_numpy_empty_like_orderK(a, dpt.int32, "device", q)
assert r0.flags["C"]

b = np.empty((10, 10), dtype=np.int32, order="F")
r1 = cu._from_numpy_empty_like_orderK(b, dpt.int32, "device", q)
assert r1.flags["F"]

c = np.empty((2, 3, 4), dtype=np.int32, order="C")
c = np.transpose(c, (1, 0, 2))
r2 = cu._from_numpy_empty_like_orderK(c, dpt.int32, "device", q)
assert not r2.flags["C"] and not r2.flags["F"]


def test_copy_utils_from_numpy_empty_like_orderK_invalid_args():
with pytest.raises(TypeError):
cu._from_numpy_empty_like_orderK([1, 2, 3], dpt.int32, "device", None)


def test_gh_2055():
"""
Test that `dpt.asarray` works on contiguous NumPy arrays with `order="K"`
when dimensions are permuted.

See: https://github.com/IntelPython/dpctl/issues/2055
"""
get_queue_or_skip()

a = np.ones((2, 3, 4), dtype=dpt.int32)
a_t = np.transpose(a, (2, 0, 1))
r = dpt.asarray(a_t)
assert not r.flags["C"] and not r.flags["F"]
Loading