Le vendredi 27 juillet 2012 à 15:07 -0600, Aaron Meurer a écrit :
> On Fri, Jul 27, 2012 at 2:55 PM, Ronan Lamy <[email protected]> wrote:
> > I'm experimenting with a system implementing a perfectly sound
> > double-dispatch, even in the face of multiple inheritance, bypassing
> > Python's byzantine and subtly incorrect rules. It is based on a callable
> > binary_operation object that stores implementations for pairs of types.
> > When it is called, it finds the implementation corresponding to the most
> > derived pair of types or raises an error in case of ambiguity.
> >
> > Concretely, with the following definitions:
> >
> > power = binary_operation()
> >
> > @power.define(Expr, Expr) # same as power[Expr, Expr] = _pow_base
> > def _pow_base(x, y):
> >     return Pow(x, y)
> >
> > @power.define(Expr, Zero)
> > def _pow_Expr_Zero(x, zero):
> >     return S.One
> >
> > @power.define(Zero, Expr)
> > def _pow_Zero_Expr(zero, x):
> >     return S.Zero
> >
> > then:
> >         * power(Symbol('x'), Integer(2)) calls Pow.
> >         * power(Symbol('x'), S.Zero) returns S.one directly.
> >         * power(S.Zero, S.Zero) raises an error, because there are two
> >         conflicting definitions. You can fix this with
> >         power[Zero, Zero] = power[Expr, Zero].
> 
> I'd say that's actually correct behavior.  0**0 is defined to be 1,
> but it's actually an ambiguous definition.

Yes, that's correct. The example was actually meant to demonstrate what
happens when you make a coding mistake (forgetting that the value of
0**0 is a matter of convention).

> >
> > Now, if we plug this function into Expr.__pow__, we'll be able to define
> > the behaviour of '**' in a modular way, provided we also turn Pow into
> > an inert object (i.e. with the behaviour currently corresponding to
> > evaluate=False). I've started coding in that direction in
> > https://github.com/rlamy/sympy/tree/binop It works (except for some
> > problems with integral transforms) but it's a bit slow as the overhead
> > of double-dispatch is significant.
> 
> I was going to ask if this was the case.  Is there any way to make it faster?

Caching the dispatch would be an obvious way, though the cache could
grow quite big, given that we have hundreds of classes. 

The algorithm could also be improved, as it simply tries all possible
combinations of superclasses ATM.

> >
> > What do you think?
> 
> 
> So what does this do that the regular dispatch doesn't do?

It guarantees that you don't need to alter existing code if you add a
subclass. The way '**' works currently is that it always calls Pow which
handles dispatch on the second argument as a bunch of special cases and
then dispatches on the first argument. So every time the second argument
needs to influence the dispatch, the code for this must go in Pow.

> Also, how does it work if I have a custom subclass that I want to
> define my own Pow behavior with?

You just define your implementations in the place where you define the
subclass. For instance:

@power.define(MatrixExpr, NegativeOne)
def _inverse_MatrixExpr(mat, n):
    return Inverse(mat)

The big advantage of this is if you want two unrelated subclasses to
interact. Suppose you have matrices and random variables, and then you
want to implement random matrices, so that Matrix**RandomVariable
returns a RandomMatrix. With this, you can just define the
implementation at the same time as RandomMatrix. With the existing
systems, you'd have to modify Matrix and/or RandomVariable.

> Is there any issue with the fact that __pow__ will still be using the
> normal dispatch?  I guess we could make it never return
> NotImplemented.

There are several possibilities here, and I don't know which is best. It
depends how we want to handle the interaction with non-Expr and
non-Basic classes. Do we handle sympification inside or outside of
power? Do we let other classes' __rpow__ run or not?

-- 
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.

Reply via email to