from sage.rings.all import (ZZ, QQ, AA, QQbar, RR, RLF, CLF,
PolynomialRing,
QuadraticField)
-from mjo.eja.eja_element import FiniteDimensionalEJAElement
+from mjo.eja.eja_element import (CartesianProductEJAElement,
+ FiniteDimensionalEJAElement)
from mjo.eja.eja_operator import FiniteDimensionalEJAOperator
from mjo.eja.eja_utils import _all2list
We should compute that an element subalgebra is associative even
if we circumvent the element method::
- sage: set_random_seed()
sage: J = random_eja(field=QQ,orthonormalize=False)
sage: x = J.random_element()
sage: A = x.subalgebra_generated_by(orthonormalize=False)
if orthonormalize:
# Now "self._matrix_span" is the vector space of our
- # algebra coordinates. The variables "X1", "X2",... refer
+ # algebra coordinates. The variables "X0", "X1",... refer
# to the entries of vectors in self._matrix_span. Thus to
# convert back and forth between the orthonormal
# coordinates and the given ones, we need to stick the
TESTS::
- sage: set_random_seed()
sage: J = random_eja()
sage: J(1)
Traceback (most recent call last):
TESTS::
- sage: set_random_seed()
sage: J = random_eja()
sage: n = J.dimension()
sage: bi = J.zero()
Our inner product is "associative," which means the following for
a symmetric bilinear form::
- sage: set_random_seed()
sage: J = random_eja()
sage: x,y,z = J.random_elements(3)
sage: (x*y).inner_product(z) == y.inner_product(x*z)
Ensure that this is the usual inner product for the algebras
over `R^n`::
- sage: set_random_seed()
sage: J = HadamardEJA.random_instance()
sage: x,y = J.random_elements(2)
sage: actual = x.inner_product(y)
one). This is in Faraut and Koranyi, and also my "On the
symmetry..." paper::
- sage: set_random_seed()
sage: J = BilinearFormEJA.random_instance()
sage: n = J.dimension()
sage: x = J.random_element()
The values we've presupplied to the constructors agree with
the computation::
- sage: set_random_seed()
sage: J = random_eja()
sage: J.is_associative() == J._jordan_product_is_associative()
True
Ensure that we can convert any element back and forth
faithfully between its matrix and algebra representations::
- sage: set_random_seed()
sage: J = random_eja()
sage: x = J.random_element()
sage: J(x.to_matrix()) == x
sage: J = JordanSpinEJA(3)
sage: p = J.characteristic_polynomial_of(); p
- X1^2 - X2^2 - X3^2 + (-2*t)*X1 + t^2
+ X0^2 - X1^2 - X2^2 + (-2*t)*X0 + t^2
sage: xvec = J.one().to_vector()
sage: p(*xvec)
t^2 - 2*t + 1
sage: J = HadamardEJA(2)
sage: J.coordinate_polynomial_ring()
- Multivariate Polynomial Ring in X1, X2...
+ Multivariate Polynomial Ring in X0, X1...
sage: J = RealSymmetricEJA(3,field=QQ,orthonormalize=False)
sage: J.coordinate_polynomial_ring()
- Multivariate Polynomial Ring in X1, X2, X3, X4, X5, X6...
+ Multivariate Polynomial Ring in X0, X1, X2, X3, X4, X5...
"""
- var_names = tuple( "X%d" % z for z in range(1, self.dimension()+1) )
+ var_names = tuple( "X%d" % z for z in range(self.dimension()) )
return PolynomialRing(self.base_ring(), var_names)
def inner_product(self, x, y):
Our inner product is "associative," which means the following for
a symmetric bilinear form::
- sage: set_random_seed()
sage: J = random_eja()
sage: x,y,z = J.random_elements(3)
sage: (x*y).inner_product(z) == y.inner_product(x*z)
Ensure that this is the usual inner product for the algebras
over `R^n`::
- sage: set_random_seed()
sage: J = HadamardEJA.random_instance()
sage: x,y = J.random_elements(2)
sage: actual = x.inner_product(y)
one). This is in Faraut and Koranyi, and also my "On the
symmetry..." paper::
- sage: set_random_seed()
sage: J = BilinearFormEJA.random_instance()
sage: n = J.dimension()
sage: x = J.random_element()
The identity element acts like the identity, regardless of
whether or not we orthonormalize::
- sage: set_random_seed()
sage: J = random_eja()
sage: x = J.random_element()
sage: J.one()*x == x and x*J.one() == x
::
- sage: set_random_seed()
sage: J = random_eja(field=QQ, orthonormalize=False)
sage: x = J.random_element()
sage: J.one()*x == x and x*J.one() == x
regardless of the base field and whether or not we
orthonormalize::
- sage: set_random_seed()
sage: J = random_eja()
sage: actual = J.one().operator().matrix()
sage: expected = matrix.identity(J.base_ring(), J.dimension())
::
- sage: set_random_seed()
sage: J = random_eja(field=QQ, orthonormalize=False)
sage: actual = J.one().operator().matrix()
sage: expected = matrix.identity(J.base_ring(), J.dimension())
Ensure that the cached unit element (often precomputed by
hand) agrees with the computed one::
- sage: set_random_seed()
sage: J = random_eja()
sage: cached = J.one()
sage: J.one.clear_cache()
::
- sage: set_random_seed()
sage: J = random_eja(field=QQ, orthonormalize=False)
sage: cached = J.one()
sage: J.one.clear_cache()
Every algebra decomposes trivially with respect to its identity
element::
- sage: set_random_seed()
sage: J = random_eja()
sage: J0,J5,J1 = J.peirce_decomposition(J.one())
sage: J0.dimension() == 0 and J5.dimension() == 0
elements in the two subalgebras are the projections onto their
respective subspaces of the superalgebra's identity element::
- sage: set_random_seed()
sage: J = random_eja()
sage: x = J.random_element()
sage: if not J.is_trivial():
for idx in range(count) )
+ def operator_polynomial_matrix(self):
+ r"""
+ Return the matrix of polynomials (over this algebra's
+ :meth:`coordinate_polynomial_ring`) that, when evaluated at
+ the basis coordinates of an element `x`, produces the basis
+ representation of `L_{x}`.
+
+ SETUP::
+
+ sage: from mjo.eja.eja_algebra import (HadamardEJA,
+ ....: JordanSpinEJA)
+
+ EXAMPLES::
+
+ sage: J = HadamardEJA(4)
+ sage: L_x = J.operator_polynomial_matrix()
+ sage: L_x
+ [X0 0 0 0]
+ [ 0 X1 0 0]
+ [ 0 0 X2 0]
+ [ 0 0 0 X3]
+ sage: x = J.one()
+ sage: d = zip(J.coordinate_polynomial_ring().gens(), x.to_vector())
+ sage: L_x.subs(dict(d))
+ [1 0 0 0]
+ [0 1 0 0]
+ [0 0 1 0]
+ [0 0 0 1]
+
+ ::
+
+ sage: J = JordanSpinEJA(4)
+ sage: L_x = J.operator_polynomial_matrix()
+ sage: L_x
+ [X0 X1 X2 X3]
+ [X1 X0 0 0]
+ [X2 0 X0 0]
+ [X3 0 0 X0]
+ sage: x = J.one()
+ sage: d = zip(J.coordinate_polynomial_ring().gens(), x.to_vector())
+ sage: L_x.subs(dict(d))
+ [1 0 0 0]
+ [0 1 0 0]
+ [0 0 1 0]
+ [0 0 0 1]
+
+ """
+ R = self.coordinate_polynomial_ring()
+
+ def L_x_i_j(i,j):
+ # From a result in my book, these are the entries of the
+ # basis representation of L_x.
+ return sum( v*self.monomial(k).operator().matrix()[i,j]
+ for (k,v) in enumerate(R.gens()) )
+
+ n = self.dimension()
+ return matrix(R, n, n, L_x_i_j)
+
@cached_method
def _charpoly_coefficients(self):
r"""
The theory shows that these are all homogeneous polynomials of
a known degree::
- sage: set_random_seed()
sage: J = random_eja()
sage: all(p.is_homogeneous() for p in J._charpoly_coefficients())
True
"""
n = self.dimension()
R = self.coordinate_polynomial_ring()
- vars = R.gens()
F = R.fraction_field()
- def L_x_i_j(i,j):
- # From a result in my book, these are the entries of the
- # basis representation of L_x.
- return sum( vars[k]*self.monomial(k).operator().matrix()[i,j]
- for k in range(n) )
-
- L_x = matrix(F, n, n, L_x_i_j)
+ L_x = self.operator_polynomial_matrix()
r = None
if self.rank.is_in_cache():
positive integer rank, unless the algebra is trivial in
which case its rank will be zero::
- sage: set_random_seed()
sage: J = random_eja()
sage: r = J.rank()
sage: r in ZZ
Ensure that computing the rank actually works, since the ranks
of all simple algebras are known and will be cached by default::
- sage: set_random_seed() # long time
sage: J = random_eja() # long time
sage: cached = J.rank() # long time
sage: J.rank.clear_cache() # long time
sage: J = JordanSpinEJA(3)
sage: J._charpoly_coefficients()
- (X1^2 - X2^2 - X3^2, -2*X1)
+ (X0^2 - X1^2 - X2^2, -2*X0)
sage: a0 = J._charpoly_coefficients()[0]
sage: J.base_ring()
Algebraic Real Field
Our basis is normalized with respect to the algebra's inner
product, unless we specify otherwise::
- sage: set_random_seed()
sage: J = ConcreteEJA.random_instance()
sage: all( b.norm() == 1 for b in J.gens() )
True
natural->EJA basis representation is an isometry and within the
EJA the operator is self-adjoint by the Jordan axiom::
- sage: set_random_seed()
sage: J = ConcreteEJA.random_instance()
sage: x = J.random_element()
sage: x.operator().is_self_adjoint()
TESTS::
- sage: set_random_seed()
sage: n = ZZ.random_element(1,5)
sage: A = MatrixSpace(QQ, n)
sage: B = MatrixEJA._denormalized_basis(A)
::
- sage: set_random_seed()
sage: n = ZZ.random_element(1,5)
sage: A = ComplexMatrixAlgebra(n, scalars=QQ)
sage: B = MatrixEJA._denormalized_basis(A)
::
- sage: set_random_seed()
sage: n = ZZ.random_element(1,5)
sage: A = QuaternionMatrixAlgebra(n, scalars=QQ)
sage: B = MatrixEJA._denormalized_basis(A)
::
- sage: set_random_seed()
sage: n = ZZ.random_element(1,5)
sage: A = OctonionMatrixAlgebra(n, scalars=QQ)
sage: B = MatrixEJA._denormalized_basis(A)
The dimension of this algebra is `(n^2 + n) / 2`::
- sage: set_random_seed()
sage: d = RealSymmetricEJA._max_random_instance_dimension()
sage: n = RealSymmetricEJA._max_random_instance_size(d)
sage: J = RealSymmetricEJA(n)
The Jordan multiplication is what we think it is::
- sage: set_random_seed()
sage: J = RealSymmetricEJA.random_instance()
sage: x,y = J.random_elements(2)
sage: actual = (x*y).to_matrix()
The dimension of this algebra is `n^2`::
- sage: set_random_seed()
sage: d = ComplexHermitianEJA._max_random_instance_dimension()
sage: n = ComplexHermitianEJA._max_random_instance_size(d)
sage: J = ComplexHermitianEJA(n)
The Jordan multiplication is what we think it is::
- sage: set_random_seed()
sage: J = ComplexHermitianEJA.random_instance()
sage: x,y = J.random_elements(2)
sage: actual = (x*y).to_matrix()
The dimension of this algebra is `2*n^2 - n`::
- sage: set_random_seed()
sage: d = QuaternionHermitianEJA._max_random_instance_dimension()
sage: n = QuaternionHermitianEJA._max_random_instance_size(d)
sage: J = QuaternionHermitianEJA(n)
The Jordan multiplication is what we think it is::
- sage: set_random_seed()
sage: J = QuaternionHermitianEJA.random_instance()
sage: x,y = J.random_elements(2)
sage: actual = (x*y).to_matrix()
matrix. We opt not to orthonormalize the basis, because if we
did, we would have to normalize the `s_{i}` in a similar manner::
- sage: set_random_seed()
sage: n = ZZ.random_element(5)
sage: M = matrix.random(QQ, max(0,n-1), algorithm='unimodular')
sage: B11 = matrix.identity(QQ,1)
Ensure that we have the usual inner product on `R^n`::
- sage: set_random_seed()
sage: J = JordanSpinEJA.random_instance()
sage: x,y = J.random_elements(2)
sage: actual = x.inner_product(y)
The Jordan product is inherited from our factors and implemented by
our CombinatorialFreeModule Cartesian product superclass::
- sage: set_random_seed()
sage: J1 = HadamardEJA(2)
sage: J2 = RealSymmetricEJA(2)
sage: J = cartesian_product([J1,J2])
The cached unit element is the same one that would be computed::
- sage: set_random_seed() # long time
sage: J1 = random_eja() # long time
sage: J2 = random_eja() # long time
sage: J = cartesian_product([J1,J2]) # long time
sage: actual == expected # long time
True
"""
+ Element = CartesianProductEJAElement
def __init__(self, factors, **kwargs):
m = len(factors)
if m == 0:
ones = tuple(J.one().to_matrix() for J in factors)
self.one.set_cache(self(ones))
+ def _sets_keys(self):
+ r"""
+
+ SETUP::
+
+ sage: from mjo.eja.eja_algebra import (ComplexHermitianEJA,
+ ....: RealSymmetricEJA)
+
+ TESTS:
+
+ The superclass uses ``_sets_keys()`` to implement its
+ ``cartesian_factors()`` method::
+
+ sage: J1 = RealSymmetricEJA(2,
+ ....: field=QQ,
+ ....: orthonormalize=False,
+ ....: prefix="a")
+ sage: J2 = ComplexHermitianEJA(2,field=QQ,orthonormalize=False)
+ sage: J = cartesian_product([J1,J2])
+ sage: x = sum(i*J.gens()[i] for i in range(len(J.gens())))
+ sage: x.cartesian_factors()
+ (a1 + 2*a2, 3*b0 + 4*b1 + 5*b2 + 6*b3)
+
+ """
+ # Copy/pasted from CombinatorialFreeModule_CartesianProduct,
+ # but returning a tuple instead of a list.
+ return tuple(range(len(self.cartesian_factors())))
+
def cartesian_factors(self):
# Copy/pasted from CombinatorialFreeModule_CartesianProduct.
return self._sets
The answer never changes::
- sage: set_random_seed()
sage: J1 = random_eja()
sage: J2 = random_eja()
sage: J = cartesian_product([J1,J2])
The answer never changes::
- sage: set_random_seed()
sage: J1 = random_eja()
sage: J2 = random_eja()
sage: J = cartesian_product([J1,J2])
produce the identity map, and mismatching them should produce
the zero map::
- sage: set_random_seed()
sage: J1 = random_eja()
sage: J2 = random_eja()
sage: J = cartesian_product([J1,J2])
TESTS::
- sage: set_random_seed()
sage: n = ZZ.random_element(1,5)
sage: J = random_eja(max_dimension=n, field=QQ, orthonormalize=False)
sage: J.dimension() <= n