+ self.print_options(bracket='')
+
+ # The multiplication table we're given is necessarily in terms
+ # of vectors, because we don't have an algebra yet for
+ # anything to be an element of. However, it's faster in the
+ # long run to have the multiplication table be in terms of
+ # algebra elements. We do this after calling the superclass
+ # constructor so that from_vector() knows what to do.
+ self._multiplication_table = [ map(lambda x: self.from_vector(x), ls)
+ for ls in mult_table ]
+
+
+ def _element_constructor_(self, elt):
+ """
+ Construct an element of this algebra from its natural
+ representation.
+
+ This gets called only after the parent element _call_ method
+ fails to find a coercion for the argument.
+
+ SETUP::
+
+ sage: from mjo.eja.eja_algebra import (JordanSpinEJA,
+ ....: RealCartesianProductEJA,
+ ....: RealSymmetricEJA)
+
+ EXAMPLES:
+
+ The identity in `S^n` is converted to the identity in the EJA::
+
+ sage: J = RealSymmetricEJA(3)
+ sage: I = matrix.identity(QQ,3)
+ sage: J(I) == J.one()
+ True
+
+ This skew-symmetric matrix can't be represented in the EJA::
+
+ sage: J = RealSymmetricEJA(3)
+ sage: A = matrix(QQ,3, lambda i,j: i-j)
+ sage: J(A)
+ Traceback (most recent call last):
+ ...
+ ArithmeticError: vector is not in free module
+
+ TESTS:
+
+ Ensure that we can convert any element of the two non-matrix
+ simple algebras (whose natural representations are their usual
+ vector representations) back and forth faithfully::
+
+ sage: set_random_seed()
+ sage: J = RealCartesianProductEJA.random_instance()
+ sage: x = J.random_element()
+ sage: J(x.to_vector().column()) == x
+ True
+ sage: J = JordanSpinEJA.random_instance()
+ sage: x = J.random_element()
+ sage: J(x.to_vector().column()) == x
+ True
+
+ """
+ if elt == 0:
+ # The superclass implementation of random_element()
+ # needs to be able to coerce "0" into the algebra.
+ return self.zero()
+
+ natural_basis = self.natural_basis()
+ basis_space = natural_basis[0].matrix_space()
+ if elt not in basis_space:
+ raise ValueError("not a naturally-represented algebra element")
+
+ # Thanks for nothing! Matrix spaces aren't vector spaces in
+ # Sage, so we have to figure out its natural-basis coordinates
+ # ourselves. We use the basis space's ring instead of the
+ # element's ring because the basis space might be an algebraic
+ # closure whereas the base ring of the 3-by-3 identity matrix
+ # could be QQ instead of QQbar.
+ V = VectorSpace(basis_space.base_ring(), elt.nrows()*elt.ncols())
+ W = V.span_of_basis( _mat2vec(s) for s in natural_basis )
+ coords = W.coordinate_vector(_mat2vec(elt))
+ return self.from_vector(coords)
+
+
+ @staticmethod
+ def _max_test_case_size():
+ """
+ Return an integer "size" that is an upper bound on the size of
+ this algebra when it is used in a random test
+ case. Unfortunately, the term "size" is quite vague -- when
+ dealing with `R^n` under either the Hadamard or Jordan spin
+ product, the "size" refers to the dimension `n`. When dealing
+ with a matrix algebra (real symmetric or complex/quaternion
+ Hermitian), it refers to the size of the matrix, which is
+ far less than the dimension of the underlying vector space.
+
+ We default to five in this class, which is safe in `R^n`. The
+ matrix algebra subclasses (or any class where the "size" is
+ interpreted to be far less than the dimension) should override
+ with a smaller number.
+ """
+ return 5