Ticket #13608: trac_13608_mpmath.patch

File trac_13608_mpmath.patch, 7.6 KB (added by Martin von Gagern, 9 years ago)
  • sage/functions/log.py

    # HG changeset patch
    # User Martin von Gagern <Martin.vGagern@gmx.net>
    # Date 1374613494 -7200
    #      Tue Jul 23 23:04:54 2013 +0200
    # Node ID 6ba26b2d1e63d6963069df8c6e3bce61946d1f97
    # Parent  0f8fd922eaed351e39f913f1317d319dcceb4c01
    Trac 13608: mpmath integration for builtin functions
    
    diff --git a/sage/functions/log.py b/sage/functions/log.py
    a b  
    130130            doctest:...: DeprecationWarning: The prec keyword argument is deprecated. Explicitly set the precision of the input, for example exp(RealField(300)(1)), or use the prec argument to .n() for exact inputs, e.g., exp(1).n(300), instead.
    131131            See http://trac.sagemath.org/7490 for details.
    132132            7.3890560989306502272304274606
     133
     134        Ensure that :trac:`13608` is fixed::
     135
     136            sage: import mpmath
     137            sage: a = mpmath.mpf('0.5')
     138            sage: exp(a)
     139            mpf('1.6487212707001282')
     140            sage: a.exp
     141            -1
    133142        """
    134143        if prec is not None:
    135144            from sage.misc.superseded import deprecation
  • sage/symbolic/function.pyx

    diff --git a/sage/symbolic/function.pyx b/sage/symbolic/function.pyx
    a b  
    208208            if x.parent().is_exact():
    209209                return None
    210210        try:
    211             return getattr(x, self.name())()
     211            memberfn = getattr(x, self.name())
    212212        except AttributeError:
    213213            pass
     214        else:
     215            return memberfn()
    214216
    215217    def __hash__(self):
    216218        """
     
    349351            sage: exp(M)
    350352            [e^x   0]
    351353            [  0  -1]
     354
     355        Make sure we can pass mpmath arguments (:trac:`13608`)::
     356
     357            sage: import mpmath
     358            sage: with mpmath.workprec(128): sin(mpmath.mpc('0.5', '1.2'))
     359            mpc(real='0.86807452059118713192871150787046523179886', imag='1.3246769633571289324095313649562791720086')
     360
    352361        """
    353362        if self._nargs > 0 and len(args) != self._nargs:
    354363            raise TypeError, "Symbolic function %s takes exactly %s arguments (%s given)"%(self._name, self._nargs, len(args))
     
    357366        if self._nargs == 1:
    358367            if isinstance(args[0], FastDoubleFunc):
    359368                try:
    360                     return getattr(args[0], self._name)()
     369                    memberfn = getattr(args[0], self._name)
    361370                except AttributeError, err:
    362371                    raise TypeError, "cannot handle fast float arguments"
     372                else:
     373                    return memberfn()
    363374
    364375        # support numpy arrays as arguments
    365376        if any([type(arg).__module__ == 'numpy' for arg in args]): # avoid importing
     
    367378            # check that at least one of the arguments is a numpy array
    368379            if any([isinstance(arg, numpy.ndarray) for arg in args]):
    369380                try:
    370                     return getattr(numpy, self.name())(*args)
     381                    modulefn = getattr(numpy, self.name())
    371382                except AttributeError:
    372383                    return self._eval_numpy_(*args)
     384                else:
     385                    return modulefn(*args)
     386
     387        # support mpmath mpf and mpc numbers as arguments
     388        if any(['mpmath' in type(arg).__module__ for arg in args]): # avoid importing
     389            import mpmath
     390            # check that at least one of the arguments is an mpmath type
     391            if any([isinstance(arg, (mpmath.mpf, mpmath.mpc)) for arg in args]):
     392                try:
     393                    modulefn = getattr(mpmath, self.name())
     394                except AttributeError:
     395                    return self._eval_mpmath_(*args)
     396                else:
     397                    return modulefn(*args)
    373398
    374399        # if the given input is a symbolic expression, we don't convert it back
    375400        # to a numeric type at the end
     
    388413                # a method with the name of this function on the object.
    389414                # This makes the following work:
    390415                #     sage: M = matrix(SR, 2, 2, [x, 0, 0, I*pi])
     416                #     sage: exp(M)
    391417                #     [e^x   0]
    392418                #     [  0  -1]
    393                 if len(args) == 1:
    394                     try:
    395                         return getattr(args[0], self._name)()
    396                     except AttributeError:
    397                         pass
     419                if len(args) == 1:
     420                    memberfn = getattr(args[0], self._name, None)
     421                    if callable(memberfn):
     422                        return memberfn()
    398423
    399424                # There is no natural coercion from QQbar to the symbolic ring
    400425                # in order to support
     
    632657        """
    633658        raise NotImplementedError("The Function %s does not support numpy arrays as arguments" % self.name())
    634659
     660    def _eval_mpmath_(self, *args):
     661        r"""
     662        Evaluates this function for arguments of mpmath types.
     663
     664        The default implementation casts its arguments to sage reals
     665        of the appropriate precision.
     666
     667        EXAMPLES::
     668
     669        At the time of this writing, mpmath had no arcsin, only asin.
     670        So the following call would actually fall back to the default
     671        implementation, using sage reals instead of mpmath ones. This
     672        might change when aliases for these functions are established.
     673
     674            sage: import mpmath
     675            sage: with mpmath.workprec(128): arcsin(mpmath.mpf('0.5'))
     676            mpf('0.52359877559829887307710723054658381403157')
     677
     678        TESTS:
     679
     680        To ensure that we actually can fall back to an implementation
     681        not using mpmath, we have to create a custom function which
     682        will certainly never get created in mpmath.
     683
     684            sage: import mpmath
     685            sage: from sage.symbolic.function import BuiltinFunction
     686            sage: class NoMpmathFn(BuiltinFunction):
     687            ....:         def _eval_(self, arg):
     688            ....:                 parent = arg.parent()
     689            ....:                 prec = parent.prec()
     690            ....:                 assert parent == RealField(prec)
     691            ....:                 return prec
     692            sage: noMpmathFn = NoMpmathFn("noMpmathFn")
     693            sage: with mpmath.workprec(64): noMpmathFn(sqrt(mpmath.mpf('2')))
     694            64
     695            sage: mpmath.noMpmathFn = lambda x: 123
     696            sage: with mpmath.workprec(64): noMpmathFn(sqrt(mpmath.mpf('2')))
     697            123
     698            sage: del mpmath.noMpmathFn
     699
     700        """
     701        import mpmath
     702        from sage.libs.mpmath.utils import mpmath_to_sage, sage_to_mpmath
     703        prec = mpmath.mp.prec
     704        args = [mpmath_to_sage(x, prec)
     705                if isinstance(x, (mpmath.mpf, mpmath.mpc)) else x
     706                for x in args]
     707        res = self(*args)
     708        res = sage_to_mpmath(res, prec)
     709        return res
     710
    635711cdef class GinacFunction(BuiltinFunction):
    636712    """
    637713    This class provides a wrapper around symbolic functions already defined in
     
    740816        # The argument dont_call_method_on_arg is used to prevent infinite loops
    741817        # when .exp(), .log(), etc. methods call this symbolic function on
    742818        # themselves
    743         if len(args) == 1 and not hold and not dont_call_method_on_arg and \
    744                 hasattr(args[0], self._name):
    745             return getattr(args[0], self._name)()
     819        if len(args) == 1 and not hold and not dont_call_method_on_arg:
     820            memberfn = getattr(args[0], self._name, None)
     821            if callable(memberfn):
     822                return memberfn()
    746823
    747824        res = super(GinacFunction, self).__call__(*args, coerce=coerce,
    748825                hold=hold)