diff --git a/src/doc/en/reference/calculus/index.rst b/src/doc/en/reference/calculus/index.rst index 73627cac21b..ecb2c931748 100644 --- a/src/doc/en/reference/calculus/index.rst +++ b/src/doc/en/reference/calculus/index.rst @@ -7,12 +7,12 @@ Symbolic Calculus sage/symbolic/expression sage/symbolic/assumptions sage/symbolic/relation - sage/symbolic/function_factory sage/calculus/calculus sage/symbolic/units sage/symbolic/ring - sage/calculus/functional sage/symbolic/function + sage/symbolic/function_factory + sage/calculus/functional sage/symbolic/integration/integral sage/calculus/test_sympy sage/calculus/tests diff --git a/src/sage/functions/bessel.py b/src/sage/functions/bessel.py index a5c1b93b74b..77e9dc95a52 100644 --- a/src/sage/functions/bessel.py +++ b/src/sage/functions/bessel.py @@ -189,7 +189,7 @@ from sage.structure.coerce import parent from sage.structure.element import get_coercion_model from sage.symbolic.constants import pi -from sage.symbolic.function import BuiltinFunction, is_inexact +from sage.symbolic.function import BuiltinFunction from sage.symbolic.expression import Expression # remove after deprecation period @@ -303,25 +303,6 @@ def __init__(self): maxima='bessel_j', sympy='besselj')) - def _eval_(self, n, x): - """ - EXAMPLES:: - - sage: a, b = var('a, b') - sage: bessel_J(a, b) - bessel_J(a, b) - sage: bessel_J(1.0, 1.0) - 0.440050585744933 - """ - if (not isinstance(n, Expression) and - not isinstance(x, Expression) and - (is_inexact(n) or is_inexact(x))): - coercion_model = get_coercion_model() - n, x = coercion_model.canonical_coercion(n, x) - return self._evalf_(n, x, parent(n)) - - return None - def _evalf_(self, n, x, parent=None, algorithm=None): """ EXAMPLES:: @@ -405,6 +386,8 @@ class Function_Bessel_Y(BuiltinFunction): 1.03440456978312 - 0.135747669767038*I sage: bessel_Y(0, 0).n() -infinity + sage: bessel_Y(0, 1).n(128) + 0.088256964215676957982926766023515162828 Examples of symbolic manipulation:: @@ -438,10 +421,23 @@ class Function_Bessel_Y(BuiltinFunction): Numerical evaluation is handled by the mpmath library. Symbolics are handled by a combination of Maxima and Sage (Ginac/Pynac). + TESTS: + Check whether the return value is real whenever the argument is real (:trac:`10251`):: sage: bessel_Y(5, 1.5) in RR True + + Coercion works correctly (see :trac:`17130`):: + + sage: r = bessel_Y(RealField(200)(1), 1.0); r + -0.781212821300289 + sage: parent(r) + Real Field with 53 bits of precision + sage: r = bessel_Y(RealField(200)(1), 1); r + -0.78121282130028871654715000004796482054990639071644460784383 + sage: parent(r) + Real Field with 200 bits of precision """ def __init__(self): """ @@ -457,24 +453,6 @@ def __init__(self): maxima='bessel_y', sympy='bessely')) - def _eval_(self, n, x): - """ - EXAMPLES:: - - sage: a,b = var('a, b') - sage: bessel_Y(a, b) - bessel_Y(a, b) - sage: bessel_Y(0, 1).n(128) - 0.088256964215676957982926766023515162828 - """ - if (not isinstance(n, Expression) and not isinstance(x, Expression) and - (is_inexact(n) or is_inexact(x))): - coercion_model = get_coercion_model() - n, x = coercion_model.canonical_coercion(n, x) - return self._evalf_(n, x, parent(n)) - - return None # leaves the expression unevaluated - def _evalf_(self, n, x, parent=None, algorithm=None): """ EXAMPLES:: @@ -632,20 +610,12 @@ def _eval_(self, n, x): sage: bessel_I(-1/2, pi) sqrt(2)*cosh(pi)/pi """ - if (not isinstance(n, Expression) and not isinstance(x, Expression) and - (is_inexact(n) or is_inexact(x))): - coercion_model = get_coercion_model() - n, x = coercion_model.canonical_coercion(n, x) - return self._evalf_(n, x, parent(n)) - # special identities if n == Integer(1) / Integer(2): return sqrt(2 / (pi * x)) * sinh(x) elif n == -Integer(1) / Integer(2): return sqrt(2 / (pi * x)) * cosh(x) - return None # leaves the expression unevaluated - def _evalf_(self, n, x, parent=None, algorithm=None): """ EXAMPLES:: @@ -810,18 +780,10 @@ def _eval_(self, n, x): sage: bessel_K(-1, 1).n(128) 0.60190723019723457473754000153561733926 """ - if (not isinstance(n, Expression) and not isinstance(x, Expression) and - (is_inexact(n) or is_inexact(x))): - coercion_model = get_coercion_model() - n, x = coercion_model.canonical_coercion(n, x) - return self._evalf_(n, x, parent(n)) - # special identity if n == Integer(1) / Integer(2) and x > 0: return sqrt(pi / 2) * exp(-x) * x ** (-Integer(1) / Integer(2)) - return None # leaves the expression unevaluated - def _evalf_(self, n, x, parent=None, algorithm=None): """ EXAMPLES:: diff --git a/src/sage/functions/exp_integral.py b/src/sage/functions/exp_integral.py index 521bd36a515..8bafb0d60ea 100644 --- a/src/sage/functions/exp_integral.py +++ b/src/sage/functions/exp_integral.py @@ -54,20 +54,14 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -import sage.interfaces.all -from sage.misc.sage_eval import sage_eval -from sage.symbolic.function import BuiltinFunction, is_inexact -from sage.calculus.calculus import maxima +from sage.symbolic.function import BuiltinFunction from sage.symbolic.expression import Expression -from sage.structure.parent import Parent from sage.structure.coerce import parent from sage.libs.mpmath import utils as mpmath_utils mpmath_utils_call = mpmath_utils.call # eliminate some overhead in _evalf_ -from sage.rings.rational_field import RationalField from sage.rings.real_mpfr import RealField -from sage.rings.complex_field import ComplexField -from sage.rings.all import ZZ, QQ, RR, RDF +from sage.rings.all import ZZ from sage.functions.log import exp, log from sage.functions.trig import sin, cos from sage.functions.hyperbolic import sinh, cosh @@ -185,12 +179,6 @@ def _eval_(self, n, z): 0.219383934395520 """ - if not isinstance(n, Expression) and not isinstance(z, Expression) and \ - (is_inexact(n) or is_inexact(z)): - coercion_model = sage.structure.element.get_coercion_model() - n, z = coercion_model.canonical_coercion(n, z) - return self._evalf_(n, z, parent(n)) - z_zero = False # special case: z == 0 and n > 1 if isinstance(z, Expression): @@ -269,7 +257,12 @@ class Function_exp_integral_e1(BuiltinFunction): see [AS]_ 5.1.4. - EXAMPLES: + EXAMPLES:: + + sage: exp_integral_e1(x) + exp_integral_e1(x) + sage: exp_integral_e1(1.0) + 0.219383934395520 Numerical evaluation is handled using mpmath:: @@ -319,21 +312,6 @@ def __init__(self): conversions=dict(maxima='expintegral_e1', sympy='E1')) - def _eval_(self, z): - """ - EXAMPLES:: - - sage: exp_integral_e1(x) - exp_integral_e1(x) - sage: exp_integral_e1(1.0) - 0.219383934395520 - - """ - if not isinstance(z, Expression) and is_inexact(z): - return self._evalf_(z, parent(z)) - - return None # leaves the expression unevaluated - def _evalf_(self, z, parent=None, algorithm=None): """ EXAMPLES:: @@ -452,15 +430,12 @@ def _eval_(self, z): 0 """ + # Special case z = 0 if isinstance(z, Expression): - if z.is_trivial_zero(): # special case: z = 0 - return z - else: - if is_inexact(z): - return self._evalf_(z, parent(z)) - elif not z: + if z.is_trivial_zero(): return z - return None # leaves the expression unevaluated + elif not z: + return z def _evalf_(self, z, parent=None, algorithm=None): """ @@ -640,9 +615,7 @@ def _eval_(self,z): 0 """ - if not isinstance(z,Expression) and is_inexact(z): - return self._evalf_(z,parent(z)) - if z==2: + if z == 2: import sage.symbolic.ring return sage.symbolic.ring.SR(0) return li(z)-li(2) @@ -814,18 +787,11 @@ def _eval_(self, z): 0 """ - if not isinstance(z, Expression) and is_inexact(z): - return self._evalf_(z, parent(z)) - - # special case: z = 0 if isinstance(z, Expression): if z.is_trivial_zero(): return z - else: - if not z: - return z - - return None # leaves the expression unevaluated + elif not z: + return z def _evalf_(self, z, parent=None, algorithm=None): """ @@ -892,7 +858,17 @@ class Function_cos_integral(BuiltinFunction): where `\gamma` is the Euler gamma constant (``euler_gamma`` in Sage), see [AS]_ 5.2.1. - EXAMPLES: + EXAMPLES:: + + sage: z = var('z') + sage: cos_integral(z) + cos_integral(z) + sage: cos_integral(3.0) + 0.119629786008000 + sage: cos_integral(0) + cos_integral(0) + sage: N(cos_integral(0)) + -infinity Numerical evaluation for real and complex arguments is handled using mpmath:: @@ -970,26 +946,6 @@ def __init__(self): conversions=dict(maxima='expintegral_ci', sympy='Ci')) - def _eval_(self, z): - """ - EXAMPLES:: - - sage: z = var('z') - sage: cos_integral(z) - cos_integral(z) - sage: cos_integral(3.0) - 0.119629786008000 - sage: cos_integral(0) - cos_integral(0) - sage: N(cos_integral(0)) - -infinity - - """ - if not isinstance(z, Expression) and is_inexact(z): - return self._evalf_(z, parent(z)) - - return None # leaves the expression unevaluated - def _evalf_(self, z, parent=None, algorithm=None): """ EXAMPLES:: @@ -1135,18 +1091,12 @@ def _eval_(self, z): 0 """ - if not isinstance(z, Expression) and is_inexact(z): - return self._evalf_(z, parent(z)) - # special case: z = 0 if isinstance(z, Expression): if z.is_trivial_zero(): return z - else: - if not z: - return z - - return None # leaves the expression unevaluated + elif not z: + return z def _evalf_(self, z, parent=None, algorithm=None): """ @@ -1192,7 +1142,13 @@ class Function_cosh_integral(BuiltinFunction): see [AS]_ 5.2.4. - EXAMPLES: + EXAMPLES:: + + sage: z = var('z') + sage: cosh_integral(z) + cosh_integral(z) + sage: cosh_integral(3.0) + 4.96039209476561 Numerical evaluation for real and complex arguments is handled using mpmath:: @@ -1269,22 +1225,6 @@ def __init__(self): conversions=dict(maxima='expintegral_chi', sympy='Chi')) - def _eval_(self, z): - """ - EXAMPLES:: - - sage: z = var('z') - sage: cosh_integral(z) - cosh_integral(z) - sage: cosh_integral(3.0) - 4.96039209476561 - - """ - if not isinstance(z, Expression) and is_inexact(z): - return self._evalf_(z, parent(z)) - - return None - def _evalf_(self, z, parent=None, algorithm=None): """ EXAMPLES:: @@ -1350,6 +1290,10 @@ class Function_exp_integral(BuiltinFunction): Ei(I + 3) sage: Ei(1.3) 2.72139888023202 + sage: Ei(10r) + Ei(10) + sage: Ei(1.3r) + 2.7213988802320235 The branch cut for this function is along the negative real axis:: @@ -1393,25 +1337,6 @@ def __init__(self): conversions=dict(maxima='expintegral_ei', sympy='Ei')) - def _eval_(self, x ): - """ - EXAMPLES:: - - sage: Ei(10) - Ei(10) - sage: Ei(I) - Ei(I) - sage: Ei(1.3) - 2.72139888023202 - sage: Ei(10r) - Ei(10) - sage: Ei(1.3r) - 2.7213988802320235 - """ - if not isinstance(x, Expression) and is_inexact(x): - return self._evalf_(x, parent(x)) - return None - def _evalf_(self, x, parent=None, algorithm=None): """ EXAMPLES:: @@ -1540,13 +1465,12 @@ def exponential_integral_1(x, n=0): else: raise NotImplementedError("Use the symbolic exponential integral " + "function: exp_integral_e1.") - elif not is_inexact(x): # x is exact and not an expression - if not x: # test if exact x == 0 quickly - from sage.rings.infinity import Infinity - return Infinity - # else x is not an exact 0 - from sage.libs.pari.all import pari + # x == 0 => return Infinity + if not x: + from sage.rings.infinity import Infinity + return Infinity + # Figure out output precision try: prec = parent(x).precision() diff --git a/src/sage/functions/hyperbolic.py b/src/sage/functions/hyperbolic.py index c0fa7037958..99994d99046 100644 --- a/src/sage/functions/hyperbolic.py +++ b/src/sage/functions/hyperbolic.py @@ -21,7 +21,6 @@ class HyperbolicFunction(BuiltinFunction): sage: f(x)._mathematica_init_() 'Foo[x]' """ - _eval_ = BuiltinFunction._eval_default def __init__(self, name, latex_name=None, conversions=None, evalf_float=None): """ @@ -540,9 +539,19 @@ def __init__(self): 0.402359478108525 - 0.553574358897045*I sage: arccoth(2).n(200) 0.54930614433405484569762261846126285232374527891137472586735 + + Using first the `.n(53)` method is slightly more precise than + converting directly to a ``float``:: + sage: float(arccoth(2)) + 0.5493061443340548 + sage: float(arccoth(2).n(53)) # Correct result to 53 bits + 0.5493061443340549 + sage: float(arccoth(2).n(100)) # Compute 100 bits and then round to 53 0.5493061443340549 + TESTS:: + sage: latex(arccoth(x)) {\rm arccoth}\left(x\right) """ diff --git a/src/sage/functions/hypergeometric.py b/src/sage/functions/hypergeometric.py index c9b10f3c60c..59ec60cbdf5 100644 --- a/src/sage/functions/hypergeometric.py +++ b/src/sage/functions/hypergeometric.py @@ -148,7 +148,7 @@ from sage.functions.other import erf from sage.symbolic.constants import pi from sage.symbolic.all import I -from sage.symbolic.function import BuiltinFunction, is_inexact +from sage.symbolic.function import BuiltinFunction from sage.symbolic.ring import SR from sage.structure.element import get_coercion_model from sage.misc.latex import latex @@ -269,16 +269,42 @@ def _eval_(self, a, b, z, **kwargs): 1 """ if not isinstance(a,tuple) or not isinstance(b,tuple): - raise ValueError('First two parameters must be of type list.') - coercion_model = get_coercion_model() - co = reduce(lambda x, y: coercion_model.canonical_coercion(x, y)[0], - a + b + (z,)) - if is_inexact(co) and not isinstance(co, Expression): - from sage.structure.coerce import parent - return self._evalf_(a, b, z, parent=parent(co)) + raise TypeError("The first two parameters must be of type list") if not isinstance(z, Expression) and z == 0: # Expression is excluded return Integer(1) # to avoid call to Maxima - return + + def _evalf_try_(self, a, b, z): + """ + Call :meth:`_evalf_` if one the arguments is numerical and none + of the arguments are symbolic. + + OUTPUT: + + - ``None`` if we didn't succeed to call :meth:`_evalf_` or if + the input wasn't suitable for it. + + - otherwise, a numerical value for the function. + + EXAMPLES:: + + sage: hypergeometric._evalf_try_((1.0,), (2.0,), 3.0) + 6.36184564106256 + sage: hypergeometric._evalf_try_((1.0, 1), (), 3.0) + -0.0377593153441588 + 0.750349833788561*I + sage: hypergeometric._evalf_try_((1, 1), (), 3) # exact input + sage: hypergeometric._evalf_try_((x,), (), 1.0) # symbolic + sage: hypergeometric._evalf_try_(1.0, 2.0, 3.0) # not tuples + """ + # We need to override this for hypergeometric functions since + # the first 2 arguments are tuples and the generic _evalf_try_ + # cannot handle that. + if not isinstance(a,tuple) or not isinstance(b,tuple): + return None + args = list(a) + list(b) + [z] + if any(self._is_numerical(x) for x in args): + if not any(isinstance(x, Expression) for x in args): + p = get_coercion_model().common_parent(*args) + return self._evalf_(a, b, z, parent=p) def _evalf_(self, a, b, z, parent, algorithm=None): """ @@ -291,7 +317,7 @@ def _evalf_(self, a, b, z, parent, algorithm=None): """ if not isinstance(a,tuple) or not isinstance(b,tuple): - raise ValueError('First two parameters must be of type list.') + raise TypeError("The first two parameters must be of type list") from mpmath import hyper aa = [rational_param_as_tuple(c) for c in a] bb = [rational_param_as_tuple(c) for c in b] diff --git a/src/sage/functions/jacobi.py b/src/sage/functions/jacobi.py index 82f8da72ea2..1448c8976b7 100644 --- a/src/sage/functions/jacobi.py +++ b/src/sage/functions/jacobi.py @@ -148,10 +148,7 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from sage.symbolic.function import BuiltinFunction, is_inexact -from sage.structure.coerce import parent -from sage.structure.element import get_coercion_model -from sage.symbolic.expression import Expression +from sage.symbolic.function import BuiltinFunction from sage.functions.trig import (arctan, arcsin, arccos, arccot, arcsec, arccsc, csc, sec, sin, cos, tan, cot) from sage.functions.hyperbolic import (arctanh, arccosh, arcsinh, arcsech, @@ -270,10 +267,6 @@ def _eval_(self, x, m): sage: almosteq(n(jacobi_cs(-6, 1, hold=True)), n(jacobi_cs(-6, 1))) True """ - coercion_model = get_coercion_model() - co = coercion_model.canonical_coercion(x, m)[0] - if is_inexact(co) and not isinstance(co, Expression): - return self._evalf_(x, m, parent(co)) if self.kind == 'nd': if m == 0: return Integer(1) @@ -641,10 +634,6 @@ def _eval_(self, x, m): ....: n(inverse_jacobi_sn(0, 6))) True """ - coercion_model = get_coercion_model() - x, m = coercion_model.canonical_coercion(x, m) - if is_inexact(x) and not isinstance(x, Expression): - return self._evalf_(x, m, parent(x)) if self.kind == 'cd': if m == 0: return arccos(x) @@ -1091,10 +1080,6 @@ def _eval_(self, x, m): sage: jacobi_am(3, 4.) -0.339059208303591 """ - coercion_model = get_coercion_model() - x, m = coercion_model.canonical_coercion(x, m) - if is_inexact(x) and not isinstance(x, Expression): - return self._evalf_(x, m, parent(x)) if m == 0: return x elif x == 0: diff --git a/src/sage/functions/log.py b/src/sage/functions/log.py index 4edf90087ec..ba2b70b4c01 100644 --- a/src/sage/functions/log.py +++ b/src/sage/functions/log.py @@ -6,11 +6,11 @@ - Yoora Yi Tenen (2012-11-16): Add documentation for :meth:`log()` (:trac:`12113`) """ -from sage.symbolic.function import GinacFunction, BuiltinFunction, is_inexact +from sage.symbolic.function import GinacFunction, BuiltinFunction from sage.symbolic.constants import e as const_e from sage.libs.mpmath import utils as mpmath_utils -from sage.structure.coerce import parent as sage_structure_coerce_parent +from sage.structure.coerce import parent as s_parent from sage.symbolic.expression import Expression from sage.rings.real_double import RDF from sage.rings.complex_double import CDF @@ -629,18 +629,15 @@ def _eval_(self, n, z): Integer Ring """ if not isinstance(z, Expression): - if is_inexact(z): - return self._evalf_(n, z, parent=sage_structure_coerce_parent(z)) - elif n == 0 and z == 0: - return sage_structure_coerce_parent(z)(Integer(0)) + if n == 0 and z == 0: + return s_parent(z)(0) elif n == 0: if z.is_trivial_zero(): - return sage_structure_coerce_parent(z)(Integer(0)) + return s_parent(z)(Integer(0)) elif (z-const_e).is_trivial_zero(): - return sage_structure_coerce_parent(z)(Integer(1)) + return s_parent(z)(Integer(1)) elif (z+1/const_e).is_trivial_zero(): - return sage_structure_coerce_parent(z)(Integer(-1)) - return None + return s_parent(z)(Integer(-1)) def _evalf_(self, n, z, parent=None, algorithm=None): """ @@ -655,11 +652,31 @@ def _evalf_(self, n, z, parent=None, algorithm=None): sage: lambert_w(RDF(1)) 0.5671432904097838 + sage: lambert_w(float(1)) + 0.5671432904097838 + sage: lambert_w(CDF(1)) + 0.5671432904097838 + sage: lambert_w(complex(1)) + (0.5671432904097838+0j) + sage: lambert_w(RDF(-1)) + -0.3181315052047642 + 1.3372357014306895*I + sage: lambert_w(float(-1)) + (-0.3181315052047642+1.3372357014306895j) """ - R = parent or sage_structure_coerce_parent(z) - if R is float or R is complex or R is RDF or R is CDF: - import scipy.special - return scipy.special.lambertw(z, n) + R = parent or s_parent(z) + if R is float or R is RDF: + from scipy.special import lambertw + res = lambertw(z, n) + # SciPy always returns a complex value, make it real if possible + if not res.imag: + return R(res.real) + elif R is float: + return complex(res) + else: + return CDF(res) + elif R is complex or R is CDF: + from scipy.special import lambertw + return R(lambertw(z, n)) else: import mpmath return mpmath_utils.call(mpmath.lambertw, z, n, parent=R) diff --git a/src/sage/functions/other.py b/src/sage/functions/other.py index dc538e56e47..9df1790cc1d 100644 --- a/src/sage/functions/other.py +++ b/src/sage/functions/other.py @@ -18,7 +18,6 @@ from sage.structure.coerce import parent as s_parent from sage.symbolic.constants import pi -from sage.symbolic.function import is_inexact from sage.functions.log import exp from sage.functions.trig import arctan2 from sage.functions.exp_integral import Ei @@ -187,14 +186,11 @@ def _eval_(self, x): sage: erf(SR(0)) 0 """ - if not isinstance(x, Expression): - if is_inexact(x): - return self._evalf_(x, parent=s_parent(x)) - elif x == Integer(0): - return Integer(0) - elif x.is_trivial_zero(): + if isinstance(x, Expression): + if x.is_trivial_zero(): + return x + elif not x: return x - return None def _evalf_(self, x, parent=None, algorithm=None): """ @@ -902,11 +898,6 @@ def _eval_(self, x, y): sage: gamma_inc(0,2) -Ei(-2) """ - if not isinstance(x, Expression) and not isinstance(y, Expression) and \ - (is_inexact(x) or is_inexact(y)): - x, y = coercion_model.canonical_coercion(x, y) - return self._evalf_(x, y, s_parent(x)) - if y == 0: return gamma(x) if x == 1: @@ -936,14 +927,22 @@ def _evalf_(self, x, y, parent=None, algorithm=None): sage: gamma(R(9), R(10^-3)) # rel tol 1e-308 40319.99999999999999999999999999988898884344822911869926361916294165058203634104838326009191542490601781777105678829520585311300510347676330951251563007679436243294653538925717144381702105700908686088851362675381239820118402497959018315224423868693918493033078310647199219674433536605771315869983788442389633 sage: numerical_approx(gamma(9, 10^(-3)) - gamma(9), digits=40) # abs tol 1e-36 - -1.110111564516556704267183273042450876294e-28 + -1.110111598370794007949063502542063148294e-28 + + Check that :trac:`17130` is fixed:: + sage: r = gamma_inc(float(0), float(1)); r + 0.21938393439552029 + sage: type(r) + """ if parent is None: - parent = ComplexField() - else: - parent = ComplexField(parent.precision()) - return parent(x).gamma_inc(y) + parent = s_parent(x) + try: + prec = parent.precision() + except Exception: + prec = 53 + return parent(ComplexField(prec)(x).gamma_inc(y)) # synonym. incomplete_gamma = gamma_inc=Function_gamma_inc() @@ -1364,8 +1363,7 @@ def _eval_(self, x): """ if isinstance(x, Rational): return gamma(x+1) - elif isinstance(x, (Integer, int)) or \ - (not isinstance(x, Expression) and is_inexact(x)): + elif isinstance(x, (Integer, int)) or self._is_numerical(x): return py_factorial_py(x) return None @@ -1518,7 +1516,7 @@ def _eval_(self, n, k): if not isinstance(k, Expression): if not isinstance(n, Expression): n, k = coercion_model.canonical_coercion(n, k) - return self._evalf_(n, k, s_parent(n)) + return self._evalf_(n, k) if k in ZZ: return self._binomial_sym(n, k) if (n - k) in ZZ: @@ -1867,18 +1865,14 @@ def _eval_(self, x): arg(sqrt(2) + I) """ - if not isinstance(x,Expression): # x contains no variables - if s_parent(x)(0)==x: #compatibility with maxima - return s_parent(x)(0) - else: - if is_inexact(x): # inexact complex numbers, e.g. 2.0+i - return self._evalf_(x, s_parent(x)) - else: # exact complex numbers, e.g. 2+i - return arctan2(imag_part(x),real_part(x)) + if isinstance(x,Expression): + if x.is_trivial_zero(): + return x else: - # x contains variables, e.g. 2+i+y or 2.0+i+y - # or x involves an expression such as sqrt(2) - return None + if not x: + return x + else: + return arctan2(imag_part(x),real_part(x)) def _evalf_(self, x, parent=None, algorithm=None): """ diff --git a/src/sage/functions/prime_pi.pyx b/src/sage/functions/prime_pi.pyx index 543d9e4df93..77d31694e4f 100644 --- a/src/sage/functions/prime_pi.pyx +++ b/src/sage/functions/prime_pi.pyx @@ -105,7 +105,7 @@ cdef class PrimePi(BuiltinFunction): These examples test a variety of odd inputs:: sage: prime_pi(3.5) - 2.00000000000000 + 2 sage: prime_pi(sqrt(2357)) 15 sage: prime_pi(mod(30957, 9750979)) diff --git a/src/sage/functions/special.py b/src/sage/functions/special.py index 5190dcfb6f3..5f4da57194e 100644 --- a/src/sage/functions/special.py +++ b/src/sage/functions/special.py @@ -204,17 +204,14 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from sage.plot.plot import plot from sage.rings.real_mpfr import RealField from sage.rings.complex_field import ComplexField -from sage.misc.sage_eval import sage_eval from sage.misc.latex import latex from sage.rings.all import ZZ, RR, RDF, CDF -from sage.functions.other import real, imag, log_gamma -from sage.symbolic.function import BuiltinFunction, is_inexact -from sage.symbolic.expression import Expression +from sage.structure.parent import Parent +from sage.functions.other import log_gamma +from sage.symbolic.function import BuiltinFunction from sage.calculus.calculus import maxima -from sage.structure.element import get_coercion_model from sage.libs.mpmath import utils as mpmath_utils from sage.functions.all import sqrt, cot, exp from sage.symbolic.all import I @@ -332,28 +329,50 @@ def _evalf_(self, *args, **kwds): sage: from sage.functions.special import MaximaFunction sage: f = MaximaFunction("jacobi_sn") - sage: f(1/2,1/2) + sage: f(1/2, 1/2) jacobi_sn(1/2, 1/2) - sage: f(1/2,1/2).n() + sage: f(1/2, 1/2).n() 0.470750473655657 + sage: f(1/2, 1/2).n(20) + 0.47075 + sage: f(1, I).n() + 0.848379519751901 - 0.0742924572771414*I TESTS:: - sage: f(1/2,1/2).n(150) + sage: f(1/2, 1/2).n(150) + Traceback (most recent call last): + ... + NotImplementedError: Maxima function jacobi_sn not implemented for Real Field with 150 bits of precision + sage: f._evalf_(1/2, 1/2, parent=int) Traceback (most recent call last): ... - NotImplementedError: jacobi_sn not implemented for precision > 53 + NotImplementedError: Maxima function jacobi_sn not implemented for + sage: f._evalf_(1/2, 1/2, parent=complex) + (0.4707504736556572+0j) + sage: f._evalf_(1/2, 1/2, parent=RDF) + 0.4707504736556572 + sage: f._evalf_(1, I, parent=CDF) + 0.8483795707591759 - 0.0742924734216079*I + sage: f._evalf_(1, I, parent=RR) + Traceback (most recent call last): + ... + TypeError: Unable to convert x (='0.848379570759176-0.0742924734216079*I') to real number. """ parent = kwds['parent'] - if hasattr(parent, 'prec') and parent.prec() > 53: - raise NotImplementedError("%s not implemented for precision > 53"%self.name()) + # The result from maxima is a machine double, which corresponds + # to RDF (or CDF). Therefore, before converting, we check that + # we can actually coerce RDF into our parent. + if parent is not float and parent is not complex: + if not isinstance(parent, Parent) or not parent.has_coerce_map_from(RDF): + raise NotImplementedError("Maxima function %s not implemented for %r"%(self.name(), parent)) _init() return parent(maxima("%s, numer"%self._maxima_init_evaled_(*args))) def _eval_(self, *args): """ - Returns a string which represents this function evaluated at - *args* in Maxima. + Try to evaluate this function at ``*args``, return ``None`` if + Maxima did not compute a numerical evaluation. EXAMPLES:: @@ -369,13 +388,23 @@ def _eval_(self, *args): sage: elliptic_e(arccoth(1), x^2*e) elliptic_e(arccoth(1), x^2*e) + + Since Maxima works only with double precision, numerical + results are in ``RDF``, no matter what the input precision is:: + + sage: R = RealField(300) + sage: r = elliptic_eu(R(1/2), R(1/8)); r + 0.4950737320232015 + sage: parent(r) + Real Double Field """ _init() try: s = maxima(self._maxima_init_evaled_(*args)) except TypeError: return None - if self.name() in repr(s): + + if self.name() in s.__repr__(): # Avoid infinite recursion return None else: return s.sage() @@ -667,17 +696,11 @@ def _eval_(self, n, m, theta, phi, **kwargs): sage: spherical_harmonic(3 + I, 2., 1, 2) -0.351154337307488 - 0.415562233975369*I """ - from sage.structure.coerce import parent - cc = get_coercion_model().canonical_coercion - coerced = cc(phi, cc(theta, cc(n, m)[0])[0])[0] - if is_inexact(coerced) and not isinstance(coerced, Expression): - return self._evalf_(n, m, theta, phi, parent=parent(coerced)) - elif n in ZZ and m in ZZ and n > -1: + if n in ZZ and m in ZZ and n > -1: if abs(m) > n: return ZZ(0) return meval("spherical_harmonic({},{},{},{})".format( ZZ(n), ZZ(m), maxima(theta), maxima(phi))) - return def _evalf_(self, n, m, theta, phi, parent, **kwds): r""" @@ -1052,7 +1075,6 @@ def error_fcn(t): try: return t.erfc() except AttributeError: - from sage.rings.real_mpfr import RR try: return RR(t).erfc() except Exception: diff --git a/src/sage/functions/transcendental.py b/src/sage/functions/transcendental.py index 37ba5b76872..0ebc901e825 100644 --- a/src/sage/functions/transcendental.py +++ b/src/sage/functions/transcendental.py @@ -18,19 +18,14 @@ #***************************************************************************** import sys -import sage.libs.pari.all -from sage.libs.pari.all import pari import sage.rings.complex_field as complex_field -from sage.structure.coerce import parent -from sage.structure.element import get_coercion_model -from sage.symbolic.expression import Expression from sage.functions.other import factorial, psi from sage.rings.all import (ComplexField, ZZ, RR, RDF) from sage.rings.complex_number import is_ComplexNumber from sage.rings.real_mpfr import (RealField, is_RealNumber) -from sage.symbolic.function import GinacFunction, BuiltinFunction, is_inexact +from sage.symbolic.function import GinacFunction, BuiltinFunction import sage.libs.mpmath.utils as mpmath_utils from sage.misc.superseded import deprecation @@ -127,9 +122,6 @@ def _eval_(self, s, x): sage: hurwitz_zeta(3, 0.5) 8.41439832211716 """ - co = get_coercion_model().canonical_coercion(s, x)[0] - if is_inexact(co) and not isinstance(co, Expression): - return self._evalf_(s, x, parent=parent(co)) if x == 1: return zeta(s) if s in ZZ and s > 1: diff --git a/src/sage/functions/trig.py b/src/sage/functions/trig.py index 7205d5580fc..5cae8414152 100644 --- a/src/sage/functions/trig.py +++ b/src/sage/functions/trig.py @@ -763,6 +763,8 @@ def __init__(self): sage: arcsec(2) arcsec(2) + sage: arcsec(2.0) + 1.04719755119660 sage: RDF(arcsec(2)) # abs tol 1e-15 1.0471975511965976 sage: arcsec(1 + I) @@ -789,6 +791,8 @@ def _evalf_(self, x, parent=None, algorithm=None): sage: arcsec(2).n(100) 1.0471975511965977461542144611 + sage: arcsec(1/2).n(100) + NaN TESTS: diff --git a/src/sage/rings/complex_double.pyx b/src/sage/rings/complex_double.pyx index 86f14ae0b7b..b42f63f4376 100644 --- a/src/sage/rings/complex_double.pyx +++ b/src/sage/rings/complex_double.pyx @@ -1911,6 +1911,18 @@ cdef class ComplexDoubleElement(FieldElement): """ return self._new_c(gsl_complex_arccot(self._complex)) + def arcsec(self): + r""" + This function returns the complex arcsecant of the complex number + `z`, `{\rm arcsec}(z) = {\rm arccos}(1/z)`. + + EXAMPLES:: + + sage: CDF(1,1).arcsec() # rel tol 1e-15 + 1.118517879643706 + 0.5306375309525178*I + """ + return self._new_c(gsl_complex_arcsec(self._complex)) + ####################################################################### # Complex Hyperbolic Functions diff --git a/src/sage/structure/coerce.pxd b/src/sage/structure/coerce.pxd index ea9404b5688..07daf9d81af 100644 --- a/src/sage/structure/coerce.pxd +++ b/src/sage/structure/coerce.pxd @@ -6,6 +6,8 @@ from sage.categories.action cimport Action from coerce_dict cimport TripleDict +cpdef py_scalar_parent(py_type) + cdef class CoercionModel_cache_maps(CoercionModel): # This MUST be a mapping to tuples, where each # tuple contains at least two elements that are either diff --git a/src/sage/symbolic/function.pyx b/src/sage/symbolic/function.pyx index 408b6da9cf0..70cd1c5d40a 100644 --- a/src/sage/symbolic/function.pyx +++ b/src/sage/symbolic/function.pyx @@ -1,29 +1,25 @@ -############################################################################### -# Sage: Open Source Mathematical Software +r""" +Classes for symbolic functions +""" + +#***************************************************************************** # Copyright (C) 2008 - 2010 Burcin Erocal # Copyright (C) 2008 William Stein -# Distributed under the terms of the GNU General Public License (GPL), -# version 2 or any later version. The full text of the GPL is available at: +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. # http://www.gnu.org/licenses/ -############################################################################### - -r""" - -Support for symbolic functions. +#***************************************************************************** -""" -include "sage/ext/interrupt.pxi" -include "sage/ext/cdefs.pxi" - -from functools import reduce from ginac cimport * from sage.structure.sage_object cimport SageObject from expression cimport new_Expression_from_GEx, Expression from ring import SR -from sage.structure.parent cimport Parent -from sage.structure.coerce import parent +from sage.structure.coerce cimport py_scalar_parent from sage.structure.element import get_coercion_model # we keep a database of symbolic functions initialized in a session @@ -191,11 +187,17 @@ cdef class Function(SageObject): self._serial = g_register_new(opt) g_foptions_assign(g_registered_functions().index(self._serial), opt) - def _eval_default(self, *args): + def _evalf_try_(self, *args): """ - Default automatic evaluation function. + Call :meth:`_evalf_` if one the arguments is numerical and none + of the arguments are symbolic. + + OUTPUT: - Calls numeric evaluation if the argument is not exact. + - ``None`` if we didn't succeed to call :meth:`_evalf_` or if + the input wasn't suitable for it. + + - otherwise, a numerical value for the function. TESTS:: @@ -210,7 +212,7 @@ cdef class Function(SageObject): ....: def _evalf_(self, x, y, parent): ....: return x + 1 ....: def _eval_(self, x, y): - ....: res = self._eval_default(x, y) + ....: res = self._evalf_try_(x, y) ....: if res: ....: return res ....: elif x == 2: @@ -234,7 +236,7 @@ cdef class Function(SageObject): ....: def _evalf_(self, x, parent): ....: return 0.5 ....: def _eval_(self, x): - ....: res = self._eval_default(x) + ....: res = self._evalf_try_(x) ....: if res: ....: return res ....: else: @@ -245,24 +247,16 @@ cdef class Function(SageObject): sage: test2(pi) 3 """ - if len(args) == 1: - x = args[0] - try: - method = getattr(x, self.name()) - except AttributeError: - pass - else: - return method() - if is_inexact(x) and not parent_c(x) is SR: - return self._evalf_(x, parent=parent(x)) - return - else: - cc = get_coercion_model().canonical_coercion - coerced = reduce(lambda x, y: cc(x, y)[0], args) - if is_inexact(coerced) and not parent_c(coerced) is SR: - return self._evalf_(*args, parent=parent(coerced)) - else: - return + # If any of the inputs is numerical and none is symbolic, + # try to call _evalf_() directly + try: + evalf = self._evalf_ # catch AttributeError early + if any(self._is_numerical(x) for x in args): + if not any(isinstance(x, Expression) for x in args): + p = get_coercion_model().common_parent(*args) + return evalf(*args, parent=p) + except Exception: + pass def __hash__(self): """ @@ -513,7 +507,6 @@ cdef class Function(SageObject): (args[0])._gobj, (args[1])._gobj, (args[2])._gobj, hold) - if not symbolic_input and is_a_numeric(res): return py_object_from_numeric(res) @@ -573,6 +566,35 @@ cdef class Function(SageObject): """ return SR.var('x') + def _is_numerical(self, x): + """ + Return True if `x` is a numerical object. + + This is used to determine whether to call the :meth:`_evalf_` + method instead of the :meth:`_eval_` method. + + This is a non-static method since whether or not an argument is + considered numerical may depend on the specific function. + + TESTS:: + + sage: sin._is_numerical(5) + False + sage: sin._is_numerical(5.) + True + sage: sin._is_numerical(pi) + False + sage: sin._is_numerical(5r) + False + sage: sin._is_numerical(5.4r) + True + """ + if isinstance(x, (float, complex)): + return True + if isinstance(x, Element): + return hasattr((x)._parent, 'precision') + return False + def _interface_init_(self, I=None): """ EXAMPLES:: @@ -741,7 +763,7 @@ cdef class Function(SageObject): ....: return prec sage: noMpmathFn = NoMpmathFn("noMpmathFn") sage: with mpmath.workprec(64): noMpmathFn(sqrt(mpmath.mpf('2'))) - mpf('64.0') + 64 sage: mpmath.noMpmathFn = lambda x: 123 sage: with mpmath.workprec(64): noMpmathFn(sqrt(mpmath.mpf('2'))) 123 @@ -868,6 +890,14 @@ cdef class BuiltinFunction(Function): sage: c(pi/2) 0 """ + # If we have an _evalf_ method, change _eval_ to a + # wrapper function which first tries to call _evalf_. + if hasattr(self, '_evalf_'): + if hasattr(self, '_eval_'): + self._eval0_ = self._eval_ + self._eval_ = self._evalf_or_eval_ + else: + self._eval_ = self._evalf_try_ Function.__init__(self, name, nargs, latex_name, conversions, evalf_params_first, alt_name = alt_name) @@ -896,57 +926,50 @@ cdef class BuiltinFunction(Function): sage: bar(A()) 'foo' """ - # if there is only one argument, and the argument has an attribute + # If there is only one argument, and the argument has an attribute # with the same name as this function, try to call it to get the result # The argument dont_call_method_on_arg is used to prevent infinite loops # when .exp(), .log(), etc. methods call this symbolic function on # themselves + res = None if len(args) == 1 and not hold and not dont_call_method_on_arg: arg = args[0] + # If arg is a Python type (e.g. float), convert it to Sage + t = py_scalar_parent(type(arg)) + if t is not None: + arg = t(arg) method = getattr(arg, self._name, None) if callable(method): - return method() + res = method() elif self._alt_name is not None: method = getattr(arg, self._alt_name, None) if method is not None: - return method() + res = method() - res = super(BuiltinFunction, self).__call__( + if res is None: + res = self._evalf_try_(*args) + if res is None: + res = super(BuiltinFunction, self).__call__( *args, coerce=coerce, hold=hold) - # we want to convert the result to the original parent if the input - # is not exact, so we store the parent here - org_parent = parent_c(args[0]) - - # convert the result back to the original parent previously stored - # otherwise we end up with - # sage: arctan(RR(1)) - # 1/4*pi - # which is surprising, to say the least... - if org_parent is not SR and \ - (org_parent is float or org_parent is complex or \ - (PY_TYPE_CHECK(org_parent, Parent) and \ - not org_parent.is_exact())): - try: - return org_parent(res) - except (TypeError, ValueError): - pass - - # conversion to the original parent failed - # we try if it works with the corresponding complex domain - if org_parent is float or org_parent is complex: - try: - from sage.rings.complex_double import CDF - return complex(CDF(res)) - except (TypeError, ValueError): - pass - elif hasattr(org_parent, 'complex_field'): - try: - return org_parent.complex_field()(res) - except (TypeError, ValueError): - pass - - return res + # If none of the input arguments was a Sage Element but the + # output is, then convert the output back to the corresponding + # Python type if possible. + if any(isinstance(x, Element) for x in args): + return res + if not isinstance(res, Element): + return res + + p = res.parent() + from sage.rings.all import ZZ, RDF, CDF + if ZZ.has_coerce_map_from(p): + return int(res) + elif RDF.has_coerce_map_from(p): + return float(res) + elif CDF.has_coerce_map_from(p): + return complex(res) + else: + return res cdef _is_registered(self): """ @@ -989,6 +1012,18 @@ cdef class BuiltinFunction(Function): return False + def _evalf_or_eval_(self, *args): + """ + First try to call :meth:`_evalf_` and return the result if it + was not ``None``. Otherwise, call :meth:`_eval0_`, which is the + original version of :meth:`_eval_` saved in :meth:`__init__`. + """ + res = self._evalf_try_(*args) + if res is None: + return self._eval0_(*args) + else: + return res + def __reduce__(self): """ EXAMPLES:: @@ -1426,6 +1461,8 @@ def is_inexact(x): sage: from sage.symbolic.function import is_inexact sage: is_inexact(5) + doctest:...: DeprecationWarning: The is_inexact() function is deprecated, use the _is_numerical() method of the Function class instead + See http://trac.sagemath.org/17130 for details. False sage: is_inexact(5.) True @@ -1436,6 +1473,8 @@ def is_inexact(x): sage: is_inexact(5.4r) True """ + from sage.misc.superseded import deprecation + deprecation(17130, 'The is_inexact() function is deprecated, use the _is_numerical() method of the Function class instead') if isinstance(x, (float, complex)): return True if isinstance(x, Element): diff --git a/src/sage/symbolic/function_factory.py b/src/sage/symbolic/function_factory.py index a72a4ec8f4e..a87a27c3a65 100644 --- a/src/sage/symbolic/function_factory.py +++ b/src/sage/symbolic/function_factory.py @@ -1,5 +1,5 @@ r""" -Symbolic functions +Factory for symbolic functions """ ###############################################################################