У меня есть два np.ndarray подкласса. Tuple @ Matrix возвращает Tuple, но Matrix @ Tuple возвращает Matrix. Как я могу заставить его вернуть Tuple вместо этого?

import numpy as np

class Tuple(np.ndarray):
    def __new__(cls, input_array, info=None):
        return np.asarray(input_array).view(cls)

class Matrix(np.ndarray):
    def __new__(cls, input_array, info=None):
        return np.asarray(input_array).view(cls)

def scaling(x, y, z):
    m = Matrix(np.identity(4))
    m[0, 0] = x
    m[1, 1] = y
    m[2, 2] = z
    return m

Пример:

>>> Tuple([1,2,3,4]) @ scaling(2,2,2)
Tuple([2., 4., 6., 4.])

>>> scaling(2,2,2) @ Tuple([1,2,3,4])
Matrix([2., 4., 6., 4.])   # XXXX I'd like this to be a Tuple

PS: Matrix @ Matrix должен вернуть Matrix

0
sigjuice 27 Июн 2019 в 10:02

4 ответа

Лучший ответ

Я сделал ошибку при копировании из примера np.matrix.

class Tuple(np.ndarray): 
    __array_priority__ = 10 
    def __new__(cls, input_array, info=None): 
        return np.asarray(input_array).view(cls) 
class Matrix(np.ndarray):
    __array_priority__ = 5.0 
    def __new__(cls, input_array, info=None): 
        return np.asarray(input_array).view(cls)

In [2]: def scaling(x, y, z):  
   ...:      ...:     m = Matrix(np.identity(4))  
   ...:      ...:     m[0, 0] = x  
   ...:      ...:     m[1, 1] = y  
   ...:      ...:     m[2, 2] = z  
   ...:      ...:     return m  
   ...:                                                                                                                                  
In [3]: Tuple([1,2,3,4]) @ scaling(2,2,2)                                                                                                
Out[3]: Tuple([2., 4., 6., 4.])
In [4]: scaling(2,2,2) @ Tuple([1,2,3,4])                                                                                                
Out[4]: Tuple([2., 4., 6., 4.])

===

Взяв подсказку из определения np.matrix: numpy.matrixlib.defmatrix.py

Добавьте атрибут __array_priority__:

In [382]: class Tuple(np.ndarray): 
     ...:     def __new__(cls, input_array, info=None): 
     ...:         __array_priority = 10 
     ...:         return np.asarray(input_array).view(cls) 
     ...:  
     ...: class Matrix(np.ndarray): 
     ...:     def __new__(cls, input_array, info=None): 
     ...:         __array_priority = 5 
     ...:         return np.asarray(input_array).view(cls) 
     ...:                                                                                            
In [383]:                                                                                            
In [383]: def scaling(x, y, z): 
     ...:     m = Matrix(np.identity(4)) 
     ...:     m[0, 0] = x 
     ...:     m[1, 1] = y 
     ...:     m[2, 2] = z 
     ...:     return m 
     ...:                                                                                            
In [384]: Tuple([1,2,3,4]) @ scaling(2,2,2)                                                          
Out[384]: Tuple([2., 4., 6., 4.])
In [385]: scaling(2,2,2) @ Tuple([1,2,3,4])                                                          
Out[385]: Matrix([2., 4., 6., 4.])
1
hpaulj 28 Июн 2019 в 00:56

Одним из способов решения этой проблемы является реализация пользовательского __matmul__ в Matrix и __rmatmul__ в Tuple:

import numpy as np

class Tuple(np.ndarray):
    def __new__(cls, input_array, info=None):
        return np.asarray(input_array).view(cls)

    def __rmatmul__(self, other):
        return super().__matmul__(other)

class Matrix(np.ndarray):
    def __new__(cls, input_array, info=None):
        return np.asarray(input_array).view(cls)

    def __matmul__(self, other):
        if not isinstance(other, Matrix):
            return NotImplemented
        return super().__matmul__(other)

def scaling(x, y, z):
    m = Matrix(np.identity(4))
    m[0, 0] = x
    m[1, 1] = y
    m[2, 2] = z
    return m

scaling(2,2,2) @ scaling(2,2,2)
# Matrix([[4., 0., 0., 0.],
#         [0., 4., 0., 0.],
#         [0., 0., 4., 0.],
#         [0., 0., 0., 1.]])
Tuple([1,2,3,4]) @ scaling(2,2,2)
# Tuple([2., 4., 6., 4.])
scaling(2,2,2) @ Tuple([1,2,3,4])
# Tuple([2., 4., 6., 4.])
0
Nils Werner 27 Июн 2019 в 14:47

Вы можете перегрузить метод __matmul__, чтобы вернуть Tuple - и если вы хотите быть Tuple, если какая-либо из переменных является Tuple и Matrix в противном случае, я думаю, что это сработает:

class Matrix(np.ndarray):
    def __new__(cls, input_array, info=None):
        return np.asarray(input_array).view(cls)

    def __matmul__(m1, m2):
         return (m2.T @ m1.T).T if isinstance(m2, Tuple) else np.matmul(m1, m2)
1
sigjuice 28 Июн 2019 в 00:06

Просто перегрузите __matmul__ класса Matrix для возврата кортежа

class Matrix(np.ndarray):
    def __new__(cls, input_array, info=None):
        return np.asarray(input_array).view(cls)

    def __matmul__(self, other):
        return other @ self

-2
Priyatham 27 Июн 2019 в 07:39