* :class:`RealSymmetricEJA`
* :class:`ComplexHermitianEJA`
* :class:`QuaternionHermitianEJA`
+ * :class:`OctonionHermitianEJA`
-Missing from this list is the algebra of three-by-three octononion
-Hermitian matrices, as there is (as of yet) no implementation of the
-octonions in SageMath. In addition to these, we provide two other
-example constructions,
+In addition to these, we provide two other example constructions,
+ * :class:`JordanSpinEJA`
* :class:`HadamardEJA`
+ * :class:`AlbertEJA`
* :class:`TrivialEJA`
The Jordan spin algebra is a bilinear form algebra where the bilinear
form is the identity. The Hadamard EJA is simply a Cartesian product
-of one-dimensional spin algebras. And last but not least, the trivial
-EJA is exactly what you think. Cartesian products of these are also
-supported using the usual ``cartesian_product()`` function; as a
-result, we support (up to isomorphism) all Euclidean Jordan algebras
-that don't involve octonions.
+of one-dimensional spin algebras. The Albert EJA is simply a special
+case of the :class:`OctonionHermitianEJA` where the matrices are
+three-by-three and the resulting space has dimension 27. And
+last/least, the trivial EJA is exactly what you think it is; it could
+also be obtained by constructing a dimension-zero instance of any of
+the other algebras. Cartesian products of these are also supported
+using the usual ``cartesian_product()`` function; as a result, we
+support (up to isomorphism) all Euclidean Jordan algebras.
SETUP::
Euclidean Jordan algebra of dimension...
"""
-from itertools import repeat
-
from sage.algebras.quatalg.quaternion_algebra import QuaternionAlgebra
from sage.categories.magmatic_algebras import MagmaticAlgebras
from sage.categories.sets_cat import cartesian_product
class RationalBasisEJA(FiniteDimensionalEJA):
r"""
- New class for algebras whose supplied basis elements have all rational entries.
+ Algebras whose supplied basis elements have all rational entries.
SETUP::
subs_dict = { X[i]: BX[i] for i in range(len(X)) }
return tuple( a_i.subs(subs_dict) for a_i in a )
-class ConcreteEJA(RationalBasisEJA):
+class ConcreteEJA(FiniteDimensionalEJA):
r"""
A class for the Euclidean Jordan algebras that we know by name.
def trace_inner_product(X,Y):
r"""
A trace inner-product for matrices that aren't embedded in the
- reals.
+ reals. It takes MATRICES as arguments, not EJA elements.
"""
- # We take the norm (absolute value) because Octonions() isn't
- # smart enough yet to coerce its one() into the base field.
- return (X*Y).trace().real().abs()
+ return (X*Y).trace().real()
class RealEmbeddedMatrixEJA(MatrixEJA):
@staticmethod
# as a REAL matrix will be 2*a = 2*Re(z_1). And so forth.
return (X*Y).trace()/cls.dimension_over_reals()
-class RealSymmetricEJA(ConcreteEJA, MatrixEJA):
+class RealSymmetricEJA(ConcreteEJA, RationalBasisEJA, MatrixEJA):
"""
The rank-n simple EJA consisting of real symmetric n-by-n
matrices, the usual symmetric Jordan product, and the trace inner
return matrix(F, n/d, elements)
-class ComplexHermitianEJA(ConcreteEJA, ComplexMatrixEJA):
+class ComplexHermitianEJA(ConcreteEJA, RationalBasisEJA, ComplexMatrixEJA):
"""
The rank-n simple EJA consisting of complex Hermitian n-by-n
matrices over the real numbers, the usual symmetric Jordan product,
return matrix(Q, n/d, elements)
-class QuaternionHermitianEJA(ConcreteEJA, QuaternionMatrixEJA):
+class QuaternionHermitianEJA(ConcreteEJA,
+ RationalBasisEJA,
+ QuaternionMatrixEJA):
r"""
The rank-n simple EJA consisting of self-adjoint n-by-n quaternion
matrices, the usual symmetric Jordan product, and the
n = ZZ.random_element(cls._max_random_instance_size() + 1)
return cls(n, **kwargs)
-class OctonionHermitianEJA(FiniteDimensionalEJA, MatrixEJA):
+class OctonionHermitianEJA(ConcreteEJA, MatrixEJA):
r"""
SETUP::
sage: J.rank.clear_cache() # long time
sage: J.rank() # long time
2
+
"""
+ @staticmethod
+ def _max_random_instance_size():
+ r"""
+ The maximum rank of a random QuaternionHermitianEJA.
+ """
+ return 1 # Dimension 1
+
+ @classmethod
+ def random_instance(cls, **kwargs):
+ """
+ Return a random instance of this type of algebra.
+ """
+ n = ZZ.random_element(cls._max_random_instance_size() + 1)
+ return cls(n, **kwargs)
+
def __init__(self, n, field=AA, **kwargs):
if n > 3:
# Otherwise we don't get an EJA.
return tuple( basis )
+ @staticmethod
+ def trace_inner_product(X,Y):
+ r"""
+ The octonions don't know that the reals are embedded in them,
+ so we have to take the e0 component ourselves.
+
+ SETUP::
+
+ sage: from mjo.eja.eja_algebra import OctonionHermitianEJA
+
+ TESTS::
+
+ sage: J = OctonionHermitianEJA(2,field=QQ,orthonormalize=False)
+ sage: I = J.one().to_matrix()
+ sage: J.trace_inner_product(I, -I)
+ -2
+
+ """
+ return (X*Y).trace().real().coefficient(0)
+
+
+class AlbertEJA(OctonionHermitianEJA):
+ r"""
+ The Albert algebra is the algebra of three-by-three Hermitian
+ matrices whose entries are octonions.
+
+ SETUP::
+
+ 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
-class HadamardEJA(ConcreteEJA):
+ """
+ def __init__(self, *args, **kwargs):
+ super().__init__(3, *args, **kwargs)
+
+
+class HadamardEJA(ConcreteEJA, RationalBasisEJA):
"""
Return the Euclidean Jordan Algebra corresponding to the set
`R^n` under the Hadamard product.
return cls(n, **kwargs)
-class BilinearFormEJA(ConcreteEJA):
+class BilinearFormEJA(ConcreteEJA, RationalBasisEJA):
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 =
return cls(n, **kwargs)
-class TrivialEJA(ConcreteEJA):
+class TrivialEJA(ConcreteEJA, RationalBasisEJA):
"""
The trivial Euclidean Jordan algebra consisting of only a zero element.
Return the space that our matrix basis lives in as a Cartesian
product.
+ We don't simply use the ``cartesian_product()`` functor here
+ because it acts differently on SageMath MatrixSpaces and our
+ custom MatrixAlgebras, which are CombinatorialFreeModules. We
+ always want the result to be represented (and indexed) as
+ an ordered tuple.
+
SETUP::
- sage: from mjo.eja.eja_algebra import (HadamardEJA,
+ sage: from mjo.eja.eja_algebra import (ComplexHermitianEJA,
+ ....: HadamardEJA,
+ ....: OctonionHermitianEJA,
....: RealSymmetricEJA)
EXAMPLES::
matrices over Algebraic Real Field, Full MatrixSpace of 2
by 2 dense matrices over Algebraic Real Field)
+ ::
+
+ sage: J1 = ComplexHermitianEJA(1)
+ sage: J2 = ComplexHermitianEJA(1)
+ sage: J = cartesian_product([J1,J2])
+ sage: J.one().to_matrix()[0]
+ [1 0]
+ [0 1]
+ sage: J.one().to_matrix()[1]
+ [1 0]
+ [0 1]
+
+ ::
+
+ sage: J1 = OctonionHermitianEJA(1)
+ sage: J2 = OctonionHermitianEJA(1)
+ sage: J = cartesian_product([J1,J2])
+ sage: J.one().to_matrix()[0]
+ +----+
+ | e0 |
+ +----+
+ sage: J.one().to_matrix()[1]
+ +----+
+ | e0 |
+ +----+
+
"""
- from sage.categories.cartesian_product import cartesian_product
- return cartesian_product( [J.matrix_space()
- for J in self.cartesian_factors()] )
+ scalars = self.cartesian_factor(0).base_ring()
+
+ # This category isn't perfect, but is good enough for what we
+ # need to do.
+ cat = MagmaticAlgebras(scalars).FiniteDimensional().WithBasis()
+ cat = cat.Unital().CartesianProducts()
+ factors = tuple( J.matrix_space() for J in self.cartesian_factors() )
+
+ from sage.sets.cartesian_product import CartesianProduct
+ return CartesianProduct(factors, cat)
+
@cached_method
def cartesian_projection(self, i):
SETUP::
- sage: from mjo.eja.eja_algebra import (JordanSpinEJA,
+ sage: from mjo.eja.eja_algebra import (HadamardEJA,
+ ....: JordanSpinEJA,
+ ....: OctonionHermitianEJA,
....: RealSymmetricEJA)
EXAMPLES:
sage: J.rank()
5
+ TESTS:
+
+ The ``cartesian_product()`` function only uses the first factor to
+ decide where the result will live; thus we have to be careful to
+ check that all factors do indeed have a `_rational_algebra` member
+ before we try to access it::
+
+ sage: J1 = OctonionHermitianEJA(1) # no rational basis
+ sage: J2 = HadamardEJA(2)
+ sage: cartesian_product([J1,J2])
+ Euclidean Jordan algebra of dimension 1 over Algebraic Real Field
+ (+) Euclidean Jordan algebra of dimension 2 over Algebraic Real Field
+ sage: cartesian_product([J2,J1])
+ Euclidean Jordan algebra of dimension 2 over Algebraic Real Field
+ (+) Euclidean Jordan algebra of dimension 1 over Algebraic Real Field
+
"""
def __init__(self, algebras, **kwargs):
CartesianProductEJA.__init__(self, algebras, **kwargs)
self._rational_algebra = None
if self.vector_space().base_field() is not QQ:
- self._rational_algebra = cartesian_product([
- r._rational_algebra for r in algebras
- ])
+ if all( hasattr(r, "_rational_algebra") for r in algebras ):
+ self._rational_algebra = cartesian_product([
+ r._rational_algebra for r in algebras
+ ])
RationalBasisEJA.CartesianProduct = RationalBasisCartesianProductEJA