Opened 4 years ago

# hypergeometric function drops keyword arguments when evaluated with mpmath

Reported by: Erik Bray major symbolics N/A

### Description

As demonstrated by this ask.sagemath.org question, when trying to evaluate `hypergeometric` numerically, if it does not appear to be converging after the default number of iterations it raises an exception:

```NoConvergence: Hypergeometric series converges too slowly. Try increasing maxterms.
```

For example:

```sage: hypergeometric([4.14 + 15*I, -3.14 + 15*I],[1. - 1.12e7*I], -500000)
....:
---------------------------------------------------------------------------
NoConvergence                             Traceback (most recent call last)
<ipython-input-1-77676b9ed860> in <module>()
----> 1 hypergeometric([RealNumber('4.14') + Integer(15)*I, -RealNumber('3.14') + Integer(15)*I],[RealNumber('1.') - RealNumber('1.12e7')*I], -Integer(500000))

/home/embray/src/sagemath/sage-python3/local/lib/python3.7/site-packages/sage/functions/hypergeometric.py in __call__(self, a, b, z, **kwargs)
275                                         SR._force_pyobject(a),
276                                         SR._force_pyobject(b),
--> 277                                         z, **kwargs)
278
279     def _print_latex_(self, a, b, z):

/home/embray/src/sagemath/sage-python3/local/lib/python3.7/site-packages/sage/symbolic/function.pyx in sage.symbolic.function.BuiltinFunction.__call__ (build/cythonized/sage/symbolic/function.cpp:11840)()
996             res = self._evalf_try_(*args)
997             if res is None:
--> 998                 res = super(BuiltinFunction, self).__call__(
999                         *args, coerce=coerce, hold=hold)
1000

/home/embray/src/sagemath/sage-python3/local/lib/python3.7/site-packages/sage/symbolic/function.pyx in sage.symbolic.function.Function.__call__ (build/cythonized/sage/symbolic/function.cpp:6979)()
493                     (<Expression>args[1])._gobj, hold)
494         elif self._nargs == 3:
--> 495             res = g_function_eval3(self._serial,
496                     (<Expression>args[0])._gobj, (<Expression>args[1])._gobj,
497                     (<Expression>args[2])._gobj, hold)

/home/embray/src/sagemath/sage-python3/local/lib/python3.7/site-packages/sage/symbolic/function.pyx in sage.symbolic.function.BuiltinFunction._evalf_or_eval_ (build/cythonized/sage/symbolic/function.cpp:12952)()
1082         original version of :meth:`_eval_` saved in :meth:`__init__`.
1083         """
-> 1084         res = self._evalf_try_(*args)
1085         if res is None:
1086             return self._eval0_(*args)

/home/embray/src/sagemath/sage-python3/local/lib/python3.7/site-packages/sage/functions/hypergeometric.py in _evalf_try_(self, a, b, z)
336             if not any(isinstance(x, Expression) for x in args):
337                 p = get_coercion_model().common_parent(*args)
--> 338                 return self._evalf_(a, b, z, parent=p)
339
340     def _evalf_(self, a, b, z, parent, algorithm=None):

/home/embray/src/sagemath/sage-python3/local/lib/python3.7/site-packages/sage/functions/hypergeometric.py in _evalf_(self, a, b, z, parent, algorithm)
353         aa = [rational_param_as_tuple(c) for c in a]
354         bb = [rational_param_as_tuple(c) for c in b]
--> 355         return mpmath_utils.call(hyper, aa, bb, z, parent=parent)
356
357     def _tderivative_(self, a, b, z, *args, **kwargs):

/home/embray/src/sagemath/sage-python3/local/lib/python3.7/site-packages/sage/libs/mpmath/utils.pyx in sage.libs.mpmath.utils.call (build/cythonized/sage/libs/mpmath/utils.c:6947)()
435     try:
436         mp.prec = prec
--> 437         y = func(*args, **kwargs)
438     finally:
439         mp.prec = orig

/home/embray/src/sagemath/sage-python3/local/lib/python3.7/site-packages/mpmath/functions/hypergeometric.py in hyper(ctx, a_s, b_s, z, **kwargs)
224         elif q == 0: return ctx._hyp1f0(a_s[0][0], z)
225     elif p == 2:
--> 226         if   q == 1: return ctx._hyp2f1(a_s, b_s, z, **kwargs)
227         elif q == 2: return ctx._hyp2f2(a_s, b_s, z, **kwargs)
228         elif q == 3: return ctx._hyp2f3(a_s, b_s, z, **kwargs)

/home/embray/src/sagemath/sage-python3/local/lib/python3.7/site-packages/mpmath/functions/hypergeometric.py in _hyp2f1(ctx, a_s, b_s, z, **kwargs)
454                 T2 = ([-z],[-b], [c,ab],[a,c-b], [b,t+b],[ctx.mpq_1-ab],  rz)
455                 return T1, T2
--> 456             v = ctx.hypercomb(h, [a,b], **kwargs)
457
458         # Use 1-z transformation

/home/embray/src/sagemath/sage-python3/local/lib/python3.7/site-packages/mpmath/functions/hypergeometric.py in hypercomb(ctx, function, params, discard_known_zeros, **kwargs)
125                 v = ctx.fprod([ctx.hyper(a_s, b_s, z, **kwargs)] + \
126                     [ctx.gamma(a) for a in alpha_s] + \
--> 127                     [ctx.rgamma(b) for b in beta_s] + \
128                     [ctx.power(w,c) for (w,c) in zip(w_s,c_s)])
129                 if verbose:

/home/embray/src/sagemath/sage-python3/local/lib/python3.7/site-packages/mpmath/functions/hypergeometric.py in hyper(ctx, a_s, b_s, z, **kwargs)
224         elif q == 0: return ctx._hyp1f0(a_s[0][0], z)
225     elif p == 2:
--> 226         if   q == 1: return ctx._hyp2f1(a_s, b_s, z, **kwargs)
227         elif q == 2: return ctx._hyp2f2(a_s, b_s, z, **kwargs)
228         elif q == 3: return ctx._hyp2f3(a_s, b_s, z, **kwargs)

/home/embray/src/sagemath/sage-python3/local/lib/python3.7/site-packages/mpmath/functions/hypergeometric.py in _hyp2f1(ctx, a_s, b_s, z, **kwargs)
441     if absz <= 0.8 or (ctx.isint(a) and a <= 0 and a >= -1000) or \
442                       (ctx.isint(b) and b <= 0 and b >= -1000):
--> 443         return ctx.hypsum(2, 1, (atype, btype, ctype), [a, b, c], z, **kwargs)
444
445     orig = ctx.prec

/home/embray/src/sagemath/sage-python3/local/lib/python3.7/site-packages/mpmath/ctx_mp.py in hypsum(ctx, p, q, flags, coeffs, z, accurate_small, **kwargs)
713                 mag_dict = {}
714             zv, have_complex, magnitude = summator(coeffs, v, prec, wp, \
--> 715                 epsshift, mag_dict, **kwargs)
716             cancel = -magnitude
717             jumps_resolved = True

/home/embray/src/sagemath/sage-python3/local/lib/python3.7/site-packages/mpmath/libmp/libhyper.py in _hypsum(coeffs, z, prec, wp, epsshift, magnitude_check, **kwargs)
319         def _hypsum(coeffs, z, prec, wp, epsshift, magnitude_check, **kwargs):
320             return hypsum_internal(p, q, param_types, ztype, coeffs, z,
--> 321                 prec, wp, epsshift, magnitude_check, kwargs)
322
323         return "(none)", _hypsum

/home/embray/src/sagemath/sage-python3/local/lib/python3.7/site-packages/sage/libs/mpmath/ext_main.pyx in sage.libs.mpmath.ext_main.hypsum_internal (build/cythonized/sage/libs/mpmath/ext_main.c:28408)()
2646     cdef mpc c
2647     c = mpc.__new__(mpc)
-> 2648     have_complex, magn = MPF_hypsum(&c.re, &c.im, p, q, param_types, \
2649         ztype, coeffs, z, prec, wp, epsshift, magnitude_check, kwargs)
2650     if have_complex:

/home/embray/src/sagemath/sage-python3/local/lib/python3.7/site-packages/sage/libs/mpmath/ext_impl.pyx in sage.libs.mpmath.ext_impl.MPF_hypsum (build/cythonized/sage/libs/mpmath/ext_impl.c:23878)()
2212         if n > MAX:
2213             from mpmath.libmp import NoConvergence
-> 2214             raise NoConvergence('Hypergeometric series converges too slowly. Try increasing maxterms.')
2215
2216         # +1 all parameters for next iteration

NoConvergence: Hypergeometric series converges too slowly. Try increasing maxterms.
```

However, when trying to call it like:

```sage: hypergeometric([4.14 + 15*I, -3.14 + 15*I],[1. - 1.12e7*I], -500000, maxterms=1e6)
```

it just gives

```---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-2-29457394b902> in <module>()
----> 1 hypergeometric([RealNumber('4.14') + Integer(15)*I, -RealNumber('3.14') + Integer(15)*I],[RealNumber('1.') - RealNumber('1.12e7')*I], -Integer(500000), maxterms=RealNumber('1e6'))
2

/home/embray/src/sagemath/sage-python3/local/lib/python3.7/site-packages/sage/functions/hypergeometric.py in __call__(self, a, b, z, **kwargs)
275                                         SR._force_pyobject(a),
276                                         SR._force_pyobject(b),
--> 277                                         z, **kwargs)
278
279     def _print_latex_(self, a, b, z):

/home/embray/src/sagemath/sage-python3/local/lib/python3.7/site-packages/sage/symbolic/function.pyx in sage.symbolic.function.BuiltinFunction.__call__ (build/cythonized/sage/symbolic/function.cpp:10399)()
895                 evalf_params_first, alt_name = alt_name)
896
--> 897     def __call__(self, *args, bint coerce=True, bint hold=False,
898             bint dont_call_method_on_arg=False):
899         r"""

TypeError: __call__() got an unexpected keyword argument 'maxterms'
```

So, somewhere in the symbolic function interface it refuses to pass through keywords that are understood only by the underlying numerical implementation.

### comment:1 Changed 3 years ago by Erik Bray

Milestone: sage-8.8

As the Sage-8.8 release milestone is pending, we should delete the sage-8.8 milestone for tickets that are not actively being worked on or that still require significant work to move forward. If you feel that this ticket should be included in the next Sage release at the soonest please set its milestone to the next release milestone (sage-8.9).

Note: See TracTickets for help on using tickets.