from sage.rings.all import (ZZ, QQ, AA, QQbar, RR, RLF, CLF,
PolynomialRing,
QuadraticField)
-from mjo.eja.eja_element import FiniteDimensionalEJAElement
+from mjo.eja.eja_element import (CartesianProductEJAElement,
+ FiniteDimensionalEJAElement)
from mjo.eja.eja_operator import FiniteDimensionalEJAOperator
from mjo.eja.eja_utils import _mat2vec
return ``True``, unless this algebra was constructed with
``check_axioms=False`` and passed an invalid multiplication table.
"""
- return all( (self.monomial(i)**2)*(self.monomial(i)*self.monomial(j))
+ return all( (self.gens()[i]**2)*(self.gens()[i]*self.gens()[j])
==
- (self.monomial(i))*((self.monomial(i)**2)*self.monomial(j))
+ (self.gens()[i])*((self.gens()[i]**2)*self.gens()[j])
for i in range(self.dimension())
for j in range(self.dimension()) )
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)
+ x = self.gens()[i]
+ y = self.gens()[j]
+ z = self.gens()[k]
diff = (x*y).inner_product(z) - x.inner_product(y*z)
if self.base_ring().is_exact():
# And to each subsequent row, prepend an entry that belongs to
# the left-side "header column."
- M += [ [self.monomial(i)] + [ self.product_on_basis(i,j)
- for j in range(n) ]
+ M += [ [self.gens()[i]] + [ self.product_on_basis(i,j)
+ for j in range(n) ]
for i in range(n) ]
return table(M, header_row=True, header_column=True, frame=True)
def L_x_i_j(i,j):
# From a result in my book, these are the entries of the
# basis representation of L_x.
- return sum( vars[k]*self.monomial(k).operator().matrix()[i,j]
+ return sum( vars[k]*self.gens()[k].operator().matrix()[i,j]
for k in range(n) )
L_x = matrix(F, n, n, L_x_i_j)
SETUP::
- sage: from mjo.eja.eja_algebra import (CartesianProductEJA,
+ sage: from mjo.eja.eja_algebra import (random_eja,
+ ....: CartesianProductEJA,
....: HadamardEJA,
....: JordanSpinEJA,
....: RealSymmetricEJA)
Real Field (+) Euclidean Jordan algebra of dimension 6 over
Algebraic Real Field
+ Rank is additive on a Cartesian product::
+
+ sage: J1 = HadamardEJA(1)
+ sage: J2 = RealSymmetricEJA(2)
+ sage: J = cartesian_product([J1,J2])
+ sage: J1.rank.clear_cache()
+ sage: J2.rank.clear_cache()
+ sage: J.rank.clear_cache()
+ sage: J.rank()
+ 3
+ sage: J.rank() == J1.rank() + J2.rank()
+ True
+
+ The same rank computation works over the rationals, with whatever
+ basis you like::
+
+ sage: J1 = HadamardEJA(1, field=QQ, orthonormalize=False)
+ sage: J2 = RealSymmetricEJA(2, field=QQ, orthonormalize=False)
+ sage: J = cartesian_product([J1,J2])
+ sage: J1.rank.clear_cache()
+ sage: J2.rank.clear_cache()
+ sage: J.rank.clear_cache()
+ sage: J.rank()
+ 3
+ sage: J.rank() == J1.rank() + J2.rank()
+ True
+
TESTS:
All factors must share the same base field::
...
ValueError: all factors must share the same base field
+ The "cached" Jordan and inner products are the componentwise
+ ones::
+
+ sage: set_random_seed()
+ sage: J1 = random_eja()
+ sage: J2 = random_eja()
+ sage: J = cartesian_product([J1,J2])
+ sage: x,y = J.random_elements(2)
+ sage: x*y == J.cartesian_jordan_product(x,y)
+ True
+ sage: x.inner_product(y) == J.cartesian_inner_product(x,y)
+ True
+
+ The cached unit element is the same one that would be computed::
+
+ sage: set_random_seed() # long time
+ sage: J1 = random_eja() # long time
+ sage: J2 = random_eja() # long time
+ sage: J = cartesian_product([J1,J2]) # long time
+ sage: actual = J.one() # long time
+ sage: J.one.clear_cache() # long time
+ sage: expected = J.one() # long time
+ sage: actual == expected # long time
+ True
+
"""
def __init__(self, modules, **kwargs):
- CombinatorialFreeModule_CartesianProduct.__init__(self, modules)
+ CombinatorialFreeModule_CartesianProduct.__init__(self,
+ modules,
+ **kwargs)
field = modules[0].base_ring()
if not all( J.base_ring() == field for J in modules ):
raise ValueError("all factors must share the same base field")
# Use whatever category the superclass came up with. Usually
# some join of the EJA and Cartesian product
# categories. There's no need to check the field since it
- # already came from an EJA. Likewise the axioms are guaranteed
- # to be satisfied.
+ # already came from an EJA.
+ #
+ # If you want the basis to be orthonormalized, orthonormalize
+ # the factors.
FiniteDimensionalEJA.__init__(self,
basis,
jordan_product,
inner_product,
field=field,
+ orthonormalize=False,
check_field=False,
check_axioms=False,
- category=self.category(),
- **kwargs)
+ category=self.category())
+ ones = tuple(J.one() for J in modules)
+ self.one.set_cache(self._cartesian_product_of_elements(ones))
self.rank.set_cache(sum(J.rank() for J in modules))
+ # Now that everything else is ready, we clobber our computed
+ # matrix basis with the "correct" one consisting of ordered
+ # tuples. Since we didn't orthonormalize our basis, we can
+ # create these from the basis that was handed to us; that is,
+ # we don't need to use the one that the earlier __init__()
+ # method came up with.
+ m = len(self.cartesian_factors())
+ MS = self.matrix_space()
+ self._matrix_basis = tuple(
+ MS(tuple( self.cartesian_projection(i)(b).to_matrix()
+ for i in range(m) ))
+ for b in self.basis()
+ )
+
+ def matrix_space(self):
+ r"""
+ Return the space that our matrix basis lives in as a Cartesian
+ product.
+
+ SETUP::
+
+ sage: from mjo.eja.eja_algebra import (HadamardEJA,
+ ....: RealSymmetricEJA)
+
+ EXAMPLES::
+
+ sage: J1 = HadamardEJA(1)
+ sage: J2 = RealSymmetricEJA(2)
+ sage: J = cartesian_product([J1,J2])
+ sage: J.matrix_space()
+ The Cartesian product of (Full MatrixSpace of 1 by 1 dense
+ matrices over Algebraic Real Field, Full MatrixSpace of 2
+ by 2 dense matrices over Algebraic Real Field)
+
+ """
+ from sage.categories.cartesian_product import cartesian_product
+ return cartesian_product( [J.matrix_space()
+ for J in self.cartesian_factors()] )
+
@cached_method
def cartesian_projection(self, i):
r"""
return sum( P(x).inner_product(P(y)) for P in projections )
- Element = FiniteDimensionalEJAElement
+ def _element_constructor_(self, elt):
+ r"""
+ Construct an element of this algebra from an ordered tuple.
+ We just apply the element constructor from each of our factors
+ to the corresponding component of the tuple, and package up
+ the result.
-class FiniteDimensionalEJA_CartesianProduct(CartesianProductEJA):
- r"""
- A wrapper around the :class:`CartesianProductEJA` class that gets
- used by the ``cartesian_product`` functor. Its one job is to set
- ``orthonormalize=False``, since ``cartesian_product()`` can't be
- made to pass that option through. And if we try to orthonormalize
- over the rationals, we get conversion errors. If you want a non-
- standard Jordan product or inner product, or if you want to
- orthonormalize the basis, use :class:`CartesianProductEJA`
- directly.
- """
- def __init__(self, modules, **options):
- CombinatorialFreeModule_CartesianProduct.__init__(self,
- modules,
- **options)
- CartesianProductEJA.__init__(self, modules, orthonormalize=False)
+ SETUP::
+
+ sage: from mjo.eja.eja_algebra import (HadamardEJA,
+ ....: RealSymmetricEJA)
+
+ EXAMPLES::
+
+ sage: J1 = HadamardEJA(3)
+ sage: J2 = RealSymmetricEJA(2)
+ sage: J = cartesian_product([J1,J2])
+ sage: J( (J1.matrix_basis()[1], J2.matrix_basis()[2]) )
+ e(0, 1) + e(1, 2)
+ """
+ m = len(self.cartesian_factors())
+ try:
+ z = tuple( self.cartesian_factors()[i](elt[i]) for i in range(m) )
+ return self._cartesian_product_of_elements(z)
+ except:
+ raise ValueError("not an element of this algebra")
+
+ Element = CartesianProductEJAElement
-FiniteDimensionalEJA.CartesianProduct = FiniteDimensionalEJA_CartesianProduct
+FiniteDimensionalEJA.CartesianProduct = CartesianProductEJA
random_eja = ConcreteEJA.random_instance