+ SETUP::
+
+ sage: from mjo.eja.eja_algebra import ComplexHermitianEJA
+
+ TESTS::
+
+ sage: set_random_seed()
+ sage: n = ZZ.random_element(1,5)
+ sage: field = QuadraticField(2, 'sqrt2')
+ sage: B = ComplexHermitianEJA._denormalized_basis(n, field)
+ sage: all( M.is_symmetric() for M in B)
+ True
+
+ """
+ R = PolynomialRing(field, 'z')
+ z = R.gen()
+ F = field.extension(z**2 + 1, 'I')
+ I = F.gen()
+
+ # This is like the symmetric case, but we need to be careful:
+ #
+ # * We want conjugate-symmetry, not just symmetry.
+ # * The diagonal will (as a result) be real.
+ #
+ S = []
+ for i in range(n):
+ for j in range(i+1):
+ Eij = matrix(F, n, lambda k,l: k==i and l==j)
+ if i == j:
+ Sij = cls.real_embed(Eij)
+ S.append(Sij)
+ else:
+ # The second one has a minus because it's conjugated.
+ Sij_real = cls.real_embed(Eij + Eij.transpose())
+ S.append(Sij_real)
+ Sij_imag = cls.real_embed(I*Eij - I*Eij.transpose())
+ S.append(Sij_imag)
+
+ # Since we embedded these, we can drop back to the "field" that we
+ # started with instead of the complex extension "F".
+ return ( s.change_ring(field) for s in S )
+
+
+ def __init__(self, n, field=AA, **kwargs):
+ basis = self._denormalized_basis(n,field)
+ super(ComplexHermitianEJA,self).__init__(field,
+ basis,
+ check=False,
+ **kwargs)
+ self.rank.set_cache(n)
+
+
+class QuaternionMatrixEuclideanJordanAlgebra(MatrixEuclideanJordanAlgebra):
+ @staticmethod
+ def real_embed(M):
+ """
+ Embed the n-by-n quaternion matrix ``M`` into the space of real
+ matrices of size 4n-by-4n by first sending each quaternion entry `z
+ = a + bi + cj + dk` to the block-complex matrix ``[[a + bi,
+ c+di],[-c + di, a-bi]]`, and then embedding those into a real
+ matrix.
+
+ SETUP::
+
+ sage: from mjo.eja.eja_algebra import \
+ ....: QuaternionMatrixEuclideanJordanAlgebra
+
+ EXAMPLES::
+
+ sage: Q = QuaternionAlgebra(QQ,-1,-1)
+ sage: i,j,k = Q.gens()
+ sage: x = 1 + 2*i + 3*j + 4*k
+ sage: M = matrix(Q, 1, [[x]])
+ sage: QuaternionMatrixEuclideanJordanAlgebra.real_embed(M)
+ [ 1 2 3 4]
+ [-2 1 -4 3]
+ [-3 4 1 -2]
+ [-4 -3 2 1]
+
+ Embedding is a homomorphism (isomorphism, in fact)::
+
+ sage: set_random_seed()
+ sage: n_max = QuaternionMatrixEuclideanJordanAlgebra._max_test_case_size()
+ sage: n = ZZ.random_element(n_max)
+ sage: Q = QuaternionAlgebra(QQ,-1,-1)
+ sage: X = random_matrix(Q, n)
+ sage: Y = random_matrix(Q, n)
+ sage: Xe = QuaternionMatrixEuclideanJordanAlgebra.real_embed(X)
+ sage: Ye = QuaternionMatrixEuclideanJordanAlgebra.real_embed(Y)
+ sage: XYe = QuaternionMatrixEuclideanJordanAlgebra.real_embed(X*Y)
+ sage: Xe*Ye == XYe
+ True
+
+ """
+ quaternions = M.base_ring()
+ n = M.nrows()
+ if M.ncols() != n:
+ raise ValueError("the matrix 'M' must be square")
+
+ F = QuadraticField(-1, 'I')
+ i = F.gen()
+
+ blocks = []
+ for z in M.list():
+ t = z.coefficient_tuple()
+ a = t[0]
+ b = t[1]
+ c = t[2]
+ d = t[3]
+ cplxM = matrix(F, 2, [[ a + b*i, c + d*i],
+ [-c + d*i, a - b*i]])
+ realM = ComplexMatrixEuclideanJordanAlgebra.real_embed(cplxM)
+ blocks.append(realM)
+
+ # We should have real entries by now, so use the realest field
+ # we've got for the return value.
+ return matrix.block(quaternions.base_ring(), n, blocks)
+
+
+
+ @staticmethod
+ def real_unembed(M):
+ """
+ The inverse of _embed_quaternion_matrix().
+
+ SETUP::
+
+ sage: from mjo.eja.eja_algebra import \
+ ....: QuaternionMatrixEuclideanJordanAlgebra
+
+ EXAMPLES::
+
+ sage: M = matrix(QQ, [[ 1, 2, 3, 4],
+ ....: [-2, 1, -4, 3],
+ ....: [-3, 4, 1, -2],
+ ....: [-4, -3, 2, 1]])
+ sage: QuaternionMatrixEuclideanJordanAlgebra.real_unembed(M)
+ [1 + 2*i + 3*j + 4*k]
+
+ TESTS:
+
+ Unembedding is the inverse of embedding::
+
+ sage: set_random_seed()
+ sage: Q = QuaternionAlgebra(QQ, -1, -1)
+ sage: M = random_matrix(Q, 3)
+ sage: Me = QuaternionMatrixEuclideanJordanAlgebra.real_embed(M)
+ sage: QuaternionMatrixEuclideanJordanAlgebra.real_unembed(Me) == M
+ True