--- /dev/null
+from sage.matrix.constructor import matrix
+from sage.structure.category_object import normalize_names
+
+from mjo.eja.eja_algebra import FiniteDimensionalEuclideanJordanAlgebra
+from mjo.eja.eja_element import FiniteDimensionalEuclideanJordanAlgebraElement
+
+
+class FiniteDimensionalEuclideanJordanElementSubalgebra(FiniteDimensionalEuclideanJordanAlgebra):
+ """
+ The subalgebra of an EJA generated by a single element.
+ """
+ @staticmethod
+ def __classcall_private__(cls, elt):
+ superalgebra = elt.parent()
+
+ # First compute the vector subspace spanned by the powers of
+ # the given element.
+ V = superalgebra.vector_space()
+ eja_basis = [superalgebra.one()]
+ basis_vectors = [superalgebra.one().vector()]
+ W = V.span_of_basis(basis_vectors)
+ for exponent in range(1, V.dimension()):
+ new_power = elt**exponent
+ basis_vectors.append( new_power.vector() )
+ try:
+ W = V.span_of_basis(basis_vectors)
+ eja_basis.append( new_power )
+ except ValueError:
+ # Vectors weren't independent; bail and keep the
+ # last subspace that worked.
+ break
+
+ # Make the basis hashable for UniqueRepresentation.
+ eja_basis = tuple(eja_basis)
+
+ # Now figure out the entries of the right-multiplication
+ # matrix for the successive basis elements b0, b1,... of
+ # that subspace.
+ F = superalgebra.base_ring()
+ mult_table = []
+ for b_right in eja_basis:
+ b_right_rows = []
+ # The first row of the right-multiplication matrix by
+ # b1 is what we get if we apply that matrix to b1. The
+ # second row of the right multiplication matrix by b1
+ # is what we get when we apply that matrix to b2...
+ #
+ # IMPORTANT: this assumes that all vectors are COLUMN
+ # vectors, unlike our superclass (which uses row vectors).
+ for b_left in eja_basis:
+ # Multiply in the original EJA, but then get the
+ # coordinates from the subalgebra in terms of its
+ # basis.
+ this_row = W.coordinates((b_left*b_right).vector())
+ b_right_rows.append(this_row)
+ b_right_matrix = matrix(F, b_right_rows)
+ mult_table.append(b_right_matrix)
+
+ for m in mult_table:
+ m.set_immutable()
+ mult_table = tuple(mult_table)
+
+ # The rank is the highest possible degree of a minimal
+ # polynomial, and is bounded above by the dimension. We know
+ # in this case that there's an element whose minimal
+ # polynomial has the same degree as the space's dimension
+ # (remember how we constructed the space?), so that must be
+ # its rank too.
+ rank = W.dimension()
+
+ # EJAs are power-associative, and this algebra is nothin but
+ # powers.
+ assume_associative=True
+
+ # TODO: Un-hard-code this. It should be possible to get the "next"
+ # name based on the parent's generator names.
+ names = 'f'
+ names = normalize_names(W.dimension(), names)
+
+ cat = superalgebra.category().Associative()
+
+ # TODO: compute this and actually specify it.
+ natural_basis = None
+
+ fdeja = super(FiniteDimensionalEuclideanJordanElementSubalgebra, cls)
+ return fdeja.__classcall__(cls,
+ F,
+ mult_table,
+ rank,
+ eja_basis,
+ W,
+ assume_associative=assume_associative,
+ names=names,
+ category=cat,
+ natural_basis=natural_basis)
+
+ def __init__(self,
+ field,
+ mult_table,
+ rank,
+ eja_basis,
+ vector_space,
+ assume_associative=True,
+ names='f',
+ category=None,
+ natural_basis=None):
+
+ self._superalgebra = eja_basis[0].parent()
+ self._vector_space = vector_space
+ self._eja_basis = eja_basis
+
+ fdeja = super(FiniteDimensionalEuclideanJordanElementSubalgebra, self)
+ fdeja.__init__(field,
+ mult_table,
+ rank,
+ assume_associative=assume_associative,
+ names=names,
+ category=category,
+ natural_basis=natural_basis)
+
+
+ def vector_space(self):
+ """
+ SETUP::
+
+ sage: from mjo.eja.eja_algebra import RealSymmetricEJA
+ sage: from mjo.eja.eja_subalgebra import FiniteDimensionalEuclideanJordanElementSubalgebra
+
+ EXAMPLES::
+
+ sage: J = RealSymmetricEJA(3)
+ sage: x = sum( i*J.gens()[i] for i in range(6) )
+ sage: K = FiniteDimensionalEuclideanJordanElementSubalgebra(x)
+ sage: K.vector_space()
+ Vector space of degree 6 and dimension 3 over Rational Field
+ User basis matrix:
+ [ 1 0 0 1 0 1]
+ [ 0 1 2 3 4 5]
+ [ 5 11 14 26 34 45]
+ sage: (x^0).vector()
+ (1, 0, 0, 1, 0, 1)
+ sage: (x^1).vector()
+ (0, 1, 2, 3, 4, 5)
+ sage: (x^2).vector()
+ (5, 11, 14, 26, 34, 45)
+
+ """
+ return self._vector_space
+
+
+ class Element(FiniteDimensionalEuclideanJordanAlgebraElement):
+ def __init__(self, A, elt=None):
+ """
+ SETUP::
+
+ sage: from mjo.eja.eja_algebra import RealSymmetricEJA
+ sage: from mjo.eja.eja_subalgebra import FiniteDimensionalEuclideanJordanElementSubalgebra
+
+ EXAMPLES::
+
+ sage: J = RealSymmetricEJA(3)
+ sage: x = sum( i*J.gens()[i] for i in range(6) )
+ sage: K = FiniteDimensionalEuclideanJordanElementSubalgebra(x)
+ sage: [ K(x^k) for k in range(J.rank()) ]
+ [f0, f1, f2]
+
+ ::
+
+ """
+ if elt in A._superalgebra:
+ # Try to convert a parent algebra element into a
+ # subalgebra element...
+ try:
+ coords = A.vector_space().coordinates(elt.vector())
+ elt = A(coords)
+ except AttributeError:
+ # Catches a missing method in elt.vector()
+ pass
+
+ FiniteDimensionalEuclideanJordanAlgebraElement.__init__(self,
+ A,
+ elt)