sagemath / sage

Main repository of SageMath
https://www.sagemath.org
Other
1.33k stars 453 forks source link

Sage is missing the lambert_w function #11888

Closed benjaminfjones closed 12 years ago

benjaminfjones commented 12 years ago

Maxima returns solutions to some exponential equations in terms of the lambert_w function. Sage is missing a conversion for this function:


sage: solve(e^(5*x)+x==0, x, to_poly_solve=True)
[x == -1/5*lambert_w(5)]
sage: S = solve(e^(5*x)+x==0, x, to_poly_solve=True)
sage: z = S[0].rhs()
sage: z
-1/5*lambert_w(5)
sage: N(z)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)

/Users/jonesbe/sage/sage-4.7.2.alpha2/devel/sage-test/sage/<ipython console> in <module>()

/Users/jonesbe/sage/latest/local/lib/python2.6/site-packages/sage/misc/functional.pyc in numerical_approx(x, prec, digits)
   1264             prec = int((digits+1) * 3.32192) + 1
   1265     try:
-> 1266         return x._numerical_approx(prec)
   1267     except AttributeError:
   1268         from sage.rings.complex_double import is_ComplexDoubleElement

/Users/jonesbe/sage/latest/local/lib/python2.6/site-packages/sage/symbolic/expression.so in sage.symbolic.expression.Expression._numerical_approx (sage/symbolic/expression.cpp:17950)()

TypeError: cannot evaluate symbolic expression numerically
sage: lambert_w(5)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)

/Users/jonesbe/sage/sage-4.7.2.alpha2/devel/sage-test/sage/<ipython console> in <module>()

NameError: name 'lambert_w' is not defined
sage: 

mpmath can evaluate the lambert_w function, so it should be easy to add a new symbolic function to Sage that will fix this issue.


Apply:

CC: @kcrisman @sagetrac-ktkohl

Component: symbolics

Keywords: lambert_w symbolics conversion maxima sd35.5 sd40.5

Author: Benjamin Jones

Reviewer: Keshav Kini, Karl-Dieter Crisman, Fredrik Johansson, Burcin Erocal, Douglas McNeil, William Stein

Merged: sage-5.1.beta4

Issue created by migration from https://trac.sagemath.org/ticket/11888

benjaminfjones commented 12 years ago

Attachment: trac_11888_v6.patch.gz

added custom latex printing and Maxima initialization

benjaminfjones commented 12 years ago
comment:35

I've posted my latest patch in case anyone wants to play around with getting integration of lambert_w to work.

This issue could be a new ticket. One solution I can see is to add lambert_w to the special_sage_to_max dictionary in sage/intefaces/maxima_lib.py. There are a few other special functions listed there (like Ei and polylog) that need special conversions to maxima.

So, I propose that we either: a. Review patch attachment: trac_11888_v6.patch and open a new ticket for the integration issue b. Agree on a simple workaround like adding lambert_w to special_sage_to_max and I'll add it to the patch.

kcrisman commented 12 years ago
comment:36

I think that b. makes sense. You'd also have to add max_lambert_w at about this spot but having numerical integrals would be worth it.

I assume that this is a pretty easy change? If not, I guess we could just document that this doesn't work yet. In either case something should be documented, though, since half of our bug reports seem to be people using new, cool functionality who then expect that new functionality to be fully featured as well - i.e., it's not really a bug report at all, but a feature request. Having good doc for what we don't do will help with that.

kcrisman commented 12 years ago
comment:37

'Needs work' for b.

benjaminfjones commented 12 years ago
comment:38

Success!

Symbolic and numerical integration now work as expected for the principle branch. I added doctests to indicate what is and is not implemented.


One other comment, to indicate what causes errors, I want to add doctests to lambert_w such as:

sage: integral(lambert_w(1,x), x)                                                                   
ERROR: An unexpected error occurred while tokenizing input                                          
...                                                                                                 
RuntimeError: ECL says: Error executing code in Maxima: lambert_w: expected exactly 1 arguments.   

and

sage: numerical_integral(lambert_w(x), 0, 1)                                                        
Exception TypeError: "function not supported for these types, and can't coerce safely to supported types" in 'sage.gsl.integration.c_ff' ignored                                                    
...                                                                                                 
(0.0, 0.0)

but the doctest framework doesn't recognize the Exception TypeError and it seems to automatically fail if a RuntimeError is raised. If I put the latter 4 lines in the docstring for lambert_w, it fails doctesting, the framework only sees the (0.0, 0.0) part at the end. Is there a way around either of these issues?

benjaminfjones commented 12 years ago

adds lambert_w function, including integration

benjaminfjones commented 12 years ago
comment:39

Attachment: trac_11888_v7.patch.gz

benjaminfjones commented 12 years ago

Description changed:

--- 
+++ 
@@ -40,5 +40,4 @@

 Apply:

-* Patch at #12507
-* [attachment: trac_11888_v5.patch](https://github.com/sagemath/sage-prod/files/10653802/trac_11888_v5.patch.gz) to `$SAGE_ROOT/devel/sage`
+* [attachment: trac_11888_v7.patch](https://github.com/sagemath/sage-prod/files/10653804/trac_11888_v7.patch.gz) to `$SAGE_ROOT/devel/sage`
11d1fc49-71a1-44e1-869f-76be013245a0 commented 12 years ago
comment:40

Apply trac_11888_v7.patch

(for patchbot, which is trying to apply all nine patches at once)

benjaminfjones commented 12 years ago

Description changed:

--- 
+++ 
@@ -40,4 +40,4 @@

 Apply:

-* [attachment: trac_11888_v7.patch](https://github.com/sagemath/sage-prod/files/10653804/trac_11888_v7.patch.gz) to `$SAGE_ROOT/devel/sage`
+* [attachment: trac_11888_v7.2.patch](https://github.com/sagemath/sage-prod/files/10653805/trac_11888_v7.2.patch.gz) to `$SAGE_ROOT/devel/sage`
benjaminfjones commented 12 years ago

Changed dependencies from #12507 to none

benjaminfjones commented 12 years ago
comment:42

I removed #12507 from dependencies since it was merged in 5.0.beta5 and now the patchbot is getting confused trying to apply #12507 to 5.0.beta12 before testing.

I verified that attachment: trac_11888_v7.2.patch applies cleanly to 5.0.beta12 and I'm rerunning a patchbot instance on it.

kcrisman commented 12 years ago
comment:43

I'm sure we can finish this off next week in Seattle. Meanwhile, an interesting update from the Maxima developers about coming attractions:


Message: 4
Date: Thu, 17 May 2012 04:31:13 +0000 (UTC)
From: Robert Dodier <robert.dodier@gmail.com>
To: maxima@math.utexas.edu
Subject: Re: [Maxima] Generalized Lambert W function - premature
       commit
Message-ID: <jp1uuh$jv8$1@dough.gmane.org>

On 2012-05-17, David Billinghurst <dbmaxima@gmail.com> wrote:

> Oops. I have accidentally committed some code for Generalized Lambert
> W function to src/specfn.lisp.  Still getting my head around git.

> The code seems functionally correct, and passes tests in
> tests/rtest_lambert_w.mac, but I hadn't finished polishing it and it
> is still undocumented.  Unless anyone objects, I will leave it in
> place for the time being.

No problem, OK by me.

> There is a new function generalized_lambert_w(k,z) that returns the
> kth branch W_k(z).  There are float and bigfloat routines for complex
> z.  generalized_lambert_w(0,z) is not (yet) simplified to
> lambert_w(z), as I hadn't decided if this should be done
> unconditionally or controlled by a flag.  Thoughts?

Is it more convenient to simplify W_0(z) instead of W(z) ? If not, then
it seems reasonable to just go ahead and simplify it.

If you decide against automatically simplifying W_0(z) to W(z), I guess
I hope you don't make it controlled by a flag; flags cause trouble,
because one can't guess by looking at some code how it's going to turn
out. How about a function to carry out the simplification.
benjaminfjones commented 12 years ago
comment:44

That will be good; should allow us to integrate the non-principle branch. Although, in Sage 5.0, we're still a major version behind the current Maxima release.

1d7ec08f-60ae-4512-91a6-8324c06eab9f commented 12 years ago
comment:45

I am reviewing an expository paper about the Lambert W function and it says "Maple this" and "Maple that". Let's get this into Sage and stay competitive with the M's! ;-)

Rob, in cheerleader mode

benjaminfjones commented 12 years ago

removed trailing whitespace

benjaminfjones commented 12 years ago
comment:46

Attachment: trac_11888_v7.2.patch.gz

I agree! In that spirit, here is a rebase of the patch for Sage-5.0.

zimmermann6 commented 12 years ago
comment:47

this ticket is pointed out on the SD40.5 wiki page. Is there any particular thing one should review?

Paul

56048686-9665-4c5e-973e-6c3add3aa805 commented 12 years ago
comment:48

It looks like a few principle/principal mixups made it through.

kcrisman commented 12 years ago
comment:49

this ticket is pointed out on the SD40.5 wiki page. Is there any particular thing one should review?

I think that just checking everything still works and that syntax is proper (and spelling, thanks dsm) is good. Checking some random values against another program would be helpful, especially for the branches. Making sure documentation builds and looks good. But this has been looked at by a lot of eyes, so I don't think it needs to be gone over completely from scratch, especially since I think Ben has doctested a lot of the issues raised in previous comments (which are worth scanning).

benjaminfjones commented 12 years ago

Changed keywords from lambert_w symbolics conversion maxima sd35.5 to lambert_w symbolics conversion maxima sd35.5 sd40.5

benjaminfjones commented 12 years ago
comment:51

I can make a quick spelling fix patch, but I'll wait and see if anyone else has changes to suggest.

56048686-9665-4c5e-973e-6c3add3aa805 commented 12 years ago
comment:52

After some real-world discussions, I'd prefer to avoid falling into Python ints for (some of) the special values:

sage: parent(lambert_w(0))
Integer Ring
sage: parent(lambert_w(e))
<type 'int'>
sage: parent(lambert_w(-1/e))
<type 'int'>
sage: parent(lambert_w(SR(-1/e)))
<type 'int'>
sage: parent(lambert_w(SR(0)))
Integer Ring

Mysteriously enough, instrumenting it reveals that _eval_ is actually returning SR(1) which then in some part of the code I don't understand becomes int(1) before we get it back. If we explicitly return Integer(1) then it seems to stay as Integer(1). This isn't the biggest deal in the world but there have been several bug reports caused by something falling out of Sagespace into Pythonspace.

56048686-9665-4c5e-973e-6c3add3aa805 commented 12 years ago
comment:53

It looks like whatever happens after _eval_ might "dereference" the SR; the call seems to give Integer(1) if _eval_ returns SR(Integer(1)). Probably someone who actually knows what's going on could explain it in one line.

benjaminfjones commented 12 years ago
comment:54

One solution is to change the return statements in these special cases where the automatic simplification returns an integer to just explicitly return Integer(1), etc..

How does that sound?

benjaminfjones commented 12 years ago

Description changed:

--- 
+++ 
@@ -40,4 +40,4 @@

 Apply:

-* [attachment: trac_11888_v7.2.patch](https://github.com/sagemath/sage-prod/files/10653805/trac_11888_v7.2.patch.gz) to `$SAGE_ROOT/devel/sage`
+* [attachment: trac_11888_v8.patch](https://github.com/sagemath/sage-prod/files/10653806/trac_11888_v8.patch.gz) to `$SAGE_ROOT/devel/sage`
benjaminfjones commented 12 years ago
comment:55

New patch is ready for review. I hope this can be the final revision!

Patchbot: apply attachment: trac_11888_v8.patch to $SAGE_ROOT/devel/sage

56048686-9665-4c5e-973e-6c3add3aa805 commented 12 years ago
comment:56

Looking at it now.

benjaminfjones commented 12 years ago

fixed spelling / grammer mistakes, returned parent(Integer(...)) for special values

56048686-9665-4c5e-973e-6c3add3aa805 commented 12 years ago
comment:57

Attachment: trac_11888_v8.patch.gz

Okay, this looks good. Two copyedits, an extra doc describing the behaviours of the derivative function, some tests making sure we can't differentiate with respect to the branch number, and the addition of lambert_W(-pi/2) = pi/2*I as a special value.

I give positive review to the preexisting parts of v8; if the new bits of v9 look okay I think we're good to go.

56048686-9665-4c5e-973e-6c3add3aa805 commented 12 years ago

Attachment: trac_11888_v9.patch.gz

post-review version of lambertw sf

benjaminfjones commented 12 years ago
comment:58

New changes look good; good catch about the derivative w.r.t. branch. All relavent tests pass for me on sage-5.0. I would say this is ready to go in! Thanks for the very thorough review, Doug.

56048686-9665-4c5e-973e-6c3add3aa805 commented 12 years ago

Changed reviewer from Keshav Kini, Karl-Dieter Crisman, Fredrik Johansson, Burcin Erocal to Keshav Kini, Karl-Dieter Crisman, Fredrik Johansson, Burcin Erocal, Douglas McNeil

56048686-9665-4c5e-973e-6c3add3aa805 commented 12 years ago
comment:60

+1. Tx for the work!

williamstein commented 12 years ago

Changed reviewer from Keshav Kini, Karl-Dieter Crisman, Fredrik Johansson, Burcin Erocal, Douglas McNeil to Keshav Kini, Karl-Dieter Crisman, Fredrik Johansson, Burcin Erocal, Douglas McNeil, William Stein

williamstein commented 12 years ago
comment:61

This patch is awesome! It's also a great example of how to make a well-documented new symbolic function that illustrates many issues. Here are a few trivial nitpicks:

When automatic simplication occurs, the parent of the output value should be 
        646         """ 
    647         The derivative of `W_n(x)` is `W_n(x)/(x \cdot W_n(x) + x)`. 

(check for similar instances throughout).

    679             raise ValueError("Derivative not defined with respect to the branch number.") 

Here's a good example of what an exception string should look like (built into python):

>>> 1/0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero
56048686-9665-4c5e-973e-6c3add3aa805 commented 12 years ago
comment:62

Updated version taking into account comments of was.

56048686-9665-4c5e-973e-6c3add3aa805 commented 12 years ago

minor edits

williamstein commented 12 years ago
comment:64

Attachment: trac_11888_v10.patch.gz

jdemeyer commented 12 years ago

Merged: sage-5.1.beta4