]> gitweb.michael.orlitzky.com - sage.d.git/blobdiff - mjo/eja/eja_algebra.py
eja: add missing transpose() to deortho matrix.
[sage.d.git] / mjo / eja / eja_algebra.py
index db1f4769ad84068903356bc119749ad90a64dc88..845503b081ce7a9282128886fa595ca4bd7027a6 100644 (file)
@@ -52,6 +52,98 @@ the other algebras. Cartesian products of these are also supported
 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
@@ -227,9 +319,11 @@ class FiniteDimensionalEJA(CombinatorialFreeModule):
         # 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.
@@ -263,8 +357,8 @@ class FiniteDimensionalEJA(CombinatorialFreeModule):
             # coordinates and the given ones, we need to stick the original
             # basis in W.
             U = V.span_of_basis( deortho_vector_basis, check=check_axioms)
-            self._deortho_matrix = matrix( U.coordinate_vector(q)
-                                           for q in vector_basis )
+            self._deortho_matrix = matrix.column( U.coordinate_vector(q)
+                                                  for q in vector_basis )
 
 
         # Now we actually compute the multiplication and inner-product
@@ -1661,13 +1755,6 @@ class RationalBasisEJA(FiniteDimensionalEJA):
         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()
@@ -2994,6 +3081,13 @@ class CartesianProductEJA(FiniteDimensionalEJA):
                                       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))