One thing that I have noticed with regard to overriding __mul__ and
__rmul__ (for example) is that you can never completely control what
happens to your class because of association. For example, suppose
that A and B are MatrixExprs and x is some Expr object (say, a
Symbol). Suppose that A*B should give a ShapeError, and that x is a
scalar, which does not matter where it is in the expression. Then you
have to program it so that all of the following give ShapeError:
1. x*(A*B)
2. (x*A)*B
3. (A*x)*B
4. A*(x*B)
5. (A*B)*x
6. A*(B*x)
Let us look at these. Obviously, 1 and 5 will work, since you have
complete control over A.__mul__(B). Similarly, in 4 and 6, you will
end up with A.__mul__(Mul(x, B)), but this is not a problem, since you
can make A.__mul__ check for the left-most noncommutative in a Mul.
But what about 2 and 3? Here, we have __mul__ being called by a Expr
type, namely, Mul(x, A). You want to get B.__rmul__(Mul(x, A)), since
you have implemented shape checking logic in MatrixExpr.__(r)mul__.
But b.__rmul__(a) is called with a*b only if a.__mul__(b) returns
NotImplemented. So, basically, we need Mul.__mul__(MatrixExpr) to
return NotImplemented. Well, let us look at the code for Mul.__mul__
(actually Expr.__mul__):
@_sympifyit('other', NotImplemented)
@call_highest_priority('__rmul__')
def __mul__(self, other):
return Mul(self, other)
Well, this is not very helpful, because the logic is buried in some
decorators. So let us look at the code for those decorators. I will
not bore you by pasting the entire code here (you can see it in
sympy/core/decorators.py), but basically, _sympifyit makes tries to
sympify other, and makes __mul__ return NotImplemented if it fails. I
am not delving into this, because sympify would not fail for
MatrixExpr (otherwise, we would actually have a problem doing any form
of x*A*B).
So call_highest_priority seems to be a better bet. This decorator I
will also not paste here, but it basically lets you define
._op_priority on your object, and if it is greater than Expr's (which
is quite arbitrarily set to 10.0), then it will call
other.__rmul__(self).
But wait, there's more. _op_priority can make the above cases work,
but * is not the only way that SymPy multiplies things. You will also
have to deal with variations on Mul(x, A, B). Mul completely ignores
_op_priority. Unfortunately, even if this may seem like a more
esoteric way to multiply things that you can just recommend users
avoid, it is used internally a lot, because Mul(*args) is more
efficient than reduce(operator.mul, args).
Thus, you see that it is quite impossible to make things work 100% of
the time without modifying the core. And actually, because of the Mul
thing that would not work at all and that is called by so many core
functions and methods, you will not even get something like things
working 90% of the time, but instead things will break when used a
certain way, and you will have hard to track bugs.
Aaron Meurer
On Tue, Jul 12, 2011 at 3:08 PM, Matthew Rocklin <[email protected]> wrote:
> Subclassing Expr has some issues as well. This is what Brian was referring
> to. Within all of our code we use Add and Mul and don't check if instead we
> should use some subclass of Add or subclass of Mul. If I feed a matrix
> expression into these objects then the special matrix structure is lost.
> This happens if, for example, you call simplify on a matrix expression.
> I think I can get around this though with a few well placed "matrixify"
> functions. Matrixify is a function which goes through the expression tree
> and makes appropriate fixes. I've had good success so far with a very very
> basic Matrixify function.
> Brian, did you have particular horror stories trying to subclass Expr? I'm
> enthusiastic about my approach but you seemed to have a bad experience. Can
> you suggest difficult test cases that I should think about?
>
>
> On Fri, Jul 8, 2011 at 3:22 PM, Aaron Meurer <[email protected]> wrote:
>>
>> On Fri, Jul 8, 2011 at 4:00 AM, SherjilOzair <[email protected]>
>> wrote:
>> > There is something I'm doing as part of my project which maybe be
>> > useful. I'm implementing a Matrix_ wrapper class which will wrap over
>> > low-level matrices. Its being written to replace the current Matrix
>> > class. Algorithmic code and user-level code is being separated into
>> > different classes. Currently, we have three internal matrices, the
>> > DenseMatrix( a modified form of the current Matrix class), DOKMatrix
>> > and LILMatrix. These three are essentially Data internal matrices.
>> > The MatrixSymbol can be another object that Matrix_ (later Matrix)
>> > would use as internal.
>> >
>> > Of what I understand from this discussion about the Matrix Symbol, we
>> > need to have an object that will be treated like a Matrix everywhere,
>> > but without the internal data. Space is being made in the Matrix
>> > module for such an object, but for it work nicely it needs to interact
>> > nicely with Expr objects. If its a problem subclassing Expr and
>> > friends, I'm +1 about making a separate codebase for matrix
>> > expressions. It is, after all, a different algebra.
>> >
>> > I'm not very familiar with Expr, Add, etc. but I think a separate
>> > algebra MatrixExpr, MatrixAdd, etc. could be developed by copy-pasting
>> > some code from Expr, Add, and modifying according to need. I say 'copy-
>> > paste' and not subclassing as the code would be need to be modified
>> > much before it can work on matrices.
>>
>> As has been discussed before, this is a bad idea. Just subclass Expr.
>>
>> Aaron Meurer
>>
>> >
>> > Presently, I'm quite busy with this new encapsulation class Matrix_
>> > and can only think of working fulltime on MatrixSymbol after I've
>> > merged in my work on DOKMatrix, LILMatrix and the ongoing work on
>> > Matrix_.
>> >
>> > Matthew, you could set up a wiki page on 'matrix expressions' with
>> > some example code in it. 'symbolic matrices' can mean a few number of
>> > things, and is somewhat vague. It would be good if everyone could see
>> > what sort of concrete things are expected from matrix expressions.
>> > Approaches to implement Matrix Algebra, with pros and cons listed,
>> > could be added there for discussion, suggestion and improvement.
>> >
>> > Regards,
>> > Sherjil Ozair
>> >
>> > --
>> > You received this message because you are subscribed to the Google
>> > Groups "sympy" group.
>> > To post to this group, send email to [email protected].
>> > To unsubscribe from this group, send email to
>> > [email protected].
>> > For more options, visit this group at
>> > http://groups.google.com/group/sympy?hl=en.
>> >
>> >
>>
>> --
>> You received this message because you are subscribed to the Google Groups
>> "sympy" group.
>> To post to this group, send email to [email protected].
>> To unsubscribe from this group, send email to
>> [email protected].
>> For more options, visit this group at
>> http://groups.google.com/group/sympy?hl=en.
>>
>
> --
> You received this message because you are subscribed to the Google Groups
> "sympy" group.
> To post to this group, send email to [email protected].
> To unsubscribe from this group, send email to
> [email protected].
> For more options, visit this group at
> http://groups.google.com/group/sympy?hl=en.
>
--
You received this message because you are subscribed to the Google Groups
"sympy" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/sympy?hl=en.