@cached_method
def characteristic_polynomial(self):
"""
+
+ .. WARNING::
+
+ This implementation doesn't guarantee that the polynomial
+ denominator in the coefficients is not identically zero, so
+ theoretically it could crash. The way that this is handled
+ in e.g. Faraut and Koranyi is to use a basis that guarantees
+ the denominator is non-zero. But, doing so requires knowledge
+ of at least one regular element, and we don't even know how
+ to do that. The trade-off is that, if we use the standard basis,
+ the resulting polynomial will accept the "usual" coordinates. In
+ other words, we don't have to do a change of basis before e.g.
+ computing the trace or determinant.
+
EXAMPLES:
The characteristic polynomial in the spin algebra is given in
sage: J = JordanSpinEJA(2)
sage: e0,e1 = J.gens()
- sage: x = e0 + e1
+ sage: x = sum( J.gens() )
sage: x.det()
0
+
+ ::
+
sage: J = JordanSpinEJA(3)
sage: e0,e1,e2 = J.gens()
- sage: x = e0 + e1 + e2
+ sage: x = sum( J.gens() )
sage: x.det()
-1
+ TESTS:
+
+ An element is invertible if and only if its determinant is
+ non-zero::
+
+ sage: set_random_seed()
+ sage: x = random_eja().random_element()
+ sage: x.is_invertible() == (x.det() != 0)
+ True
+
"""
- 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')
+ P = self.parent()
+ r = P.rank()
+ p = P._charpoly_coeff(0)
+ # The _charpoly_coeff function already adds the factor of
+ # -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())
def inverse(self):
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::
+ If an element has an inverse, it acts like one::
sage: set_random_seed()
sage: J = random_eja()
sage: x = J.random_element()
- sage: try:
- ....: x.inverse()*x == J.one()
- ....: except:
- ....: True
+ sage: (not x.is_invertible()) or (x.inverse()*x == J.one())
True
"""
EXAMPLES::
sage: J = JordanSpinEJA(3)
- sage: e0,e1,e2 = J.gens()
- sage: x = e0 + e1 + e2
+ sage: x = sum(J.gens())
sage: x.trace()
2
+ ::
+
+ sage: J = RealCartesianProductEJA(5)
+ sage: J.one().trace()
+ 5
+
+ TESTS:
+
+ The trace of an element is a real number::
+
+ sage: set_random_seed()
+ sage: J = random_eja()
+ sage: J.random_element().trace() in J.base_ring()
+ True
+
"""
- cs = self.characteristic_polynomial().coefficients(sparse=False)
- if len(cs) >= 2:
- return -1*cs[-2]
- else:
- raise ValueError('charpoly had fewer than 2 coefficients')
+ P = self.parent()
+ r = P.rank()
+ p = P._charpoly_coeff(r-1)
+ # The _charpoly_coeff function already adds the factor of
+ # -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())
def trace_inner_product(self, other):
"""
Return the trace inner product of myself and ``other``.
+
+ TESTS:
+
+ The trace inner product is commutative::
+
+ sage: set_random_seed()
+ sage: J = random_eja()
+ sage: x = J.random_element(); y = J.random_element()
+ 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: actual = (a*(x+z)).trace_inner_product(y)
+ sage: expected = ( a*x.trace_inner_product(y) +
+ ....: a*z.trace_inner_product(y) )
+ sage: actual == expected
+ True
+ sage: actual = x.trace_inner_product(a*(y+z))
+ sage: expected = ( a*x.trace_inner_product(y) +
+ ....: 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: (x*y).trace_inner_product(z) == y.trace_inner_product(x*z)
+ True
+
"""
if not other in self.parent():
raise TypeError("'other' must live in the same algebra")