Repository: sympy/sympy

Issue:
pretty printing: `Trace` could be improved (and LaTeX)
````
In [69]: X = ImmutableMatrix([[1, 2], [3, 4]])

In [70]: pprint(X)
⎡1  2⎤
⎢    ⎥
⎣3  4⎦

In [71]: w = Trace(X) + Trace(2*X)

In [72]: pprint(w)
Trace(Matrix([ + Trace(Matrix([
   [1, 2],          [2, 4],    
  [3, 4]]))        [6, 8]]))   
````

I suppose `pprint(Trace(X))` should be:
````
  ⎛⎡1  2⎤⎞ 
tr⎜⎢    ⎥⎟ 
  ⎝⎣3  4⎦⎠ 
````

Discussion:

Patch:
diff --git a/sympy/combinatorics/perm_groups.py b/sympy/combinatorics/perm_groups.py
index b1565fd52f..720abd62d3 100644
--- a/sympy/combinatorics/perm_groups.py
+++ b/sympy/combinatorics/perm_groups.py
@@ -123,10 +123,10 @@ def __new__(cls, *args, **kwargs):
         """The default constructor. Accepts Cycle and Permutation forms.
         Removes duplicates unless ``dups`` keyword is False.
         """
+        args = list(args[0] if is_sequence(args[0]) else args)
         if not args:
-            args = [Permutation()]
-        else:
-            args = list(args[0] if is_sequence(args[0]) else args)
+            raise ValueError('must supply one or more permutations '
+            'to define the group')
         if any(isinstance(a, Cycle) for a in args):
             args = [Permutation(a) for a in args]
         if has_variety(a.size for a in args):
@@ -1568,7 +1568,7 @@ def is_nilpotent(self):
         else:
             return self._is_nilpotent
 
-    def is_normal(self, gr, strict=True):
+    def is_normal(self, gr):
         """Test if G=self is a normal subgroup of gr.
 
         G is normal in gr if
@@ -1590,20 +1590,12 @@ def is_normal(self, gr, strict=True):
         True
 
         """
-        d_self = self.degree
-        d_gr = gr.degree
-        new_self = self.copy()
-        if not strict and d_self != d_gr:
-            if d_self < d_gr:
-                new_self = PermGroup(new_self.generators + [Permutation(d_gr - 1)])
-            else:
-                gr = PermGroup(gr.generators + [Permutation(d_self - 1)])
-        gens2 = [p._array_form for p in new_self.generators]
+        gens2 = [p._array_form for p in self.generators]
         gens1 = [p._array_form for p in gr.generators]
         for g1 in gens1:
             for g2 in gens2:
                 p = _af_rmuln(g1, g2, _af_invert(g1))
-                if not new_self.coset_factor(p, True):
+                if not self.coset_factor(p, True):
                     return False
         return True
 
diff --git a/sympy/combinatorics/permutations.py b/sympy/combinatorics/permutations.py
index cf68727df8..7e7a052247 100644
--- a/sympy/combinatorics/permutations.py
+++ b/sympy/combinatorics/permutations.py
@@ -235,7 +235,7 @@ class Cycle(dict):
     Wrapper around dict which provides the functionality of a disjoint cycle.
 
     A cycle shows the rule to use to move subsets of elements to obtain
-    a permutation. The Cycle class is more flexible than Permutation in
+    a permutation. The Cycle class is more flexible that Permutation in
     that 1) all elements need not be present in order to investigate how
     multiple cycles act in sequence and 2) it can contain singletons:
 
diff --git a/sympy/printing/pretty/pretty.py b/sympy/printing/pretty/pretty.py
index 034c977ef6..32b1592010 100644
--- a/sympy/printing/pretty/pretty.py
+++ b/sympy/printing/pretty/pretty.py
@@ -664,6 +664,13 @@ def _print_MatrixBase(self, e):
     _print_ImmutableMatrix = _print_MatrixBase
     _print_Matrix = _print_MatrixBase
 
+    def _print_Trace(self, e):
+        D = self._print(e.arg)
+        D = prettyForm(*D.parens('(',')'))
+        D.baseline = D.height()//2
+        D = prettyForm(*D.left('\n'*(0) + 'tr'))
+        return D
+
 
     def _print_MatrixElement(self, expr):
         from sympy.matrices import MatrixSymbol


Test patch:
diff --git a/sympy/combinatorics/tests/test_perm_groups.py b/sympy/combinatorics/tests/test_perm_groups.py
index 3f45083ba7..d8f7118cda 100644
--- a/sympy/combinatorics/tests/test_perm_groups.py
+++ b/sympy/combinatorics/tests/test_perm_groups.py
@@ -60,7 +60,6 @@ def test_order():
     b = Permutation([2, 1, 3, 4, 5, 6, 7, 8, 9, 0])
     g = PermutationGroup([a, b])
     assert g.order() == 1814400
-    assert PermutationGroup().order() == 1
 
 
 def test_equality():
@@ -274,10 +273,6 @@ def test_is_normal():
     assert G1.is_subgroup(G6)
     assert not G1.is_subgroup(G4)
     assert G2.is_subgroup(G4)
-    s4 = PermutationGroup(Permutation(0, 1, 2, 3), Permutation(3)(0, 1))
-    s6 = PermutationGroup(Permutation(0, 1, 2, 3, 5), Permutation(5)(0, 1))
-    assert s6.is_normal(s4, strict=False)
-    assert not s4.is_normal(s6, strict=False)
 
 
 def test_eq():
@@ -722,7 +717,3 @@ def test_elements():
 def test_is_group():
     assert PermutationGroup(Permutation(1,2), Permutation(2,4)).is_group == True
     assert SymmetricGroup(4).is_group == True
-
-
-def test_PermutationGroup():
-    assert PermutationGroup() == PermutationGroup(Permutation())
diff --git a/sympy/printing/pretty/tests/test_pretty.py b/sympy/printing/pretty/tests/test_pretty.py
index 0724493609..25a0e30c75 100644
--- a/sympy/printing/pretty/tests/test_pretty.py
+++ b/sympy/printing/pretty/tests/test_pretty.py
@@ -4,7 +4,7 @@
     FiniteSet, Function, Ge, Gt, I, Implies, Integral,
     Lambda, Le, Limit, Lt, Matrix, Mul, Nand, Ne, Nor, Not, O, Or,
     Pow, Product, QQ, RR, Rational, Ray, rootof, RootSum, S,
-    Segment, Subs, Sum, Symbol, Tuple, Xor, ZZ, conjugate,
+    Segment, Subs, Sum, Symbol, Tuple, Trace, Xor, ZZ, conjugate,
     groebner, oo, pi, symbols, ilex, grlex, Range, Contains,
     SeqPer, SeqFormula, SeqAdd, SeqMul, Interval, Union, fourier_series, fps,
     Complement, FiniteSet, Interval, Intersection, Union)
@@ -2627,6 +2627,39 @@ def test_Adjoint():
     assert upretty(Transpose(Adjoint(X))) == \
         u("    T\n⎛ †⎞ \n⎝X ⎠ ")
 
+def test_pretty_Trace_issue_9044():
+    X = Matrix([[1, 2], [3, 4]])
+    Y = Matrix([[2, 4], [6, 8]])
+    ascii_str_1 = \
+"""\
+  /[1  2]\\
+tr|[    ]|
+  \[3  4]/\
+"""
+    ucode_str_1 = \
+u("""\
+  ⎛⎡1  2⎤⎞
+tr⎜⎢    ⎥⎟
+  ⎝⎣3  4⎦⎠\
+""")
+    ascii_str_2 = \
+"""\
+  /[1  2]\     /[2  4]\\
+tr|[    ]| + tr|[    ]|
+  \[3  4]/     \[6  8]/\
+"""
+    ucode_str_2 = \
+u("""\
+  ⎛⎡1  2⎤⎞     ⎛⎡2  4⎤⎞
+tr⎜⎢    ⎥⎟ + tr⎜⎢    ⎥⎟
+  ⎝⎣3  4⎦⎠     ⎝⎣6  8⎦⎠\
+""")
+    assert pretty(Trace(X)) == ascii_str_1
+    assert upretty(Trace(X)) == ucode_str_1
+
+    assert pretty(Trace(X) + Trace(Y)) == ascii_str_2
+    assert upretty(Trace(X) + Trace(Y)) == ucode_str_2
+
 
 def test_pretty_piecewise():
     expr = Piecewise((x, x < 1), (x**2, True))