Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
from __future__ import print_function, division
from sympy.core import sympify from sympy.core.add import Add from sympy.core.function import Lambda, Function, ArgumentIndexError from sympy.core.cache import cacheit from sympy.core.numbers import Integer from sympy.core.power import Pow from sympy.core.singleton import S from sympy.core.symbol import Wild, Dummy from sympy.core.mul import Mul from sympy.core.logic import fuzzy_not
from sympy.functions.combinatorial.factorials import factorial from sympy.functions.elementary.miscellaneous import sqrt from sympy.ntheory import multiplicity, perfect_power from sympy.core.compatibility import range
# NOTE IMPORTANT # The series expansion code in this file is an important part of the gruntz # algorithm for determining limits. _eval_nseries has to return a generalized # power series with coefficients in C(log(x), log). # In more detail, the result of _eval_nseries(self, x, n) must be # c_0*x**e_0 + ... (finitely many terms) # where e_i are numbers (not necessarily integers) and c_i involve only # numbers, the function log, and log(x). [This also means it must not contain # log(x(1+p)), this *has* to be expanded to log(x)+log(1+p) if x.is_positive and # p.is_positive.]
class ExpBase(Function):
unbranched = True
def inverse(self, argindex=1): """ Returns the inverse function of ``exp(x)``. """
def as_numer_denom(self): """ Returns this with a positive exponent as a 2-tuple (a fraction).
Examples ========
>>> from sympy.functions import exp >>> from sympy.abc import x >>> exp(-x).as_numer_denom() (1, exp(x)) >>> exp(x).as_numer_denom() (exp(x), 1) """ # this should be the same as Pow.as_numer_denom wrt # exponent handling
@property def exp(self): """ Returns the exponent of the function. """
def as_base_exp(self): """ Returns the 2-tuple (base, exponent). """
def _eval_conjugate(self):
def _eval_is_finite(self): if arg.is_negative: return True if arg.is_positive: return False
def _eval_is_rational(self): return True else: return s.is_rational
def _eval_is_zero(self):
def _eval_power(self, other): """exp(arg)**e -> exp(arg*e) if assumptions allow it. """
def _eval_expand_power_exp(self, **hints):
class exp_polar(ExpBase): r""" Represent a 'polar number' (see g-function Sphinx documentation).
``exp_polar`` represents the function `Exp: \mathbb{C} \rightarrow \mathcal{S}`, sending the complex number `z = a + bi` to the polar number `r = exp(a), \theta = b`. It is one of the main functions to construct polar numbers.
>>> from sympy import exp_polar, pi, I, exp
The main difference is that polar numbers don't "wrap around" at `2 \pi`:
>>> exp(2*pi*I) 1 >>> exp_polar(2*pi*I) exp_polar(2*I*pi)
apart from that they behave mostly like classical complex numbers:
>>> exp_polar(2)*exp_polar(3) exp_polar(5)
See also ========
sympy.simplify.simplify.powsimp sympy.functions.elementary.complexes.polar_lift sympy.functions.elementary.complexes.periodic_argument sympy.functions.elementary.complexes.principal_branch """
is_polar = True is_comparable = False # cannot be evalf'd
def _eval_Abs(self): from sympy import expand_mul return sqrt( expand_mul(self * self.conjugate()) )
def _eval_evalf(self, prec): """ Careful! any evalf of polar numbers is flaky """ from sympy import im, pi, re i = im(self.args[0]) try: bad = (i <= -pi or i > pi) except TypeError: bad = True if bad: return self # cannot evalf for this argument res = exp(self.args[0])._eval_evalf(prec) if i > 0 and im(res) < 0: # i ~ pi, but exp(I*i) evaluated to argument slightly bigger than pi return re(res) return res
def _eval_power(self, other): return self.func(self.args[0]*other)
def _eval_is_real(self): if self.args[0].is_real: return True
def as_base_exp(self): # XXX exp_polar(0) is special! if self.args[0] == 0: return self, S(1) return ExpBase.as_base_exp(self)
class exp(ExpBase): """ The exponential function, :math:`e^x`.
See Also ========
log """
def fdiff(self, argindex=1): """ Returns the first derivative of this function. """ else: raise ArgumentIndexError(self, argindex)
def _eval_refine(self, assumptions): from sympy.assumptions import ask, Q arg = self.args[0] if arg.is_Mul: Ioo = S.ImaginaryUnit*S.Infinity if arg in [Ioo, -Ioo]: return S.NaN
coeff = arg.as_coefficient(S.Pi*S.ImaginaryUnit) if coeff: if ask(Q.integer(2*coeff)): if ask(Q.even(coeff)): return S.One elif ask(Q.odd(coeff)): return S.NegativeOne elif ask(Q.even(coeff + S.Half)): return -S.ImaginaryUnit elif ask(Q.odd(coeff + S.Half)): return S.ImaginaryUnit
@classmethod def eval(cls, arg): return S.NaN return S.Infinity return S.Zero return AccumBounds(exp(arg.min), exp(arg.max)) return S.One return -S.ImaginaryUnit
# Warning: code in risch.py will be very sensitive to changes # in this (see DifferentialExtension).
# look for a single log factor
# but it can't be multiplied by oo return None
else: return None else:
add.append(a) continue else: out.append(newa) return Mul(*out)*cls(Add(*add), evaluate=False)
return arg.exp()
@property def base(self): """ Returns the base of the exponential function. """ return S.Exp1
@staticmethod @cacheit def taylor_term(n, x, *previous_terms): """ Calculates the next term in the Taylor series expansion. """ if n < 0: return S.Zero if n == 0: return S.One x = sympify(x) if previous_terms: p = previous_terms[-1] if p is not None: return p * x / n return x**n/factorial(n)
def as_real_imag(self, deep=True, **hints): """ Returns this function as a 2-tuple representing a complex number.
Examples ========
>>> from sympy import I >>> from sympy.abc import x >>> from sympy.functions import exp >>> exp(x).as_real_imag() (exp(re(x))*cos(im(x)), exp(re(x))*sin(im(x))) >>> exp(1).as_real_imag() (E, 0) >>> exp(I).as_real_imag() (cos(1), sin(1)) >>> exp(1+I).as_real_imag() (E*cos(1), E*sin(1))
See Also ========
sympy.functions.elementary.complexes.re sympy.functions.elementary.complexes.im """
def _eval_subs(self, old, new): # keep processing of power-like args centralized in Pow old = exp(old.exp*log(old.base)) old = exp a.is_Pow or a.func is exp) else a
return new**self.exp._subs(old, new)
def _eval_is_real(self):
def _eval_is_algebraic(self): return False else: return s.is_algebraic
def _eval_is_positive(self):
def _eval_nseries(self, x, n, logx): # NOTE Please see the comment at the beginning of this file, labelled # IMPORTANT. from sympy import limit, oo, Order, powsimp arg = self.args[0] arg_series = arg._eval_nseries(x, n=n, logx=logx) if arg_series.is_Order: return 1 + arg_series arg0 = limit(arg_series.removeO(), x, 0) if arg0 in [-oo, oo]: return self t = Dummy("t") exp_series = exp(t)._taylor(t, n) o = exp_series.getO() exp_series = exp_series.removeO() r = exp(arg0)*exp_series.subs(t, arg_series - arg0) r += Order(o.expr.subs(t, (arg_series - arg0)), x) r = r.expand() return powsimp(r, deep=True, combine='exp')
def _taylor(self, x, n): from sympy import Order l = [] g = None for i in range(n): g = self.taylor_term(i, self.args[0], g) g = g.nseries(x, n=n) l.append(g) return Add(*l) + Order(x**n, x)
def _eval_as_leading_term(self, x): from sympy import Order arg = self.args[0] if arg.is_Add: return Mul(*[exp(f).as_leading_term(x) for f in arg.args]) arg = self.args[0].as_leading_term(x) if Order(1, x).contains(arg): return S.One return exp(arg)
def _eval_rewrite_as_sin(self, arg): from sympy import sin I = S.ImaginaryUnit return sin(I*arg + S.Pi/2) - I*sin(I*arg)
def _eval_rewrite_as_cos(self, arg):
def _eval_rewrite_as_tanh(self, arg): from sympy import tanh return (1 + tanh(arg/2))/(1 - tanh(arg/2))
class log(Function): """ The natural logarithm function `\ln(x)` or `\log(x)`. Logarithms are taken with the natural base, `e`. To get a logarithm of a different base ``b``, use ``log(x, b)``, which is essentially short-hand for ``log(x)/log(b)``.
See Also ========
exp """
def fdiff(self, argindex=1): """ Returns the first derivative of the function. """ s = Dummy('x') return Lambda(s**(-1), s) else: raise ArgumentIndexError(self, argindex)
def inverse(self, argindex=1): """ Returns `e^x`, the inverse function of `\log(x)`. """
@classmethod def eval(cls, arg, base=None):
if arg == 1: return S.NaN else: return S.ComplexInfinity # handle extraction of powers of the base now # or else expand_log in Mul would have to handle this else: return n + log(arg / den) / log(base) else: return log(arg)/log(base) except ValueError: pass if base is not S.Exp1: return cls(arg)/cls(base) else: return cls(arg)
return S.Infinity return S.Infinity return S.NaN
return unpolarify(arg.exp) if arg.min.is_positive: return AccumBounds(log(arg.min), log(arg.max)) else: return
return S.ComplexInfinity
# don't autoexpand Pow or Mul (see the issue 3351):
return S.Infinity return S.Infinity else: return -S.Pi * S.ImaginaryUnit * S.Half + cls(-coeff)
def as_base_exp(self): """ Returns this function in the form (base, exponent). """
@staticmethod @cacheit def taylor_term(n, x, *previous_terms): # of log(1+x) """ Returns the next term in the Taylor series expansion of `\log(1+x)`. """ from sympy import powsimp if n < 0: return S.Zero x = sympify(x) if n == 0: return x if previous_terms: p = previous_terms[-1] if p is not None: return powsimp((-n) * p * x / (n + 1), deep=True, combine='exp') return (1 - 2*(n % 2)) * x**(n + 1)/(n + 1)
def _eval_expand_log(self, deep=True, **hints): return expand_log(self.func(*self.args), deep=deep, force=force) # remove perfect powers a = self.func(x) if isinstance(a, log): expr.append(self.func(x)._eval_expand_log(**hints)) else: expr.append(a) a = self.func(-x) expr.append(a) nonpos.append(S.NegativeOne) else: arg.base.is_polar: else: return unpolarify(e) * a if arg.function.is_positive: return Sum(log(arg.function), *arg.limits)
def _eval_simplify(self, ratio, measure): return simplify(self.func(*self.args), ratio=ratio, measure=measure)
def as_real_imag(self, deep=True, **hints): """ Returns this function as a complex coordinate.
Examples ========
>>> from sympy import I >>> from sympy.abc import x >>> from sympy.functions import log >>> log(x).as_real_imag() (log(Abs(x)), arg(x)) >>> log(I).as_real_imag() (0, pi/2) >>> log(1 + I).as_real_imag() (log(sqrt(2)), pi/4) >>> log(I*x).as_real_imag() (log(Abs(x)), arg(I*x))
""" else: abs = Abs(self.args[0]) arg = arg(self.args[0]) hints['complex'] = False return (log(abs).expand(deep, **hints), arg) else:
def _eval_is_rational(self): return True else: return s.is_rational
def _eval_is_algebraic(self): return True else: return s.is_algebraic
def _eval_is_real(self):
def _eval_is_finite(self): return False
def _eval_is_positive(self):
def _eval_is_zero(self):
def _eval_is_nonnegative(self):
def _eval_nseries(self, x, n, logx): # NOTE Please see the comment at the beginning of this file, labelled # IMPORTANT. logx = log(x) return logx
# TODO new and probably slow s = self.args[0].nseries(x, n=n, logx=logx) while s.is_Order: n += 1 s = self.args[0].nseries(x, n=n, logx=logx) a, b = s.leadterm(x) p = cancel(s/(a*x**b) - 1) g = None l = [] for i in range(n + 2): g = log.taylor_term(i, p, g) g = g.nseries(x, n=n, logx=logx) l.append(g) return log(a) + b*logx + Add(*l) + Order(p**n, x)
def _eval_as_leading_term(self, x): return (self.args[0] - 1).as_leading_term(x)
class LambertW(Function): """ The Lambert W function `W(z)` is defined as the inverse function of `w \exp(w)` [1]_.
In other words, the value of `W(z)` is such that `z = W(z) \exp(W(z))` for any complex number `z`. The Lambert W function is a multivalued function with infinitely many branches `W_k(z)`, indexed by `k \in \mathbb{Z}`. Each branch gives a different solution `w` of the equation `z = w \exp(w)`.
The Lambert W function has two partially real branches: the principal branch (`k = 0`) is real for real `z > -1/e`, and the `k = -1` branch is real for `-1/e < z < 0`. All branches except `k = 0` have a logarithmic singularity at `z = 0`.
Examples ========
>>> from sympy import LambertW >>> LambertW(1.2) 0.635564016364870 >>> LambertW(1.2, -1).n() -1.34747534407696 - 4.41624341514535*I >>> LambertW(-1).is_real False
References ==========
.. [1] http://en.wikipedia.org/wiki/Lambert_W_function """
@classmethod def eval(cls, x, k=None): return cls(x)
return S.Zero return S.One return S.NegativeOne return -log(2) return S.Infinity
if x is S.Zero: return S.NegativeInfinity if x == -S.Pi/2: return -S.ImaginaryUnit*S.Pi/2 elif x == -1/S.Exp1: return S.NegativeOne elif x == -2*exp(-2): return -Integer(2)
def fdiff(self, argindex=1): """ Return the first derivative of this function. """ x = self.args[0]
if len(self.args) == 1: if argindex == 1: return LambertW(x)/(x*(1 + LambertW(x))) else: k = self.args[1] if argindex == 1: return LambertW(x, k)/(x*(1 + LambertW(x, k)))
raise ArgumentIndexError(self, argindex)
def _eval_is_real(self): x = self.args[0] if len(self.args) == 1: k = S.Zero else: k = self.args[1] if k.is_zero: if (x + 1/S.Exp1).is_positive: return True elif (x + 1/S.Exp1).is_nonpositive: return False elif (k + 1).is_zero: if x.is_negative and (x + 1/S.Exp1).is_positive: return True elif x.is_nonpositive or (x + 1/S.Exp1).is_nonnegative: return False elif fuzzy_not(k.is_zero) and fuzzy_not((k + 1).is_zero): if x.is_real: return False
def _eval_is_algebraic(self): s = self.func(*self.args) if s.func == self.func: if fuzzy_not(self.args[0].is_zero) and self.args[0].is_algebraic: return False else: return s.is_algebraic
from sympy.core.function import _coeff_isneg |