+# -*- coding: utf-8 -*-
+
+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
Return ``self`` raised to the power ``n``.
Jordan algebras are always power-associative; see for
- example Faraut and Koranyi, Proposition II.1.2 (ii).
+ example Faraut and Korányi, Proposition II.1.2 (ii).
We have to override this because our superclass uses row
vectors instead of column vectors! We, on the other hand,
True
Ensure that the determinant is multiplicative on an associative
- subalgebra as in Faraut and Koranyi's Proposition II.2.2::
+ subalgebra as in Faraut and Korányi's Proposition II.2.2::
sage: set_random_seed()
sage: J = random_eja().random_element().subalgebra_generated_by()
SETUP::
- sage: from mjo.eja.eja_algebra import (JordanSpinEJA,
+ sage: from mjo.eja.eja_algebra import (ComplexHermitianEJA,
+ ....: JordanSpinEJA,
....: random_eja)
EXAMPLES:
sage: x.inverse() == J.from_vector(x_inverse)
True
+ Trying to invert a non-invertible element throws an error:
+
+ sage: JordanSpinEJA(3).zero().inverse()
+ Traceback (most recent call last):
+ ...
+ ValueError: element is not invertible
+
TESTS:
The identity element is its own inverse::
sage: (not x.is_invertible()) or (x.inverse().inverse() == x)
True
- The zero element is never invertible::
+ Proposition II.2.3 in Faraut and Korányi says that the inverse
+ of an element is the inverse of its left-multiplication operator
+ applied to the algebra's identity, when that inverse exists::
sage: set_random_seed()
- sage: J = random_eja().zero().inverse()
- Traceback (most recent call last):
- ...
- ValueError: element is not invertible
+ sage: J = random_eja()
+ sage: x = J.random_element()
+ sage: (not x.operator().is_invertible()) or (
+ ....: x.operator().inverse()(J.one()) == x.inverse() )
+ True
+
+ Proposition II.2.4 in Faraut and Korányi gives a formula for
+ the inverse based on the characteristic polynomial and the
+ Cayley-Hamilton theorem for Euclidean Jordan algebras::
+
+ sage: set_random_seed()
+ sage: J = ComplexHermitianEJA(3)
+ sage: x = J.random_element()
+ sage: while not x.is_invertible():
+ ....: x = J.random_element()
+ sage: r = J.rank()
+ sage: a = x.characteristic_polynomial().coefficients(sparse=False)
+ sage: expected = (-1)^(r+1)/x.det()
+ sage: expected *= sum( a[i+1]*x^i for i in range(r) )
+ sage: x.inverse() == expected
+ True
"""
if not self.is_invertible():
TESTS:
- The identity element is never nilpotent::
+ The identity element is never nilpotent, except in a trivial EJA::
sage: set_random_seed()
- sage: random_eja().one().is_nilpotent()
+ sage: J = random_eja()
+ sage: J.one().is_nilpotent() and not J.is_trivial()
False
The additive identity is always nilpotent::
TESTS:
The zero element should never be regular, unless the parent
- algebra has dimension one::
+ algebra has dimension less than or equal to one::
sage: set_random_seed()
sage: J = random_eja()
- sage: J.dimension() == 1 or not J.zero().is_regular()
+ sage: J.dimension() <= 1 or not J.zero().is_regular()
True
The unit element isn't regular unless the algebra happens to
sage: set_random_seed()
sage: J = random_eja()
- sage: J.dimension() == 1 or not J.one().is_regular()
+ sage: J.dimension() <= 1 or not J.one().is_regular()
True
"""
TESTS:
- The zero and unit elements are both of degree one::
+ The zero and unit elements are both of degree one in nontrivial
+ algebras::
sage: set_random_seed()
sage: J = random_eja()
- sage: J.zero().degree()
- 1
- sage: J.one().degree()
- 1
+ sage: d = J.zero().degree()
+ sage: (J.is_trivial() and d == 0) or d == 1
+ True
+ sage: d = J.one().degree()
+ sage: (J.is_trivial() and d == 0) or d == 1
+ True
Our implementation agrees with the definition::
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: J2 = RealSymmetricEJA(n,QQ,normalize_basis=False)
sage: X = random_matrix(QQ,n)
sage: X = X*X.transpose()
sage: x1 = J1(X)
"""
B = self.parent().natural_basis()
W = self.parent().natural_basis_space()
- return W.linear_combination(zip(B,self.to_vector()))
+ return W.linear_combination(izip(B,self.to_vector()))
def norm(self):
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:
+ def spectral_decomposition(self):
+ """
+ Return the unique spectral decomposition of this element.
+
+ ALGORITHM:
+
+ Following Faraut and Korányi's Theorem III.1.1, we restrict this
+ element's left-multiplication-by operator to the subalgebra it
+ generates. We then compute the spectral decomposition of that
+ operator, and the spectral projectors we get back must be the
+ left-multiplication-by operators for the idempotents we
+ seek. Thus applying them to the identity element gives us those
+ idempotents.
+
+ Since the eigenvalues are required to be distinct, we take
+ the spectral decomposition of the zero element to be zero
+ times the identity element of the algebra (which is idempotent,
+ obviously).
+
+ SETUP::
+
+ sage: from mjo.eja.eja_algebra import RealSymmetricEJA
+
+ EXAMPLES:
+
+ The spectral decomposition of the identity is ``1`` times itself,
+ and the spectral decomposition of zero is ``0`` times the identity::
+
+ sage: J = RealSymmetricEJA(3,AA)
+ sage: J.one()
+ e0 + e2 + e5
+ sage: J.one().spectral_decomposition()
+ [(1, e0 + e2 + e5)]
+ sage: J.zero().spectral_decomposition()
+ [(0, e0 + e2 + e5)]
+
+ TESTS::
+
+ sage: J = RealSymmetricEJA(4,AA)
+ sage: x = sum(J.gens())
+ sage: sd = x.spectral_decomposition()
+ sage: l0 = sd[0][0]
+ sage: l1 = sd[1][0]
+ sage: c0 = sd[0][1]
+ sage: c1 = sd[1][1]
+ sage: c0.inner_product(c1) == 0
+ True
+ sage: c0.is_idempotent()
+ True
+ sage: c1.is_idempotent()
+ True
+ sage: c0 + c1 == J.one()
+ True
+ sage: l0*c0 + l1*c1 == x
+ True
+
+ """
+ P = self.parent()
+ A = self.subalgebra_generated_by(orthonormalize_basis=True)
+ result = []
+ for (evalue, proj) in A(self).operator().spectral_decomposition():
+ result.append( (evalue, proj(A.one()).superalgebra_element()) )
+ return result
- 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.
+ Since our parent algebra is unital, we want "subalgebra" to mean
+ "unital subalgebra" as well; thus the subalgebra that an element
+ generates will itself be a Euclidean Jordan algebra after
+ restricting the algebra operations appropriately. This is the
+ subalgebra that Faraut and Korányi work with in section II.2, for
+ example.
+
SETUP::
sage: from mjo.eja.eja_algebra import random_eja
sage: A(x^2) == A(x)*A(x)
True
- The subalgebra generated by the zero element is trivial::
+ By definition, the subalgebra generated by the zero element is
+ the one-dimensional algebra generated by the identity
+ element... unless the original algebra was trivial, in which
+ case the subalgebra is trivial too::
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
+ sage: (A.is_trivial() and A.dimension() == 0) or A.dimension() == 1
+ True
"""
- return FiniteDimensionalEuclideanJordanElementSubalgebra(self)
+ return FiniteDimensionalEuclideanJordanElementSubalgebra(self, orthonormalize_basis)
def subalgebra_idempotent(self):
TESTS:
- The trace inner product is commutative, bilinear, and satisfies
- the Jordan axiom:
+ The trace inner product is commutative, bilinear, and associative::
sage: set_random_seed()
sage: J = random_eja()
....: a*x.trace_inner_product(z) )
sage: actual == expected
True
- sage: # jordan axiom
+ sage: # associative
sage: (x*y).trace_inner_product(z) == y.trace_inner_product(x*z)
True