2 There are two types of functions:
3 1) defined function like exp or sin that has a name and body
4 (in the sense that function can be evaluated).
6 2) undefined function with a name but no body. Undefined
7 functions can be defined using a Function class as follows:
9 (the result will be Function instance)
10 3) this isn't implemented yet: anonymous function or lambda function that has
11 no name but has body with dummy variables. An anonymous function
12 object creation examples:
13 f = Lambda(x, exp(x)*x)
14 f = Lambda(exp(x)*x) # free symbols in the expression define the number of arguments
16 4) isn't implemented yet: composition of functions, like (sin+cos)(x), this
17 works in sympycore, but needs to be ported back to SymPy.
21 >>> from sympy import *
26 >>> print srepr(f(x).func)
32 from basic
import Basic
, Atom
, S
, C
, sympify
33 from basic
import BasicType
, BasicMeta
34 from methods
import ArithMeths
, RelMeths
35 from operations
import AssocOp
36 from cache
import cacheit
37 from itertools
import repeat
38 from numbers
import Rational
,Integer
39 from symbol
import Symbol
41 from multidimensional
import vectorize
43 from sympy
import mpmath
45 class FunctionClass(BasicMeta
):
47 Base class for function classes. FunctionClass is a subclass of type.
49 Use Function('<function name>' [ , signature ]) to create
50 undefined function classes.
55 def __new__(cls
, arg1
, arg2
, arg3
=None, **options
):
56 assert not options
,`options`
57 if isinstance(arg1
, type):
58 ftype
, name
, signature
= arg1
, arg2
, arg3
59 #XXX this probably needs some fixing:
60 assert ftype
.__name
__.endswith('Function'),`ftype`
61 attrdict
= ftype
.__dict
__.copy()
62 attrdict
['undefined_Function'] = True
63 if signature
is not None:
64 attrdict
['signature'] = signature
66 return type.__new
__(cls
, name
, bases
, attrdict
)
68 name
, bases
, attrdict
= arg1
, arg2
, arg3
69 return type.__new
__(cls
, name
, bases
, attrdict
)
74 class Function(Basic
, ArithMeths
, RelMeths
):
76 Base class for applied functions.
77 Constructor of undefined classes.
81 __metaclass__
= FunctionClass
89 def __new__(cls
, *args
, **options
):
90 # NOTE: this __new__ is twofold:
92 # 1 -- it can create another *class*, which can then be instantiated by
93 # itself e.g. Function('f') creates a new class f(Function)
95 # 2 -- on the other hand, we instantiate -- that is we create an
96 # *instance* of a class created earlier in 1.
98 # So please keep, both (1) and (2) in mind.
100 # (1) create new function class
103 #when user writes Function("f"), do an equivalent of:
104 #taking the whole class Function(...):
105 #and rename the Function to "f" and return f, thus:
106 #In [13]: isinstance(f, Function)
108 #In [14]: isinstance(f, FunctionClass)
111 if len(args
) == 1 and isinstance(args
[0], str):
112 #always create Function
113 return FunctionClass(Function
, *args
)
114 return FunctionClass(Function
, *args
, **options
)
118 raise Exception("You need to specify exactly one string")
120 # (2) create new instance of a class created in (1)
121 # UC: Function('f')(x)
123 args
= map(sympify
, args
)
124 # these lines should be refactored
125 for opt
in ["nargs", "dummy", "comparable", "noncommutative", "commutative"]:
129 if options
.get('evaluate') is False:
130 return Basic
.__new
__(cls
, *args
, **options
)
131 r
= cls
.canonize(*args
)
132 if isinstance(r
, Basic
):
136 elif not isinstance(r
, tuple):
138 return Basic
.__new
__(cls
, *args
, **options
)
141 def is_commutative(self
):
145 def canonize(cls
, *args
):
147 Returns a canonical form of cls applied to arguments args.
149 The canonize() method is called when the class cls is about to be
150 instantiated and it should return either some simplified instance
151 (possible of some other class), or if the class cls should be
152 unmodified, return None.
154 Example of canonize() for the function "sign"
155 ---------------------------------------------
158 def canonize(cls, arg):
161 if arg is S.Zero: return S.Zero
162 if arg.is_positive: return S.One
163 if arg.is_negative: return S.NegativeOne
164 if isinstance(arg, C.Mul):
165 coeff, terms = arg.as_coeff_terms()
166 if coeff is not S.One:
167 return cls(coeff) * cls(C.Mul(*terms))
174 return self
.__class
__
176 def _eval_subs(self
, old
, new
):
179 elif isinstance(old
, FunctionClass
) and isinstance(new
, FunctionClass
):
180 if old
== self
.func
and old
.nargs
== new
.nargs
:
181 return new(*self
.args
[:])
182 obj
= self
.func
._eval
_apply
_subs
(*(self
.args
[:] + (old
,) + (new
,)))
185 return Basic
._seq
_subs
(self
, old
, new
)
187 def _eval_expand_basic(self
, *args
):
190 def _eval_evalf(self
, prec
):
191 # Lookup mpmath function based on name
192 fname
= self
.func
.__name
__
194 if not hasattr(mpmath
, fname
):
195 from sympy
.utilities
.lambdify
import MPMATH_TRANSLATIONS
196 fname
= MPMATH_TRANSLATIONS
[fname
]
197 func
= getattr(mpmath
, fname
)
198 except (AttributeError, KeyError):
201 # Convert all args to mpf or mpc
203 args
= [arg
._to
_mpmath
(prec
) for arg
in self
.args
]
207 # Set mpmath precision and apply. Make sure precision is restored
209 orig
= mpmath
.mp
.prec
211 mpmath
.mp
.prec
= prec
214 mpmath
.mp
.prec
= orig
216 return Basic
._from
_mpmath
(v
, prec
)
218 def _eval_is_comparable(self
):
228 def _eval_derivative(self
, s
):
229 # f(x).diff(s) -> x.diff(s) * f.fdiff(1)(s)
238 if isinstance(self
.func
, FunctionClass
):
243 def _eval_is_commutative(self
):
247 if c
is None: return None
251 def _eval_eq_nonzero(self
, other
):
252 if isinstance(other
.func
, self
.func
.__class
__) and len(self
)==len(other
):
253 for a1
,a2
in zip(self
,other
):
258 def as_base_exp(self
):
261 def count_ops(self
, symbolic
=True):
263 return 1 + Add(*[t
.count_ops(symbolic
) for t
in self
.args
])
265 def _eval_oseries(self
, order
):
266 assert self
.func
.nargs
==1,`self
.func`
269 if not C
.Order(1,x
).contains(arg
):
270 return self
.func(arg
)
271 arg0
= arg
.limit(x
, 0)
272 if arg0
is not S
.Zero
:
276 #for example when e = sin(x+1) or e = sin(cos(x))
277 #let's try the general algorithm
278 term
= e
.subs(x
, S
.Zero
)
282 while not order
.contains(term
) or term
== 0:
287 term
= e
.subs(x
, S
.Zero
)*(x
**i
)/fact
290 #print '%s(%s).oseries(%s) is unevaluated' % (self.func,arg,order)
291 return e1
.oseries(order
)
292 return self
._compute
_oseries
(arg
, order
, self
.func
.taylor_term
, self
.func
)
294 def nseries(self
, x
, x0
, n
):
295 return self
.series(x
, x0
, n
)
297 def _eval_is_polynomial(self
, syms
):
298 for arg
in self
.args
:
303 def _eval_expand_complex(self
, *args
):
304 func
= self
.func(*[ a
._eval
_expand
_complex
(*args
) for a
in self
.args
])
305 return C
.re(func
) + S
.ImaginaryUnit
* C
.im(func
)
307 def _eval_rewrite(self
, pattern
, rule
, **hints
):
308 if hints
.get('deep', False):
309 args
= [ a
._eval
_rewrite
(pattern
, rule
, **hints
) for a
in self
]
313 if pattern
is None or isinstance(self
.func
, pattern
):
314 if hasattr(self
, rule
):
315 rewritten
= getattr(self
, rule
)(*args
)
317 if rewritten
is not None:
320 return self
.func(*args
, **self
._assumptions
)
322 def fdiff(self
, argindex
=1):
323 if self
.nargs
is not None:
324 if isinstance(self
.nargs
, tuple):
325 nargs
= self
.nargs
[-1]
328 if not (1<=argindex
<=nargs
):
329 raise TypeError("argument index %r is out of range [1,%s]" % (argindex
,nargs
))
330 return Derivative(self
,self
.args
[argindex
-1],evaluate
=False)
333 def _eval_apply_evalf(cls
, arg
):
334 arg
= arg
.evalf(prec
)
337 # common case for functions with 1 argument
340 func_evalf
= getattr(arg
, cls
.__name
__)
343 def _eval_as_leading_term(self
, x
):
344 """General method for the leading term"""
345 arg
= self
.args
[0].as_leading_term(x
)
347 if C
.Order(1,x
).contains(arg
):
350 return self
.func(arg
)
353 def taylor_term(cls
, n
, x
, *previous_terms
):
354 """General method for the taylor term.
356 This method is slow, because it differentiates n-times. Subclasses can
357 redefine it to make it faster by using the "previous_terms".
360 return cls(x
).diff(x
, n
).subs(x
, 0) * x
**n
/ C
.Factorial(n
)
362 class WildFunction(Function
, Atom
):
364 WildFunction() matches any expression but another WildFunction()
365 XXX is this as intended, does it work ?
370 def __new__(cls
, name
=None, **assumptions
):
372 name
= 'Wf%s' % (Symbol
.dummycount
+ 1) # XXX refactor dummy counting
373 Symbol
.dummycount
+= 1
374 obj
= Function
.__new
__(cls
, name
, **assumptions
)
378 def matches(pattern
, expr
, repl_dict
={}, evaluate
=False):
379 for p
,v
in repl_dict
.items():
381 if v
==expr
: return repl_dict
383 if pattern
.nargs
is not None:
384 if pattern
.nargs
!= expr
.nargs
:
386 repl_dict
= repl_dict
.copy()
387 repl_dict
[pattern
] = expr
391 def _eval_apply_evalf(cls
, arg
):
398 class Derivative(Basic
, ArithMeths
, RelMeths
):
400 Carries out differentation of the given expression with respect to symbols.
402 expr must define ._eval_derivative(symbol) method that returns
403 the differentation result or None.
407 Derivative(Derivative(expr, x), y) -> Derivative(expr, x, y)
408 Derivative(expr, x, 3) -> Derivative(expr, x, x, x)
412 def _symbolgen(*symbols
):
414 Generator of all symbols in the argument of the Derivative.
417 >> ._symbolgen(x, 3, y)
419 >> ._symbolgen(x, 10**6)
420 (x, x, x, x, x, x, x, ...)
422 The second example shows why we don't return a list, but a generator,
423 so that the code that calls _symbolgen can return earlier for special
424 cases, like x.diff(x, 10**6).
426 last_s
= sympify(symbols
[len(symbols
)-1])
427 for i
in xrange(len(symbols
)):
428 s
= sympify(symbols
[i
])
431 next_s
= sympify(symbols
[i
+1])
433 if isinstance(s
, Integer
):
435 elif isinstance(s
, Symbol
):
436 # handle cases like (x, 3)
437 if isinstance(next_s
, Integer
):
439 for copy_s
in repeat(s
,int(next_s
)):
446 def __new__(cls
, expr
, *symbols
, **assumptions
):
448 if not symbols
: return expr
449 symbols
= Derivative
._symbolgen
(*symbols
)
450 if not assumptions
.get("evaluate", False):
451 obj
= Basic
.__new
__(cls
, expr
, *symbols
)
453 unevaluated_symbols
= []
456 assert isinstance(s
, Symbol
),`s`
459 obj
= expr
._eval
_derivative
(s
)
461 unevaluated_symbols
.append(s
)
467 if not unevaluated_symbols
:
469 return Basic
.__new
__(cls
, expr
, *unevaluated_symbols
)
471 def _eval_derivative(self
, s
):
476 if s
not in self
.symbols
:
477 obj
= self
.expr
.diff(s
)
478 if isinstance(obj
, Derivative
):
479 return Derivative(obj
.expr
, *(obj
.symbols
+self
.symbols
))
480 return Derivative(obj
, *self
.symbols
)
481 return Derivative(self
.expr
, *((s
,)+self
.symbols
), **{'evaluate': False})
484 return Derivative(self
.expr
, *self
.symbols
,**{'evaluate': True})
492 return self
._args
[1:]
494 def _eval_subs(self
, old
, new
):
495 return Derivative(self
.args
[0].subs(old
, new
), *self
.args
[1:], **{'evaluate': True})
497 def matches(pattern
, expr
, repl_dict
={}, evaluate
=False):
498 # this method needs a cleanup.
500 #print "? :",pattern, expr, repl_dict, evaluate
503 for p
,v
in repl_dict
.items():
505 if v
==expr
: return repl_dict
507 assert isinstance(pattern
, Derivative
)
508 if isinstance(expr
, Derivative
):
509 if len(expr
.symbols
) == len(pattern
.symbols
):
510 #print "MAYBE:",pattern, expr, repl_dict, evaluate
511 return Basic
.matches(pattern
, expr
, repl_dict
, evaluate
)
512 #print "NONE:",pattern, expr, repl_dict, evaluate
514 #print pattern, expr, repl_dict, evaluate
516 if pattern
.nargs
is not None:
517 if pattern
.nargs
!= expr
.nargs
:
519 repl_dict
= repl_dict
.copy()
520 repl_dict
[pattern
] = expr
523 class Lambda(Function
):
525 Lambda(x, expr) represents a lambda function similar to Python's
526 'lambda x: expr'. A function of several variables is written as
527 Lambda((x, y, ...), expr).
530 >>> from sympy import Symbol
532 >>> f = Lambda(x, x**2)
536 For multivariate functions, use:
541 >>> f2 = Lambda(x,y,z,t,x+y**z+t**z)
545 Multivariate functions can be curries for partial applications:
546 >>> sum2numbers = Lambda(x,y,x+y)
549 >>> plus1 = sum2numbers(1)
553 A handy shortcut for lots of arguments:
554 >>> from sympy import *
558 >>> f = Lambda(p, x + y*z)
564 # a minimum of 2 arguments (parameter, expression) are needed
566 def __new__(cls
,*args
):
568 assert len(args
) >= 2,"Must have at least one parameter and an expression"
569 if len(args
) == 2 and isinstance(args
[0], (list, tuple)):
570 args
= tuple(args
[0])+(args
[1],)
571 obj
= Function
.__new
__(cls
,*args
)
572 obj
.nargs
= len(args
)
576 def canonize(cls
,*args
):
577 obj
= Basic
.__new
__(cls
, *args
)
578 #use dummy variables internally, just to be sure
581 expression
= args
[nargs
-1]
582 funargs
= [Symbol(arg
.name
, dummy
=True) for arg
in args
[:nargs
-1]]
583 #probably could use something like foldl here
584 for arg
,funarg
in zip(args
[:nargs
-1],funargs
):
585 expression
= expression
.subs(arg
,funarg
)
586 funargs
.append(expression
)
587 obj
._args
= tuple(funargs
)
591 def apply(self
, *args
):
592 """Applies the Lambda function "self" to the arguments given.
593 This supports partial application.
596 >>> from sympy import Symbol
599 >>> f = Lambda(x, x**2)
602 >>> sum2numbers = Lambda(x,y,x+y)
605 >>> plus1 = sum2numbers(1)
611 nparams
= self
.nargs
- 1
612 assert nparams
>= len(args
),"Cannot call function with more parameters than function variables: %s (%d variables) called with %d arguments" % (str(self
),nparams
,len(args
))
616 expression
= self
.args
[self
.nargs
-1]
617 for arg
,funarg
in zip(args
,self
.args
[:nparams
]):
618 expression
= expression
.subs(funarg
,arg
)
621 if nparams
!= len(args
):
622 unused_args
= list(self
.args
[len(args
):nparams
])
623 unused_args
.append(expression
)
624 return Lambda(*tuple(unused_args
))
627 def __call__(self
, *args
):
628 return self
.apply(*args
)
630 def __eq__(self
, other
):
631 if isinstance(other
, Lambda
):
632 if not len(self
.args
) == len(other
.args
):
635 selfexpr
= self
.args
[self
.nargs
-1]
636 otherexpr
= other
.args
[other
.nargs
-1]
637 for selfarg
,otherarg
in zip(self
.args
[:self
.nargs
-1],other
.args
[:other
.nargs
-1]):
638 otherexpr
= otherexpr
.subs(otherarg
,selfarg
)
639 if selfexpr
== otherexpr
:
641 # if self.args[1] == other.args[1].subs(other.args[0], self.args[0]):
646 def diff(f
, x
, times
= 1, evaluate
=True):
647 """Differentiate f with respect to x
649 It's just a wrapper to unify .diff() and the Derivative class,
650 it's interface is similar to that of integrate()
652 see http://documents.wolfram.com/v5/Built-inFunctions/AlgebraicComputation/Calculus/D.html
655 return Derivative(f
,x
,times
, **{'evaluate':evaluate
})
658 def expand(e
, **hints
):
660 Expand an expression using hints.
662 This is just a wrapper around Basic.expand(), see it's docstring of for a
663 thourough docstring for this function. In isympy you can just type
664 Basic.expand? and enter.
666 return sympify(e
).expand(**hints
)
671 _
.Derivative
= Derivative
672 _
.FunctionClass
= FunctionClass
676 _
.FunctionClass
= FunctionClass
680 _
.FunctionClass
= FunctionClass
681 _
.WildFunction
= WildFunction
684 import operations
as _
686 _
.WildFunction
= WildFunction
690 _
.Function
= Function
691 _
.WildFunction
= WildFunction
695 _
.FunctionClass
= FunctionClass