]> gitweb.michael.orlitzky.com - sage.d.git/commitdiff
mjo/clan: initial implementation of the Hermitian complex matrix clan
authorMichael Orlitzky <michael@orlitzky.com>
Sat, 24 Jan 2026 02:39:13 +0000 (21:39 -0500)
committerMichael Orlitzky <michael@orlitzky.com>
Sat, 24 Jan 2026 02:39:13 +0000 (21:39 -0500)
mjo/clan/unital_clan.py

index a38ea801751a2ac436fc68dbbb46b5a70cc14ff7..40fe94e5970d7f04051b8ffd9fbcf5ba4d55190e 100644 (file)
@@ -364,3 +364,114 @@ class SnClan(NormalDecomposition):
 
         """
         return self.from_matrix(self._vector_space.ambient()(l))
+
+
+
+class HnClan(NormalDecomposition):
+    r"""
+    The normally-decomposed clan of complex Hermitian matrices of a
+    given size with the clan product and lower-triangular basis
+    ordering of Ishi, based on the up-hat and down-hat products of
+    Vinberg.
+
+    EXAMPLES:
+
+    The rank of this clan is the size of the matrices::
+
+        sage: n = ZZ.random_element(1,5)
+        sage: C = HnClan(n)
+        sage: C.rank() == n
+        True
+
+    Verifying the axioms::
+
+        sage: n = 3
+        sage: C = HnClan(n)
+        sage: e = C.basis()
+        sage: r = C.rank()
+        sage: all( e[i,j,k]*e[i,j,k] == e[i,j,k]
+        ....:      for (i,j,k) in e.keys()
+        ....:      if i == j )
+        True
+        sage: all( C.idempotent(i)*e[j,i,k] == e[j,i,k]/2
+        ....:      for (i,j,k) in e.keys()
+        ....:      if i < j )
+        True
+        sage: all( C.idempotent(i)*e[i,k,z] == e[i,k,z]/2
+        ....:      for (i,k,z) in e.keys()
+        ....:      if k < i)
+        True
+        sage: all( (C.idempotent(i)*e[j,k,l]).is_zero()
+        ....:      for i in range(r)
+        ....:      for (j,k,l) in e.keys()
+        ....:      if k <= j and i not in [j,k] )
+        True
+        sage: all( (e[i,k,l]*C.idempotent(i)).is_zero()
+        ....:      for (i,k,l) in e.keys()
+        ....:      if k < i )
+        True
+        sage: all( (e[j,k,l]*C.idempotent(i)).is_zero()
+        ....:      for i in range(r)
+        ....:      for (j,k,l) in e.keys()
+        ....:      if i not in [j,k] )
+        True
+
+    With a little effort, we can lift elements of the clan back into
+    the original (asymmetric) matrix space::
+
+        sage: C = HnClan(3)
+        sage: x = C.an_element(); x
+        2*B(0, 0, 1) + 3*B(1, 0, I) + 2*B(1, 0, 1)
+        sage: x.lift().lift()
+        ┌─────────┬──────────┬───┐
+        │ 2       │ -3*I + 2 │ 0 │
+        ├─────────┼──────────┼───┤
+        │ 3*I + 2 │ 0        │ 0 │
+        ├─────────┼──────────┼───┤
+        │ 0       │ 0        │ 0 │
+        └─────────┴──────────┴───┘
+
+    """
+    def __init__(self, n, scalar_field=QQ, **kwargs):
+        from mjo.hurwitz import ComplexMatrixAlgebra
+        from sage.rings.qqbar import QQbar
+        Mn = ComplexMatrixAlgebra(n, QQbar, scalar_field)
+        b = Mn.basis()
+
+        from sage.sets.family import Family
+        Hn_basis = Family({
+          (i,j,k) : z
+          for (i,j,k) in b.keys()
+          if j <= i
+          if (a := 1 - (i == j)/scalar_field(2))
+          if (z := a*(b[i,j,k] + b[i,j,k].conjugate_transpose()))
+        })
+
+        # Mn.submodule() destroys our basis keys, so use
+        # SubmoduleWithBasis directly.
+        Hn = SubmoduleWithBasis(Hn_basis,
+                                support_order=b.keys(),
+                                ambient=Mn)
+
+        def up_hat(x):
+            return Mn.sum_of_terms(
+              ((i,j,k), a*c)
+              for ((i,j,k),c) in x.items()
+              if i <= j
+              if (a := 1 - (i == j)/scalar_field(2))
+            )
+
+        def down_hat(x):
+            return up_hat(x.transpose()).transpose()
+
+        def cp(x,y):
+            x = x.lift()
+            y = y.lift()
+            return Hn(down_hat(x)*y + y*up_hat(x))
+
+        def ip(x,y):
+            p = cp(x,y) / scalar_field(2)
+            one = QQbar(1)  # weird indexing in Hurwitz matrix algebras
+            return sum( p[i,i,one] for i in range(n) )
+
+        super().__init__(Hn, cp, ip, **kwargs)