Skip to content

Commit 7ebd405

Browse files
Merge master into improve_perf_det_slogdet
2 parents aab2d56 + 07d455e commit 7ebd405

21 files changed

+1347
-17
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1616
* Added implementation of `dpnp.ndarray.data` and `dpnp.ndarray.data.ptr` attributes [#2521](https://github.com/IntelPython/dpnp/pull/2521)
1717
* Added `dpnp.ndarray.__contains__` method [#2534](https://github.com/IntelPython/dpnp/pull/2534)
1818
* Added implementation of `dpnp.linalg.lu_factor` (SciPy-compatible) [#2557](https://github.com/IntelPython/dpnp/pull/2557), [#2565](https://github.com/IntelPython/dpnp/pull/2565)
19+
* Added implementation of `dpnp.piecewise` [#2550](https://github.com/IntelPython/dpnp/pull/2550)
1920

2021
### Changed
2122

@@ -58,6 +59,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
5859
* Fixed `dpnp.unique` with 1d input array and `axis=0`, `equal_nan=True` keywords passed where the produced result doesn't collapse the NaNs [#2530](https://github.com/IntelPython/dpnp/pull/2530)
5960
* Resolved issue when `dpnp.ndarray` constructor is called with `dpnp.ndarray.data` as `buffer` keyword [#2533](https://github.com/IntelPython/dpnp/pull/2533)
6061
* Fixed `dpnp.linalg.cond` to always return a real dtype [#2547](https://github.com/IntelPython/dpnp/pull/2547)
62+
* Resolved the issue in `dpnp.random` functions to allow any value of `size` where each element is castable to `Py_ssize_t` type [#2578](https://github.com/IntelPython/dpnp/pull/2578)
6163

6264
### Security
6365

dpnp/backend/extensions/lapack/getrs.cpp

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ std::pair<sycl::event, sycl::event>
166166
const dpctl::tensor::usm_ndarray &a_array,
167167
const dpctl::tensor::usm_ndarray &ipiv_array,
168168
const dpctl::tensor::usm_ndarray &b_array,
169+
oneapi::mkl::transpose trans,
169170
const std::vector<sycl::event> &depends)
170171
{
171172
const int a_array_nd = a_array.get_ndim();
@@ -264,12 +265,6 @@ std::pair<sycl::event, sycl::event>
264265
const std::int64_t lda = std::max<size_t>(1UL, n);
265266
const std::int64_t ldb = std::max<size_t>(1UL, n);
266267

267-
// Use transpose::T if the LU-factorized array is passed as C-contiguous.
268-
// For F-contiguous we use transpose::N.
269-
oneapi::mkl::transpose trans = is_a_array_c_contig
270-
? oneapi::mkl::transpose::T
271-
: oneapi::mkl::transpose::N;
272-
273268
char *a_array_data = a_array.get_data();
274269
char *b_array_data = b_array.get_data();
275270
char *ipiv_array_data = ipiv_array.get_data();

dpnp/backend/extensions/lapack/getrs.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ extern std::pair<sycl::event, sycl::event>
3737
const dpctl::tensor::usm_ndarray &a_array,
3838
const dpctl::tensor::usm_ndarray &ipiv_array,
3939
const dpctl::tensor::usm_ndarray &b_array,
40+
oneapi::mkl::transpose trans,
4041
const std::vector<sycl::event> &depends = {});
4142

4243
extern void init_getrs_dispatch_vector(void);

dpnp/backend/extensions/lapack/lapack_py.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,13 @@ void init_dispatch_tables(void)
7676

7777
PYBIND11_MODULE(_lapack_impl, m)
7878
{
79+
// Expose oneMKL transpose enum to Python
80+
py::enum_<oneapi::mkl::transpose>(m, "Transpose")
81+
.value("N", oneapi::mkl::transpose::N)
82+
.value("T", oneapi::mkl::transpose::T)
83+
.value("C", oneapi::mkl::transpose::C)
84+
.export_values(); // Optional, allows access like `Transpose.N`
85+
7986
// Register a custom LinAlgError exception in the dpnp.linalg submodule
8087
py::module_ linalg_module = py::module_::import("dpnp.linalg");
8188
py::register_exception<lapack_ext::LinAlgError>(
@@ -160,7 +167,8 @@ PYBIND11_MODULE(_lapack_impl, m)
160167
"the solves of linear equations with an LU-factored "
161168
"square coefficient matrix, with multiple right-hand sides",
162169
py::arg("sycl_queue"), py::arg("a_array"), py::arg("ipiv_array"),
163-
py::arg("b_array"), py::arg("depends") = py::list());
170+
py::arg("b_array"), py::arg("trans") = oneapi::mkl::transpose::N,
171+
py::arg("depends") = py::list());
164172

165173
m.def("_orgqr_batch", &lapack_ext::orgqr_batch,
166174
"Call `_orgqr_batch` from OneMKL LAPACK library to return "

dpnp/dpnp_iface_functional.py

Lines changed: 143 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
3737
"""
3838

39+
# pylint: disable=protected-access
3940

4041
from dpctl.tensor._numpy_helper import (
4142
normalize_axis_index,
@@ -44,7 +45,10 @@
4445

4546
import dpnp
4647

47-
__all__ = ["apply_along_axis", "apply_over_axes"]
48+
# pylint: disable=no-name-in-module
49+
from dpnp.dpnp_utils import get_usm_allocations
50+
51+
__all__ = ["apply_along_axis", "apply_over_axes", "piecewise"]
4852

4953

5054
def apply_along_axis(func1d, axis, arr, *args, **kwargs):
@@ -266,3 +270,141 @@ def apply_over_axes(func, a, axes):
266270
)
267271
a = res
268272
return res
273+
274+
275+
def piecewise(x, condlist, funclist):
276+
"""
277+
Evaluate a piecewise-defined function.
278+
279+
Given a set of conditions and corresponding functions, evaluate each
280+
function on the input data wherever its condition is true.
281+
282+
For full documentation refer to :obj:`numpy.piecewise`.
283+
284+
Parameters
285+
----------
286+
x : {dpnp.ndarray, usm_ndarray}
287+
The input domain.
288+
condlist : {sequence of array-like boolean, bool scalars}
289+
Each boolean array/scalar corresponds to a function in `funclist`.
290+
Wherever `condlist[i]` is ``True``, `funclist[i](x)` is used as the
291+
output value.
292+
293+
Each boolean array in `condlist` selects a piece of `x`, and should
294+
therefore be of the same shape as `x`.
295+
296+
The length of `condlist` must correspond to that of `funclist`.
297+
If one extra function is given, i.e. if
298+
``len(funclist) == len(condlist) + 1``, then that extra function
299+
is the default value, used wherever all conditions are ``False``.
300+
funclist : {array-like of scalars}
301+
A constant value is returned wherever corresponding condition of `x`
302+
is ``True``.
303+
304+
Returns
305+
-------
306+
out : dpnp.ndarray
307+
The output is the same shape and type as `x` and is found by
308+
calling the functions in `funclist` on the appropriate portions of `x`,
309+
as defined by the boolean arrays in `condlist`. Portions not covered
310+
by any condition have a default value of ``0``.
311+
312+
Limitations
313+
-----------
314+
Parameters `args` and `kw` are not supported and `funclist` cannot include a
315+
callable functions.
316+
317+
See Also
318+
--------
319+
:obj:`dpnp.choose` : Construct an array from an index array and a set of
320+
arrays to choose from.
321+
:obj:`dpnp.select` : Return an array drawn from elements in `choicelist`,
322+
depending on conditions.
323+
:obj:`dpnp.where` : Return elements from one of two arrays depending
324+
on condition.
325+
326+
Examples
327+
--------
328+
>>> import dpnp as np
329+
330+
Define the signum function, which is -1 for ``x < 0`` and +1 for ``x >= 0``.
331+
332+
>>> x = np.linspace(-2.5, 2.5, 6)
333+
>>> np.piecewise(x, [x < 0, x >= 0], [-1, 1])
334+
array([-1., -1., -1., 1., 1., 1.])
335+
336+
"""
337+
dpnp.check_supported_arrays_type(x)
338+
x_dtype = x.dtype
339+
if dpnp.is_supported_array_type(condlist) and condlist.ndim in [0, 1]:
340+
condlist = [condlist]
341+
elif dpnp.isscalar(condlist) or (
342+
dpnp.isscalar(condlist[0]) and x.ndim != 0
343+
):
344+
# convert scalar to a list of one array
345+
# convert list of scalars to a list of one array
346+
condlist = [
347+
dpnp.full(
348+
x.shape, condlist, usm_type=x.usm_type, sycl_queue=x.sycl_queue
349+
)
350+
]
351+
elif not dpnp.is_supported_array_type(condlist[0]):
352+
# convert list of lists to list of arrays
353+
# convert list of scalars to a list of 0d arrays (for 0d input)
354+
tmp = []
355+
for _, cond in enumerate(condlist):
356+
tmp.append(
357+
dpnp.array(cond, usm_type=x.usm_type, sycl_queue=x.sycl_queue)
358+
)
359+
condlist = tmp
360+
361+
dpnp.check_supported_arrays_type(*condlist)
362+
if dpnp.is_supported_array_type(funclist):
363+
usm_type, exec_q = get_usm_allocations([x, *condlist, funclist])
364+
else:
365+
usm_type, exec_q = get_usm_allocations([x, *condlist])
366+
367+
result = dpnp.empty_like(x, usm_type=usm_type, sycl_queue=exec_q)
368+
369+
condlen = len(condlist)
370+
try:
371+
if isinstance(funclist, str):
372+
raise TypeError("funclist must be a non-string sequence")
373+
funclen = len(funclist)
374+
except TypeError as e:
375+
raise TypeError("funclist must be a sequence of scalars") from e
376+
377+
if condlen == funclen:
378+
# default value is zero
379+
default_value = x_dtype.type(0)
380+
elif condlen + 1 == funclen:
381+
# default value is the last element of funclist
382+
default_value = funclist[-1]
383+
if callable(default_value):
384+
raise NotImplementedError(
385+
"Callable functions are not supported currently"
386+
)
387+
if isinstance(default_value, dpnp.ndarray):
388+
default_value = default_value.astype(x_dtype, copy=False)
389+
else:
390+
default_value = x_dtype.type(default_value)
391+
funclist = funclist[:-1]
392+
else:
393+
raise ValueError(
394+
f"with {condlen} condition(s), either {condlen} or {condlen + 1} "
395+
"functions are expected"
396+
)
397+
398+
for condition, func in zip(condlist, funclist):
399+
if callable(func):
400+
raise NotImplementedError(
401+
"Callable functions are not supported currently"
402+
)
403+
if isinstance(func, dpnp.ndarray):
404+
func = func.astype(x_dtype, copy=False)
405+
else:
406+
func = x_dtype.type(func)
407+
dpnp.where(condition, func, default_value, out=result)
408+
default_value = result
409+
410+
return result

dpnp/dpnp_utils/dpnp_algo_utils.pyx

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -390,10 +390,26 @@ cpdef inline tuple _object_to_tuple(object obj):
390390
if obj is None:
391391
return ()
392392

393-
if cpython.PySequence_Check(obj):
394-
return tuple(obj)
393+
# dpnp.ndarray unconditionally succeeds in PySequence_Check as it implements __getitem__
394+
if cpython.PySequence_Check(obj) and not dpnp.is_supported_array_type(obj):
395+
if isinstance(obj, numpy.ndarray):
396+
obj = numpy.atleast_1d(obj)
397+
398+
nd = len(obj)
399+
shape = []
400+
401+
for i in range(0, nd):
402+
if cpython.PyBool_Check(obj[i]):
403+
raise TypeError("DPNP object_to_tuple(): no item in size can be bool")
404+
405+
# Assumes each item is castable to Py_ssize_t,
406+
# otherwise TypeError will be raised
407+
shape.append(<Py_ssize_t> obj[i])
408+
return tuple(shape)
395409

396410
if dpnp.isscalar(obj):
411+
if cpython.PyBool_Check(obj):
412+
raise TypeError("DPNP object_to_tuple(): 'obj' can't be bool")
397413
return (obj, )
398414

399415
raise ValueError("DPNP object_to_tuple(): 'obj' should be 'None', collections.abc.Sequence, or 'int'")

dpnp/tests/helper.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,14 @@ def has_support_aspect64(device=None):
404404
return dev.has_aspect_fp64
405405

406406

407+
def is_arl_or_mtl(device=None):
408+
"""
409+
Return True if a test is running on Arrow Lake or Meteor Lake GPU device,
410+
False otherwise.
411+
"""
412+
return _get_dev_mask(device) == 0x7D00
413+
414+
407415
def is_cpu_device(device=None):
408416
"""
409417
Return True if a test is running on CPU device, False otherwise.

0 commit comments

Comments
 (0)