From 51f63fc5d548ff299d1e93517bc104a96a23470a Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Tue, 3 Mar 2026 21:00:57 -0500 Subject: [PATCH] mjo/clan/jordan_spin_clan.py: first steps towards an implementation --- mjo/clan/jordan_spin_clan.py | 112 +++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 mjo/clan/jordan_spin_clan.py diff --git a/mjo/clan/jordan_spin_clan.py b/mjo/clan/jordan_spin_clan.py new file mode 100644 index 0000000..12282f9 --- /dev/null +++ b/mjo/clan/jordan_spin_clan.py @@ -0,0 +1,112 @@ +from mjo.clan.normal_decomposition import NormalDecomposition + +class JordanSpinClan(NormalDecomposition): + r""" + The clan associated with the T-algebra associated with the + Jordan spin (Euclidean Jordan) algebra. + + Named after :class:`mjo.eja.eja_algebra.JordanSpinEJA`. This could + be a :class:`mjo.clan.t_algebra_clan.TAlgebraClan`, but it isn't + implemented that way because the T-algebra is not readily + available (we'd have to built it, too). The T-algebra formulation + is described in *T-algebras and linear optimization over symmetric + cones* by Chek Beng Chua, but the formula for the diagonals is actually + wrong. It has to be (a*b)_11 = a11*b11 + and similarly + for (a*b)_22; otherwise, the identity isn't an identity. + + SETUP:: + + sage: from mjo.clan.jordan_spin_clan import JordanSpinClan + + TESTS: + + Verifying the axioms:: + + sage: n = ZZ.random_element(2,10) + sage: C = JordanSpinClan(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 + + """ + from sage.rings.rational_field import QQ + def __init__(self, n, scalar_field=QQ, **kwargs): + if n < 2: + raise ValueError("You want the real numbers?") + from sage.modules.free_module import VectorSpace + + # We need an Ishi basis (i,j,k) for R^N if we want to use + # NormalDecomposition. + indices = [(0,0,1)] + [(1,0,k) for k in range(1, n-1)] + [(1,1,1)] + RN = VectorSpace(scalar_field, indices) + + two = scalar_field(2) + + def cp(x,y): + # keep in mind, x and y will be (basis) elements of RN + x = x.to_vector() + x11 = x[0] + x_bar = x[1:-1] + x22 = x[-1] + y = y.to_vector() + y11 = y[0] + y_bar = y[1:-1] + y22 = y[-1] + + # z = x*y + V = x.parent() + z11 = x11*y11 + z21 = y_bar*(x11 + x22)/two + y11*x_bar + z22 = x22*y22 + x_bar.inner_product(y_bar) + z_coords = [z11] + list(z21) + [z22] + return RN.from_vector(V(z_coords)) + + def ip(x,y): + p = cp(x,y) / two + return p[(0,0,1)] + p[(1,1,1)] # sum of diagonals + + super().__init__(RN, cp, ip, **kwargs) + + + def __repr__(self) -> str: + r""" + The string representation of this clan. + + SETUP:: + + sage: from mjo.clan.jordan_spin_clan import JordanSpinClan + + EXAMPLES:: + + sage: JordanSpinClan(3) + Jordan spin clan of dimension 3 over Rational Field + + """ + return (f"Jordan spin clan of dimension {self.dimension()} " + f"over {self.base_ring()}") + -- 2.51.0