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
from mjo.clan.clan_element import ClanElement
"""
return self._mt[i][j]
+
+ def rank(self) -> int:
+ r"""
+ The rank of this clan. Not implemented by default,
+ because it is only easy to deduce from a normal decomposition.
+ """
+ raise NotImplementedError
+
+
+class SnClan(UnitalClan):
+ r"""
+ The 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.
+
+ EXAMPLES:
+
+ Verifying the axioms::
+
+ sage: n = 3
+ sage: C = SnClan(n)
+ sage: e = C.basis()
+ sage: all( e[i,i]*e[i,i] == e[i,i] for i in range(n) )
+ True
+ sage: all( e[i,i]*e[j,i] == e[j,i]/2
+ ....: for i in range(n)
+ ....: for j in range(i+1,n))
+ True
+ sage: all( e[i,i]*e[i,k] == e[i,k]/2
+ ....: for i in range(n)
+ ....: for k in range(i))
+ True
+ sage: all( (e[i,i]*e[j,k]).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]*e[i,i]).is_zero()
+ ....: for i in range(n)
+ ....: for k in range(i) )
+ True
+ sage: all( (e[j,k]*e[i,i]).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 = SnClan(3)
+ sage: x = C.an_element(); x
+ 2*B(0, 0) + 2*B(1, 0) + 3*B(1, 1)
+ sage: x.lift().lift()
+ [2 2 0]
+ [2 3 0]
+ [0 0 0]
+
+ """
+ def __init__(self, n, scalar_field=QQ, **kwargs):
+ self._rank = n
+
+ Mn = MatrixSpace(scalar_field, n)
+ b = Mn.basis()
+
+ from sage.sets.family import Family
+ Sn_basis = Family({ (i,j) :
+ 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)
+
+ 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.
+
+ """
+ def l(i,j):
+ if i < j:
+ return x[i,j]
+ if i == j:
+ return x[i,j]/scalar_field(2)
+ else:
+ return 0
+ return Mn(l)
+
+ 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 up_hat(x.T).T
+
+ 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.
+
+ """
+ x = x.lift()
+ y = y.lift()
+ return Sn(down_hat(x)*y + y*up_hat(x))
+
+ def ip(x,y):
+ p = cp(x,y) / scalar_field(2)
+ return sum( p[(i,i)] for i in range(n) )
+
+ super().__init__(Sn, cp, ip, **kwargs)
+
+
+ def rank(self) -> int:
+ return self._rank
+
+
+ def __repr__(self) -> str:
+ r"""
+ The string representation of this clan.
+
+ EXAMPLES::
+
+ sage: SnClan(5)
+ Clan S^5 over Rational Field
+
+ """
+ return f"Clan S^{self.rank()} over {self._vector_space().base_ring()}"