New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace exp(x) with E**x internally #4898

Open
asmeurer opened this Issue Jan 26, 2010 · 26 comments

Comments

Projects
None yet
9 participants
@asmeurer
Copy link
Member

asmeurer commented Jan 26, 2010

Me:
"I think things might be easier if exp(x) just returned E**x, the same way that sqrt(x) is just a shortcut to
x**(1/2) [see issue 3489 ].  It would probably require a bit of work to change though."

Chris:
"I would *love* to see this change [from exp(x) to E**x]...there's a lot of code that is written as a work 
around for this dichotomy (exp(x) being a function instead of a power). Not sure I
can tackle it right now, though."

Vinzent:
"We need also stuff like .rewrite(exp), but this should not be a problem."

(see issue 4865 )

So basically, we should change exp(x) to return E**x (the Pow), which would make checking for a power 
much easier.  Checking for an exponential is as easy as checking expr.is_Pow and expr.base == E.  

A note when fixing this.  It isn't enough to just change the code and fix the tests.  If code has

if expr.is_Pow or type(expr) == exp:

or one of the dozen equivalents, it will still work, but will be completely unnecessary and thus confusing to 
keep the type(expr) == exp part.  So it will require some care (and also a bit of work, though nothing too 
difficult, I think).

Original issue for #4898: http://code.google.com/p/sympy/issues/detail?id=1799
Original author: https://code.google.com/u/asmeurer@gmail.com/
Referenced issues: #4865, #6933, #3489

@vks

This comment has been minimized.

Copy link
Contributor

vks commented Jan 25, 2010

And we should probably add a special case for the differentiation of Pow(E, *) for speed.

Original comment: http://code.google.com/p/sympy/issues/detail?id=1799#c1
Original author: https://code.google.com/u/Vinzent.Steinberg@gmail.com/

@rlamy

This comment has been minimized.

Copy link
Member

rlamy commented Jan 25, 2010

+exp(I*pi).

I consider that raising to the power of anything except a rational is an abuse of
notation. In contrast, exp is well-defined on any Banach algebra. So, converting from
the latter to the former means introducing a dodgy or possibly even incorrect
expression. I certainly don't want to see exp(A) converted to E**A when A is a matrix.

Original comment: http://code.google.com/p/sympy/issues/detail?id=1799#c2
Original author: https://code.google.com/u/101272611947379421629/

@vks

This comment has been minimized.

Copy link
Contributor

vks commented Jan 25, 2010

So maybe the function exp should still exist, but return E**x for any valid case? Why
do you think that E**x where x is complex is an abuse of notation?

Should exp(x) != E**x by default (where x is a symbol)?

Original comment: http://code.google.com/p/sympy/issues/detail?id=1799#c3
Original author: https://code.google.com/u/Vinzent.Steinberg@gmail.com/

@vks

This comment has been minimized.

Copy link
Contributor

vks commented Jan 25, 2010

And as a user you don't see whether exp(Matrix) is internally represented using Pow.

Original comment: http://code.google.com/p/sympy/issues/detail?id=1799#c4
Original author: https://code.google.com/u/Vinzent.Steinberg@gmail.com/

@asmeurer

This comment has been minimized.

Copy link
Member

asmeurer commented Jan 25, 2010

I would rather see a bunch of special case code for E in Pow than having all the special case expr.is_Pow or expr.is_Func and 
expr.func == exp scattered throughout sympy.  

Of course, we should still keep the exp function, but I think it should be no more than what sqrt is, just a shortcut.  

And I would consider it to be an extension, not an abuse of the notation, at least for real powers, because it is well defined in 
terms of sequences to say x**y where x and y are arbitrary real number.  i.e., for any sequence of rational numbers (rn) that 
converges to y, (x**rn) will converge to a unique value, which we can then call x**y.  It's no more an abuse than x**(a/b) itself, for 
integer a and b, which only makes sense because (x**a)**(1/b) == (x**(1/b)**a.

For complex, I think the use of Euler's formula can maybe be considered arbitrary (I haven't take complex analysis yet, though), 
but it is very universal and the only thing used in SymPy if I am correct.

Original comment: http://code.google.com/p/sympy/issues/detail?id=1799#c5
Original author: https://code.google.com/u/asmeurer@gmail.com/

@rlamy

This comment has been minimized.

Copy link
Member

rlamy commented Jan 25, 2010

Actually, rational powers are as problematic as irrational or complex ones.
(-1**2)**½ != (-1**½)**2

a**b behaves nicely in only 2 cases:
- if b is an integer
- if a and b are positive reals

Original comment: http://code.google.com/p/sympy/issues/detail?id=1799#c6
Original author: https://code.google.com/u/101272611947379421629/

@asmeurer

This comment has been minimized.

Copy link
Member

asmeurer commented Jan 25, 2010

Right.  I was talking about the real case only (I have only taken real analysis, so the complex stuff can throw me off sometimes).  

As for matrices, I am not sure how it will affect them, even if a**M only makes sense when a == E, because this is only an internal change.  To me, 
making the matrix code check that the base is E sounds better than making all the other code check for both Pow and exp.  Everything will still print and 
run the same, except for exp(x).is_Pow, which means not necessarily that it is a power in the mathematical sense, but that it is an instance of the class 
Pow, which has the data descriptors .base and .exp, and so on.

**Summary:** Replace exp(x) with E**x internally  

Original comment: http://code.google.com/p/sympy/issues/detail?id=1799#c7
Original author: https://code.google.com/u/asmeurer@gmail.com/

@fredrik-johansson

This comment has been minimized.

Copy link
Contributor

fredrik-johansson commented Jan 25, 2010

z**A makes sense for any complex number z and for any element of a Banach algebra A,
using the definition z**A = exp(log(z)*A) and the usual principal logarithm for
complex numbers (certainly log(E) = 1). So I don't see any conceptual problem with
this; allowing exp(A) to be represented by E**A is just acknowledging that 1*A = A.

It is more problematic to define A^z, where you have to define log(A), but that's a
different matter.

In any case, this is only about the internal representation. As long as Pow handles
noncommutative arguments correctly, there shouldn't be a problem.

evalf also needs to be special-cased, by the way; I don't remember if it already does
this, but I think it does.

Original comment: http://code.google.com/p/sympy/issues/detail?id=1799#c8
Original author: https://code.google.com/u/111502149103757882156/

@asmeurer

This comment has been minimized.

Copy link
Member

asmeurer commented Aug 4, 2010

We will need to decide either way on issue 4898 before doing this.

Referenced issues: #4898
Original comment: http://code.google.com/p/sympy/issues/detail?id=1799#c9
Original author: https://code.google.com/u/asmeurer@gmail.com/

@asmeurer

This comment has been minimized.

Copy link
Member

asmeurer commented Aug 25, 2010

That should have been issue 5107 .

Referenced issues: #5107
Original comment: http://code.google.com/p/sympy/issues/detail?id=1799#c10
Original author: https://code.google.com/u/asmeurer@gmail.com/

@asmeurer

This comment has been minimized.

Copy link
Member

asmeurer commented Aug 25, 2010

**Blockedon:** 5107  

Referenced issues: #5107
Original comment: http://code.google.com/p/sympy/issues/detail?id=1799#c11
Original author: https://code.google.com/u/asmeurer@gmail.com/

@smichr

This comment has been minimized.

Copy link
Member

smichr commented Sep 2, 2010

I think that perhaps the way to do this is leave exp alone. It has several methods that would become messy to handle--just my hunch. How about this, though:

1) give expr a property ".is_pow" which is False
2) override in Pow and exp with being True
3) give exp the properties .base and .exp

Now when you don't care if you get a Pow or exp, ask if eq.is_pow and if it is, proceed as usual with the base and exp.

Original comment: http://code.google.com/p/sympy/issues/detail?id=1799#c12
Original author: https://code.google.com/u/117933771799683895267/

@asmeurer

This comment has been minimized.

Copy link
Member

asmeurer commented Sep 2, 2010

Somehow, it seems that it would be less messy to special case E all over Pow than to special case Pow vs. exp() all over the entire codebase.  is_pow would make sense if we had several special classes that were really Pows, but we only have one, which is exp.

By the way, I already fixed #3 in my integration3 branch (commit a559d2a8bed7).  It's completely standalone from everything else if you want to review it and cherry-pick it onto master.

Original comment: http://code.google.com/p/sympy/issues/detail?id=1799#c13
Original author: https://code.google.com/u/asmeurer@gmail.com/

@asmeurer

This comment has been minimized.

Copy link
Member

asmeurer commented Jun 3, 2011

That was pushed into master (see 2402).

Original comment: http://code.google.com/p/sympy/issues/detail?id=1799#c14
Original author: https://code.google.com/u/asmeurer@gmail.com/

@asmeurer

This comment has been minimized.

Copy link
Member

asmeurer commented Mar 20, 2012

@asmeurer

This comment has been minimized.

Copy link
Member

asmeurer commented May 19, 2013

**Blockedon:** -sympy:2008 sympy:2008  
**Blocking:** sympy:3834  

Original comment: http://code.google.com/p/sympy/issues/detail?id=1799#c16
Original author: https://code.google.com/u/asmeurer@gmail.com/

@jrioux

This comment has been minimized.

Copy link
Member

jrioux commented May 19, 2013

I think something like comment #12 is the cleanest way.

Original comment: http://code.google.com/p/sympy/issues/detail?id=1799#c17
Original author: https://code.google.com/u/102137482174297837682/

@asmeurer

This comment has been minimized.

Copy link
Member

asmeurer commented May 19, 2013

I stand by my rebuttal in comment #13.

Original comment: http://code.google.com/p/sympy/issues/detail?id=1799#c18
Original author: https://code.google.com/u/asmeurer@gmail.com/

@asmeurer

This comment has been minimized.

Copy link
Member

asmeurer commented Jun 14, 2013

**Blocking:** 6988  

Referenced issues: #6988
Original comment: http://code.google.com/p/sympy/issues/detail?id=1799#c19
Original author: https://code.google.com/u/asmeurer@gmail.com/

@kaushik94

This comment has been minimized.

Copy link
Contributor

kaushik94 commented May 29, 2014

I think it is not possible to return Ex without making E a standard symbol. For example
pi
2 gives pi2 whereas E2 gives 7.3890.... and E**1 gives E. This is clearly an inconsistency and I vote for special casing E all over the code base. I guess I can take up a PR for that if everyone is up for it

@skirpichev

This comment has been minimized.

Copy link
Contributor

skirpichev commented May 29, 2014

I think it is not possible to return E**x without making E a standard symbol.

?

pi2 gives pi2 whereas E**2 gives 7.3890....

Sorry, we don't have your problems...

In [1]: pi**2
Out[1]: 
 2
π 

In [2]: E**2
Out[2]: 
 2
ℯ 

This is clearly an inconsistency

There is clearly some typo or misunderstanding. Maybe you have definition for E to be a float (or a Float). Or you are using 2.0 instead of 2 (Integer):

In [1]: pi**2.0
Out[1]: 
 2.0
π   

In [2]: E**2.0
Out[2]: 7.38905609893065

The difference is: S.Pi doesn't have _eval_power method.

@kaushik94

This comment has been minimized.

Copy link
Contributor

kaushik94 commented May 29, 2014

I don't get it. Am I doing something wrong ?

In [3]: from sympy import *

In [4]: type(2)
Out[4]: int

In [5]: pi**2
Out[5]: pi**2

In [6]: E**2
Out[6]: mpf('7.3890560989306495')
@skirpichev

This comment has been minimized.

Copy link
Contributor

skirpichev commented May 29, 2014

Am I doing something wrong ?

Sure. But I can't guess.

@asmeurer

This comment has been minimized.

Copy link
Member

asmeurer commented May 29, 2014

What is type(E)?

@kaushik94

This comment has been minimized.

Copy link
Contributor

kaushik94 commented May 29, 2014

In [1]: from sympy import *

In [2]: type(E)
Out[2]: sympy.core.numbers.Exp1

In [3]: type(2)
Out[3]: int

In [4]: pi**2
Out[4]: pi**2

In [5]: E**2
Out[5]: mpf('7.3890560989306495')

In [6]: E**1
Out[6]: E
@vks

This comment has been minimized.

Copy link
Contributor

vks commented May 30, 2014

I cannot reproduce it either (as of commit 4a91baa):

In [1]: from sympy import *

In [2]: type(E)
Out[2]: sympy.core.numbers.Exp1

In [3]: pi**2
Out[3]: pi**2

In [4]: E**2
Out[4]: exp(2)

In [5]: E**1
Out[5]: E

skirpichev added a commit to diofant/diofant that referenced this issue Sep 22, 2015

skirpichev added a commit to diofant/diofant that referenced this issue Sep 23, 2015

XXX Consolidate exp and Pow (replace exp(x) with E**x)
Fixes sympy/sympy#4898

fix expr.func == exp in the ode.py

ode: fix has(exp)

Adopt gruntz and fix first two gruntz limit

Fix Pow._eval_as_leading_term for base=Exp1

more fixes

more fixes (foo.args[0] -> foo.exp)

fix as_real_imag, more fixes

manualintegrate.py fixes, XXX: more?

Fix is_rational/is_algebraic/is_complex helpers XXX: simplify

fix aseries

fix special case for exp in sympy/integrals/transforms.py

fix heurisch

fix numberfields.py

fix refine

fix _minpoly_exp

Adapt old (for exp) methods _eval_rewrite_as_sin/cos/tanh

fix atoms(exp) usage

fix _invert

fix some assumptions helpers for Pow

XXX fix new assumptions

fix simplify with powers of E

skirpichev added a commit to diofant/diofant that referenced this issue Sep 30, 2015

XXX Consolidate exp and Pow (replace exp(x) with E**x)
Fixes sympy/sympy#4898

fix expr.func == exp in the ode.py

ode: fix has(exp)

Adopt gruntz and fix first two gruntz limit

Fix Pow._eval_as_leading_term for base=Exp1

more fixes

more fixes (foo.args[0] -> foo.exp)

fix as_real_imag, more fixes

manualintegrate.py fixes, XXX: more?

Fix is_rational/is_algebraic/is_complex helpers XXX: simplify

fix aseries

fix special case for exp in sympy/integrals/transforms.py

fix heurisch

fix numberfields.py

fix refine

fix _minpoly_exp

Adapt old (for exp) methods _eval_rewrite_as_sin/cos/tanh

fix atoms(exp) usage

fix _invert

fix some assumptions helpers for Pow

XXX fix new assumptions

fix simplify with powers of E

fix inverse_laplace_transform

fix test_separatevars

restore separate branch for exp in meijerint_inversion

XXX fix manualintegrate

skirpichev added a commit to diofant/diofant that referenced this issue Oct 1, 2015

XXX Consolidate exp and Pow (replace exp(x) with E**x)
Fixes sympy/sympy#4898

fix expr.func == exp in the ode.py

ode: fix has(exp)

Adopt gruntz and fix first two gruntz limit

Fix Pow._eval_as_leading_term for base=Exp1

more fixes

more fixes (foo.args[0] -> foo.exp)

fix as_real_imag, more fixes

manualintegrate.py fixes, XXX: more?

Fix is_rational/is_algebraic/is_complex helpers XXX: simplify

fix aseries

fix special case for exp in sympy/integrals/transforms.py

fix heurisch

fix numberfields.py

fix refine

fix _minpoly_exp

Adapt old (for exp) methods _eval_rewrite_as_sin/cos/tanh

fix atoms(exp) usage

fix _invert

fix some assumptions helpers for Pow

XXX fix new assumptions

fix simplify with powers of E

fix inverse_laplace_transform

fix test_separatevars

restore separate branch for exp in meijerint_inversion

XXX fix manualintegrate

skirpichev added a commit to diofant/diofant that referenced this issue Oct 9, 2015

XXX Consolidate exp and Pow (replace exp(x) with E**x)
Fixes sympy/sympy#4898

fix expr.func == exp in the ode.py

ode: fix has(exp)

Adopt gruntz and fix first two gruntz limit

Fix Pow._eval_as_leading_term for base=Exp1

more fixes

more fixes (foo.args[0] -> foo.exp)

fix as_real_imag, more fixes

manualintegrate.py fixes, XXX: more?

Fix is_rational/is_algebraic/is_complex helpers XXX: simplify

fix aseries

fix special case for exp in sympy/integrals/transforms.py

fix heurisch

fix numberfields.py

fix refine

fix _minpoly_exp

Adapt old (for exp) methods _eval_rewrite_as_sin/cos/tanh

fix atoms(exp) usage

fix _invert

fix some assumptions helpers for Pow

XXX fix new assumptions

fix simplify with powers of E

fix inverse_laplace_transform

fix test_separatevars

restore separate branch for exp in meijerint_inversion

XXX fix manualintegrate

XXX: fix trigsimp (using atoms(exp))

fix test_risch.py?

fix inverse_laplace_transform(exp(-a*s)/s, s, t)

XXX fix test_fourier_transform

fix test_branch_bug

Fix tests

fix test_count_ops.py

fix printing tests

fix test_as_integral

fix test_issue_7081

fix test_separable2

fix test in test_lie_group

fix RootSum(foo, exp) tests

fix RootSum call

fix test_functions_subs

fix test_issue_3538: isinstance check exp -> Pow

Fix gosper_sum for issue 6033

fix test_sin_exp_rewrite: rewrite(exp, foo) -> rewrite(Pow, foo)

Adapt expression for test_exp2

test_exp_log: removed inverse test for exp

Removed tests for subs with exp

fix docstrings in the sphinx docs

doctest fixes

fix doctest

fix more doctests

fix more doctests

fix doctest

adapt aseries doctest

fix doctest

skirpichev added a commit to diofant/diofant that referenced this issue Oct 9, 2015

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment