]> gitweb.michael.orlitzky.com - sage.d.git/commitdiff
eja: don't mess with the user's basis.
authorMichael Orlitzky <michael@orlitzky.com>
Tue, 2 Mar 2021 20:35:08 +0000 (15:35 -0500)
committerMichael Orlitzky <michael@orlitzky.com>
Tue, 2 Mar 2021 20:35:08 +0000 (15:35 -0500)
This is a pre-pre-prerequisite for implementing the octonion algebras.
In a generic matrix algebra, the scalar ring may not be the same ring
that the entries of the matrix comes from.

mjo/eja/DESIGN
mjo/eja/eja_algebra.py
mjo/eja/eja_utils.py

index 7607b1ceffeddabb19687a9da54e362b07c9d5f3..75302ca0f9ba4323ac634834f4be816aa8866010 100644 (file)
@@ -81,6 +81,14 @@ the same way as the octonion algebra, but for the sake of the user
 interface, we must also support at least the usual SageMath vectors
 and matrices.
 
+Note: this has one less-than-obvious consequence: we have to assume
+that the user has supplied an entirely-correct basis (with entries in
+the correct structure). We generally cannot mess witht the entries of
+his basis, or use them to figure out what (for example) the ambient
+scalar ring is. None of these are insurmountable obstacles; we just
+have to be a little careful distinguishing between what's inside the
+algebra elements and what's outside them.
+
 Basis normalization
 -------------------
 For performance reasons, we prefer the algebra constructors to
index c55061e34fb2b7a4d04f127d74148984ce21f9fa..4d0c802c38c8320a8f10f8faeb50678a85d84e95 100644 (file)
@@ -152,11 +152,6 @@ class FiniteDimensionalEJA(CombinatorialFreeModule):
                 # we've specified a real embedding.
                 raise ValueError("scalar field is not real")
 
-        from mjo.eja.eja_utils import _change_ring
-        # If the basis given to us wasn't over the field that it's
-        # supposed to be over, fix that. Or, you know, crash.
-        basis = tuple( _change_ring(b, field) for b in basis )
-
         if check_axioms:
             # Check commutativity of the Jordan and inner-products.
             # This has to be done before we build the multiplication
@@ -1917,7 +1912,7 @@ class RealSymmetricEJA(ConcreteEJA, RealMatrixEJA):
 
     """
     @classmethod
-    def _denormalized_basis(cls, n):
+    def _denormalized_basis(cls, n, field):
         """
         Return a basis for the space of real symmetric n-by-n matrices.
 
@@ -1929,7 +1924,7 @@ class RealSymmetricEJA(ConcreteEJA, RealMatrixEJA):
 
             sage: set_random_seed()
             sage: n = ZZ.random_element(1,5)
-            sage: B = RealSymmetricEJA._denormalized_basis(n)
+            sage: B = RealSymmetricEJA._denormalized_basis(n,ZZ)
             sage: all( M.is_symmetric() for M in  B)
             True
 
@@ -1939,7 +1934,7 @@ class RealSymmetricEJA(ConcreteEJA, RealMatrixEJA):
         S = []
         for i in range(n):
             for j in range(i+1):
-                Eij = matrix(ZZ, n, lambda k,l: k==i and l==j)
+                Eij = matrix(field, n, lambda k,l: k==i and l==j)
                 if i == j:
                     Sij = Eij
                 else:
@@ -1960,7 +1955,7 @@ class RealSymmetricEJA(ConcreteEJA, RealMatrixEJA):
         n = ZZ.random_element(cls._max_random_instance_size() + 1)
         return cls(n, **kwargs)
 
-    def __init__(self, n, **kwargs):
+    def __init__(self, n, field=AA, **kwargs):
         # We know this is a valid EJA, but will double-check
         # if the user passes check_axioms=True.
         if "check_axioms" not in kwargs: kwargs["check_axioms"] = False
@@ -1969,9 +1964,10 @@ class RealSymmetricEJA(ConcreteEJA, RealMatrixEJA):
         if n <= 1:
             associative = True
 
-        super().__init__(self._denormalized_basis(n),
+        super().__init__(self._denormalized_basis(n,field),
                          self.jordan_product,
                          self.trace_inner_product,
+                         field=field,
                          associative=associative,
                          **kwargs)
 
@@ -2191,7 +2187,7 @@ class ComplexHermitianEJA(ConcreteEJA, ComplexMatrixEJA):
     """
 
     @classmethod
-    def _denormalized_basis(cls, n):
+    def _denormalized_basis(cls, n, field):
         """
         Returns a basis for the space of complex Hermitian n-by-n matrices.
 
@@ -2209,15 +2205,14 @@ class ComplexHermitianEJA(ConcreteEJA, ComplexMatrixEJA):
 
             sage: set_random_seed()
             sage: n = ZZ.random_element(1,5)
-            sage: B = ComplexHermitianEJA._denormalized_basis(n)
+            sage: B = ComplexHermitianEJA._denormalized_basis(n,ZZ)
             sage: all( M.is_symmetric() for M in  B)
             True
 
         """
-        field = ZZ
-        R = PolynomialRing(field, 'z')
+        R = PolynomialRing(ZZ, 'z')
         z = R.gen()
-        F = field.extension(z**2 + 1, 'I')
+        F = ZZ.extension(z**2 + 1, 'I')
         I = F.gen(1)
 
         # This is like the symmetric case, but we need to be careful:
@@ -2248,12 +2243,12 @@ class ComplexHermitianEJA(ConcreteEJA, ComplexMatrixEJA):
                 # "erase" E_ij
                 Eij[i,j] = 0
 
-        # Since we embedded these, we can drop back to the "field" that we
-        # started with instead of the complex extension "F".
+        # Since we embedded the entries, we can drop back to the
+        # desired real "field" instead of the extension "F".
         return tuple( s.change_ring(field) for s in S )
 
 
-    def __init__(self, n, **kwargs):
+    def __init__(self, n, field=AA, **kwargs):
         # We know this is a valid EJA, but will double-check
         # if the user passes check_axioms=True.
         if "check_axioms" not in kwargs: kwargs["check_axioms"] = False
@@ -2262,9 +2257,10 @@ class ComplexHermitianEJA(ConcreteEJA, ComplexMatrixEJA):
         if n <= 1:
             associative = True
 
-        super().__init__(self._denormalized_basis(n),
+        super().__init__(self._denormalized_basis(n,field),
                          self.jordan_product,
                          self.trace_inner_product,
+                         field=field,
                          associative=associative,
                          **kwargs)
         # TODO: this could be factored out somehow, but is left here
@@ -2492,7 +2488,7 @@ class QuaternionHermitianEJA(ConcreteEJA, QuaternionMatrixEJA):
 
     """
     @classmethod
-    def _denormalized_basis(cls, n):
+    def _denormalized_basis(cls, n, field):
         """
         Returns a basis for the space of quaternion Hermitian n-by-n matrices.
 
@@ -2510,12 +2506,11 @@ class QuaternionHermitianEJA(ConcreteEJA, QuaternionMatrixEJA):
 
             sage: set_random_seed()
             sage: n = ZZ.random_element(1,5)
-            sage: B = QuaternionHermitianEJA._denormalized_basis(n)
+            sage: B = QuaternionHermitianEJA._denormalized_basis(n,ZZ)
             sage: all( M.is_symmetric() for M in B )
             True
 
         """
-        field = ZZ
         Q = QuaternionAlgebra(QQ,-1,-1)
         I,J,K = Q.gens()
 
@@ -2559,12 +2554,12 @@ class QuaternionHermitianEJA(ConcreteEJA, QuaternionMatrixEJA):
                 # "erase" E_ij
                 Eij[i,j] = 0
 
-        # Since we embedded these, we can drop back to the "field" that we
-        # started with instead of the quaternion algebra "Q".
+        # Since we embedded the entries, we can drop back to the
+        # desired real "field" instead of the quaternion algebra "Q".
         return tuple( s.change_ring(field) for s in S )
 
 
-    def __init__(self, n, **kwargs):
+    def __init__(self, n, field=AA, **kwargs):
         # We know this is a valid EJA, but will double-check
         # if the user passes check_axioms=True.
         if "check_axioms" not in kwargs: kwargs["check_axioms"] = False
@@ -2573,9 +2568,10 @@ class QuaternionHermitianEJA(ConcreteEJA, QuaternionMatrixEJA):
         if n <= 1:
             associative = True
 
-        super().__init__(self._denormalized_basis(n),
+        super().__init__(self._denormalized_basis(n,field),
                          self.jordan_product,
                          self.trace_inner_product,
+                         field=field,
                          associative=associative,
                          **kwargs)
 
@@ -2643,7 +2639,7 @@ class HadamardEJA(ConcreteEJA):
         (r0, r1, r2)
 
     """
-    def __init__(self, n, **kwargs):
+    def __init__(self, n, field=AA, **kwargs):
         if n == 0:
             jordan_product = lambda x,y: x
             inner_product = lambda x,y: x
@@ -2664,10 +2660,12 @@ class HadamardEJA(ConcreteEJA):
         if "orthonormalize" not in kwargs: kwargs["orthonormalize"] = False
         if "check_axioms" not in kwargs: kwargs["check_axioms"] = False
 
-        column_basis = tuple( b.column() for b in FreeModule(ZZ, n).basis() )
+        column_basis = tuple( b.column()
+                              for b in FreeModule(field, n).basis() )
         super().__init__(column_basis,
                          jordan_product,
                          inner_product,
+                         field=field,
                          associative=True,
                          **kwargs)
         self.rank.set_cache(n)
@@ -2775,7 +2773,7 @@ class BilinearFormEJA(ConcreteEJA):
         True
 
     """
-    def __init__(self, B, **kwargs):
+    def __init__(self, B, field=AA, **kwargs):
         # The matrix "B" is supplied by the user in most cases,
         # so it makes sense to check whether or not its positive-
         # definite unless we are specifically asked not to...
@@ -2803,7 +2801,8 @@ class BilinearFormEJA(ConcreteEJA):
             return P([z0] + zbar.list())
 
         n = B.nrows()
-        column_basis = tuple( b.column() for b in FreeModule(ZZ, n).basis() )
+        column_basis = tuple( b.column()
+                              for b in FreeModule(field, n).basis() )
 
         # TODO: I haven't actually checked this, but it seems legit.
         associative = False
@@ -2813,6 +2812,7 @@ class BilinearFormEJA(ConcreteEJA):
         super().__init__(column_basis,
                          jordan_product,
                          inner_product,
+                         field=field,
                          associative=associative,
                          **kwargs)
 
@@ -2908,7 +2908,7 @@ class JordanSpinEJA(BilinearFormEJA):
             True
 
     """
-    def __init__(self, n, **kwargs):
+    def __init__(self, n, *args, **kwargs):
         # This is a special case of the BilinearFormEJA with the
         # identity matrix as its bilinear form.
         B = matrix.identity(ZZ, n)
@@ -2919,7 +2919,7 @@ class JordanSpinEJA(BilinearFormEJA):
 
         # But also don't pass check_field=False here, because the user
         # can pass in a field!
-        super().__init__(B, **kwargs)
+        super().__init__(B, *args, **kwargs)
 
     @staticmethod
     def _max_random_instance_size():
index 81b5634bc921736b0d007f3df04013d0dc6df265..832dcef1fac0baa573b4883bc4e2ddd3fbfd55a8 100644 (file)
@@ -2,57 +2,6 @@ from sage.functions.other import sqrt
 from sage.matrix.constructor import matrix
 from sage.modules.free_module_element import vector
 
-def _change_ring(x, R):
-    r"""
-    Change the ring of a vector, matrix, or a cartesian product of
-    those things.
-
-    SETUP::
-
-        sage: from mjo.eja.eja_utils import _change_ring
-
-    EXAMPLES::
-
-        sage: v = vector(QQ, (1,2,3))
-        sage: m = matrix(QQ, [[1,2],[3,4]])
-        sage: _change_ring(v, RDF)
-        (1.0, 2.0, 3.0)
-        sage: _change_ring(m, RDF)
-        [1.0 2.0]
-        [3.0 4.0]
-        sage: _change_ring((v,m), RDF)
-        (
-                         [1.0 2.0]
-        (1.0, 2.0, 3.0), [3.0 4.0]
-        )
-        sage: V1 = cartesian_product([v.parent(), v.parent()])
-        sage: V = cartesian_product([v.parent(), V1])
-        sage: V((v, (v, v)))
-        ((1, 2, 3), ((1, 2, 3), (1, 2, 3)))
-        sage: _change_ring(V((v, (v, v))), RDF)
-        ((1.0, 2.0, 3.0), ((1.0, 2.0, 3.0), (1.0, 2.0, 3.0)))
-
-    """
-    try:
-        return x.change_ring(R)
-    except AttributeError:
-        try:
-            from sage.categories.sets_cat import cartesian_product
-            if hasattr(x, 'element_class'):
-                # x is a parent and we're in a recursive call.
-                return cartesian_product( [_change_ring(x_i, R)
-                                           for x_i in x.cartesian_factors()] )
-            else:
-                # x is an element, and we want to change the ring
-                # of its parent.
-                P = x.parent()
-                Q = cartesian_product( [_change_ring(P_i, R)
-                                        for P_i in P.cartesian_factors()] )
-                return Q(x)
-        except AttributeError:
-            # No parent for x
-            return x.__class__( _change_ring(x_i, R) for x_i in x )
-
 def _scale(x, alpha):
     r"""
     Scale the vector, matrix, or cartesian-product-of-those-things