_eval_apply() converted to canonize()
[sympy.git] / sympy / functions / elementary / exponential.py
blob959561c80460840db13f9e2ac532534d0714b0bb
2 from sympy.core.basic import Basic, S, cache_it, cache_it_immutable
3 from sympy.core.function import Lambda, SingleValuedFunction, Function
5 class exp(SingleValuedFunction):
7 nofargs = 1
9 def fdiff(self, argindex=1):
10 if argindex == 1:
11 return self
12 else:
13 raise ArgumentIndexError(self, argindex)
15 def inverse(self, argindex=1):
16 return S.Log
18 @classmethod
19 def _eval_apply_subs(self, *args):
20 return
22 #XXX: investigate why we need the **optionsXXX and remove it
23 @classmethod
24 def canonize(cls, arg, **optionsXXX):
25 arg = Basic.sympify(arg)
27 if isinstance(arg, Basic.Number):
28 if isinstance(arg, Basic.NaN):
29 return S.NaN
30 elif isinstance(arg, Basic.Zero):
31 return S.One
32 elif isinstance(arg, Basic.One):
33 return S.Exp1
34 elif isinstance(arg, Basic.Infinity):
35 return S.Infinity
36 elif isinstance(arg, Basic.NegativeInfinity):
37 return S.Zero
38 elif isinstance(arg, log):
39 return arg[0]
40 elif isinstance(arg, Basic.Mul):
41 coeff = arg.as_coefficient(S.Pi*S.ImaginaryUnit)
43 if coeff is not None:
44 if isinstance(2*coeff, Basic.Integer):
45 cst_table = {
46 0 : S.One,
47 1 : S.ImaginaryUnit,
48 2 : S.NegativeOne,
49 3 : -S.ImaginaryUnit,
52 return cst_table[int(2*coeff) % 4]
54 if isinstance(arg, Basic.Add):
55 args = arg[:]
56 else:
57 args = [arg]
59 included, excluded = [], []
61 for arg in args:
62 coeff, terms = arg.as_coeff_terms()
64 if isinstance(coeff, Basic.Infinity):
65 excluded.append(coeff**Basic.Mul(*terms))
66 else:
67 coeffs, log_term = [coeff], None
69 for term in terms:
70 if isinstance(term, log):
71 if log_term is None:
72 log_term = term[0]
73 else:
74 log_term = None
75 break
76 elif term.is_comparable:
77 coeffs.append(term)
78 else:
79 break
81 if log_term is not None:
82 excluded.append(log_term**Basic.Mul(*coeffs))
83 else:
84 included.append(arg)
86 if excluded:
87 return Basic.Mul(*(excluded+[cls(Basic.Add(*included))]))
89 @classmethod
90 @cache_it_immutable
91 def taylor_term(self, n, x, *previous_terms):
92 if n<0: return S.Zero
93 if n==0: return S.One
94 x = Basic.sympify(x)
95 if previous_terms:
96 p = previous_terms[-1]
97 if p is not None:
98 return p * x / n
99 return x**n/Basic.Factorial()(n)
101 def _eval_expand_complex(self, *args):
102 re, im = self[0].as_real_imag()
103 cos, sin = Basic.cos(im), Basic.sin(im)
104 return exp(re) * cos + S.ImaginaryUnit * exp(re) * sin
106 def _eval_conjugate(self):
107 return self.func(self[0].conjugate())
109 def as_base_exp(self):
110 coeff, terms = self[0].as_coeff_terms()
111 return self.func(Basic.Mul(*terms)), coeff
113 def as_coeff_terms(self, x=None):
114 arg = self[0]
115 if x is not None:
116 c,f = arg.as_coeff_factors(x)
117 return self.func(c), [self.func(a) for a in f]
118 if isinstance(arg, Basic.Add):
119 return S.One, [self.func(a) for a in arg]
120 return S.One,[self]
122 def _eval_subs(self, old, new):
123 if self==old: return new
124 arg = self[0]
125 o = old
126 if isinstance(old, Basic.Pow): # handle (exp(3*log(x))).subs(x**2, z) -> z**(3/2)
127 old = exp(old.exp * S.Log(old.base))
128 if isinstance(old, exp):
129 b,e = self.as_base_exp()
130 bo,eo = old.as_base_exp()
131 if b==bo:
132 return new ** (e/eo) # exp(2/3*x*3).subs(exp(3*x),y) -> y**(2/3)
133 if isinstance(arg, Basic.Add): # exp(2*x+a).subs(exp(3*x),y) -> y**(2/3) * exp(a)
134 # exp(exp(x) + exp(x**2)).subs(exp(exp(x)), w) -> w * exp(exp(x**2))
135 oarg = old[0]
136 new_l = []
137 old_al = []
138 coeff2,terms2 = oarg.as_coeff_terms()
139 for a in arg:
140 a = a.subs(old, new)
141 coeff1,terms1 = a.as_coeff_terms()
142 if terms1==terms2:
143 new_l.append(new**(coeff1/coeff2))
144 else:
145 old_al.append(a.subs(old, new))
146 if new_l:
147 new_l.append(self.func(Basic.Add(*old_al)))
148 r = Basic.Mul(*new_l)
149 return r
150 old = o
151 return Function._eval_subs(self, old, new)
153 def _eval_is_real(self):
154 return self[0].is_real
155 def _eval_is_positive(self):
156 if self[0].is_real:
157 return True
158 def _eval_is_bounded(self):
159 arg = self[0]
160 if arg.is_unbounded:
161 if arg.is_negative: return True
162 if arg.is_positive: return False
163 if arg.is_bounded:
164 return True
165 if arg.is_real:
166 return False
167 def _eval_is_zero(self):
168 return isinstance(self[0], Basic.NegativeInfinity)
170 def _eval_power(b, e):
171 """exp(b[0])**e -> exp(b[0]*e)"""
172 return exp(b[0] * e)
174 def _eval_oseries(self, order):
175 #XXX quick hack, to pass the tests:
176 #print "XX", self, order
177 #w = Basic.Symbol("w")
178 #if self[0] == (1 + w)*(-log(w) + log(Basic.sin(2*w))):
179 # if order == Basic.Order(w**3,w):
180 # return self
181 # else:
182 # return self
183 #if self[0] == w*log(2*Basic.cos(w)*Basic.sin(w)) - w*log(w):
184 # if order == Basic.Order(w**3,w):
185 # return 1 + w*log(2) + w**2*log(2)**2/2
186 # else:
187 # return Basic.One()
188 #if self[0] == (1 + w)*log(1/w*Basic.sin(2*w)):
189 # return exp((1 + w)*(-log(w) + log(Basic.sin(2*w))))
190 #print "XX2...."
191 #Example:
192 # self = exp(log(1 + x)/x)
193 # order = O(x**2)
195 arg = self[0]
196 # arg = log(1 + x)/x
197 x = order.symbols[0]
198 # x = x
199 if not Basic.Order(1,x).contains(arg): # singularity
200 arg0 = arg.as_leading_term(x)
201 d = (arg-arg0).limit(x, S.Zero)
202 if not isinstance(d, Basic.Zero):
203 return exp(arg)
204 else:
205 # arg = log(1+x)/x ~ O(1)
206 arg0 = arg.limit(x, S.Zero)
207 # arg0 = 1
208 o = order * exp(-arg0)
209 # o = O(x**2) * exp(-1)
210 return self._compute_oseries(arg-arg0, o, exp.taylor_term, exp) * exp(arg0)
212 def _eval_as_leading_term(self, x):
213 arg = self[0]
214 if isinstance(arg, Basic.Add):
215 return Basic.Mul(*[exp(f).as_leading_term(x) for f in arg])
216 arg = self[0].as_leading_term(x)
217 if Basic.Order(1,x).contains(arg):
218 return S.One
219 return exp(arg)
221 def _eval_expand_basic(self, *args):
222 arg = self[0].expand()
223 if isinstance(arg, Basic.Add):
224 expr = 1
225 for x in arg:
226 expr *= self.func(x).expand()
227 return expr
228 return self.func(arg)
230 def _sage_(self):
231 import sage.all as sage
232 return sage.exp(self[0]._sage_())
234 class log(SingleValuedFunction):
236 nofargs = (1,2)
237 is_comparable = True
239 def fdiff(self, argindex=1):
240 if argindex == 1:
241 return 1/self[0]
242 s = Basic.Symbol('x', dummy=True)
243 return Lambda(s**(-1), s)
244 else:
245 raise ArgumentIndexError(self, argindex)
247 def inverse(self, argindex=1):
248 return exp
250 @classmethod
251 def _eval_apply_subs(self, *args):
252 return
254 #XXX: why is the fixme parameter needed here?
255 @classmethod
256 def canonize(cls, arg, base=None, **fixme):
257 if base is not None:
258 base = Basic.sympify(base)
260 if not isinstance(base, Basic.Exp1):
261 return cls(arg)/cls(base)
263 arg = Basic.sympify(arg)
265 if isinstance(arg, Basic.Number):
266 if isinstance(arg, Basic.Zero):
267 return S.NegativeInfinity
268 elif isinstance(arg, Basic.One):
269 return S.Zero
270 elif isinstance(arg, Basic.Infinity):
271 return S.Infinity
272 elif isinstance(arg, Basic.NegativeInfinity):
273 return S.Infinity
274 elif isinstance(arg, Basic.NaN):
275 return S.NaN
276 elif arg.is_negative:
277 return S.Pi * S.ImaginaryUnit + cls(-arg)
278 elif isinstance(arg, Basic.Exp1):
279 return S.One
280 #this doesn't work due to caching: :(
281 #elif isinstance(arg, exp) and arg[0].is_real:
282 #using this one instead:
283 elif isinstance(arg, exp):
284 return arg[0]
285 #this shouldn't happen automatically (see the issue 252):
286 #elif isinstance(arg, Basic.Pow):
287 # if isinstance(arg.exp, Basic.Number) or \
288 # isinstance(arg.exp, Basic.NumberSymbol) or arg.exp.is_number:
289 # return arg.exp * self(arg.base)
290 #elif isinstance(arg, Basic.Mul) and arg.is_real:
291 # return Basic.Add(*[self(a) for a in arg])
292 elif not isinstance(arg, Basic.Add):
293 coeff = arg.as_coefficient(S.ImaginaryUnit)
295 if coeff is not None:
296 if isinstance(coeff, Basic.Infinity):
297 return S.Infinity
298 elif isinstance(coeff, Basic.NegativeInfinity):
299 return S.Infinity
300 elif isinstance(coeff, Basic.Rational):
301 if coeff.is_nonnegative:
302 return S.Pi * S.ImaginaryUnit * S.Half + cls(coeff)
303 else:
304 return -S.Pi * S.ImaginaryUnit * S.Half + cls(-coeff)
306 def as_base_exp(self):
307 return self, S.One
308 #why is this here:?
309 return exp, S.NegativeOne
311 def _calc_apply_positive(self, x):
312 if x.is_positive and x.is_unbounded: return True
314 def _calc_apply_unbounded(self, x):
315 return x.is_unbounded
317 @classmethod
318 @cache_it_immutable
319 def taylor_term(self, n, x, *previous_terms): # of log(1+x)
320 if n<0: return S.Zero
321 x = Basic.sympify(x)
322 if n==0: return x
323 if previous_terms:
324 p = previous_terms[-1]
325 if p is not None:
326 return (-n) * p * x / (n+1)
327 return (1-2*(n%2)) * x**(n+1)/(n+1)
329 def _eval_expand_complex(self, *args):
330 re, im = self[0].as_real_imag()
331 return S.Log(S.Sqrt(re) + S.Sqrt(im)) + \
332 S.ImaginaryUnit * S.Arg(self[0])
334 def _eval_is_real(self):
335 return self[0].is_positive
337 def _eval_is_bounded(self):
338 arg = self[0]
339 if arg.is_infinitesimal:
340 return False
341 return arg.is_bounded
343 def _eval_is_positive(self):
344 arg = self[0]
345 if arg.is_positive:
346 if arg.is_unbounded: return True
347 if arg.is_infinitesimal: return False
348 if isinstance(arg, Basic.Number):
349 return arg>1
351 def _eval_is_zero(self):
352 # XXX This is not quite useless. Try evaluating log(0.5).is_negative
353 # without it. There's probably a nicer way though.
354 return isinstance(self[0], Basic.One)
356 def as_numer_denom(self):
357 n, d = self[0].as_numer_denom()
358 if isinstance(d, Basic.One):
359 return self.func(n), d
360 return (self.func(n) - self.func(d)).as_numer_denom()
362 # similar code must be added to other functions with have singularites
363 # in their domains eg. cot(), tan() ...
364 # the trick is to factor out the singularity and leave it as is, and expand
365 # the rest, that can be expanded.
366 def _eval_oseries(self, order):
367 arg = self[0]
368 x = order.symbols[0]
369 ln = Basic.log
370 use_lt = not Basic.Order(1,x).contains(arg)
371 if not use_lt:
372 arg0 = arg.limit(x, 0)
373 use_lt = isinstance(arg0, Basic.Zero)
374 if use_lt: # singularity, #example: self = log(sin(x))
375 # arg = (arg / lt) * lt
376 lt = arg.as_leading_term(x) # arg = sin(x); lt = x
377 a = (arg/lt).expand() # a = sin(x)/x
378 #the idea is to recursively call ln(a).series(), but the problem
379 #is, that ln(sin(x)/x) gets "simplified" to -log(x)+ln(sin(x)) and
380 #an infinite recursion occurs, see also the issue 252.
381 return ln(lt) + ln(a).oseries(order)
382 # arg -> arg0 + (arg - arg0) -> arg0 * (1 + (arg/arg0 - 1))
383 z = (arg/arg0 - 1)
384 return self._compute_oseries(z, order, ln.taylor_term, lambda z: ln(1+z)) + ln(arg0)
386 def _eval_as_leading_term(self, x):
387 arg = self[0].as_leading_term(x)
388 if isinstance(arg, Basic.One):
389 return (self[0] - 1).as_leading_term(x)
390 return self.func(arg)
392 def _eval_expand_basic(self, *args):
393 arg = self[0]
394 if isinstance(arg, Basic.Mul) and arg.is_real:
395 expr = 0
396 for x in arg:
397 expr += self.func(x).expand()
398 return expr
399 elif isinstance(arg, Basic.Pow):
400 if isinstance(arg.exp, Basic.Number) or \
401 isinstance(arg.exp, Basic.NumberSymbol):
402 return arg.exp * self.func(arg.base).expand()
403 return self
405 #this is a lot faster:
406 @classmethod
407 def _eval_apply_evalf(cls, arg):
408 arg = arg.evalf()
409 if arg.is_number:
410 import math
411 from sympy import Real
412 return Real(math.log(arg))
414 def _sage_(self):
415 import sage.all as sage
416 return sage.log(self[0]._sage_())
418 # MrvLog is used by limit.py
419 class MrvLog(log):
421 def subs(self, old, new):
422 old = Basic.sympify(old)
423 if old==self.func:
424 arg = self[0]
425 new = Basic.sympify(new)
426 return new(arg.subs(old, new))
427 return self