]> gitweb.michael.orlitzky.com - sage.d.git/blobdiff - mjo/eja/eja_subalgebra.py
eja: refactor the element subalgebra stuff into generic subalgebra.
[sage.d.git] / mjo / eja / eja_subalgebra.py
index c43e53f2b030ed920e6cc22d10c3097c4b704cec..b07f7e25ee599322cad6ec0d94bdd23bf0a964fe 100644 (file)
@@ -3,8 +3,7 @@ from sage.matrix.constructor import matrix
 from mjo.eja.eja_algebra import FiniteDimensionalEuclideanJordanAlgebra
 from mjo.eja.eja_element import FiniteDimensionalEuclideanJordanAlgebraElement
 
 from mjo.eja.eja_algebra import FiniteDimensionalEuclideanJordanAlgebra
 from mjo.eja.eja_element import FiniteDimensionalEuclideanJordanAlgebraElement
 
-
-class FiniteDimensionalEuclideanJordanElementSubalgebraElement(FiniteDimensionalEuclideanJordanAlgebraElement):
+class FiniteDimensionalEuclideanJordanSubalgebraElement(FiniteDimensionalEuclideanJordanAlgebraElement):
     """
     SETUP::
 
     """
     SETUP::
 
@@ -23,6 +22,17 @@ class FiniteDimensionalEuclideanJordanElementSubalgebraElement(FiniteDimensional
         sage: actual == expected
         True
 
         sage: actual == expected
         True
 
+    The left-multiplication-by operator for elements in the subalgebra
+    works like it does in the superalgebra, even if we orthonormalize
+    our basis::
+
+        sage: set_random_seed()
+        sage: x = random_eja(AA).random_element()
+        sage: A = x.subalgebra_generated_by(orthonormalize_basis=True)
+        sage: y = A.random_element()
+        sage: y.operator()(A.one()) == y
+        True
+
     """
 
     def superalgebra_element(self):
     """
 
     def superalgebra_element(self):
@@ -68,13 +78,14 @@ class FiniteDimensionalEuclideanJordanElementSubalgebraElement(FiniteDimensional
 
 
 
 
 
 
-class FiniteDimensionalEuclideanJordanElementSubalgebra(FiniteDimensionalEuclideanJordanAlgebra):
+class FiniteDimensionalEuclideanJordanSubalgebra(FiniteDimensionalEuclideanJordanAlgebra):
     """
     """
-    The subalgebra of an EJA generated by a single element.
+    A subalgebra of an EJA with a given basis.
 
     SETUP::
 
 
     SETUP::
 
-        sage: from mjo.eja.eja_algebra import JordanSpinEJA
+        sage: from mjo.eja.eja_algebra import (ComplexHermitianEJA,
+        ....:                                  JordanSpinEJA)
 
     TESTS:
 
 
     TESTS:
 
@@ -90,40 +101,20 @@ class FiniteDimensionalEuclideanJordanElementSubalgebra(FiniteDimensionalEuclide
         sage: J.one().subalgebra_generated_by().gens()
         (c0,)
 
         sage: J.one().subalgebra_generated_by().gens()
         (c0,)
 
+    Ensure that we can find subalgebras of subalgebras::
+
+        sage: A = ComplexHermitianEJA(3).one().subalgebra_generated_by()
+        sage: B = A.one().subalgebra_generated_by()
+        sage: B.dimension()
+        1
+
     """
     """
-    def __init__(self, elt):
-        superalgebra = elt.parent()
-
-        # First compute the vector subspace spanned by the powers of
-        # the given element.
-        V = superalgebra.vector_space()
-        superalgebra_basis = [superalgebra.one()]
-        basis_vectors = [superalgebra.one().to_vector()]
-        W = V.span_of_basis(basis_vectors)
-        for exponent in range(1, V.dimension()):
-            new_power = elt**exponent
-            basis_vectors.append( new_power.to_vector() )
-            try:
-                W = V.span_of_basis(basis_vectors)
-                superalgebra_basis.append( new_power )
-            except ValueError:
-                # Vectors weren't independent; bail and keep the
-                # last subspace that worked.
-                break
-
-        # Make the basis hashable for UniqueRepresentation.
-        superalgebra_basis = tuple(superalgebra_basis)
-
-        # Now figure out the entries of the right-multiplication
-        # matrix for the successive basis elements b0, b1,... of
-        # that subspace.
-        field = superalgebra.base_ring()
-        n = len(superalgebra_basis)
-        mult_table = [[W.zero() for i in range(n)] for j in range(n)]
-        for i in range(n):
-            for j in range(n):
-                product = superalgebra_basis[i]*superalgebra_basis[j]
-                mult_table[i][j] = W.coordinate_vector(product.to_vector())
+    def __init__(self, superalgebra, basis, rank=None, category=None):
+        self._superalgebra = superalgebra
+        V = self._superalgebra.vector_space()
+        field = self._superalgebra.base_ring()
+        if category is None:
+            category = self._superalgebra.category()
 
         # A half-assed attempt to ensure that we don't collide with
         # the superalgebra's prefix (ignoring the fact that there
 
         # A half-assed attempt to ensure that we don't collide with
         # the superalgebra's prefix (ignoring the fact that there
@@ -133,28 +124,36 @@ class FiniteDimensionalEuclideanJordanElementSubalgebra(FiniteDimensionalEuclide
         # are off-limits.
         prefixen = [ 'f', 'g', 'h', 'a', 'b', 'c', 'd' ]
         try:
         # are off-limits.
         prefixen = [ 'f', 'g', 'h', 'a', 'b', 'c', 'd' ]
         try:
-            prefix = prefixen[prefixen.index(superalgebra.prefix()) + 1]
+            prefix = prefixen[prefixen.index(self._superalgebra.prefix()) + 1]
         except ValueError:
             prefix = prefixen[0]
 
         except ValueError:
             prefix = prefixen[0]
 
-        # The rank is the highest possible degree of a minimal
-        # polynomial, and is bounded above by the dimension. We know
-        # in this case that there's an element whose minimal
-        # polynomial has the same degree as the space's dimension
-        # (remember how we constructed the space?), so that must be
-        # its rank too.
-        rank = W.dimension()
+        basis_vectors = [ b.to_vector() for b in basis ]
+        superalgebra_basis = [ self._superalgebra.from_vector(b)
+                               for b in basis_vectors ]
+
+        W = V.span_of_basis( V.from_vector(v) for v in basis_vectors )
+        n = len(superalgebra_basis)
+        mult_table = [[W.zero() for i in range(n)] for j in range(n)]
+        for i in range(n):
+            for j in range(n):
+                product = superalgebra_basis[i]*superalgebra_basis[j]
+                # product.to_vector() might live in a vector subspace
+                # if our parent algebra is already a subalgebra. We
+                # use V.from_vector() to make it "the right size" in
+                # that case.
+                product_vector = V.from_vector(product.to_vector())
+                mult_table[i][j] = W.coordinate_vector(product_vector)
 
 
-        category = superalgebra.category().Associative()
         natural_basis = tuple( b.natural_representation()
                                for b in superalgebra_basis )
 
         natural_basis = tuple( b.natural_representation()
                                for b in superalgebra_basis )
 
-        self._superalgebra = superalgebra
+
         self._vector_space = W
         self._superalgebra_basis = superalgebra_basis
 
 
         self._vector_space = W
         self._superalgebra_basis = superalgebra_basis
 
 
-        fdeja = super(FiniteDimensionalEuclideanJordanElementSubalgebra, self)
+        fdeja = super(FiniteDimensionalEuclideanJordanSubalgebra, self)
         return fdeja.__init__(field,
                               mult_table,
                               rank,
         return fdeja.__init__(field,
                               mult_table,
                               rank,
@@ -163,6 +162,7 @@ class FiniteDimensionalEuclideanJordanElementSubalgebra(FiniteDimensionalEuclide
                               natural_basis=natural_basis)
 
 
                               natural_basis=natural_basis)
 
 
+
     def _element_constructor_(self, elt):
         """
         Construct an element of this subalgebra from the given one.
     def _element_constructor_(self, elt):
         """
         Construct an element of this subalgebra from the given one.
@@ -172,35 +172,25 @@ class FiniteDimensionalEuclideanJordanElementSubalgebra(FiniteDimensionalEuclide
         SETUP::
 
             sage: from mjo.eja.eja_algebra import RealSymmetricEJA
         SETUP::
 
             sage: from mjo.eja.eja_algebra import RealSymmetricEJA
-            sage: from mjo.eja.eja_subalgebra import FiniteDimensionalEuclideanJordanElementSubalgebra
+            sage: from mjo.eja.eja_subalgebra import FiniteDimensionalEuclideanJordanSubalgebra
 
         EXAMPLES::
 
             sage: J = RealSymmetricEJA(3)
             sage: x = sum( i*J.gens()[i] for i in range(6) )
 
         EXAMPLES::
 
             sage: J = RealSymmetricEJA(3)
             sage: x = sum( i*J.gens()[i] for i in range(6) )
-            sage: K = FiniteDimensionalEuclideanJordanElementSubalgebra(x)
+            sage: basis = tuple( x^k for k in range(J.rank()) )
+            sage: K = FiniteDimensionalEuclideanJordanSubalgebra(J,basis)
             sage: [ K(x^k) for k in range(J.rank()) ]
             [f0, f1, f2]
 
         ::
 
         """
             sage: [ K(x^k) for k in range(J.rank()) ]
             [f0, f1, f2]
 
         ::
 
         """
-        if elt == 0:
-            # Just as in the superalgebra class, we need to hack
-            # this special case to ensure that random_element() can
-            # coerce a ring zero into the algebra.
-            return self.zero()
+        if elt not in self.superalgebra():
+            raise ValueError("not an element of this subalgebra")
 
 
-        if elt in self.superalgebra():
-            coords = self.vector_space().coordinate_vector(elt.to_vector())
-            return self.from_vector(coords)
-
-
-    def one_basis(self):
-        """
-        Return the basis-element-index of this algebra's unit element.
-        """
-        return 0
+        coords = self.vector_space().coordinate_vector(elt.to_vector())
+        return self.from_vector(coords)
 
 
     def one(self):
 
 
     def one(self):
@@ -208,10 +198,10 @@ class FiniteDimensionalEuclideanJordanElementSubalgebra(FiniteDimensionalEuclide
         Return the multiplicative identity element of this algebra.
 
         The superclass method computes the identity element, which is
         Return the multiplicative identity element of this algebra.
 
         The superclass method computes the identity element, which is
-        beyond overkill in this case: the algebra identity should be our
-        first basis element. We implement this via :meth:`one_basis`
-        because that method can optionally be used by other parts of the
-        category framework.
+        beyond overkill in this case: the superalgebra identity
+        restricted to this algebra is its identity. Note that we can't
+        count on the first basis element being the identity -- it migth
+        have been scaled if we orthonormalized the basis.
 
         SETUP::
 
 
         SETUP::
 
@@ -232,24 +222,66 @@ class FiniteDimensionalEuclideanJordanElementSubalgebra(FiniteDimensionalEuclide
 
         TESTS:
 
 
         TESTS:
 
-        The identity element acts like the identity::
+        The identity element acts like the identity over the rationals::
 
             sage: set_random_seed()
 
             sage: set_random_seed()
-            sage: J = random_eja().random_element().subalgebra_generated_by()
-            sage: x = J.random_element()
-            sage: J.one()*x == x and x*J.one() == x
+            sage: x = random_eja().random_element()
+            sage: A = x.subalgebra_generated_by()
+            sage: x = A.random_element()
+            sage: A.one()*x == x and x*A.one() == x
+            True
+
+        The identity element acts like the identity over the algebraic
+        reals with an orthonormal basis::
+
+            sage: set_random_seed()
+            sage: x = random_eja(AA).random_element()
+            sage: A = x.subalgebra_generated_by(orthonormalize_basis=True)
+            sage: x = A.random_element()
+            sage: A.one()*x == x and x*A.one() == x
+            True
+
+        The matrix of the unit element's operator is the identity over
+        the rationals::
+
+            sage: set_random_seed()
+            sage: x = random_eja().random_element()
+            sage: A = x.subalgebra_generated_by()
+            sage: actual = A.one().operator().matrix()
+            sage: expected = matrix.identity(A.base_ring(), A.dimension())
+            sage: actual == expected
             True
 
             True
 
-        The matrix of the unit element's operator is the identity::
+        The matrix of the unit element's operator is the identity over
+        the algebraic reals with an orthonormal basis::
 
             sage: set_random_seed()
 
             sage: set_random_seed()
-            sage: J = random_eja().random_element().subalgebra_generated_by()
-            sage: actual = J.one().operator().matrix()
-            sage: expected = matrix.identity(J.base_ring(), J.dimension())
+            sage: x = random_eja(AA).random_element()
+            sage: A = x.subalgebra_generated_by(orthonormalize_basis=True)
+            sage: actual = A.one().operator().matrix()
+            sage: expected = matrix.identity(A.base_ring(), A.dimension())
             sage: actual == expected
             True
             sage: actual == expected
             True
+
+        """
+        if self.dimension() == 0:
+            return self.zero()
+        else:
+            sa_one = self.superalgebra().one().to_vector()
+            sa_coords = self.vector_space().coordinate_vector(sa_one)
+            return self.from_vector(sa_coords)
+
+
+    def natural_basis_space(self):
         """
         """
-        return self.monomial(self.one_basis())
+        Return the natural basis space of this algebra, which is identical
+        to that of its superalgebra.
+
+        This is correct "by definition," and avoids a mismatch when the
+        subalgebra is trivial (with no natural basis to infer anything
+        from) and the parent is not.
+        """
+        return self.superalgebra().natural_basis_space()
 
 
     def superalgebra(self):
 
 
     def superalgebra(self):
@@ -264,28 +296,29 @@ class FiniteDimensionalEuclideanJordanElementSubalgebra(FiniteDimensionalEuclide
         SETUP::
 
             sage: from mjo.eja.eja_algebra import RealSymmetricEJA
         SETUP::
 
             sage: from mjo.eja.eja_algebra import RealSymmetricEJA
-            sage: from mjo.eja.eja_subalgebra import FiniteDimensionalEuclideanJordanElementSubalgebra
+            sage: from mjo.eja.eja_subalgebra import FiniteDimensionalEuclideanJordanSubalgebra
 
         EXAMPLES::
 
             sage: J = RealSymmetricEJA(3)
 
         EXAMPLES::
 
             sage: J = RealSymmetricEJA(3)
-            sage: x = sum( i*J.gens()[i] for i in range(6) )
-            sage: K = FiniteDimensionalEuclideanJordanElementSubalgebra(x)
+            sage: x = J.monomial(0) + 2*J.monomial(2) + 5*J.monomial(5)
+            sage: basis = (x^0, x^1, x^2)
+            sage: K = FiniteDimensionalEuclideanJordanSubalgebra(J,basis)
             sage: K.vector_space()
             sage: K.vector_space()
-            Vector space of degree 6 and dimension 3 over Rational Field
+            Vector space of degree 6 and dimension 3 over...
             User basis matrix:
             [ 1  0  1  0  0  1]
             User basis matrix:
             [ 1  0  1  0  0  1]
-            [ 0  1  2  3  4  5]
-            [10 14 21 19 31 50]
+            [ 1  0  2  0  0  5]
+            [ 1  0  4  0  0 25]
             sage: (x^0).to_vector()
             (1, 0, 1, 0, 0, 1)
             sage: (x^1).to_vector()
             sage: (x^0).to_vector()
             (1, 0, 1, 0, 0, 1)
             sage: (x^1).to_vector()
-            (0, 1, 2, 3, 4, 5)
+            (1, 0, 2, 0, 0, 5)
             sage: (x^2).to_vector()
             sage: (x^2).to_vector()
-            (10, 14, 21, 19, 31, 50)
+            (1, 0, 4, 0, 0, 25)
 
         """
         return self._vector_space
 
 
 
         """
         return self._vector_space
 
 
-    Element = FiniteDimensionalEuclideanJordanElementSubalgebraElement
+    Element = FiniteDimensionalEuclideanJordanSubalgebraElement