12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454 |
- from typing import Tuple as tTuple
- from sympy.core import S, Add, Mul, sympify, Symbol, Dummy, Basic
- from sympy.core.expr import Expr
- from sympy.core.exprtools import factor_terms
- from sympy.core.function import (Function, Derivative, ArgumentIndexError,
- AppliedUndef, expand_mul)
- from sympy.core.logic import fuzzy_not, fuzzy_or
- from sympy.core.numbers import pi, I, oo
- from sympy.core.power import Pow
- from sympy.core.relational import Eq
- from sympy.functions.elementary.exponential import exp, exp_polar, log
- from sympy.functions.elementary.integers import ceiling
- from sympy.functions.elementary.miscellaneous import sqrt
- from sympy.functions.elementary.piecewise import Piecewise
- from sympy.functions.elementary.trigonometric import atan, atan2
- ###############################################################################
- ######################### REAL and IMAGINARY PARTS ############################
- ###############################################################################
- class re(Function):
- """
- Returns real part of expression. This function performs only
- elementary analysis and so it will fail to decompose properly
- more complicated expressions. If completely simplified result
- is needed then use ``Basic.as_real_imag()`` or perform complex
- expansion on instance of this function.
- Examples
- ========
- >>> from sympy import re, im, I, E, symbols
- >>> x, y = symbols('x y', real=True)
- >>> re(2*E)
- 2*E
- >>> re(2*I + 17)
- 17
- >>> re(2*I)
- 0
- >>> re(im(x) + x*I + 2)
- 2
- >>> re(5 + I + 2)
- 7
- Parameters
- ==========
- arg : Expr
- Real or complex expression.
- Returns
- =======
- expr : Expr
- Real part of expression.
- See Also
- ========
- im
- """
- args: tTuple[Expr]
- is_extended_real = True
- unbranched = True # implicitly works on the projection to C
- _singularities = True # non-holomorphic
- @classmethod
- def eval(cls, arg):
- if arg is S.NaN:
- return S.NaN
- elif arg is S.ComplexInfinity:
- return S.NaN
- elif arg.is_extended_real:
- return arg
- elif arg.is_imaginary or (S.ImaginaryUnit*arg).is_extended_real:
- return S.Zero
- elif arg.is_Matrix:
- return arg.as_real_imag()[0]
- elif arg.is_Function and isinstance(arg, conjugate):
- return re(arg.args[0])
- else:
- included, reverted, excluded = [], [], []
- args = Add.make_args(arg)
- for term in args:
- coeff = term.as_coefficient(S.ImaginaryUnit)
- if coeff is not None:
- if not coeff.is_extended_real:
- reverted.append(coeff)
- elif not term.has(S.ImaginaryUnit) and term.is_extended_real:
- excluded.append(term)
- else:
- # Try to do some advanced expansion. If
- # impossible, don't try to do re(arg) again
- # (because this is what we are trying to do now).
- real_imag = term.as_real_imag(ignore=arg)
- if real_imag:
- excluded.append(real_imag[0])
- else:
- included.append(term)
- if len(args) != len(included):
- a, b, c = (Add(*xs) for xs in [included, reverted, excluded])
- return cls(a) - im(b) + c
- def as_real_imag(self, deep=True, **hints):
- """
- Returns the real number with a zero imaginary part.
- """
- return (self, S.Zero)
- def _eval_derivative(self, x):
- if x.is_extended_real or self.args[0].is_extended_real:
- return re(Derivative(self.args[0], x, evaluate=True))
- if x.is_imaginary or self.args[0].is_imaginary:
- return -S.ImaginaryUnit \
- * im(Derivative(self.args[0], x, evaluate=True))
- def _eval_rewrite_as_im(self, arg, **kwargs):
- return self.args[0] - S.ImaginaryUnit*im(self.args[0])
- def _eval_is_algebraic(self):
- return self.args[0].is_algebraic
- def _eval_is_zero(self):
- # is_imaginary implies nonzero
- return fuzzy_or([self.args[0].is_imaginary, self.args[0].is_zero])
- def _eval_is_finite(self):
- if self.args[0].is_finite:
- return True
- def _eval_is_complex(self):
- if self.args[0].is_finite:
- return True
- class im(Function):
- """
- Returns imaginary part of expression. This function performs only
- elementary analysis and so it will fail to decompose properly more
- complicated expressions. If completely simplified result is needed then
- use ``Basic.as_real_imag()`` or perform complex expansion on instance of
- this function.
- Examples
- ========
- >>> from sympy import re, im, E, I
- >>> from sympy.abc import x, y
- >>> im(2*E)
- 0
- >>> im(2*I + 17)
- 2
- >>> im(x*I)
- re(x)
- >>> im(re(x) + y)
- im(y)
- >>> im(2 + 3*I)
- 3
- Parameters
- ==========
- arg : Expr
- Real or complex expression.
- Returns
- =======
- expr : Expr
- Imaginary part of expression.
- See Also
- ========
- re
- """
- args: tTuple[Expr]
- is_extended_real = True
- unbranched = True # implicitly works on the projection to C
- _singularities = True # non-holomorphic
- @classmethod
- def eval(cls, arg):
- if arg is S.NaN:
- return S.NaN
- elif arg is S.ComplexInfinity:
- return S.NaN
- elif arg.is_extended_real:
- return S.Zero
- elif arg.is_imaginary or (S.ImaginaryUnit*arg).is_extended_real:
- return -S.ImaginaryUnit * arg
- elif arg.is_Matrix:
- return arg.as_real_imag()[1]
- elif arg.is_Function and isinstance(arg, conjugate):
- return -im(arg.args[0])
- else:
- included, reverted, excluded = [], [], []
- args = Add.make_args(arg)
- for term in args:
- coeff = term.as_coefficient(S.ImaginaryUnit)
- if coeff is not None:
- if not coeff.is_extended_real:
- reverted.append(coeff)
- else:
- excluded.append(coeff)
- elif term.has(S.ImaginaryUnit) or not term.is_extended_real:
- # Try to do some advanced expansion. If
- # impossible, don't try to do im(arg) again
- # (because this is what we are trying to do now).
- real_imag = term.as_real_imag(ignore=arg)
- if real_imag:
- excluded.append(real_imag[1])
- else:
- included.append(term)
- if len(args) != len(included):
- a, b, c = (Add(*xs) for xs in [included, reverted, excluded])
- return cls(a) + re(b) + c
- def as_real_imag(self, deep=True, **hints):
- """
- Return the imaginary part with a zero real part.
- """
- return (self, S.Zero)
- def _eval_derivative(self, x):
- if x.is_extended_real or self.args[0].is_extended_real:
- return im(Derivative(self.args[0], x, evaluate=True))
- if x.is_imaginary or self.args[0].is_imaginary:
- return -S.ImaginaryUnit \
- * re(Derivative(self.args[0], x, evaluate=True))
- def _eval_rewrite_as_re(self, arg, **kwargs):
- return -S.ImaginaryUnit*(self.args[0] - re(self.args[0]))
- def _eval_is_algebraic(self):
- return self.args[0].is_algebraic
- def _eval_is_zero(self):
- return self.args[0].is_extended_real
- def _eval_is_finite(self):
- if self.args[0].is_finite:
- return True
- def _eval_is_complex(self):
- if self.args[0].is_finite:
- return True
- ###############################################################################
- ############### SIGN, ABSOLUTE VALUE, ARGUMENT and CONJUGATION ################
- ###############################################################################
- class sign(Function):
- """
- Returns the complex sign of an expression:
- Explanation
- ===========
- If the expression is real the sign will be:
- * $1$ if expression is positive
- * $0$ if expression is equal to zero
- * $-1$ if expression is negative
- If the expression is imaginary the sign will be:
- * $I$ if im(expression) is positive
- * $-I$ if im(expression) is negative
- Otherwise an unevaluated expression will be returned. When evaluated, the
- result (in general) will be ``cos(arg(expr)) + I*sin(arg(expr))``.
- Examples
- ========
- >>> from sympy import sign, I
- >>> sign(-1)
- -1
- >>> sign(0)
- 0
- >>> sign(-3*I)
- -I
- >>> sign(1 + I)
- sign(1 + I)
- >>> _.evalf()
- 0.707106781186548 + 0.707106781186548*I
- Parameters
- ==========
- arg : Expr
- Real or imaginary expression.
- Returns
- =======
- expr : Expr
- Complex sign of expression.
- See Also
- ========
- Abs, conjugate
- """
- is_complex = True
- _singularities = True
- def doit(self, **hints):
- s = super().doit()
- if s == self and self.args[0].is_zero is False:
- return self.args[0] / Abs(self.args[0])
- return s
- @classmethod
- def eval(cls, arg):
- # handle what we can
- if arg.is_Mul:
- c, args = arg.as_coeff_mul()
- unk = []
- s = sign(c)
- for a in args:
- if a.is_extended_negative:
- s = -s
- elif a.is_extended_positive:
- pass
- else:
- if a.is_imaginary:
- ai = im(a)
- if ai.is_comparable: # i.e. a = I*real
- s *= S.ImaginaryUnit
- if ai.is_extended_negative:
- # can't use sign(ai) here since ai might not be
- # a Number
- s = -s
- else:
- unk.append(a)
- else:
- unk.append(a)
- if c is S.One and len(unk) == len(args):
- return None
- return s * cls(arg._new_rawargs(*unk))
- if arg is S.NaN:
- return S.NaN
- if arg.is_zero: # it may be an Expr that is zero
- return S.Zero
- if arg.is_extended_positive:
- return S.One
- if arg.is_extended_negative:
- return S.NegativeOne
- if arg.is_Function:
- if isinstance(arg, sign):
- return arg
- if arg.is_imaginary:
- if arg.is_Pow and arg.exp is S.Half:
- # we catch this because non-trivial sqrt args are not expanded
- # e.g. sqrt(1-sqrt(2)) --x--> to I*sqrt(sqrt(2) - 1)
- return S.ImaginaryUnit
- arg2 = -S.ImaginaryUnit * arg
- if arg2.is_extended_positive:
- return S.ImaginaryUnit
- if arg2.is_extended_negative:
- return -S.ImaginaryUnit
- def _eval_Abs(self):
- if fuzzy_not(self.args[0].is_zero):
- return S.One
- def _eval_conjugate(self):
- return sign(conjugate(self.args[0]))
- def _eval_derivative(self, x):
- if self.args[0].is_extended_real:
- from sympy.functions.special.delta_functions import DiracDelta
- return 2 * Derivative(self.args[0], x, evaluate=True) \
- * DiracDelta(self.args[0])
- elif self.args[0].is_imaginary:
- from sympy.functions.special.delta_functions import DiracDelta
- return 2 * Derivative(self.args[0], x, evaluate=True) \
- * DiracDelta(-S.ImaginaryUnit * self.args[0])
- def _eval_is_nonnegative(self):
- if self.args[0].is_nonnegative:
- return True
- def _eval_is_nonpositive(self):
- if self.args[0].is_nonpositive:
- return True
- def _eval_is_imaginary(self):
- return self.args[0].is_imaginary
- def _eval_is_integer(self):
- return self.args[0].is_extended_real
- def _eval_is_zero(self):
- return self.args[0].is_zero
- def _eval_power(self, other):
- if (
- fuzzy_not(self.args[0].is_zero) and
- other.is_integer and
- other.is_even
- ):
- return S.One
- def _eval_nseries(self, x, n, logx, cdir=0):
- arg0 = self.args[0]
- x0 = arg0.subs(x, 0)
- if x0 != 0:
- return self.func(x0)
- if cdir != 0:
- cdir = arg0.dir(x, cdir)
- return -S.One if re(cdir) < 0 else S.One
- def _eval_rewrite_as_Piecewise(self, arg, **kwargs):
- if arg.is_extended_real:
- return Piecewise((1, arg > 0), (-1, arg < 0), (0, True))
- def _eval_rewrite_as_Heaviside(self, arg, **kwargs):
- from sympy.functions.special.delta_functions import Heaviside
- if arg.is_extended_real:
- return Heaviside(arg) * 2 - 1
- def _eval_rewrite_as_Abs(self, arg, **kwargs):
- return Piecewise((0, Eq(arg, 0)), (arg / Abs(arg), True))
- def _eval_simplify(self, **kwargs):
- return self.func(factor_terms(self.args[0])) # XXX include doit?
- class Abs(Function):
- """
- Return the absolute value of the argument.
- Explanation
- ===========
- This is an extension of the built-in function ``abs()`` to accept symbolic
- values. If you pass a SymPy expression to the built-in ``abs()``, it will
- pass it automatically to ``Abs()``.
- Examples
- ========
- >>> from sympy import Abs, Symbol, S, I
- >>> Abs(-1)
- 1
- >>> x = Symbol('x', real=True)
- >>> Abs(-x)
- Abs(x)
- >>> Abs(x**2)
- x**2
- >>> abs(-x) # The Python built-in
- Abs(x)
- >>> Abs(3*x + 2*I)
- sqrt(9*x**2 + 4)
- >>> Abs(8*I)
- 8
- Note that the Python built-in will return either an Expr or int depending on
- the argument::
- >>> type(abs(-1))
- <... 'int'>
- >>> type(abs(S.NegativeOne))
- <class 'sympy.core.numbers.One'>
- Abs will always return a SymPy object.
- Parameters
- ==========
- arg : Expr
- Real or complex expression.
- Returns
- =======
- expr : Expr
- Absolute value returned can be an expression or integer depending on
- input arg.
- See Also
- ========
- sign, conjugate
- """
- args: tTuple[Expr]
- is_extended_real = True
- is_extended_negative = False
- is_extended_nonnegative = True
- unbranched = True
- _singularities = True # non-holomorphic
- def fdiff(self, argindex=1):
- """
- Get the first derivative of the argument to Abs().
- """
- if argindex == 1:
- return sign(self.args[0])
- else:
- raise ArgumentIndexError(self, argindex)
- @classmethod
- def eval(cls, arg):
- from sympy.simplify.simplify import signsimp
- if hasattr(arg, '_eval_Abs'):
- obj = arg._eval_Abs()
- if obj is not None:
- return obj
- if not isinstance(arg, Expr):
- raise TypeError("Bad argument type for Abs(): %s" % type(arg))
- # handle what we can
- arg = signsimp(arg, evaluate=False)
- n, d = arg.as_numer_denom()
- if d.free_symbols and not n.free_symbols:
- return cls(n)/cls(d)
- if arg.is_Mul:
- known = []
- unk = []
- for t in arg.args:
- if t.is_Pow and t.exp.is_integer and t.exp.is_negative:
- bnew = cls(t.base)
- if isinstance(bnew, cls):
- unk.append(t)
- else:
- known.append(Pow(bnew, t.exp))
- else:
- tnew = cls(t)
- if isinstance(tnew, cls):
- unk.append(t)
- else:
- known.append(tnew)
- known = Mul(*known)
- unk = cls(Mul(*unk), evaluate=False) if unk else S.One
- return known*unk
- if arg is S.NaN:
- return S.NaN
- if arg is S.ComplexInfinity:
- return S.Infinity
- if arg.is_Pow:
- base, exponent = arg.as_base_exp()
- if base.is_extended_real:
- if exponent.is_integer:
- if exponent.is_even:
- return arg
- if base is S.NegativeOne:
- return S.One
- return Abs(base)**exponent
- if base.is_extended_nonnegative:
- return base**re(exponent)
- if base.is_extended_negative:
- return (-base)**re(exponent)*exp(-S.Pi*im(exponent))
- return
- elif not base.has(Symbol): # complex base
- # express base**exponent as exp(exponent*log(base))
- a, b = log(base).as_real_imag()
- z = a + I*b
- return exp(re(exponent*z))
- if isinstance(arg, exp):
- return exp(re(arg.args[0]))
- if isinstance(arg, AppliedUndef):
- if arg.is_positive:
- return arg
- elif arg.is_negative:
- return -arg
- return
- if arg.is_Add and arg.has(S.Infinity, S.NegativeInfinity):
- if any(a.is_infinite for a in arg.as_real_imag()):
- return S.Infinity
- if arg.is_zero:
- return S.Zero
- if arg.is_extended_nonnegative:
- return arg
- if arg.is_extended_nonpositive:
- return -arg
- if arg.is_imaginary:
- arg2 = -S.ImaginaryUnit * arg
- if arg2.is_extended_nonnegative:
- return arg2
- if arg.is_extended_real:
- return
- # reject result if all new conjugates are just wrappers around
- # an expression that was already in the arg
- conj = signsimp(arg.conjugate(), evaluate=False)
- new_conj = conj.atoms(conjugate) - arg.atoms(conjugate)
- if new_conj and all(arg.has(i.args[0]) for i in new_conj):
- return
- if arg != conj and arg != -conj:
- ignore = arg.atoms(Abs)
- abs_free_arg = arg.xreplace({i: Dummy(real=True) for i in ignore})
- unk = [a for a in abs_free_arg.free_symbols if a.is_extended_real is None]
- if not unk or not all(conj.has(conjugate(u)) for u in unk):
- return sqrt(expand_mul(arg*conj))
- def _eval_is_real(self):
- if self.args[0].is_finite:
- return True
- def _eval_is_integer(self):
- if self.args[0].is_extended_real:
- return self.args[0].is_integer
- def _eval_is_extended_nonzero(self):
- return fuzzy_not(self._args[0].is_zero)
- def _eval_is_zero(self):
- return self._args[0].is_zero
- def _eval_is_extended_positive(self):
- is_z = self.is_zero
- if is_z is not None:
- return not is_z
- def _eval_is_rational(self):
- if self.args[0].is_extended_real:
- return self.args[0].is_rational
- def _eval_is_even(self):
- if self.args[0].is_extended_real:
- return self.args[0].is_even
- def _eval_is_odd(self):
- if self.args[0].is_extended_real:
- return self.args[0].is_odd
- def _eval_is_algebraic(self):
- return self.args[0].is_algebraic
- def _eval_power(self, exponent):
- if self.args[0].is_extended_real and exponent.is_integer:
- if exponent.is_even:
- return self.args[0]**exponent
- elif exponent is not S.NegativeOne and exponent.is_Integer:
- return self.args[0]**(exponent - 1)*self
- return
- def _eval_nseries(self, x, n, logx, cdir=0):
- direction = self.args[0].leadterm(x)[0]
- if direction.has(log(x)):
- direction = direction.subs(log(x), logx)
- s = self.args[0]._eval_nseries(x, n=n, logx=logx)
- return (sign(direction)*s).expand()
- def _eval_derivative(self, x):
- if self.args[0].is_extended_real or self.args[0].is_imaginary:
- return Derivative(self.args[0], x, evaluate=True) \
- * sign(conjugate(self.args[0]))
- rv = (re(self.args[0]) * Derivative(re(self.args[0]), x,
- evaluate=True) + im(self.args[0]) * Derivative(im(self.args[0]),
- x, evaluate=True)) / Abs(self.args[0])
- return rv.rewrite(sign)
- def _eval_rewrite_as_Heaviside(self, arg, **kwargs):
- # Note this only holds for real arg (since Heaviside is not defined
- # for complex arguments).
- from sympy.functions.special.delta_functions import Heaviside
- if arg.is_extended_real:
- return arg*(Heaviside(arg) - Heaviside(-arg))
- def _eval_rewrite_as_Piecewise(self, arg, **kwargs):
- if arg.is_extended_real:
- return Piecewise((arg, arg >= 0), (-arg, True))
- elif arg.is_imaginary:
- return Piecewise((I*arg, I*arg >= 0), (-I*arg, True))
- def _eval_rewrite_as_sign(self, arg, **kwargs):
- return arg/sign(arg)
- def _eval_rewrite_as_conjugate(self, arg, **kwargs):
- return (arg*conjugate(arg))**S.Half
- class arg(Function):
- r"""
- Returns the argument (in radians) of a complex number. The argument is
- evaluated in consistent convention with ``atan2`` where the branch-cut is
- taken along the negative real axis and ``arg(z)`` is in the interval
- $(-\pi,\pi]$. For a positive number, the argument is always 0; the
- argument of a negative number is $\pi$; and the argument of 0
- is undefined and returns ``nan``. So the ``arg`` function will never nest
- greater than 3 levels since at the 4th application, the result must be
- nan; for a real number, nan is returned on the 3rd application.
- Examples
- ========
- >>> from sympy import arg, I, sqrt, Dummy
- >>> from sympy.abc import x
- >>> arg(2.0)
- 0
- >>> arg(I)
- pi/2
- >>> arg(sqrt(2) + I*sqrt(2))
- pi/4
- >>> arg(sqrt(3)/2 + I/2)
- pi/6
- >>> arg(4 + 3*I)
- atan(3/4)
- >>> arg(0.8 + 0.6*I)
- 0.643501108793284
- >>> arg(arg(arg(arg(x))))
- nan
- >>> real = Dummy(real=True)
- >>> arg(arg(arg(real)))
- nan
- Parameters
- ==========
- arg : Expr
- Real or complex expression.
- Returns
- =======
- value : Expr
- Returns arc tangent of arg measured in radians.
- """
- is_extended_real = True
- is_real = True
- is_finite = True
- _singularities = True # non-holomorphic
- @classmethod
- def eval(cls, arg):
- a = arg
- for i in range(3):
- if isinstance(a, cls):
- a = a.args[0]
- else:
- if i == 2 and a.is_extended_real:
- return S.NaN
- break
- else:
- return S.NaN
- if isinstance(arg, exp_polar):
- return periodic_argument(arg, oo)
- if not arg.is_Atom:
- c, arg_ = factor_terms(arg).as_coeff_Mul()
- if arg_.is_Mul:
- arg_ = Mul(*[a if (sign(a) not in (-1, 1)) else
- sign(a) for a in arg_.args])
- arg_ = sign(c)*arg_
- else:
- arg_ = arg
- if any(i.is_extended_positive is None for i in arg_.atoms(AppliedUndef)):
- return
- x, y = arg_.as_real_imag()
- rv = atan2(y, x)
- if rv.is_number:
- return rv
- if arg_ != arg:
- return cls(arg_, evaluate=False)
- def _eval_derivative(self, t):
- x, y = self.args[0].as_real_imag()
- return (x * Derivative(y, t, evaluate=True) - y *
- Derivative(x, t, evaluate=True)) / (x**2 + y**2)
- def _eval_rewrite_as_atan2(self, arg, **kwargs):
- x, y = self.args[0].as_real_imag()
- return atan2(y, x)
- class conjugate(Function):
- """
- Returns the *complex conjugate* [1]_ of an argument.
- In mathematics, the complex conjugate of a complex number
- is given by changing the sign of the imaginary part.
- Thus, the conjugate of the complex number
- :math:`a + ib` (where $a$ and $b$ are real numbers) is :math:`a - ib`
- Examples
- ========
- >>> from sympy import conjugate, I
- >>> conjugate(2)
- 2
- >>> conjugate(I)
- -I
- >>> conjugate(3 + 2*I)
- 3 - 2*I
- >>> conjugate(5 - I)
- 5 + I
- Parameters
- ==========
- arg : Expr
- Real or complex expression.
- Returns
- =======
- arg : Expr
- Complex conjugate of arg as real, imaginary or mixed expression.
- See Also
- ========
- sign, Abs
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Complex_conjugation
- """
- _singularities = True # non-holomorphic
- @classmethod
- def eval(cls, arg):
- obj = arg._eval_conjugate()
- if obj is not None:
- return obj
- def inverse(self):
- return conjugate
- def _eval_Abs(self):
- return Abs(self.args[0], evaluate=True)
- def _eval_adjoint(self):
- return transpose(self.args[0])
- def _eval_conjugate(self):
- return self.args[0]
- def _eval_derivative(self, x):
- if x.is_real:
- return conjugate(Derivative(self.args[0], x, evaluate=True))
- elif x.is_imaginary:
- return -conjugate(Derivative(self.args[0], x, evaluate=True))
- def _eval_transpose(self):
- return adjoint(self.args[0])
- def _eval_is_algebraic(self):
- return self.args[0].is_algebraic
- class transpose(Function):
- """
- Linear map transposition.
- Examples
- ========
- >>> from sympy import transpose, Matrix, MatrixSymbol
- >>> A = MatrixSymbol('A', 25, 9)
- >>> transpose(A)
- A.T
- >>> B = MatrixSymbol('B', 9, 22)
- >>> transpose(B)
- B.T
- >>> transpose(A*B)
- B.T*A.T
- >>> M = Matrix([[4, 5], [2, 1], [90, 12]])
- >>> M
- Matrix([
- [ 4, 5],
- [ 2, 1],
- [90, 12]])
- >>> transpose(M)
- Matrix([
- [4, 2, 90],
- [5, 1, 12]])
- Parameters
- ==========
- arg : Matrix
- Matrix or matrix expression to take the transpose of.
- Returns
- =======
- value : Matrix
- Transpose of arg.
- """
- @classmethod
- def eval(cls, arg):
- obj = arg._eval_transpose()
- if obj is not None:
- return obj
- def _eval_adjoint(self):
- return conjugate(self.args[0])
- def _eval_conjugate(self):
- return adjoint(self.args[0])
- def _eval_transpose(self):
- return self.args[0]
- class adjoint(Function):
- """
- Conjugate transpose or Hermite conjugation.
- Examples
- ========
- >>> from sympy import adjoint, MatrixSymbol
- >>> A = MatrixSymbol('A', 10, 5)
- >>> adjoint(A)
- Adjoint(A)
- Parameters
- ==========
- arg : Matrix
- Matrix or matrix expression to take the adjoint of.
- Returns
- =======
- value : Matrix
- Represents the conjugate transpose or Hermite
- conjugation of arg.
- """
- @classmethod
- def eval(cls, arg):
- obj = arg._eval_adjoint()
- if obj is not None:
- return obj
- obj = arg._eval_transpose()
- if obj is not None:
- return conjugate(obj)
- def _eval_adjoint(self):
- return self.args[0]
- def _eval_conjugate(self):
- return transpose(self.args[0])
- def _eval_transpose(self):
- return conjugate(self.args[0])
- def _latex(self, printer, exp=None, *args):
- arg = printer._print(self.args[0])
- tex = r'%s^{\dagger}' % arg
- if exp:
- tex = r'\left(%s\right)^{%s}' % (tex, exp)
- return tex
- def _pretty(self, printer, *args):
- from sympy.printing.pretty.stringpict import prettyForm
- pform = printer._print(self.args[0], *args)
- if printer._use_unicode:
- pform = pform**prettyForm('\N{DAGGER}')
- else:
- pform = pform**prettyForm('+')
- return pform
- ###############################################################################
- ############### HANDLING OF POLAR NUMBERS #####################################
- ###############################################################################
- class polar_lift(Function):
- """
- Lift argument to the Riemann surface of the logarithm, using the
- standard branch.
- Examples
- ========
- >>> from sympy import Symbol, polar_lift, I
- >>> p = Symbol('p', polar=True)
- >>> x = Symbol('x')
- >>> polar_lift(4)
- 4*exp_polar(0)
- >>> polar_lift(-4)
- 4*exp_polar(I*pi)
- >>> polar_lift(-I)
- exp_polar(-I*pi/2)
- >>> polar_lift(I + 2)
- polar_lift(2 + I)
- >>> polar_lift(4*x)
- 4*polar_lift(x)
- >>> polar_lift(4*p)
- 4*p
- Parameters
- ==========
- arg : Expr
- Real or complex expression.
- See Also
- ========
- sympy.functions.elementary.exponential.exp_polar
- periodic_argument
- """
- is_polar = True
- is_comparable = False # Cannot be evalf'd.
- @classmethod
- def eval(cls, arg):
- from sympy.functions.elementary.complexes import arg as argument
- if arg.is_number:
- ar = argument(arg)
- # In general we want to affirm that something is known,
- # e.g. `not ar.has(argument) and not ar.has(atan)`
- # but for now we will just be more restrictive and
- # see that it has evaluated to one of the known values.
- if ar in (0, pi/2, -pi/2, pi):
- return exp_polar(I*ar)*abs(arg)
- if arg.is_Mul:
- args = arg.args
- else:
- args = [arg]
- included = []
- excluded = []
- positive = []
- for arg in args:
- if arg.is_polar:
- included += [arg]
- elif arg.is_positive:
- positive += [arg]
- else:
- excluded += [arg]
- if len(excluded) < len(args):
- if excluded:
- return Mul(*(included + positive))*polar_lift(Mul(*excluded))
- elif included:
- return Mul(*(included + positive))
- else:
- return Mul(*positive)*exp_polar(0)
- def _eval_evalf(self, prec):
- """ Careful! any evalf of polar numbers is flaky """
- return self.args[0]._eval_evalf(prec)
- def _eval_Abs(self):
- return Abs(self.args[0], evaluate=True)
- class periodic_argument(Function):
- r"""
- Represent the argument on a quotient of the Riemann surface of the
- logarithm. That is, given a period $P$, always return a value in
- $(-P/2, P/2]$, by using $\exp(PI) = 1$.
- Examples
- ========
- >>> from sympy import exp_polar, periodic_argument
- >>> from sympy import I, pi
- >>> periodic_argument(exp_polar(10*I*pi), 2*pi)
- 0
- >>> periodic_argument(exp_polar(5*I*pi), 4*pi)
- pi
- >>> from sympy import exp_polar, periodic_argument
- >>> from sympy import I, pi
- >>> periodic_argument(exp_polar(5*I*pi), 2*pi)
- pi
- >>> periodic_argument(exp_polar(5*I*pi), 3*pi)
- -pi
- >>> periodic_argument(exp_polar(5*I*pi), pi)
- 0
- Parameters
- ==========
- ar : Expr
- A polar number.
- period : Expr
- The period $P$.
- See Also
- ========
- sympy.functions.elementary.exponential.exp_polar
- polar_lift : Lift argument to the Riemann surface of the logarithm
- principal_branch
- """
- @classmethod
- def _getunbranched(cls, ar):
- if ar.is_Mul:
- args = ar.args
- else:
- args = [ar]
- unbranched = 0
- for a in args:
- if not a.is_polar:
- unbranched += arg(a)
- elif isinstance(a, exp_polar):
- unbranched += a.exp.as_real_imag()[1]
- elif a.is_Pow:
- re, im = a.exp.as_real_imag()
- unbranched += re*unbranched_argument(
- a.base) + im*log(abs(a.base))
- elif isinstance(a, polar_lift):
- unbranched += arg(a.args[0])
- else:
- return None
- return unbranched
- @classmethod
- def eval(cls, ar, period):
- # Our strategy is to evaluate the argument on the Riemann surface of the
- # logarithm, and then reduce.
- # NOTE evidently this means it is a rather bad idea to use this with
- # period != 2*pi and non-polar numbers.
- if not period.is_extended_positive:
- return None
- if period == oo and isinstance(ar, principal_branch):
- return periodic_argument(*ar.args)
- if isinstance(ar, polar_lift) and period >= 2*pi:
- return periodic_argument(ar.args[0], period)
- if ar.is_Mul:
- newargs = [x for x in ar.args if not x.is_positive]
- if len(newargs) != len(ar.args):
- return periodic_argument(Mul(*newargs), period)
- unbranched = cls._getunbranched(ar)
- if unbranched is None:
- return None
- if unbranched.has(periodic_argument, atan2, atan):
- return None
- if period == oo:
- return unbranched
- if period != oo:
- n = ceiling(unbranched/period - S.Half)*period
- if not n.has(ceiling):
- return unbranched - n
- def _eval_evalf(self, prec):
- z, period = self.args
- if period == oo:
- unbranched = periodic_argument._getunbranched(z)
- if unbranched is None:
- return self
- return unbranched._eval_evalf(prec)
- ub = periodic_argument(z, oo)._eval_evalf(prec)
- return (ub - ceiling(ub/period - S.Half)*period)._eval_evalf(prec)
- def unbranched_argument(arg):
- '''
- Returns periodic argument of arg with period as infinity.
- Examples
- ========
- >>> from sympy import exp_polar, unbranched_argument
- >>> from sympy import I, pi
- >>> unbranched_argument(exp_polar(15*I*pi))
- 15*pi
- >>> unbranched_argument(exp_polar(7*I*pi))
- 7*pi
- See also
- ========
- periodic_argument
- '''
- return periodic_argument(arg, oo)
- class principal_branch(Function):
- """
- Represent a polar number reduced to its principal branch on a quotient
- of the Riemann surface of the logarithm.
- Explanation
- ===========
- This is a function of two arguments. The first argument is a polar
- number `z`, and the second one a positive real number or infinity, `p`.
- The result is ``z mod exp_polar(I*p)``.
- Examples
- ========
- >>> from sympy import exp_polar, principal_branch, oo, I, pi
- >>> from sympy.abc import z
- >>> principal_branch(z, oo)
- z
- >>> principal_branch(exp_polar(2*pi*I)*3, 2*pi)
- 3*exp_polar(0)
- >>> principal_branch(exp_polar(2*pi*I)*3*z, 2*pi)
- 3*principal_branch(z, 2*pi)
- Parameters
- ==========
- x : Expr
- A polar number.
- period : Expr
- Positive real number or infinity.
- See Also
- ========
- sympy.functions.elementary.exponential.exp_polar
- polar_lift : Lift argument to the Riemann surface of the logarithm
- periodic_argument
- """
- is_polar = True
- is_comparable = False # cannot always be evalf'd
- @classmethod
- def eval(self, x, period):
- if isinstance(x, polar_lift):
- return principal_branch(x.args[0], period)
- if period == oo:
- return x
- ub = periodic_argument(x, oo)
- barg = periodic_argument(x, period)
- if ub != barg and not ub.has(periodic_argument) \
- and not barg.has(periodic_argument):
- pl = polar_lift(x)
- def mr(expr):
- if not isinstance(expr, Symbol):
- return polar_lift(expr)
- return expr
- pl = pl.replace(polar_lift, mr)
- # Recompute unbranched argument
- ub = periodic_argument(pl, oo)
- if not pl.has(polar_lift):
- if ub != barg:
- res = exp_polar(I*(barg - ub))*pl
- else:
- res = pl
- if not res.is_polar and not res.has(exp_polar):
- res *= exp_polar(0)
- return res
- if not x.free_symbols:
- c, m = x, ()
- else:
- c, m = x.as_coeff_mul(*x.free_symbols)
- others = []
- for y in m:
- if y.is_positive:
- c *= y
- else:
- others += [y]
- m = tuple(others)
- arg = periodic_argument(c, period)
- if arg.has(periodic_argument):
- return None
- if arg.is_number and (unbranched_argument(c) != arg or
- (arg == 0 and m != () and c != 1)):
- if arg == 0:
- return abs(c)*principal_branch(Mul(*m), period)
- return principal_branch(exp_polar(I*arg)*Mul(*m), period)*abs(c)
- if arg.is_number and ((abs(arg) < period/2) == True or arg == period/2) \
- and m == ():
- return exp_polar(arg*I)*abs(c)
- def _eval_evalf(self, prec):
- z, period = self.args
- p = periodic_argument(z, period)._eval_evalf(prec)
- if abs(p) > pi or p == -pi:
- return self # Cannot evalf for this argument.
- return (abs(z)*exp(I*p))._eval_evalf(prec)
- def _polarify(eq, lift, pause=False):
- from sympy.integrals.integrals import Integral
- if eq.is_polar:
- return eq
- if eq.is_number and not pause:
- return polar_lift(eq)
- if isinstance(eq, Symbol) and not pause and lift:
- return polar_lift(eq)
- elif eq.is_Atom:
- return eq
- elif eq.is_Add:
- r = eq.func(*[_polarify(arg, lift, pause=True) for arg in eq.args])
- if lift:
- return polar_lift(r)
- return r
- elif eq.is_Pow and eq.base == S.Exp1:
- return eq.func(S.Exp1, _polarify(eq.exp, lift, pause=False))
- elif eq.is_Function:
- return eq.func(*[_polarify(arg, lift, pause=False) for arg in eq.args])
- elif isinstance(eq, Integral):
- # Don't lift the integration variable
- func = _polarify(eq.function, lift, pause=pause)
- limits = []
- for limit in eq.args[1:]:
- var = _polarify(limit[0], lift=False, pause=pause)
- rest = _polarify(limit[1:], lift=lift, pause=pause)
- limits.append((var,) + rest)
- return Integral(*((func,) + tuple(limits)))
- else:
- return eq.func(*[_polarify(arg, lift, pause=pause)
- if isinstance(arg, Expr) else arg for arg in eq.args])
- def polarify(eq, subs=True, lift=False):
- """
- Turn all numbers in eq into their polar equivalents (under the standard
- choice of argument).
- Note that no attempt is made to guess a formal convention of adding
- polar numbers, expressions like $1 + x$ will generally not be altered.
- Note also that this function does not promote ``exp(x)`` to ``exp_polar(x)``.
- If ``subs`` is ``True``, all symbols which are not already polar will be
- substituted for polar dummies; in this case the function behaves much
- like :func:`~.posify`.
- If ``lift`` is ``True``, both addition statements and non-polar symbols are
- changed to their ``polar_lift()``ed versions.
- Note that ``lift=True`` implies ``subs=False``.
- Examples
- ========
- >>> from sympy import polarify, sin, I
- >>> from sympy.abc import x, y
- >>> expr = (-x)**y
- >>> expr.expand()
- (-x)**y
- >>> polarify(expr)
- ((_x*exp_polar(I*pi))**_y, {_x: x, _y: y})
- >>> polarify(expr)[0].expand()
- _x**_y*exp_polar(_y*I*pi)
- >>> polarify(x, lift=True)
- polar_lift(x)
- >>> polarify(x*(1+y), lift=True)
- polar_lift(x)*polar_lift(y + 1)
- Adds are treated carefully:
- >>> polarify(1 + sin((1 + I)*x))
- (sin(_x*polar_lift(1 + I)) + 1, {_x: x})
- """
- if lift:
- subs = False
- eq = _polarify(sympify(eq), lift)
- if not subs:
- return eq
- reps = {s: Dummy(s.name, polar=True) for s in eq.free_symbols}
- eq = eq.subs(reps)
- return eq, {r: s for s, r in reps.items()}
- def _unpolarify(eq, exponents_only, pause=False):
- if not isinstance(eq, Basic) or eq.is_Atom:
- return eq
- if not pause:
- if isinstance(eq, exp_polar):
- return exp(_unpolarify(eq.exp, exponents_only))
- if isinstance(eq, principal_branch) and eq.args[1] == 2*pi:
- return _unpolarify(eq.args[0], exponents_only)
- if (
- eq.is_Add or eq.is_Mul or eq.is_Boolean or
- eq.is_Relational and (
- eq.rel_op in ('==', '!=') and 0 in eq.args or
- eq.rel_op not in ('==', '!='))
- ):
- return eq.func(*[_unpolarify(x, exponents_only) for x in eq.args])
- if isinstance(eq, polar_lift):
- return _unpolarify(eq.args[0], exponents_only)
- if eq.is_Pow:
- expo = _unpolarify(eq.exp, exponents_only)
- base = _unpolarify(eq.base, exponents_only,
- not (expo.is_integer and not pause))
- return base**expo
- if eq.is_Function and getattr(eq.func, 'unbranched', False):
- return eq.func(*[_unpolarify(x, exponents_only, exponents_only)
- for x in eq.args])
- return eq.func(*[_unpolarify(x, exponents_only, True) for x in eq.args])
- def unpolarify(eq, subs=None, exponents_only=False):
- """
- If `p` denotes the projection from the Riemann surface of the logarithm to
- the complex line, return a simplified version `eq'` of `eq` such that
- `p(eq') = p(eq)`.
- Also apply the substitution subs in the end. (This is a convenience, since
- ``unpolarify``, in a certain sense, undoes :func:`polarify`.)
- Examples
- ========
- >>> from sympy import unpolarify, polar_lift, sin, I
- >>> unpolarify(polar_lift(I + 2))
- 2 + I
- >>> unpolarify(sin(polar_lift(I + 7)))
- sin(7 + I)
- """
- if isinstance(eq, bool):
- return eq
- eq = sympify(eq)
- if subs is not None:
- return unpolarify(eq.subs(subs))
- changed = True
- pause = False
- if exponents_only:
- pause = True
- while changed:
- changed = False
- res = _unpolarify(eq, exponents_only, pause)
- if res != eq:
- changed = True
- eq = res
- if isinstance(res, bool):
- return res
- # Finally, replacing Exp(0) by 1 is always correct.
- # So is polar_lift(0) -> 0.
- return res.subs({exp_polar(0): 1, polar_lift(0): 0})
|