Skip to content

Commit 51f74e9

Browse files
Merge master into impl_unstack
2 parents daa6321 + ac3ab04 commit 51f74e9

File tree

12 files changed

+269
-41
lines changed

12 files changed

+269
-41
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313
### Fixed
1414

1515

16-
## [0.16.0] - 09/DD/2024
16+
## [0.16.0] - 10/14/2024
1717

1818
This release reaches an important milestone by making offloading fully asynchronous. Calls to `dpnp` submit tasks for execution to DPC++ runtime and return without waiting for execution of these tasks to finish. The sequential semantics a user comes to expect from execution of Python script is preserved though.
1919
In addition, this release completes implementation of `dpnp.fft` module and adds several new array manipulation, indexing and elementwise routines. Moreover, it adds support to build `dpnp` for Nvidia GPUs.
@@ -117,6 +117,7 @@ In addition, this release completes implementation of `dpnp.fft` module and adds
117117
* Updated the test suit for `dpnp.fft` module [#2071](https://github.com/IntelPython/dpnp/pull/2071)
118118
* Reworked `dpnp.clip` implementation to align with Python Array API 2023.12 specification [#2048](https://github.com/IntelPython/dpnp/pull/2048)
119119
* Skipped outdated tests for `dpnp.linalg.solve` due to compatibility issues with NumPy 2.0 [#2074](https://github.com/IntelPython/dpnp/pull/2074)
120+
* Updated installation instructions [#2098](https://github.com/IntelPython/dpnp/pull/2098)
120121

121122
### Fixed
122123

doc/reference/linalg.rst

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ Norms and other numbers
6969
dpnp.trace
7070
dpnp.linalg.trace (Array API compatible)
7171

72-
7372
Solving linear equations
7473
--------------------------
7574

doc/reference/ndarray.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ Other attributes
9292
:nosignatures:
9393

9494
dpnp.ndarray.T
95+
dpnp.ndarray.mT
9596
dpnp.ndarray.real
9697
dpnp.ndarray.imag
9798
dpnp.ndarray.flat

dpnp/dpnp_array.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,49 @@ def T(self):
108108
"""View of the transposed array."""
109109
return self.transpose()
110110

111+
@property
112+
def mT(self):
113+
"""
114+
View of the matrix transposed array.
115+
116+
The matrix transpose is the transpose of the last two dimensions, even
117+
if the array is of higher dimension.
118+
119+
Raises
120+
------
121+
ValueError
122+
If the array is of dimension less than 2.
123+
124+
Examples
125+
--------
126+
>>> import dpnp as np
127+
>>> a = np.array([[1, 2], [3, 4]])
128+
>>> a
129+
array([[1, 2],
130+
[3, 4]])
131+
>>> a.mT
132+
array([[1, 3],
133+
[2, 4]])
134+
135+
>>> a = np.arange(8).reshape((2, 2, 2))
136+
>>> a
137+
array([[[0, 1],
138+
[2, 3]],
139+
[[4, 5],
140+
[6, 7]]])
141+
>>> a.mT
142+
array([[[0, 2],
143+
[1, 3]],
144+
[[4, 6],
145+
[5, 7]]])
146+
147+
"""
148+
149+
if self.ndim < 2:
150+
raise ValueError("matrix transpose with ndim < 2 is undefined")
151+
152+
return self._array_obj.mT
153+
111154
def to_device(self, target_device):
112155
"""Transfer array to target device."""
113156

dpnp/dpnp_iface_manipulation.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
"flipud",
7474
"hsplit",
7575
"hstack",
76+
"matrix_transpose",
7677
"moveaxis",
7778
"ndim",
7879
"permute_dims",
@@ -1755,6 +1756,58 @@ def hstack(tup, *, dtype=None, casting="same_kind"):
17551756
return dpnp.concatenate(arrs, axis=1, dtype=dtype, casting=casting)
17561757

17571758

1759+
def matrix_transpose(x, /):
1760+
"""
1761+
Transposes a matrix (or a stack of matrices) `x`.
1762+
1763+
For full documentation refer to :obj:`numpy.matrix_transpose`.
1764+
1765+
Parameters
1766+
----------
1767+
x : (..., M, N) {dpnp.ndarray, usm_ndarray}
1768+
Input array with ``x.ndim >= 2`` and whose two innermost
1769+
dimensions form ``MxN`` matrices.
1770+
1771+
Returns
1772+
-------
1773+
out : dpnp.ndarray
1774+
An array containing the transpose for each matrix and having shape
1775+
(..., N, M).
1776+
1777+
See Also
1778+
--------
1779+
:obj:`dpnp.transpose` : Returns an array with axes transposed.
1780+
:obj:`dpnp.linalg.matrix_transpose` : Equivalent function.
1781+
:obj:`dpnp.ndarray.mT` : Equivalent method.
1782+
1783+
Examples
1784+
--------
1785+
>>> import dpnp as np
1786+
>>> a = np.array([[1, 2], [3, 4]])
1787+
>>> np.matrix_transpose(a)
1788+
array([[1, 3],
1789+
[2, 4]])
1790+
1791+
>>> b = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
1792+
>>> np.matrix_transpose(b)
1793+
array([[[1, 3],
1794+
[2, 4]],
1795+
[[5, 7],
1796+
[6, 8]]])
1797+
1798+
"""
1799+
1800+
usm_x = dpnp.get_usm_ndarray(x)
1801+
if usm_x.ndim < 2:
1802+
raise ValueError(
1803+
"Input array must be at least 2-dimensional, "
1804+
f"but it is {usm_x.ndim}"
1805+
)
1806+
1807+
usm_res = dpt.matrix_transpose(usm_x)
1808+
return dpnp_array._create_from_usm_ndarray(usm_res)
1809+
1810+
17581811
def moveaxis(a, source, destination):
17591812
"""
17601813
Move axes of an array to new positions. Other axes remain in their original

dpnp/dpnp_utils/dpnp_utils_einsum.py

Lines changed: 11 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -109,29 +109,6 @@ def _compute_size_by_dict(indices, idx_dict):
109109
return ret
110110

111111

112-
def _compute_size(start, shape):
113-
"""
114-
Compute the total size of a multi-dimensional array starting from a given index.
115-
116-
Parameters
117-
----------
118-
start : int
119-
The starting index from which to compute the size.
120-
shape : tuple
121-
The shape of the multi-dimensional array.
122-
123-
Returns
124-
-------
125-
out : int
126-
The total size of the array.
127-
128-
"""
129-
ret = 1
130-
for i in range(start, len(shape)):
131-
ret *= shape[i]
132-
return ret
133-
134-
135112
def _einsum_diagonals(input_subscripts, operands):
136113
"""
137114
Adopted from _einsum_diagonals in cupy/core/_einsum.py
@@ -818,11 +795,11 @@ def _parse_int_subscript(list_subscript):
818795
"For this input type lists must contain "
819796
"either int or Ellipsis"
820797
) from e
821-
if isinstance(s, int):
822-
if not 0 <= s < len(_einsum_symbols):
823-
raise ValueError(
824-
f"subscript is not within the valid range [0, {len(_einsum_symbols)})."
825-
)
798+
799+
if not 0 <= s < len(_einsum_symbols):
800+
raise ValueError(
801+
f"subscript is not within the valid range [0, {len(_einsum_symbols)})."
802+
)
826803
str_subscript += _einsum_symbols[s]
827804
return str_subscript
828805

@@ -1116,12 +1093,14 @@ def dpnp_einsum(
11161093
f"'{_chr(label)}' which never appeared in an input."
11171094
)
11181095
if len(output_subscript) != len(set(output_subscript)):
1096+
repeated_subscript = []
11191097
for label in output_subscript:
11201098
if output_subscript.count(label) >= 2:
1121-
raise ValueError(
1122-
"einstein sum subscripts string includes output "
1123-
f"subscript '{_chr(label)}' multiple times."
1124-
)
1099+
repeated_subscript.append(_chr(label))
1100+
raise ValueError(
1101+
"einstein sum subscripts string includes output "
1102+
f"subscript {set(repeated_subscript)} multiple times."
1103+
)
11251104

11261105
_einsum_diagonals(input_subscripts, operands)
11271106

dpnp/dpnp_utils/dpnp_utils_statistics.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626

2727
import dpnp
28-
from dpnp.dpnp_utils import get_usm_allocations
28+
from dpnp.dpnp_utils import get_usm_allocations, map_dtype_to_device
2929

3030
__all__ = ["dpnp_cov"]
3131

@@ -73,12 +73,7 @@ def _get_2dmin_array(x, dtype):
7373
dtypes.append(y.dtype)
7474
dtype = dpnp.result_type(*dtypes)
7575
# TODO: remove when dpctl.result_type() is returned dtype based on fp64
76-
fp64 = queue.sycl_device.has_aspect_fp64
77-
if not fp64:
78-
if dtype == dpnp.float64:
79-
dtype = dpnp.float32
80-
elif dtype == dpnp.complex128:
81-
dtype = dpnp.complex64
76+
dtype = map_dtype_to_device(dtype, queue.sycl_device)
8277

8378
X = _get_2dmin_array(m, dtype)
8479
if y is not None:

dpnp/linalg/dpnp_iface_linalg.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
"matrix_norm",
8282
"matrix_power",
8383
"matrix_rank",
84+
"matrix_transpose",
8485
"multi_dot",
8586
"norm",
8687
"outer",
@@ -1117,6 +1118,50 @@ def matrix_rank(A, tol=None, hermitian=False):
11171118
return dpnp_matrix_rank(A, tol=tol, hermitian=hermitian)
11181119

11191120

1121+
def matrix_transpose(x, /):
1122+
"""
1123+
Transposes a matrix (or a stack of matrices) `x`.
1124+
1125+
For full documentation refer to :obj:`numpy.linalg.matrix_transpose`.
1126+
1127+
Parameters
1128+
----------
1129+
x : (..., M, N) {dpnp.ndarray, usm_ndarray}
1130+
Input array with ``x.ndim >= 2`` and whose two innermost
1131+
dimensions form ``MxN`` matrices.
1132+
1133+
Returns
1134+
-------
1135+
out : dpnp.ndarray
1136+
An array containing the transpose for each matrix and having shape
1137+
(..., N, M).
1138+
1139+
See Also
1140+
--------
1141+
:obj:`dpnp.transpose` : Returns an array with axes transposed.
1142+
:obj:`dpnp.matrix_transpose` : Equivalent function.
1143+
:obj:`dpnp.ndarray.mT` : Equivalent method.
1144+
1145+
Examples
1146+
--------
1147+
>>> import dpnp as np
1148+
>>> a = np.array([[1, 2], [3, 4]])
1149+
>>> np.linalg.matrix_transpose(a)
1150+
array([[1, 3],
1151+
[2, 4]])
1152+
1153+
>>> b = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
1154+
>>> np.linalg.matrix_transpose(b)
1155+
array([[[1, 3],
1156+
[2, 4]],
1157+
[[5, 7],
1158+
[6, 8]]])
1159+
1160+
"""
1161+
1162+
return dpnp.matrix_transpose(x)
1163+
1164+
11201165
def multi_dot(arrays, *, out=None):
11211166
"""
11221167
Compute the dot product of two or more arrays in a single function call.

tests/test_arraymanipulation.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,50 @@ def test_one_element(self):
556556
assert_array_equal(res, a)
557557

558558

559+
# numpy.matrix_transpose() is available since numpy >= 2.0
560+
@testing.with_requires("numpy>=2.0")
561+
class TestMatrixtranspose:
562+
@pytest.mark.parametrize("dtype", get_all_dtypes(no_bool=True))
563+
@pytest.mark.parametrize(
564+
"shape",
565+
[(3, 5), (4, 2), (2, 5, 2), (2, 3, 3, 6)],
566+
ids=["(3,5)", "(4,2)", "(2,5,2)", "(2,3,3,6)"],
567+
)
568+
def test_matrix_transpose(self, dtype, shape):
569+
a = numpy.arange(numpy.prod(shape), dtype=dtype).reshape(shape)
570+
dp_a = dpnp.array(a)
571+
572+
expected = numpy.matrix_transpose(a)
573+
result = dpnp.matrix_transpose(dp_a)
574+
575+
assert_allclose(result, expected)
576+
577+
@pytest.mark.parametrize(
578+
"shape",
579+
[(0, 0), (1, 0, 0), (0, 2, 2), (0, 1, 0, 4)],
580+
ids=["(0,0)", "(1,0,0)", "(0,2,2)", "(0, 1, 0, 4)"],
581+
)
582+
def test_matrix_transpose_empty(self, shape):
583+
a = numpy.empty(shape, dtype=dpnp.default_float_type())
584+
dp_a = dpnp.array(a)
585+
586+
expected = numpy.matrix_transpose(a)
587+
result = dpnp.matrix_transpose(dp_a)
588+
589+
assert_allclose(result, expected)
590+
591+
def test_matrix_transpose_errors(self):
592+
a_dp = dpnp.array([[1, 2], [3, 4]], dtype="float32")
593+
594+
# unsupported type
595+
a_np = dpnp.asnumpy(a_dp)
596+
assert_raises(TypeError, dpnp.matrix_transpose, a_np)
597+
598+
# a.ndim < 2
599+
a_dp_ndim_1 = a_dp.flatten()
600+
assert_raises(ValueError, dpnp.matrix_transpose, a_dp_ndim_1)
601+
602+
559603
class TestRollaxis:
560604
data = [
561605
(0, 0),

0 commit comments

Comments
 (0)