r"""
All user-facing imports from mjo.clan.
"""
-from mjo.clan.clan import (
- Clans,
- ComplexHermitianClan,
- RealSymmetricClan
-)
-
+from mjo.clan.clan import Clans
+from mjo.clan.t_algebra_clan import ( ComplexHermitianClan,
+ RealSymmetricClan )
from mjo.clan.vinberg_clan import VinbergClan
"""
from sage.categories.category_types import Category_over_base_ring
-from sage.categories.magmatic_algebras import MagmaticAlgebras
from sage.combinat.free_module import CombinatorialFreeModule
-from sage.modules.with_basis.subquotient import SubmoduleWithBasis
from sage.rings.rational_field import QQ
class Clans(Category_over_base_ring):
"""
def super_categories(self):
+ from sage.categories.magmatic_algebras import MagmaticAlgebras
R = self.base_ring()
return [MagmaticAlgebras(R).FiniteDimensional().WithBasis()]
class ParentMethods:
class MorphismMethods:
pass
+
class Clan(CombinatorialFreeModule):
r"""
Base class for clans.
category=category,
prefix=prefix,
bracket=bracket)
-
-
-
-class NormalDecomposition(UnitalClan):
- r"""
- A unital clan for which a normal decomposition is available.
- This is needed to compute the rank of the cone, and the clan trace
- as defined by Ishi.
- """
- from mjo.clan.normal_decomposition_element import (
- NormalDecompositionElement as Element
- )
-
- def __init__(self,
- vector_space,
- clan_product,
- inner_product,
- scalar_field=QQ,
- category=None,
- prefix=None,
- bracket=False):
- # The normal decomposition assumes that we already have a
- # lower-triangular (Ishi) basis of the form (i,j,k).
- self._rank = 1 + max( k[0]
- for k in vector_space.indices()
- if k[0] == k[1] )
-
- super().__init__(vector_space,
- clan_product,
- inner_product,
- scalar_field=scalar_field,
- category=None,
- prefix=None,
- bracket=False)
-
- def idempotent(self, i):
- r"""
- Return the i'th idempotent in this clan.
-
- SETUP::
-
- sage: from mjo.clan.clan import RealSymmetricClan
-
- EXAMPLES::
-
- sage: C = RealSymmetricClan(3)
- sage: C.idempotent(0)
- B(0, 0, 1)
- sage: C.idempotent(1)
- B(1, 1, 1)
- sage: C.idempotent(2)
- B(2, 2, 1)
-
- """
- return self.sum_of_monomials(
- (j,k,l)
- for (j,k,l) in self.indices()
- if j == i and k == i
- )
-
- def one(self):
- r"""
- Return the unit element of this clan.
-
- SETUP::
-
- sage: from mjo.clan.clan import RealSymmetricClan
-
- EXAMPLES::
-
- sage: C = RealSymmetricClan(4)
- sage: I = C.one()
- sage: I.lift().lift()
- [1 0 0 0]
- [0 1 0 0]
- [0 0 1 0]
- [0 0 0 1]
- sage: all( I*b == b and b*I == b for b in C.basis() )
- True
-
- """
- return self.sum_of_monomials(
- (i,j,k)
- for (i,j,k) in self.indices()
- if i == j
- )
-
- def rank(self) -> int:
- r"""
- The rank of this clan. Only implemented for normal
- decompositions because we don't know the rank until
- we know the (number of) idempotents used for the
- normal decomposition.
-
- SETUP::
-
- sage: from mjo.clan.clan import ComplexHermitianClan
-
- EXAMPLES::
-
- sage: C = ComplexHermitianClan(2)
- sage: C.rank()
- 2
-
- """
- return self._rank
-
-
-class MatrixClan(NormalDecomposition):
- r"""
- A clan arising from a T-algebra of Hermitian matrices.
- """
- @staticmethod
- def _up_hat(x):
- r"""
- The "up-hat" function on a T-algebra.
-
- This is the "top half" of a matrix, in the sense that we keep
- the strictly upper-triangular part, and keep half of the
- diagonal (so that ``up_hat(x) + down_hat(x) == x``. It is
- defined by Vinberg on page 381, and is used to construct a
- clan whose associated cone equals that of the the T-algebra.
- """
- one = x.base_ring().one()
- two = 2*one
- return x.parent().sum_of_terms(
- (idx, a*c)
- for (idx, c) in x.items()
- if idx[0] <= idx[1]
- if (a := one - (idx[0] == idx[1])/two)
- )
-
- @staticmethod
- def _down_hat(x):
- r"""
- The "down-hat" function on a T-algebra.
-
- This is the "lower half" of a matrix, in the sense that we
- keep the strictly lower-triangular part, and keep half of the
- diagonal (so that ``up_hat(x) + down_hat(x) == x``. It is
- defined by Vinberg on page 381, and is used to construct a
- clan whose associated cone equals that of the the T-algebra.
- """
- return MatrixClan._up_hat(x.transpose()).transpose()
-
- @staticmethod
- def _cp(x,y):
- r"""
- The clan product associated with a T-algebra.
-
- This is defined by Vinberg on page 381 (as Delta), who then
- proceeds to explain, over the course of the next few pages,
- why it results in a clan when the we take the underlying set
- to be the subspace of symmetric elements.
-
- .. WARNING:
-
- We use Ishi's version of this product that reverses
- the indices.
-
- """
- # First we lift x,y out of the clan and into
- # the general matrix space.
- H = x.parent()
- x = x.lift()
- y = y.lift()
-
- # Note: within a Hermitian matrix space we can't represent the
- # (non-Hermitian) intermediate terms _down_hat(x) and _up_hat(x)!
- return H(MatrixClan._down_hat(x)*y + y*MatrixClan._up_hat(x))
-
- @staticmethod
- def _ip(x,y):
- r"""
- The Ishi inner product (NOT the canonical one) on a clan.
- """
- two = x.base_ring()(2)
- p = MatrixClan._cp(x,y) / two
- return sum( p[idx]
- for idx in p.monomial_coefficients()
- if idx[0] == idx[1] )
-
- def from_matrix(self, x):
- r"""
- Construct an element of this clan from a Hermitian matrix.
-
- SETUP::
-
- sage: from mjo.clan.clan import ( ComplexHermitianClan,
- ....: RealSymmetricClan )
- sage: from mjo.hurwitz import ComplexMatrixAlgebra
-
- EXAMPLES::
-
- sage: C = RealSymmetricClan(2)
- sage: X = matrix(QQ, [[2,1],
- ....: [1,4]])
- sage: C.from_matrix(X).lift().lift()
- [2 1]
- [1 4]
-
- ::
-
- sage: C = ComplexHermitianClan(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.
-
- SETUP::
-
- sage: from mjo.clan.clan import ( ComplexHermitianClan,
- ....: RealSymmetricClan )
-
- EXAMPLES::
-
- sage: C = ComplexHermitianClan(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 = RealSymmetricClan(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 RealSymmetricClan(MatrixClan):
- r"""
- The normally-decomposed clan of real symmetric 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.
-
- SETUP::
-
- sage: from mjo.clan.clan import RealSymmetricClan
-
- EXAMPLES:
-
- The rank of this clan is the size of the matrices::
-
- sage: n = ZZ.random_element(1,5)
- sage: C = RealSymmetricClan(n)
- sage: C.rank() == n
- True
-
- Verifying the axioms::
-
- sage: n = 3
- sage: C = RealSymmetricClan(n)
- sage: e = C.basis()
- sage: all( e[i,i,1]*e[i,i,1] == e[i,i,1] for i in range(n) )
- True
- sage: all( e[i,i,1]*e[j,i,1] == e[j,i,1]/2
- ....: for i in range(n)
- ....: for j in range(i+1,n))
- True
- sage: all( e[i,i,1]*e[i,k,1] == e[i,k,1]/2
- ....: for i in range(n)
- ....: for k in range(i))
- True
- sage: all( (e[i,i,1]*e[j,k,1]).is_zero()
- ....: for i in range(n)
- ....: for j in range(n)
- ....: for k in range(j+1)
- ....: if not i in [j,k] )
- True
- sage: all( (e[i,k,1]*e[i,i,1]).is_zero()
- ....: for i in range(n)
- ....: for k in range(i) )
- True
- sage: all( (e[j,k,1]*e[i,i,1]).is_zero()
- ....: for i in range(n)
- ....: for j in range(n)
- ....: for k in range(j)
- ....: if not i in [j,k] )
- True
-
- With a little effort, we can lift elements of the clan back into
- the original (asymmetric) matrix space::
-
- sage: C = RealSymmetricClan(3)
- sage: x = C.an_element(); x
- 2*B(0, 0, 1) + 2*B(1, 0, 1) + 3*B(1, 1, 1)
- sage: x.lift().lift()
- [2 2 0]
- [2 3 0]
- [0 0 0]
-
- """
- def __init__(self, n, scalar_field=QQ, **kwargs):
- from sage.matrix.matrix_space import MatrixSpace
- Mn = MatrixSpace(scalar_field, n)
- b = Mn.basis()
-
- from sage.sets.family import Family
- Sn_basis = Family({ (i,j,1) :
- a*(b[(i,j)] + b[(j,i)])
- for i in range(n)
- for j in range(i+1)
- if (a := 1 - (i == j)/scalar_field(2))
- })
-
- # Mn.submodule() destroys our basis keys, so use
- # SubmoduleWithBasis directly.
- Sn = SubmoduleWithBasis(Sn_basis,
- support_order=b.keys(),
- ambient=Mn)
-
- super().__init__(Sn, MatrixClan._cp, MatrixClan._ip, **kwargs)
-
-
- def __repr__(self) -> str:
- r"""
- The string representation of this clan.
-
- SETUP::
-
- sage: from mjo.clan.clan import RealSymmetricClan
-
- EXAMPLES::
-
- sage: RealSymmetricClan(5)
- Clan S^5 over Rational Field
-
- """
- return f"Clan S^{self.rank()} over {self.base_ring()}"
-
-
-class ComplexHermitianClan(MatrixClan):
- 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.
-
- SETUP::
-
- sage: from mjo.clan.clan import ComplexHermitianClan
-
- EXAMPLES:
-
- The rank of this clan is the size of the matrices::
-
- sage: n = ZZ.random_element(1,5)
- sage: C = ComplexHermitianClan(n)
- sage: C.rank() == n
- True
-
- Verifying the axioms::
-
- sage: n = 3
- sage: C = ComplexHermitianClan(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 = ComplexHermitianClan(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)
-
- super().__init__(Hn, MatrixClan._cp, MatrixClan._ip, **kwargs)
-
-
- def __repr__(self) -> str:
- r"""
- The string representation of this clan.
-
- SETUP::
-
- sage: from mjo.clan.clan import ComplexHermitianClan
-
- EXAMPLES::
-
- sage: ComplexHermitianClan(1)
- Clan H^1 over Rational Field
-
- """
- return f"Clan H^{self.rank()} over {self.base_ring()}"
SETUP::
- sage: from mjo.clan.clan import RealSymmetricClan
+ sage: from mjo.clan.t_algebra_clan import RealSymmetricClan
EXAMPLES:
SETUP::
- sage: from mjo.clan.clan import RealSymmetricClan
+ sage: from mjo.clan.t_algebra_clan import RealSymmetricClan
EXAMPLES:
SETUP::
- sage: from mjo.clan.clan import RealSymmetricClan
sage: from mjo.clan.clan_operator import ClanOperator
+ sage: from mjo.clan.t_algebra_clan import RealSymmetricClan
EXAMPLES:
SETUP::
sage: from mjo.clan.clan_operator import ClanOperator
- sage: from mjo.clan.clan import ( ComplexHermitianClan,
- ....: RealSymmetricClan )
+ sage: from mjo.clan.t_algebra_clan import (
+ ....: ComplexHermitianClan,
+ ....: RealSymmetricClan
+ ....: )
sage: from mjo.clan.vinberg_clan import VinbergClan
EXAMPLES::
SETUP::
- sage: from mjo.clan.clan import ComplexHermitianClan
+ sage: from mjo.clan.t_algebra_clan import ComplexHermitianClan
sage: from mjo.clan.clan_operator import ClanOperator
EXAMPLES:
SETUP::
sage: from mjo.clan.clan_operator import ClanOperator
- sage: from mjo.clan.clan import ( ComplexHermitianClan,
- ....: RealSymmetricClan )
+ sage: from mjo.clan.t_algebra_clan import (
+ ....: ComplexHermitianClan,
+ ....: RealSymmetricClan
+ ....: )
EXAMPLES::
SETUP::
sage: from mjo.clan.clan_operator import ClanOperator
- sage: from mjo.clan.clan import ComplexHermitianClan
+ sage: from mjo.clan.t_algebra_clan import ComplexHermitianClan
EXAMPLES::
SETUP::
sage: from mjo.clan.clan_operator import ClanOperator
- sage: from mjo.clan.clan import RealSymmetricClan
+ sage: from mjo.clan.t_algebra_clan import RealSymmetricClan
EXAMPLES::
--- /dev/null
+from mjo.clan.clan import UnitalClan
+
+class NormalDecomposition(UnitalClan):
+ r"""
+ A unital clan for which a normal decomposition is available.
+ This is needed to compute the rank of the cone, and the clan trace
+ as defined by Ishi.
+ """
+ from mjo.clan.normal_decomposition_element import (
+ NormalDecompositionElement as Element
+ )
+
+ from sage.rings.rational_field import QQ
+ def __init__(self,
+ vector_space,
+ clan_product,
+ inner_product,
+ scalar_field=QQ,
+ category=None,
+ prefix=None,
+ bracket=False):
+ # The normal decomposition assumes that we already have a
+ # lower-triangular (Ishi) basis of the form (i,j,k).
+ self._rank = 1 + max( k[0]
+ for k in vector_space.indices()
+ if k[0] == k[1] )
+
+ super().__init__(vector_space,
+ clan_product,
+ inner_product,
+ scalar_field=scalar_field,
+ category=None,
+ prefix=None,
+ bracket=False)
+
+ def idempotent(self, i):
+ r"""
+ Return the i'th idempotent in this clan.
+
+ SETUP::
+
+ sage: from mjo.clan.t_algebra_clan import RealSymmetricClan
+
+ EXAMPLES::
+
+ sage: C = RealSymmetricClan(3)
+ sage: C.idempotent(0)
+ B(0, 0, 1)
+ sage: C.idempotent(1)
+ B(1, 1, 1)
+ sage: C.idempotent(2)
+ B(2, 2, 1)
+
+ """
+ return self.sum_of_monomials(
+ (j,k,l)
+ for (j,k,l) in self.indices()
+ if j == i and k == i
+ )
+
+ def one(self):
+ r"""
+ Return the unit element of this clan.
+
+ SETUP::
+
+ sage: from mjo.clan.t_algebra_clan import RealSymmetricClan
+
+ EXAMPLES::
+
+ sage: C = RealSymmetricClan(4)
+ sage: I = C.one()
+ sage: I.lift().lift()
+ [1 0 0 0]
+ [0 1 0 0]
+ [0 0 1 0]
+ [0 0 0 1]
+ sage: all( I*b == b and b*I == b for b in C.basis() )
+ True
+
+ """
+ return self.sum_of_monomials(
+ (i,j,k)
+ for (i,j,k) in self.indices()
+ if i == j
+ )
+
+ def rank(self) -> int:
+ r"""
+ The rank of this clan. Only implemented for normal
+ decompositions because we don't know the rank until
+ we know the (number of) idempotents used for the
+ normal decomposition.
+
+ SETUP::
+
+ sage: from mjo.clan.t_algebra_clan import ComplexHermitianClan
+
+ EXAMPLES::
+
+ sage: C = ComplexHermitianClan(2)
+ sage: C.rank()
+ 2
+
+ """
+ return self._rank
SETUP::
- sage: from mjo.clan.clan import RealSymmetricClan
+ sage: from mjo.clan.t_algebra_clan import RealSymmetricClan
EXAMPLES::
SETUP::
- sage: from mjo.clan.clan import RealSymmetricClan
+ sage: from mjo.clan.t_algebra_clan import RealSymmetricClan
EXAMPLES::
SETUP::
- sage: from mjo.clan.clan import RealSymmetricClan
+ sage: from mjo.clan.t_algebra_clan import RealSymmetricClan
EXAMPLES::
SETUP::
- sage: from mjo.clan.clan import RealSymmetricClan
+ sage: from mjo.clan.t_algebra_clan import RealSymmetricClan
EXAMPLES:
SETUP::
- sage: from mjo.clan.clan import RealSymmetricClan
+ sage: from mjo.clan.t_algebra_clan import RealSymmetricClan
sage: from mjo.clan.vinberg_clan import VinbergClan
EXAMPLES:
SETUP::
- sage: from mjo.clan.clan import RealSymmetricClan
+ sage: from mjo.clan.t_algebra_clan import RealSymmetricClan
EXAMPLES:
--- /dev/null
+from sage.rings.rational_field import QQ
+
+from mjo.clan.normal_decomposition import NormalDecomposition
+
+
+class TAlgebraClan(NormalDecomposition):
+ r"""
+ A clan arising from a T-algebra of Hermitian matrices.
+ """
+ @staticmethod
+ def _up_hat(x):
+ r"""
+ The "up-hat" function on a T-algebra.
+
+ This is the "top half" of a matrix, in the sense that we keep
+ the strictly upper-triangular part, and keep half of the
+ diagonal (so that ``up_hat(x) + down_hat(x) == x``. It is
+ defined by Vinberg on page 381, and is used to construct a
+ clan whose associated cone equals that of the the T-algebra.
+ """
+ one = x.base_ring().one()
+ two = 2*one
+ return x.parent().sum_of_terms(
+ (idx, a*c)
+ for (idx, c) in x.items()
+ if idx[0] <= idx[1]
+ if (a := one - (idx[0] == idx[1])/two)
+ )
+
+ @staticmethod
+ def _down_hat(x):
+ r"""
+ The "down-hat" function on a T-algebra.
+
+ This is the "lower half" of a matrix, in the sense that we
+ keep the strictly lower-triangular part, and keep half of the
+ diagonal (so that ``up_hat(x) + down_hat(x) == x``. It is
+ defined by Vinberg on page 381, and is used to construct a
+ clan whose associated cone equals that of the the T-algebra.
+ """
+ return TAlgebraClan._up_hat(x.transpose()).transpose()
+
+ @staticmethod
+ def _cp(x,y):
+ r"""
+ The clan product associated with a T-algebra.
+
+ This is defined by Vinberg on page 381 (as Delta), who then
+ proceeds to explain, over the course of the next few pages,
+ why it results in a clan when the we take the underlying set
+ to be the subspace of symmetric elements.
+
+ .. WARNING:
+
+ We use Ishi's version of this product that reverses
+ the indices.
+
+ """
+ # First we lift x,y out of the clan and into
+ # the general matrix space.
+ H = x.parent()
+ x = x.lift()
+ y = y.lift()
+
+ # Note: within a Hermitian matrix space we can't represent the
+ # (non-Hermitian) intermediate terms _down_hat(x) and _up_hat(x)!
+ return H(TAlgebraClan._down_hat(x)*y + y*TAlgebraClan._up_hat(x))
+
+ @staticmethod
+ def _ip(x,y):
+ r"""
+ The Ishi inner product (NOT the canonical one) on a clan.
+ """
+ two = x.base_ring()(2)
+ p = TAlgebraClan._cp(x,y) / two
+ return sum( p[idx]
+ for idx in p.monomial_coefficients()
+ if idx[0] == idx[1] )
+
+ def from_matrix(self, x):
+ r"""
+ Construct an element of this clan from a Hermitian matrix.
+
+ SETUP::
+
+ sage: from mjo.clan.t_algebra_clan import (
+ ....: ComplexHermitianClan,
+ ....: RealSymmetricClan
+ ....: )
+ sage: from mjo.hurwitz import ComplexMatrixAlgebra
+
+ EXAMPLES::
+
+ sage: C = RealSymmetricClan(2)
+ sage: X = matrix(QQ, [[2,1],
+ ....: [1,4]])
+ sage: C.from_matrix(X).lift().lift()
+ [2 1]
+ [1 4]
+
+ ::
+
+ sage: C = ComplexHermitianClan(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.
+
+ SETUP::
+
+ sage: from mjo.clan.t_algebra_clan import (
+ ....: ComplexHermitianClan,
+ ....: RealSymmetricClan
+ ....: )
+
+ EXAMPLES::
+
+ sage: C = ComplexHermitianClan(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 = RealSymmetricClan(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 RealSymmetricClan(TAlgebraClan):
+ r"""
+ The normally-decomposed clan of real symmetric 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.
+
+ SETUP::
+
+ sage: from mjo.clan.t_algebra_clan import RealSymmetricClan
+
+ EXAMPLES:
+
+ The rank of this clan is the size of the matrices::
+
+ sage: n = ZZ.random_element(1,5)
+ sage: C = RealSymmetricClan(n)
+ sage: C.rank() == n
+ True
+
+ Verifying the axioms::
+
+ sage: n = 3
+ sage: C = RealSymmetricClan(n)
+ sage: e = C.basis()
+ sage: all( e[i,i,1]*e[i,i,1] == e[i,i,1] for i in range(n) )
+ True
+ sage: all( e[i,i,1]*e[j,i,1] == e[j,i,1]/2
+ ....: for i in range(n)
+ ....: for j in range(i+1,n))
+ True
+ sage: all( e[i,i,1]*e[i,k,1] == e[i,k,1]/2
+ ....: for i in range(n)
+ ....: for k in range(i))
+ True
+ sage: all( (e[i,i,1]*e[j,k,1]).is_zero()
+ ....: for i in range(n)
+ ....: for j in range(n)
+ ....: for k in range(j+1)
+ ....: if not i in [j,k] )
+ True
+ sage: all( (e[i,k,1]*e[i,i,1]).is_zero()
+ ....: for i in range(n)
+ ....: for k in range(i) )
+ True
+ sage: all( (e[j,k,1]*e[i,i,1]).is_zero()
+ ....: for i in range(n)
+ ....: for j in range(n)
+ ....: for k in range(j)
+ ....: if not i in [j,k] )
+ True
+
+ With a little effort, we can lift elements of the clan back into
+ the original (asymmetric) matrix space::
+
+ sage: C = RealSymmetricClan(3)
+ sage: x = C.an_element(); x
+ 2*B(0, 0, 1) + 2*B(1, 0, 1) + 3*B(1, 1, 1)
+ sage: x.lift().lift()
+ [2 2 0]
+ [2 3 0]
+ [0 0 0]
+
+ """
+ def __init__(self, n, scalar_field=QQ, **kwargs):
+ from sage.matrix.matrix_space import MatrixSpace
+ Mn = MatrixSpace(scalar_field, n)
+ b = Mn.basis()
+
+ from sage.sets.family import Family
+ Sn_basis = Family({ (i,j,1) :
+ a*(b[(i,j)] + b[(j,i)])
+ for i in range(n)
+ for j in range(i+1)
+ if (a := 1 - (i == j)/scalar_field(2))
+ })
+
+ # Mn.submodule() destroys our basis keys, so use
+ # SubmoduleWithBasis directly.
+ from sage.modules.with_basis.subquotient import SubmoduleWithBasis
+ Sn = SubmoduleWithBasis(Sn_basis,
+ support_order=b.keys(),
+ ambient=Mn)
+
+ super().__init__(Sn, TAlgebraClan._cp, TAlgebraClan._ip, **kwargs)
+
+
+ def __repr__(self) -> str:
+ r"""
+ The string representation of this clan.
+
+ SETUP::
+
+ sage: from mjo.clan.t_algebra_clan import RealSymmetricClan
+
+ EXAMPLES::
+
+ sage: RealSymmetricClan(5)
+ Clan S^5 over Rational Field
+
+ """
+ return f"Clan S^{self.rank()} over {self.base_ring()}"
+
+
+class ComplexHermitianClan(TAlgebraClan):
+ 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.
+
+ SETUP::
+
+ sage: from mjo.clan.t_algebra_clan import ComplexHermitianClan
+
+ EXAMPLES:
+
+ The rank of this clan is the size of the matrices::
+
+ sage: n = ZZ.random_element(1,5)
+ sage: C = ComplexHermitianClan(n)
+ sage: C.rank() == n
+ True
+
+ Verifying the axioms::
+
+ sage: n = 3
+ sage: C = ComplexHermitianClan(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 = ComplexHermitianClan(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.
+ from sage.modules.with_basis.subquotient import SubmoduleWithBasis
+ Hn = SubmoduleWithBasis(Hn_basis,
+ support_order=b.keys(),
+ ambient=Mn)
+
+ super().__init__(Hn, TAlgebraClan._cp, TAlgebraClan._ip, **kwargs)
+
+
+ def __repr__(self) -> str:
+ r"""
+ The string representation of this clan.
+
+ SETUP::
+
+ sage: from mjo.clan.t_algebra_clan import ComplexHermitianClan
+
+ EXAMPLES::
+
+ sage: ComplexHermitianClan(1)
+ Clan H^1 over Rational Field
+
+ """
+ return f"Clan H^{self.rank()} over {self.base_ring()}"
from sage.rings.rational_field import QQ
-from mjo.clan.clan import MatrixClan, NormalDecomposition
+from mjo.clan.normal_decomposition import NormalDecomposition
class VinbergClan(NormalDecomposition):
r"""
# clan product rather than duplicating them locally.
self._vector_space = R5
+ # Let's borrow these up_hat() and down_hat() implementations
+ from mjo.clan.t_algebra_clan import TAlgebraClan
+
def cp(x,y):
# up_hat and down_hat need MatrixSpace elements, not
# submodules with custom bases, so we need to lift
X2 = X2.lift()
Y1 = Y1.lift()
Y2 = Y2.lift()
- Z1 = MatrixClan._down_hat(X1)*Y1 + Y1*MatrixClan._up_hat(X1)
- Z2 = MatrixClan._down_hat(X2)*Y2 + Y2*MatrixClan._up_hat(X2)
+ Z1 = TAlgebraClan._down_hat(X1)*Y1 + Y1*TAlgebraClan._up_hat(X1)
+ Z2 = TAlgebraClan._down_hat(X2)*Y2 + Y2*TAlgebraClan._up_hat(X2)
return self._unlift((self._S2(Z1), self._S2(Z2)))
def ip(x,y):