I'm trying to do some symbolic matrix calculations with sympy. My goal is to obtain a symbolic representation of the result of some matrix computations. I've run into some problems which I have boiled down to this simple example, in which I try to evaluate the result of a exponentiating a specified matrix and multiplying it by an arbitrary vector.
>>> import sympy
>>> v = sympy.MatrixSymbol('v', 2, 1)
>>> Z = sympy.zeros(2, 2) # create 2x2 zero matrix
>>> I = sympy.exp(Z) # exponentiate zero matrix to get identity matrix
>>> I * v
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "sympy/matrices/matrices.py", line 507, in __mul__
blst = B.T.tolist()
AttributeError: 'Transpose' object has no attribute 'tolist'
In contrast, if I directly create the identity matrix and then multiply it by v, then there is no problem:
>>> I_ = sympy.eye(2) # directly create the identity matrix
>>> I_ == I # check the two matrices are equal
True
>>> I_ * v
v
One thing that I've noted is that the two identity matrices are of different classes:
>>> I.__class__
sympy.matrices.immutable.ImmutableMatrix
>>> I_.__class__
sympy.matrices.dense.MutableDenseMatrix
I also found that calling the as_mutable()
method provided a work-around.
>>> I.as_mutable() * v
v
Is it always necessary to put as_mutable()
calls throughout one's linear algebra calculations? I'm guessing not, and that instead these errors suggest that I'm using the wrong strategy to solve my problem, but I can't figure out what the right strategy would be. Does anyone have any pointers?
I have read the documentation page on Immutable Matrices but I could still use some help understanding how their differences with standard mutable matrices are important here, and why some operations (e.g. sympy.exp) convert between these different classes.
I'd claim that this is a bug in Sympy:
In Python, you can overload the multiplication operator from both sides. A*B
may internally be handled by either calling A.__mul__(B)
, or B.__rmul__(A)
. Python first calls A.__mul__
, and if this method does not exist or returns NotImplemented
, then Python tries B.__rmul__
automatically. SymPy instead uses a decorator called call_highest_priority to decide which of both implementations to use. It looks up the _op_priority
of the involved classes and calls the function of the implementation with higher priority. The priorities in your case are 11 for v
and I
and 10.01 for I_
, so I
is preferred. Also, the base implementation of __mul__
, which I
uses, lacks the decorator.
Long story short, I*v
ends up always calling I.__mul__
, and __mul__
cannot handle MatrixSymbol
s but does not return NotImplemented
either. v.__rmul__(I)
works as expected.
The proper fix would be to capture the AttributeError
in matrices.py
and return NotImplemented
, i.e.
try:
blst = B.T.tolist()
except AttributeError:
return NotImplemented
Python would then automatically fallback to __rmul__
. The hack'ish fix would be to adjust _op_priority
. Either way, you should file a bug report: If the error was by design (that is, if you accidentally tried something that's not supposed to work), then the error message would say so.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With