Repository: sympy/sympy

Issue:
complex log is not simplified
```
sympy.log should be able to symplify at least simple examples :

>>> sympy.log("sqrt(2)/2+sqrt(2)/2*I")
log(2**(1/2)/2 + I*2**(1/2)/2)

simplify does not help here :

>>> sympy.simplify(sympy.log("sqrt(2)/2+sqrt(2)/2*I"))
-log(4) + log(2*2**(1/2) + 2*I*2**(1/2))

I think sympy.arg could handle this :

>>> sympy.arg("sqrt(2)/2+sqrt(2)/2*I")
pi/4

What do you think about sympy.log(z) always returning sympy.log(abs(z)) +
sympy.arg(z)*I, if z is not real ?
```

Original issue for #4612: http://code.google.com/p/sympy/issues/detail?id=1513
Original author: https://code.google.com/u/117997262464115802198/
Original owner: https://code.google.com/u/117997262464115802198/


Discussion:

Patch:
diff --git a/sympy/functions/elementary/exponential.py b/sympy/functions/elementary/exponential.py
index 4ac1397a3b..32fd694088 100644
--- a/sympy/functions/elementary/exponential.py
+++ b/sympy/functions/elementary/exponential.py
@@ -4,7 +4,8 @@
 from sympy.core.add import Add
 from sympy.core.cache import cacheit
 from sympy.core.compatibility import range
-from sympy.core.function import Function, ArgumentIndexError, _coeff_isneg
+from sympy.core.function import (Function, ArgumentIndexError, _coeff_isneg,
+        expand_mul)
 from sympy.core.logic import fuzzy_not
 from sympy.core.mul import Mul
 from sympy.core.numbers import Integer
@@ -12,6 +13,7 @@
 from sympy.core.singleton import S
 from sympy.core.symbol import Wild, Dummy
 from sympy.functions.combinatorial.factorials import factorial
+from sympy.functions.elementary.miscellaneous import sqrt
 from sympy.ntheory import multiplicity, perfect_power
 
 # NOTE IMPORTANT
@@ -417,7 +419,7 @@ def _eval_is_extended_positive(self):
     def _eval_nseries(self, x, n, logx):
         # NOTE Please see the comment at the beginning of this file, labelled
         #      IMPORTANT.
-        from sympy import limit, oo, Order, powsimp
+        from sympy import limit, oo, Order, powsimp, Wild, expand_complex
         arg = self.args[0]
         arg_series = arg._eval_nseries(x, n=n, logx=logx)
         if arg_series.is_Order:
@@ -432,7 +434,12 @@ def _eval_nseries(self, x, n, logx):
         r = exp(arg0)*exp_series.subs(t, arg_series - arg0)
         r += Order(o.expr.subs(t, (arg_series - arg0)), x)
         r = r.expand()
-        return powsimp(r, deep=True, combine='exp')
+        r = powsimp(r, deep=True, combine='exp')
+        # powsimp may introduce unexpanded (-1)**Rational; see PR #17201
+        simplerat = lambda x: x.is_Rational and x.q in [3, 4, 6]
+        w = Wild('w', properties=[simplerat])
+        r = r.replace((-1)**w, expand_complex((-1)**w))
+        return r
 
     def _taylor(self, x, n):
         from sympy import Order
@@ -487,6 +494,7 @@ def _eval_rewrite_as_Pow(self, arg, **kwargs):
 class log(Function):
     r"""
     The natural logarithm function `\ln(x)` or `\log(x)`.
+
     Logarithms are taken with the natural base, `e`. To get
     a logarithm of a different base ``b``, use ``log(x, b)``,
     which is essentially short-hand for ``log(x)/log(b)``.
@@ -494,16 +502,19 @@ class log(Function):
     Examples
     ========
 
-    >>> from sympy import log, S
+    >>> from sympy import log, sqrt, S, I
     >>> log(8, 2)
     3
     >>> log(S(8)/3, 2)
     -log(3)/log(2) + 3
+    >>> log(-1 + I*sqrt(3))
+    log(2) + 2*I*pi/3
 
     See Also
     ========
 
     exp
+
     """
 
     def fdiff(self, argindex=1):
@@ -526,6 +537,7 @@ def eval(cls, arg, base=None):
         from sympy import unpolarify
         from sympy.calculus import AccumBounds
         from sympy.sets.setexpr import SetExpr
+        from sympy.functions.elementary.complexes import Abs, re, im
 
         arg = sympify(arg)
 
@@ -578,8 +590,9 @@ def eval(cls, arg, base=None):
             return arg._eval_func(cls)
 
         if arg.is_number:
+            I = S.ImaginaryUnit
             if arg.is_negative:
-                return S.Pi * S.ImaginaryUnit + cls(-arg)
+                return S.Pi * I + cls(-arg)
             elif arg is S.ComplexInfinity:
                 return S.ComplexInfinity
             elif arg is S.Exp1:
@@ -600,6 +613,63 @@ def eval(cls, arg, base=None):
                     else:
                         return -S.Pi * S.ImaginaryUnit * S.Half + cls(-coeff)
 
+        if arg.is_number and arg.is_algebraic:
+            I = S.ImaginaryUnit
+            # Match arg = coeff*(r_ + i_*I) with coeff>0, r_ and i_ real.
+            coeff, arg_ = arg.as_independent(I, as_Add=False)
+            if coeff.is_negative:
+                coeff *= -1
+                arg_ *= -1
+            arg_ = expand_mul(arg_, deep=False)
+            r_, i_ = arg_.as_independent(I, as_Add=True)
+            i_ = i_.as_coefficient(I)
+            if coeff.is_real and i_ and i_.is_real and r_.is_real:
+                if r_.is_zero:
+                    if i_.is_positive:
+                        return S.Pi * I * S.Half + cls(coeff * i_)
+                    elif i_.is_negative:
+                        return -S.Pi * I * S.Half + cls(coeff * -i_)
+                    elif i_.is_zero:
+                        return zoo
+                else:
+                    from sympy.simplify import ratsimp
+                    # Check for arguments involving rational multiples of pi
+                    t = (i_/r_).cancel()
+                    atan_table = {
+                        # first quadrant only
+                        sqrt(3): S.Pi/3,
+                        1: S.Pi/4,
+                        sqrt(5 - 2*sqrt(5)): S.Pi/5,
+                        sqrt(2)*sqrt(5 - sqrt(5))/(1 + sqrt(5)): S.Pi/5,
+                        sqrt(5 + 2*sqrt(5)): 2*S.Pi/5,
+                        sqrt(2)*sqrt(sqrt(5) + 5)/(-1 + sqrt(5)): 2*S.Pi/5,
+                        sqrt(3)/3: S.Pi/6,
+                        sqrt(2) - 1: S.Pi/8,
+                        sqrt(2 - sqrt(2))/sqrt(sqrt(2) + 2): S.Pi/8,
+                        sqrt(2) + 1: 3*S.Pi/8,
+                        sqrt(sqrt(2) + 2)/sqrt(2 - sqrt(2)): 3*S.Pi/8,
+                        sqrt(1 - 2*sqrt(5)/5): S.Pi/10,
+                        (-sqrt(2) + sqrt(10))/(2*sqrt(sqrt(5) + 5)): S.Pi/10,
+                        sqrt(1 + 2*sqrt(5)/5): 3*S.Pi/10,
+                        (sqrt(2) + sqrt(10))/(2*sqrt(5 - sqrt(5))): 3*S.Pi/10,
+                        2 - sqrt(3): S.Pi/12,
+                        (-1 + sqrt(3))/(1 + sqrt(3)): S.Pi/12,
+                        2 + sqrt(3): 5*S.Pi/12,
+                        (1 + sqrt(3))/(-1 + sqrt(3)): 5*S.Pi/12
+                    }
+                    if t in atan_table:
+                        modulus = ratsimp(coeff * Abs(arg_))
+                        if r_.is_positive:
+                            return cls(modulus) + I * atan_table[t]
+                        else:
+                            return cls(modulus) + I * (atan_table[t] - S.Pi)
+                    elif -t in atan_table:
+                        modulus = ratsimp(coeff * Abs(arg_))
+                        if r_.is_positive:
+                            return cls(modulus) + I * (-atan_table[-t])
+                        else:
+                            return cls(modulus) + I * (S.Pi - atan_table[-t])
+
     def as_base_exp(self):
         """
         Returns this function in the form (base, exponent).


Test patch:
diff --git a/sympy/functions/elementary/tests/test_exponential.py b/sympy/functions/elementary/tests/test_exponential.py
index 55ee1a4b5e..b1f094223d 100644
--- a/sympy/functions/elementary/tests/test_exponential.py
+++ b/sympy/functions/elementary/tests/test_exponential.py
@@ -1,8 +1,8 @@
 from sympy import (
     symbols, log, ln, Float, nan, oo, zoo, I, pi, E, exp, Symbol,
     LambertW, sqrt, Rational, expand_log, S, sign, conjugate, refine,
-    sin, cos, sinh, cosh, tanh, exp_polar, re, simplify,
-    AccumBounds, MatrixSymbol, Pow)
+    sin, cos, sinh, cosh, tanh, exp_polar, re, Function, simplify,
+    AccumBounds, MatrixSymbol, Pow, gcd)
 from sympy.abc import x, y, z
 from sympy.core.expr import unchanged
 from sympy.core.function import ArgumentIndexError
@@ -217,6 +217,48 @@ def test_log_values():
     assert log(2*3**2).func is log
 
 
+def test_log_exact():
+    # check for pi/2, pi/3, pi/4, pi/6, pi/8, pi/12; pi/5, pi/10:
+    for n in range(-23, 24):
+        if gcd(n, 24) != 1:
+            assert log(exp(n*I*pi/24).rewrite(sqrt)) == n*I*pi/24
+        for n in range(-9, 10):
+            assert log(exp(n*I*pi/10).rewrite(sqrt)) == n*I*pi/10
+
+    assert log(S.Half - I*sqrt(3)/2) == -I*pi/3
+    assert log(-S.Half + I*sqrt(3)/2) == I*2*pi/3
+    assert log(-sqrt(2)/2 - I*sqrt(2)/2) == -I*3*pi/4
+    assert log(-sqrt(3)/2 - I*S.Half) == -I*5*pi/6
+
+    assert log(-S(1)/4 + sqrt(5)/4 - I*sqrt(sqrt(5)/8 + S(5)/8)) == -I*2*pi/5
+    assert log(sqrt(S(5)/8 - sqrt(5)/8) + I*(S(1)/4 + sqrt(5)/4)) == I*3*pi/10
+    assert log(-sqrt(sqrt(2)/4 + S(1)/2) + I*sqrt(S(1)/2 - sqrt(2)/4)) == I*7*pi/8
+    assert log(-sqrt(6)/4 - sqrt(2)/4 + I*(-sqrt(6)/4 + sqrt(2)/4)) == -I*11*pi/12
+
+    assert log(-1 + I*sqrt(3)) == log(2) + I*2*pi/3
+    assert log(5 + 5*I) == log(5*sqrt(2)) + I*pi/4
+    assert log(sqrt(-12)) == log(2*sqrt(3)) + I*pi/2
+    assert log(-sqrt(6) + sqrt(2) - I*sqrt(6) - I*sqrt(2)) == log(4) - I*7*pi/12
+    assert log(-sqrt(6-3*sqrt(2)) - I*sqrt(6+3*sqrt(2))) == log(2*sqrt(3)) - 5*I*pi/8
+    assert log(1 + I*sqrt(2-sqrt(2))/sqrt(2+sqrt(2))) == log(2/sqrt(sqrt(2) + 2)) + I*pi/8
+    assert log(cos(7*pi/12) + I*sin(7*pi/12)) == 7*I*pi/12
+    assert log(cos(6*pi/5) + I*sin(6*pi/5)) == -4*I*pi/5
+
+    assert log(5*(1 + I)/sqrt(2)) == log(5) + I*pi/4
+    assert log(sqrt(2)*(-sqrt(3) + 1 - sqrt(3)*I - I)) == log(4) - I*7*pi/12
+    assert log(-sqrt(2)*(1 - I*sqrt(3))) == log(2*sqrt(2)) + 2*I*pi/3
+    assert log(sqrt(3)*I*(-sqrt(6 - 3*sqrt(2)) - I*sqrt(3*sqrt(2) + 6))) == log(6) - I*pi/8
+
+    zero = (1 + sqrt(2))**2 - 3 - 2*sqrt(2)
+    assert log(zero - I*sqrt(3)) == log(sqrt(3)) - I*pi/2
+    assert unchanged(log, zero + I*zero) or log(zero + zero*I) == zoo
+
+    # bail quickly if no obvious simplification is possible:
+    assert unchanged(log, (sqrt(2)-1/sqrt(sqrt(3)+I))**1000)
+    # beware of non-real coefficients
+    assert unchanged(log, sqrt(2-sqrt(5))*(1 + I))
+
+
 def test_log_base():
     assert log(1, 2) == 0
     assert log(2, 2) == 1
diff --git a/sympy/solvers/tests/test_solvers.py b/sympy/solvers/tests/test_solvers.py
index 7ffc5f335a..367e6d1654 100644
--- a/sympy/solvers/tests/test_solvers.py
+++ b/sympy/solvers/tests/test_solvers.py
@@ -1345,16 +1345,10 @@ def test_failing_assumptions():
 
 def test_issue_6056():
     assert solve(tanh(x + 3)*tanh(x - 3) - 1) == []
-    assert set([simplify(w) for w in solve(tanh(x - 1)*tanh(x + 1) + 1)]) == set([
-        -log(2)/2 + log(1 - I),
-        -log(2)/2 + log(-1 - I),
-        -log(2)/2 + log(1 + I),
-        -log(2)/2 + log(-1 + I),])
-    assert set([simplify(w) for w in solve((tanh(x + 3)*tanh(x - 3) + 1)**2)]) == set([
-        -log(2)/2 + log(1 - I),
-        -log(2)/2 + log(-1 - I),
-        -log(2)/2 + log(1 + I),
-        -log(2)/2 + log(-1 + I),])
+    assert solve(tanh(x - 1)*tanh(x + 1) + 1) == \
+            [-3*I*pi/4, -I*pi/4, I*pi/4, 3*I*pi/4]
+    assert solve((tanh(x + 3)*tanh(x - 3) + 1)**2) == \
+            [-3*I*pi/4, -I*pi/4, I*pi/4, 3*I*pi/4]
 
 
 def test_issue_5673():
@@ -1862,19 +1856,16 @@ def test_issue_8828():
 @slow
 def test_issue_2840_8155():
     assert solve(sin(3*x) + sin(6*x)) == [
-        0, -pi, pi, 14*pi/9, 16*pi/9, 2*pi, 2*I*(log(2) - log(-1 - sqrt(3)*I)),
-        2*I*(log(2) - log(-1 + sqrt(3)*I)), 2*I*(log(2) - log(1 - sqrt(3)*I)),
-        2*I*(log(2) - log(1 + sqrt(3)*I)), 2*I*(log(2) - log(-sqrt(3) - I)),
-        2*I*(log(2) - log(-sqrt(3) + I)), 2*I*(log(2) - log(sqrt(3) - I)),
-        2*I*(log(2) - log(sqrt(3) + I)), -2*I*log(-(-1)**(S(1)/9)), -2*I*log(
-        -(-1)**(S(2)/9)), -2*I*log(-sin(pi/18) - I*cos(pi/18)), -2*I*log(-sin(
-        pi/18) + I*cos(pi/18)), -2*I*log(sin(pi/18) - I*cos(pi/18)), -2*I*log(
-        sin(pi/18) + I*cos(pi/18)), -2*I*log(exp(-2*I*pi/9)), -2*I*log(exp(
-        -I*pi/9)), -2*I*log(exp(I*pi/9)), -2*I*log(exp(2*I*pi/9))]
+        0, -5*pi/3, -4*pi/3, -pi, -2*pi/3, -pi/3, pi/3, 2*pi/3, pi, 4*pi/3,
+        14*pi/9, 5*pi/3, 16*pi/9, 2*pi, -2*I*log(-(-1)**(S(1)/9)),
+        -2*I*log(-(-1)**(S(2)/9)), -2*I*log(-sin(pi/18) - I*cos(pi/18)),
+        -2*I*log(-sin(pi/18) + I*cos(pi/18)),
+        -2*I*log(sin(pi/18) - I*cos(pi/18)),
+        -2*I*log(sin(pi/18) + I*cos(pi/18)),
+        -2*I*log(exp(-2*I*pi/9)), -2*I*log(exp(-I*pi/9)),
+        -2*I*log(exp(I*pi/9)), -2*I*log(exp(2*I*pi/9))]
     assert solve(2*sin(x) - 2*sin(2*x)) == [
-        0, -pi, pi, 2*I*(log(2) - log(-sqrt(3) - I)), 2*I*(log(2) -
-        log(-sqrt(3) + I)), 2*I*(log(2) - log(sqrt(3) - I)), 2*I*(log(2) -
-        log(sqrt(3) + I))]
+        0, -5*pi/3, -pi, -pi/3, pi/3, pi, 5*pi/3]
 
 
 def test_issue_9567():