Path: blob/master/src/sage/symbolic/function_factory.py
8815 views
r"""1Symbolic functions2"""34###############################################################################5# Sage: Open Source Mathematical Software6# Copyright (C) 2009 Burcin Erocal <[email protected]>7# Distributed under the terms of the GNU General Public License (GPL),8# version 2 or any later version. The full text of the GPL is available at:9# http://www.gnu.org/licenses/10###############################################################################111213from sage.symbolic.function import SymbolicFunction, sfunctions_funcs, \14unpickle_wrapper15from sage.misc.decorators import sage_wraps1617def function_factory(name, nargs=0, latex_name=None, conversions=None,18evalf_params_first=True, eval_func=None, evalf_func=None,19conjugate_func=None, real_part_func=None, imag_part_func=None,20derivative_func=None, tderivative_func=None, power_func=None,21series_func=None, print_func=None, print_latex_func=None):22r"""23Create a formal symbolic function. For an explanation of the arguments see24the documentation for the method :meth:`function`.2526EXAMPLES::2728sage: from sage.symbolic.function_factory import function_factory29sage: f = function_factory('f', 2, '\\foo', {'mathematica':'Foo'})30sage: f(2,4)31f(2, 4)32sage: latex(f(1,2))33\foo\left(1, 2\right)34sage: f._mathematica_init_()35'Foo'3637sage: def evalf_f(self, x, parent=None): return x*.5r38sage: g = function_factory('g',1,evalf_func=evalf_f)39sage: g(2)40g(2)41sage: g(2).n()421.0000000000000043"""44class NewSymbolicFunction(SymbolicFunction):45def __init__(self):46"""47EXAMPLES::4849sage: from sage.symbolic.function_factory import function_factory50sage: f = function_factory('f', 2) # indirect doctest51sage: f(2,4)52f(2, 4)53"""54SymbolicFunction.__init__(self, name, nargs, latex_name,55conversions, evalf_params_first)5657def _maxima_init_(self):58"""59EXAMPLES::6061sage: from sage.symbolic.function_factory import function_factory62sage: f = function_factory('f', 2) # indirect doctest63sage: f._maxima_init_()64"'f"65"""66return "'%s"%self.name()6768def __reduce__(self):69"""70EXAMPLES::7172sage: from sage.symbolic.function_factory import function_factory73sage: f = function_factory('f', 2) # indirect doctest74sage: nf = loads(dumps(f))75sage: nf(1, 2)76f(1, 2)77"""78pickled_functions = self.__getstate__()[6]79return (unpickle_function, (name, nargs, latex_name, conversions,80evalf_params_first, pickled_functions))8182l = locals()83for func_name in sfunctions_funcs:84func = l.get(func_name+"_func", None)85if func:86if not callable(func):87raise ValueError, func_name + "_func" + " parameter must be callable"88setattr(NewSymbolicFunction, '_%s_'%func_name, func)8990return NewSymbolicFunction()9192def unpickle_function(name, nargs, latex_name, conversions, evalf_params_first,93pickled_funcs):94r"""95This is returned by the ``__reduce__`` method of symbolic functions to be96called during unpickling to recreate the given function.9798It calls :meth:`function_factory` with the supplied arguments.99100EXAMPLES::101102sage: from sage.symbolic.function_factory import unpickle_function103sage: nf = unpickle_function('f', 2, '\\foo', {'mathematica':'Foo'}, True, [])104sage: nf105f106sage: nf(1,2)107f(1, 2)108sage: latex(nf(x,x))109\foo\left(x, x\right)110sage: nf._mathematica_init_()111'Foo'112113sage: from sage.symbolic.function import pickle_wrapper114sage: def evalf_f(self, x, parent=None): return 2r*x + 5r115sage: def conjugate_f(self, x): return x/2r116sage: nf = unpickle_function('g', 1, None, None, True, [None, pickle_wrapper(evalf_f), pickle_wrapper(conjugate_f)] + [None]*8)117sage: nf118g119sage: nf(2)120g(2)121sage: nf(2).n()1229.00000000000000123sage: nf(2).conjugate()1241125"""126funcs = map(unpickle_wrapper, pickled_funcs)127args = [name, nargs, latex_name, conversions, evalf_params_first] + funcs128return function_factory(*args)129130def function(s, *args, **kwds):131r"""132Create a formal symbolic function with the name *s*.133134INPUT:135136- ``args`` - arguments to the function, if specified returns the new137function evaluated at the given arguments138- ``nargs=0`` - number of arguments the function accepts, defaults to139variable number of arguments, or 0140- ``latex_name`` - name used when printing in latex mode141- ``conversions`` - a dictionary specifying names of this function in142other systems, this is used by the interfaces internally during conversion143- ``eval_func`` - method used for automatic evaluation144- ``evalf_func`` - method used for numeric evaluation145- ``evalf_params_first`` - bool to indicate if parameters should be146evaluated numerically before calling the custom evalf function147- ``conjugate_func`` - method used for complex conjugation148- ``real_part_func`` - method used when taking real parts149- ``imag_part_func`` - method used when taking imaginary parts150- ``derivative_func`` - method to be used for (partial) derivation151This method should take a keyword argument deriv_param specifying152the index of the argument to differentiate w.r.t153- ``tderivative_func`` - method to be used for derivatives154- ``power_func`` - method used when taking powers155This method should take a keyword argument power_param specifying156the exponent157- ``series_func`` - method used for series expansion158This method should expect keyword arguments159- ``order`` - order for the expansion to be computed160- ``var`` - variable to expand w.r.t.161- ``at`` - expand at this value162- ``print_func`` - method for custom printing163- ``print_latex_func`` - method for custom printing in latex mode164165Note that custom methods must be instance methods, i.e., expect the instance166of the symbolic function as the first argument.167168EXAMPLES::169170sage: var('a, b')171(a, b)172sage: f = function('cr', a)173sage: g = f.diff(a).integral(b)174sage: g175b*D[0](cr)(a)176sage: foo = function("foo", nargs=2)177sage: x,y,z = var("x y z")178sage: foo(x, y) + foo(y, z)^2179foo(y, z)^2 + foo(x, y)180181In Sage 4.0, you need to use :meth:`substitute_function` to182replace all occurrences of a function with another::183184sage: g.substitute_function(cr, cos)185-b*sin(a)186187sage: g.substitute_function(cr, (sin(x) + cos(x)).function(x))188b*(cos(a) - sin(a))189190In Sage 4.0, basic arithmetic with unevaluated functions is no191longer supported::192193sage: x = var('x')194sage: f = function('f')195sage: 2*f196Traceback (most recent call last):197...198TypeError: unsupported operand parent(s) for '*': 'Integer Ring' and '<class 'sage.symbolic.function_factory.NewSymbolicFunction'>'199200You now need to evaluate the function in order to do the arithmetic::201202sage: 2*f(x)2032*f(x)204205206We create a formal function of one variable, write down207an expression that involves first and second derivatives,208and extract off coefficients.209210::211212sage: r, kappa = var('r,kappa')213sage: psi = function('psi', nargs=1)(r); psi214psi(r)215sage: g = 1/r^2*(2*r*psi.derivative(r,1) + r^2*psi.derivative(r,2)); g216(r^2*D[0, 0](psi)(r) + 2*r*D[0](psi)(r))/r^2217sage: g.expand()2182*D[0](psi)(r)/r + D[0, 0](psi)(r)219sage: g.coeff(psi.derivative(r,2))2201221sage: g.coeff(psi.derivative(r,1))2222/r223224Defining custom methods for automatic or numeric evaluation, derivation,225conjugation, etc. is supported::226227228sage: def ev(self, x): return 2*x229sage: foo = function("foo", nargs=1, eval_func=ev)230sage: foo(x)2312*x232sage: foo = function("foo", nargs=1, eval_func=lambda self, x: 5)233sage: foo(x)2345235sage: def ef(self, x): pass236sage: bar = function("bar", nargs=1, eval_func=ef)237sage: bar(x)238bar(x)239240sage: def evalf_f(self, x, parent=None): return 6241sage: foo = function("foo", nargs=1, evalf_func=evalf_f)242sage: foo(x)243foo(x)244sage: foo(x).n()2456246247sage: foo = function("foo", nargs=1, conjugate_func=ev)248sage: foo(x).conjugate()2492*x250251sage: def deriv(self, *args,**kwds): print args, kwds; return args[kwds['diff_param']]^2252sage: foo = function("foo", nargs=2, derivative_func=deriv)253sage: foo(x,y).derivative(y)254(x, y) {'diff_param': 1}255y^2256257sage: def pow(self, x, power_param=None): print x, power_param; return x*power_param258sage: foo = function("foo", nargs=1, power_func=pow)259sage: foo(y)^(x+y)260y x + y261(x + y)*y262263sage: def expand(self, *args, **kwds): print args, kwds; return sum(args[0]^i for i in range(kwds['order']))264sage: foo = function("foo", nargs=1, series_func=expand)265sage: foo(y).series(y, 5)266(y,) {'var': y, 'options': 0, 'at': 0, 'order': 5}267y^4 + y^3 + y^2 + y + 1268269sage: def my_print(self, *args): return "my args are: " + ', '.join(map(repr, args))270sage: foo = function('t', nargs=2, print_func=my_print)271sage: foo(x,y^z)272my args are: x, y^z273274sage: latex(foo(x,y^z))275t\left(x, y^{z}\right)276sage: foo = function('t', nargs=2, print_latex_func=my_print)277sage: foo(x,y^z)278t(x, y^z)279sage: latex(foo(x,y^z))280my args are: x, y^z281sage: foo = function('t', nargs=2, latex_name='foo')282sage: latex(foo(x,y^z))283foo\left(x, y^{z}\right)284285Chain rule::286287sage: def print_args(self, *args, **kwds): print "args:",args; print "kwds:",kwds; return args[0]288sage: foo = function('t', nargs=2, tderivative_func=print_args)289sage: foo(x,x).derivative(x)290args: (x, x)291kwds: {'diff_param': x}292x293sage: foo = function('t', nargs=2, derivative_func=print_args)294sage: foo(x,x).derivative(x)295args: (x, x)296kwds: {'diff_param': 0}297args: (x, x)298kwds: {'diff_param': 1}2992*x300"""301if not isinstance(s, (str, unicode)):302raise TypeError, "expect string as first argument"303304# create the function305if ',' in s:306names = s.split(',')307elif ' ' in s:308names = s.split(' ')309else:310names = [s]311312funcs = [function_factory(name, **kwds) for name in names]313314if len(args) > 0:315res = [f(*args) for f in funcs]316else:317res = funcs318319if len(res) == 1:320return res[0]321return tuple(res)322323def deprecated_custom_evalf_wrapper(func):324"""325This is used while pickling old symbolic functions that define a custom326evalf method.327328The protocol for numeric evaluation functions was changed to include a329``parent`` argument instead of ``prec``. This function creates a wrapper330around the old custom method, which extracts the precision information331from the given ``parent``, and passes it on to the old function.332333EXAMPLES::334335sage: from sage.symbolic.function_factory import deprecated_custom_evalf_wrapper as dcew336sage: def old_func(x, prec=0): print "x: %s, prec: %s"%(x,prec)337sage: new_func = dcew(old_func)338sage: new_func(5, parent=RR)339x: 5, prec: 53340sage: new_func(0r, parent=ComplexField(100))341x: 0, prec: 100342"""343def new_evalf(*args, **kwds):344parent = kwds['parent']345if parent:346prec = parent.prec()347else:348prec = 53349return func(*args, prec=prec)350return new_evalf351352353# This code is used when constructing dynamic methods for symbolic354# expressions representing evaluated symbolic functions. See355# get_dynamic_class_for_function in sage/symbolic/expression.pyx.356# Since Cython does not support closures, this needs to live in a Python357# file. This file is the only pure Python file we have related to symbolic358# functions.359def eval_on_operands(f):360"""361Given a method ``f`` return a new method which takes a single symbolic362expression argument and passes the operands of the given expression as363arguments to ``f``.364365EXAMPLES::366367sage: def f(x, y):368....: '''369....: Some documentation.370....: '''371....: return x + 2*y372....:373sage: f(x, 1)374x + 2375sage: from sage.symbolic.function_factory import eval_on_operands376sage: g = eval_on_operands(f)377sage: g(x + 1)378x + 2379sage: g.__doc__.strip()380'Some documentation.'381"""382@sage_wraps(f)383def new_f(ex):384return f(*ex.operands())385return new_f386387388