]> 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.
 
 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
 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")
 
                 # 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
         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
 
     """
     @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.
 
         """
         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: 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
 
             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):
         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:
                 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)
 
         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
         # 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
 
         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,
                          self.jordan_product,
                          self.trace_inner_product,
+                         field=field,
                          associative=associative,
                          **kwargs)
 
                          associative=associative,
                          **kwargs)
 
@@ -2191,7 +2187,7 @@ class ComplexHermitianEJA(ConcreteEJA, ComplexMatrixEJA):
     """
 
     @classmethod
     """
 
     @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.
 
         """
         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: 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
 
         """
             sage: all( M.is_symmetric() for M in  B)
             True
 
         """
-        field = ZZ
-        R = PolynomialRing(field, 'z')
+        R = PolynomialRing(ZZ, 'z')
         z = R.gen()
         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:
         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
 
                 # "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 )
 
 
         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
         # 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
 
         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,
                          self.jordan_product,
                          self.trace_inner_product,
+                         field=field,
                          associative=associative,
                          **kwargs)
         # TODO: this could be factored out somehow, but is left here
                          associative=associative,
                          **kwargs)
         # TODO: this could be factored out somehow, but is left here
@@ -2492,7 +2488,7 @@ class QuaternionHermitianEJA(ConcreteEJA, QuaternionMatrixEJA):
 
     """
     @classmethod
 
     """
     @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.
 
         """
         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: 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
 
         """
             sage: all( M.is_symmetric() for M in B )
             True
 
         """
-        field = ZZ
         Q = QuaternionAlgebra(QQ,-1,-1)
         I,J,K = Q.gens()
 
         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
 
                 # "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 )
 
 
         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
         # 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
 
         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,
                          self.jordan_product,
                          self.trace_inner_product,
+                         field=field,
                          associative=associative,
                          **kwargs)
 
                          associative=associative,
                          **kwargs)
 
@@ -2643,7 +2639,7 @@ class HadamardEJA(ConcreteEJA):
         (r0, r1, r2)
 
     """
         (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
         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
 
         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,
         super().__init__(column_basis,
                          jordan_product,
                          inner_product,
+                         field=field,
                          associative=True,
                          **kwargs)
         self.rank.set_cache(n)
                          associative=True,
                          **kwargs)
         self.rank.set_cache(n)
@@ -2775,7 +2773,7 @@ class BilinearFormEJA(ConcreteEJA):
         True
 
     """
         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...
         # 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()
             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
 
         # 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,
         super().__init__(column_basis,
                          jordan_product,
                          inner_product,
+                         field=field,
                          associative=associative,
                          **kwargs)
 
                          associative=associative,
                          **kwargs)
 
@@ -2908,7 +2908,7 @@ class JordanSpinEJA(BilinearFormEJA):
             True
 
     """
             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)
         # 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!
 
         # 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():
 
     @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
 
 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
 def _scale(x, alpha):
     r"""
     Scale the vector, matrix, or cartesian-product-of-those-things