问题
This question is close to what is asked in Overriding other __rmul__ with your class's __mul__ but I am under the impression that this is a more general problem then only numerical data. Also that is not answered and I really don't want to use the matrix multiplication @
for this operation. Hence, the question.
I do have an object which accepts multiplication with scalars and numerical arrays. As usual, the left multiplication works fine since it is the myobj()
methods are used but in the right multiplication, NumPy uses broadcasting rules and gives elementwise results with dtype=object
.
This has also the side-effect of not being able to check the size of the array whether the size is compatible or not.
Therefore, the question is
Is there a way to force numpy array to look for the
__rmul__()
of the other object instead of broadcasting and performing elementwise__mul__()
?
In my particular case, the object is a MIMO (multiple-input, multiple-output) transfer function matrix (or filter coefficients matrix if you will) so matrix multiplication has a special meaning in terms of adding and multiplying linear systems. Hence in each entry there is SISO system.
import numpy as np
class myobj():
def __init__(self):
pass
def __mul__(self, other):
if isinstance(other, type(np.array([0.]))):
if other.size == 1:
print('Scalar multiplication')
else:
print('Multiplication of arrays')
def __rmul__(self, other):
if isinstance(other, type(np.array([0.]))):
if other.size == 1:
print('Scalar multiplication')
else:
print('Multiplication of arrays')
A = myobj()
a = np.array([[[1+1j]]]) # some generic scalar
B = np.random.rand(3, 3)
With these definitions, the following commands show the undesired behavior.
In [123]: A*a
Scalar multiplication
In [124]: a*A
Out[124]: array([[[None]]], dtype=object)
In [125]: B*A
Out[125]:
array([[None, None, None],
[None, None, None],
[None, None, None]], dtype=object)
In [126]: A*B
Multiplication of arrays
In [127]: 5 * A
In [128]: A.__rmul__(B) # This is the desired behavior for B*A
Multiplication of arrays
回答1:
By default, NumPy assumes that unknown object (not inheriting from ndarray) are scalars, and it needs to "vectorize" multiplication over each element of any NumPy arrays.
To control the operations yourself, you need to set either __array_priority__
(most backwards compatible) or __array_ufunc__
(NumPy 1.13+ only). For example:
class myworkingobj(myobj):
__array_priority__ = 1000
A = myworkingobj()
B = np.random.rand(3, 3)
B * A # Multiplication of arrays
回答2:
I'll try to demonstrate what is going on.
In [494]: B=np.random.rand(3,3)
barebones class:
In [497]: class myobj():
...: pass
...:
In [498]: B*myobj()
...
TypeError: unsupported operand type(s) for *: 'float' and 'myobj'
add a __mul__
In [500]: class myobj():
...: pass
...: def __mul__(self,other):
...: print('myobj mul')
...: return 12.3
...:
In [501]: B*myobj()
...
TypeError: unsupported operand type(s) for *: 'float' and 'myobj'
In [502]: myobj()*B
myobj mul
Out[502]: 12.3
add a rmul
:
In [515]: class myobj():
...: pass
...: def __mul__(self,other):
...: print('myobj mul',other)
...: return 12.3
...: def __rmul__(self,other):
...: print('myobj rmul',other)
...: return 4.32
...:
In [516]: B*myobj()
myobj rmul 0.792751549595306
myobj rmul 0.5668783619454384
myobj rmul 0.2196204913660168
myobj rmul 0.5474970289273348
myobj rmul 0.2079367474424587
myobj rmul 0.5374571198848628
myobj rmul 0.35748803226628456
myobj rmul 0.41306113085906715
myobj rmul 0.499598995529441
Out[516]:
array([[4.32, 4.32, 4.32],
[4.32, 4.32, 4.32],
[4.32, 4.32, 4.32]], dtype=object)
B*myobj()
is given to B
, as B.__mul__(myobj())
, which proceeds to perform myobj().__rmul__(i)
for each element of B
.
In myobj()*B
translates to myobj.__mul__(B)
:
In [517]: myobj()*B
myobj mul [[ 0.79275155 0.56687836 0.21962049]
[ 0.54749703 0.20793675 0.53745712]
[ 0.35748803 0.41306113 0.499599 ]]
Out[517]: 12.3
In [518]: myobj().__rmul__(B)
myobj rmul [[ 0.79275155 0.56687836 0.21962049]
[ 0.54749703 0.20793675 0.53745712]
[ 0.35748803 0.41306113 0.499599 ]]
Out[518]: 4.32
You can't do anything in myobj
to override the translation of B*myobj()
to B.__mul__(myobj())
. Use functions or methods if you need greater control over operation. It's hard to fight the interpreter.
来源:https://stackoverflow.com/questions/40694380/forcing-multiplication-to-use-rmul-instead-of-numpy-array-mul-or-byp