]> gitweb.michael.orlitzky.com - sage.d.git/commitdiff
eja: rework the random_eja() bounds in terms of dimension.
authorMichael Orlitzky <michael@orlitzky.com>
Thu, 11 Mar 2021 00:06:46 +0000 (19:06 -0500)
committerMichael Orlitzky <michael@orlitzky.com>
Thu, 11 Mar 2021 00:06:46 +0000 (19:06 -0500)
mjo/eja/eja_algebra.py

index a2adbb2bed47499aea53b38fe175547b887fb2a9..70a150ab2d841d00c6576ee29f9fad275e2cca12 100644 (file)
@@ -1715,25 +1715,35 @@ class ConcreteEJA(FiniteDimensionalEJA):
     """
 
     @staticmethod
-    def _max_random_instance_size():
+    def _max_random_instance_dimension():
+        r"""
+        The maximum dimension of any random instance. Ten dimensions seems
+        to be about the point where everything takes a turn for the
+        worse. And dimension ten (but not nine) allows the 4-by-4 real
+        Hermitian matrices, the 2-by-2 quaternion Hermitian matrices,
+        and the 2-by-2 octonion Hermitian matrices.
+        """
+        return 10
+
+    @staticmethod
+    def _max_random_instance_size(max_dimension):
         """
         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 ambiguous -- 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.
+        this algebra when it is used in a random test case. This size
+        (which can be passed to the algebra's constructor) is itself
+        based on the ``max_dimension`` parameter.
 
         This method must be implemented in each subclass.
         """
         raise NotImplementedError
 
     @classmethod
-    def random_instance(cls, *args, **kwargs):
+    def random_instance(cls, max_dimension=None, *args, **kwargs):
         """
-        Return a random instance of this type of algebra.
+        Return a random instance of this type of algebra whose dimension
+        is less than or equal to ``max_dimension``. If the dimension bound
+        is omitted, then the ``_max_random_instance_dimension()`` is used
+        to get a suitable bound.
 
         This method should be implemented in each subclass.
         """
@@ -1969,15 +1979,19 @@ class RealSymmetricEJA(MatrixEJA, RationalBasisEJA, ConcreteEJA):
 
     """
     @staticmethod
-    def _max_random_instance_size():
-        return 4 # Dimension 10
+    def _max_random_instance_size(max_dimension):
+        # Obtained by solving d = (n^2 + n)/2.
+        return int(ZZ(8*max_dimension + 1).sqrt()/2 - 1/2)
 
     @classmethod
-    def random_instance(cls, **kwargs):
+    def random_instance(cls, max_dimension=None, **kwargs):
         """
         Return a random instance of this type of algebra.
         """
-        n = ZZ.random_element(cls._max_random_instance_size() + 1)
+        if max_dimension is None:
+            max_dimension = cls._max_random_instance_dimension()
+        max_size = cls._max_random_instance_size(max_dimension) + 1
+        n = ZZ.random_element(max_size)
         return cls(n, **kwargs)
 
     def __init__(self, n, field=AA, **kwargs):
@@ -2068,6 +2082,7 @@ class ComplexHermitianEJA(MatrixEJA, RationalBasisEJA, ConcreteEJA):
 
         sage: ComplexHermitianEJA(0)
         Euclidean Jordan algebra of dimension 0 over Algebraic Real Field
+
     """
     def __init__(self, n, field=AA, **kwargs):
         # We know this is a valid EJA, but will double-check
@@ -2087,15 +2102,18 @@ class ComplexHermitianEJA(MatrixEJA, RationalBasisEJA, ConcreteEJA):
                 self._rational_algebra._charpoly_coefficients.set_cache(a)
 
     @staticmethod
-    def _max_random_instance_size():
-        return 3 # Dimension 9
+    def _max_random_instance_size(max_dimension):
+        # Obtained by solving d = n^2.
+        return int(ZZ(max_dimension).sqrt())
 
     @classmethod
-    def random_instance(cls, **kwargs):
+    def random_instance(cls, max_dimension=None, **kwargs):
         """
         Return a random instance of this type of algebra.
         """
-        n = ZZ.random_element(cls._max_random_instance_size() + 1)
+        if max_dimension is None:
+            max_dimension = cls._max_random_instance_dimension()
+        n = ZZ.random_element(cls._max_random_instance_size(max_dimension) + 1)
         return cls(n, **kwargs)
 
 
@@ -2176,18 +2194,21 @@ class QuaternionHermitianEJA(MatrixEJA, RationalBasisEJA, ConcreteEJA):
 
 
     @staticmethod
-    def _max_random_instance_size():
+    def _max_random_instance_size(max_dimension):
         r"""
         The maximum rank of a random QuaternionHermitianEJA.
         """
-        return 2 # Dimension 6
+        # Obtained by solving d = 2n^2 - n.
+        return int(ZZ(8*max_dimension + 1).sqrt()/4 + 1/4)
 
     @classmethod
-    def random_instance(cls, **kwargs):
+    def random_instance(cls, max_dimension=None, **kwargs):
         """
         Return a random instance of this type of algebra.
         """
-        n = ZZ.random_element(cls._max_random_instance_size() + 1)
+        if max_dimension is None:
+            max_dimension = cls._max_random_instance_dimension()
+        n = ZZ.random_element(cls._max_random_instance_size(max_dimension) + 1)
         return cls(n, **kwargs)
 
 class OctonionHermitianEJA(MatrixEJA, RationalBasisEJA, ConcreteEJA):
@@ -2278,18 +2299,29 @@ class OctonionHermitianEJA(MatrixEJA, RationalBasisEJA, ConcreteEJA):
 
     """
     @staticmethod
-    def _max_random_instance_size():
+    def _max_random_instance_size(max_dimension):
         r"""
         The maximum rank of a random QuaternionHermitianEJA.
         """
-        return 1 # Dimension 1
+        # There's certainly a formula for this, but with only four
+        # cases to worry about, I'm not that motivated to derive it.
+        if max_dimension >= 27:
+            return 3
+        elif max_dimension >= 10:
+            return 2
+        elif max_dimension >= 1:
+            return 1
+        else:
+            return 0
 
     @classmethod
-    def random_instance(cls, **kwargs):
+    def random_instance(cls, max_dimension=None, **kwargs):
         """
         Return a random instance of this type of algebra.
         """
-        n = ZZ.random_element(cls._max_random_instance_size() + 1)
+        if max_dimension is None:
+            max_dimension = cls._max_random_instance_dimension()
+        n = ZZ.random_element(cls._max_random_instance_size(max_dimension) + 1)
         return cls(n, **kwargs)
 
     def __init__(self, n, field=AA, **kwargs):
@@ -2410,18 +2442,28 @@ class HadamardEJA(RationalBasisEJA, ConcreteEJA):
         self.one.set_cache( self.sum(self.gens()) )
 
     @staticmethod
-    def _max_random_instance_size():
+    def _max_random_instance_dimension():
         r"""
-        The maximum dimension of a random HadamardEJA.
+        There's no reason to go higher than five here. That's
+        enough to get the point across.
         """
         return 5
 
+    @staticmethod
+    def _max_random_instance_size(max_dimension):
+        r"""
+        The maximum size (=dimension) of a random HadamardEJA.
+        """
+        return max_dimension
+
     @classmethod
-    def random_instance(cls, **kwargs):
+    def random_instance(cls, max_dimension=None, **kwargs):
         """
         Return a random instance of this type of algebra.
         """
-        n = ZZ.random_element(cls._max_random_instance_size() + 1)
+        if max_dimension is None:
+            max_dimension = cls._max_random_instance_dimension()
+        n = ZZ.random_element(cls._max_random_instance_size(max_dimension) + 1)
         return cls(n, **kwargs)
 
 
@@ -2561,18 +2603,28 @@ class BilinearFormEJA(RationalBasisEJA, ConcreteEJA):
             self.one.set_cache( self.monomial(0) )
 
     @staticmethod
-    def _max_random_instance_size():
+    def _max_random_instance_dimension():
         r"""
-        The maximum dimension of a random BilinearFormEJA.
+        There's no reason to go higher than five here. That's
+        enough to get the point across.
         """
         return 5
 
+    @staticmethod
+    def _max_random_instance_size(max_dimension):
+        r"""
+        The maximum size (=dimension) of a random BilinearFormEJA.
+        """
+        return max_dimension
+
     @classmethod
-    def random_instance(cls, **kwargs):
+    def random_instance(cls, max_dimension=None, **kwargs):
         """
         Return a random instance of this algebra.
         """
-        n = ZZ.random_element(cls._max_random_instance_size() + 1)
+        if max_dimension is None:
+            max_dimension = cls._max_random_instance_dimension()
+        n = ZZ.random_element(cls._max_random_instance_size(max_dimension) + 1)
         if n.is_zero():
             B = matrix.identity(ZZ, n)
             return cls(B, **kwargs)
@@ -2655,21 +2707,16 @@ class JordanSpinEJA(BilinearFormEJA):
         # can pass in a field!
         super().__init__(B, *args, **kwargs)
 
-    @staticmethod
-    def _max_random_instance_size():
-        r"""
-        The maximum dimension of a random JordanSpinEJA.
-        """
-        return 5
-
     @classmethod
-    def random_instance(cls, **kwargs):
+    def random_instance(cls, max_dimension=None, **kwargs):
         """
         Return a random instance of this type of algebra.
 
         Needed here to override the implementation for ``BilinearFormEJA``.
         """
-        n = ZZ.random_element(cls._max_random_instance_size() + 1)
+        if max_dimension is None:
+            max_dimension = cls._max_random_instance_dimension()
+        n = ZZ.random_element(cls._max_random_instance_size(max_dimension) + 1)
         return cls(n, **kwargs)
 
 
@@ -3273,15 +3320,23 @@ class RationalBasisCartesianProductEJA(CartesianProductEJA,
 
 RationalBasisEJA.CartesianProduct = RationalBasisCartesianProductEJA
 
-def random_eja(*args, **kwargs):
-    J1 = ConcreteEJA.random_instance(*args, **kwargs)
+def random_eja(max_dimension=None, *args, **kwargs):
+    # Use the ConcreteEJA default as the total upper bound (regardless
+    # of any whether or not any individual factors set a lower limit).
+    if max_dimension is None:
+        max_dimension = ConcreteEJA._max_random_instance_dimension()
+    J1 = ConcreteEJA.random_instance(max_dimension, *args, **kwargs)
 
-    # This might make Cartesian products appear roughly as often as
-    # any other ConcreteEJA.
-    if ZZ.random_element(len(ConcreteEJA.__subclasses__()) + 1) == 0:
-        # Use random_eja() again so we can get more than two factors.
-        J2 = random_eja(*args, **kwargs)
-        J = cartesian_product([J1,J2])
-        return J
-    else:
+
+    # Roll the dice to see if we attempt a Cartesian product.
+    dice_roll = ZZ.random_element(len(ConcreteEJA.__subclasses__()) + 1)
+    new_max_dimension = max_dimension - J1.dimension()
+    if new_max_dimension == 0 or dice_roll != 0:
+        # If it's already as big as we're willing to tolerate, just
+        # return it and don't worry about Cartesian products.
         return J1
+    else:
+        # Use random_eja() again so we can get more than two factors
+        # if the sub-call also Decides on a cartesian product.
+        J2 = random_eja(new_max_dimension, *args, **kwargs)
+        return cartesian_product([J1,J2])