|
from __future__ import annotations |
|
|
|
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 (DefinedFunction, Derivative, ArgumentIndexError, |
|
AppliedUndef, expand_mul, PoleError) |
|
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.miscellaneous import sqrt |
|
from sympy.functions.elementary.piecewise import Piecewise |
|
|
|
|
|
|
|
|
|
|
|
|
|
class re(DefinedFunction): |
|
""" |
|
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: tuple[Expr] |
|
|
|
is_extended_real = True |
|
unbranched = True |
|
_singularities = True |
|
|
|
@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 (I*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(I) |
|
|
|
if coeff is not None: |
|
if not coeff.is_extended_real: |
|
reverted.append(coeff) |
|
elif not term.has(I) and term.is_extended_real: |
|
excluded.append(term) |
|
else: |
|
|
|
|
|
|
|
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 -I \ |
|
* im(Derivative(self.args[0], x, evaluate=True)) |
|
|
|
def _eval_rewrite_as_im(self, arg, **kwargs): |
|
return self.args[0] - I*im(self.args[0]) |
|
|
|
def _eval_is_algebraic(self): |
|
return self.args[0].is_algebraic |
|
|
|
def _eval_is_zero(self): |
|
|
|
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(DefinedFunction): |
|
""" |
|
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: tuple[Expr] |
|
|
|
is_extended_real = True |
|
unbranched = True |
|
_singularities = True |
|
|
|
@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 (I*arg).is_extended_real: |
|
return -I * 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(I) |
|
|
|
if coeff is not None: |
|
if not coeff.is_extended_real: |
|
reverted.append(coeff) |
|
else: |
|
excluded.append(coeff) |
|
elif term.has(I) or not term.is_extended_real: |
|
|
|
|
|
|
|
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 -I \ |
|
* re(Derivative(self.args[0], x, evaluate=True)) |
|
|
|
def _eval_rewrite_as_re(self, arg, **kwargs): |
|
return -I*(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 |
|
|
|
|
|
|
|
|
|
|
|
class sign(DefinedFunction): |
|
""" |
|
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): |
|
|
|
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: |
|
s *= I |
|
if ai.is_extended_negative: |
|
|
|
|
|
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: |
|
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: |
|
|
|
|
|
return I |
|
arg2 = -I * arg |
|
if arg2.is_extended_positive: |
|
return I |
|
if arg2.is_extended_negative: |
|
return -I |
|
|
|
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(-I * 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])) |
|
|
|
|
|
class Abs(DefinedFunction): |
|
""" |
|
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: tuple[Expr] |
|
|
|
is_extended_real = True |
|
is_extended_negative = False |
|
is_extended_nonnegative = True |
|
unbranched = True |
|
_singularities = True |
|
|
|
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)) |
|
|
|
|
|
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 oo |
|
from sympy.functions.elementary.exponential import exp, log |
|
|
|
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(-pi*im(exponent)) |
|
return |
|
elif not base.has(Symbol): |
|
|
|
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(oo, S.NegativeInfinity): |
|
if any(a.is_infinite for a in arg.as_real_imag()): |
|
return oo |
|
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 = -I * arg |
|
if arg2.is_extended_nonnegative: |
|
return arg2 |
|
if arg.is_extended_real: |
|
return |
|
|
|
|
|
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): |
|
return fuzzy_not(self._args[0].is_zero) |
|
|
|
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): |
|
from sympy.functions.elementary.exponential import log |
|
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): |
|
|
|
|
|
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 sqrt(arg*conjugate(arg)) |
|
|
|
|
|
class arg(DefinedFunction): |
|
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 |
|
|
|
@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 |
|
from sympy.functions.elementary.exponential import exp, exp_polar |
|
if isinstance(arg, exp_polar): |
|
return periodic_argument(arg, oo) |
|
elif isinstance(arg, exp): |
|
i_ = im(arg.args[0]) |
|
if i_.is_comparable: |
|
i_ %= 2*S.Pi |
|
if i_ > S.Pi: |
|
i_ -= 2*S.Pi |
|
return i_ |
|
|
|
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 |
|
from sympy.functions.elementary.trigonometric import atan2 |
|
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): |
|
from sympy.functions.elementary.trigonometric import atan2 |
|
x, y = self.args[0].as_real_imag() |
|
return atan2(y, x) |
|
|
|
def _eval_as_leading_term(self, x, logx, cdir): |
|
arg0 = self.args[0] |
|
t = Dummy('t', positive=True) |
|
if cdir == 0: |
|
cdir = 1 |
|
z = arg0.subs(x, cdir*t) |
|
if z.is_positive: |
|
return S.Zero |
|
elif z.is_negative: |
|
return S.Pi |
|
else: |
|
raise PoleError("Cannot expand %s around 0" % (self)) |
|
|
|
def _eval_nseries(self, x, n, logx, cdir=0): |
|
from sympy.series.order import Order |
|
if n <= 0: |
|
return Order(1) |
|
return self._eval_as_leading_term(x, logx=logx, cdir=cdir) |
|
|
|
|
|
class conjugate(DefinedFunction): |
|
""" |
|
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 |
|
|
|
@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(DefinedFunction): |
|
""" |
|
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(DefinedFunction): |
|
""" |
|
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 |
|
|
|
|
|
|
|
|
|
|
|
|
|
class polar_lift(DefinedFunction): |
|
""" |
|
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 |
|
|
|
@classmethod |
|
def eval(cls, arg): |
|
from sympy.functions.elementary.complexes import arg as argument |
|
if arg.is_number: |
|
ar = argument(arg) |
|
|
|
|
|
|
|
|
|
if ar in (0, pi/2, -pi/2, pi): |
|
from sympy.functions.elementary.exponential import exp_polar |
|
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: |
|
from sympy.functions.elementary.exponential import exp_polar |
|
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(DefinedFunction): |
|
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): |
|
from sympy.functions.elementary.exponential import exp_polar, log |
|
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): |
|
|
|
|
|
|
|
|
|
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 |
|
from sympy.functions.elementary.trigonometric import atan, atan2 |
|
if unbranched.has(periodic_argument, atan2, atan): |
|
return None |
|
if period == oo: |
|
return unbranched |
|
if period != oo: |
|
from sympy.functions.elementary.integers import ceiling |
|
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) |
|
from sympy.functions.elementary.integers import ceiling |
|
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(DefinedFunction): |
|
""" |
|
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 |
|
|
|
@classmethod |
|
def eval(self, x, period): |
|
from sympy.functions.elementary.exponential import exp_polar |
|
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) |
|
|
|
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 |
|
from sympy.functions.elementary.exponential import exp |
|
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): |
|
|
|
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: |
|
from sympy.functions.elementary.exponential import exp, exp_polar |
|
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 |
|
|
|
|
|
from sympy.functions.elementary.exponential import exp_polar |
|
return res.subs({exp_polar(0): 1, polar_lift(0): 0}) |
|
|