Skip to content

Commit 3e5bc83

Browse files
authored
Merge branch 'master' into impl_tensorinv
2 parents 7f74061 + e44469c commit 3e5bc83

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1171
-1058
lines changed

dpnp/linalg/dpnp_iface_linalg.py

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
dpnp_det,
5252
dpnp_eigh,
5353
dpnp_inv,
54+
dpnp_matrix_power,
5455
dpnp_matrix_rank,
5556
dpnp_multi_dot,
5657
dpnp_pinv,
@@ -371,33 +372,65 @@ def inv(a):
371372
return dpnp_inv(a)
372373

373374

374-
def matrix_power(input, count):
375+
def matrix_power(a, n):
375376
"""
376-
Raise a square matrix to the (integer) power `count`.
377+
Raise a square matrix to the (integer) power `n`.
378+
379+
For full documentation refer to :obj:`numpy.linalg.matrix_power`.
377380
378381
Parameters
379382
----------
380-
input : sequence of array_like
383+
a : (..., M, M) {dpnp.ndarray, usm_ndarray}
384+
Matrix to be "powered".
385+
n : int
386+
The exponent can be any integer or long integer, positive, negative, or zero.
381387
382388
Returns
383389
-------
384-
output : array
385-
Returns the dot product of the supplied arrays.
390+
a**n : (..., M, M) dpnp.ndarray
391+
The return value is the same shape and type as `M`;
392+
if the exponent is positive or zero then the type of the
393+
elements is the same as those of `M`. If the exponent is
394+
negative the elements are floating-point.
386395
387-
See Also
388-
--------
389-
:obj:`numpy.linalg.matrix_power`
396+
>>> import dpnp as np
397+
>>> i = np.array([[0, 1], [-1, 0]]) # matrix equiv. of the imaginary unit
398+
>>> np.linalg.matrix_power(i, 3) # should = -i
399+
array([[ 0, -1],
400+
[ 1, 0]])
401+
>>> np.linalg.matrix_power(i, 0)
402+
array([[1, 0],
403+
[0, 1]])
404+
>>> np.linalg.matrix_power(i, -3) # should = 1/(-i) = i, but w/ f.p. elements
405+
array([[ 0., 1.],
406+
[-1., 0.]])
407+
408+
Somewhat more sophisticated example
409+
410+
>>> q = np.zeros((4, 4))
411+
>>> q[0:2, 0:2] = -i
412+
>>> q[2:4, 2:4] = i
413+
>>> q # one of the three quaternion units not equal to 1
414+
array([[ 0., -1., 0., 0.],
415+
[ 1., 0., 0., 0.],
416+
[ 0., 0., 0., 1.],
417+
[ 0., 0., -1., 0.]])
418+
>>> np.linalg.matrix_power(q, 2) # = -np.eye(4)
419+
array([[-1., 0., 0., 0.],
420+
[ 0., -1., 0., 0.],
421+
[ 0., 0., -1., 0.],
422+
[ 0., 0., 0., -1.]])
390423
391424
"""
392425

393-
if not use_origin_backend() and count > 0:
394-
result = input
395-
for _ in range(count - 1):
396-
result = dpnp.matmul(result, input)
426+
dpnp.check_supported_arrays_type(a)
427+
check_stacked_2d(a)
428+
check_stacked_square(a)
397429

398-
return result
430+
if not isinstance(n, int):
431+
raise TypeError("exponent must be an integer")
399432

400-
return call_origin(numpy.linalg.matrix_power, input, count)
433+
return dpnp_matrix_power(a, n)
401434

402435

403436
def matrix_rank(A, tol=None, hermitian=False):

dpnp/linalg/dpnp_utils_linalg.py

Lines changed: 85 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"dpnp_det",
4141
"dpnp_eigh",
4242
"dpnp_inv",
43+
"dpnp_matrix_power",
4344
"dpnp_matrix_rank",
4445
"dpnp_multi_dot",
4546
"dpnp_pinv",
@@ -526,9 +527,50 @@ def _stacked_identity(
526527
"""
527528

528529
shape = batch_shape + (n, n)
529-
idx = dpnp.arange(n, usm_type=usm_type, sycl_queue=sycl_queue)
530-
x = dpnp.zeros(shape, dtype=dtype, usm_type=usm_type, sycl_queue=sycl_queue)
531-
x[..., idx, idx] = 1
530+
x = dpnp.empty(shape, dtype=dtype, usm_type=usm_type, sycl_queue=sycl_queue)
531+
x[...] = dpnp.eye(
532+
n, dtype=x.dtype, usm_type=x.usm_type, sycl_queue=x.sycl_queue
533+
)
534+
return x
535+
536+
537+
def _stacked_identity_like(x):
538+
"""
539+
Create stacked identity matrices based on the shape and properties of `x`.
540+
541+
Parameters
542+
----------
543+
x : dpnp.ndarray
544+
Input array based on whose properties (shape, data type, USM type and SYCL queue)
545+
the identity matrices will be created.
546+
547+
Returns
548+
-------
549+
out : dpnp.ndarray
550+
Array of stacked `n x n` identity matrices,
551+
where `n` is the size of the last dimension of `x`.
552+
The returned array has the same shape, data type, USM type
553+
and uses the same SYCL queue as `x`, if applicable.
554+
555+
Example
556+
-------
557+
>>> import dpnp
558+
>>> x = dpnp.zeros((2, 3, 3), dtype=dpnp.int64)
559+
>>> _stacked_identity_like(x)
560+
array([[[1, 0, 0],
561+
[0, 1, 0],
562+
[0, 0, 1]],
563+
564+
[[1, 0, 0],
565+
[0, 1, 0],
566+
[0, 0, 1]]], dtype=int32)
567+
568+
"""
569+
570+
x = dpnp.empty_like(x)
571+
x[...] = dpnp.eye(
572+
x.shape[-2], dtype=x.dtype, usm_type=x.usm_type, sycl_queue=x.sycl_queue
573+
)
532574
return x
533575

534576

@@ -1082,6 +1124,46 @@ def dpnp_inv(a):
10821124
return b_f
10831125

10841126

1127+
def dpnp_matrix_power(a, n):
1128+
"""
1129+
dpnp_matrix_power(a, n)
1130+
1131+
Raise a square matrix to the (integer) power `n`.
1132+
1133+
"""
1134+
1135+
if n == 0:
1136+
return _stacked_identity_like(a)
1137+
elif n < 0:
1138+
a = dpnp.linalg.inv(a)
1139+
n *= -1
1140+
1141+
if n == 1:
1142+
return a
1143+
elif n == 2:
1144+
return dpnp.matmul(a, a)
1145+
elif n == 3:
1146+
return dpnp.matmul(dpnp.matmul(a, a), a)
1147+
1148+
# Use binary decomposition to reduce the number of matrix
1149+
# multiplications for n > 3.
1150+
# `result` will hold the final matrix power,
1151+
# while `acc` serves as an accumulator for the intermediate matrix powers.
1152+
result = None
1153+
acc = a.copy()
1154+
while n > 0:
1155+
n, bit = divmod(n, 2)
1156+
if bit:
1157+
if result is None:
1158+
result = acc.copy()
1159+
else:
1160+
dpnp.matmul(result, acc, out=result)
1161+
if n > 0:
1162+
dpnp.matmul(acc, acc, out=acc)
1163+
1164+
return result
1165+
1166+
10851167
def dpnp_matrix_rank(A, tol=None, hermitian=False):
10861168
"""
10871169
dpnp_matrix_rank(A, tol=None, hermitian=False)

0 commit comments

Comments
 (0)