1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291 |
- from itertools import product
- from typing import Tuple as tTuple
- from sympy.core.add import Add
- from sympy.core.cache import cacheit
- from sympy.core.expr import Expr
- from sympy.core.function import (Function, ArgumentIndexError, expand_log,
- expand_mul, FunctionClass, PoleError, expand_multinomial, expand_complex)
- from sympy.core.logic import fuzzy_and, fuzzy_not, fuzzy_or
- from sympy.core.mul import Mul
- from sympy.core.numbers import Integer, Rational, pi, I, ImaginaryUnit
- from sympy.core.parameters import global_parameters
- from sympy.core.power import Pow
- from sympy.core.singleton import S
- from sympy.core.symbol import Wild, Dummy
- from sympy.core.sympify import sympify
- from sympy.functions.combinatorial.factorials import factorial
- from sympy.functions.elementary.complexes import arg, unpolarify, im, re, Abs
- from sympy.functions.elementary.miscellaneous import sqrt
- from sympy.ntheory import multiplicity, perfect_power
- from sympy.ntheory.factor_ import factorint
- # 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
- _singularities = (S.ComplexInfinity,)
- @property
- def kind(self):
- return self.exp.kind
- def inverse(self, argindex=1):
- """
- Returns the inverse function of ``exp(x)``.
- """
- return log
- def as_numer_denom(self):
- """
- Returns this with a positive exponent as a 2-tuple (a fraction).
- Examples
- ========
- >>> from sympy 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
- exp = self.exp
- neg_exp = exp.is_negative
- if not neg_exp and not (-exp).is_negative:
- neg_exp = exp.could_extract_minus_sign()
- if neg_exp:
- return S.One, self.func(-exp)
- return self, S.One
- @property
- def exp(self):
- """
- Returns the exponent of the function.
- """
- return self.args[0]
- def as_base_exp(self):
- """
- Returns the 2-tuple (base, exponent).
- """
- return self.func(1), Mul(*self.args)
- def _eval_adjoint(self):
- return self.func(self.exp.adjoint())
- def _eval_conjugate(self):
- return self.func(self.exp.conjugate())
- def _eval_transpose(self):
- return self.func(self.exp.transpose())
- def _eval_is_finite(self):
- arg = self.exp
- if arg.is_infinite:
- if arg.is_extended_negative:
- return True
- if arg.is_extended_positive:
- return False
- if arg.is_finite:
- return True
- def _eval_is_rational(self):
- s = self.func(*self.args)
- if s.func == self.func:
- z = s.exp.is_zero
- if z:
- return True
- elif s.exp.is_rational and fuzzy_not(z):
- return False
- else:
- return s.is_rational
- def _eval_is_zero(self):
- return self.exp is S.NegativeInfinity
- def _eval_power(self, other):
- """exp(arg)**e -> exp(arg*e) if assumptions allow it.
- """
- b, e = self.as_base_exp()
- return Pow._eval_power(Pow(b, e, evaluate=False), other)
- def _eval_expand_power_exp(self, **hints):
- from sympy.concrete.products import Product
- from sympy.concrete.summations import Sum
- arg = self.args[0]
- if arg.is_Add and arg.is_commutative:
- return Mul.fromiter(self.func(x) for x in arg.args)
- elif isinstance(arg, Sum) and arg.is_commutative:
- return Product(self.func(arg.function), *arg.limits)
- return self.func(arg)
- class exp_polar(ExpBase):
- r"""
- Represent a *polar number* (see g-function Sphinx documentation).
- Explanation
- ===========
- ``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.
- Examples
- ========
- >>> from sympy import exp_polar, pi, I, exp
- The main difference is that polar numbers do not "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.powsimp.powsimp
- polar_lift
- periodic_argument
- principal_branch
- """
- is_polar = True
- is_comparable = False # cannot be evalf'd
- def _eval_Abs(self): # Abs is never a polar number
- return exp(re(self.args[0]))
- def _eval_evalf(self, prec):
- """ Careful! any evalf of polar numbers is flaky """
- 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_extended_real(self):
- if self.args[0].is_extended_real:
- return True
- def as_base_exp(self):
- # XXX exp_polar(0) is special!
- if self.args[0] == 0:
- return self, S.One
- return ExpBase.as_base_exp(self)
- class ExpMeta(FunctionClass):
- def __instancecheck__(cls, instance):
- if exp in instance.__class__.__mro__:
- return True
- return isinstance(instance, Pow) and instance.base is S.Exp1
- class exp(ExpBase, metaclass=ExpMeta):
- """
- The exponential function, :math:`e^x`.
- Examples
- ========
- >>> from sympy import exp, I, pi
- >>> from sympy.abc import x
- >>> exp(x)
- exp(x)
- >>> exp(x).diff(x)
- exp(x)
- >>> exp(I*pi)
- -1
- Parameters
- ==========
- arg : Expr
- See Also
- ========
- log
- """
- def fdiff(self, argindex=1):
- """
- Returns the first derivative of this function.
- """
- if argindex == 1:
- return self
- 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 = I*S.Infinity
- if arg in [Ioo, -Ioo]:
- return S.NaN
- coeff = arg.as_coefficient(pi*I)
- 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 -I
- elif ask(Q.odd(coeff + S.Half)):
- return I
- @classmethod
- def eval(cls, arg):
- from sympy.calculus import AccumBounds
- from sympy.matrices.matrices import MatrixBase
- from sympy.sets.setexpr import SetExpr
- from sympy.simplify.simplify import logcombine
- if isinstance(arg, MatrixBase):
- return arg.exp()
- elif global_parameters.exp_is_pow:
- return Pow(S.Exp1, arg)
- elif arg.is_Number:
- if arg is S.NaN:
- return S.NaN
- elif arg.is_zero:
- return S.One
- elif arg is S.One:
- return S.Exp1
- elif arg is S.Infinity:
- return S.Infinity
- elif arg is S.NegativeInfinity:
- return S.Zero
- elif arg is S.ComplexInfinity:
- return S.NaN
- elif isinstance(arg, log):
- return arg.args[0]
- elif isinstance(arg, AccumBounds):
- return AccumBounds(exp(arg.min), exp(arg.max))
- elif isinstance(arg, SetExpr):
- return arg._eval_func(cls)
- elif arg.is_Mul:
- coeff = arg.as_coefficient(pi*I)
- if coeff:
- if (2*coeff).is_integer:
- if coeff.is_even:
- return S.One
- elif coeff.is_odd:
- return S.NegativeOne
- elif (coeff + S.Half).is_even:
- return -I
- elif (coeff + S.Half).is_odd:
- return I
- elif coeff.is_Rational:
- ncoeff = coeff % 2 # restrict to [0, 2pi)
- if ncoeff > 1: # restrict to (-pi, pi]
- ncoeff -= 2
- if ncoeff != coeff:
- return cls(ncoeff*pi*I)
- # Warning: code in risch.py will be very sensitive to changes
- # in this (see DifferentialExtension).
- # look for a single log factor
- coeff, terms = arg.as_coeff_Mul()
- # but it can't be multiplied by oo
- if coeff in [S.NegativeInfinity, S.Infinity]:
- if terms.is_number:
- if coeff is S.NegativeInfinity:
- terms = -terms
- if re(terms).is_zero and terms is not S.Zero:
- return S.NaN
- if re(terms).is_positive and im(terms) is not S.Zero:
- return S.ComplexInfinity
- if re(terms).is_negative:
- return S.Zero
- return None
- coeffs, log_term = [coeff], None
- for term in Mul.make_args(terms):
- term_ = logcombine(term)
- if isinstance(term_, log):
- if log_term is None:
- log_term = term_.args[0]
- else:
- return None
- elif term.is_comparable:
- coeffs.append(term)
- else:
- return None
- return log_term**Mul(*coeffs) if log_term else None
- elif arg.is_Add:
- out = []
- add = []
- argchanged = False
- for a in arg.args:
- if a is S.One:
- add.append(a)
- continue
- newa = cls(a)
- if isinstance(newa, cls):
- if newa.args[0] != a:
- add.append(newa.args[0])
- argchanged = True
- else:
- add.append(a)
- else:
- out.append(newa)
- if out or argchanged:
- return Mul(*out)*cls(Add(*add), evaluate=False)
- if arg.is_zero:
- return S.One
- @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 exp, I
- >>> from sympy.abc import x
- >>> 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
- """
- from sympy.functions.elementary.trigonometric import cos, sin
- re, im = self.args[0].as_real_imag()
- if deep:
- re = re.expand(deep, **hints)
- im = im.expand(deep, **hints)
- cos, sin = cos(im), sin(im)
- return (exp(re)*cos, exp(re)*sin)
- def _eval_subs(self, old, new):
- # keep processing of power-like args centralized in Pow
- if old.is_Pow: # handle (exp(3*log(x))).subs(x**2, z) -> z**(3/2)
- old = exp(old.exp*log(old.base))
- elif old is S.Exp1 and new.is_Function:
- old = exp
- if isinstance(old, exp) or old is S.Exp1:
- f = lambda a: Pow(*a.as_base_exp(), evaluate=False) if (
- a.is_Pow or isinstance(a, exp)) else a
- return Pow._eval_subs(f(self), f(old), new)
- if old is exp and not new.is_Function:
- return new**self.exp._subs(old, new)
- return Function._eval_subs(self, old, new)
- def _eval_is_extended_real(self):
- if self.args[0].is_extended_real:
- return True
- elif self.args[0].is_imaginary:
- arg2 = -S(2) * I * self.args[0] / pi
- return arg2.is_even
- def _eval_is_complex(self):
- def complex_extended_negative(arg):
- yield arg.is_complex
- yield arg.is_extended_negative
- return fuzzy_or(complex_extended_negative(self.args[0]))
- def _eval_is_algebraic(self):
- if (self.exp / pi / I).is_rational:
- return True
- if fuzzy_not(self.exp.is_zero):
- if self.exp.is_algebraic:
- return False
- elif (self.exp / pi).is_rational:
- return False
- def _eval_is_extended_positive(self):
- if self.exp.is_extended_real:
- return self.args[0] is not S.NegativeInfinity
- elif self.exp.is_imaginary:
- arg2 = -I * self.args[0] / pi
- return arg2.is_even
- def _eval_nseries(self, x, n, logx, cdir=0):
- # NOTE Please see the comment at the beginning of this file, labelled
- # IMPORTANT.
- from sympy.functions.elementary.complexes import sign
- from sympy.functions.elementary.integers import ceiling
- from sympy.series.limits import limit
- from sympy.series.order import Order
- from sympy.simplify.powsimp import powsimp
- arg = self.exp
- 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 is S.NegativeInfinity:
- return Order(x**n, x)
- if arg0 is S.Infinity:
- return self
- # checking for indecisiveness/ sign terms in arg0
- if any(isinstance(arg, (sign, ImaginaryUnit)) for arg in arg0.args):
- return self
- t = Dummy("t")
- nterms = n
- try:
- cf = Order(arg.as_leading_term(x, logx=logx), x).getn()
- except (NotImplementedError, PoleError):
- cf = 0
- if cf and cf > 0:
- nterms = ceiling(n/cf)
- exp_series = exp(t)._taylor(t, nterms)
- r = exp(arg0)*exp_series.subs(t, arg_series - arg0)
- rep = {logx: log(x)} if logx is not None else {}
- if r.subs(rep) == self:
- return r
- if cf and cf > 1:
- r += Order((arg_series - arg0)**n, x)/x**((cf-1)*n)
- else:
- r += Order((arg_series - arg0)**n, x)
- r = r.expand()
- r = powsimp(r, deep=True, combine='exp')
- # powsimp may introduce unexpanded (-1)**Rational; see PR #17201
- simplerat = lambda x: x.is_Rational and x.q in [3, 4, 6]
- w = Wild('w', properties=[simplerat])
- r = r.replace(S.NegativeOne**w, expand_complex(S.NegativeOne**w))
- return r
- def _taylor(self, x, n):
- 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.removeO())
- return Add(*l)
- def _eval_as_leading_term(self, x, logx=None, cdir=0):
- from sympy.calculus.util import AccumBounds
- arg = self.args[0].cancel().as_leading_term(x, logx=logx)
- arg0 = arg.subs(x, 0)
- if arg is S.NaN:
- return S.NaN
- if isinstance(arg0, AccumBounds):
- # This check addresses a corner case involving AccumBounds.
- # if isinstance(arg, AccumBounds) is True, then arg0 can either be 0,
- # AccumBounds(-oo, 0) or AccumBounds(-oo, oo).
- # Check out function: test_issue_18473() in test_exponential.py and
- # test_limits.py for more information.
- if re(cdir) < S.Zero:
- return exp(-arg0)
- return exp(arg0)
- if arg0 is S.NaN:
- arg0 = arg.limit(x, 0)
- if arg0.is_infinite is False:
- return exp(arg0)
- raise PoleError("Cannot expand %s around 0" % (self))
- def _eval_rewrite_as_sin(self, arg, **kwargs):
- from sympy.functions.elementary.trigonometric import sin
- return sin(I*arg + pi/2) - I*sin(I*arg)
- def _eval_rewrite_as_cos(self, arg, **kwargs):
- from sympy.functions.elementary.trigonometric import cos
- return cos(I*arg) + I*cos(I*arg + pi/2)
- def _eval_rewrite_as_tanh(self, arg, **kwargs):
- from sympy.functions.elementary.hyperbolic import tanh
- return (1 + tanh(arg/2))/(1 - tanh(arg/2))
- def _eval_rewrite_as_sqrt(self, arg, **kwargs):
- from sympy.functions.elementary.trigonometric import sin, cos
- if arg.is_Mul:
- coeff = arg.coeff(pi*I)
- if coeff and coeff.is_number:
- cosine, sine = cos(pi*coeff), sin(pi*coeff)
- if not isinstance(cosine, cos) and not isinstance (sine, sin):
- return cosine + I*sine
- def _eval_rewrite_as_Pow(self, arg, **kwargs):
- if arg.is_Mul:
- logs = [a for a in arg.args if isinstance(a, log) and len(a.args) == 1]
- if logs:
- return Pow(logs[0].args[0], arg.coeff(logs[0]))
- def match_real_imag(expr):
- r"""
- Try to match expr with $a + Ib$ for real $a$ and $b$.
- ``match_real_imag`` returns a tuple containing the real and imaginary
- parts of expr or ``(None, None)`` if direct matching is not possible. Contrary
- to :func:`~.re()`, :func:`~.im()``, and ``as_real_imag()``, this helper will not force things
- by returning expressions themselves containing ``re()`` or ``im()`` and it
- does not expand its argument either.
- """
- r_, i_ = expr.as_independent(I, as_Add=True)
- if i_ == 0 and r_.is_real:
- return (r_, i_)
- i_ = i_.as_coefficient(I)
- if i_ and i_.is_real and r_.is_real:
- return (r_, i_)
- else:
- return (None, None) # simpler to check for than None
- class log(Function):
- r"""
- The natural logarithm function `\ln(x)` or `\log(x)`.
- Explanation
- ===========
- 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)``.
- ``log`` represents the principal branch of the natural
- logarithm. As such it has a branch cut along the negative
- real axis and returns values having a complex argument in
- `(-\pi, \pi]`.
- Examples
- ========
- >>> from sympy import log, sqrt, S, I
- >>> log(8, 2)
- 3
- >>> log(S(8)/3, 2)
- -log(3)/log(2) + 3
- >>> log(-1 + I*sqrt(3))
- log(2) + 2*I*pi/3
- See Also
- ========
- exp
- """
- args: tTuple[Expr]
- _singularities = (S.Zero, S.ComplexInfinity)
- def fdiff(self, argindex=1):
- """
- Returns the first derivative of the function.
- """
- if argindex == 1:
- return 1/self.args[0]
- else:
- raise ArgumentIndexError(self, argindex)
- def inverse(self, argindex=1):
- r"""
- Returns `e^x`, the inverse function of `\log(x)`.
- """
- return exp
- @classmethod
- def eval(cls, arg, base=None):
- from sympy.calculus import AccumBounds
- from sympy.sets.setexpr import SetExpr
- arg = sympify(arg)
- if base is not None:
- base = sympify(base)
- if base == 1:
- if arg == 1:
- return S.NaN
- else:
- return S.ComplexInfinity
- try:
- # handle extraction of powers of the base now
- # or else expand_log in Mul would have to handle this
- n = multiplicity(base, arg)
- if n:
- return n + log(arg / base**n) / 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)
- if arg.is_Number:
- if arg.is_zero:
- return S.ComplexInfinity
- elif arg is S.One:
- return S.Zero
- elif arg is S.Infinity:
- return S.Infinity
- elif arg is S.NegativeInfinity:
- return S.Infinity
- elif arg is S.NaN:
- return S.NaN
- elif arg.is_Rational and arg.p == 1:
- return -cls(arg.q)
- if arg.is_Pow and arg.base is S.Exp1 and arg.exp.is_extended_real:
- return arg.exp
- if isinstance(arg, exp) and arg.exp.is_extended_real:
- return arg.exp
- elif isinstance(arg, exp) and arg.exp.is_number:
- r_, i_ = match_real_imag(arg.exp)
- if i_ and i_.is_comparable:
- i_ %= 2*pi
- if i_ > pi:
- i_ -= 2*pi
- return r_ + expand_mul(i_ * I, deep=False)
- elif isinstance(arg, exp_polar):
- return unpolarify(arg.exp)
- elif isinstance(arg, AccumBounds):
- if arg.min.is_positive:
- return AccumBounds(log(arg.min), log(arg.max))
- elif arg.min.is_zero:
- return AccumBounds(S.NegativeInfinity, log(arg.max))
- else:
- return S.NaN
- elif isinstance(arg, SetExpr):
- return arg._eval_func(cls)
- if arg.is_number:
- if arg.is_negative:
- return pi * I + cls(-arg)
- elif arg is S.ComplexInfinity:
- return S.ComplexInfinity
- elif arg is S.Exp1:
- return S.One
- if arg.is_zero:
- return S.ComplexInfinity
- # don't autoexpand Pow or Mul (see the issue 3351):
- if not arg.is_Add:
- coeff = arg.as_coefficient(I)
- if coeff is not None:
- if coeff is S.Infinity:
- return S.Infinity
- elif coeff is S.NegativeInfinity:
- return S.Infinity
- elif coeff.is_Rational:
- if coeff.is_nonnegative:
- return pi * I * S.Half + cls(coeff)
- else:
- return -pi * I * S.Half + cls(-coeff)
- if arg.is_number and arg.is_algebraic:
- # Match arg = coeff*(r_ + i_*I) with coeff>0, r_ and i_ real.
- coeff, arg_ = arg.as_independent(I, as_Add=False)
- if coeff.is_negative:
- coeff *= -1
- arg_ *= -1
- arg_ = expand_mul(arg_, deep=False)
- r_, i_ = arg_.as_independent(I, as_Add=True)
- i_ = i_.as_coefficient(I)
- if coeff.is_real and i_ and i_.is_real and r_.is_real:
- if r_.is_zero:
- if i_.is_positive:
- return pi * I * S.Half + cls(coeff * i_)
- elif i_.is_negative:
- return -pi * I * S.Half + cls(coeff * -i_)
- else:
- from sympy.simplify import ratsimp
- # Check for arguments involving rational multiples of pi
- t = (i_/r_).cancel()
- t1 = (-t).cancel()
- atan_table = _log_atan_table()
- if t in atan_table:
- modulus = ratsimp(coeff * Abs(arg_))
- if r_.is_positive:
- return cls(modulus) + I * atan_table[t]
- else:
- return cls(modulus) + I * (atan_table[t] - pi)
- elif t1 in atan_table:
- modulus = ratsimp(coeff * Abs(arg_))
- if r_.is_positive:
- return cls(modulus) + I * (-atan_table[t1])
- else:
- return cls(modulus) + I * (pi - atan_table[t1])
- def as_base_exp(self):
- """
- Returns this function in the form (base, exponent).
- """
- return self, S.One
- @staticmethod
- @cacheit
- def taylor_term(n, x, *previous_terms): # of log(1+x)
- r"""
- Returns the next term in the Taylor series expansion of `\log(1+x)`.
- """
- from sympy.simplify.powsimp 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):
- from sympy.concrete import Sum, Product
- force = hints.get('force', False)
- factor = hints.get('factor', False)
- if (len(self.args) == 2):
- return expand_log(self.func(*self.args), deep=deep, force=force)
- arg = self.args[0]
- if arg.is_Integer:
- # remove perfect powers
- p = perfect_power(arg)
- logarg = None
- coeff = 1
- if p is not False:
- arg, coeff = p
- logarg = self.func(arg)
- # expand as product of its prime factors if factor=True
- if factor:
- p = factorint(arg)
- if arg not in p.keys():
- logarg = sum(n*log(val) for val, n in p.items())
- if logarg is not None:
- return coeff*logarg
- elif arg.is_Rational:
- return log(arg.p) - log(arg.q)
- elif arg.is_Mul:
- expr = []
- nonpos = []
- for x in arg.args:
- if force or x.is_positive or x.is_polar:
- a = self.func(x)
- if isinstance(a, log):
- expr.append(self.func(x)._eval_expand_log(**hints))
- else:
- expr.append(a)
- elif x.is_negative:
- a = self.func(-x)
- expr.append(a)
- nonpos.append(S.NegativeOne)
- else:
- nonpos.append(x)
- return Add(*expr) + log(Mul(*nonpos))
- elif arg.is_Pow or isinstance(arg, exp):
- if force or (arg.exp.is_extended_real and (arg.base.is_positive or ((arg.exp+1)
- .is_positive and (arg.exp-1).is_nonpositive))) or arg.base.is_polar:
- b = arg.base
- e = arg.exp
- a = self.func(b)
- if isinstance(a, log):
- return unpolarify(e) * a._eval_expand_log(**hints)
- else:
- return unpolarify(e) * a
- elif isinstance(arg, Product):
- if force or arg.function.is_positive:
- return Sum(log(arg.function), *arg.limits)
- return self.func(arg)
- def _eval_simplify(self, **kwargs):
- from sympy.simplify.simplify import expand_log, simplify, inversecombine
- if len(self.args) == 2: # it's unevaluated
- return simplify(self.func(*self.args), **kwargs)
- expr = self.func(simplify(self.args[0], **kwargs))
- if kwargs['inverse']:
- expr = inversecombine(expr)
- expr = expand_log(expr, deep=True)
- return min([expr, self], key=kwargs['measure'])
- def as_real_imag(self, deep=True, **hints):
- """
- Returns this function as a complex coordinate.
- Examples
- ========
- >>> from sympy import I, log
- >>> from sympy.abc import x
- >>> 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))
- """
- sarg = self.args[0]
- if deep:
- sarg = self.args[0].expand(deep, **hints)
- sarg_abs = Abs(sarg)
- if sarg_abs == sarg:
- return self, S.Zero
- sarg_arg = arg(sarg)
- if hints.get('log', False): # Expand the log
- hints['complex'] = False
- return (log(sarg_abs).expand(deep, **hints), sarg_arg)
- else:
- return log(sarg_abs), sarg_arg
- def _eval_is_rational(self):
- s = self.func(*self.args)
- if s.func == self.func:
- if (self.args[0] - 1).is_zero:
- return True
- if s.args[0].is_rational and fuzzy_not((self.args[0] - 1).is_zero):
- return False
- else:
- return s.is_rational
- def _eval_is_algebraic(self):
- s = self.func(*self.args)
- if s.func == self.func:
- if (self.args[0] - 1).is_zero:
- return True
- elif fuzzy_not((self.args[0] - 1).is_zero):
- if self.args[0].is_algebraic:
- return False
- else:
- return s.is_algebraic
- def _eval_is_extended_real(self):
- return self.args[0].is_extended_positive
- def _eval_is_complex(self):
- z = self.args[0]
- return fuzzy_and([z.is_complex, fuzzy_not(z.is_zero)])
- def _eval_is_finite(self):
- arg = self.args[0]
- if arg.is_zero:
- return False
- return arg.is_finite
- def _eval_is_extended_positive(self):
- return (self.args[0] - 1).is_extended_positive
- def _eval_is_zero(self):
- return (self.args[0] - 1).is_zero
- def _eval_is_extended_nonnegative(self):
- return (self.args[0] - 1).is_extended_nonnegative
- def _eval_nseries(self, x, n, logx, cdir=0):
- # NOTE Please see the comment at the beginning of this file, labelled
- # IMPORTANT.
- from sympy.series.order import Order
- from sympy.simplify.simplify import logcombine
- from sympy.core.symbol import Dummy
- if self.args[0] == x:
- return log(x) if logx is None else logx
- arg = self.args[0]
- t = Dummy('t', positive=True)
- if cdir == 0:
- cdir = 1
- z = arg.subs(x, cdir*t)
- k, l = Wild("k"), Wild("l")
- r = z.match(k*t**l)
- if r is not None:
- k, l = r[k], r[l]
- if l != 0 and not l.has(t) and not k.has(t):
- r = l*log(x) if logx is None else l*logx
- r += log(k) - l*log(cdir) # XXX true regardless of assumptions?
- return r
- def coeff_exp(term, x):
- coeff, exp = S.One, S.Zero
- for factor in Mul.make_args(term):
- if factor.has(x):
- base, exp = factor.as_base_exp()
- if base != x:
- try:
- return term.leadterm(x)
- except ValueError:
- return term, S.Zero
- else:
- coeff *= factor
- return coeff, exp
- # TODO new and probably slow
- try:
- a, b = z.leadterm(t, logx=logx, cdir=1)
- except (ValueError, NotImplementedError, PoleError):
- s = z._eval_nseries(t, n=n, logx=logx, cdir=1)
- while s.is_Order:
- n += 1
- s = z._eval_nseries(t, n=n, logx=logx, cdir=1)
- try:
- a, b = s.removeO().leadterm(t, cdir=1)
- except ValueError:
- a, b = s.removeO().as_leading_term(t, cdir=1), S.Zero
- p = (z/(a*t**b) - 1)._eval_nseries(t, n=n, logx=logx, cdir=1)
- if p.has(exp):
- p = logcombine(p)
- if isinstance(p, Order):
- n = p.getn()
- _, d = coeff_exp(p, t)
- logx = log(x) if logx is None else logx
- if not d.is_positive:
- res = log(a) - b*log(cdir) + b*logx
- _res = res
- logflags = {"deep": True, "log": True, "mul": False, "power_exp": False,
- "power_base": False, "multinomial": False, "basic": False, "force": True,
- "factor": False}
- expr = self.expand(**logflags)
- if (not a.could_extract_minus_sign() and
- logx.could_extract_minus_sign()):
- _res = _res.subs(-logx, -log(x)).expand(**logflags)
- else:
- _res = _res.subs(logx, log(x)).expand(**logflags)
- if _res == expr:
- return res
- return res + Order(x**n, x)
- def mul(d1, d2):
- res = {}
- for e1, e2 in product(d1, d2):
- ex = e1 + e2
- if ex < n:
- res[ex] = res.get(ex, S.Zero) + d1[e1]*d2[e2]
- return res
- pterms = {}
- for term in Add.make_args(p.removeO()):
- co1, e1 = coeff_exp(term, t)
- pterms[e1] = pterms.get(e1, S.Zero) + co1
- k = S.One
- terms = {}
- pk = pterms
- while k*d < n:
- coeff = -S.NegativeOne**k/k
- for ex in pk:
- _ = terms.get(ex, S.Zero) + coeff*pk[ex]
- terms[ex] = _.nsimplify()
- pk = mul(pk, pterms)
- k += S.One
- res = log(a) - b*log(cdir) + b*logx
- for ex in terms:
- res += terms[ex]*t**(ex)
- if a.is_negative and im(z) != 0:
- from sympy.functions.special.delta_functions import Heaviside
- for i, term in enumerate(z.lseries(t)):
- if not term.is_real or i == 5:
- break
- if i < 5:
- coeff, _ = term.as_coeff_exponent(t)
- res += -2*I*pi*Heaviside(-im(coeff), 0)
- res = res.subs(t, x/cdir)
- return res + Order(x**n, x)
- def _eval_as_leading_term(self, x, logx=None, cdir=0):
- # NOTE
- # Refer https://github.com/sympy/sympy/pull/23592 for more information
- # on each of the following steps involved in this method.
- arg0 = self.args[0].together()
- # STEP 1
- t = Dummy('t', positive=True)
- if cdir == 0:
- cdir = 1
- z = arg0.subs(x, cdir*t)
- # STEP 2
- try:
- c, e = z.leadterm(t, logx=logx, cdir=1)
- except ValueError:
- arg = arg0.as_leading_term(x, logx=logx, cdir=cdir)
- return log(arg)
- if c.has(t):
- c = c.subs(t, x/cdir)
- if e != 0:
- raise PoleError("Cannot expand %s around 0" % (self))
- return log(c)
- # STEP 3
- if c == S.One and e == S.Zero:
- return (arg0 - S.One).as_leading_term(x, logx=logx)
- # STEP 4
- res = log(c) - e*log(cdir)
- logx = log(x) if logx is None else logx
- res += e*logx
- # STEP 5
- if c.is_negative and im(z) != 0:
- from sympy.functions.special.delta_functions import Heaviside
- for i, term in enumerate(z.lseries(t)):
- if not term.is_real or i == 5:
- break
- if i < 5:
- coeff, _ = term.as_coeff_exponent(t)
- res += -2*I*pi*Heaviside(-im(coeff), 0)
- return res
- class LambertW(Function):
- r"""
- The Lambert W function $W(z)$ is defined as the inverse
- function of $w \exp(w)$ [1]_.
- Explanation
- ===========
- 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] https://en.wikipedia.org/wiki/Lambert_W_function
- """
- _singularities = (-Pow(S.Exp1, -1, evaluate=False), S.ComplexInfinity)
- @classmethod
- def eval(cls, x, k=None):
- if k == S.Zero:
- return cls(x)
- elif k is None:
- k = S.Zero
- if k.is_zero:
- if x.is_zero:
- return S.Zero
- if x is S.Exp1:
- return S.One
- if x == -1/S.Exp1:
- return S.NegativeOne
- if x == -log(2)/2:
- return -log(2)
- if x == 2*log(2):
- return log(2)
- if x == -pi/2:
- return I*pi/2
- if x == exp(1 + S.Exp1):
- return S.Exp1
- if x is S.Infinity:
- return S.Infinity
- if x.is_zero:
- return S.Zero
- if fuzzy_not(k.is_zero):
- if x.is_zero:
- return S.NegativeInfinity
- if k is S.NegativeOne:
- if x == -pi/2:
- return -I*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_extended_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_extended_real:
- return False
- def _eval_is_finite(self):
- return self.args[0].is_finite
- 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
- def _eval_as_leading_term(self, x, logx=None, cdir=0):
- if len(self.args) == 1:
- arg = self.args[0]
- arg0 = arg.subs(x, 0).cancel()
- if not arg0.is_zero:
- return self.func(arg0)
- return arg.as_leading_term(x)
- def _eval_nseries(self, x, n, logx, cdir=0):
- if len(self.args) == 1:
- from sympy.functions.elementary.integers import ceiling
- from sympy.series.order import Order
- arg = self.args[0].nseries(x, n=n, logx=logx)
- lt = arg.as_leading_term(x, logx=logx)
- lte = 1
- if lt.is_Pow:
- lte = lt.exp
- if ceiling(n/lte) >= 1:
- s = Add(*[(-S.One)**(k - 1)*Integer(k)**(k - 2)/
- factorial(k - 1)*arg**k for k in range(1, ceiling(n/lte))])
- s = expand_multinomial(s)
- else:
- s = S.Zero
- return s + Order(x**n, x)
- return super()._eval_nseries(x, n, logx)
- def _eval_is_zero(self):
- x = self.args[0]
- if len(self.args) == 1:
- return x.is_zero
- else:
- return fuzzy_and([x.is_zero, self.args[1].is_zero])
- @cacheit
- def _log_atan_table():
- return {
- # first quadrant only
- sqrt(3): pi / 3,
- 1: pi / 4,
- sqrt(5 - 2 * sqrt(5)): pi / 5,
- sqrt(2) * sqrt(5 - sqrt(5)) / (1 + sqrt(5)): pi / 5,
- sqrt(5 + 2 * sqrt(5)): pi * Rational(2, 5),
- sqrt(2) * sqrt(sqrt(5) + 5) / (-1 + sqrt(5)): pi * Rational(2, 5),
- sqrt(3) / 3: pi / 6,
- sqrt(2) - 1: pi / 8,
- sqrt(2 - sqrt(2)) / sqrt(sqrt(2) + 2): pi / 8,
- sqrt(2) + 1: pi * Rational(3, 8),
- sqrt(sqrt(2) + 2) / sqrt(2 - sqrt(2)): pi * Rational(3, 8),
- sqrt(1 - 2 * sqrt(5) / 5): pi / 10,
- (-sqrt(2) + sqrt(10)) / (2 * sqrt(sqrt(5) + 5)): pi / 10,
- sqrt(1 + 2 * sqrt(5) / 5): pi * Rational(3, 10),
- (sqrt(2) + sqrt(10)) / (2 * sqrt(5 - sqrt(5))): pi * Rational(3, 10),
- 2 - sqrt(3): pi / 12,
- (-1 + sqrt(3)) / (1 + sqrt(3)): pi / 12,
- 2 + sqrt(3): pi * Rational(5, 12),
- (1 + sqrt(3)) / (-1 + sqrt(3)): pi * Rational(5, 12)
- }
|