Repository: sympy/sympy
Pull request: lambdify loggamma now works for mpmath
<!-- Your title above should be a short description of what
was changed. Do not include the issue number in the title. -->

#### References to other Issues or PRs
<!-- If this pull request fixes an issue, write "Fixes #NNNN" in that exact
format, e.g. "Fixes #1234" (see
https://tinyurl.com/auto-closing for more information). Also, please
write a comment on that issue linking back to this pull request once it is
open. -->
Fixed #17411

#### Brief description of what is fixed or changed
Lambdifying an expression with loggamma doesn't work for mpmath previously but I made some fixes so that it works. File changed are pycode and test_pycode.

#### Other comments
Nan

#### Release Notes

<!-- Write the release notes for this release below. See
https://github.com/sympy/sympy/wiki/Writing-Release-Notes for more information
on how to write release notes. The bot will check your release notes
automatically to see if they are formatted correctly. -->

<!-- BEGIN RELEASE NOTES -->
* printing
    * The mpmath code printer now correctly prints the `loggamma` function.
* utilities
    * Lambdifying an expression with `loggamma` using mpmath no longer raises `ImportError`.
<!-- END RELEASE NOTES -->

Problem statement:
lambdify loggamma doesn't work for mpmath
Lambdifying an expression with loggamma doesn't work for mpmath
```julia
In [6]: lambdify(x, loggamma(x), 'mpmath')                                                                                        
---------------------------------------------------------------------------
ImportError                               Traceback (most recent call last)
<ipython-input-6-02428e0a17ae> in <module>
----> 1 lambdify(x, loggamma(x), 'mpmath')

~/current/sympy/sympy/sympy/utilities/lambdify.py in lambdify(args, expr, modules, printer, use_imps, dummify)
    766                 imp_mod_lines.append("from %s import %s" % (mod, k))
    767     for ln in imp_mod_lines:
--> 768         exec_(ln, {}, namespace)
    769 
    770     # Provide lambda expression with builtins, and compatible implementation of range

<string> in <module>

ImportError: cannot import name 'lgamma'
```
I think this is because it's called loggamma rather than lgamma:
```julia
In [7]: import mpmath                                                                                                             

In [8]: mpmath.loggamma(1)                                                                                                        
Out[8]: mpf('0.0')
```

I originally got this error from
```julia
In [11]: nsolve(im(loggamma(I*x)), x, 3.439*I)                                                                                    
---------------------------------------------------------------------------
ImportError
...
```

Hints from issue discussion:
`lgamma` is the name in `math`. There seems to be some code to harvest all mpmath names and add them, but it doesn't seem to work (or some other magic happens, see "Other comments" in #17394, the printer does one thing, but lambdify another...).
Is it possible to write a test that automatically tries to import everything in these `_known_functions_*` lists?
Whatever, just adding `loggamma` to `knwon_functions_mpmath` can swiftly fix the problem.

```
diff --git a/sympy/printing/pycode.py b/sympy/printing/pycode.py
index 3270a171df..4bac55f1cc 100644
--- a/sympy/printing/pycode.py
+++ b/sympy/printing/pycode.py
@@ -491,6 +491,7 @@ def pycode(expr, **settings):
 _in_mpmath = [(k, v) for k, v in _known_functions_math.items() if k not in _not_in_mpmath]
 _known_functions_mpmath = dict(_in_mpmath, **{
     'sign': 'sign',
+    'loggamma': 'loggamma',
 })
 _known_constants_mpmath = {
     'Pi': 'pi'
```
Ahh, it wasn't defined there. Not strange then.

```
In [23]: MpmathPrinter().doprint(loggamma(x))
Out[23]: 'mpmath.lgamma(x)'
```

I thought it was related to this behaviour:
```
In [36]: F =lambdify([x, y], beta(x, y), modules=['numpy'])

In [37]: F?
Signature: F(x, y)
Docstring:
Created with lambdify. Signature:

func(x, y)

Expression:

beta(x, y)

Source code:

def _lambdifygenerated(x, y):
    return (beta(x, y))


Imported modules:
File:      /nobackup/data/sympy/sympy/<lambdifygenerated-5>
Type:      function

In [38]: NumPyPrinter().doprint(beta(x, y))
Out[38]: 'math.gamma(x)*math.gamma(y)/math.gamma(x + y)'
```
Yes, if the name is the same, it works automatically. Otherwise it has to be added to that list in lambdify. Actually I think the correct list is MPMATH_TRANSLATIONS in lambdify.py. That way it also works with the evalf mapping. 
Is there a reason this was on the release milestone? I removed it, but if it should be there please explain and readd it. 
I don't see any reason why this needs a milestone
If no one is working on this issue can I start contributing on it?
This issue is open for contributions. Please feel free to raise a PR. Thanks.

Commit message: Added testcase for loggamma

Patch to review:
diff --git a/sympy/printing/pycode.py b/sympy/printing/pycode.py
index 5862c8fb514e..c35def62cdd2 100644
--- a/sympy/printing/pycode.py
+++ b/sympy/printing/pycode.py
@@ -502,6 +502,7 @@ def pycode(expr, **settings):
     'fresnelc': 'fresnelc',
     'fresnels': 'fresnels',
     'sign': 'sign',
+    'loggamma': 'loggamma',
 })
 _known_constants_mpmath = {
     'Exp1': 'e',
diff --git a/sympy/printing/tests/test_pycode.py b/sympy/printing/tests/test_pycode.py
index 48fb134da5ef..a1cbbf7a9032 100644
--- a/sympy/printing/tests/test_pycode.py
+++ b/sympy/printing/tests/test_pycode.py
@@ -17,6 +17,7 @@
 from sympy.tensor import IndexedBase
 from sympy.testing.pytest import skip
 from sympy.external import import_module
+from sympy.functions.special.gamma_functions import loggamma
 
 x, y, z = symbols('x y z')
 p = IndexedBase("p")
@@ -75,6 +76,7 @@ def test_MpmathPrinter():
     assert p.doprint(S.NaN) == 'mpmath.nan'
     assert p.doprint(S.Infinity) == 'mpmath.inf'
     assert p.doprint(S.NegativeInfinity) == 'mpmath.ninf'
+    assert p.doprint(loggamma(x)) == 'mpmath.lgamma(x)'
 
 
 def test_NumPyPrinter():


Code review comments:
diff_hunk: @@ -75,6 +76,7 @@ def test_MpmathPrinter():
     assert p.doprint(S.NaN) == 'mpmath.nan'
     assert p.doprint(S.Infinity) == 'mpmath.inf'
     assert p.doprint(S.NegativeInfinity) == 'mpmath.ninf'
+    assert p.doprint(loggamma(x)) == 'mpmath.lgamma(x)'
path: sympy/printing/tests/test_pycode.py
text: @user1:
```suggestion
    assert p.doprint(loggamma(x)) == 'mpmath.loggamma(x)'
```