from sage.matrix.constructor import matrix from mjo.eja.eja_algebra import FiniteDimensionalEuclideanJordanAlgebra from mjo.eja.eja_element import FiniteDimensionalEuclideanJordanAlgebraElement class FiniteDimensionalEuclideanJordanSubalgebraElement(FiniteDimensionalEuclideanJordanAlgebraElement): """ SETUP:: sage: from mjo.eja.eja_algebra import random_eja TESTS:: The matrix representation of an element in the subalgebra is the same as its matrix representation in the superalgebra:: sage: set_random_seed() sage: A = random_eja().random_element().subalgebra_generated_by() sage: y = A.random_element() sage: actual = y.to_matrix() sage: expected = y.superalgebra_element().to_matrix() sage: actual == expected True The left-multiplication-by operator for elements in the subalgebra works like it does in the superalgebra, even if we orthonormalize our basis:: sage: set_random_seed() sage: x = random_eja(AA).random_element() sage: A = x.subalgebra_generated_by(orthonormalize_basis=True) sage: y = A.random_element() sage: y.operator()(A.one()) == y True """ def superalgebra_element(self): """ Return the object in our algebra's superalgebra that corresponds to myself. SETUP:: sage: from mjo.eja.eja_algebra import (RealSymmetricEJA, ....: random_eja) EXAMPLES:: sage: J = RealSymmetricEJA(3) sage: x = sum(J.gens()) sage: x e0 + e1 + e2 + e3 + e4 + e5 sage: A = x.subalgebra_generated_by() sage: A(x) f1 sage: A(x).superalgebra_element() e0 + e1 + e2 + e3 + e4 + e5 sage: y = sum(A.gens()) sage: y f0 + f1 sage: B = y.subalgebra_generated_by() sage: B(y) g1 sage: B(y).superalgebra_element() f0 + f1 TESTS: We can convert back and forth faithfully:: sage: set_random_seed() sage: J = random_eja() sage: x = J.random_element() sage: A = x.subalgebra_generated_by() sage: A(x).superalgebra_element() == x True sage: y = A.random_element() sage: A(y.superalgebra_element()) == y True sage: B = y.subalgebra_generated_by() sage: B(y).superalgebra_element() == y True """ # As with the _element_constructor_() method on the # algebra... even in a subspace of a subspace, the basis # elements belong to the ambient space. As a result, only one # level of coordinate_vector() is needed, regardless of how # deeply we're nested. W = self.parent().vector_space() V = self.parent().superalgebra().vector_space() # Multiply on the left because basis_matrix() is row-wise. ambient_coords = self.to_vector()*W.basis_matrix() V_coords = V.coordinate_vector(ambient_coords) return self.parent().superalgebra().from_vector(V_coords) class FiniteDimensionalEuclideanJordanSubalgebra(FiniteDimensionalEuclideanJordanAlgebra): """ A subalgebra of an EJA with a given basis. SETUP:: sage: from mjo.eja.eja_algebra import (ComplexHermitianEJA, ....: JordanSpinEJA, ....: RealSymmetricEJA) sage: from mjo.eja.eja_subalgebra import FiniteDimensionalEuclideanJordanSubalgebra EXAMPLES: The following Peirce subalgebras of the 2-by-2 real symmetric matrices do not contain the superalgebra's identity element:: sage: J = RealSymmetricEJA(2) sage: E11 = matrix(AA, [ [1,0], ....: [0,0] ]) sage: E22 = matrix(AA, [ [0,0], ....: [0,1] ]) sage: K1 = FiniteDimensionalEuclideanJordanSubalgebra(J, (J(E11),)) sage: K1.one().to_matrix() [1 0] [0 0] sage: K2 = FiniteDimensionalEuclideanJordanSubalgebra(J, (J(E22),)) sage: K2.one().to_matrix() [0 0] [0 1] TESTS: Ensure that our generator names don't conflict with the superalgebra:: sage: J = JordanSpinEJA(3) sage: J.one().subalgebra_generated_by().gens() (f0,) sage: J = JordanSpinEJA(3, prefix='f') sage: J.one().subalgebra_generated_by().gens() (g0,) sage: J = JordanSpinEJA(3, prefix='b') sage: J.one().subalgebra_generated_by().gens() (c0,) Ensure that we can find subalgebras of subalgebras:: sage: A = ComplexHermitianEJA(3).one().subalgebra_generated_by() sage: B = A.one().subalgebra_generated_by() sage: B.dimension() 1 """ def __init__(self, superalgebra, basis, category=None, check_axioms=True): self._superalgebra = superalgebra V = self._superalgebra.vector_space() field = self._superalgebra.base_ring() if category is None: category = self._superalgebra.category() # A half-assed attempt to ensure that we don't collide with # the superalgebra's prefix (ignoring the fact that there # could be super-superelgrbas in scope). If possible, we # try to "increment" the parent algebra's prefix, although # this idea goes out the window fast because some prefixen # are off-limits. prefixen = [ 'f', 'g', 'h', 'a', 'b', 'c', 'd' ] try: prefix = prefixen[prefixen.index(self._superalgebra.prefix()) + 1] except ValueError: prefix = prefixen[0] # If our superalgebra is a subalgebra of something else, then # these vectors won't have the right coordinates for # V.span_of_basis() unless we use V.from_vector() on them. W = V.span_of_basis( V.from_vector(b.to_vector()) for b in basis ) n = len(basis) mult_table = [[W.zero() for i in range(n)] for j in range(n)] ip_table = [ [ self._superalgebra.inner_product(basis[i],basis[j]) for i in range(n) ] for j in range(n) ] for i in range(n): for j in range(n): product = basis[i]*basis[j] # product.to_vector() might live in a vector subspace # if our parent algebra is already a subalgebra. We # use V.from_vector() to make it "the right size" in # that case. product_vector = V.from_vector(product.to_vector()) mult_table[i][j] = W.coordinate_vector(product_vector) self._inner_product_matrix = matrix(field, ip_table) matrix_basis = tuple( b.to_matrix() for b in basis ) self._vector_space = W fdeja = super(FiniteDimensionalEuclideanJordanSubalgebra, self) fdeja.__init__(field, mult_table, prefix=prefix, category=category, matrix_basis=matrix_basis, check_field=False, check_axioms=check_axioms) def _element_constructor_(self, elt): """ Construct an element of this subalgebra from the given one. The only valid arguments are elements of the parent algebra that happen to live in this subalgebra. SETUP:: sage: from mjo.eja.eja_algebra import RealSymmetricEJA sage: from mjo.eja.eja_subalgebra import FiniteDimensionalEuclideanJordanSubalgebra EXAMPLES:: sage: J = RealSymmetricEJA(3) sage: X = matrix(AA, [ [0,0,1], ....: [0,1,0], ....: [1,0,0] ]) sage: x = J(X) sage: basis = ( x, x^2 ) # x^2 is the identity matrix sage: K = FiniteDimensionalEuclideanJordanSubalgebra(J, basis) sage: K(J.one()) f1 sage: K(J.one() + x) f0 + f1 :: """ if elt not in self.superalgebra(): raise ValueError("not an element of this subalgebra") # The extra hackery is because foo.to_vector() might not live # in foo.parent().vector_space()! Subspaces of subspaces still # have user bases in the ambient space, though, so only one # level of coordinate_vector() is needed. In other words, if V # is itself a subspace, the basis elements for W will be of # the same length as the basis elements for V -- namely # whatever the dimension of the ambient (parent of V?) space is. V = self.superalgebra().vector_space() W = self.vector_space() # Multiply on the left because basis_matrix() is row-wise. ambient_coords = elt.to_vector()*V.basis_matrix() W_coords = W.coordinate_vector(ambient_coords) return self.from_vector(W_coords) def matrix_space(self): """ Return the matrix space of this algebra, which is identical to that of its superalgebra. This is correct "by definition," and avoids a mismatch when the subalgebra is trivial (with no matrix basis elements to infer anything from) and the parent is not. """ return self.superalgebra().matrix_space() def superalgebra(self): """ Return the superalgebra that this algebra was generated from. """ return self._superalgebra def vector_space(self): """ SETUP:: sage: from mjo.eja.eja_algebra import RealSymmetricEJA sage: from mjo.eja.eja_subalgebra import FiniteDimensionalEuclideanJordanSubalgebra EXAMPLES:: sage: J = RealSymmetricEJA(3) sage: E11 = matrix(ZZ, [ [1,0,0], ....: [0,0,0], ....: [0,0,0] ]) sage: E22 = matrix(ZZ, [ [0,0,0], ....: [0,1,0], ....: [0,0,0] ]) sage: b1 = J(E11) sage: b2 = J(E22) sage: basis = (b1, b2) sage: K = FiniteDimensionalEuclideanJordanSubalgebra(J,basis) sage: K.vector_space() Vector space of degree 6 and dimension 2 over... User basis matrix: [1 0 0 0 0 0] [0 0 1 0 0 0] sage: b1.to_vector() (1, 0, 0, 0, 0, 0) sage: b2.to_vector() (0, 0, 1, 0, 0, 0) """ return self._vector_space Element = FiniteDimensionalEuclideanJordanSubalgebraElement