Le samedi 09 juillet 2011 à 10:29 -0700, Brian Granger a écrit : > > As many of you may know, the main thing blocking the merge of my work > > on the Risch algorithm (see my integration3 branch) is not any > > deficiency in the algorithm, though there are several parts that are > > still not implemented, but the lack of a so called "atomic > > substitution" framework. The relevant issue here is 2026 > > (http://code.google.com/p/sympy/issues/detail?id=2026). > > > > Basically, the following breaks the preprocessing code in risch_integrate: > > > > In [1]: exp(2*x).subs(exp(x), y) > > Out[1]: > > 2 > > y > > > > I need a way for subs to behave exactly, so the above would return > > exp(2*x). Thus, I have disabled this completely in my integration3 > > branch, but this is only a temporary solution, as there is a lot of > > code that relies on this behavior (especially in the series/limits > > code), and it would be a regression anyway. > > I have always thought that subs should not know about any mathematical > relationships, but should behave as you are proposing (atomic or > exact=True). In my mind, subs is a foundation that can be used to > build more advanced pattern matching and rule capabilities. But those > more advanced rules (such as done by power) should be in subs itself, > but in that higher level. Thus.
I agree. We need some way of performing direct replacements, based only on the structure of the expression. We also need a way to transform expressions while preserving their mathematical meaning. Currently, subs() tries to do both, which causes most of the problems with it. > > I am +1 on the exact or atomic keyword to subs (I prefer exact). > > I am ++1 on having that be the default behavior -1 on adding keyword arguments. These are different operations that require different interfaces and implementations. Lumping together distinct functions under the same name via keyword switches always creates a mess. > > Cheers, > > Brian > > > So there needs to be a way to do > > > >>>> exp(2*x).subs(exp(x), y, atomic=True) > > exp(2*x) > > > > Now, as it turns out, it has come up in other places that people want > > control over the way that subs works in other ways. In the issue, I > > talk about something called integer_powers, which would work like > > > >>>> exp(2*x).subs(exp(x), y, integer_powers=True) > > y**2 > >>>> exp(x).subs(exp(2*x), y, integer_powers=True) > > exp(x) > > > > In other words, it does not do power manipulation in the replacement > > unless the resulting power is an integer. This is needed in some > > places such as the heurisch algorithm to ensure that the resulting > > expression will be a polynomial (actually, a rational function) in the > > substitution variable. In addition, there is also some concern about > > the assumptions validity of certain algebraic substitution rules. See > > issues 2081 and 2552. > > > > So in the interest of doing this right, I think there needs to be some > > kind of hints mechanism to subs. My question is, what do you think > > would be the best way to implement this? Presently the expand > > function has something like this, but I'm not really convinced that > > the way that it's implemented is a very good one. > > > > Here's (roughly) the way that subs works now: Basic defines two > > methods, .subs and ._eval_subs. Basic.subs() is of course the user > > level function that everyone calls, and pretty much no subclass of > > Basic overrides it. The actual substitution happens in ._eval_subs, > > which is also responsible for recursing the substitution down the > > .args. Basic has a simple implementation, but most classes end up > > overriding it (for example, exp has overridden it to allow the above > > fancy algebraic substitution). > > > > What's the best way to implement the various hints I want to add to > > .subs()? A few things to take into consideration: > > > > - .expand() works, as I mentioned earlier, by having > > ._eval_expand_hint() methods. I don't think this is the best way, so > > that's why I'm asking here to see if anyone has any better ideas. > > > > - It should remain backwards compatible with any class that defines > > ._eval_subs(self, old, new). Unfortunately, there wasn't much > > foresight when this was originally designed, so the protocol does not > > call for any *args or **kwargs. However, that doesn't necessarily > > weigh those options out, as we could easily make Basic.subs() check > > for an old style definition and ignore hints in that case. > > > > - I haven't looked at it, but we might be able to implement at least > > atomic substitution entirely in Basic (no class need override any > > methods to get it to work). This is because it is so simple that the > > default agnostic method might be able to do it entirely. The rule for > > atomic substitution by the way is that expr.subs(old, new, > > atomic=True) should replace old with new in expr if and only if old is > > in expr.atoms(old.__class__). > > > > So I'm open to any ideas on how to implement this, API-wise. > > > > Also, Chris, did you start this at all in any of your branches and/or > > are you willing to help with this? > > > > Aaron Meurer > > > > -- > > 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. > > > > > > > > -- > Brian E. Granger > Cal Poly State University, San Luis Obispo > [email protected] and [email protected] > -- 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.
