from sage.matrix.constructor import matrix from mjo.eja.eja_algebra import FiniteDimensionalEJA from mjo.eja.eja_element import FiniteDimensionalEJAElement class FiniteDimensionalEJASubalgebraElement(FiniteDimensionalEJAElement): """ 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(field=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 """ return self.parent().superalgebra()(self.to_matrix()) class FiniteDimensionalEJASubalgebra(FiniteDimensionalEJA): """ 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 FiniteDimensionalEJASubalgebra 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 = FiniteDimensionalEJASubalgebra(J, (J(E11),)) sage: K1.one().to_matrix() [1 0] [0 0] sage: K2 = FiniteDimensionalEJASubalgebra(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, **kwargs): self._superalgebra = superalgebra V = self._superalgebra.vector_space() field = self._superalgebra.base_ring() # 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] # The superalgebra constructor expects these to be in original matrix # form, not algebra-element form. matrix_basis = tuple( b.to_matrix() for b in basis ) def jordan_product(x,y): return (self._superalgebra(x)*self._superalgebra(y)).to_matrix() def inner_product(x,y): return self._superalgebra(x).inner_product(self._superalgebra(y)) super().__init__(matrix_basis, jordan_product, inner_product, prefix=prefix, **kwargs) 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 FiniteDimensionalEJASubalgebra 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 = FiniteDimensionalEJASubalgebra(J, basis, orthonormalize=False) sage: K(J.one()) f1 sage: K(J.one() + x) f0 + f1 :: """ if elt in self.superalgebra(): return super()._element_constructor_(elt.to_matrix()) else: return super()._element_constructor_(elt) 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 Element = FiniteDimensionalEJASubalgebraElement