]> gitweb.michael.orlitzky.com - sage.d.git/commitdiff
mjo/clan: move matrix-clan methods to an intermediate class
authorMichael Orlitzky <michael@orlitzky.com>
Sat, 24 Jan 2026 16:16:27 +0000 (11:16 -0500)
committerMichael Orlitzky <michael@orlitzky.com>
Sat, 24 Jan 2026 16:16:27 +0000 (11:16 -0500)
mjo/clan/unital_clan.py

index 9079af86593663af540a069a936ab86ec0d75ba0..46ed222ace8b89be078f27e506e1ed02dfea6845 100644 (file)
@@ -183,7 +183,92 @@ class NormalDecomposition(UnitalClan):
         """
         return self._rank
 
-class SnClan(NormalDecomposition):
+class MatrixClan(NormalDecomposition):
+    r"""
+    A clan arising from a T-algebra of Hermitian matrices.
+    """
+    def from_matrix(self, x):
+        r"""
+        Construct an element of this clan from a Hermitian matrix.
+
+        EXAMPLES::
+
+            sage: C = SnClan(2)
+            sage: X = matrix(QQ, [[2,1],
+            ....:                 [1,4]])
+            sage: C.from_matrix(X).lift().lift()
+            [2 1]
+            [1 4]
+
+        ::
+
+            sage: from mjo.hurwitz import ComplexMatrixAlgebra
+            sage: C = HnClan(2)
+            sage: A = ComplexMatrixAlgebra(2, QQbar, QQ)
+            sage: X = A([ [ 2,       1 + 2*I],
+            ....:         [ 1 - 2*I,      -1] ])
+            sage: C.from_matrix(X).lift().lift()
+            ┌──────────┬─────────┐
+            │ 2        │ 2*I + 1 │
+            ├──────────┼─────────┤
+            │ -2*I + 1 │ -1      │
+            └──────────┴─────────┘
+
+        """
+        if not x.base_ring() == self.base_ring():
+            raise ValueError(f"base ring of matrix ({x.base_ring()})"
+                             " does not match clan ({self.base_ring()})")
+        if not x == x.conjugate_transpose():
+            raise ValueError("matrix is not Hermitian")
+        if not self.rank() == x.nrows():
+            raise ValueError(f"matrix must have {self.rank()} rows/columns")
+
+        try:
+            # HurwitzMatrixAlgebra
+            return self.sum_of_terms( (idx, x[idx])
+                                      for idx in self.indices() )
+        except IndexError:
+            # MatrixSpace
+            return self.sum_of_terms( ((i,j,k), x[i,j])
+                                      for (i,j,k) in self.indices() )
+
+
+
+
+    def from_list(self, l):
+        r"""
+        Construct an element of this clan from a list.
+
+        This is a shortcut for :meth:`from_matrix`, as the clan knows
+        what the ambient matrix space was.
+
+        EXAMPLES::
+
+            sage: C = HnClan(2)
+            sage: X = C.from_list([[0,I],[-I,0]])
+            sage: X.lift().lift()
+            ┌────┬───┐
+            │ 0  │ I │
+            ├────┼───┤
+            │ -I │ 0 │
+            └────┴───┘
+
+        This relies on the ambient vector space to convert a list to a
+        matrix, so in the real case, we can use one long list (as
+        opposed to a list of lists):
+
+            sage: C = SnClan(3)
+            sage: X = C.from_list([2,6,10,6,10,14,10,14,18])
+            sage: X.lift().lift()
+            [ 2  6 10]
+            [ 6 10 14]
+            [10 14 18]
+
+        """
+        return self.from_matrix(self._vector_space.ambient()(l))
+
+
+class SnClan(MatrixClan):
     r"""
     The normally-decomposed clan of real symmetric matrices of a
     given size with the clan product and lower-triangular basis
@@ -333,52 +418,7 @@ class SnClan(NormalDecomposition):
         return f"Clan S^{self.rank()} over {self.base_ring()}"
 
 
-    def from_matrix(self, x):
-        r"""
-        Construct an element of this clan from a symmetric matrix.
-
-        EXAMPLES::
-
-            sage: C = SnClan(2)
-            sage: X = matrix(QQ, [[2,1],
-            ....:                 [1,4]])
-            sage: C.from_matrix(X).lift().lift()
-            [2 1]
-            [1 4]
-
-        """
-        if not x.base_ring() == self.base_ring():
-            raise ValueError
-        if not x == x.transpose():
-            raise ValueError
-        if not self.rank() == x.nrows():
-            raise ValueError
-        return self.sum_of_terms( ((i,j,k), x[i,j])
-                                  for (i,j,k) in self.indices() )
-
-
-    def from_list(self, l):
-        r"""
-        Construct an element of this clan from a list.
-
-        This is a shortcut for :meth:`from_matrix`, as the clan knows
-        what the ambient matrix space was.
-
-        EXAMPLES::
-
-            sage: C = SnClan(3)
-            sage: X = C.from_list([2,6,10,6,10,14,10,14,18])
-            sage: X.lift().lift()
-            [ 2  6 10]
-            [ 6 10 14]
-            [10 14 18]
-
-        """
-        return self.from_matrix(self._vector_space.ambient()(l))
-
-
-
-class HnClan(NormalDecomposition):
+class HnClan(MatrixClan):
     r"""
     The normally-decomposed clan of complex Hermitian matrices of a
     given size with the clan product and lower-triangular basis
@@ -486,3 +526,16 @@ class HnClan(NormalDecomposition):
             return sum( p[i,i,one] for i in range(n) )
 
         super().__init__(Hn, cp, ip, **kwargs)
+
+
+    def __repr__(self) -> str:
+        r"""
+        The string representation of this clan.
+
+        EXAMPLES::
+
+            sage: HnClan(1)
+            Clan H^1 over Rational Field
+
+        """
+        return f"Clan H^{self.rank()} over {self.base_ring()}"