def test_derivatives_issue_4757(): x = Symbol('x', real=True) y = Symbol('y', imaginary=True) f = Function('f') assert re(f(x)).diff(x) == re(f(x).diff(x)) assert im(f(x)).diff(x) == im(f(x).diff(x)) assert re(f(y)).diff(y) == -I * im(f(y).diff(y)) assert im(f(y)).diff(y) == -I * re(f(y).diff(y)) assert Abs(f(x)).diff(x).subs(f(x), 1 + I * x).doit() == x / sqrt(1 + x**2) assert arg(f(x)).diff(x).subs(f(x), 1 + I * x**2).doit() == 2 * x / (1 + x**4) assert Abs(f(y)).diff(y).subs(f(y), 1 + y).doit() == -y / sqrt(1 - y**2) assert arg(f(y)).diff(y).subs(f(y), I + y**2).doit() == 2 * y / (1 + y**4)
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)) """ from sympy.functions.elementary.complexes import (Abs, arg) sarg = self.args[0] if deep: sarg = self.args[0].expand(deep, **hints) abs = Abs(sarg) if abs == sarg: return self, S.Zero arg = arg(sarg) if hints.get('log', False): # Expand the log hints['complex'] = False return (log(abs).expand(deep, **hints), arg) else: return log(abs), arg
def test_tensorflow_complexes(): if not tensorflow: skip("tensorflow not installed") func1 = lambdify(x, re(x), modules="tensorflow") func2 = lambdify(x, im(x), modules="tensorflow") func3 = lambdify(x, Abs(x), modules="tensorflow") func4 = lambdify(x, arg(x), modules="tensorflow") with tensorflow.compat.v1.Session() as s: # For versions before # https://github.com/tensorflow/tensorflow/issues/30029 # resolved, using python numeric types may not work a = tensorflow.constant(1 + 2j) assert func1(a).eval(session=s) == 1 assert func2(a).eval(session=s) == 2 tensorflow_result = func3(a).eval(session=s) sympy_result = Abs(1 + 2j).evalf() assert abs(tensorflow_result - sympy_result) < 10**-6 tensorflow_result = func4(a).eval(session=s) sympy_result = arg(1 + 2j).evalf() assert abs(tensorflow_result - sympy_result) < 10**-6
def is_eq(lhs, rhs, assumptions=None): """ Fuzzy bool representing mathematical equality between *lhs* and *rhs*. Parameters ========== lhs : Expr The left-hand side of the expression, must be sympified. rhs : Expr The right-hand side of the expression, must be sympified. assumptions: Boolean, optional Assumptions taken to evaluate the equality. Returns ======= ``True`` if *lhs* is equal to *rhs*, ``False`` is *lhs* is not equal to *rhs*, and ``None`` if the comparison between *lhs* and *rhs* is indeterminate. Explanation =========== This function is intended to give a relatively fast determination and deliberately does not attempt slow calculations that might help in obtaining a determination of True or False in more difficult cases. :func:`~.is_neq` calls this function to return its value, so supporting new type with this function will ensure correct behavior for ``is_neq`` as well. Examples ======== >>> from sympy import Q, S >>> from sympy.core.relational import is_eq, is_neq >>> from sympy.abc import x >>> is_eq(S(0), S(0)) True >>> is_neq(S(0), S(0)) False >>> is_eq(S(0), S(2)) False >>> is_neq(S(0), S(2)) True Assumptions can be passed to evaluate the equality which is otherwise indeterminate. >>> print(is_eq(x, S(0))) None >>> is_eq(x, S(0), assumptions=Q.zero(x)) True New types can be supported by dispatching to ``_eval_is_eq``. >>> from sympy import Basic, sympify >>> from sympy.multipledispatch import dispatch >>> class MyBasic(Basic): ... def __new__(cls, arg): ... return Basic.__new__(cls, sympify(arg)) ... @property ... def value(self): ... return self.args[0] ... >>> @dispatch(MyBasic, MyBasic) ... def _eval_is_eq(a, b): ... return is_eq(a.value, b.value) ... >>> a = MyBasic(1) >>> b = MyBasic(1) >>> is_eq(a, b) True >>> is_neq(a, b) False """ from sympy.assumptions.wrapper import (AssumptionsWrapper, is_infinite, is_extended_real) from sympy.core.add import Add from sympy.functions.elementary.complexes import arg from sympy.simplify.simplify import clear_coefficients from sympy.utilities.iterables import sift # here, _eval_Eq is only called for backwards compatibility # new code should use is_eq with multiple dispatch as # outlined in the docstring for side1, side2 in (lhs, rhs), (rhs, lhs): eval_func = getattr(side1, '_eval_Eq', None) if eval_func is not None: retval = eval_func(side2) if retval is not None: return retval retval = _eval_is_eq(lhs, rhs) if retval is not None: return retval if dispatch(type(lhs), type(rhs)) != dispatch(type(rhs), type(lhs)): retval = _eval_is_eq(rhs, lhs) if retval is not None: return retval # retval is still None, so go through the equality logic # If expressions have the same structure, they must be equal. if lhs == rhs: return True # e.g. True == True elif all(isinstance(i, BooleanAtom) for i in (rhs, lhs)): return False # True != False elif not (lhs.is_Symbol or rhs.is_Symbol) and (isinstance(lhs, Boolean) != isinstance(rhs, Boolean)): return False # only Booleans can equal Booleans _lhs = AssumptionsWrapper(lhs, assumptions) _rhs = AssumptionsWrapper(rhs, assumptions) if _lhs.is_infinite or _rhs.is_infinite: if fuzzy_xor([_lhs.is_infinite, _rhs.is_infinite]): return False if fuzzy_xor([_lhs.is_extended_real, _rhs.is_extended_real]): return False if fuzzy_and([_lhs.is_extended_real, _rhs.is_extended_real]): return fuzzy_xor([ _lhs.is_extended_positive, fuzzy_not(_rhs.is_extended_positive) ]) # Try to split real/imaginary parts and equate them I = S.ImaginaryUnit def split_real_imag(expr): real_imag = lambda t: ( 'real' if is_extended_real(t, assumptions) else 'imag' if is_extended_real(I * t, assumptions) else None) return sift(Add.make_args(expr), real_imag) lhs_ri = split_real_imag(lhs) if not lhs_ri[None]: rhs_ri = split_real_imag(rhs) if not rhs_ri[None]: eq_real = is_eq(Add(*lhs_ri['real']), Add(*rhs_ri['real']), assumptions) eq_imag = is_eq(I * Add(*lhs_ri['imag']), I * Add(*rhs_ri['imag']), assumptions) return fuzzy_and(map(fuzzy_bool, [eq_real, eq_imag])) # Compare e.g. zoo with 1+I*oo by comparing args arglhs = arg(lhs) argrhs = arg(rhs) # Guard against Eq(nan, nan) -> Falsesymp if not (arglhs == S.NaN and argrhs == S.NaN): return fuzzy_bool(is_eq(arglhs, argrhs, assumptions)) if all(isinstance(i, Expr) for i in (lhs, rhs)): # see if the difference evaluates dif = lhs - rhs _dif = AssumptionsWrapper(dif, assumptions) z = _dif.is_zero if z is not None: if z is False and _dif.is_commutative: # issue 10728 return False if z: return True n2 = _n2(lhs, rhs) if n2 is not None: return _sympify(n2 == 0) # see if the ratio evaluates n, d = dif.as_numer_denom() rv = None _n = AssumptionsWrapper(n, assumptions) _d = AssumptionsWrapper(d, assumptions) if _n.is_zero: rv = _d.is_nonzero elif _n.is_finite: if _d.is_infinite: rv = True elif _n.is_zero is False: rv = _d.is_infinite if rv is None: # if the condition that makes the denominator # infinite does not make the original expression # True then False can be returned l, r = clear_coefficients(d, S.Infinity) args = [_.subs(l, r) for _ in (lhs, rhs)] if args != [lhs, rhs]: rv = fuzzy_bool(is_eq(*args, assumptions)) if rv is True: rv = None elif any(is_infinite(a, assumptions) for a in Add.make_args(n)): # (inf or nan)/x != 0 rv = False if rv is not None: return rv
def test_meijerint(): from sympy.core.function import expand from sympy.core.symbol import symbols from sympy.functions.elementary.complexes import arg s, t, mu = symbols('s t mu', real=True) assert integrate( meijerg([], [], [0], [], s * t) * meijerg([], [], [mu / 2], [-mu / 2], t**2 / 4), (t, 0, oo)).is_Piecewise s = symbols('s', positive=True) assert integrate(x**s*meijerg([[], []], [[0], []], x), (x, 0, oo)) == \ gamma(s + 1) assert integrate(x**s * meijerg([[], []], [[0], []], x), (x, 0, oo), meijerg=True) == gamma(s + 1) assert isinstance( integrate(x**s * meijerg([[], []], [[0], []], x), (x, 0, oo), meijerg=False), Integral) assert meijerint_indefinite(exp(x), x) == exp(x) # TODO what simplifications should be done automatically? # This tests "extra case" for antecedents_1. a, b = symbols('a b', positive=True) assert simplify(meijerint_definite(x**a, x, 0, b)[0]) == \ b**(a + 1)/(a + 1) # This tests various conditions and expansions: assert meijerint_definite((x + 1)**3 * exp(-x), x, 0, oo) == (16, True) # Again, how about simplifications? sigma, mu = symbols('sigma mu', positive=True) i, c = meijerint_definite(exp(-((x - mu) / (2 * sigma))**2), x, 0, oo) assert simplify(i) == sqrt(pi) * sigma * (2 - erfc(mu / (2 * sigma))) assert c == True i, _ = meijerint_definite(exp(-mu * x) * exp(sigma * x), x, 0, oo) # TODO it would be nice to test the condition assert simplify(i) == 1 / (mu - sigma) # Test substitutions to change limits assert meijerint_definite(exp(x), x, -oo, 2) == (exp(2), True) # Note: causes a NaN in _check_antecedents assert expand(meijerint_definite(exp(x), x, 0, I)[0]) == exp(I) - 1 assert expand(meijerint_definite(exp(-x), x, 0, x)[0]) == \ 1 - exp(-exp(I*arg(x))*abs(x)) # Test -oo to oo assert meijerint_definite(exp(-x**2), x, -oo, oo) == (sqrt(pi), True) assert meijerint_definite(exp(-abs(x)), x, -oo, oo) == (2, True) assert meijerint_definite(exp(-(2*x - 3)**2), x, -oo, oo) == \ (sqrt(pi)/2, True) assert meijerint_definite(exp(-abs(2 * x - 3)), x, -oo, oo) == (1, True) assert meijerint_definite( exp(-((x - mu) / sigma)**2 / 2) / sqrt(2 * pi * sigma**2), x, -oo, oo) == (1, True) assert meijerint_definite(sinc(x)**2, x, -oo, oo) == (pi, True) # Test one of the extra conditions for 2 g-functinos assert meijerint_definite(exp(-x) * sin(x), x, 0, oo) == (S.Half, True) # Test a bug def res(n): return (1 / (1 + x**2)).diff(x, n).subs(x, 1) * (-1)**n for n in range(6): assert integrate(exp(-x)*sin(x)*x**n, (x, 0, oo), meijerg=True) == \ res(n) # This used to test trigexpand... now it is done by linear substitution assert simplify(integrate(exp(-x) * sin(x + a), (x, 0, oo), meijerg=True)) == sqrt(2) * sin(a + pi / 4) / 2 # Test the condition 14 from prudnikov. # (This is besselj*besselj in disguise, to stop the product from being # recognised in the tables.) a, b, s = symbols('a b s') from sympy.functions.elementary.complexes import re assert meijerint_definite( meijerg([], [], [a / 2], [-a / 2], x / 4) * meijerg([], [], [b / 2], [-b / 2], x / 4) * x**(s - 1), x, 0, oo) == ((4 * 2**(2 * s - 2) * gamma(-2 * s + 1) * gamma(a / 2 + b / 2 + s) / (gamma(-a / 2 + b / 2 - s + 1) * gamma(a / 2 - b / 2 - s + 1) * gamma(a / 2 + b / 2 - s + 1)), (re(s) < 1) & (re(s) < S(1) / 2) & (re(a) / 2 + re(b) / 2 + re(s) > 0))) # test a bug assert integrate(sin(x**a)*sin(x**b), (x, 0, oo), meijerg=True) == \ Integral(sin(x**a)*sin(x**b), (x, 0, oo)) # test better hyperexpand assert integrate(exp(-x**2)*log(x), (x, 0, oo), meijerg=True) == \ (sqrt(pi)*polygamma(0, S.Half)/4).expand() # Test hyperexpand bug. from sympy.functions.special.gamma_functions import lowergamma n = symbols('n', integer=True) assert simplify(integrate(exp(-x)*x**n, x, meijerg=True)) == \ lowergamma(n + 1, x) # Test a bug with argument 1/x alpha = symbols('alpha', positive=True) assert meijerint_definite((2 - x)**alpha*sin(alpha/x), x, 0, 2) == \ (sqrt(pi)*alpha*gamma(alpha + 1)*meijerg(((), (alpha/2 + S.Half, alpha/2 + 1)), ((0, 0, S.Half), (Rational(-1, 2),)), alpha**2/16)/4, True) # test a bug related to 3016 a, s = symbols('a s', positive=True) assert simplify(integrate(x**s*exp(-a*x**2), (x, -oo, oo))) == \ a**(-s/2 - S.Half)*((-1)**s + 1)*gamma(s/2 + S.Half)/2
def test_sympy__functions__elementary__complexes__arg(): from sympy.functions.elementary.complexes import arg assert _test_args(arg(x))
def test_arg(): x = Symbol('x', complex = True) assert refine(arg(x), Q.positive(x)) == 0 assert refine(arg(x), Q.negative(x)) == pi
def bode_phase_evalf(system, point): expr = system.to_expr() _w = Dummy("w", real=True) w_expr = expr.subs({system.var: I * _w}) return arg(w_expr).subs({_w: point}).evalf()
def is_eq(lhs, rhs): """ Fuzzy bool representing mathematical equality between lhs and rhs. Parameters ========== lhs: Expr The left-hand side of the expression, must be sympified. rhs: Expr The right-hand side of the expression, must be sympified. Returns ======= True if lhs is equal to rhs, false is lhs is not equal to rhs, and None if the comparison between lhs and rhs is indeterminate. Explanation =========== This function is intended to give a relatively fast determination and deliberately does not attempt slow calculations that might help in obtaining a determination of True or False in more difficult cases. InEquality classes, such as Lt, Gt, etc. Use one of is_ge, is_le, etc. To implement comparisons with ``Gt(a, b)`` or ``a > b`` etc for an ``Expr`` subclass it is only necessary to define a dispatcher method for ``_eval_is_ge`` like >>> from sympy.core.relational import is_eq >>> from sympy.core.relational import is_neq >>> from sympy import S, Basic, Eq, sympify >>> from sympy.abc import x >>> from sympy.multipledispatch import dispatch >>> class MyBasic(Basic): ... def __new__(cls, arg): ... return Basic.__new__(cls, sympify(arg)) ... @property ... def value(self): ... return self.args[0] ... >>> @dispatch(MyBasic, MyBasic) ... def _eval_is_eq(a, b): ... return is_eq(a.value, b.value) ... >>> a = MyBasic(1) >>> b = MyBasic(1) >>> a == b True >>> Eq(a, b) True >>> a != b False >>> is_eq(a, b) True Examples ======== >>> is_eq(S(0), S(0)) True >>> Eq(0, 0) True >>> is_neq(S(0), S(0)) False >>> is_eq(S(0), S(2)) False >>> Eq(0, 2) False >>> is_neq(S(0), S(2)) True >>> is_eq(S(0), x) >>> Eq(S(0), x) Eq(0, x) """ from sympy.core.add import Add from sympy.functions.elementary.complexes import arg from sympy.simplify.simplify import clear_coefficients from sympy.utilities.iterables import sift # here, _eval_Eq is only called for backwards compatibility # new code should use is_eq with multiple dispatch as # outlined in the docstring for side1, side2 in (lhs, rhs), (rhs, lhs): eval_func = getattr(side1, '_eval_Eq', None) if eval_func is not None: retval = eval_func(side2) if retval is not None: return retval retval = _eval_is_eq(lhs, rhs) if retval is not None: return retval if dispatch(type(lhs), type(rhs)) != dispatch(type(rhs), type(lhs)): retval = _eval_is_eq(rhs, lhs) if retval is not None: return retval # retval is still None, so go through the equality logic # If expressions have the same structure, they must be equal. if lhs == rhs: return True # e.g. True == True elif all(isinstance(i, BooleanAtom) for i in (rhs, lhs)): return False # True != False elif not (lhs.is_Symbol or rhs.is_Symbol) and (isinstance(lhs, Boolean) != isinstance(rhs, Boolean)): return False # only Booleans can equal Booleans if lhs.is_infinite or rhs.is_infinite: if fuzzy_xor([lhs.is_infinite, rhs.is_infinite]): return False if fuzzy_xor([lhs.is_extended_real, rhs.is_extended_real]): return False if fuzzy_and([lhs.is_extended_real, rhs.is_extended_real]): return fuzzy_xor([ lhs.is_extended_positive, fuzzy_not(rhs.is_extended_positive) ]) # Try to split real/imaginary parts and equate them I = S.ImaginaryUnit def split_real_imag(expr): real_imag = lambda t: ('real' if t.is_extended_real else 'imag' if (I * t).is_extended_real else None) return sift(Add.make_args(expr), real_imag) lhs_ri = split_real_imag(lhs) if not lhs_ri[None]: rhs_ri = split_real_imag(rhs) if not rhs_ri[None]: eq_real = Eq(Add(*lhs_ri['real']), Add(*rhs_ri['real'])) eq_imag = Eq(I * Add(*lhs_ri['imag']), I * Add(*rhs_ri['imag'])) return fuzzy_and(map(fuzzy_bool, [eq_real, eq_imag])) # Compare e.g. zoo with 1+I*oo by comparing args arglhs = arg(lhs) argrhs = arg(rhs) # Guard against Eq(nan, nan) -> Falsesymp if not (arglhs == S.NaN and argrhs == S.NaN): return fuzzy_bool(Eq(arglhs, argrhs)) if all(isinstance(i, Expr) for i in (lhs, rhs)): # see if the difference evaluates dif = lhs - rhs z = dif.is_zero if z is not None: if z is False and dif.is_commutative: # issue 10728 return False if z: return True n2 = _n2(lhs, rhs) if n2 is not None: return _sympify(n2 == 0) # see if the ratio evaluates n, d = dif.as_numer_denom() rv = None if n.is_zero: rv = d.is_nonzero elif n.is_finite: if d.is_infinite: rv = True elif n.is_zero is False: rv = d.is_infinite if rv is None: # if the condition that makes the denominator # infinite does not make the original expression # True then False can be returned l, r = clear_coefficients(d, S.Infinity) args = [_.subs(l, r) for _ in (lhs, rhs)] if args != [lhs, rhs]: rv = fuzzy_bool(Eq(*args)) if rv is True: rv = None elif any(a.is_infinite for a in Add.make_args(n)): # (inf or nan)/x != 0 rv = False if rv is not None: return rv
def __new__(cls, lhs, rhs=None, **options): from sympy.core.add import Add from sympy.core.containers import Tuple from sympy.core.logic import fuzzy_bool, fuzzy_xor, fuzzy_and, fuzzy_not from sympy.core.expr import _n2 from sympy.functions.elementary.complexes import arg from sympy.simplify.simplify import clear_coefficients from sympy.utilities.iterables import sift if rhs is None: SymPyDeprecationWarning(feature="Eq(expr) with rhs default to 0", useinstead="Eq(expr, 0)", issue=16587, deprecated_since_version="1.5").warn() rhs = 0 lhs = _sympify(lhs) rhs = _sympify(rhs) evaluate = options.pop('evaluate', global_parameters.evaluate) if evaluate: # If one expression has an _eval_Eq, return its results. if hasattr(lhs, '_eval_Eq'): r = lhs._eval_Eq(rhs) if r is not None: return r if hasattr(rhs, '_eval_Eq'): r = rhs._eval_Eq(lhs) if r is not None: return r # If expressions have the same structure, they must be equal. if lhs == rhs: return S.true # e.g. True == True elif all(isinstance(i, BooleanAtom) for i in (rhs, lhs)): return S.false # True != False elif not (lhs.is_Symbol or rhs.is_Symbol) and (isinstance( lhs, Boolean) != isinstance(rhs, Boolean)): return S.false # only Booleans can equal Booleans if lhs.is_infinite or rhs.is_infinite: if fuzzy_xor([lhs.is_infinite, rhs.is_infinite]): return S.false if fuzzy_xor([lhs.is_extended_real, rhs.is_extended_real]): return S.false if fuzzy_and([lhs.is_extended_real, rhs.is_extended_real]): r = fuzzy_xor([ lhs.is_extended_positive, fuzzy_not(rhs.is_extended_positive) ]) return S(r) # Try to split real/imaginary parts and equate them I = S.ImaginaryUnit def split_real_imag(expr): real_imag = lambda t: ('real' if t.is_extended_real else 'imag' if (I * t).is_extended_real else None) return sift(Add.make_args(expr), real_imag) lhs_ri = split_real_imag(lhs) if not lhs_ri[None]: rhs_ri = split_real_imag(rhs) if not rhs_ri[None]: eq_real = Eq(Add(*lhs_ri['real']), Add(*rhs_ri['real'])) eq_imag = Eq(I * Add(*lhs_ri['imag']), I * Add(*rhs_ri['imag'])) res = fuzzy_and(map(fuzzy_bool, [eq_real, eq_imag])) if res is not None: return S(res) # Compare e.g. zoo with 1+I*oo by comparing args arglhs = arg(lhs) argrhs = arg(rhs) # Guard against Eq(nan, nan) -> False if not (arglhs == S.NaN and argrhs == S.NaN): res = fuzzy_bool(Eq(arglhs, argrhs)) if res is not None: return S(res) return Relational.__new__(cls, lhs, rhs, **options) if all(isinstance(i, Expr) for i in (lhs, rhs)): # see if the difference evaluates dif = lhs - rhs z = dif.is_zero if z is not None: if z is False and dif.is_commutative: # issue 10728 return S.false if z: return S.true # evaluate numerically if possible n2 = _n2(lhs, rhs) if n2 is not None: return _sympify(n2 == 0) # see if the ratio evaluates n, d = dif.as_numer_denom() rv = None if n.is_zero: rv = d.is_nonzero elif n.is_finite: if d.is_infinite: rv = S.true elif n.is_zero is False: rv = d.is_infinite if rv is None: # if the condition that makes the denominator # infinite does not make the original expression # True then False can be returned l, r = clear_coefficients(d, S.Infinity) args = [_.subs(l, r) for _ in (lhs, rhs)] if args != [lhs, rhs]: rv = fuzzy_bool(Eq(*args)) if rv is True: rv = None elif any(a.is_infinite for a in Add.make_args(n)): # (inf or nan)/x != 0 rv = S.false if rv is not None: return _sympify(rv) return Relational.__new__(cls, lhs, rhs, **options)
def test_issue_4035(): x = Symbol('x') assert Abs(x).expand(trig=True) == Abs(x) assert sign(x).expand(trig=True) == sign(x) assert arg(x).expand(trig=True) == arg(x)
def test_arg_rewrite(): assert arg(1 + I) == atan2(1, 1) x = Symbol('x', real=True) y = Symbol('y', real=True) assert arg(x + I * y).rewrite(atan2) == atan2(y, x)
def test_arg(): assert arg(0) is nan assert arg(1) == 0 assert arg(-1) == pi assert arg(I) == pi / 2 assert arg(-I) == -pi / 2 assert arg(1 + I) == pi / 4 assert arg(-1 + I) == pi * Rational(3, 4) assert arg(1 - I) == -pi / 4 assert arg(exp_polar(4 * pi * I)) == 4 * pi assert arg(exp_polar(-7 * pi * I)) == -7 * pi assert arg(exp_polar(5 - 3 * pi * I / 4)) == pi * Rational(-3, 4) f = Function('f') assert not arg(f(0) + I * f(1)).atoms(re) # check nesting x = Symbol('x') assert arg(arg(arg(x))) is not S.NaN assert arg(arg(arg(arg(x)))) is S.NaN r = Symbol('r', extended_real=True) assert arg(arg(r)) is not S.NaN assert arg(arg(arg(r))) is S.NaN p = Function('p', extended_positive=True) assert arg(p(x)) == 0 assert arg((3 + I) * p(x)) == arg(3 + I) p = Symbol('p', positive=True) assert arg(p) == 0 assert arg(p * I) == pi / 2 n = Symbol('n', negative=True) assert arg(n) == pi assert arg(n * I) == -pi / 2 x = Symbol('x') assert conjugate(arg(x)) == arg(x) e = p + I * p**2 assert arg(e) == arg(1 + p * I) # make sure sign doesn't swap e = -2 * p + 4 * I * p**2 assert arg(e) == arg(-1 + 2 * p * I) # make sure sign isn't lost x = symbols('x', real=True) # could be zero e = x + I * x assert arg(e) == arg(x * (1 + I)) assert arg(e / p) == arg(x * (1 + I)) e = p * cos(p) + I * log(p) * exp(p) assert arg(e).args[0] == e # keep it simple -- let the user do more advanced cancellation e = (p + 1) + I * (p**2 - 1) assert arg(e).args[0] == e f = Function('f') e = 2 * x * (f(0) - 1) - 2 * x * f(0) assert arg(e) == arg(-2 * x) assert arg(f(0)).func == arg and arg(f(0)).args == (f(0), )