]> gitweb.michael.orlitzky.com - sage.d.git/blobdiff - mjo/eja/eja_element.py
eja: add a WIP gram-schmidt for EJA elements.
[sage.d.git] / mjo / eja / eja_element.py
index 00a15a1c56897172f57aeec2d43a391f3b367a45..c2f9fe652851fa4dc2ce519856160efc221debcc 100644 (file)
@@ -1,6 +1,8 @@
-from sage.algebras.finite_dimensional_algebras.finite_dimensional_algebra_element import FiniteDimensionalAlgebraElement
+from itertools import izip
+
 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
@@ -10,7 +12,7 @@ lazy_import('mjo.eja.eja_subalgebra',
 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.
     """
@@ -25,68 +27,7 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
                       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):
         """
@@ -139,7 +80,7 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
         elif n == 1:
             return self
         else:
-            return (self.operator()**(n-1))(self)
+            return (self**(n-1))*self
 
 
     def apply_univariate_polynomial(self, p):
@@ -226,9 +167,24 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
             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()
-        return p(*self.vector())
+        return p(*self.to_vector())
 
 
     def inner_product(self, other):
@@ -255,7 +211,7 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
             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
@@ -289,9 +245,8 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
 
             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
 
         """
@@ -326,9 +281,7 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
         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
@@ -338,9 +291,7 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
         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()
@@ -352,10 +303,7 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
         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()
@@ -369,10 +317,7 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
         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()
@@ -429,6 +374,15 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
             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()
@@ -437,7 +391,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.
-        return ((-1)**r)*p(*self.vector())
+        return ((-1)**r)*p(*self.to_vector())
 
 
     def inverse(self):
@@ -460,18 +414,17 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
         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_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: x.inverse() == J(x_inverse)
+            sage: x.inverse() == J.from_vector(x_inverse)
             True
 
         TESTS:
@@ -543,15 +496,23 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
             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: J.zero().is_invertible()
+            sage: (not J.is_trivial()) and J.zero().is_invertible()
             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)
 
@@ -679,8 +640,7 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
         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
@@ -704,6 +664,11 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
             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()
 
 
@@ -732,6 +697,7 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
         SETUP::
 
             sage: from mjo.eja.eja_algebra import (JordanSpinEJA,
+            ....:                                  RealSymmetricEJA,
             ....:                                  random_eja)
 
         TESTS:
@@ -757,16 +723,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
-        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: 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: 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)
@@ -781,7 +749,34 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
             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,normalize_basis=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()
 
@@ -806,7 +801,7 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
 
             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]
@@ -819,7 +814,7 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
 
             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]
@@ -836,8 +831,35 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
 
         """
         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(izip(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):
@@ -853,8 +875,7 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
 
             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
@@ -862,11 +883,12 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
 
         """
         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,
-                 fda_elt.matrix().transpose() )
+                 L.matrix() )
 
 
     def quadratic_representation(self, other=None):
@@ -884,10 +906,9 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
         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)])
@@ -904,8 +925,7 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
 
             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()
@@ -922,7 +942,7 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
 
         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
 
@@ -950,10 +970,10 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
             sage: not x.is_invertible() or (
             ....:   x.quadratic_representation(x.inverse())*Qx
             ....:   ==
-            ....:   2*x.operator()*Qex - Qx )
+            ....:   2*Lx*Qex - Qx )
             True
 
-            sage: 2*x.operator()*Qex - Qx == Lxx
+            sage: 2*Lx*Qex - Qx == Lxx
             True
 
         Property 5:
@@ -991,7 +1011,7 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
 
 
 
-    def subalgebra_generated_by(self):
+    def subalgebra_generated_by(self, orthonormalize_basis=False):
         """
         Return the associative subalgebra of the parent EJA generated
         by this element.
@@ -1000,11 +1020,15 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
 
             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: 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
@@ -1016,8 +1040,17 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
             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, orthonormalize_basis)
 
 
     def subalgebra_idempotent(self):
@@ -1070,7 +1103,7 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
         # 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()
@@ -1105,7 +1138,7 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
 
             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
 
         """
@@ -1116,7 +1149,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.
-        return -p(*self.vector())
+        return -p(*self.to_vector())
 
 
     def trace_inner_product(self, other):
@@ -1129,22 +1162,16 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
 
         TESTS:
 
-        The trace inner product is commutative::
+        The trace inner product is commutative, bilinear, and associative::
 
             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
-
-        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) )
@@ -1155,15 +1182,7 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
             ....:              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: # associative
             sage: (x*y).trace_inner_product(z) == y.trace_inner_product(x*z)
             True
 
@@ -1172,3 +1191,30 @@ class FiniteDimensionalEuclideanJordanAlgebraElement(FiniteDimensionalAlgebraEle
             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()