From 093846a825b8293cc81661f0b4927662c5a0446d Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Sun, 22 Nov 2020 17:39:08 -0500 Subject: [PATCH] eja: add ConcreteEJA class for EJAs we can make random instances of. --- mjo/eja/eja_algebra.py | 193 ++++++++++++++++++++++++++--------------- 1 file changed, 122 insertions(+), 71 deletions(-) diff --git a/mjo/eja/eja_algebra.py b/mjo/eja/eja_algebra.py index c40c8be..d9a3abc 100644 --- a/mjo/eja/eja_algebra.py +++ b/mjo/eja/eja_algebra.py @@ -3,6 +3,17 @@ Euclidean Jordan Algebras. These are formally-real Jordan Algebras; specifically those where u^2 + v^2 = 0 implies that u = v = 0. They are used in optimization, and have some additional nice methods beyond what can be supported in a general Jordan Algebra. + + +SETUP:: + + sage: from mjo.eja.eja_algebra import random_eja + +EXAMPLES:: + + sage: random_eja() + Euclidean Jordan algebra of dimension... + """ from itertools import repeat @@ -14,7 +25,6 @@ from sage.matrix.constructor import matrix from sage.matrix.matrix_space import MatrixSpace from sage.misc.cachefunc import cached_method from sage.misc.lazy_import import lazy_import -from sage.misc.prandom import choice from sage.misc.table import table from sage.modules.free_module import FreeModule, VectorSpace from sage.rings.all import (ZZ, QQ, AA, QQbar, RR, RLF, CLF, @@ -216,25 +226,6 @@ class FiniteDimensionalEuclideanJordanAlgebra(CombinatorialFreeModule): coords = W.coordinate_vector(_mat2vec(elt)) return self.from_vector(coords) - @staticmethod - def _max_random_instance_size(): - """ - 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 quite vague -- 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. - - We default to five in this class, which is safe in `R^n`. The - matrix algebra subclasses (or any class where the "size" is - interpreted to be far less than the dimension) should override - with a smaller number. - """ - raise NotImplementedError - def _repr_(self): """ Return a string representation of ``self``. @@ -842,16 +833,6 @@ class FiniteDimensionalEuclideanJordanAlgebra(CombinatorialFreeModule): return tuple( self.random_element(thorough) for idx in range(count) ) - @classmethod - def random_instance(cls, field=AA, **kwargs): - """ - Return a random instance of this type of algebra. - - Beware, this will crash for "most instances" because the - constructor below looks wrong. - """ - n = ZZ.random_element(cls._max_random_instance_size() + 1) - return cls(n, field, **kwargs) @cached_method def _charpoly_coefficients(self): @@ -1013,32 +994,6 @@ class FiniteDimensionalEuclideanJordanAlgebra(CombinatorialFreeModule): Element = FiniteDimensionalEuclideanJordanAlgebraElement - -def random_eja(field=AA): - """ - Return a "random" finite-dimensional Euclidean Jordan Algebra. - - SETUP:: - - sage: from mjo.eja.eja_algebra import random_eja - - TESTS:: - - sage: random_eja() - Euclidean Jordan algebra of dimension... - - """ - classname = choice([TrivialEJA, - HadamardEJA, - JordanSpinEJA, - RealSymmetricEJA, - ComplexHermitianEJA, - QuaternionHermitianEJA]) - return classname.random_instance(field=field) - - - - class RationalBasisEuclideanJordanAlgebra(FiniteDimensionalEuclideanJordanAlgebra): r""" Algebras whose basis consists of vectors with rational @@ -1098,12 +1053,45 @@ class RationalBasisEuclideanJordanAlgebra(FiniteDimensionalEuclideanJordanAlgebr return tuple(map(lambda x: x.change_ring(self.base_ring()), a)) -class MatrixEuclideanJordanAlgebra(FiniteDimensionalEuclideanJordanAlgebra): +class ConcreteEuclideanJordanAlgebra: + r""" + A class for the Euclidean Jordan algebras that we know by name. + + These are the Jordan algebras whose basis, multiplication table, + rank, and so on are known a priori. More to the point, they are + the Euclidean Jordan algebras for which we are able to conjure up + a "random instance." + """ + @staticmethod def _max_random_instance_size(): - # Play it safe, since this will be squared and the underlying - # field can have dimension 4 (quaternions) too. - return 2 + """ + 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 method must be implemented in each subclass. + """ + raise NotImplementedError + + @classmethod + def random_instance(cls, field=AA, **kwargs): + """ + Return a random instance of this type of algebra. + + This method should be implemented in each subclass. + """ + from sage.misc.prandom import choice + eja_class = choice(cls.__subclasses__()) + return eja_class.random_instance(field) + + +class MatrixEuclideanJordanAlgebra(FiniteDimensionalEuclideanJordanAlgebra): def __init__(self, field, basis, normalize_basis=True, **kwargs): """ @@ -1287,7 +1275,8 @@ class RealMatrixEuclideanJordanAlgebra(MatrixEuclideanJordanAlgebra): return M -class RealSymmetricEJA(RealMatrixEuclideanJordanAlgebra): +class RealSymmetricEJA(RealMatrixEuclideanJordanAlgebra, + ConcreteEuclideanJordanAlgebra): """ The rank-n simple EJA consisting of real symmetric n-by-n matrices, the usual symmetric Jordan product, and the trace inner @@ -1407,6 +1396,13 @@ class RealSymmetricEJA(RealMatrixEuclideanJordanAlgebra): def _max_random_instance_size(): return 4 # Dimension 10 + @classmethod + def random_instance(cls, field=AA, **kwargs): + """ + Return a random instance of this type of algebra. + """ + n = ZZ.random_element(cls._max_random_instance_size() + 1) + return cls(n, field, **kwargs) def __init__(self, n, field=AA, **kwargs): basis = self._denormalized_basis(n, field) @@ -1450,8 +1446,7 @@ class ComplexMatrixEuclideanJordanAlgebra(MatrixEuclideanJordanAlgebra): Embedding is a homomorphism (isomorphism, in fact):: sage: set_random_seed() - sage: n_max = ComplexMatrixEuclideanJordanAlgebra._max_random_instance_size() - sage: n = ZZ.random_element(n_max) + sage: n = ZZ.random_element(3) sage: F = QuadraticField(-1, 'I') sage: X = random_matrix(F, n) sage: Y = random_matrix(F, n) @@ -1576,7 +1571,8 @@ class ComplexMatrixEuclideanJordanAlgebra(MatrixEuclideanJordanAlgebra): return RealMatrixEuclideanJordanAlgebra.natural_inner_product(X,Y)/2 -class ComplexHermitianEJA(ComplexMatrixEuclideanJordanAlgebra): +class ComplexHermitianEJA(ComplexMatrixEuclideanJordanAlgebra, + ConcreteEuclideanJordanAlgebra): """ The rank-n simple EJA consisting of complex Hermitian n-by-n matrices over the real numbers, the usual symmetric Jordan product, @@ -1715,6 +1711,17 @@ class ComplexHermitianEJA(ComplexMatrixEuclideanJordanAlgebra): **kwargs) self.rank.set_cache(n) + @staticmethod + def _max_random_instance_size(): + return 3 # Dimension 9 + + @classmethod + def random_instance(cls, field=AA, **kwargs): + """ + Return a random instance of this type of algebra. + """ + n = ZZ.random_element(cls._max_random_instance_size() + 1) + return cls(n, field, **kwargs) class QuaternionMatrixEuclideanJordanAlgebra(MatrixEuclideanJordanAlgebra): @staticmethod @@ -1746,8 +1753,7 @@ class QuaternionMatrixEuclideanJordanAlgebra(MatrixEuclideanJordanAlgebra): Embedding is a homomorphism (isomorphism, in fact):: sage: set_random_seed() - sage: n_max = QuaternionMatrixEuclideanJordanAlgebra._max_random_instance_size() - sage: n = ZZ.random_element(n_max) + sage: n = ZZ.random_element(2) sage: Q = QuaternionAlgebra(QQ,-1,-1) sage: X = random_matrix(Q, n) sage: Y = random_matrix(Q, n) @@ -1879,8 +1885,9 @@ class QuaternionMatrixEuclideanJordanAlgebra(MatrixEuclideanJordanAlgebra): return RealMatrixEuclideanJordanAlgebra.natural_inner_product(X,Y)/4 -class QuaternionHermitianEJA(QuaternionMatrixEuclideanJordanAlgebra): - """ +class QuaternionHermitianEJA(QuaternionMatrixEuclideanJordanAlgebra, + ConcreteEuclideanJordanAlgebra): + r""" The rank-n simple EJA consisting of self-adjoint n-by-n quaternion matrices, the usual symmetric Jordan product, and the real-part-of-trace inner product. It has dimension `2n^2 - n` over @@ -2019,8 +2026,24 @@ class QuaternionHermitianEJA(QuaternionMatrixEuclideanJordanAlgebra): **kwargs) self.rank.set_cache(n) + @staticmethod + def _max_random_instance_size(): + r""" + The maximum rank of a random QuaternionHermitianEJA. + """ + return 2 # Dimension 6 + + @classmethod + def random_instance(cls, field=AA, **kwargs): + """ + Return a random instance of this type of algebra. + """ + n = ZZ.random_element(cls._max_random_instance_size() + 1) + return cls(n, field, **kwargs) + -class HadamardEJA(RationalBasisEuclideanJordanAlgebra): +class HadamardEJA(RationalBasisEuclideanJordanAlgebra, + ConcreteEuclideanJordanAlgebra): """ Return the Euclidean Jordan Algebra corresponding to the set `R^n` under the Hadamard product. @@ -2078,8 +2101,20 @@ class HadamardEJA(RationalBasisEuclideanJordanAlgebra): @staticmethod def _max_random_instance_size(): + r""" + The maximum dimension of a random HadamardEJA. + """ return 5 + @classmethod + def random_instance(cls, field=AA, **kwargs): + """ + Return a random instance of this type of algebra. + """ + n = ZZ.random_element(cls._max_random_instance_size() + 1) + return cls(n, field, **kwargs) + + def inner_product(self, x, y): """ Faster to reimplement than to use natural representations. @@ -2105,7 +2140,8 @@ class HadamardEJA(RationalBasisEuclideanJordanAlgebra): return x.to_vector().inner_product(y.to_vector()) -class BilinearFormEJA(RationalBasisEuclideanJordanAlgebra): +class BilinearFormEJA(RationalBasisEuclideanJordanAlgebra, + ConcreteEuclideanJordanAlgebra): r""" The rank-2 simple EJA consisting of real vectors ``x=(x0, x_bar)`` with the half-trace inner product and jordan product ``x*y = @@ -2222,6 +2258,9 @@ class BilinearFormEJA(RationalBasisEuclideanJordanAlgebra): @staticmethod def _max_random_instance_size(): + r""" + The maximum dimension of a random BilinearFormEJA. + """ return 5 @classmethod @@ -2334,6 +2373,13 @@ class JordanSpinEJA(BilinearFormEJA): B = matrix.identity(field, n) super(JordanSpinEJA, self).__init__(B, field, **kwargs) + @staticmethod + def _max_random_instance_size(): + r""" + The maximum dimension of a random JordanSpinEJA. + """ + return 5 + @classmethod def random_instance(cls, field=AA, **kwargs): """ @@ -2345,7 +2391,8 @@ class JordanSpinEJA(BilinearFormEJA): return cls(n, field, **kwargs) -class TrivialEJA(FiniteDimensionalEuclideanJordanAlgebra): +class TrivialEJA(FiniteDimensionalEuclideanJordanAlgebra, + ConcreteEuclideanJordanAlgebra): """ The trivial Euclidean Jordan algebra consisting of only a zero element. @@ -2572,3 +2619,7 @@ class DirectSumEJA(FiniteDimensionalEuclideanJordanAlgebra): y2 = pi_right(y) return (x1.inner_product(y1) + x2.inner_product(y2)) + + + +random_eja = ConcreteEuclideanJordanAlgebra.random_instance -- 2.44.2