Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
r""" Classes for symbolic functions """
#***************************************************************************** # Copyright (C) 2008 - 2010 Burcin Erocal <burcin@erocal.org> # Copyright (C) 2008 William Stein <wstein@gmail.com> # Copyright (C) 2016 Vincent Delecroix <vincent.delecroix@u-bordeaux.fr> # # 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/ #***************************************************************************** from __future__ import division, absolute_import
from sage.libs.pynac.pynac cimport * from sage.rings.integer cimport smallInteger from sage.structure.sage_object cimport SageObject from sage.structure.element cimport Element, parent from .expression cimport new_Expression_from_GEx, Expression from .ring import SR
from sage.structure.coerce cimport py_scalar_to_element, is_numpy_type, is_mpmath_type from sage.structure.element cimport coercion_model from sage.structure.richcmp cimport richcmp
# we keep a database of symbolic functions initialized in a session # this also makes the .operator() method of symbolic expressions work cdef dict sfunction_serial_dict = {}
from sage.misc.fpickle import pickle_function, unpickle_function from sage.cpython.string cimport str_to_bytes from sage.ext.fast_eval import FastDoubleFunc
# List of functions which ginac allows us to define custom behavior for. # Changing the order of this list could cause problems unpickling old pickles. sfunctions_funcs = ['eval', 'evalf', 'conjugate', 'real_part', 'imag_part', 'derivative', 'power', 'series', 'print', 'print_latex', 'tderivative']
cdef class Function(SageObject): """ Base class for symbolic functions defined through Pynac in Sage.
This is an abstract base class, with generic code for the interfaces and a :meth:`__call__` method. Subclasses should implement the :meth:`_is_registered` and :meth:`_register_function` methods.
This class is not intended for direct use, instead use one of the subclasses :class:`BuiltinFunction` or :class:`SymbolicFunction`. """ def __init__(self, name, nargs, latex_name=None, conversions=None, evalf_params_first=True, alt_name=None): """ This is an abstract base class. It's not possible to test it directly.
EXAMPLES::
sage: f = function('f', nargs=1, conjugate_func=lambda self,x: 2r*x) # indirect doctest sage: f(2) f(2) sage: f(2).conjugate() 4
TESTS::
# eval_func raises exception sage: def ef(self, x): raise RuntimeError("foo") sage: bar = function("bar", nargs=1, eval_func=ef) sage: bar(x) Traceback (most recent call last): ... RuntimeError: foo
# eval_func returns non coercible sage: def ef(self, x): return ZZ sage: bar = function("bar", nargs=1, eval_func=ef) sage: bar(x) Traceback (most recent call last): ... TypeError: function did not return a symbolic expression or an element that can be coerced into a symbolic expression
# eval_func is not callable sage: bar = function("bar", nargs=1, eval_func=5) Traceback (most recent call last): ... ValueError: eval_func parameter must be callable """
# handle custom printing # if print_func is defined, it is used instead of name # latex printing can be customised either by setting a string latex_name # or giving a custom function argument print_latex_func raise ValueError("only one of latex_name or _print_latex_ should be specified.")
# only one of derivative and tderivative should be defined raise ValueError("only one of _derivative_ or _tderivative_ should be defined.")
raise ValueError(real_fname + " parameter must be callable")
global sfunction_serial_dict
cdef _is_registered(self): """ Check if this function is already registered. If it is, set `self._serial` to the right value. """ raise NotImplementedError("this is an abstract base class, it shouldn't be initialized directly")
cdef _register_function(self): """
TESTS:
After :trac:`9240`, pickling and unpickling of symbolic functions was broken. We check here that this is fixed (:trac:`11919`)::
sage: f = function('f')(x) sage: s = dumps(f) sage: loads(s) f(x) sage: deepcopy(f) f(x)
""" cdef GFunctionOpt opt
opt.do_not_evalf_params()
opt.real_part_func(self)
opt.imag_part_func(self)
# custom print functions are called from python # so we don't register them with the ginac function_options object
def _evalf_try_(self, *args): """ 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.
TESTS::
sage: coth(5) # indirect doctest coth(5) sage: coth(0.5) 2.16395341373865 sage: from sage.symbolic.function import BuiltinFunction sage: class Test(BuiltinFunction): ....: def __init__(self): ....: BuiltinFunction.__init__(self, 'test', nargs=2) ....: def _evalf_(self, x, y, parent): ....: return x + 1 ....: def _eval_(self, x, y): ....: res = self._evalf_try_(x, y) ....: if res: ....: return res ....: elif x == 2: ....: return 3 ....: else: ....: return sage: test = Test() sage: test(1.3, 4) 2.30000000000000 sage: test(pi, 4) test(pi, 4) sage: test(2, x) 3 sage: test(2., 4) 3.00000000000000 sage: test(1 + 1.0*I, 2) 2.00000000000000 + 1.00000000000000*I sage: class Test2(BuiltinFunction): ....: def __init__(self): ....: BuiltinFunction.__init__(self, 'test', nargs=1) ....: def _evalf_(self, x, parent): ....: return 0.5 ....: def _eval_(self, x): ....: res = self._evalf_try_(x) ....: if res: ....: return res ....: else: ....: return 3 sage: test2 = Test2() sage: test2(1.3) 0.500000000000000 sage: test2(pi) 3 """ # If any of the inputs is numerical and none is symbolic, # try to call _evalf_() directly pass
def __hash__(self): """ EXAMPLES::
sage: f = function('f', nargs=1, conjugate_func=lambda self,x: 2r*x) sage: f.__hash__() #random -2224334885124003860 sage: hash(f(2)) #random 4168614485 """
def __repr__(self): """ EXAMPLES::
sage: foo = function("foo", nargs=2); foo foo """
def _latex_(self): r""" EXAMPLES::
sage: from sage.symbolic.function import SymbolicFunction sage: s = SymbolicFunction('foo'); s foo sage: latex(s) foo sage: s = SymbolicFunction('foo', latex_name=r'{\rm foo}') sage: latex(s) {\rm foo} sage: s._latex_() '{\\rm foo}' """ else:
def __richcmp__(self, other, op): """ TESTS::
sage: foo = function("foo", nargs=2) sage: foo == foo True sage: foo == 2 False sage: foo(1,2).operator() == foo True
""" except AttributeError: return NotImplemented
def __call__(self, *args, bint coerce=True, bint hold=False): """ Evaluates this function at the given arguments.
We coerce the arguments into symbolic expressions if coerce=True, then call the Pynac evaluation method, which in turn passes the arguments to a custom automatic evaluation method if ``_eval_()`` is defined.
EXAMPLES::
sage: foo = function("foo", nargs=2) sage: x,y,z = var("x y z") sage: foo(x,y) foo(x, y)
sage: foo(y) Traceback (most recent call last): ... TypeError: Symbolic function foo takes exactly 2 arguments (1 given)
sage: bar = function("bar") sage: bar(x) bar(x) sage: bar(x,y) bar(x, y)
The `hold` argument prevents automatic evaluation of the function::
sage: exp(log(x)) x sage: exp(log(x), hold=True) e^log(x)
We can also handle numpy types::
sage: import numpy sage: sin(numpy.arange(5)) array([ 0. , 0.84147098, 0.90929743, 0.14112001, -0.7568025 ])
Symbolic functions evaluate non-exact input numerically, and return symbolic expressions on exact input, or if any input is symbolic::
sage: arctan(1) 1/4*pi sage: arctan(float(1)) 0.7853981633974483 sage: type(lambert_w(SR(0))) <type 'sage.symbolic.expression.Expression'>
Precision of the result depends on the precision of the input::
sage: arctan(RR(1)) 0.785398163397448 sage: arctan(RealField(100)(1)) 0.78539816339744830961566084582
Return types for non-exact input depends on the input type::
sage: type(exp(float(0))) <... 'float'> sage: exp(RR(0)).parent() Real Field with 53 bits of precision
TESTS:
Test coercion::
sage: bar(ZZ) Traceback (most recent call last): ... TypeError: cannot coerce arguments: ... sage: exp(QQbar(I)) e^I
For functions with single argument, if coercion fails we try to call a method with the name of the function on the object::
sage: M = matrix(SR, 2, 2, [x, 0, 0, I*pi]) sage: exp(M) [e^x 0] [ 0 -1]
Make sure we can pass mpmath arguments (:trac:`13608`)::
sage: import mpmath sage: with mpmath.workprec(128): sin(mpmath.mpc('0.5', '1.2')) mpc(real='0.86807452059118713192871150787046523179886', imag='1.3246769633571289324095313649562791720086')
Check that :trac:`10133` is fixed::
sage: out = sin(0) sage: out, parent(out) (0, Integer Ring) sage: out = sin(int(0)) sage: (out, parent(out)) (0, <... 'int'>) sage: out = arctan2(int(0), float(1)) sage: (out, parent(out)) (0, <... 'int'>) sage: out = arctan2(int(0), RR(1)) sage: (out, parent(out)) (0, Integer Ring)
Check that `real_part` and `imag_part` still works after :trac:`21216`::
sage: import numpy sage: a = numpy.array([1+2*I, -2-3*I], dtype=numpy.complex) sage: real_part(a) array([ 1., -2.]) sage: imag_part(a) array([ 2., -3.]) """
# support fast_float else: return method()
# if the given input is a symbolic expression, we don't convert it back # to a numeric type at the end else:
cdef Py_ssize_t i # If the function takes only one argument, we try to call # a method with the name of this function on the object. # This makes the following work: # sage: M = matrix(SR, 2, 2, [x, 0, 0, I*pi]) # sage: exp(M) # [e^x 0] # [ 0 -1] return method()
else: # coerce == False for a in args: if not isinstance(a, Expression): raise TypeError("arguments must be symbolic expressions")
cdef GEx res cdef GExVector vec
def name(self): """ Returns the name of this function.
EXAMPLES::
sage: foo = function("foo", nargs=2) sage: foo.name() 'foo' """
def number_of_arguments(self): """ Returns the number of arguments that this function takes.
EXAMPLES::
sage: foo = function("foo", nargs=2) sage: foo.number_of_arguments() 2 sage: foo(x,x) foo(x, x)
sage: foo(x) Traceback (most recent call last): ... TypeError: Symbolic function foo takes exactly 2 arguments (1 given) """
def variables(self): """ Returns the variables (of which there are none) present in this SFunction.
EXAMPLES::
sage: sin.variables() () """
def default_variable(self): """ Returns a default variable.
EXAMPLES::
sage: sin.default_variable() 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 """
def _interface_init_(self, I=None): """ EXAMPLES::
sage: sin._interface_init_(maxima) 'sin' """ return self._name
def _mathematica_init_(self): """ EXAMPLES::
sage: sin._mathematica_init_() 'Sin' sage: exp._mathematica_init_() 'Exp' sage: (exp(x) + sin(x) + tan(x))._mathematica_init_() '(Exp[x])+(Sin[x])+(Tan[x])' """
def _sympy_init_(self, I=None): """ EXAMPLES::
sage: arcsin._sympy_init_() 'asin' sage: from sage.symbolic.function import SymbolicFunction sage: g = SymbolicFunction('g', conversions=dict(sympy='gg')) sage: g._sympy_init_() 'gg' sage: g(x)._sympy_() gg(x) """
def _maxima_init_(self, I=None): """ EXAMPLES::
sage: exp._maxima_init_() 'exp' sage: from sage.symbolic.function import SymbolicFunction sage: f = SymbolicFunction('f', latex_name='f', conversions=dict(maxima='ff')) sage: f._maxima_init_() 'ff' """
def _fast_float_(self, *vars): """ Returns an object which provides fast floating point evaluation of self.
See sage.ext.fast_eval? for more information.
EXAMPLES::
sage: sin._fast_float_() <sage.ext.fast_eval.FastDoubleFunc object at 0x...> sage: sin._fast_float_()(0) 0.0
::
sage: ff = cos._fast_float_(); ff <sage.ext.fast_eval.FastDoubleFunc object at 0x...> sage: ff.is_pure_c() True sage: ff(0) 1.0
::
sage: ff = erf._fast_float_() sage: ff.is_pure_c() False sage: ff(1.5) # tol 1e-15 0.9661051464753108 sage: erf(1.5) 0.966105146475311 """
def _fast_callable_(self, etb): r""" Given an ExpressionTreeBuilder, return an Expression representing this value.
EXAMPLES::
sage: from sage.ext.fast_callable import ExpressionTreeBuilder sage: etb = ExpressionTreeBuilder(vars=['x','y']) sage: sin._fast_callable_(etb) sin(v_0) sage: erf._fast_callable_(etb) {erf}(v_0) """
def _eval_numpy_(self, *args): r""" Evaluates this function at the given arguments.
At least one of elements of args is supposed to be a numpy array.
EXAMPLES::
sage: import numpy sage: a = numpy.arange(5) sage: csc(a) doctest:...: RuntimeWarning: divide by zero encountered in ...divide array([ inf, 1.18839511, 1.09975017, 7.0861674 , -1.32134871])
sage: factorial(a) Traceback (most recent call last): ... NotImplementedError: The Function factorial does not support numpy arrays as arguments """
def _eval_mpmath_(self, *args): r""" Evaluates this function for arguments of mpmath types.
This is only called when no such mpmath function exists. It casts its arguments to sage reals of the appropriate precision.
EXAMPLES::
At the time of this writing, mpmath had no arcsin, only asin. So the following call would actually fall back to the default implementation, using sage reals instead of mpmath ones. This might change when aliases for these functions are established.
sage: import mpmath sage: with mpmath.workprec(128): arcsin(mpmath.mpf('0.5')) mpf('0.52359877559829887307710723054658381403157')
TESTS:
To ensure that we actually can fall back to an implementation not using mpmath, we have to create a custom function which will certainly never get created in mpmath. ::
sage: import mpmath sage: from sage.symbolic.function import BuiltinFunction sage: class NoMpmathFn(BuiltinFunction): ....: def _eval_(self, arg): ....: parent = arg.parent() ....: prec = parent.prec() ....: assert parent == RealField(prec) ....: return prec sage: noMpmathFn = NoMpmathFn("noMpmathFn") sage: with mpmath.workprec(64): noMpmathFn(sqrt(mpmath.mpf('2'))) 64 sage: mpmath.noMpmathFn = lambda x: 123 sage: with mpmath.workprec(64): noMpmathFn(sqrt(mpmath.mpf('2'))) 123 sage: del mpmath.noMpmathFn
"""
cdef class GinacFunction(BuiltinFunction): """ This class provides a wrapper around symbolic functions already defined in Pynac/GiNaC.
GiNaC provides custom methods for these functions defined at the C++ level. It is still possible to define new custom functionality or override those already defined.
There is also no need to register these functions. """ def __init__(self, name, nargs=1, latex_name=None, conversions=None, ginac_name=None, evalf_params_first=True, preserved_arg=None, alt_name=None): """ TESTS::
sage: from sage.functions.trig import Function_sin sage: s = Function_sin() # indirect doctest sage: s(0) 0 sage: s(pi) 0 sage: s(pi/2) 1 """
cdef _is_registered(self): # Since this is function is defined in C++, it is already in # ginac's function registry # get serial except RuntimeError as err: raise ValueError("cannot find GiNaC function with name %s and %s arguments" % (fname, self._nargs))
global sfunction_serial_dict
cdef _register_function(self): # We don't need to add anything to GiNaC's function registry # However, if any custom methods were provided in the python class, # we should set the properties of the function_options object # corresponding to this function cdef GFunctionOpt opt = g_registered_functions().index(self._serial)
if hasattr(self, '_eval_'): opt.eval_func(self)
if not self._evalf_params_first: opt.do_not_evalf_params()
if hasattr(self, '_evalf_'): opt.evalf_func(self)
if hasattr(self, '_conjugate_'): opt.conjugate_func(self)
if hasattr(self, '_real_part_'): opt.real_part_func(self)
if hasattr(self, '_imag_part_'): opt.imag_part_func(self)
if hasattr(self, '_derivative_'): opt.derivative_func(self)
if hasattr(self, '_tderivative_'): opt.do_not_apply_chain_rule() opt.derivative_func(self)
if hasattr(self, '_power_'): opt.power_func(self)
if hasattr(self, '_series_'): opt.series_func(self)
# overriding print functions is not supported
if self._latex_name: opt.latex_name(str_to_bytes(self._latex_name))
g_foptions_assign(g_registered_functions().index(self._serial), opt)
cdef class BuiltinFunction(Function): """ This is the base class for symbolic functions defined in Sage.
If a function is provided by the Sage library, we don't need to pickle the custom methods, since we can just initialize the same library function again. This allows us to use Cython for custom methods.
We assume that each subclass of this class will define one symbolic function. Make sure you use subclasses and not just call the initializer of this class. """ def __init__(self, name, nargs=1, latex_name=None, conversions=None, evalf_params_first=True, alt_name=None, preserved_arg=None): """ TESTS::
sage: from sage.functions.trig import Function_cot sage: c = Function_cot() # indirect doctest sage: c(pi/2) 0 """ raise ValueError("preserved_arg must be between 1 and nargs")
# If we have an _evalf_ method, change _eval_ to a # wrapper function which first tries to call _evalf_. else:
def __call__(self, *args, bint coerce=True, bint hold=False, bint dont_call_method_on_arg=False): r""" Evaluate this function on the given arguments and return the result.
EXAMPLES::
sage: exp(5) e^5 sage: gamma(15) 87178291200
Python float, Python complex, mpmath mpf and mpc as well as numpy inputs are sent to the relevant ``math``, ``cmath``, ``mpmath`` or ``numpy`` function::
sage: cos(1.r) 0.5403023058681398 sage: assert type(_) is float sage: gamma(4.r) 6.0 sage: assert type(_) is float
sage: cos(1jr) # abstol 1e-15 (1.5430806348152437-0j) sage: assert type(_) is complex
sage: import mpmath sage: cos(mpmath.mpf('1.321412')) mpf('0.24680737898640387') sage: cos(mpmath.mpc(1,1)) mpc(real='0.83373002513114902', imag='-0.98889770576286506')
sage: import numpy sage: sin(numpy.int32(0)) 0.0 sage: type(_) <type 'numpy.float64'>
TESTS::
sage: from sage.symbolic.function import BuiltinFunction sage: class A: ....: def foo(self): ....: return 'foo' sage: foo = BuiltinFunction(name='foo') sage: foo(A()) 'foo' sage: bar = BuiltinFunction(name='bar', alt_name='foo') sage: bar(A()) 'foo' """ # try calling the relevant math, cmath, mpmath or numpy function. # And as a fallback try the custom self._eval_numpy_ or # self._eval_mpmath_
pass
# then try to see whether there exists a method on the object with # the given name
# Convert the output back to the corresponding # Python type if possible. except TypeError: return res else:
else:
cdef _is_registered(self): """ TESTS:
Check if :trac:`13586` is fixed::
sage: from sage.symbolic.function import BuiltinFunction sage: class AFunction(BuiltinFunction): ....: def __init__(self, name, exp=1): ....: self.exponent=exp ....: BuiltinFunction.__init__(self, name, nargs=1) ....: def _eval_(self, arg): ....: return arg**self.exponent sage: p2 = AFunction('p2', 2) sage: p2(x) x^2 sage: p3 = AFunction('p3', 3) sage: p3(x) x^3 sage: loads(dumps(cot)) == cot # trac #15138 True """ # check if already defined
# search ginac registry for name and nargs pass
# if match, get operator from function table global sfunction_serial_dict # if the returned function is of the same type
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__`. """ else:
def __reduce__(self): """ EXAMPLES::
sage: cot.__reduce__() (<class 'sage.functions.trig.Function_cot'>, ())
sage: f = loads(dumps(cot)) #indirect doctest sage: f(pi/2) 0 """
# this is required to read old pickles of erf, elliptic_ec, etc. def __setstate__(self, state): """ EXAMPLES::
sage: cot.__setstate__([1,0]) Traceback (most recent call last): ... ValueError: cannot read pickle sage: cot.__setstate__([0]) #don't try this at home """ # old pickle data # we call __init__ since Python only allocates the class and does # not call __init__ before passing the pickled state to __setstate__ else: # we should never end up here
cdef class SymbolicFunction(Function): """ This is the basis for user defined symbolic functions. We try to pickle or hash the custom methods, so subclasses must be defined in Python not Cython. """ def __init__(self, name, nargs=0, latex_name=None, conversions=None, evalf_params_first=True): """ EXAMPLES::
sage: from sage.symbolic.function import SymbolicFunction sage: class my_function(SymbolicFunction): ....: def __init__(self): ....: SymbolicFunction.__init__(self, 'foo', nargs=2) ....: def _evalf_(self, x, y, parent=None, algorithm=None): ....: return x*y*2r ....: def _conjugate_(self, x, y): ....: return x sage: foo = my_function() sage: foo foo sage: foo(2,3) foo(2, 3) sage: foo(2,3).n() 12.0000000000000 sage: foo(2,3).conjugate() 2 """
cdef _is_registered(SymbolicFunction self): # see if there is already an SFunction with the same state cdef Function sfunc # found one, set self._serial to be a copy
# cache the hash value of this function # this is used very often while unpickling to see if there is already # a function with the same properties cdef long _hash_(self) except -1: # create a string representation of this SFunction else:
def __hash__(self): """ TESTS::
sage: foo = function("foo", nargs=2) sage: hash(foo) # random output -6859868030555295348
sage: def ev(self, x): return 2*x sage: foo = function("foo", nargs=2, eval_func = ev) sage: hash(foo) # random output -6859868030555295348 """
def __getstate__(self): """ Returns a tuple describing the state of this object for pickling.
Pickling SFunction objects is limited by the ability to pickle functions in python. We use sage.misc.fpickle.pickle_function for this purpose, which only works if there are no nested functions.
This should return all information that will be required to unpickle the object. The functionality for unpickling is implemented in __setstate__().
In order to pickle SFunction objects, we return a tuple containing
* 0 - as pickle version number in case we decide to change the pickle format in the feature * name of this function * number of arguments * latex_name * a tuple containing attempts to pickle the following optional functions, in the order below * eval_f * evalf_f * conjugate_f * real_part_f * imag_part_f * derivative_f * power_f * series_f * print_f * print_latex_f
EXAMPLES::
sage: foo = function("foo", nargs=2) sage: foo.__getstate__() (2, 'foo', 2, None, {}, True, [None, None, None, None, None, None, None, None, None, None, None]) sage: t = loads(dumps(foo)) sage: t == foo True sage: var('x,y') (x, y) sage: t(x,y) foo(x, y)
sage: def ev(self, x,y): return 2*x sage: foo = function("foo", nargs=2, eval_func = ev) sage: foo.__getstate__() (2, 'foo', 2, None, {}, True, [..., None, None, None, None, None, None, None, None, None, None])
sage: u = loads(dumps(foo)) sage: u == foo True sage: t == u False sage: u(y,x) 2*y
sage: def evalf_f(self, x, **kwds): return int(6) sage: foo = function("foo", nargs=1, evalf_func=evalf_f) sage: foo.__getstate__() (2, 'foo', 1, None, {}, True, [None, ..., None, None, None, None, None, None, None, None, None])
sage: v = loads(dumps(foo)) sage: v == foo True sage: v == u False sage: foo(y).n() 6 sage: v(y).n() 6
Test pickling expressions with symbolic functions::
sage: u = loads(dumps(foo(x)^2 + foo(y) + x^y)); u foo(x)^2 + x^y + foo(y) sage: u.subs(y=0) foo(x)^2 + foo(0) + 1 sage: u.subs(y=0).n() 43.0000000000000 """ self._evalf_params_first,
def __setstate__(self, state): """ Initializes the state of the object from data saved in a pickle.
During unpickling __init__ methods of classes are not called, the saved data is passed to the class via this function instead.
TESTS::
sage: var('x,y') (x, y) sage: foo = function("foo", nargs=2) sage: bar = function("bar", nargs=1) sage: bar.__setstate__(foo.__getstate__())
::
sage: g = function('g', nargs=1, conjugate_func=lambda y,x: 2*x) sage: st = g.__getstate__() sage: f = function('f') sage: f(x) f(x) sage: f(x).conjugate() # no special conjugate method conjugate(f(x)) sage: f.__setstate__(st) sage: f(x+1).conjugate() # now there is a special method 2*x + 2
Note that the other direction doesn't work here, since foo._hash_() hash already been initialized.::
sage: bar foo sage: bar(x,y) foo(x, y) """ # check input raise ValueError("unknown state information")
evalf_params_first = True function_pickles = state[5]
cdef class DeprecatedSFunction(SymbolicFunction): cdef dict __dict__ def __init__(self, name, nargs=0, latex_name=None): """ EXAMPLES::
sage: from sage.symbolic.function import DeprecatedSFunction sage: foo = DeprecatedSFunction("foo", 2) sage: foo foo sage: foo(x,2) foo(x, 2) sage: foo(2) Traceback (most recent call last): ... TypeError: Symbolic function foo takes exactly 2 arguments (1 given) """
def __getattr__(self, attr): """ This method allows us to access attributes set by :meth:`__setattr__`.
EXAMPLES::
sage: from sage.symbolic.function import DeprecatedSFunction sage: foo = DeprecatedSFunction("foo", 2) sage: foo.bar = 4 sage: foo.bar 4 """
def __setattr__(self, attr, value): """ This method allows us to store arbitrary Python attributes on symbolic functions which is normally not possible with Cython extension types.
EXAMPLES::
sage: from sage.symbolic.function import DeprecatedSFunction sage: foo = DeprecatedSFunction("foo", 2) sage: foo.bar = 4 sage: foo.bar 4 """
def __reduce__(self): """ EXAMPLES::
sage: from sage.symbolic.function import DeprecatedSFunction sage: foo = DeprecatedSFunction("foo", 2) sage: foo.__reduce__() (<function unpickle_function at ...>, ('foo', 2, None, {}, True, [None, None, None, None, None, None, None, None, None, None, None])) """
def __setstate__(self, state): """ EXAMPLES::
sage: from sage.symbolic.function import DeprecatedSFunction sage: foo = DeprecatedSFunction("foo", 2) sage: foo.__setstate__([0, 'bar', 1, '\\bar', [None]*10]) sage: foo bar sage: foo(x) bar(x) sage: latex(foo(x)) \bar\left(x\right) """ if fname == 'evalf': from sage.symbolic.function_factory import \ deprecated_custom_evalf_wrapper setattr(self, '_evalf_', deprecated_custom_evalf_wrapper( unpickle_function(pickle))) continue real_fname = '_%s_'%fname setattr(self, real_fname, unpickle_function(pickle))
SFunction = DeprecatedSFunction PrimitiveFunction = DeprecatedSFunction
def get_sfunction_from_serial(serial): """ Returns an already created SFunction given the serial. These are stored in the dictionary `sage.symbolic.function.sfunction_serial_dict`.
EXAMPLES::
sage: from sage.symbolic.function import get_sfunction_from_serial sage: get_sfunction_from_serial(65) #random f """ global sfunction_serial_dict
def pickle_wrapper(f): """ Returns a pickled version of the function f if f is not None; otherwise, it returns None. This is a wrapper around :func:`pickle_function`.
EXAMPLES::
sage: from sage.symbolic.function import pickle_wrapper sage: def f(x): return x*x sage: isinstance(pickle_wrapper(f), bytes) True sage: pickle_wrapper(None) is None True """
def unpickle_wrapper(p): """ Returns a unpickled version of the function defined by *p* if *p* is not None; otherwise, it returns None. This is a wrapper around :func:`unpickle_function`.
EXAMPLES::
sage: from sage.symbolic.function import pickle_wrapper, unpickle_wrapper sage: def f(x): return x*x sage: s = pickle_wrapper(f) sage: g = unpickle_wrapper(s) sage: g(2) 4 sage: unpickle_wrapper(None) is None True """
|