Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/symbolic/function_factory.py
8815 views
1
r"""
2
Symbolic functions
3
"""
4
5
###############################################################################
6
# Sage: Open Source Mathematical Software
7
# Copyright (C) 2009 Burcin Erocal <[email protected]>
8
# Distributed under the terms of the GNU General Public License (GPL),
9
# version 2 or any later version. The full text of the GPL is available at:
10
# http://www.gnu.org/licenses/
11
###############################################################################
12
13
14
from sage.symbolic.function import SymbolicFunction, sfunctions_funcs, \
15
unpickle_wrapper
16
from sage.misc.decorators import sage_wraps
17
18
def function_factory(name, nargs=0, latex_name=None, conversions=None,
19
evalf_params_first=True, eval_func=None, evalf_func=None,
20
conjugate_func=None, real_part_func=None, imag_part_func=None,
21
derivative_func=None, tderivative_func=None, power_func=None,
22
series_func=None, print_func=None, print_latex_func=None):
23
r"""
24
Create a formal symbolic function. For an explanation of the arguments see
25
the documentation for the method :meth:`function`.
26
27
EXAMPLES::
28
29
sage: from sage.symbolic.function_factory import function_factory
30
sage: f = function_factory('f', 2, '\\foo', {'mathematica':'Foo'})
31
sage: f(2,4)
32
f(2, 4)
33
sage: latex(f(1,2))
34
\foo\left(1, 2\right)
35
sage: f._mathematica_init_()
36
'Foo'
37
38
sage: def evalf_f(self, x, parent=None): return x*.5r
39
sage: g = function_factory('g',1,evalf_func=evalf_f)
40
sage: g(2)
41
g(2)
42
sage: g(2).n()
43
1.00000000000000
44
"""
45
class NewSymbolicFunction(SymbolicFunction):
46
def __init__(self):
47
"""
48
EXAMPLES::
49
50
sage: from sage.symbolic.function_factory import function_factory
51
sage: f = function_factory('f', 2) # indirect doctest
52
sage: f(2,4)
53
f(2, 4)
54
"""
55
SymbolicFunction.__init__(self, name, nargs, latex_name,
56
conversions, evalf_params_first)
57
58
def _maxima_init_(self):
59
"""
60
EXAMPLES::
61
62
sage: from sage.symbolic.function_factory import function_factory
63
sage: f = function_factory('f', 2) # indirect doctest
64
sage: f._maxima_init_()
65
"'f"
66
"""
67
return "'%s"%self.name()
68
69
def __reduce__(self):
70
"""
71
EXAMPLES::
72
73
sage: from sage.symbolic.function_factory import function_factory
74
sage: f = function_factory('f', 2) # indirect doctest
75
sage: nf = loads(dumps(f))
76
sage: nf(1, 2)
77
f(1, 2)
78
"""
79
pickled_functions = self.__getstate__()[6]
80
return (unpickle_function, (name, nargs, latex_name, conversions,
81
evalf_params_first, pickled_functions))
82
83
l = locals()
84
for func_name in sfunctions_funcs:
85
func = l.get(func_name+"_func", None)
86
if func:
87
if not callable(func):
88
raise ValueError, func_name + "_func" + " parameter must be callable"
89
setattr(NewSymbolicFunction, '_%s_'%func_name, func)
90
91
return NewSymbolicFunction()
92
93
def unpickle_function(name, nargs, latex_name, conversions, evalf_params_first,
94
pickled_funcs):
95
r"""
96
This is returned by the ``__reduce__`` method of symbolic functions to be
97
called during unpickling to recreate the given function.
98
99
It calls :meth:`function_factory` with the supplied arguments.
100
101
EXAMPLES::
102
103
sage: from sage.symbolic.function_factory import unpickle_function
104
sage: nf = unpickle_function('f', 2, '\\foo', {'mathematica':'Foo'}, True, [])
105
sage: nf
106
f
107
sage: nf(1,2)
108
f(1, 2)
109
sage: latex(nf(x,x))
110
\foo\left(x, x\right)
111
sage: nf._mathematica_init_()
112
'Foo'
113
114
sage: from sage.symbolic.function import pickle_wrapper
115
sage: def evalf_f(self, x, parent=None): return 2r*x + 5r
116
sage: def conjugate_f(self, x): return x/2r
117
sage: nf = unpickle_function('g', 1, None, None, True, [None, pickle_wrapper(evalf_f), pickle_wrapper(conjugate_f)] + [None]*8)
118
sage: nf
119
g
120
sage: nf(2)
121
g(2)
122
sage: nf(2).n()
123
9.00000000000000
124
sage: nf(2).conjugate()
125
1
126
"""
127
funcs = map(unpickle_wrapper, pickled_funcs)
128
args = [name, nargs, latex_name, conversions, evalf_params_first] + funcs
129
return function_factory(*args)
130
131
def function(s, *args, **kwds):
132
r"""
133
Create a formal symbolic function with the name *s*.
134
135
INPUT:
136
137
- ``args`` - arguments to the function, if specified returns the new
138
function evaluated at the given arguments
139
- ``nargs=0`` - number of arguments the function accepts, defaults to
140
variable number of arguments, or 0
141
- ``latex_name`` - name used when printing in latex mode
142
- ``conversions`` - a dictionary specifying names of this function in
143
other systems, this is used by the interfaces internally during conversion
144
- ``eval_func`` - method used for automatic evaluation
145
- ``evalf_func`` - method used for numeric evaluation
146
- ``evalf_params_first`` - bool to indicate if parameters should be
147
evaluated numerically before calling the custom evalf function
148
- ``conjugate_func`` - method used for complex conjugation
149
- ``real_part_func`` - method used when taking real parts
150
- ``imag_part_func`` - method used when taking imaginary parts
151
- ``derivative_func`` - method to be used for (partial) derivation
152
This method should take a keyword argument deriv_param specifying
153
the index of the argument to differentiate w.r.t
154
- ``tderivative_func`` - method to be used for derivatives
155
- ``power_func`` - method used when taking powers
156
This method should take a keyword argument power_param specifying
157
the exponent
158
- ``series_func`` - method used for series expansion
159
This method should expect keyword arguments
160
- ``order`` - order for the expansion to be computed
161
- ``var`` - variable to expand w.r.t.
162
- ``at`` - expand at this value
163
- ``print_func`` - method for custom printing
164
- ``print_latex_func`` - method for custom printing in latex mode
165
166
Note that custom methods must be instance methods, i.e., expect the instance
167
of the symbolic function as the first argument.
168
169
EXAMPLES::
170
171
sage: var('a, b')
172
(a, b)
173
sage: f = function('cr', a)
174
sage: g = f.diff(a).integral(b)
175
sage: g
176
b*D[0](cr)(a)
177
sage: foo = function("foo", nargs=2)
178
sage: x,y,z = var("x y z")
179
sage: foo(x, y) + foo(y, z)^2
180
foo(y, z)^2 + foo(x, y)
181
182
In Sage 4.0, you need to use :meth:`substitute_function` to
183
replace all occurrences of a function with another::
184
185
sage: g.substitute_function(cr, cos)
186
-b*sin(a)
187
188
sage: g.substitute_function(cr, (sin(x) + cos(x)).function(x))
189
b*(cos(a) - sin(a))
190
191
In Sage 4.0, basic arithmetic with unevaluated functions is no
192
longer supported::
193
194
sage: x = var('x')
195
sage: f = function('f')
196
sage: 2*f
197
Traceback (most recent call last):
198
...
199
TypeError: unsupported operand parent(s) for '*': 'Integer Ring' and '<class 'sage.symbolic.function_factory.NewSymbolicFunction'>'
200
201
You now need to evaluate the function in order to do the arithmetic::
202
203
sage: 2*f(x)
204
2*f(x)
205
206
207
We create a formal function of one variable, write down
208
an expression that involves first and second derivatives,
209
and extract off coefficients.
210
211
::
212
213
sage: r, kappa = var('r,kappa')
214
sage: psi = function('psi', nargs=1)(r); psi
215
psi(r)
216
sage: g = 1/r^2*(2*r*psi.derivative(r,1) + r^2*psi.derivative(r,2)); g
217
(r^2*D[0, 0](psi)(r) + 2*r*D[0](psi)(r))/r^2
218
sage: g.expand()
219
2*D[0](psi)(r)/r + D[0, 0](psi)(r)
220
sage: g.coeff(psi.derivative(r,2))
221
1
222
sage: g.coeff(psi.derivative(r,1))
223
2/r
224
225
Defining custom methods for automatic or numeric evaluation, derivation,
226
conjugation, etc. is supported::
227
228
229
sage: def ev(self, x): return 2*x
230
sage: foo = function("foo", nargs=1, eval_func=ev)
231
sage: foo(x)
232
2*x
233
sage: foo = function("foo", nargs=1, eval_func=lambda self, x: 5)
234
sage: foo(x)
235
5
236
sage: def ef(self, x): pass
237
sage: bar = function("bar", nargs=1, eval_func=ef)
238
sage: bar(x)
239
bar(x)
240
241
sage: def evalf_f(self, x, parent=None): return 6
242
sage: foo = function("foo", nargs=1, evalf_func=evalf_f)
243
sage: foo(x)
244
foo(x)
245
sage: foo(x).n()
246
6
247
248
sage: foo = function("foo", nargs=1, conjugate_func=ev)
249
sage: foo(x).conjugate()
250
2*x
251
252
sage: def deriv(self, *args,**kwds): print args, kwds; return args[kwds['diff_param']]^2
253
sage: foo = function("foo", nargs=2, derivative_func=deriv)
254
sage: foo(x,y).derivative(y)
255
(x, y) {'diff_param': 1}
256
y^2
257
258
sage: def pow(self, x, power_param=None): print x, power_param; return x*power_param
259
sage: foo = function("foo", nargs=1, power_func=pow)
260
sage: foo(y)^(x+y)
261
y x + y
262
(x + y)*y
263
264
sage: def expand(self, *args, **kwds): print args, kwds; return sum(args[0]^i for i in range(kwds['order']))
265
sage: foo = function("foo", nargs=1, series_func=expand)
266
sage: foo(y).series(y, 5)
267
(y,) {'var': y, 'options': 0, 'at': 0, 'order': 5}
268
y^4 + y^3 + y^2 + y + 1
269
270
sage: def my_print(self, *args): return "my args are: " + ', '.join(map(repr, args))
271
sage: foo = function('t', nargs=2, print_func=my_print)
272
sage: foo(x,y^z)
273
my args are: x, y^z
274
275
sage: latex(foo(x,y^z))
276
t\left(x, y^{z}\right)
277
sage: foo = function('t', nargs=2, print_latex_func=my_print)
278
sage: foo(x,y^z)
279
t(x, y^z)
280
sage: latex(foo(x,y^z))
281
my args are: x, y^z
282
sage: foo = function('t', nargs=2, latex_name='foo')
283
sage: latex(foo(x,y^z))
284
foo\left(x, y^{z}\right)
285
286
Chain rule::
287
288
sage: def print_args(self, *args, **kwds): print "args:",args; print "kwds:",kwds; return args[0]
289
sage: foo = function('t', nargs=2, tderivative_func=print_args)
290
sage: foo(x,x).derivative(x)
291
args: (x, x)
292
kwds: {'diff_param': x}
293
x
294
sage: foo = function('t', nargs=2, derivative_func=print_args)
295
sage: foo(x,x).derivative(x)
296
args: (x, x)
297
kwds: {'diff_param': 0}
298
args: (x, x)
299
kwds: {'diff_param': 1}
300
2*x
301
"""
302
if not isinstance(s, (str, unicode)):
303
raise TypeError, "expect string as first argument"
304
305
# create the function
306
if ',' in s:
307
names = s.split(',')
308
elif ' ' in s:
309
names = s.split(' ')
310
else:
311
names = [s]
312
313
funcs = [function_factory(name, **kwds) for name in names]
314
315
if len(args) > 0:
316
res = [f(*args) for f in funcs]
317
else:
318
res = funcs
319
320
if len(res) == 1:
321
return res[0]
322
return tuple(res)
323
324
def deprecated_custom_evalf_wrapper(func):
325
"""
326
This is used while pickling old symbolic functions that define a custom
327
evalf method.
328
329
The protocol for numeric evaluation functions was changed to include a
330
``parent`` argument instead of ``prec``. This function creates a wrapper
331
around the old custom method, which extracts the precision information
332
from the given ``parent``, and passes it on to the old function.
333
334
EXAMPLES::
335
336
sage: from sage.symbolic.function_factory import deprecated_custom_evalf_wrapper as dcew
337
sage: def old_func(x, prec=0): print "x: %s, prec: %s"%(x,prec)
338
sage: new_func = dcew(old_func)
339
sage: new_func(5, parent=RR)
340
x: 5, prec: 53
341
sage: new_func(0r, parent=ComplexField(100))
342
x: 0, prec: 100
343
"""
344
def new_evalf(*args, **kwds):
345
parent = kwds['parent']
346
if parent:
347
prec = parent.prec()
348
else:
349
prec = 53
350
return func(*args, prec=prec)
351
return new_evalf
352
353
354
# This code is used when constructing dynamic methods for symbolic
355
# expressions representing evaluated symbolic functions. See
356
# get_dynamic_class_for_function in sage/symbolic/expression.pyx.
357
# Since Cython does not support closures, this needs to live in a Python
358
# file. This file is the only pure Python file we have related to symbolic
359
# functions.
360
def eval_on_operands(f):
361
"""
362
Given a method ``f`` return a new method which takes a single symbolic
363
expression argument and passes the operands of the given expression as
364
arguments to ``f``.
365
366
EXAMPLES::
367
368
sage: def f(x, y):
369
....: '''
370
....: Some documentation.
371
....: '''
372
....: return x + 2*y
373
....:
374
sage: f(x, 1)
375
x + 2
376
sage: from sage.symbolic.function_factory import eval_on_operands
377
sage: g = eval_on_operands(f)
378
sage: g(x + 1)
379
x + 2
380
sage: g.__doc__.strip()
381
'Some documentation.'
382
"""
383
@sage_wraps(f)
384
def new_f(ex):
385
return f(*ex.operands())
386
return new_f
387
388