On Sat, Jul 9, 2011 at 12:48 PM, Ronan Lamy <ronan.l...@gmail.com> wrote: > 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.
Yes, exactly. I actually need both in my Risch code: a way to do very precise exact substitution of terms I find using .atoms(), and a method that tries to be as mathematically smart as possible to "get a term back in the expression" regardless of its form. The former is needed for the preprocessing step, where I only want to substitute dummy symbols for exactly the expressions I want, and the latter is for after integration, where I often need to do a back substitution to make the integral look like the integrand. For example, to integrate 2**x, it first has to be converted to exp(x*log(2)). But I try to replace it back after integration, so the integral has 2**x like the user expects. If the integrand had 2**x and the integral has exp(2*x*log(x)), it won't work without fancy power substitution that recognizes that as (2**x)**2. I will also need ways to convert trig expressions back and forth to each other once I have that implemented. >> >> 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. Well, how would you do it? I am very open to suggestions. Aaron Meurer >> >> 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 sympy@googlegroups.com. >> > To unsubscribe from this group, send email to >> > sympy+unsubscr...@googlegroups.com. >> > 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 >> bgran...@calpoly.edu and elliso...@gmail.com >> > > > -- > You received this message because you are subscribed to the Google Groups > "sympy" group. > To post to this group, send email to sympy@googlegroups.com. > To unsubscribe from this group, send email to > sympy+unsubscr...@googlegroups.com. > 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 sympy@googlegroups.com. To unsubscribe from this group, send email to sympy+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/sympy?hl=en.