From: Michael Orlitzky Date: Sat, 24 Jan 2026 02:39:13 +0000 (-0500) Subject: mjo/clan: initial implementation of the Hermitian complex matrix clan X-Git-Url: https://gitweb.michael.orlitzky.com/?a=commitdiff_plain;h=1e54c5d593f0bcb78205daca65207422321e504b;p=sage.d.git mjo/clan: initial implementation of the Hermitian complex matrix clan --- diff --git a/mjo/clan/unital_clan.py b/mjo/clan/unital_clan.py index a38ea80..40fe94e 100644 --- a/mjo/clan/unital_clan.py +++ b/mjo/clan/unital_clan.py @@ -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)