+
+
+ @staticmethod
+ def _max_random_instance_size(max_dimension):
+ r"""
+ The maximum rank of a random QuaternionHermitianEJA.
+ """
+ # Obtained by solving d = 2n^2 - n.
+ return int(ZZ(8*max_dimension + 1).sqrt()/4 + 1/4)
+
+ @classmethod
+ def random_instance(cls, max_dimension=None, **kwargs):
+ """
+ Return a random instance of this type of algebra.
+ """
+ if max_dimension is None:
+ max_dimension = cls._max_random_instance_dimension()
+ n = ZZ.random_element(cls._max_random_instance_size(max_dimension) + 1)
+ return cls(n, **kwargs)
+
+class OctonionHermitianEJA(MatrixEJA, RationalBasisEJA, ConcreteEJA):
+ r"""
+ SETUP::
+
+ sage: from mjo.eja.eja_algebra import (FiniteDimensionalEJA,
+ ....: OctonionHermitianEJA)
+ sage: from mjo.hurwitz import Octonions, OctonionMatrixAlgebra
+
+ EXAMPLES:
+
+ The 3-by-3 algebra satisfies the axioms of an EJA::
+
+ sage: OctonionHermitianEJA(3, # long time
+ ....: field=QQ, # long time
+ ....: orthonormalize=False, # long time
+ ....: check_axioms=True) # long time
+ Euclidean Jordan algebra of dimension 27 over Rational Field
+
+ After a change-of-basis, the 2-by-2 algebra has the same
+ multiplication table as the ten-dimensional Jordan spin algebra::
+
+ sage: A = OctonionMatrixAlgebra(2,Octonions(QQ),QQ)
+ sage: b = OctonionHermitianEJA._denormalized_basis(A)
+ sage: basis = (b[0] + b[9],) + b[1:9] + (b[0] - b[9],)
+ sage: jp = OctonionHermitianEJA.jordan_product
+ sage: ip = OctonionHermitianEJA.trace_inner_product
+ sage: J = FiniteDimensionalEJA(basis,
+ ....: jp,
+ ....: ip,
+ ....: field=QQ,
+ ....: orthonormalize=False)
+ sage: J.multiplication_table()
+ +----++----+----+----+----+----+----+----+----+----+----+
+ | * || b0 | b1 | b2 | b3 | b4 | b5 | b6 | b7 | b8 | b9 |
+ +====++====+====+====+====+====+====+====+====+====+====+
+ | b0 || b0 | b1 | b2 | b3 | b4 | b5 | b6 | b7 | b8 | b9 |
+ +----++----+----+----+----+----+----+----+----+----+----+
+ | b1 || b1 | b0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+ +----++----+----+----+----+----+----+----+----+----+----+
+ | b2 || b2 | 0 | b0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+ +----++----+----+----+----+----+----+----+----+----+----+
+ | b3 || b3 | 0 | 0 | b0 | 0 | 0 | 0 | 0 | 0 | 0 |
+ +----++----+----+----+----+----+----+----+----+----+----+
+ | b4 || b4 | 0 | 0 | 0 | b0 | 0 | 0 | 0 | 0 | 0 |
+ +----++----+----+----+----+----+----+----+----+----+----+
+ | b5 || b5 | 0 | 0 | 0 | 0 | b0 | 0 | 0 | 0 | 0 |
+ +----++----+----+----+----+----+----+----+----+----+----+
+ | b6 || b6 | 0 | 0 | 0 | 0 | 0 | b0 | 0 | 0 | 0 |
+ +----++----+----+----+----+----+----+----+----+----+----+
+ | b7 || b7 | 0 | 0 | 0 | 0 | 0 | 0 | b0 | 0 | 0 |
+ +----++----+----+----+----+----+----+----+----+----+----+
+ | b8 || b8 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | b0 | 0 |
+ +----++----+----+----+----+----+----+----+----+----+----+
+ | b9 || b9 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | b0 |
+ +----++----+----+----+----+----+----+----+----+----+----+
+
+ TESTS:
+
+ We can actually construct the 27-dimensional Albert algebra,
+ and we get the right unit element if we recompute it::
+
+ sage: J = OctonionHermitianEJA(3, # long time
+ ....: field=QQ, # long time
+ ....: orthonormalize=False) # long time
+ sage: J.one.clear_cache() # long time
+ sage: J.one() # long time
+ b0 + b9 + b26
+ sage: J.one().to_matrix() # long time
+ +----+----+----+
+ | e0 | 0 | 0 |
+ +----+----+----+
+ | 0 | e0 | 0 |
+ +----+----+----+
+ | 0 | 0 | e0 |
+ +----+----+----+
+
+ The 2-by-2 algebra is isomorphic to the ten-dimensional Jordan
+ spin algebra, but just to be sure, we recompute its rank::
+
+ sage: J = OctonionHermitianEJA(2, # long time
+ ....: field=QQ, # long time
+ ....: orthonormalize=False) # long time
+ sage: J.rank.clear_cache() # long time
+ sage: J.rank() # long time
+ 2
+
+ """
+ @staticmethod
+ def _max_random_instance_size(max_dimension):
+ r"""
+ The maximum rank of a random QuaternionHermitianEJA.
+ """
+ # There's certainly a formula for this, but with only four
+ # cases to worry about, I'm not that motivated to derive it.
+ if max_dimension >= 27:
+ return 3
+ elif max_dimension >= 10:
+ return 2
+ elif max_dimension >= 1:
+ return 1
+ else:
+ return 0
+
+ @classmethod
+ def random_instance(cls, max_dimension=None, **kwargs):
+ """
+ Return a random instance of this type of algebra.
+ """
+ if max_dimension is None:
+ max_dimension = cls._max_random_instance_dimension()
+ n = ZZ.random_element(cls._max_random_instance_size(max_dimension) + 1)
+ return cls(n, **kwargs)
+
+ def __init__(self, n, field=AA, **kwargs):
+ if n > 3:
+ # Otherwise we don't get an EJA.
+ raise ValueError("n cannot exceed 3")
+
+ # We know this is a valid EJA, but will double-check
+ # if the user passes check_axioms=True.
+ if "check_axioms" not in kwargs: kwargs["check_axioms"] = False
+
+ from mjo.hurwitz import OctonionMatrixAlgebra
+ A = OctonionMatrixAlgebra(n, scalars=field)
+ super().__init__(A, **kwargs)
+
+ from mjo.eja.eja_cache import octonion_hermitian_eja_coeffs
+ a = octonion_hermitian_eja_coeffs(self)
+ if a is not None:
+ if self._rational_algebra is None:
+ self._charpoly_coefficients.set_cache(a)
+ else:
+ self._rational_algebra._charpoly_coefficients.set_cache(a)
+
+
+class AlbertEJA(OctonionHermitianEJA):
+ r"""
+ The Albert algebra is the algebra of three-by-three Hermitian
+ matrices whose entries are octonions.
+
+ SETUP::
+
+ sage: from mjo.eja.eja_algebra import AlbertEJA
+
+ EXAMPLES::
+
+ sage: AlbertEJA(field=QQ, orthonormalize=False)
+ Euclidean Jordan algebra of dimension 27 over Rational Field
+ sage: AlbertEJA() # long time
+ Euclidean Jordan algebra of dimension 27 over Algebraic Real Field
+
+ """
+ def __init__(self, *args, **kwargs):
+ super().__init__(3, *args, **kwargs)
+
+
+class HadamardEJA(RationalBasisEJA, ConcreteEJA):
+ """
+ Return the Euclidean Jordan algebra on `R^n` with the Hadamard
+ (pointwise real-number multiplication) Jordan product and the
+ usual inner-product.
+
+ This is nothing more than the Cartesian product of ``n`` copies of
+ the one-dimensional Jordan spin algebra, and is the most common
+ example of a non-simple Euclidean Jordan algebra.
+
+ SETUP::
+
+ sage: from mjo.eja.eja_algebra import HadamardEJA
+
+ EXAMPLES:
+
+ This multiplication table can be verified by hand::
+
+ sage: J = HadamardEJA(3)
+ sage: b0,b1,b2 = J.gens()
+ sage: b0*b0
+ b0
+ sage: b0*b1
+ 0
+ sage: b0*b2
+ 0
+ sage: b1*b1
+ b1
+ sage: b1*b2
+ 0
+ sage: b2*b2
+ b2
+
+ TESTS:
+
+ We can change the generator prefix::
+
+ sage: HadamardEJA(3, prefix='r').gens()
+ (r0, r1, r2)
+ """
+ def __init__(self, n, field=AA, **kwargs):
+ MS = MatrixSpace(field, n, 1)
+
+ if n == 0:
+ jordan_product = lambda x,y: x
+ inner_product = lambda x,y: x
+ else:
+ def jordan_product(x,y):
+ return MS( xi*yi for (xi,yi) in zip(x,y) )
+
+ def inner_product(x,y):
+ return (x.T*y)[0,0]
+
+ # New defaults for keyword arguments. Don't orthonormalize
+ # because our basis is already orthonormal with respect to our
+ # inner-product. Don't check the axioms, because we know this
+ # is a valid EJA... but do double-check if the user passes
+ # check_axioms=True. Note: we DON'T override the "check_field"
+ # default here, because the user can pass in a field!
+ if "orthonormalize" not in kwargs: kwargs["orthonormalize"] = False
+ if "check_axioms" not in kwargs: kwargs["check_axioms"] = False
+
+ column_basis = tuple( MS(b) for b in FreeModule(field, n).basis() )
+ super().__init__(column_basis,
+ jordan_product,
+ inner_product,
+ field=field,
+ matrix_space=MS,
+ associative=True,
+ **kwargs)
+ self.rank.set_cache(n)
+
+ self.one.set_cache( self.sum(self.gens()) )
+
+ @staticmethod
+ def _max_random_instance_dimension():
+ r"""
+ There's no reason to go higher than five here. That's
+ enough to get the point across.
+ """
+ return 5
+
+ @staticmethod
+ def _max_random_instance_size(max_dimension):
+ r"""
+ The maximum size (=dimension) of a random HadamardEJA.
+ """
+ return max_dimension
+
+ @classmethod
+ def random_instance(cls, max_dimension=None, **kwargs):
+ """
+ Return a random instance of this type of algebra.
+ """
+ if max_dimension is None:
+ max_dimension = cls._max_random_instance_dimension()
+ n = ZZ.random_element(cls._max_random_instance_size(max_dimension) + 1)
+ return cls(n, **kwargs)
+
+
+class BilinearFormEJA(RationalBasisEJA, ConcreteEJA):
+ r"""
+ The rank-2 simple EJA consisting of real vectors ``x=(x0, x_bar)``
+ with the half-trace inner product and jordan product ``x*y =
+ (<Bx,y>,y_bar>, x0*y_bar + y0*x_bar)`` where `B = 1 \times B22` is
+ a symmetric positive-definite "bilinear form" matrix. Its
+ dimension is the size of `B`, and it has rank two in dimensions
+ larger than two. It reduces to the ``JordanSpinEJA`` when `B` is
+ the identity matrix of order ``n``.
+
+ We insist that the one-by-one upper-left identity block of `B` be
+ passed in as well so that we can be passed a matrix of size zero
+ to construct a trivial algebra.
+
+ SETUP::
+
+ sage: from mjo.eja.eja_algebra import (BilinearFormEJA,
+ ....: JordanSpinEJA)
+
+ EXAMPLES:
+
+ When no bilinear form is specified, the identity matrix is used,
+ and the resulting algebra is the Jordan spin algebra::
+
+ sage: B = matrix.identity(AA,3)
+ sage: J0 = BilinearFormEJA(B)
+ sage: J1 = JordanSpinEJA(3)
+ sage: J0.multiplication_table() == J0.multiplication_table()