Source code for sympy.functions.elementary.trigonometric

from __future__ import print_function, division

from sympy.core.add import Add
from sympy.core.basic import C, sympify, cacheit
from sympy.core.singleton import S
from sympy.core.numbers import igcdex
from sympy.core.function import Function, ArgumentIndexError
from sympy.functions.elementary.miscellaneous import sqrt
from sympy.functions.elementary.exponential import log
from sympy.functions.elementary.hyperbolic import HyperbolicFunction
from sympy.utilities.iterables import numbered_symbols
from sympy.core.compatibility import xrange

###############################################################################
########################## TRIGONOMETRIC FUNCTIONS ############################
###############################################################################


class TrigonometricFunction(Function):
    """Base class for trigonometric functions. """

    unbranched = True

    def _eval_is_rational(self):
        s = self.func(*self.args)
        if s.func == self.func:
            if s.args[0].is_rational:
                return False
        else:
            return s.is_rational


def _peeloff_pi(arg):
    """
    Split ARG into two parts, a "rest" and a multiple of pi/2.
    This assumes ARG to be an Add.
    The multiple of pi returned in the second position is always a Rational.

    Examples:
    >>> from sympy.functions.elementary.trigonometric import _peeloff_pi as peel
    >>> from sympy import pi
    >>> from sympy.abc import x, y
    >>> peel(x + pi/2)
    (x, pi/2)
    >>> peel(x + 2*pi/3 + pi*y)
    (x + pi*y + pi/6, pi/2)
    """
    for a in Add.make_args(arg):
        if a is S.Pi:
            K = S.One
            break
        elif a.is_Mul:
            K, p = a.as_two_terms()
            if p is S.Pi and K.is_Rational:
                break
    else:
        return arg, S.Zero

    m1 = (K % S.Half) * S.Pi
    m2 = K*S.Pi - m1
    return arg - m2, m2


def _pi_coeff(arg, cycles=1):
    """
    When arg is a Number times pi (e.g. 3*pi/2) then return the Number
    normalized to be in the range [0, 2], else None.

    When an even multiple of pi is encountered, if it is multiplying
    something with known parity then the multiple is returned as 0 otherwise
    as 2.

    Examples
    ========

    >>> from sympy.functions.elementary.trigonometric import _pi_coeff as coeff
    >>> from sympy import pi
    >>> from sympy.abc import x, y
    >>> coeff(3*x*pi)
    3*x
    >>> coeff(11*pi/7)
    11/7
    >>> coeff(-11*pi/7)
    3/7
    >>> coeff(4*pi)
    0
    >>> coeff(5*pi)
    1
    >>> coeff(5.0*pi)
    1
    >>> coeff(5.5*pi)
    3/2
    >>> coeff(2 + pi)

    """
    arg = sympify(arg)
    if arg is S.Pi:
        return S.One
    elif not arg:
        return S.Zero
    elif arg.is_Mul:
        cx = arg.coeff(S.Pi)
        if cx:
            c, x = cx.as_coeff_Mul()  # pi is not included as coeff
            if c.is_Float:
                # recast exact binary fractions to Rationals
                f = abs(c) % 1
                if f != 0:
                    p = -int(round(log(f, 2).evalf()))
                    m = 2**p
                    cm = c*m
                    i = int(cm)
                    if i == cm:
                        c = C.Rational(i, m)
                        cx = c*x
                else:
                    c = C.Rational(int(c))
                    cx = c*x
            if x.is_integer:
                c2 = c % 2
                if c2 == 1:
                    return x
                elif not c2:
                    if x.is_even is not None:  # known parity
                        return S.Zero
                    return 2*x
                else:
                    return c2*x
            return cx


[docs]class sin(TrigonometricFunction): """ The sine function. * sin(x) -> Returns the sine of x (measured in radians) Notes ===== * sin(x) will evaluate automatically in the case x is a multiple of pi, pi/2, pi/3, pi/4 and pi/6. Examples ======== >>> from sympy import sin, pi >>> from sympy.abc import x >>> sin(x**2).diff(x) 2*x*cos(x**2) >>> sin(1).diff(x) 0 >>> sin(pi) 0 >>> sin(pi/2) 1 >>> sin(pi/6) 1/2 See Also ======== cos, tan, asin References ========== .. [1] http://planetmath.org/encyclopedia/DefinitionsInTrigonometry.html """ nargs = 1 def fdiff(self, argindex=1): if argindex == 1: return cos(self.args[0]) else: raise ArgumentIndexError(self, argindex) @classmethod def eval(cls, arg): if arg.is_Number: if arg is S.NaN: return S.NaN elif arg is S.Zero: return S.Zero elif arg is S.Infinity or arg is S.NegativeInfinity: return if arg.could_extract_minus_sign(): return -cls(-arg) i_coeff = arg.as_coefficient(S.ImaginaryUnit) if i_coeff is not None: return S.ImaginaryUnit * C.sinh(i_coeff) pi_coeff = _pi_coeff(arg) if pi_coeff is not None: if pi_coeff.is_integer: return S.Zero if (2*pi_coeff).is_integer: return S.NegativeOne**(pi_coeff - S.Half) if not pi_coeff.is_Rational: narg = pi_coeff*S.Pi if narg != arg: return cls(narg) return None # http://code.google.com/p/sympy/issues/detail?id=2949 # transform a sine to a cosine, to avoid redundant code if pi_coeff.is_Rational: x = pi_coeff % 2 if x > 1: return -cls((x % 1)*S.Pi) if 2*x > 1: return cls((1 - x)*S.Pi) narg = ((pi_coeff + C.Rational(3, 2)) % 2)*S.Pi result = cos(narg) if not isinstance(result, cos): return result if pi_coeff*S.Pi != arg: return cls(pi_coeff*S.Pi) return None if arg.is_Add: x, m = _peeloff_pi(arg) if m: return sin(m)*cos(x) + cos(m)*sin(x) if arg.func is asin: return arg.args[0] if arg.func is atan: x = arg.args[0] return x / sqrt(1 + x**2) if arg.func is atan2: y, x = arg.args return y / sqrt(x**2 + y**2) if arg.func is acos: x = arg.args[0] return sqrt(1 - x**2) if arg.func is acot: x = arg.args[0] return 1 / (sqrt(1 + 1 / x**2) * x) @staticmethod @cacheit def taylor_term(n, x, *previous_terms): if n < 0 or n % 2 == 0: return S.Zero else: x = sympify(x) if len(previous_terms) > 2: p = previous_terms[-2] return -p * x**2 / (n*(n - 1)) else: return (-1)**(n//2) * x**(n)/C.factorial(n) def _eval_rewrite_as_exp(self, arg): exp, I = C.exp, S.ImaginaryUnit if isinstance(arg, TrigonometricFunction) or isinstance(arg, HyperbolicFunction): arg = arg.func(arg.args[0]).rewrite(exp) return (exp(arg*I) - exp(-arg*I)) / (2*I) def _eval_rewrite_as_Pow(self, arg): if arg.func is log: I = S.ImaginaryUnit x = arg.args[0] return I*x**-I / 2 - I*x**I /2 def _eval_rewrite_as_cos(self, arg): return -cos(arg + S.Pi/2) def _eval_rewrite_as_tan(self, arg): tan_half = tan(S.Half*arg) return 2*tan_half/(1 + tan_half**2) def _eval_rewrite_as_sincos(self, arg): return sin(arg)*cos(arg)/cos(arg) def _eval_rewrite_as_cot(self, arg): cot_half = cot(S.Half*arg) return 2*cot_half/(1 + cot_half**2) def _eval_rewrite_as_pow(self, arg): return self.rewrite(cos).rewrite(pow) def _eval_rewrite_as_sqrt(self, arg): return self.rewrite(cos).rewrite(sqrt) def _eval_conjugate(self): return self.func(self.args[0].conjugate()) def as_real_imag(self, deep=True, **hints): if self.args[0].is_real: if deep: hints['complex'] = False return (self.expand(deep, **hints), S.Zero) else: return (self, S.Zero) if deep: re, im = self.args[0].expand(deep, **hints).as_real_imag() else: re, im = self.args[0].as_real_imag() return (sin(re)*C.cosh(im), cos(re)*C.sinh(im)) def _eval_expand_trig(self, **hints): from sympy import expand_mul arg = self.args[0] x = None if arg.is_Add: # TODO, implement more if deep stuff here # TODO: Do this more efficiently for more than two terms x, y = arg.as_two_terms() sx = sin(x, evaluate=False)._eval_expand_trig() sy = sin(y, evaluate=False)._eval_expand_trig() cx = cos(x, evaluate=False)._eval_expand_trig() cy = cos(y, evaluate=False)._eval_expand_trig() return sx*cy + sy*cx else: n, x = arg.as_coeff_Mul(rational=True) if n.is_Integer: # n will be positive because of .eval # canonicalization # See http://mathworld.wolfram.com/Multiple-AngleFormulas.html if n.is_odd: return (-1)**((n - 1)/2)*C.chebyshevt(n, sin(x)) else: return expand_mul((-1)**(n/2 - 1)*cos(x)*C.chebyshevu(n - 1, sin(x)), deep=False) pi_coeff = _pi_coeff(arg) if pi_coeff is not None: if pi_coeff.is_Rational: return self.rewrite(sqrt) return sin(arg) def _eval_as_leading_term(self, x): arg = self.args[0].as_leading_term(x) if x in arg.free_symbols and C.Order(1, x).contains(arg): return arg else: return self.func(arg) def _eval_is_real(self): return self.args[0].is_real def _eval_is_bounded(self): arg = self.args[0] if arg.is_real: return True def _sage_(self): import sage.all as sage return sage.sin(self.args[0]._sage_())
[docs]class cos(TrigonometricFunction): """ The cosine function. * cos(x) -> Returns the cosine of x (measured in radians) Notes ===== * cos(x) will evaluate automatically in the case x is a multiple of pi, pi/2, pi/3, pi/4 and pi/6. Examples ======== >>> from sympy import cos, pi >>> from sympy.abc import x >>> cos(x**2).diff(x) -2*x*sin(x**2) >>> cos(1).diff(x) 0 >>> cos(pi) -1 >>> cos(pi/2) 0 >>> cos(2*pi/3) -1/2 See Also ======== sin, tan, acos References ========== .. [1] http://planetmath.org/encyclopedia/DefinitionsInTrigonometry.html """ nargs = 1 def fdiff(self, argindex=1): if argindex == 1: return -sin(self.args[0]) else: raise ArgumentIndexError(self, argindex) @classmethod def eval(cls, arg): if arg.is_Number: if arg is S.NaN: return S.NaN elif arg is S.Zero: return S.One elif arg is S.Infinity or arg is S.NegativeInfinity: # In this cases, it is unclear if we should # return S.NaN or leave un-evaluated. One # useful test case is how "limit(sin(x)/x,x,oo)" # is handled. # See test_sin_cos_with_infinity() an # Test for issue 209 # http://code.google.com/p/sympy/issues/detail?id=2097 # For now, we return un-evaluated. return if arg.could_extract_minus_sign(): return cls(-arg) i_coeff = arg.as_coefficient(S.ImaginaryUnit) if i_coeff is not None: return C.cosh(i_coeff) pi_coeff = _pi_coeff(arg) if pi_coeff is not None: if pi_coeff.is_integer: return (S.NegativeOne)**pi_coeff if (2*pi_coeff).is_integer: return S.Zero if not pi_coeff.is_Rational: narg = pi_coeff*S.Pi if narg != arg: return cls(narg) return None # cosine formula ##################### # http://code.google.com/p/sympy/issues/detail?id=2949 # explicit calculations are preformed for # cos(k pi / 8), cos(k pi /10), and cos(k pi / 12) # Some other exact values like cos(k pi/15) can be # calculated using a partial-fraction decomposition # by calling cos( X ).rewrite(sqrt) cst_table_some = { 3: S.Half, 5: (sqrt(5) + 1)/4, } if pi_coeff.is_Rational: q = pi_coeff.q p = pi_coeff.p % (2*q) if p > q: narg = (pi_coeff - 1)*S.Pi return -cls(narg) if 2*p > q: narg = (1 - pi_coeff)*S.Pi return -cls(narg) # If nested sqrt's are worse than un-evaluation # you can require q in (1, 2, 3, 4, 6) # q <= 12 returns expressions with 2 or fewer nestings. if q > 12: return None if q in cst_table_some: cts = cst_table_some[pi_coeff.q] return C.chebyshevt(pi_coeff.p, cts).expand() if 0 == q % 2: narg = (pi_coeff*2)*S.Pi nval = cls(narg) if None == nval: return None x = (2*pi_coeff + 1)/2 sign_cos = (-1)**((-1 if x < 0 else 1)*int(abs(x))) return sign_cos*sqrt( (1 + nval)/2 ) return None if arg.is_Add: x, m = _peeloff_pi(arg) if m: return cos(m)*cos(x) - sin(m)*sin(x) if arg.func is acos: return arg.args[0] if arg.func is atan: x = arg.args[0] return 1 / sqrt(1 + x**2) if arg.func is atan2: y, x = arg.args return x / sqrt(x**2 + y**2) if arg.func is asin: x = arg.args[0] return sqrt(1 - x ** 2) if arg.func is acot: x = arg.args[0] return 1 / sqrt(1 + 1 / x**2) @staticmethod @cacheit def taylor_term(n, x, *previous_terms): if n < 0 or n % 2 == 1: return S.Zero else: x = sympify(x) if len(previous_terms) > 2: p = previous_terms[-2] return -p * x**2 / (n*(n - 1)) else: return (-1)**(n//2)*x**(n)/C.factorial(n) def _eval_rewrite_as_exp(self, arg): exp, I = C.exp, S.ImaginaryUnit if isinstance(arg, TrigonometricFunction) or isinstance(arg, HyperbolicFunction): arg = arg.func(arg.args[0]).rewrite(exp) return (exp(arg*I) + exp(-arg*I)) / 2 def _eval_rewrite_as_Pow(self, arg): if arg.func is log: I = S.ImaginaryUnit x = arg.args[0] return x**I/2 + x**-I/2 def _eval_rewrite_as_sin(self, arg): return sin(arg + S.Pi/2) def _eval_rewrite_as_tan(self, arg): tan_half = tan(S.Half*arg)**2 return (1 - tan_half)/(1 + tan_half) def _eval_rewrite_as_sincos(self, arg): return sin(arg)*cos(arg)/sin(arg) def _eval_rewrite_as_cot(self, arg): cot_half = cot(S.Half*arg)**2 return (cot_half - 1)/(cot_half + 1) def _eval_rewrite_as_pow(self, arg): return self._eval_rewrite_as_sqrt(arg) def _eval_rewrite_as_sqrt(self, arg): _EXPAND_INTS = False def migcdex(x): # recursive calcuation of gcd and linear combination # for a sequence of integers. # Given (x1, x2, x3) # Returns (y1, y1, y3, g) # such that g is the gcd and x1*y1+x2*y2+x3*y3 - g = 0 # Note, that this is only one such linear combination. if len(x) == 1: return (1, x[0]) if len(x) == 2: return igcdex(x[0], x[-1]) g = migcdex(x[1:]) u, v, h = igcdex(x[0], g[-1]) return tuple([u] + [v*i for i in g[0:-1] ] + [h]) def ipartfrac(r, factors=None): if isinstance(r, int): return r assert isinstance(r, C.Rational) n = r.q if 2 > r.q*r.q: return r.q if None == factors: a = [n//x**y for x, y in factorint(r.q).items()] else: a = [n//x for x in factors] if len(a) == 1: return [ r ] h = migcdex(a) ans = [ r.p*C.Rational(i*j, r.q) for i, j in zip(h[:-1], a) ] assert r == sum(ans) return ans pi_coeff = _pi_coeff(arg) if pi_coeff is None: return None assert not pi_coeff.is_integer, "should have been simplified already" if not pi_coeff.is_Rational: return None cst_table_some = { 3: S.Half, 5: (sqrt(5) + 1)/4, 17: sqrt((15 + sqrt(17))/32 + sqrt(2)*(sqrt(17 - sqrt(17)) + sqrt(sqrt(2)*(-8*sqrt(17 + sqrt(17)) - (1 - sqrt(17)) *sqrt(17 - sqrt(17))) + 6*sqrt(17) + 34))/32) # 65537 and 257 are the only other known Fermat primes # Please add if you would like them } def fermatCoords(n): assert isinstance(n, int) assert n > 0 if n == 1 or 0 == n % 2: return False primes = dict( [(p, 0) for p in cst_table_some ] ) assert 1 not in primes for p_i in primes: while 0 == n % p_i: n = n/p_i primes[p_i] += 1 if 1 != n: return False if max(primes.values()) > 1: return False return tuple([ p for p in primes if primes[p] == 1]) if pi_coeff.q in cst_table_some: return C.chebyshevt(pi_coeff.p, cst_table_some[pi_coeff.q]).expand() if 0 == pi_coeff.q % 2: # recursively remove powers of 2 narg = (pi_coeff*2)*S.Pi nval = cos(narg) if None == nval: return None nval = nval.rewrite(sqrt) if not _EXPAND_INTS: if (isinstance(nval, cos) or isinstance(-nval, cos)): return None x = (2*pi_coeff + 1)/2 sign_cos = (-1)**((-1 if x < 0 else 1)*int(abs(x))) return sign_cos*sqrt( (1 + nval)/2 ) FC = fermatCoords(pi_coeff.q) if FC: decomp = ipartfrac(pi_coeff, FC) X = [(x[1], x[0]*S.Pi) for x in zip(decomp, numbered_symbols('z'))] pcls = cos(sum([x[0] for x in X]))._eval_expand_trig().subs(X) return pcls.rewrite(sqrt) if _EXPAND_INTS: decomp = ipartfrac(pi_coeff) X = [(x[1], x[0]*S.Pi) for x in zip(decomp, numbered_symbols('z'))] pcls = cos(sum([x[0] for x in X]))._eval_expand_trig().subs(X) return pcls return None def _eval_conjugate(self): return self.func(self.args[0].conjugate()) def as_real_imag(self, deep=True, **hints): if self.args[0].is_real: if deep: hints['complex'] = False return (self.expand(deep, **hints), S.Zero) else: return (self, S.Zero) if deep: re, im = self.args[0].expand(deep, **hints).as_real_imag() else: re, im = self.args[0].as_real_imag() return (cos(re)*C.cosh(im), -sin(re)*C.sinh(im)) def _eval_expand_trig(self, **hints): arg = self.args[0] x = None if arg.is_Add: # TODO: Do this more efficiently for more than two terms x, y = arg.as_two_terms() sx = sin(x, evaluate=False)._eval_expand_trig() sy = sin(y, evaluate=False)._eval_expand_trig() cx = cos(x, evaluate=False)._eval_expand_trig() cy = cos(y, evaluate=False)._eval_expand_trig() return cx*cy - sx*sy else: coeff, terms = arg.as_coeff_Mul(rational=True) if coeff.is_Integer: return C.chebyshevt(coeff, cos(terms)) pi_coeff = _pi_coeff(arg) if pi_coeff is not None: if pi_coeff.is_Rational: return self.rewrite(sqrt) return cos(arg) def _eval_as_leading_term(self, x): arg = self.args[0].as_leading_term(x) if x in arg.free_symbols and C.Order(1, x).contains(arg): return S.One else: return self.func(arg) def _eval_is_real(self): return self.args[0].is_real def _eval_is_bounded(self): arg = self.args[0] if arg.is_real: return True def _sage_(self): import sage.all as sage return sage.cos(self.args[0]._sage_())
class ReciprocalTrigonometricFunction(TrigonometricFunction): """Base class for reciprocal functions of trigonometric functions. """ nargs = 1 _reciprocal_of = None # mandatory, to be defined in subclass # _is_even and _is_odd are used for correct evaluation of csc(-x), sec(-x) # TODO refactor into TrigonometricFunction common parts of # trigonometric functions eval() like even/odd, func(x+2*k*pi), etc. _is_even = None # optional, to be defined in subclass _is_odd = None # optional, to be defined in subclass def _call_reciprocal(self, method_name, *args, **kwargs): # Calls method_name on _reciprocal_of o = self._reciprocal_of(self.args[0]) if kwargs: return getattr(o, method_name)(**kwargs) else: return getattr(o, method_name)(*args) def _calculate_reciprocal(self, method_name, *args, **kwargs): # If calling method_name on _reciprocal_of returns a value != None # then return the reciprocal of that value t = self._call_reciprocal(method_name, *args, **kwargs) return 1/t if t != None else t def _rewrite_reciprocal(self, method_name, arg): # Special handling for rewrite functions. If reciprocal rewrite returns # unmodified expression, then return None t = self._call_reciprocal(method_name, arg) if t != None and t != self._reciprocal_of(arg): return 1/t else: return def fdiff(self, argindex=1): return self._calculate_reciprocal("fdiff", argindex) def _eval_rewrite_as_exp(self, arg): return self._rewrite_reciprocal("_eval_rewrite_as_exp", arg) def _eval_rewrite_as_Pow(self, arg): return self._rewrite_reciprocal("_eval_rewrite_as_Pow", arg) def _eval_rewrite_as_sin(self, arg): return self._rewrite_reciprocal("_eval_rewrite_as_sin", arg) def _eval_rewrite_as_cos(self, arg): return self._rewrite_reciprocal("_eval_rewrite_as_cos", arg) def _eval_rewrite_as_tan(self, arg): return self._rewrite_reciprocal("_eval_rewrite_as_tan", arg) def _eval_rewrite_as_pow(self, arg): return self._rewrite_reciprocal("_eval_rewrite_as_pow", arg) def _eval_rewrite_as_sqrt(self, arg): return self._rewrite_reciprocal("_eval_rewrite_as_sqrt", arg) def _eval_conjugate(self): return self.func(self.args[0].conjugate()) def as_real_imag(self, deep=True, **hints): return (1/self._reciprocal_of(self.args[0])).as_real_imag(deep, **hints) def _eval_expand_trig(self, **hints): return self._calculate_reciprocal("_eval_expand_trig", **hints) def _eval_is_real(self): return self._reciprocal_of(self.args[0])._eval_is_real() def _eval_as_leading_term(self, x): return (1/self._reciprocal_of(self.args[0]))._eval_as_leading_term(x) def _eval_is_bounded(self): return (1/self._reciprocal_of(self.args[0])).is_bounded def _eval_nseries(self, x, n, logx): return (1/self._reciprocal_of(self.args[0]))._eval_nseries(x, n, logx) @classmethod def eval(cls, arg): if arg.could_extract_minus_sign(): if cls._is_even: return cls(-arg) if cls._is_odd: return -cls(-arg) pi_coeff = _pi_coeff(arg) if (pi_coeff is not None and not (2*pi_coeff).is_integer and pi_coeff.is_Rational): q = pi_coeff.q p = pi_coeff.p % (2*q) if p > q: narg = (pi_coeff - 1)*S.Pi return -cls(narg) if 2*p > q: narg = (1 - pi_coeff)*S.Pi return -cls(narg) t = cls._reciprocal_of.eval(arg) return 1/t if t != None else t class sec(ReciprocalTrigonometricFunction): _reciprocal_of = cos _is_even = True def _eval_rewrite_as_cos(self, arg): return (1/cos(arg)) def _eval_rewrite_as_sincos(self, arg): return sin(arg)/(cos(arg)*sin(arg)) def fdiff(self, argindex=1): if argindex == 1: return tan(self.args[0])*sec(self.args[0]) else: raise ArgumentIndexError(self, argindex) # TODO def taylor_term(n, x, *previous_terms): def _sage_(self): import sage.all as sage return sage.sec(self.args[0]._sage_()) class csc(ReciprocalTrigonometricFunction): _reciprocal_of = sin _is_odd = True def _eval_rewrite_as_sin(self, arg): return (1/sin(arg)) def _eval_rewrite_as_sincos(self, arg): return cos(arg)/(sin(arg)*cos(arg)) def fdiff(self, argindex=1): if argindex == 1: return -cot(self.args[0])*csc(self.args[0]) else: raise ArgumentIndexError(self, argindex) # TODO def taylor_term(n, x, *previous_terms): def _sage_(self): import sage.all as sage return sage.csc(self.args[0]._sage_())
[docs]class tan(TrigonometricFunction): """ tan(x) -> Returns the tangent of x (measured in radians) Notes ===== * tan(x) will evaluate automatically in the case x is a multiple of pi. Examples ======== >>> from sympy import tan >>> from sympy.abc import x >>> tan(x**2).diff(x) 2*x*(tan(x**2)**2 + 1) >>> tan(1).diff(x) 0 See Also ======== sin, cos, atan References ========== .. [1] http://planetmath.org/encyclopedia/DefinitionsInTrigonometry.html """ nargs = 1 def fdiff(self, argindex=1): if argindex == 1: return S.One + self**2 else: raise ArgumentIndexError(self, argindex)
[docs] def inverse(self, argindex=1): """ Returns the inverse of this function. """ return atan
@classmethod def eval(cls, arg): if arg.is_Number: if arg is S.NaN: return S.NaN elif arg is S.Zero: return S.Zero if arg.could_extract_minus_sign(): return -cls(-arg) i_coeff = arg.as_coefficient(S.ImaginaryUnit) if i_coeff is not None: return S.ImaginaryUnit * C.tanh(i_coeff) pi_coeff = _pi_coeff(arg, 2) if pi_coeff is not None: if pi_coeff.is_integer: return S.Zero if not pi_coeff.is_Rational: narg = pi_coeff*S.Pi if narg != arg: return cls(narg) return None if pi_coeff.is_Rational: narg = ((pi_coeff + S.Half) % 1 - S.Half)*S.Pi # see cos() to specify which expressions should be # expanded automatically in terms of radicals cresult, sresult = cos(narg), cos(narg - S.Pi/2) if not isinstance(cresult, cos) \ and not isinstance(sresult, cos): if cresult == 0: return S.ComplexInfinity return (sresult/cresult) if narg != arg: return cls(narg) if arg.is_Add: x, m = _peeloff_pi(arg) if m: tanm = tan(m) tanx = tan(x) if tanm is S.ComplexInfinity: return -cot(x) return (tanm + tanx)/(1 - tanm*tanx) if arg.func is atan: return arg.args[0] if arg.func is atan2: y, x = arg.args return y/x if arg.func is asin: x = arg.args[0] return x / sqrt(1 - x**2) if arg.func is acos: x = arg.args[0] return sqrt(1 - x**2) / x if arg.func is acot: x = arg.args[0] return 1 / x @staticmethod @cacheit def taylor_term(n, x, *previous_terms): if n < 0 or n % 2 == 0: return S.Zero else: x = sympify(x) a, b = ((n - 1)//2), 2**(n + 1) B = C.bernoulli(n + 1) F = C.factorial(n + 1) return (-1)**a * b*(b - 1) * B/F * x**n def _eval_nseries(self, x, n, logx): i = self.args[0].limit(x, 0)*2/S.Pi if i and i.is_Integer: return self.rewrite(cos)._eval_nseries(x, n=n, logx=logx) return Function._eval_nseries(self, x, n=n, logx=logx) def _eval_rewrite_as_Pow(self, arg): if arg.func is log: I = S.ImaginaryUnit x = arg.args[0] return I*(x**-I - x**I)/(x**-I + x**I) def _eval_conjugate(self): return self.func(self.args[0].conjugate()) def as_real_imag(self, deep=True, **hints): if self.args[0].is_real: if deep: hints['complex'] = False return (self.expand(deep, **hints), S.Zero) else: return (self, S.Zero) if deep: re, im = self.args[0].expand(deep, **hints).as_real_imag() else: re, im = self.args[0].as_real_imag() denom = cos(re)**2 + C.sinh(im)**2 return (sin(re)*cos(re)/denom, C.sinh(im)*C.cosh(im)/denom) def _eval_expand_trig(self, **hints): arg = self.args[0] x = None if arg.is_Add: from sympy import symmetric_poly n = len(arg.args) TX = [] for x in arg.args: tx = tan(x, evaluate=False)._eval_expand_trig() TX.append(tx) Yg = numbered_symbols('Y') Y = [ next(Yg) for i in xrange(n) ] p = [0, 0] for i in xrange(n + 1): p[1 - i % 2] += symmetric_poly(i, Y)*(-1)**((i % 4)//2) return (p[0]/p[1]).subs(list(zip(Y, TX))) else: coeff, terms = arg.as_coeff_Mul(rational=True) if coeff.is_Integer and coeff > 1: I = S.ImaginaryUnit z = C.Symbol('dummy', real=True) P = ((1 + I*z)**coeff).expand() return (C.im(P)/C.re(P)).subs([(z, tan(terms))]) return tan(arg) def _eval_rewrite_as_exp(self, arg): exp, I = C.exp, S.ImaginaryUnit if isinstance(arg, TrigonometricFunction) or isinstance(arg, HyperbolicFunction): arg = arg.func(arg.args[0]).rewrite(exp) neg_exp, pos_exp = exp(-arg*I), exp(arg*I) return I*(neg_exp - pos_exp)/(neg_exp + pos_exp) def _eval_rewrite_as_sin(self, x): return 2*sin(x)**2/sin(2*x) def _eval_rewrite_as_cos(self, x): return -cos(x + S.Pi/2)/cos(x) def _eval_rewrite_as_sincos(self, arg): return sin(arg)/cos(arg) def _eval_rewrite_as_cot(self, arg): return 1/cot(arg) def _eval_rewrite_as_pow(self, arg): y = self.rewrite(cos).rewrite(pow) if y.has(cos): return None return y def _eval_rewrite_as_sqrt(self, arg): y = self.rewrite(cos).rewrite(sqrt) if y.has(cos): return None return y def _eval_as_leading_term(self, x): arg = self.args[0].as_leading_term(x) if x in arg.free_symbols and C.Order(1, x).contains(arg): return arg else: return self.func(arg) def _eval_is_real(self): return self.args[0].is_real def _eval_is_bounded(self): arg = self.args[0] if arg.is_imaginary: return True def _sage_(self): import sage.all as sage return sage.tan(self.args[0]._sage_())
[docs]class cot(TrigonometricFunction): """ cot(x) -> Returns the cotangent of x (measured in radians) """ nargs = 1 def fdiff(self, argindex=1): if argindex == 1: return S.NegativeOne - self**2 else: raise ArgumentIndexError(self, argindex)
[docs] def inverse(self, argindex=1): """ Returns the inverse of this function. """ return acot
@classmethod def eval(cls, arg): if arg.is_Number: if arg is S.NaN: return S.NaN if arg is S.Zero: return S.ComplexInfinity if arg.could_extract_minus_sign(): return -cls(-arg) i_coeff = arg.as_coefficient(S.ImaginaryUnit) if i_coeff is not None: return -S.ImaginaryUnit * C.coth(i_coeff) pi_coeff = _pi_coeff(arg, 2) if pi_coeff is not None: if pi_coeff.is_integer: return S.ComplexInfinity if not pi_coeff.is_Rational: narg = pi_coeff*S.Pi if narg != arg: return cls(narg) return None if pi_coeff.is_Rational: narg = (((pi_coeff + S.Half) % 1) - S.Half)*S.Pi # see cos() to specify which expressions should be # expanded automatically in terms of radicals cresult, sresult = cos(narg), cos(narg - S.Pi/2) if not isinstance(cresult, cos) \ and not isinstance(sresult, cos): if sresult == 0: return S.ComplexInfinity return cresult / sresult if narg != arg: return cls(narg) if arg.is_Add: x, m = _peeloff_pi(arg) if m: cotm = cot(m) if cotm == 0: return -tan(x) cotx = cot(x) if cotm is S.ComplexInfinity: return cotx if cotm.is_Rational: return (cotm*cotx - 1) / (cotm + cotx) return None if arg.func is acot: return arg.args[0] if arg.func is atan: x = arg.args[0] return 1 / x if arg.func is atan2: y, x = arg.args return x/y if arg.func is asin: x = arg.args[0] return sqrt(1 - x**2) / x if arg.func is acos: x = arg.args[0] return x / sqrt(1 - x**2) @staticmethod @cacheit def taylor_term(n, x, *previous_terms): if n == 0: return 1 / sympify(x) elif n < 0 or n % 2 == 0: return S.Zero else: x = sympify(x) B = C.bernoulli(n + 1) F = C.factorial(n + 1) return (-1)**((n + 1)//2) * 2**(n + 1) * B/F * x**n def _eval_nseries(self, x, n, logx): i = self.args[0].limit(x, 0)/S.Pi if i and i.is_Integer: return self.rewrite(cos)._eval_nseries(x, n=n, logx=logx) return self.rewrite(tan)._eval_nseries(x, n=n, logx=logx) def _eval_conjugate(self): assert len(self.args) == 1 return self.func(self.args[0].conjugate()) def as_real_imag(self, deep=True, **hints): if self.args[0].is_real: if deep: hints['complex'] = False return (self.expand(deep, **hints), S.Zero) else: return (self, S.Zero) if deep: re, im = self.args[0].expand(deep, **hints).as_real_imag() else: re, im = self.args[0].as_real_imag() denom = sin(re)**2 + C.sinh(im)**2 return (sin(re)*cos(re)/denom, -C.sinh(im)*C.cosh(im)/denom) def _eval_rewrite_as_exp(self, arg): exp, I = C.exp, S.ImaginaryUnit if isinstance(arg, TrigonometricFunction) or isinstance(arg, HyperbolicFunction): arg = arg.func(arg.args[0]).rewrite(exp) neg_exp, pos_exp = exp(-arg*I), exp(arg*I) return I*(pos_exp + neg_exp)/(pos_exp - neg_exp) def _eval_rewrite_as_Pow(self, arg): if arg.func is log: I = S.ImaginaryUnit x = arg.args[0] return -I*(x**-I + x**I)/(x**-I - x**I) def _eval_rewrite_as_sin(self, x): return 2*sin(2*x)/sin(x)**2 def _eval_rewrite_as_cos(self, x): return -cos(x)/cos(x + S.Pi/2) def _eval_rewrite_as_sincos(self, arg): return cos(arg)/sin(arg) def _eval_rewrite_as_tan(self, arg): return 1/tan(arg) def _eval_rewrite_as_pow(self, arg): y = self.rewrite(cos).rewrite(pow) if y.has(cos): return None return y def _eval_rewrite_as_sqrt(self, arg): y = self.rewrite(cos).rewrite(sqrt) if y.has(cos): return None return y def _eval_as_leading_term(self, x): arg = self.args[0].as_leading_term(x) if x in arg.free_symbols and C.Order(1, x).contains(arg): return 1/arg else: return self.func(arg) def _eval_is_real(self): return self.args[0].is_real def _eval_expand_trig(self, **hints): arg = self.args[0] x = None if arg.is_Add: from sympy import symmetric_poly n = len(arg.args) CX = [] for x in arg.args: cx = cot(x, evaluate=False)._eval_expand_trig() CX.append(cx) Yg = numbered_symbols('Y') Y = [ next(Yg) for i in xrange(n) ] p = [0, 0] for i in xrange(n, -1, -1): p[(n - i) % 2] += symmetric_poly(i, Y)*(-1)**(((n - i) % 4)//2) return (p[0]/p[1]).subs(list(zip(Y, CX))) else: coeff, terms = arg.as_coeff_Mul(rational=True) if coeff.is_Integer and coeff > 1: I = S.ImaginaryUnit z = C.Symbol('dummy', real=True) P = ((z + I)**coeff).expand() return (C.re(P)/C.im(P)).subs([(z, cot(terms))]) return cot(arg) def _sage_(self): import sage.all as sage return sage.cot(self.args[0]._sage_()) ############################################################################### ########################### TRIGONOMETRIC INVERSES ############################ ###############################################################################
[docs]class asin(Function): """ asin(x) -> Returns the arc sine of x (measured in radians) Notes ===== * asin(x) will evaluate automatically in the cases oo, -oo, 0, 1, -1 Examples ======== >>> from sympy import asin, oo, pi >>> asin(1) pi/2 >>> asin(-1) -pi/2 See Also ======== acos, atan, sin """ nargs = 1 def fdiff(self, argindex=1): if argindex == 1: return 1/sqrt(1 - self.args[0]**2) else: raise ArgumentIndexError(self, argindex) def _eval_is_rational(self): s = self.func(*self.args) if s.func == self.func: if s.args[0].is_rational: return False else: return s.is_rational @classmethod def eval(cls, arg): if arg.is_Number: if arg is S.NaN: return S.NaN elif arg is S.Infinity: return S.NegativeInfinity * S.ImaginaryUnit elif arg is S.NegativeInfinity: return S.Infinity * S.ImaginaryUnit elif arg is S.Zero: return S.Zero elif arg is S.One: return S.Pi / 2 elif arg is S.NegativeOne: return -S.Pi / 2 if arg.could_extract_minus_sign(): return -cls(-arg) if arg.is_number: cst_table = { sqrt(3)/2: 3, -sqrt(3)/2: -3, sqrt(2)/2: 4, -sqrt(2)/2: -4, 1/sqrt(2): 4, -1/sqrt(2): -4, sqrt((5 - sqrt(5))/8): 5, -sqrt((5 - sqrt(5))/8): -5, S.Half: 6, -S.Half: -6, sqrt(2 - sqrt(2))/2: 8, -sqrt(2 - sqrt(2))/2: -8, (sqrt(5) - 1)/4: 10, (1 - sqrt(5))/4: -10, (sqrt(3) - 1)/sqrt(2**3): 12, (1 - sqrt(3))/sqrt(2**3): -12, (sqrt(5) + 1)/4: S(10)/3, -(sqrt(5) + 1)/4: -S(10)/3 } if arg in cst_table: return S.Pi / cst_table[arg] i_coeff = arg.as_coefficient(S.ImaginaryUnit) if i_coeff is not None: return S.ImaginaryUnit * C.asinh(i_coeff) @staticmethod @cacheit def taylor_term(n, x, *previous_terms): if n < 0 or n % 2 == 0: return S.Zero else: x = sympify(x) if len(previous_terms) >= 2 and n > 2: p = previous_terms[-2] return p * (n - 2)**2/(n*(n - 1)) * x**2 else: k = (n - 1) // 2 R = C.RisingFactorial(S.Half, k) F = C.factorial(k) return R / F * x**n / n def _eval_as_leading_term(self, x): arg = self.args[0].as_leading_term(x) if x in arg.free_symbols and C.Order(1, x).contains(arg): return arg else: return self.func(arg) def _eval_rewrite_as_acos(self, x): return S.Pi/2 - acos(x) def _eval_rewrite_as_atan(self, x): return 2*atan(x/(1 + sqrt(1 - x**2))) def _eval_rewrite_as_log(self, x): return -S.ImaginaryUnit*C.log(S.ImaginaryUnit*x + sqrt(1 - x**2)) def _eval_is_real(self): return self.args[0].is_real and (self.args[0] >= -1 and self.args[0] <= 1)
[docs] def inverse(self, argindex=1): """ Returns the inverse of this function. """ return sin
def _sage_(self): import sage.all as sage return sage.asin(self.args[0]._sage_())
[docs]class acos(Function): """ acos(x) -> Returns the arc cosine of x (measured in radians) Notes ===== * acos(x) will evaluate automatically in the cases oo, -oo, 0, 1, -1 Examples ======== >>> from sympy import acos, oo, pi >>> acos(1) 0 >>> acos(0) pi/2 >>> acos(oo) oo*I See Also ======== asin, atan, cos """ nargs = 1 def fdiff(self, argindex=1): if argindex == 1: return -1/sqrt(1 - self.args[0]**2) else: raise ArgumentIndexError(self, argindex) def _eval_is_rational(self): s = self.func(*self.args) if s.func == self.func: if s.args[0].is_rational: return False else: return s.is_rational @classmethod def eval(cls, arg): if arg.is_Number: if arg is S.NaN: return S.NaN elif arg is S.Infinity: return S.Infinity * S.ImaginaryUnit elif arg is S.NegativeInfinity: return S.NegativeInfinity * S.ImaginaryUnit elif arg is S.Zero: return S.Pi / 2 elif arg is S.One: return S.Zero elif arg is S.NegativeOne: return S.Pi if arg.is_number: cst_table = { S.Half: S.Pi/3, -S.Half: 2*S.Pi/3, sqrt(2)/2: S.Pi/4, -sqrt(2)/2: 3*S.Pi/4, 1/sqrt(2): S.Pi/4, -1/sqrt(2): 3*S.Pi/4, sqrt(3)/2: S.Pi/6, -sqrt(3)/2: 5*S.Pi/6, } if arg in cst_table: return cst_table[arg] @staticmethod @cacheit def taylor_term(n, x, *previous_terms): if n == 0: return S.Pi / 2 elif n < 0 or n % 2 == 0: return S.Zero else: x = sympify(x) if len(previous_terms) >= 2 and n > 2: p = previous_terms[-2] return p * (n - 2)**2/(n*(n - 1)) * x**2 else: k = (n - 1) // 2 R = C.RisingFactorial(S.Half, k) F = C.factorial(k) return -R / F * x**n / n def _eval_as_leading_term(self, x): arg = self.args[0].as_leading_term(x) if x in arg.free_symbols and C.Order(1, x).contains(arg): return arg else: return self.func(arg) def _eval_is_real(self): return self.args[0].is_real and (self.args[0] >= -1 and self.args[0] <= 1) def _eval_rewrite_as_log(self, x): return S.Pi/2 + S.ImaginaryUnit * C.log(S.ImaginaryUnit * x + sqrt(1 - x**2)) def _eval_rewrite_as_asin(self, x): return S.Pi/2 - asin(x) def _eval_rewrite_as_atan(self, x): return atan(sqrt(1 - x**2)/x) + (S.Pi/2)*(1 - x*sqrt(1/x**2))
[docs] def inverse(self, argindex=1): """ Returns the inverse of this function. """ return cos
def _sage_(self): import sage.all as sage return sage.acos(self.args[0]._sage_())
[docs]class atan(Function): """ atan(x) -> Returns the arc tangent of x (measured in radians) Notes ===== * atan(x) will evaluate automatically in the cases oo, -oo, 0, 1, -1 Examples ======== >>> from sympy import atan, oo, pi >>> atan(0) 0 >>> atan(1) pi/4 >>> atan(oo) pi/2 See Also ======== acos, asin, tan """ nargs = 1 def fdiff(self, argindex=1): if argindex == 1: return 1/(1 + self.args[0]**2) else: raise ArgumentIndexError(self, argindex) def _eval_is_rational(self): s = self.func(*self.args) if s.func == self.func: if s.args[0].is_rational: return False else: return s.is_rational @classmethod def eval(cls, arg): if arg.is_Number: if arg is S.NaN: return S.NaN elif arg is S.Infinity: return S.Pi / 2 elif arg is S.NegativeInfinity: return -S.Pi / 2 elif arg is S.Zero: return S.Zero elif arg is S.One: return S.Pi / 4 elif arg is S.NegativeOne: return -S.Pi / 4 if arg.could_extract_minus_sign(): return -cls(-arg) if arg.is_number: cst_table = { sqrt(3)/3: 6, -sqrt(3)/3: -6, 1/sqrt(3): 6, -1/sqrt(3): -6, sqrt(3): 3, -sqrt(3): -3, (1 + sqrt(2)): S(8)/3, -(1 + sqrt(2)): S(8)/3, (sqrt(2) - 1): 8, (1 - sqrt(2)): -8, sqrt((5 + 2*sqrt(5))): S(5)/2, -sqrt((5 + 2*sqrt(5))): -S(5)/2, (2 - sqrt(3)): 12, -(2 - sqrt(3)): -12 } if arg in cst_table: return S.Pi / cst_table[arg] i_coeff = arg.as_coefficient(S.ImaginaryUnit) if i_coeff is not None: return S.ImaginaryUnit * C.atanh(i_coeff) @staticmethod @cacheit def taylor_term(n, x, *previous_terms): if n < 0 or n % 2 == 0: return S.Zero else: x = sympify(x) return (-1)**((n - 1)//2) * x**n / n def _eval_as_leading_term(self, x): arg = self.args[0].as_leading_term(x) if x in arg.free_symbols and C.Order(1, x).contains(arg): return arg else: return self.func(arg) def _eval_is_real(self): return self.args[0].is_real def _eval_rewrite_as_log(self, x): return S.ImaginaryUnit/2 * (C.log( (S(1) - S.ImaginaryUnit * x)/(S(1) + S.ImaginaryUnit * x))) def _eval_aseries(self, n, args0, x, logx): if args0[0] == S.Infinity: return S.Pi/2 - atan(1/self.args[0]) elif args0[0] == S.NegativeInfinity: return -S.Pi/2 - atan(1/self.args[0]) else: return super(atan, self)._eval_aseries(n, args0, x, logx)
[docs] def inverse(self, argindex=1): """ Returns the inverse of this function. """ return tan
def _sage_(self): import sage.all as sage return sage.atan(self.args[0]._sage_())
[docs]class acot(Function): """ acot(x) -> Returns the arc cotangent of x (measured in radians) """ nargs = 1 def fdiff(self, argindex=1): if argindex == 1: return -1 / (1 + self.args[0]**2) else: raise ArgumentIndexError(self, argindex) def _eval_is_rational(self): s = self.func(*self.args) if s.func == self.func: if s.args[0].is_rational: return False else: return s.is_rational @classmethod def eval(cls, arg): if arg.is_Number: if arg is S.NaN: return S.NaN elif arg is S.Infinity: return S.Zero elif arg is S.NegativeInfinity: return S.Zero elif arg is S.Zero: return S.Pi/ 2 elif arg is S.One: return S.Pi / 4 elif arg is S.NegativeOne: return -S.Pi / 4 if arg.could_extract_minus_sign(): return -cls(-arg) if arg.is_number: cst_table = { sqrt(3)/3: 3, -sqrt(3)/3: -3, 1/sqrt(3): 3, -1/sqrt(3): -3, sqrt(3): 6, -sqrt(3): -6, (1 + sqrt(2)): 8, -(1 + sqrt(2)): -8, (1 - sqrt(2)): -S(8)/3, (sqrt(2) - 1): S(8)/3, sqrt(5 + 2*sqrt(5)): 10, -sqrt(5 + 2*sqrt(5)): -10, (2 + sqrt(3)): 12, -(2 + sqrt(3)): -12, (2 - sqrt(3)): S(12)/5, -(2 - sqrt(3)): -S(12)/5, } if arg in cst_table: return S.Pi / cst_table[arg] i_coeff = arg.as_coefficient(S.ImaginaryUnit) if i_coeff is not None: return -S.ImaginaryUnit * C.acoth(i_coeff) @staticmethod @cacheit def taylor_term(n, x, *previous_terms): if n == 0: return S.Pi / 2 # FIX THIS elif n < 0 or n % 2 == 0: return S.Zero else: x = sympify(x) return (-1)**((n + 1)//2) * x**n / n def _eval_as_leading_term(self, x): arg = self.args[0].as_leading_term(x) if x in arg.free_symbols and C.Order(1, x).contains(arg): return arg else: return self.func(arg) def _eval_is_real(self): return self.args[0].is_real def _eval_aseries(self, n, args0, x, logx): if args0[0] == S.Infinity: return S.Pi/2 - acot(1/self.args[0]) elif args0[0] == S.NegativeInfinity: return 3*S.Pi/2 - acot(1/self.args[0]) else: return super(atan, self)._eval_aseries(n, args0, x, logx) def _eval_rewrite_as_log(self, x): return S.ImaginaryUnit/2 * \ (C.log((x - S.ImaginaryUnit)/(x + S.ImaginaryUnit)))
[docs] def inverse(self, argindex=1): """ Returns the inverse of this function. """ return cot
def _sage_(self): import sage.all as sage return sage.acot(self.args[0]._sage_())
[docs]class atan2(Function): r""" The function ``atan2(y, x)`` computes `\operatorname{atan}(y/x)` taking two arguments `y` and `x`. Signs of both `y` and `x` are considered to determine the appropriate quadrant of `\operatorname{atan}(y/x)`. The range is `(-\pi, \pi]`. The complete definition reads as follows: .. math:: \operatorname{atan2}(y, x) = \begin{cases} \arctan\left(\frac y x\right) & \qquad x > 0 \\ \arctan\left(\frac y x\right) + \pi& \qquad y \ge 0 , x < 0 \\ \arctan\left(\frac y x\right) - \pi& \qquad y < 0 , x < 0 \\ +\frac{\pi}{2} & \qquad y > 0 , x = 0 \\ -\frac{\pi}{2} & \qquad y < 0 , x = 0 \\ \text{undefined} & \qquad y = 0, x = 0 \end{cases} Attention: Note the role reversal of both arguments. The `y`-coordinate is the first argument and the `x`-coordinate the second. Examples ======== Going counter-clock wise around the origin we find the following angles: >>> from sympy import atan2 >>> atan2(0, 1) 0 >>> atan2(1, 1) pi/4 >>> atan2(1, 0) pi/2 >>> atan2(1, -1) 3*pi/4 >>> atan2(0, -1) pi >>> atan2(-1, -1) -3*pi/4 >>> atan2(-1, 0) -pi/2 >>> atan2(-1, 1) -pi/4 which are all correct. Compare this to the results of the ordinary `\operatorname{atan}` function for the point `(x, y) = (-1, 1)` >>> from sympy import atan, S >>> atan(S(1) / -1) -pi/4 >>> atan2(1, -1) 3*pi/4 where only the `\operatorname{atan2}` function reurns what we expect. We can differentiate the function with respect to both arguments: >>> from sympy import diff >>> from sympy.abc import x, y >>> diff(atan2(y, x), x) -y/(x**2 + y**2) >>> diff(atan2(y, x), y) x/(x**2 + y**2) We can express the `\operatorname{atan2}` function in terms of complex logarithms: >>> from sympy import log >>> atan2(y, x).rewrite(log) -I*log((x + I*y)/sqrt(x**2 + y**2)) and in terms of `\operatorname(atan)`: >>> from sympy import atan >>> atan2(y, x).rewrite(atan) 2*atan(y/(x + sqrt(x**2 + y**2))) but note that this form is undefined on the negative real axis. See Also ======== sin, cos, sec, csc, tan, cot asin, acos, atan References ========== .. [1] http://en.wikipedia.org/wiki/Atan2 .. [2] http://functions.wolfram.com/ElementaryFunctions/ArcTan2/ """ nargs = 2 @classmethod def eval(cls, y, x): if x is S.NegativeInfinity: if y.is_zero: # Special case y = 0 because we define Heaviside(0) = 1/2 return S.Pi return 2*S.Pi*(C.Heaviside(C.re(y))) - S.Pi elif x is S.Infinity: return S.Zero if x.is_real and y.is_real: if x.is_positive: return atan(y / x) elif x.is_negative: if y.is_negative: return atan(y / x) - S.Pi else: return atan(y / x) + S.Pi elif x.is_zero: if y.is_positive: return S.Pi/2 elif y.is_negative: return -S.Pi/2 elif y.is_zero: return S.NaN if y.is_zero and x.is_real and x.is_nonzero: return S.Pi * (S.One - C.Heaviside(x)) def _eval_rewrite_as_log(self, y, x): return -S.ImaginaryUnit*C.log((x + S.ImaginaryUnit*y) / sqrt(x**2 + y**2)) def _eval_rewrite_as_atan(self, y, x): return 2*atan(y / (sqrt(x**2 + y**2) + x)) def _eval_rewrite_as_arg(self, y, x): if (x.is_real or x.is_imaginary) and (y.is_real or y.is_imaginary): return C.arg(x + y*S.ImaginaryUnit) def _eval_is_real(self): return self.args[0].is_real and self.args[1].is_real def _eval_conjugate(self): return self.func(self.args[0].conjugate(), self.args[1].conjugate()) def fdiff(self, argindex): y, x = self.args if argindex == 1: # Diff wrt y return x/(x**2 + y**2) elif argindex == 2: # Diff wrt x return -y/(x**2 + y**2) else: raise ArgumentIndexError(self, argindex) def _eval_evalf(self, prec): y, x = self.args if x.is_real and y.is_real: super(atan2, self)._eval_evalf(prec) def _sage_(self): import sage.all as sage return sage.atan2(self.args[0]._sage_(), self.args[1]._sage_())