--- /dev/null
+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 + <a12,b21> 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()}")
+