+
+ def __init__(self, algebras, **kwargs):
+ CombinatorialFreeModule_CartesianProduct.__init__(self,
+ algebras,
+ **kwargs)
+ field = algebras[0].base_ring()
+ if not all( J.base_ring() == field for J in algebras ):
+ raise ValueError("all factors must share the same base field")
+
+ associative = all( m.is_associative() for m in algebras )
+
+ # The definition of matrix_space() and self.basis() relies
+ # only on the stuff in the CFM_CartesianProduct class, which
+ # we've already initialized.
+ Js = self.cartesian_factors()
+ m = len(Js)
+ MS = self.matrix_space()
+ basis = tuple(
+ MS(tuple( self.cartesian_projection(i)(b).to_matrix()
+ for i in range(m) ))
+ for b in self.basis()
+ )
+
+ # Define jordan/inner products that operate on that matrix_basis.
+ def jordan_product(x,y):
+ return MS(tuple(
+ (Js[i](x[i])*Js[i](y[i])).to_matrix() for i in range(m)
+ ))
+
+ def inner_product(x, y):
+ return sum(
+ Js[i](x[i]).inner_product(Js[i](y[i])) for i in range(m)
+ )
+
+ # There's no need to check the field since it already came
+ # from an EJA. Likewise the axioms are guaranteed to be
+ # satisfied, unless the guy writing this class sucks.
+ #
+ # If you want the basis to be orthonormalized, orthonormalize
+ # the factors.
+ FiniteDimensionalEJA.__init__(self,
+ basis,
+ jordan_product,
+ inner_product,
+ field=field,
+ orthonormalize=False,
+ associative=associative,
+ cartesian_product=True,
+ check_field=False,
+ check_axioms=False)
+
+ ones = tuple(J.one() for J in algebras)
+ self.one.set_cache(self._cartesian_product_of_elements(ones))
+ self.rank.set_cache(sum(J.rank() for J in algebras))
+
+ def _monomial_to_generator(self, mon):
+ r"""
+ Convert a monomial index into a generator index.
+
+ SETUP::
+
+ sage: from mjo.eja.eja_algebra import random_eja()
+
+ TESTS::
+
+ sage: J1 = random_eja(field=QQ, orthonormalize=False)
+ sage: J2 = random_eja(field=QQ, orthonormalize=False)
+ sage: J = cartesian_product([J1,J2])
+ sage: all( J.monomial(m)
+ ....: ==
+ ....: J.gens()[J._monomial_to_generator(m)]
+ ....: for m in J.basis().keys() )
+
+ """
+ # The superclass method indexes into a matrix, so we have to
+ # turn the tuples i and j into integers. This is easy enough
+ # given that the first coordinate of i and j corresponds to
+ # the factor, and the second coordinate corresponds to the
+ # index of the generator within that factor.
+ try:
+ factor = mon[0]
+ except TypeError: # 'int' object is not subscriptable
+ return mon
+ idx_in_factor = self._monomial_to_generator(mon[1])
+
+ offset = sum( f.dimension()
+ for f in self.cartesian_factors()[:factor] )
+ return offset + idx_in_factor
+
+ def product_on_basis(self, i, j):