]> gitweb.michael.orlitzky.com - sage.d.git/blobdiff - mjo/eja/eja_algebra.py
eja: factor a few things out of the FDEJA constructor.
[sage.d.git] / mjo / eja / eja_algebra.py
index ee2b52665e8a7d73b50fb4e159430627bcf8b36b..79187594472d82e24a0b700305b7ddfc7b192536 100644 (file)
@@ -170,6 +170,17 @@ from mjo.eja.eja_element import FiniteDimensionalEJAElement
 from mjo.eja.eja_operator import FiniteDimensionalEJAOperator
 from mjo.eja.eja_utils import _all2list, _mat2vec
 
+def EuclideanJordanAlgebras(field):
+    r"""
+    The category of Euclidean Jordan algebras over ``field``, which
+    must be a subfield of the real numbers. For now this is just a
+    convenient wrapper around all of the other category axioms that
+    apply to all EJAs.
+    """
+    category = MagmaticAlgebras(field).FiniteDimensional()
+    category = category.WithBasis().Unital().Commutative()
+    return category
+
 class FiniteDimensionalEJA(CombinatorialFreeModule):
     r"""
     A finite-dimensional Euclidean Jordan algebra.
@@ -228,6 +239,26 @@ class FiniteDimensionalEJA(CombinatorialFreeModule):
     """
     Element = FiniteDimensionalEJAElement
 
+    @staticmethod
+    def _check_input_field(field):
+        if not field.is_subring(RR):
+            # Note: this does return true for the real algebraic
+            # field, the rationals, and any quadratic field where
+            # we've specified a real embedding.
+            raise ValueError("scalar field is not real")
+
+    @staticmethod
+    def _check_input_axioms(basis, jordan_product, inner_product):
+        if not all( jordan_product(bi,bj) == jordan_product(bj,bi)
+                    for bi in basis
+                    for bj in basis ):
+            raise ValueError("Jordan product is not commutative")
+
+        if not all( inner_product(bi,bj) == inner_product(bj,bi)
+                    for bi in basis
+                    for bj in basis ):
+            raise ValueError("inner-product is not commutative")
+
     def __init__(self,
                  basis,
                  jordan_product,
@@ -244,30 +275,14 @@ class FiniteDimensionalEJA(CombinatorialFreeModule):
         n = len(basis)
 
         if check_field:
-            if not field.is_subring(RR):
-                # Note: this does return true for the real algebraic
-                # field, the rationals, and any quadratic field where
-                # we've specified a real embedding.
-                raise ValueError("scalar field is not real")
+            self._check_input_field(field)
 
         if check_axioms:
             # Check commutativity of the Jordan and inner-products.
             # This has to be done before we build the multiplication
             # and inner-product tables/matrices, because we take
             # advantage of symmetry in the process.
-            if not all( jordan_product(bi,bj) == jordan_product(bj,bi)
-                        for bi in basis
-                        for bj in basis ):
-                raise ValueError("Jordan product is not commutative")
-
-            if not all( inner_product(bi,bj) == inner_product(bj,bi)
-                        for bi in basis
-                        for bj in basis ):
-                raise ValueError("inner-product is not commutative")
-
-
-        category = MagmaticAlgebras(field).FiniteDimensional()
-        category = category.WithBasis().Unital().Commutative()
+            self._check_input_axioms(basis, jordan_product, inner_product)
 
         if n <= 1:
             # All zero- and one-dimensional algebras are just the real
@@ -286,6 +301,8 @@ class FiniteDimensionalEJA(CombinatorialFreeModule):
                                for bj in basis
                                for bk in basis)
 
+        category = EuclideanJordanAlgebras(field)
+
         if associative:
             # Element subalgebras can take advantage of this.
             category = category.Associative()
@@ -347,14 +364,19 @@ class FiniteDimensionalEJA(CombinatorialFreeModule):
         # its own set of non-ambient coordinates (in terms of the
         # supplied basis).
         vector_basis = tuple( V(_all2list(b)) for b in basis )
-        W = V.span_of_basis( vector_basis, check=check_axioms)
+
+        # Save the span of our matrix basis (when written out as long
+        # vectors) because otherwise we'll have to reconstruct it
+        # every time we want to coerce a matrix into the algebra.
+        self._matrix_span = V.span_of_basis( vector_basis, check=check_axioms)
 
         if orthonormalize:
-            # Now "W" is the vector space of our algebra coordinates. The
-            # variables "X1", "X2",...  refer to the entries of vectors in
-            # W. Thus to convert back and forth between the orthonormal
-            # coordinates and the given ones, we need to stick the original
-            # basis in W.
+            # Now "self._matrix_span" is the vector space of our
+            # algebra coordinates. The variables "X1", "X2",...  refer
+            # to the entries of vectors in self._matrix_span. Thus to
+            # convert back and forth between the orthonormal
+            # coordinates and the given ones, we need to stick the
+            # original basis in self._matrix_span.
             U = V.span_of_basis( deortho_vector_basis, check=check_axioms)
             self._deortho_matrix = matrix.column( U.coordinate_vector(q)
                                                   for q in vector_basis )
@@ -378,7 +400,7 @@ class FiniteDimensionalEJA(CombinatorialFreeModule):
                 # The jordan product returns a matrixy answer, so we
                 # have to convert it to the algebra coordinates.
                 elt = jordan_product(q_i, q_j)
-                elt = W.coordinate_vector(V(_all2list(elt)))
+                elt = self._matrix_span.coordinate_vector(V(_all2list(elt)))
                 self._multiplication_table[i][j] = self.from_vector(elt)
 
                 if not orthonormalize:
@@ -685,8 +707,8 @@ class FiniteDimensionalEJA(CombinatorialFreeModule):
 
     def _element_constructor_(self, elt):
         """
-        Construct an element of this algebra from its vector or matrix
-        representation.
+        Construct an element of this algebra or a subalgebra from its
+        EJA element, vector, or matrix representation.
 
         This gets called only after the parent element _call_ method
         fails to find a coercion for the argument.
@@ -725,6 +747,16 @@ class FiniteDimensionalEJA(CombinatorialFreeModule):
             sage: J( (J1.matrix_basis()[1], J2.matrix_basis()[2]) )
             b1 + b5
 
+        Subalgebra elements are embedded into the superalgebra::
+
+            sage: J = JordanSpinEJA(3)
+            sage: J.one()
+            b0
+            sage: x = sum(J.gens())
+            sage: A = x.subalgebra_generated_by()
+            sage: J(A.one())
+            b0
+
         TESTS:
 
         Ensure that we can convert any element back and forth
@@ -749,6 +781,7 @@ class FiniteDimensionalEJA(CombinatorialFreeModule):
             Traceback (most recent call last):
             ...
             ValueError: not an element of this algebra
+
         """
         msg = "not an element of this algebra"
         if elt in self.base_ring():
@@ -758,13 +791,16 @@ class FiniteDimensionalEJA(CombinatorialFreeModule):
             # that the integer 3 belongs to the space of 2-by-2 matrices.
             raise ValueError(msg)
 
-        try:
-            # Try to convert a vector into a column-matrix...
+        if hasattr(elt, 'superalgebra_element'):
+            # Handle subalgebra elements
+            if elt.parent().superalgebra() == self:
+                return elt.superalgebra_element()
+
+        if hasattr(elt, 'sparse_vector'):
+            # Convert a vector into a column-matrix. We check for
+            # "sparse_vector" and not "column" because matrices also
+            # have a "column" method.
             elt = elt.column()
-        except (AttributeError, TypeError):
-            # and ignore failure, because we weren't really expecting
-            # a vector as an argument anyway.
-            pass
 
         if elt not in self.matrix_space():
             raise ValueError(msg)
@@ -781,15 +817,10 @@ class FiniteDimensionalEJA(CombinatorialFreeModule):
         # is that we're already converting everything to long vectors,
         # and that strategy works for tuples as well.
         #
-        # We pass check=False because the matrix basis is "guaranteed"
-        # to be linearly independent... right? Ha ha.
-        elt = _all2list(elt)
-        V = VectorSpace(self.base_ring(), len(elt))
-        W = V.span_of_basis( (V(_all2list(s)) for s in self.matrix_basis()),
-                             check=False)
+        elt = self._matrix_span.ambient_vector_space()(_all2list(elt))
 
         try:
-            coords = W.coordinate_vector(V(elt))
+            coords = self._matrix_span.coordinate_vector(elt)
         except ArithmeticError:  # vector is not in free module
             raise ValueError(msg)
 
@@ -1395,7 +1426,7 @@ class FiniteDimensionalEJA(CombinatorialFreeModule):
         # corresponding to trivial spaces (e.g. it returns only the
         # eigenspace corresponding to lambda=1 if you take the
         # decomposition relative to the identity element).
-        trivial = self.subalgebra(())
+        trivial = self.subalgebra((), check_axioms=False)
         J0 = trivial                          # eigenvalue zero
         J5 = VectorSpace(self.base_ring(), 0) # eigenvalue one-half
         J1 = trivial                          # eigenvalue one