]> gitweb.michael.orlitzky.com - sage.d.git/blobdiff - mjo/eja/eja_element.py
eja: speed up the computation of element powers.
[sage.d.git] / mjo / eja / eja_element.py
index 00a15a1c56897172f57aeec2d43a391f3b367a45..7c861834723344ea6403f9f5da289af8aa299ae7 100644 (file)
@@ -1,6 +1,6 @@
-from sage.algebras.finite_dimensional_algebras.finite_dimensional_algebra_element import FiniteDimensionalAlgebraElement
 from sage.matrix.constructor import matrix
 from sage.modules.free_module import VectorSpace
 from sage.matrix.constructor import matrix
 from sage.modules.free_module import VectorSpace
+from sage.modules.with_basis.indexed_element import IndexedFreeModuleElement
 
 # TODO: make this unnecessary somehow.
 from sage.misc.lazy_import import lazy_import
 
 # TODO: make this unnecessary somehow.
 from sage.misc.lazy_import import lazy_import
@@ -10,7 +10,7 @@ lazy_import('mjo.eja.eja_subalgebra',
 from mjo.eja.eja_operator import FiniteDimensionalEuclideanJordanAlgebraOperator
 from mjo.eja.eja_utils import _mat2vec
 
 from mjo.eja.eja_operator import FiniteDimensionalEuclideanJordanAlgebraOperator
 from mjo.eja.eja_utils import _mat2vec
 
-class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraElement):
+class FiniteDimensionalEuclideanJordanAlgebraElement(IndexedFreeModuleElement):
     """
     An element of a Euclidean Jordan algebra.
     """
     """
     An element of a Euclidean Jordan algebra.
     """
@@ -25,68 +25,7 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
                       dir(self.__class__) )
 
 
                       dir(self.__class__) )
 
 
-    def __init__(self, A, elt=None):
-        """
-
-        SETUP::
-
-            sage: from mjo.eja.eja_algebra import (RealSymmetricEJA,
-            ....:                                  random_eja)
-
-        EXAMPLES:
-
-        The identity in `S^n` is converted to the identity in the EJA::
-
-            sage: J = RealSymmetricEJA(3)
-            sage: I = matrix.identity(QQ,3)
-            sage: J(I) == J.one()
-            True
-
-        This skew-symmetric matrix can't be represented in the EJA::
-
-            sage: J = RealSymmetricEJA(3)
-            sage: A = matrix(QQ,3, lambda i,j: i-j)
-            sage: J(A)
-            Traceback (most recent call last):
-            ...
-            ArithmeticError: vector is not in free module
-
-        TESTS:
-
-        Ensure that we can convert any element of the parent's
-        underlying vector space back into an algebra element whose
-        vector representation is what we started with::
 
 
-            sage: set_random_seed()
-            sage: J = random_eja()
-            sage: v = J.vector_space().random_element()
-            sage: J(v).vector() == v
-            True
-
-        """
-        # Goal: if we're given a matrix, and if it lives in our
-        # parent algebra's "natural ambient space," convert it
-        # into an algebra element.
-        #
-        # The catch is, we make a recursive call after converting
-        # the given matrix into a vector that lives in the algebra.
-        # This we need to try the parent class initializer first,
-        # to avoid recursing forever if we're given something that
-        # already fits into the algebra, but also happens to live
-        # in the parent's "natural ambient space" (this happens with
-        # vectors in R^n).
-        try:
-            FiniteDimensionalAlgebraElement.__init__(self, A, elt)
-        except ValueError:
-            natural_basis = A.natural_basis()
-            if elt in natural_basis[0].matrix_space():
-                # Thanks for nothing! Matrix spaces aren't vector
-                # spaces in Sage, so we have to figure out its
-                # natural-basis coordinates ourselves.
-                V = VectorSpace(elt.base_ring(), elt.nrows()**2)
-                W = V.span( _mat2vec(s) for s in natural_basis )
-                coords =  W.coordinates(_mat2vec(elt))
-                FiniteDimensionalAlgebraElement.__init__(self, A, coords)
 
     def __pow__(self, n):
         """
 
     def __pow__(self, n):
         """
@@ -139,7 +78,7 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
         elif n == 1:
             return self
         else:
         elif n == 1:
             return self
         else:
-            return (self.operator()**(n-1))(self)
+            return (self**(n-1))*self
 
 
     def apply_univariate_polynomial(self, p):
 
 
     def apply_univariate_polynomial(self, p):
@@ -226,9 +165,24 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
             sage: x.apply_univariate_polynomial(p)
             0
 
             sage: x.apply_univariate_polynomial(p)
             0
 
+        The characteristic polynomials of the zero and unit elements
+        should be what we think they are in a subalgebra, too::
+
+            sage: J = RealCartesianProductEJA(3)
+            sage: p1 = J.one().characteristic_polynomial()
+            sage: q1 = J.zero().characteristic_polynomial()
+            sage: e0,e1,e2 = J.gens()
+            sage: A = (e0 + 2*e1 + 3*e2).subalgebra_generated_by() # dim 3
+            sage: p2 = A.one().characteristic_polynomial()
+            sage: q2 = A.zero().characteristic_polynomial()
+            sage: p1 == p2
+            True
+            sage: q1 == q2
+            True
+
         """
         p = self.parent().characteristic_polynomial()
         """
         p = self.parent().characteristic_polynomial()
-        return p(*self.vector())
+        return p(*self.to_vector())
 
 
     def inner_product(self, other):
 
 
     def inner_product(self, other):
@@ -255,7 +209,7 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
             sage: y = vector(QQ,[4,5,6])
             sage: x.inner_product(y)
             32
             sage: y = vector(QQ,[4,5,6])
             sage: x.inner_product(y)
             32
-            sage: J(x).inner_product(J(y))
+            sage: J.from_vector(x).inner_product(J.from_vector(y))
             32
 
         The inner product on `S^n` is `<X,Y> = trace(X*Y)`, where
             32
 
         The inner product on `S^n` is `<X,Y> = trace(X*Y)`, where
@@ -289,9 +243,8 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
 
             sage: set_random_seed()
             sage: J = random_eja()
 
             sage: set_random_seed()
             sage: J = random_eja()
-            sage: x = J.random_element()
-            sage: y = J.random_element()
-            sage: x.inner_product(y) in RR
+            sage: x,y = J.random_elements(2)
+            sage: x.inner_product(y) in RLF
             True
 
         """
             True
 
         """
@@ -326,9 +279,7 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
         Test Lemma 1 from Chapter III of Koecher::
 
             sage: set_random_seed()
         Test Lemma 1 from Chapter III of Koecher::
 
             sage: set_random_seed()
-            sage: J = random_eja()
-            sage: u = J.random_element()
-            sage: v = J.random_element()
+            sage: u,v = random_eja().random_elements(2)
             sage: lhs = u.operator_commutes_with(u*v)
             sage: rhs = v.operator_commutes_with(u^2)
             sage: lhs == rhs
             sage: lhs = u.operator_commutes_with(u*v)
             sage: rhs = v.operator_commutes_with(u^2)
             sage: lhs == rhs
@@ -338,9 +289,7 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
         Chapter III, or from Baes (2.3)::
 
             sage: set_random_seed()
         Chapter III, or from Baes (2.3)::
 
             sage: set_random_seed()
-            sage: J = random_eja()
-            sage: x = J.random_element()
-            sage: y = J.random_element()
+            sage: x,y = random_eja().random_elements(2)
             sage: Lx = x.operator()
             sage: Ly = y.operator()
             sage: Lxx = (x*x).operator()
             sage: Lx = x.operator()
             sage: Ly = y.operator()
             sage: Lxx = (x*x).operator()
@@ -352,10 +301,7 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
         Baes (2.4)::
 
             sage: set_random_seed()
         Baes (2.4)::
 
             sage: set_random_seed()
-            sage: J = random_eja()
-            sage: x = J.random_element()
-            sage: y = J.random_element()
-            sage: z = J.random_element()
+            sage: x,y,z = random_eja().random_elements(3)
             sage: Lx = x.operator()
             sage: Ly = y.operator()
             sage: Lz = z.operator()
             sage: Lx = x.operator()
             sage: Ly = y.operator()
             sage: Lz = z.operator()
@@ -369,10 +315,7 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
         Baes (2.5)::
 
             sage: set_random_seed()
         Baes (2.5)::
 
             sage: set_random_seed()
-            sage: J = random_eja()
-            sage: u = J.random_element()
-            sage: y = J.random_element()
-            sage: z = J.random_element()
+            sage: u,y,z = random_eja().random_elements(3)
             sage: Lu = u.operator()
             sage: Ly = y.operator()
             sage: Lz = z.operator()
             sage: Lu = u.operator()
             sage: Ly = y.operator()
             sage: Lz = z.operator()
@@ -429,6 +372,15 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
             sage: x.is_invertible() == (x.det() != 0)
             True
 
             sage: x.is_invertible() == (x.det() != 0)
             True
 
+        Ensure that the determinant is multiplicative on an associative
+        subalgebra as in Faraut and Koranyi's Proposition II.2.2::
+
+            sage: set_random_seed()
+            sage: J = random_eja().random_element().subalgebra_generated_by()
+            sage: x,y = J.random_elements(2)
+            sage: (x*y).det() == x.det()*y.det()
+            True
+
         """
         P = self.parent()
         r = P.rank()
         """
         P = self.parent()
         r = P.rank()
@@ -437,7 +389,7 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
         # -1 to ensure that _charpoly_coeff(0) is really what
         # appears in front of t^{0} in the charpoly. However,
         # we want (-1)^r times THAT for the determinant.
         # -1 to ensure that _charpoly_coeff(0) is really what
         # appears in front of t^{0} in the charpoly. However,
         # we want (-1)^r times THAT for the determinant.
-        return ((-1)**r)*p(*self.vector())
+        return ((-1)**r)*p(*self.to_vector())
 
 
     def inverse(self):
 
 
     def inverse(self):
@@ -460,18 +412,17 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
         Example 11.11::
 
             sage: set_random_seed()
         Example 11.11::
 
             sage: set_random_seed()
-            sage: n = ZZ.random_element(1,10)
-            sage: J = JordanSpinEJA(n)
+            sage: J = JordanSpinEJA.random_instance()
             sage: x = J.random_element()
             sage: while not x.is_invertible():
             ....:     x = J.random_element()
             sage: x = J.random_element()
             sage: while not x.is_invertible():
             ....:     x = J.random_element()
-            sage: x_vec = x.vector()
+            sage: x_vec = x.to_vector()
             sage: x0 = x_vec[0]
             sage: x_bar = x_vec[1:]
             sage: coeff = ~(x0^2 - x_bar.inner_product(x_bar))
             sage: inv_vec = x_vec.parent()([x0] + (-x_bar).list())
             sage: x_inverse = coeff*inv_vec
             sage: x0 = x_vec[0]
             sage: x_bar = x_vec[1:]
             sage: coeff = ~(x0^2 - x_bar.inner_product(x_bar))
             sage: inv_vec = x_vec.parent()([x0] + (-x_bar).list())
             sage: x_inverse = coeff*inv_vec
-            sage: x.inverse() == J(x_inverse)
+            sage: x.inverse() == J.from_vector(x_inverse)
             True
 
         TESTS:
             True
 
         TESTS:
@@ -543,15 +494,23 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
             sage: J.one().is_invertible()
             True
 
             sage: J.one().is_invertible()
             True
 
-        The zero element is never invertible::
+        The zero element is never invertible in a non-trivial algebra::
 
             sage: set_random_seed()
             sage: J = random_eja()
 
             sage: set_random_seed()
             sage: J = random_eja()
-            sage: J.zero().is_invertible()
+            sage: (not J.is_trivial()) and J.zero().is_invertible()
             False
 
         """
             False
 
         """
-        zero = self.parent().zero()
+        if self.is_zero():
+            if self.parent().is_trivial():
+                return True
+            else:
+                return False
+
+        # In fact, we only need to know if the constant term is non-zero,
+        # so we can pass in the field's zero element instead.
+        zero = self.base_ring().zero()
         p = self.minimal_polynomial()
         return not (p(zero) == zero)
 
         p = self.minimal_polynomial()
         return not (p(zero) == zero)
 
@@ -679,8 +638,7 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
         aren't multiples of the identity are regular::
 
             sage: set_random_seed()
         aren't multiples of the identity are regular::
 
             sage: set_random_seed()
-            sage: n = ZZ.random_element(1,10)
-            sage: J = JordanSpinEJA(n)
+            sage: J = JordanSpinEJA.random_instance()
             sage: x = J.random_element()
             sage: x == x.coefficient(0)*J.one() or x.degree() == 2
             True
             sage: x = J.random_element()
             sage: x == x.coefficient(0)*J.one() or x.degree() == 2
             True
@@ -704,6 +662,11 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
             True
 
         """
             True
 
         """
+        if self.is_zero() and not self.parent().is_trivial():
+            # The minimal polynomial of zero in a nontrivial algebra
+            # is "t"; in a trivial algebra it's "1" by convention
+            # (it's an empty product).
+            return 1
         return self.subalgebra_generated_by().dimension()
 
 
         return self.subalgebra_generated_by().dimension()
 
 
@@ -732,6 +695,7 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
         SETUP::
 
             sage: from mjo.eja.eja_algebra import (JordanSpinEJA,
         SETUP::
 
             sage: from mjo.eja.eja_algebra import (JordanSpinEJA,
+            ....:                                  RealSymmetricEJA,
             ....:                                  random_eja)
 
         TESTS:
             ....:                                  random_eja)
 
         TESTS:
@@ -757,16 +721,18 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
         The minimal polynomial and the characteristic polynomial coincide
         and are known (see Alizadeh, Example 11.11) for all elements of
         the spin factor algebra that aren't scalar multiples of the
         The minimal polynomial and the characteristic polynomial coincide
         and are known (see Alizadeh, Example 11.11) for all elements of
         the spin factor algebra that aren't scalar multiples of the
-        identity::
+        identity. We require the dimension of the algebra to be at least
+        two here so that said elements actually exist::
 
             sage: set_random_seed()
 
             sage: set_random_seed()
-            sage: n = ZZ.random_element(2,10)
+            sage: n_max = max(2, JordanSpinEJA._max_test_case_size())
+            sage: n = ZZ.random_element(2, n_max)
             sage: J = JordanSpinEJA(n)
             sage: y = J.random_element()
             sage: while y == y.coefficient(0)*J.one():
             ....:     y = J.random_element()
             sage: J = JordanSpinEJA(n)
             sage: y = J.random_element()
             sage: while y == y.coefficient(0)*J.one():
             ....:     y = J.random_element()
-            sage: y0 = y.vector()[0]
-            sage: y_bar = y.vector()[1:]
+            sage: y0 = y.to_vector()[0]
+            sage: y_bar = y.to_vector()[1:]
             sage: actual = y.minimal_polynomial()
             sage: t = PolynomialRing(J.base_ring(),'t').gen(0)
             sage: expected = t^2 - 2*y0*t + (y0^2 - norm(y_bar)^2)
             sage: actual = y.minimal_polynomial()
             sage: t = PolynomialRing(J.base_ring(),'t').gen(0)
             sage: expected = t^2 - 2*y0*t + (y0^2 - norm(y_bar)^2)
@@ -781,7 +747,34 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
             sage: x.apply_univariate_polynomial(p)
             0
 
             sage: x.apply_univariate_polynomial(p)
             0
 
+        The minimal polynomial is invariant under a change of basis,
+        and in particular, a re-scaling of the basis::
+
+            sage: set_random_seed()
+            sage: n_max = RealSymmetricEJA._max_test_case_size()
+            sage: n = ZZ.random_element(1, n_max)
+            sage: J1 = RealSymmetricEJA(n,QQ)
+            sage: J2 = RealSymmetricEJA(n,QQ,False)
+            sage: X = random_matrix(QQ,n)
+            sage: X = X*X.transpose()
+            sage: x1 = J1(X)
+            sage: x2 = J2(X)
+            sage: x1.minimal_polynomial() == x2.minimal_polynomial()
+            True
+
         """
         """
+        if self.is_zero():
+            # We would generate a zero-dimensional subalgebra
+            # where the minimal polynomial would be constant.
+            # That might be correct, but only if *this* algebra
+            # is trivial too.
+            if not self.parent().is_trivial():
+                # Pretty sure we know what the minimal polynomial of
+                # the zero operator is going to be. This ensures
+                # consistency of e.g. the polynomial variable returned
+                # in the "normal" case without us having to think about it.
+                return self.operator().minimal_polynomial()
+
         A = self.subalgebra_generated_by()
         return A(self).operator().minimal_polynomial()
 
         A = self.subalgebra_generated_by()
         return A(self).operator().minimal_polynomial()
 
@@ -806,7 +799,7 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
 
             sage: J = ComplexHermitianEJA(3)
             sage: J.one()
 
             sage: J = ComplexHermitianEJA(3)
             sage: J.one()
-            e0 + e5 + e8
+            e0 + e3 + e8
             sage: J.one().natural_representation()
             [1 0 0 0 0 0]
             [0 1 0 0 0 0]
             sage: J.one().natural_representation()
             [1 0 0 0 0 0]
             [0 1 0 0 0 0]
@@ -819,7 +812,7 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
 
             sage: J = QuaternionHermitianEJA(3)
             sage: J.one()
 
             sage: J = QuaternionHermitianEJA(3)
             sage: J.one()
-            e0 + e9 + e14
+            e0 + e5 + e14
             sage: J.one().natural_representation()
             [1 0 0 0 0 0 0 0 0 0 0 0]
             [0 1 0 0 0 0 0 0 0 0 0 0]
             sage: J.one().natural_representation()
             [1 0 0 0 0 0 0 0 0 0 0 0]
             [0 1 0 0 0 0 0 0 0 0 0 0]
@@ -836,8 +829,35 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
 
         """
         B = self.parent().natural_basis()
 
         """
         B = self.parent().natural_basis()
-        W = B[0].matrix_space()
-        return W.linear_combination(zip(self.vector(), B))
+        W = self.parent().natural_basis_space()
+        return W.linear_combination(zip(B,self.to_vector()))
+
+
+    def norm(self):
+        """
+        The norm of this element with respect to :meth:`inner_product`.
+
+        SETUP::
+
+            sage: from mjo.eja.eja_algebra import (JordanSpinEJA,
+            ....:                                  RealCartesianProductEJA)
+
+        EXAMPLES::
+
+            sage: J = RealCartesianProductEJA(2)
+            sage: x = sum(J.gens())
+            sage: x.norm()
+            sqrt(2)
+
+        ::
+
+            sage: J = JordanSpinEJA(4)
+            sage: x = sum(J.gens())
+            sage: x.norm()
+            2
+
+        """
+        return self.inner_product(self).sqrt()
 
 
     def operator(self):
 
 
     def operator(self):
@@ -853,8 +873,7 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
 
             sage: set_random_seed()
             sage: J = random_eja()
 
             sage: set_random_seed()
             sage: J = random_eja()
-            sage: x = J.random_element()
-            sage: y = J.random_element()
+            sage: x,y = J.random_elements(2)
             sage: x.operator()(y) == x*y
             True
             sage: y.operator()(x) == x*y
             sage: x.operator()(y) == x*y
             True
             sage: y.operator()(x) == x*y
@@ -862,11 +881,12 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
 
         """
         P = self.parent()
 
         """
         P = self.parent()
-        fda_elt = FiniteDimensionalAlgebraElement(P, self)
+        left_mult_by_self = lambda y: self*y
+        L = P.module_morphism(function=left_mult_by_self, codomain=P)
         return FiniteDimensionalEuclideanJordanAlgebraOperator(
                  P,
                  P,
         return FiniteDimensionalEuclideanJordanAlgebraOperator(
                  P,
                  P,
-                 fda_elt.matrix().transpose() )
+                 L.matrix() )
 
 
     def quadratic_representation(self, other=None):
 
 
     def quadratic_representation(self, other=None):
@@ -884,10 +904,9 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
         Alizadeh's Example 11.12::
 
             sage: set_random_seed()
         Alizadeh's Example 11.12::
 
             sage: set_random_seed()
-            sage: n = ZZ.random_element(1,10)
-            sage: J = JordanSpinEJA(n)
-            sage: x = J.random_element()
-            sage: x_vec = x.vector()
+            sage: x = JordanSpinEJA.random_instance().random_element()
+            sage: x_vec = x.to_vector()
+            sage: n = x_vec.degree()
             sage: x0 = x_vec[0]
             sage: x_bar = x_vec[1:]
             sage: A = matrix(QQ, 1, [x_vec.inner_product(x_vec)])
             sage: x0 = x_vec[0]
             sage: x_bar = x_vec[1:]
             sage: A = matrix(QQ, 1, [x_vec.inner_product(x_vec)])
@@ -904,8 +923,7 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
 
             sage: set_random_seed()
             sage: J = random_eja()
 
             sage: set_random_seed()
             sage: J = random_eja()
-            sage: x = J.random_element()
-            sage: y = J.random_element()
+            sage: x,y = J.random_elements(2)
             sage: Lx = x.operator()
             sage: Lxx = (x*x).operator()
             sage: Qx = x.quadratic_representation()
             sage: Lx = x.operator()
             sage: Lxx = (x*x).operator()
             sage: Qx = x.quadratic_representation()
@@ -922,7 +940,7 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
 
         Property 2 (multiply on the right for :trac:`28272`):
 
 
         Property 2 (multiply on the right for :trac:`28272`):
 
-            sage: alpha = QQ.random_element()
+            sage: alpha = J.base_ring().random_element()
             sage: (alpha*x).quadratic_representation() == Qx*(alpha^2)
             True
 
             sage: (alpha*x).quadratic_representation() == Qx*(alpha^2)
             True
 
@@ -1000,11 +1018,15 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
 
             sage: from mjo.eja.eja_algebra import random_eja
 
 
             sage: from mjo.eja.eja_algebra import random_eja
 
-        TESTS::
+        TESTS:
+
+        This subalgebra, being composed of only powers, is associative::
 
             sage: set_random_seed()
 
             sage: set_random_seed()
-            sage: x = random_eja().random_element()
-            sage: x.subalgebra_generated_by().is_associative()
+            sage: x0 = random_eja().random_element()
+            sage: A = x0.subalgebra_generated_by()
+            sage: x,y,z = A.random_elements(3)
+            sage: (x*y)*z == x*(y*z)
             True
 
         Squaring in the subalgebra should work the same as in
             True
 
         Squaring in the subalgebra should work the same as in
@@ -1016,6 +1038,15 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
             sage: A(x^2) == A(x)*A(x)
             True
 
             sage: A(x^2) == A(x)*A(x)
             True
 
+        The subalgebra generated by the zero element is trivial::
+
+            sage: set_random_seed()
+            sage: A = random_eja().zero().subalgebra_generated_by()
+            sage: A
+            Euclidean Jordan algebra of dimension 0 over...
+            sage: A.one()
+            0
+
         """
         return FiniteDimensionalEuclideanJordanElementSubalgebra(self)
 
         """
         return FiniteDimensionalEuclideanJordanElementSubalgebra(self)
 
@@ -1070,7 +1101,7 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
         # Our FiniteDimensionalAlgebraElement superclass uses rows.
         u_next = u**(s+1)
         A = u_next.operator().matrix()
         # Our FiniteDimensionalAlgebraElement superclass uses rows.
         u_next = u**(s+1)
         A = u_next.operator().matrix()
-        c = J(A.solve_right(u_next.vector()))
+        c = J.from_vector(A.solve_right(u_next.to_vector()))
 
         # Now c is the idempotent we want, but it still lives in the subalgebra.
         return c.superalgebra_element()
 
         # Now c is the idempotent we want, but it still lives in the subalgebra.
         return c.superalgebra_element()
@@ -1105,7 +1136,7 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
 
             sage: set_random_seed()
             sage: J = random_eja()
 
             sage: set_random_seed()
             sage: J = random_eja()
-            sage: J.random_element().trace() in J.base_ring()
+            sage: J.random_element().trace() in RLF
             True
 
         """
             True
 
         """
@@ -1116,7 +1147,7 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
         # -1 to ensure that _charpoly_coeff(r-1) is really what
         # appears in front of t^{r-1} in the charpoly. However,
         # we want the negative of THAT for the trace.
         # -1 to ensure that _charpoly_coeff(r-1) is really what
         # appears in front of t^{r-1} in the charpoly. However,
         # we want the negative of THAT for the trace.
-        return -p(*self.vector())
+        return -p(*self.to_vector())
 
 
     def trace_inner_product(self, other):
 
 
     def trace_inner_product(self, other):
@@ -1129,22 +1160,17 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
 
         TESTS:
 
 
         TESTS:
 
-        The trace inner product is commutative::
+        The trace inner product is commutative, bilinear, and satisfies
+        the Jordan axiom:
 
             sage: set_random_seed()
             sage: J = random_eja()
 
             sage: set_random_seed()
             sage: J = random_eja()
-            sage: x = J.random_element(); y = J.random_element()
+            sage: x,y,z = J.random_elements(3)
+            sage: # commutative
             sage: x.trace_inner_product(y) == y.trace_inner_product(x)
             True
             sage: x.trace_inner_product(y) == y.trace_inner_product(x)
             True
-
-        The trace inner product is bilinear::
-
-            sage: set_random_seed()
-            sage: J = random_eja()
-            sage: x = J.random_element()
-            sage: y = J.random_element()
-            sage: z = J.random_element()
-            sage: a = QQ.random_element();
+            sage: # bilinear
+            sage: a = J.base_ring().random_element();
             sage: actual = (a*(x+z)).trace_inner_product(y)
             sage: expected = ( a*x.trace_inner_product(y) +
             ....:              a*z.trace_inner_product(y) )
             sage: actual = (a*(x+z)).trace_inner_product(y)
             sage: expected = ( a*x.trace_inner_product(y) +
             ....:              a*z.trace_inner_product(y) )
@@ -1155,15 +1181,7 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
             ....:              a*x.trace_inner_product(z) )
             sage: actual == expected
             True
             ....:              a*x.trace_inner_product(z) )
             sage: actual == expected
             True
-
-        The trace inner product satisfies the compatibility
-        condition in the definition of a Euclidean Jordan algebra::
-
-            sage: set_random_seed()
-            sage: J = random_eja()
-            sage: x = J.random_element()
-            sage: y = J.random_element()
-            sage: z = J.random_element()
+            sage: # jordan axiom
             sage: (x*y).trace_inner_product(z) == y.trace_inner_product(x*z)
             True
 
             sage: (x*y).trace_inner_product(z) == y.trace_inner_product(x*z)
             True
 
@@ -1172,3 +1190,30 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
             raise TypeError("'other' must live in the same algebra")
 
         return (self*other).trace()
             raise TypeError("'other' must live in the same algebra")
 
         return (self*other).trace()
+
+
+    def trace_norm(self):
+        """
+        The norm of this element with respect to :meth:`trace_inner_product`.
+
+        SETUP::
+
+            sage: from mjo.eja.eja_algebra import (JordanSpinEJA,
+            ....:                                  RealCartesianProductEJA)
+
+        EXAMPLES::
+
+            sage: J = RealCartesianProductEJA(2)
+            sage: x = sum(J.gens())
+            sage: x.trace_norm()
+            sqrt(2)
+
+        ::
+
+            sage: J = JordanSpinEJA(4)
+            sage: x = sum(J.gens())
+            sage: x.trace_norm()
+            2*sqrt(2)
+
+        """
+        return self.trace_inner_product(self).sqrt()