_eval_apply() converted to canonize()
[sympy.git] / sympy / core / function.py
blob8298282d48740599c6667857dfacae00503d11ca
1 """
2 NOTE: this doctest is for now obsoleted interface.
4 There are different types of functions:
5 1) defined function like exp or sin that has a name and body
6 (in the sense that function can be evaluated).
7 e = exp
8 2) undefined function with a name but no body. Undefined
9 functions can be defined using Symbol class as follows:
10 f = Symbol('f', function=True)
11 (the result will be Function instance)
13 f = Function('f')
14 3) anonymous function or lambda function that has no name
15 but has body with dummy variables. An anonymous function
16 object creation examples:
17 f = Lambda(x, exp(x)*x)
18 f = Lambda(exp(x)*x) # free symbols in the expression define the number of arguments
19 f = exp * Lambda(x,x)
20 4) composition of functions, inverse functions
22 One can perform certain operations with functions, the
23 result will be a lambda function. Allowed operations are
24 addition and multiplication. Function multiplication is
25 elementise ie (f*g)(x) is equivalent to f(x) * g(x).
26 Multiplication by a number is equivalent to multiplication
27 by a constant function.
28 Composition of functions is achived via Composition class@
31 f+Composition(2,exp,sin) -> lambda _x: f(x)+2*exp(sin(x))
33 In the above functions did not have arguments, then
34 it is said that functions are in unevaluated form.
35 When calling a function with arguments, then we get
36 an instance of the function value. Eg
38 exp(1) -> 1
39 (2*f)(x) -> 2 * f(x)
40 Lambda(x, exp(x)*x)(y) -> exp(y)*y
42 One can construct undefined function values from Symbol
43 object:
44 f = Symbol('f')
45 fx = f(x)
46 fx.func -> Function('f')
47 fx[:] -> (Symbol('x'),)
48 As seen above, function values are Apply instances and
49 have attributes .func and [:].
50 """
52 from basic import Basic, Singleton, Atom, cache_it, S
53 from basic_methods import BasicType, MetaBasicMeths
54 from methods import ArithMeths, NoRelMeths, RelMeths
55 from operations import AssocOp
57 class FunctionClass(MetaBasicMeths):
58 """
59 Base class for function classes. FunctionClass is a subclass of type.
61 Use Function('<function name>' [ , signature ]) to create
62 undefined function classes.
63 """
65 _new = type.__new__
67 def __new__(cls, arg1, arg2, arg3=None, **options):
68 assert not options,`options`
69 if isinstance(arg1, type):
70 ftype, name, signature = arg1, arg2, arg3
71 #XXX this probably needs some fixing:
72 assert ftype.__name__.endswith('Function'),`ftype`
73 attrdict = ftype.__dict__.copy()
74 attrdict['undefined_Function'] = True
75 if signature is not None:
76 attrdict['signature'] = signature
77 bases = (ftype,)
78 return type.__new__(cls, name, bases, attrdict)
79 else:
80 name, bases, attrdict = arg1, arg2, arg3
81 return type.__new__(cls, name, bases, attrdict)
83 def torepr(cls):
84 return cls.__name__
86 class Function(Basic, RelMeths):
87 """
88 Base class for applied functions.
89 Constructor of undefined classes.
91 """
93 __metaclass__ = FunctionClass
95 precedence = Basic.Apply_precedence
97 nofargs = None
99 @cache_it
100 def __new__(cls, *args, **options):
101 if cls is SingleValuedFunction or cls is Function:
102 #when user writes SingleValuedFunction("f"), do an equivalent of:
103 #taking the whole class SingleValuedFunction(...):
104 #and rename the SingleValuedFunction to "f" and return f, thus:
105 #In [13]: isinstance(f, SingleValuedFunction)
106 #Out[13]: False
107 #In [14]: isinstance(f, FunctionClass)
108 #Out[14]: True
110 if len(args) == 1 and isinstance(args[0], str):
111 #always create SingleValuedFunction
112 return FunctionClass(SingleValuedFunction, *args)
113 return FunctionClass(SingleValuedFunction, *args, **options)
114 else:
115 print args
116 print type(args[0])
117 raise Exception("You need to specify exactly one string")
118 args = map(Basic.sympify, args)
119 # these lines should be refactored
120 if "nofargs" in options:
121 del options["nofargs"]
122 if "dummy" in options:
123 del options["dummy"]
124 if "comparable" in options:
125 del options["comparable"]
126 if "noncommutative" in options:
127 del options["noncommutative"]
128 if "commutative" in options:
129 del options["commutative"]
130 # up to here.
131 r = cls.canonize(*args, **options)
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_comparable(self):
142 return True
144 @property
145 def is_commutative(self):
146 return True
148 @classmethod
149 def canonize(cls, *args, **options):
150 return
152 @property
153 def func(self):
154 return self.__class__
156 def _eval_subs(self, old, new):
157 if self == old:
158 return new
159 elif isinstance(old, FunctionClass) and isinstance(new, FunctionClass):
160 if old == self.func and old.nofargs == new.nofargs:
161 return new(*self[:])
162 obj = self.func._eval_apply_subs(*(self[:] + (old,) + (new,)))
163 if obj is not None:
164 return obj
165 return Basic._seq_subs(self, old, new)
167 def _eval_expand_basic(self, *args):
168 return self
170 def _eval_evalf(self):
171 obj = self.func._eval_apply_evalf(*self[:])
172 if obj is None:
173 return self
174 return obj
176 def _eval_is_comparable(self):
177 if isinstance(self.func, DefinedFunction):
178 r = True
179 for s in self:
180 c = s.is_comparable
181 if c is None: return
182 if not c: r = False
183 return r
184 return
186 def _eval_derivative(self, s):
187 # Apply(f(x), x).diff(s) -> x.diff(s) * f.fdiff(1)(s)
188 i = 0
189 l = []
190 r = Basic.Zero()
191 for a in self:
192 i += 1
193 da = a.diff(s)
194 if isinstance(da, Basic.Zero):
195 continue
196 if isinstance(self.func, FunctionClass):
197 df = self.fdiff(i)
198 l.append(df * da)
199 #else:
200 # df = self.func.fdiff(i)
201 # l.append(Apply(df,*self[:]) * da)
202 return Basic.Add(*l)
204 def _eval_power(b, e):
205 if len(b[:])==1:
206 return b.func._eval_apply_power(b[0], e)
207 return
209 def _eval_is_commutative(self):
210 r = True
211 for a in self._args:
212 c = a.is_commutative
213 if c is None: return None
214 if not c: r = False
215 return r
217 def _calc_positive(self):
218 return self.func._calc_apply_positive(*self[:])
220 def _calc_real(self):
221 return self.func._calc_apply_real(*self[:])
223 def _calc_unbounded(self):
224 return self.func._calc_apply_unbounded(*self[:])
226 def _eval_eq_nonzero(self, other):
227 if isinstance(other.func, self.func.__class__) and len(self)==len(other):
228 for a1,a2 in zip(self,other):
229 if not (a1==a2):
230 return False
231 return True
233 def as_base_exp(self):
234 return self, Basic.One()
236 def count_ops(self, symbolic=True):
237 # f() args
238 return 1 + Basic.Add(*[t.count_ops(symbolic) for t in self])
240 def _eval_oseries(self, order):
241 assert self.func.nofargs==1,`self.func`
242 arg = self[0]
243 x = order.symbols[0]
244 if not Basic.Order(1,x).contains(arg):
245 return self.func(arg)
246 arg0 = arg.limit(x, 0)
247 if not isinstance(arg0, Basic.Zero):
248 e = self.func(arg)
249 e1 = e.expand()
250 if e==e1:
251 #one example is e = sin(x+1)
252 #let's try the general algorithm
253 term = e.subs(x, Basic.Rational(0))
254 series = Basic.Rational(0)
255 fact = Basic.Rational(1)
256 i = 0
257 while not order.contains(term):
258 series += term
259 i += 1
260 fact *= Basic.Rational(i)
261 e = e.diff(x)
262 term = e.subs(x, Basic.Rational(0))*(x**i)/fact
263 return series
265 #print '%s(%s).oseries(%s) is unevaluated' % (self.func,arg,order)
266 return e1.oseries(order)
267 return self._compute_oseries(arg, order, self.func.taylor_term, self.func)
269 def _eval_is_polynomial(self, syms):
270 for arg in self:
271 if arg.has(*syms):
272 return False
273 return True
275 def _eval_expand_complex(self, *args):
276 func = self.func(*[ a._eval_expand_complex(*args) for a in self ])
277 return Basic.Re()(func) + S.ImaginaryUnit * Basic.Im()(func)
279 def _eval_rewrite(self, pattern, rule, **hints):
280 if hints.get('deep', False):
281 args = [ a._eval_rewrite(pattern, rule, **hints) for a in self ]
282 else:
283 args = self[:]
285 if pattern is None or isinstance(self.func, pattern):
286 if hasattr(self, rule):
287 rewritten = getattr(self, rule)(*args)
289 if rewritten is not None:
290 return rewritten
292 return self.func(*args, **self._assumptions)
294 def fdiff(self, argindex=1):
295 if self.nofargs is not None:
296 if isinstance(self.nofargs, tuple):
297 nofargs = self.nofargs[-1]
298 else:
299 nofargs = self.nofargs
300 if not (1<=argindex<=nofargs):
301 raise TypeError("argument index %r is out of range [1,%s]" % (i,nofargs))
302 return Derivative(self,self[argindex-1],evaluate=False)
304 def tostr(self, level=0):
305 p = self.precedence
306 r = '%s(%s)' % (self.func.__name__, ', '.join([a.tostr() for a in self]))
307 if p <= level:
308 return '(%s)' % (r)
309 return r
311 @classmethod
312 def _eval_apply_evalf(cls, arg):
313 return
315 class WildFunction(Function, Atom):
317 nofargs = 1
319 def matches(pattern, expr, repl_dict={}, evaluate=False):
320 for p,v in repl_dict.items():
321 if p==pattern:
322 if v==expr: return repl_dict
323 return None
324 if pattern.nofargs is not None:
325 if pattern.nofargs != expr.nofargs:
326 return None
327 repl_dict = repl_dict.copy()
328 repl_dict[pattern] = expr
329 return repl_dict
331 def tostr(self, level=0):
332 return self.name + '_'
334 @classmethod
335 def _eval_apply_evalf(cls, arg):
336 return
338 class Lambda(Function):
340 Lambda(expr, arg1, arg2, ...) -> lambda arg1, arg2,... : expr
342 Lambda instance has the same assumptions as its body.
345 precedence = Basic.Lambda_precedence
346 name = None
347 has_derivative = True
349 def __new__(cls, expr, *args):
350 expr = Basic.sympify(expr)
351 args = tuple(map(Basic.sympify, args))
352 #if isinstance(expr, Apply):
353 # if expr[:]==args:
354 # return expr.func
355 dummy_args = []
356 for a in args:
357 if not isinstance(a, Basic.Symbol):
358 raise TypeError("%s %s-th argument must be Symbol instance (got %r)" \
359 % (cls.__name__, len(dummy_args)+1,a))
360 d = a.as_dummy()
361 expr = expr.subs(a, d)
362 dummy_args.append(d)
363 obj = Basic.__new__(cls, expr, *dummy_args, **expr._assumptions)
364 return obj
366 def _hashable_content(self):
367 return self._args
369 @property
370 def nofargs(self):
371 return len(self._args)-1
373 def __getitem__(self, iter):
374 return self._args[1:][iter]
376 def __len__(self):
377 return len(self[:])
379 @property
380 def body(self):
381 return self._args[0]
383 def tostr(self, level=0):
384 precedence = self.precedence
385 r = 'lambda %s: %s' % (', '.join([a.tostr() for a in self]),
386 self.body.tostr(precedence))
387 if precedence <= level:
388 return '(%s)' % r
389 return r
391 def torepr(self):
392 return '%s(%s)' % (self.__class__.__name__, ', '.join([a.torepr() for a in self]))
394 def as_coeff_terms(self, x=None):
395 c,t = self.body.as_coeff_terms(x)
396 return c, [Lambda(Basic.Mul(*t),*self[:])]
398 def _eval_power(b, e):
400 (lambda x:f(x))**e -> (lambda x:f(x)**e)
402 return Lambda(b.body**e, *b[:])
404 def _eval_fpower(b, e):
406 FPow(lambda x:f(x), 2) -> lambda x:f(f(x)))
408 if isinstance(e, Basic.Integer) and e.is_positive and e.p < 10 and len(b)==1:
409 r = b.body
410 for i in xrange(e.p-1):
411 r = b(r)
412 return Lambda(r, *b[:])
414 def with_dummy_arguments(self, args = None):
415 if args is None:
416 args = tuple([a.as_dummy() for a in self])
417 if len(args) != len(self):
418 raise TypeError("different number of arguments in Lambda functions: %s, %s" % (len(args), len(self)))
419 expr = self.body
420 for a,na in zip(self, args):
421 expr = expr.subs(a, na)
422 return expr, args
424 def _eval_expand_basic(self, *args):
425 return Lambda(self.body._eval_expand_basic(*args), *self[:])
427 def diff(self, *symbols):
428 return Lambda(self.body.diff(*symbols), *self[:])
430 def fdiff(self, argindex=1):
431 if not (1<=argindex<=len(self)):
432 raise TypeError("%s.fderivative() argindex %r not in the range [1,%s]"\
433 % (self.__class__, argindex, len(self)))
434 s = self[argindex-1]
435 expr = self.body.diff(s)
436 return Lambda(expr, *self[:])
438 _eval_subs = Basic._seq_subs
440 def canonize(cls, *args):
441 n = cls.nofargs
442 if n!=len(args):
443 raise TypeError('%s takes exactly %s arguments (got %s)'\
444 % (cls, n, len(args)))
445 expr = cls.body
446 for da,a in zip(cls, args):
447 expr = expr.subs(da,a)
448 return expr
450 class Derivative(Basic, ArithMeths, RelMeths):
452 Carries out differentation of the given expression with respect to symbols.
454 expr must define ._eval_derivative(symbol) method that returns differentation result or None.
456 Derivative(Derivative(expr, x), y) -> Derivative(expr, x, y)
459 precedence = Basic.Apply_precedence
461 def __new__(cls, expr, *symbols, **assumptions):
462 expr = Basic.sympify(expr)
463 if not symbols: return expr
464 symbols = map(Basic.sympify, symbols)
466 if not assumptions.get("evaluate", True):
467 obj = Basic.__new__(cls, expr, *symbols)
468 return obj
470 for s in symbols:
471 assert isinstance(s, Basic.Symbol),`s`
472 if not expr.has(s):
473 return Basic.Zero()
475 unevaluated_symbols = []
476 for s in symbols:
477 obj = expr._eval_derivative(s)
478 if obj is None:
479 unevaluated_symbols.append(s)
480 else:
481 expr = obj
483 if not unevaluated_symbols:
484 return expr
485 return Basic.__new__(cls, expr, *unevaluated_symbols)
487 def xas_apply(self):
488 # Derivative(f(x),x) -> Apply(Lambda(f(_x),_x), x)
489 symbols = []
490 indices = []
491 for s in self.symbols:
492 if s not in symbols:
493 symbols.append(s)
494 indices.append(len(symbols))
495 else:
496 indices.append(symbols.index(s)+1)
497 stop
498 return Apply(FApply(FDerivative(*indices), Lambda(self.expr, *symbols)), *symbols)
500 def _eval_derivative(self, s):
501 #print
502 #print self
503 #print s
504 #stop
505 if s not in self.symbols:
506 obj = self.expr.diff(s)
507 if isinstance(obj, Derivative):
508 return Derivative(obj.expr, *(obj.symbols+self.symbols))
509 return Derivative(obj, *self.symbols)
510 return Derivative(self.expr, *((s,)+self.symbols), **{'evaluate': False})
512 def doit(self):
513 return Derivative(self.expr, *self.symbols)
515 @property
516 def expr(self):
517 return self._args[0]
519 @property
520 def symbols(self):
521 return self._args[1:]
523 def tostr(self, level=0):
524 r = 'D' + `tuple(self)`
525 if self.precedence <= level:
526 r = '(%s)' % (r)
527 return r
529 def _eval_subs(self, old, new):
530 return Derivative(self[0].subs(old, new), *self[1:])
532 def matches(pattern, expr, repl_dict={}, evaluate=False):
533 # this method needs a cleanup.
535 #print "? :",pattern, expr, repl_dict, evaluate
536 #if repl_dict:
537 # return repl_dict
538 for p,v in repl_dict.items():
539 if p==pattern:
540 if v==expr: return repl_dict
541 return None
542 assert isinstance(pattern, Derivative)
543 if isinstance(expr, Derivative):
544 if len(expr.symbols) == len(pattern.symbols):
545 #print "MAYBE:",pattern, expr, repl_dict, evaluate
546 return Basic.matches(pattern, expr, repl_dict, evaluate)
547 #print "NONE:",pattern, expr, repl_dict, evaluate
548 return None
549 #print pattern, expr, repl_dict, evaluate
550 stop
551 if pattern.nofargs is not None:
552 if pattern.nofargs != expr.nofargs:
553 return None
554 repl_dict = repl_dict.copy()
555 repl_dict[pattern] = expr
556 return repl_dict
559 def diff(f, x, times = 1, evaluate=True):
560 """Differentiate f with respect to x
562 It's just a wrapper to unify .diff() and the Derivative class,
563 it's interface is similar to that of integrate()
565 see http://documents.wolfram.com/v5/Built-inFunctions/AlgebraicComputation/Calculus/D.html
567 f = Basic.sympify(f)
568 if evaluate == True:
569 for i in range(0,times):
570 f = f.diff(x)
571 return f
572 else:
573 return Derivative(f, x, evaluate=evaluate)
577 # TODO rename me to something more appropriate? e.g. ArithFunction (or just
578 # Function?)
579 class SingleValuedFunction(ArithMeths, Function):
581 Single-valued functions.
584 @classmethod
585 def _eval_apply_evalf(cls, arg):
586 arg = arg.evalf()
588 #if cls.nofargs == 1:
589 # common case for functions with 1 argument
590 #if isinstance(arg, Basic.Number):
591 if arg.is_number:
592 func_evalf = getattr(arg, cls.__name__)
593 return func_evalf()