[1/6] let's remove multiple inheritance (Singleton)
[sympy.git] / sympy / core / function.py
blob3d49abee50449ad9ff13d8dd93847696e1a0d641
1 """
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).
5 e = exp
6 2) undefined function with a name but no body. Undefined
7 functions can be defined using a Function class as follows:
8 f = Function('f')
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
15 f = exp * Lambda(x,x)
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.
20 Example:
21 >>> from sympy import *
22 >>> f = Function("f")
23 >>> x = Symbol("x")
24 >>> f(x)
25 f(x)
26 >>> print srepr(f(x).func)
27 Function('f')
28 >>> f(x).args
29 (x,)
30 """
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
40 from add import Add
41 from multidimensional import vectorize
43 from sympy import mpmath
45 class FunctionClass(BasicMeta):
46 """
47 Base class for function classes. FunctionClass is a subclass of type.
49 Use Function('<function name>' [ , signature ]) to create
50 undefined function classes.
51 """
53 _new = type.__new__
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
65 bases = (ftype,)
66 return type.__new__(cls, name, bases, attrdict)
67 else:
68 name, bases, attrdict = arg1, arg2, arg3
69 return type.__new__(cls, name, bases, attrdict)
71 def __repr__(cls):
72 return cls.__name__
74 class Function(Basic, ArithMeths, RelMeths):
75 """
76 Base class for applied functions.
77 Constructor of undefined classes.
79 """
81 __metaclass__ = FunctionClass
83 is_Function = True
85 nargs = None
87 @vectorize(1)
88 @cacheit
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
101 # UC: Function('f')
102 if cls is Function:
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)
107 #Out[13]: False
108 #In [14]: isinstance(f, FunctionClass)
109 #Out[14]: True
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)
115 else:
116 print args
117 print type(args[0])
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)
122 # UC: sin(x)
123 args = map(sympify, args)
124 # these lines should be refactored
125 for opt in ["nargs", "dummy", "comparable", "noncommutative", "commutative"]:
126 if opt in options:
127 del options[opt]
128 # up to here.
129 if options.get('evaluate') is False:
130 return Basic.__new__(cls, *args, **options)
131 r = cls.canonize(*args)
132 if isinstance(r, Basic):
133 return r
134 elif r is None:
135 pass
136 elif not isinstance(r, tuple):
137 args = (r,)
138 return Basic.__new__(cls, *args, **options)
140 @property
141 def is_commutative(self):
142 return True
144 @classmethod
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 ---------------------------------------------
157 @classmethod
158 def canonize(cls, arg):
159 if arg is S.NaN:
160 return S.NaN
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))
170 return
172 @property
173 def func(self):
174 return self.__class__
176 def _eval_subs(self, old, new):
177 if self == old:
178 return 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,)))
183 if obj is not None:
184 return obj
185 return Basic._seq_subs(self, old, new)
187 def _eval_expand_basic(self, *args):
188 return None
190 def _eval_evalf(self, prec):
191 # Lookup mpmath function based on name
192 fname = self.func.__name__
193 try:
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):
199 return
201 # Convert all args to mpf or mpc
202 try:
203 args = [arg._to_mpmath(prec) for arg in self.args]
204 except ValueError:
205 return
207 # Set mpmath precision and apply. Make sure precision is restored
208 # afterwards
209 orig = mpmath.mp.prec
210 try:
211 mpmath.mp.prec = prec
212 v = func(*args)
213 finally:
214 mpmath.mp.prec = orig
216 return Basic._from_mpmath(v, prec)
218 def _eval_is_comparable(self):
219 if self.is_Function:
220 r = True
221 for s in self.args:
222 c = s.is_comparable
223 if c is None: return
224 if not c: r = False
225 return r
226 return
228 def _eval_derivative(self, s):
229 # f(x).diff(s) -> x.diff(s) * f.fdiff(1)(s)
230 i = 0
231 l = []
232 r = S.Zero
233 for a in self.args:
234 i += 1
235 da = a.diff(s)
236 if da is S.Zero:
237 continue
238 if isinstance(self.func, FunctionClass):
239 df = self.fdiff(i)
240 l.append(df * da)
241 return Add(*l)
243 def _eval_is_commutative(self):
244 r = True
245 for a in self._args:
246 c = a.is_commutative
247 if c is None: return None
248 if not c: r = False
249 return r
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):
254 if not (a1==a2):
255 return False
256 return True
258 def as_base_exp(self):
259 return self, S.One
261 def count_ops(self, symbolic=True):
262 # f() args
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`
267 arg = self.args[0]
268 x = order.symbols[0]
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:
273 e = self.func(arg)
274 e1 = e.expand()
275 if e==e1:
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)
279 series = S.Zero
280 fact = S.One
281 i = 0
282 while not order.contains(term) or term == 0:
283 series += term
284 i += 1
285 fact *= Rational(i)
286 e = e.diff(x)
287 term = e.subs(x, S.Zero)*(x**i)/fact
288 return series
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:
299 if arg.has(*syms):
300 return False
301 return True
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 ]
310 else:
311 args = self.args[:]
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:
318 return rewritten
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]
326 else:
327 nargs = self.nargs
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)
332 @classmethod
333 def _eval_apply_evalf(cls, arg):
334 arg = arg.evalf(prec)
336 #if cls.nargs == 1:
337 # common case for functions with 1 argument
338 #if arg.is_Number:
339 if arg.is_number:
340 func_evalf = getattr(arg, cls.__name__)
341 return func_evalf()
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):
348 return arg
349 else:
350 return self.func(arg)
352 @classmethod
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".
359 x = sympify(x)
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 ?
368 nargs = 1
370 def __new__(cls, name=None, **assumptions):
371 if name is None:
372 name = 'Wf%s' % (Symbol.dummycount + 1) # XXX refactor dummy counting
373 Symbol.dummycount += 1
374 obj = Function.__new__(cls, name, **assumptions)
375 obj.name = name
376 return obj
378 def matches(pattern, expr, repl_dict={}, evaluate=False):
379 for p,v in repl_dict.items():
380 if p==pattern:
381 if v==expr: return repl_dict
382 return None
383 if pattern.nargs is not None:
384 if pattern.nargs != expr.nargs:
385 return None
386 repl_dict = repl_dict.copy()
387 repl_dict[pattern] = expr
388 return repl_dict
390 @classmethod
391 def _eval_apply_evalf(cls, arg):
392 return
394 @property
395 def is_number(self):
396 return False
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.
405 Examples:
407 Derivative(Derivative(expr, x), y) -> Derivative(expr, x, y)
408 Derivative(expr, x, 3) -> Derivative(expr, x, x, x)
411 @staticmethod
412 def _symbolgen(*symbols):
414 Generator of all symbols in the argument of the Derivative.
416 Example:
417 >> ._symbolgen(x, 3, y)
418 (x, x, x, 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])
429 next_s = None
430 if s != last_s:
431 next_s = sympify(symbols[i+1])
433 if isinstance(s, Integer):
434 continue
435 elif isinstance(s, Symbol):
436 # handle cases like (x, 3)
437 if isinstance(next_s, Integer):
438 # yield (x, x, x)
439 for copy_s in repeat(s,int(next_s)):
440 yield copy_s
441 else:
442 yield s
443 else:
444 yield s
446 def __new__(cls, expr, *symbols, **assumptions):
447 expr = sympify(expr)
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)
452 return obj
453 unevaluated_symbols = []
454 for s in symbols:
455 s = sympify(s)
456 assert isinstance(s, Symbol),`s`
457 if not expr.has(s):
458 return S.Zero
459 obj = expr._eval_derivative(s)
460 if obj is None:
461 unevaluated_symbols.append(s)
462 elif obj is S.Zero:
463 return expr
464 else:
465 expr = obj
467 if not unevaluated_symbols:
468 return expr
469 return Basic.__new__(cls, expr, *unevaluated_symbols)
471 def _eval_derivative(self, s):
472 #print
473 #print self
474 #print s
475 #stop
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})
483 def doit(self):
484 return Derivative(self.expr, *self.symbols,**{'evaluate': True})
486 @property
487 def expr(self):
488 return self._args[0]
490 @property
491 def symbols(self):
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
501 #if repl_dict:
502 # return repl_dict
503 for p,v in repl_dict.items():
504 if p==pattern:
505 if v==expr: return repl_dict
506 return None
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
513 return None
514 #print pattern, expr, repl_dict, evaluate
515 stop
516 if pattern.nargs is not None:
517 if pattern.nargs != expr.nargs:
518 return None
519 repl_dict = repl_dict.copy()
520 repl_dict[pattern] = expr
521 return repl_dict
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).
529 A simple example:
530 >>> from sympy import Symbol
531 >>> x = Symbol('x')
532 >>> f = Lambda(x, x**2)
533 >>> f(4)
536 For multivariate functions, use:
537 >>> x = Symbol('x')
538 >>> y = Symbol('y')
539 >>> z = Symbol('z')
540 >>> t = Symbol('t')
541 >>> f2 = Lambda(x,y,z,t,x+y**z+t**z)
542 >>> f2(1,2,3,4)
545 Multivariate functions can be curries for partial applications:
546 >>> sum2numbers = Lambda(x,y,x+y)
547 >>> sum2numbers(1,2)
549 >>> plus1 = sum2numbers(1)
550 >>> plus1(3)
553 A handy shortcut for lots of arguments:
554 >>> from sympy import *
555 >>> var('x y z')
556 (x, y, z)
557 >>> p = x, y, z
558 >>> f = Lambda(p, x + y*z)
559 >>> f(*p)
560 x + y*z
564 # a minimum of 2 arguments (parameter, expression) are needed
565 nargs = 2
566 def __new__(cls,*args):
567 # nargs = len(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)
573 return obj
575 @classmethod
576 def canonize(cls,*args):
577 obj = Basic.__new__(cls, *args)
578 #use dummy variables internally, just to be sure
579 nargs = len(args)
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)
589 return obj
591 def apply(self, *args):
592 """Applies the Lambda function "self" to the arguments given.
593 This supports partial application.
595 Example:
596 >>> from sympy import Symbol
597 >>> x = Symbol('x')
598 >>> y = Symbol('y')
599 >>> f = Lambda(x, x**2)
600 >>> f.apply(4)
602 >>> sum2numbers = Lambda(x,y,x+y)
603 >>> sum2numbers(1,2)
605 >>> plus1 = sum2numbers(1)
606 >>> plus1(3)
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))
615 #replace arguments
616 expression = self.args[self.nargs-1]
617 for arg,funarg in zip(args,self.args[:nparams]):
618 expression = expression.subs(funarg,arg)
620 #curry the rest
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))
625 return expression
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):
633 return False
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:
640 return True
641 # if self.args[1] == other.args[1].subs(other.args[0], self.args[0]):
642 # return True
643 return False
645 @vectorize(0,1,2)
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})
657 @vectorize(0)
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)
669 # /cyclic/
670 import basic as _
671 _.Derivative = Derivative
672 _.FunctionClass = FunctionClass
673 del _
675 import add as _
676 _.FunctionClass = FunctionClass
677 del _
679 import mul as _
680 _.FunctionClass = FunctionClass
681 _.WildFunction = WildFunction
682 del _
684 import operations as _
685 _.Lambda = Lambda
686 _.WildFunction = WildFunction
687 del _
689 import symbol as _
690 _.Function = Function
691 _.WildFunction = WildFunction
692 del _
694 import numbers as _
695 _.FunctionClass = FunctionClass
696 del _