+ raise NotImplementedError('irregular element')
+
+
+ def inner_product(self, other):
+ """
+ Return the parent algebra's inner product of myself and ``other``.
+
+ EXAMPLES:
+
+ The inner product in the Jordan spin algebra is the usual
+ inner product on `R^n` (this example only works because the
+ basis for the Jordan algebra is the standard basis in `R^n`)::
+
+ sage: J = JordanSpinSimpleEJA(3)
+ sage: x = vector(QQ,[1,2,3])
+ sage: y = vector(QQ,[4,5,6])
+ sage: x.inner_product(y)
+ 32
+ sage: J(x).inner_product(J(y))
+ 32
+
+ The inner product on `S^n` is `<X,Y> = trace(X*Y)`, where
+ multiplication is the usual matrix multiplication in `S^n`,
+ so the inner product of the identity matrix with itself
+ should be the `n`::
+
+ sage: J = RealSymmetricSimpleEJA(3)
+ sage: J.one().inner_product(J.one())
+ 3
+
+ Likewise, the inner product on `C^n` is `<X,Y> =
+ Re(trace(X*Y))`, where we must necessarily take the real
+ part because the product of Hermitian matrices may not be
+ Hermitian::
+
+ sage: J = ComplexHermitianSimpleEJA(3)
+ sage: J.one().inner_product(J.one())
+ 3
+
+ TESTS:
+
+ Ensure that we can always compute an inner product, and that
+ it gives us back a real number::
+
+ 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
+ True
+
+ """
+ P = self.parent()
+ if not other in P:
+ raise TypeError("'other' must live in the same algebra")
+
+ return P.inner_product(self, other)
+
+
+ def operator_commutes_with(self, other):
+ """
+ Return whether or not this element operator-commutes
+ with ``other``.
+
+ EXAMPLES:
+
+ The definition of a Jordan algebra says that any element
+ operator-commutes with its square::
+
+ sage: set_random_seed()
+ sage: x = random_eja().random_element()
+ sage: x.operator_commutes_with(x^2)
+ True
+
+ TESTS:
+
+ 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: lhs = u.operator_commutes_with(u*v)
+ sage: rhs = v.operator_commutes_with(u^2)
+ sage: lhs == rhs
+ True
+
+ """
+ if not other in self.parent():
+ raise TypeError("'other' must live in the same algebra")
+
+ A = self.operator_matrix()
+ B = other.operator_matrix()
+ return (A*B == B*A)
+
+
+ def det(self):
+ """
+ Return my determinant, the product of my eigenvalues.
+
+ EXAMPLES::
+
+ sage: J = JordanSpinSimpleEJA(2)
+ sage: e0,e1 = J.gens()
+ sage: x = e0 + e1
+ sage: x.det()
+ 0
+ sage: J = JordanSpinSimpleEJA(3)
+ sage: e0,e1,e2 = J.gens()
+ sage: x = e0 + e1 + e2
+ sage: x.det()
+ -1
+
+ """
+ cs = self.characteristic_polynomial().coefficients(sparse=False)
+ r = len(cs) - 1
+ if r >= 0:
+ return cs[0] * (-1)**r
+ else:
+ raise ValueError('charpoly had no coefficients')
+
+
+ def inverse(self):
+ """
+ Return the Jordan-multiplicative inverse of this element.
+
+ We can't use the superclass method because it relies on the
+ algebra being associative.
+
+ EXAMPLES:
+
+ The inverse in the spin factor algebra is given in Alizadeh's
+ Example 11.11::
+
+ sage: set_random_seed()
+ sage: n = ZZ.random_element(1,10)
+ sage: J = JordanSpinSimpleEJA(n)
+ sage: x = J.random_element()
+ sage: while x.is_zero():
+ ....: x = J.random_element()
+ sage: x_vec = x.vector()
+ sage: x0 = x_vec[0]
+ sage: x_bar = x_vec[1:]
+ sage: coeff = 1/(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)
+ True
+
+ TESTS:
+
+ The identity element is its own inverse::
+
+ sage: set_random_seed()
+ sage: J = random_eja()
+ sage: J.one().inverse() == J.one()
+ True
+
+ If an element has an inverse, it acts like one. TODO: this
+ can be a lot less ugly once ``is_invertible`` doesn't crash
+ on irregular elements::
+
+ sage: set_random_seed()
+ sage: J = random_eja()
+ sage: x = J.random_element()
+ sage: try:
+ ....: x.inverse()*x == J.one()
+ ....: except:
+ ....: True
+ True
+
+ """
+ if self.parent().is_associative():
+ elt = FiniteDimensionalAlgebraElement(self.parent(), self)
+ return elt.inverse()
+
+ # TODO: we can do better once the call to is_invertible()
+ # doesn't crash on irregular elements.
+ #if not self.is_invertible():
+ # raise ValueError('element is not invertible')
+
+ # We do this a little different than the usual recursive
+ # call to a finite-dimensional algebra element, because we
+ # wind up with an inverse that lives in the subalgebra and
+ # we need information about the parent to convert it back.
+ V = self.span_of_powers()
+ assoc_subalg = self.subalgebra_generated_by()
+ # Mis-design warning: the basis used for span_of_powers()
+ # and subalgebra_generated_by() must be the same, and in
+ # the same order!
+ elt = assoc_subalg(V.coordinates(self.vector()))
+
+ # This will be in the subalgebra's coordinates...
+ fda_elt = FiniteDimensionalAlgebraElement(assoc_subalg, elt)
+ subalg_inverse = fda_elt.inverse()
+
+ # So we have to convert back...
+ basis = [ self.parent(v) for v in V.basis() ]
+ pairs = zip(subalg_inverse.vector(), basis)
+ return self.parent().linear_combination(pairs)
+
+
+ def is_invertible(self):
+ """
+ Return whether or not this element is invertible.
+
+ We can't use the superclass method because it relies on
+ the algebra being associative.
+ """
+ return not self.det().is_zero()