On Thu, Apr 14, 2022 at 5:01 PM Oscar Benjamin
<[email protected]> wrote:
>
> On Thu, 14 Apr 2022 at 23:22, Aaron Meurer <[email protected]> wrote:
> >
> > On Thu, Apr 14, 2022 at 3:12 PM Oscar Benjamin
> > <[email protected]> wrote:
> > >
> > > On Thu, 14 Apr 2022 at 20:30, Aaron Meurer <[email protected]> wrote:
> > > >
> > > > On Thu, Apr 14, 2022 at 12:26 PM Oscar Benjamin
> > > > <[email protected]> wrote:
> > > > >
> > > > > As an example it would be trivial in SymPy to make substitutions
> > > > > involving large expressions and many replacements much faster with a
> > > > > small redesign. The problem though is backwards compatibility: each
> > > > > expression class can implement its own _eval_subs method so for
> > > > > backwards compatibility the subs implementation must recurse in the
> > > > > same slow way once throughout the whole tree for each replacement that
> > > > > is to be made. (There are ways to work around this but the design
> > > > > makes it harder than it should be.)
> > > >
> > > > Just to be clear, isn't this already fixed with xreplace, which does
> > > > only replacement with no smart substitution and no dispatching to
> > > > _eval methods? That isn't to say xreplace itself couldn't be more
> > > > performant, which I'm sure it could.
> > >
> > > Even xreplace runs the evaluation code which is baked into __new__.
> >
> > Right, so the "redesign" isn't so much about the substitution but
> > rather in removing automatic evaluation.
>
> Removing automatic evaluation can mean different things. Automatic
> evaluation is nice from a user perspective for most tasks (but not
> all). Automatic evaluation is a nightmare from an algorithmic
> perspective if you don't have any good way to control it. Right now it
> isn't possible to distinguish between an internal operation for which
> evaluation should be postponed and a user-facing operation for which
> it might be desired: everything goes through the same __new__ so even
> xreplace can't get around it. The class-based design makes it possible
> to design classes that are completely broken if you don't let them
> evaluate and also means that the choice to evaluate lies with each
> class.
>
> In any case what I'm talking about is still not just about evaluation.
> It's about representing expressions with data structures rather than
> polymorphic classes. It should be possible to define something like a
> tan function and then use different kinds of data structures to
> represent expressions involving that function. The design should not
> be tied to any particular data structure and that means that tan can
> not be a class that chooses its own internal representation for any
> expression involving the tan function. Of course the lower-level data
> structures wouldn't have automatic evaluation though (you wouldn't
> expect a dict or a list to "evaluate").

I think you're just restating my restricted computation domain point
in a different way. To take the polys, they are just a special data
structure for representing a polynomial, with wrapper classes for
convenience at the highest level, but it's also purely functional at
the lowest level.

This level based design where the lowest level where the fast
algorithms are implemented operates directly on the data structure and
the top level has a wrapper class around it works well. The existence
of the lower level removes the temptation to do too much magic in the
class (the polys do have some stuff for convenience, like automatic
domain promotion).

>
> > > Also it recurses down through all of the args in the tree which can in
> > > many cases be done much more efficiently if the expression is
> > > represented as a DAG rather than a tree.
> >
> > Not sure that's so hard to do with the current design. I think it
> > would be possible to add some optimizations to effectively
> > de-duplicate identical subexpressions.
>
> I want a design where these things mostly just happen automatically.
> The current design seems to maximise the amount of procedural code
> that needs to be written which simultaneously maximises bugs and also
> the amount of work that is needed to optimise anything.
>
> > It's already basically possible
> > to do this, you just have to assume that == is not a slow operation
> > (which it mostly isn't, although it could be faster).
>
> It's slower than it should be. A different design can easily make it
> faster but not if you have classes that are allowed to define their
> own __eq__ methods.

I agree that almost nothing should be defining its own __eq__ method.
That's a pretty bad antipattern which we mostly avoid in library code,
but I expect there's a good bit of user code that does it because it
doesn't adhere to the same standards.

Aaron Meurer

>
> --
> Oscar
>
> --
> You received this message because you are subscribed to the Google Groups 
> "sympy" group.
> To unsubscribe from this group and stop receiving emails from it, send an 
> email to [email protected].
> To view this discussion on the web visit 
> https://groups.google.com/d/msgid/sympy/CAHVvXxTz1YW0JQ7_ySkHNUsQQws1oVYxwZtLqhYznWzEQ0sgag%40mail.gmail.com.

-- 
You received this message because you are subscribed to the Google Groups 
"sympy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/sympy/CAKgW%3D6%2BnKPpO0ddDym50u6gSPV1Cjgz_brgdH7%2BJbKFSPYcuMw%40mail.gmail.com.

Reply via email to