using the usual ``cartesian_product()`` function; as a result, we
support (up to isomorphism) all Euclidean Jordan algebras.
+At a minimum, the following are required to construct a Euclidean
+Jordan algebra:
+
+ * A basis of matrices, column vectors, or MatrixAlgebra elements
+ * A Jordan product defined on the basis
+ * Its inner product defined on the basis
+
+The real numbers form a Euclidean Jordan algebra when both the Jordan
+and inner products are the usual multiplication. We use this as our
+example, and demonstrate a few ways to construct an EJA.
+
+First, we can use one-by-one SageMath matrices with algebraic real
+entries to represent real numbers. We define the Jordan and inner
+products to be essentially real-number multiplication, with the only
+difference being that the Jordan product again returns a one-by-one
+matrix, whereas the inner product must return a scalar. Our basis for
+the one-by-one matrices is of course the set consisting of a single
+matrix with its sole entry non-zero::
+
+ sage: from mjo.eja.eja_algebra import FiniteDimensionalEJA
+ sage: jp = lambda X,Y: X*Y
+ sage: ip = lambda X,Y: X[0,0]*Y[0,0]
+ sage: b1 = matrix(AA, [[1]])
+ sage: J1 = FiniteDimensionalEJA((b1,), jp, ip)
+ sage: J1
+ Euclidean Jordan algebra of dimension 1 over Algebraic Real Field
+
+In fact, any positive scalar multiple of that inner-product would work::
+
+ sage: ip2 = lambda X,Y: 16*ip(X,Y)
+ sage: J2 = FiniteDimensionalEJA((b1,), jp, ip2)
+ sage: J2
+ Euclidean Jordan algebra of dimension 1 over Algebraic Real Field
+
+But beware that your basis will be orthonormalized _with respect to the
+given inner-product_ unless you pass ``orthonormalize=False`` to the
+constructor. For example::
+
+ sage: J3 = FiniteDimensionalEJA((b1,), jp, ip2, orthonormalize=False)
+ sage: J3
+ Euclidean Jordan algebra of dimension 1 over Algebraic Real Field
+
+To see the difference, you can take the first and only basis element
+of the resulting algebra, and ask for it to be converted back into
+matrix form::
+
+ sage: J1.basis()[0].to_matrix()
+ [1]
+ sage: J2.basis()[0].to_matrix()
+ [1/4]
+ sage: J3.basis()[0].to_matrix()
+ [1]
+
+Since square roots are used in that process, the default scalar field
+that we use is the field of algebraic real numbers, ``AA``. You can
+also Use rational numbers, but only if you either pass
+``orthonormalize=False`` or know that orthonormalizing your basis
+won't stray beyond the rational numbers. The example above would
+have worked only because ``sqrt(16) == 4`` is rational.
+
+Another option for your basis is to use elemebts of a
+:class:`MatrixAlgebra`::
+
+ sage: from mjo.matrix_algebra import MatrixAlgebra
+ sage: A = MatrixAlgebra(1,AA,AA)
+ sage: J4 = FiniteDimensionalEJA(A.gens(), jp, ip)
+ sage: J4
+ Euclidean Jordan algebra of dimension 1 over Algebraic Real Field
+ sage: J4.basis()[0].to_matrix()
+ +---+
+ | 1 |
+ +---+
+
+An easier way to view the entire EJA basis in its original (but
+perhaps orthonormalized) matrix form is to use the ``matrix_basis``
+method::
+
+ sage: J4.matrix_basis()
+ (+---+
+ | 1 |
+ +---+,)
+
+In particular, a :class:`MatrixAlgebra` is needed to work around the
+fact that matrices in SageMath must have entries in the same
+(commutative and associative) ring as its scalars. There are many
+Euclidean Jordan algebras whose elements are matrices that violate
+those assumptions. The complex, quaternion, and octonion Hermitian
+matrices all have entries in a ring (the complex numbers, quaternions,
+or octonions...) that differs from the algebra's scalar ring (the real
+numbers). Quaternions are also non-commutative; the octonions are
+neither commutative nor associative.
+
SETUP::
sage: from mjo.eja.eja_algebra import random_eja
# written out as "long vectors."
V = VectorSpace(field, degree)
- # The matrix that will hole the orthonormal -> unorthonormal
- # coordinate transformation.
- self._deortho_matrix = None
+ # The matrix that will hold the orthonormal -> unorthonormal
+ # coordinate transformation. Default to an identity matrix of
+ # the appropriate size to avoid special cases for None
+ # everywhere.
+ self._deortho_matrix = matrix.identity(field,n)
if orthonormalize:
# Save a copy of the un-orthonormalized basis for later.
a = ( a_i.change_ring(self.base_ring())
for a_i in self._rational_algebra._charpoly_coefficients() )
- if self._deortho_matrix is None:
- # This can happen if our base ring was, say, AA and we
- # chose not to (or didn't need to) orthonormalize. It's
- # still faster to do the computations over QQ even if
- # the numbers in the boxes stay the same.
- return tuple(a)
-
# Otherwise, convert the coordinate variables back to the
# deorthonormalized ones.
R = self.coordinate_polynomial_ring()
check_field=False,
check_axioms=False)
+ # Since we don't (re)orthonormalize the basis, the FDEJA
+ # constructor is going to set self._deortho_matrix to the
+ # identity matrix. Here we set it to the correct value using
+ # the deortho matrices from our factors.
+ self._deortho_matrix = matrix.block_diagonal( [J._deortho_matrix
+ for J in factors] )
+
self.rank.set_cache(sum(J.rank() for J in factors))
ones = tuple(J.one().to_matrix() for J in factors)
self.one.set_cache(self(ones))