"""
SETUP::
- sage: from mjo.eja.eja_algebra import (JordanSpinEJA, random_eja)
+ sage: from mjo.eja.eja_algebra import (
+ ....: FiniteDimensionalEuclideanJordanAlgebra,
+ ....: JordanSpinEJA,
+ ....: random_eja)
EXAMPLES:
TESTS:
- The ``field`` we're given must be real::
+ The ``field`` we're given must be real with ``check=True``::
sage: JordanSpinEJA(2,QQbar)
Traceback (most recent call last):
...
ValueError: field is not real
+ The multiplication table must be square with ``check=True``::
+
+ sage: FiniteDimensionalEuclideanJordanAlgebra(QQ,((),()))
+ Traceback (most recent call last):
+ ...
+ ValueError: multiplication table is not square
+
"""
if check:
if not field.is_subring(RR):
category = MagmaticAlgebras(field).FiniteDimensional()
category = category.WithBasis().Unital()
+ # The multiplication table had better be square
+ n = len(mult_table)
+ if check:
+ if not all( len(l) == n for l in mult_table ):
+ raise ValueError("multiplication table is not square")
+
fda = super(FiniteDimensionalEuclideanJordanAlgebra, self)
fda.__init__(field,
- range(len(mult_table)),
+ range(n),
prefix=prefix,
category=category)
self.print_options(bracket='')
for ls in mult_table
]
+ if check:
+ if not self._is_commutative():
+ raise ValueError("algebra is not commutative")
+ if not self._is_jordanian():
+ raise ValueError("Jordan identity does not hold")
+ if not self._inner_product_is_associative():
+ raise ValueError("inner product is not associative")
def _element_constructor_(self, elt):
"""
def product_on_basis(self, i, j):
return self._multiplication_table[i][j]
+ def _is_commutative(self):
+ r"""
+ Whether or not this algebra's multiplication table is commutative.
+
+ This method should of course always return ``True``, unless
+ this algebra was constructed with ``check=False`` and passed
+ an invalid multiplication table.
+ """
+ return all( self.product_on_basis(i,j) == self.product_on_basis(i,j)
+ for i in range(self.dimension())
+ for j in range(self.dimension()) )
+
+ def _is_jordanian(self):
+ r"""
+ Whether or not this algebra's multiplication table respects the
+ Jordan identity `(x^{2})(xy) = x(x^{2}y)`.
+
+ We only check one arrangement of `x` and `y`, so for a
+ ``True`` result to be truly true, you should also check
+ :meth:`_is_commutative`. This method should of course always
+ return ``True``, unless this algebra was constructed with
+ ``check=False`` and passed an invalid multiplication table.
+ """
+ return all( (self.monomial(i)**2)*(self.monomial(i)*self.monomial(j))
+ ==
+ (self.monomial(i))*((self.monomial(i)**2)*self.monomial(j))
+ for i in range(self.dimension())
+ for j in range(self.dimension()) )
+
+ def _inner_product_is_associative(self):
+ r"""
+ Return whether or not this algebra's inner product `B` is
+ associative; that is, whether or not `B(xy,z) = B(x,yz)`.
+
+ This method should of course always return ``True``, unless
+ this algebra was constructed with ``check=False`` and passed
+ an invalid multiplication table.
+ """
+
+ # Used to check whether or not something is zero in an inexact
+ # ring. This number is sufficient to allow the construction of
+ # QuaternionHermitianEJA(2, RDF) with check=True.
+ epsilon = 1e-16
+
+ for i in range(self.dimension()):
+ for j in range(self.dimension()):
+ for k in range(self.dimension()):
+ x = self.monomial(i)
+ y = self.monomial(j)
+ z = self.monomial(k)
+ diff = (x*y).inner_product(z) - x.inner_product(y*z)
+
+ if self.base_ring().is_exact():
+ if diff != 0:
+ return False
+ else:
+ if diff.abs() > epsilon:
+ return False
+
+ return True
+
@cached_method
def characteristic_polynomial_of(self):
"""
# Do this over the rationals and convert back at the end.
# Only works because we know the entries of the basis are
- # integers.
+ # integers. The argument ``check=False`` is required
+ # because the trace inner-product method for this
+ # class is a stub and can't actually be checked.
J = MatrixEuclideanJordanAlgebra(QQ,
basis,
- normalize_basis=False)
+ normalize_basis=False,
+ check=False)
a = J._charpoly_coefficients()
# Unfortunately, changing the basis does change the
# largest subalgebra generated by any element.
fdeja.__init__(field, mult_table, **kwargs)
self.rank.set_cache(0)
+
+
+class DirectSumEJA(FiniteDimensionalEuclideanJordanAlgebra):
+ r"""
+ The external (orthogonal) direct sum of two other Euclidean Jordan
+ algebras. Essentially the Cartesian product of its two factors.
+ Every Euclidean Jordan algebra decomposes into an orthogonal
+ direct sum of simple Euclidean Jordan algebras, so no generality
+ is lost by providing only this construction.
+
+ SETUP::
+
+ sage: from mjo.eja.eja_algebra import (HadamardEJA,
+ ....: RealSymmetricEJA,
+ ....: DirectSumEJA)
+
+ EXAMPLES::
+
+ sage: J1 = HadamardEJA(2)
+ sage: J2 = RealSymmetricEJA(3)
+ sage: J = DirectSumEJA(J1,J2)
+ sage: J.dimension()
+ 8
+ sage: J.rank()
+ 5
+
+ """
+ def __init__(self, J1, J2, field=AA, **kwargs):
+ n1 = J1.dimension()
+ n2 = J2.dimension()
+ n = n1+n2
+ V = VectorSpace(field, n)
+ mult_table = [ [ V.zero() for j in range(n) ]
+ for i in range(n) ]
+ for i in range(n1):
+ for j in range(n1):
+ p = (J1.monomial(i)*J1.monomial(j)).to_vector()
+ mult_table[i][j] = V(p.list() + [field.zero()]*n2)
+
+ for i in range(n2):
+ for j in range(n2):
+ p = (J2.monomial(i)*J2.monomial(j)).to_vector()
+ mult_table[n1+i][n1+j] = V([field.zero()]*n1 + p.list())
+
+ fdeja = super(DirectSumEJA, self)
+ fdeja.__init__(field, mult_table, **kwargs)
+ self.rank.set_cache(J1.rank() + J2.rank())