Well, the thing that bugs me the most is that you can do the same things with 
what would appear to be rational functions:

In [7]: Poly(1/x)
Out[7]: Poly(1/x, 1/x, domain='ZZ')

In [8]: Poly(1/x)*x
Out[8]: Poly(x*1/x, 1/x, domain='ZZ[x]')

In [9]: Poly(1/x)*x - 1
Out[9]: Poly(x*1/x - 1, 1/x, domain='ZZ[x]')

In [10]: (Poly(1/x)*x - 1).is_zero
Out[10]: False

This was the original problem in issue 2032.

On Jun 16, 2011, at 2:51 PM, Mateusz Paprocki wrote:

> Hi,
> 
> On 16 June 2011 13:28, Aaron Meurer <asmeu...@gmail.com> wrote:
> This is related to
> http://code.google.com/p/sympy/issues/detail?id=2032.  The polys
> pretend that they can work in K[x, 1/x], but they actually do not
> implement things properly, which can lead to wrong results:
> 
> In [3]: Poly(exp(-x))
> Out[3]: Poly(exp(-x), exp(-x), domain='ZZ')
> 
> In [4]: Poly(exp(-x))*exp(x)
> Out[4]: Poly(exp(x)*exp(-x), exp(-x), domain='ZZ[exp(x)]')
> 
> In [5]: Poly(exp(-x))*exp(x) - 1
> Out[5]: Poly(exp(x)*exp(-x) - 1, exp(-x), domain='ZZ[exp(x)]')
> 
> In [6]: (Poly(exp(-x))*exp(x) - 1).is_zero
> Out[6]: False
> 
> And I could go further, as an incorrect is_zero result can easily be
> exacerbated to larger wrong results, but you get the idea.
> 
> Result [6] is actually a valid result, because generators of polynomial 
> expressions are assumed to be algebraically independent, so in [6] you can 
> write (Poly(u)*v - 1).is_zero (which of course is False) as well. If you want 
> [6] to give True, then you would have to imply an algebraic relation between 
> exp(-x) and exp(x) (really u and v). A reasonable solution I see to this 
> problem is to implement LaurentPoly which would allow negative exponents, 
> which in turn would mean that exp(-x) and exp(x) would result in the same 
> generator, with exponent -1 and 1 respectively.
> Another solution would be to add postprocessor to Poly, which would apply 
> known algebraic relations of generators, after performing the main 
> computation.

This is a great idea.  You see, there is actually a complete algorithm to find 
algebraic relations on elementary functions, called the Risch structure 
theorems.  

The transcendental Risch algorithm requires that each elementary extension be 
transcendental over the others, or else certain results (namely nonelementary 
results) will not be correct. The algorithm not only determines if a function 
is transcendental over a differential extension, but provides the algebraic 
relation when it is not. 

So it was necessary to implement this algorithm.  So far, only the algorithm 
for exponentials and logarithms has been implemented, but there is also an 
algorithm for tangents, arctangents, and nonelementary extensions (functions 
defined in terms of a nonelementary integral of a transcendental function, like 
erf()).  You can handle cosine and sine by first converting them to tangents.

If you want to play around with this, the easiest way is to import 
DifferentialExtension from sympy.integrals.risch in my integration3 branch and 
create a differential extension out of the expression. For example, you can see 
it handles the above example with ease

In [1]: from sympy.integrals.risch import DifferentialExtension

In [2]: DifferentialExtension(exp(x) + exp(-x), x)
Out[2]: (Poly(_t0**2 + 1, _t0, domain='ZZ'), Poly(_t0, _t0, domain='ZZ'), 
[Poly(1, x, domain='ZZ'), Poly(_t0, _t0, domain='ZZ')], [x, _t0], [Lambda(_i, 
exp(_i))], [], [1], [x], [], [])

The output consists of a lot of stuff, but the main thing is that the first two 
Polys are the numerator and denominator of the resulting expression, and the 
list of Lambdas near the end gives the function definition of t0, t1, t2, etc.  
So in this case, we see that it has rewritten exp(x) + exp(-x) as (exp(x)**2 + 
1)/(exp(x)).  Note that I have implemented heuristics to take the exponentials 
in such a way to avoid algebraic relations, like

In [3]: DifferentialExtension(exp(x) + exp(x/2) + exp(2*x), x)
Out[3]: (Poly(_t0**4 + _t0**2 + _t0, _t0, domain='ZZ'), Poly(1, _t0, 
domain='ZZ'), [Poly(1, x, domain='ZZ'), Poly(1/2*_t0, _t0, domain='QQ')], [x, 
_t0], [Lambda(_i, exp(_i/2))], [], [1], [x/2], [], [])

Notice that t0 is exp(x/2).  If we had made t0 exp(x), then exp(x/2) would have 
been sqrt(t0), which is no good for the Risch algorithm (and also in the 
polys), because we can't handle algebraic relations yet.  Note that the 
algorithms here are very powerful, and can handle much more nontrivial cases 
than any heuristic would

In [4]: DifferentialExtension(log(exp(x) + 1) + log(exp(-x) + 1), x)
Out[4]: 
(Poly(2*_t1 - x, _t1, domain='ZZ[x]'), Poly(1, _t1, domain='ZZ'), [Poly(1, x, 
domain='ZZ'), Poly(_t0, _t0, domain='ZZ'), Poly(_t0/(1 + _t0), _t1, 
domain='ZZ(_t0)')], [x, _t0, _t1], [Lambda(_i, exp(_i)
), Lambda(_i, log(1 + _t0))], [], [1], [x], [2], [1 + _t0])

(rewrote log(exp(x) + 1) + log(exp(-x) + 1) as 2*log(exp(x) + 1) - x.  The 
nature of the algorithms is to always rewrite the expression in terms of some 
part of the rest.  Sometimes, it is necessary to rewrite things in a more 
canonical form before integrating, for example

In [5]: DifferentialExtension(2**x, x)
Out[5]: 
(Poly(_t0, _t0, domain='ZZ'), Poly(1, _t0, domain='ZZ'), [Poly(1, x, 
domain='ZZ'), Poly(log(2)*_t0, _t0, domain='EX')], [x, _t0], [Lambda(_i, 
exp(_i*log(2)))], [(exp(x*log(2)), 2**x)], [1], [x*log(2)]
, [], [])

2**x is rewritten as exp(x*log(2)).  But the object contains an attribute 
backsubs which puts it back to the way it was after integration

In [6]: DifferentialExtension(2**x, x).backsubs
Out[6]: 
⎡⎛ x⋅log(2)   x⎞⎤
⎣⎝ℯ        , 2 ⎠⎦

Finally, I want to note that the algorithm for sin, cos, and tan is very 
nontrivial.  It's described in Bronstein's paper, "Simplification of Real 
Elementary Functions," and involves rewriting all sin's and cos's as tangents 
(using the half angle formulas), then the dependence on the tangents is given 
by Flory's theorem (rewriting tangents in terms of symmetric polynomials).

Right now, this code is all written with integration in mind, but it would not 
be too difficult to adapt it to other uses, like finding algebraic dependancies 
in Poly).

By the way, this all applies only to the univariate case.  I don't know if any 
of it can be extended to the multivariate case.

Aaron Meurer


>  
> 
> Aaron Meurer
> 
> On Thu, Jun 16, 2011 at 12:15 PM, Mateusz Paprocki <matt...@gmail.com> wrote:
> > Hi,
> >
> > On 16 June 2011 20:09, smichr <smi...@gmail.com> wrote:
> >>
> >> The exp(x)*exp(-x) term in the Poly should cancel, shouldn't it?
> >>    >>> Poly(exp(x) + exp(-x) - y)*exp(x)
> >>    Poly(-y*exp(x) + exp(-x)*exp(x) + exp(x)**2, y, exp(-x), exp(x),
> >>    domain='ZZ')
> >
> > Polynomials can't contain negative exponents, so exp(x) and exp(-x) =
> > 1/exp(x) are treated as two different generators.
> >
> >>
> >> --
> >> 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.
> >>
> >
> > Mateusz
> >
> > --
> > 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.
> 
> 
> Mateusz
> 
> -- 
> 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.

Reply via email to