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