X-Git-Url: http://gitweb.michael.orlitzky.com/?a=blobdiff_plain;f=mjo%2Feja%2Feja_algebra.py;h=fec8a39f00840c10ee22a493756e75abcc6da317;hb=47ff028e7a57a9818d9527664efca945acbe6359;hp=389efbf77219acff73db032e848e34d8da1670e9;hpb=9632a965029feadbb70d18574f8704812ed71321;p=sage.d.git diff --git a/mjo/eja/eja_algebra.py b/mjo/eja/eja_algebra.py index 389efbf..fec8a39 100644 --- a/mjo/eja/eja_algebra.py +++ b/mjo/eja/eja_algebra.py @@ -17,7 +17,7 @@ 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, RR, RLF, CLF, +from sage.rings.all import (ZZ, QQ, AA, QQbar, RR, RLF, CLF, PolynomialRing, QuadraticField) from mjo.eja.eja_element import FiniteDimensionalEuclideanJordanAlgebraElement @@ -207,8 +207,8 @@ class FiniteDimensionalEuclideanJordanAlgebra(CombinatorialFreeModule): Ensure that it says what we think it says:: - sage: JordanSpinEJA(2, field=QQ) - Euclidean Jordan algebra of dimension 2 over Rational Field + sage: JordanSpinEJA(2, field=AA) + Euclidean Jordan algebra of dimension 2 over Algebraic Real Field sage: JordanSpinEJA(3, field=RDF) Euclidean Jordan algebra of dimension 3 over Real Double Field @@ -551,8 +551,8 @@ class FiniteDimensionalEuclideanJordanAlgebra(CombinatorialFreeModule): Finite family {0: e0, 1: e1, 2: e2} sage: J.natural_basis() ( - [1 0] [ 0 1/2*sqrt2] [0 0] - [0 0], [1/2*sqrt2 0], [0 1] + [1 0] [ 0 0.7071067811865475?] [0 0] + [0 0], [0.7071067811865475? 0], [0 1] ) :: @@ -757,7 +757,7 @@ class FiniteDimensionalEuclideanJordanAlgebra(CombinatorialFreeModule): J5 = VectorSpace(self.base_ring(), 0) # eigenvalue one-half J1 = trivial # eigenvalue one - for (eigval, eigspace) in c.operator().matrix().left_eigenspaces(): + for (eigval, eigspace) in c.operator().matrix().right_eigenspaces(): if eigval == ~(self.base_ring()(2)): J5 = eigspace else: @@ -773,128 +773,104 @@ class FiniteDimensionalEuclideanJordanAlgebra(CombinatorialFreeModule): return (J0, J5, J1) - def a_jordan_frame(self): - r""" - Generate a Jordan frame for this algebra. - - This implementation is based on the so-called "central - orthogonal idempotents" implemented for (semisimple) centers - of SageMath ``FiniteDimensionalAlgebrasWithBasis``. Since all - Euclidean Jordan algebas are commutative (and thus equal to - their own centers) and semisimple, the method should work more - or less as implemented, if it ever worked in the first place. - (I don't know the justification for the original implementation. - yet). - - How it works: we loop through the algebras generators, looking - for their eigenspaces. If there's more than one eigenspace, - and if they result in more than one subalgebra, then we split - those subalgebras recursively until we get to subalgebras of - dimension one (whose idempotent is the unit element). Why does - some generator have to produce at least two subalgebras? I - dunno. But it seems to work. - - Beware that Koecher defines the "center" of a Jordan algebra to - be something else, because the usual definition is stupid in a - (necessarily commutative) Jordan algebra. + def random_elements(self, count): + """ + Return ``count`` random elements as a tuple. SETUP:: - sage: from mjo.eja.eja_algebra import (random_eja, - ....: JordanSpinEJA, - ....: TrivialEJA) - - EXAMPLES: - - A Jordan frame for the trivial algebra has to be empty - (zero-length) since its rank is zero. More to the point, there - are no non-zero idempotents in the trivial EJA. This does not - cause any problems so long as we adopt the convention that the - empty sum is zero, since then the sole element of the trivial - EJA has an (empty) spectral decomposition:: - - sage: J = TrivialEJA() - sage: J.a_jordan_frame() - () - - A one-dimensional algebra has rank one (equal to its dimension), - and only one primitive idempotent, namely the algebra's unit - element:: - - sage: J = JordanSpinEJA(1) - sage: J.a_jordan_frame() - (e0,) + sage: from mjo.eja.eja_algebra import JordanSpinEJA - TESTS:: + EXAMPLES:: - sage: J = random_eja() - sage: c = J.a_jordan_frame() - sage: all( x^2 == x for x in c ) + sage: J = JordanSpinEJA(3) + sage: x,y,z = J.random_elements(3) + sage: all( [ x in J, y in J, z in J ]) True - sage: r = len(c) - sage: all( c[i]*c[j] == c[i]*(i==j) for i in range(r) - ....: for j in range(r) ) + sage: len( J.random_elements(10) ) == 10 True """ - if self.dimension() == 0: - return () - if self.dimension() == 1: - return (self.one(),) - - for g in self.gens(): - eigenpairs = g.operator().matrix().right_eigenspaces() - if len(eigenpairs) >= 2: - subalgebras = [] - for eigval, eigspace in eigenpairs: - # Make sub-EJAs from the matrix eigenspaces... - sb = tuple( self.from_vector(b) for b in eigspace.basis() ) - try: - # This will fail if e.g. the eigenspace basis - # contains two elements and their product - # isn't a linear combination of the two of - # them (i.e. the generated EJA isn't actually - # two dimensional). - s = FiniteDimensionalEuclideanJordanSubalgebra(self, sb) - subalgebras.append(s) - except ArithmeticError as e: - if str(e) == "vector is not in free module": - # Ignore only the "not a sub-EJA" error - pass - - if len(subalgebras) >= 2: - # apply this method recursively. - return tuple( c.superalgebra_element() - for subalgebra in subalgebras - for c in subalgebra.a_jordan_frame() ) - - # If we got here, the algebra didn't decompose, at least not when we looked at - # the eigenspaces corresponding only to basis elements of the algebra. The - # implementation I stole says that this should work because of Schur's Lemma, - # so I personally blame Schur's Lemma if it does not. - raise Exception("Schur's Lemma didn't work!") + return tuple( self.random_element() for idx in range(count) ) - def random_elements(self, count): - """ - Return ``count`` random elements as a tuple. + def _rank_computation(self): + r""" + Compute the rank of this algebra using highly suspicious voodoo. + + ALGORITHM: + + We first compute the basis representation of the operator L_x + using polynomial indeterminates are placeholders for the + coordinates of "x", which is arbitrary. We then use that + matrix to compute the (polynomial) entries of x^0, x^1, ..., + x^d,... for increasing values of "d", starting at zero. The + idea is that. If we also add "coefficient variables" a_0, + a_1,... to the ring, we can form the linear combination + a_0*x^0 + ... + a_d*x^d = 0, and ask what dimension the + solution space has as an affine variety. When "d" is smaller + than the rank, we expect that dimension to be the number of + coordinates of "x", since we can set *those* to whatever we + want, but linear independence forces the coefficients a_i to + be zero. Eventually, when "d" passes the rank, the dimension + of the solution space begins to grow, because we can *still* + set the coordinates of "x" arbitrarily, but now there are some + coefficients that make the sum zero as well. So, when the + dimension of the variety jumps, we return the corresponding + "d" as the rank of the algebra. This appears to work. SETUP:: - sage: from mjo.eja.eja_algebra import JordanSpinEJA + sage: from mjo.eja.eja_algebra import (HadamardEJA, + ....: JordanSpinEJA, + ....: RealSymmetricEJA, + ....: ComplexHermitianEJA, + ....: QuaternionHermitianEJA) EXAMPLES:: - sage: J = JordanSpinEJA(3) - sage: x,y,z = J.random_elements(3) - sage: all( [ x in J, y in J, z in J ]) + sage: J = HadamardEJA(5) + sage: J._rank_computation() == J.rank() True - sage: len( J.random_elements(10) ) == 10 + sage: J = JordanSpinEJA(5) + sage: J._rank_computation() == J.rank() + True + sage: J = RealSymmetricEJA(4) + sage: J._rank_computation() == J.rank() + True + sage: J = ComplexHermitianEJA(3) + sage: J._rank_computation() == J.rank() + True + sage: J = QuaternionHermitianEJA(2) + sage: J._rank_computation() == J.rank() True """ - return tuple( self.random_element() for idx in range(count) ) - + n = self.dimension() + var_names = [ "X" + str(z) for z in range(1,n+1) ] + d = 0 + ideal_dim = len(var_names) + def L_x_i_j(i,j): + # From a result in my book, these are the entries of the + # basis representation of L_x. + return sum( vars[d+k]*self.monomial(k).operator().matrix()[i,j] + for k in range(n) ) + + while ideal_dim == len(var_names): + coeff_names = [ "a" + str(z) for z in range(d) ] + R = PolynomialRing(self.base_ring(), coeff_names + var_names) + vars = R.gens() + L_x = matrix(R, n, n, L_x_i_j) + x_powers = [ vars[k]*(L_x**k)*self.one().to_vector() + for k in range(d) ] + eqs = [ sum(x_powers[k][j] for k in range(d)) for j in range(n) ] + ideal_dim = R.ideal(eqs).dimension() + d += 1 + + # Subtract one because we increment one too many times, and + # subtract another one because "d" is one greater than the + # answer anyway; when d=3, we go up to x^2. + return d-2 def rank(self): """ @@ -1013,7 +989,7 @@ class KnownRankEJA(object): return 5 @classmethod - def random_instance(cls, field=QQ, **kwargs): + def random_instance(cls, field=AA, **kwargs): """ Return a random instance of this type of algebra. @@ -1069,7 +1045,7 @@ class HadamardEJA(FiniteDimensionalEuclideanJordanAlgebra, KnownRankEJA): (r0, r1, r2) """ - def __init__(self, n, field=QQ, **kwargs): + def __init__(self, n, field=AA, **kwargs): V = VectorSpace(field, n) mult_table = [ [ V.gen(i)*(i == j) for j in range(n) ] for i in range(n) ] @@ -1102,7 +1078,7 @@ class HadamardEJA(FiniteDimensionalEuclideanJordanAlgebra, KnownRankEJA): return x.to_vector().inner_product(y.to_vector()) -def random_eja(field=QQ, nontrivial=False): +def random_eja(field=AA, nontrivial=False): """ Return a "random" finite-dimensional Euclidean Jordan Algebra. @@ -1322,8 +1298,8 @@ class RealSymmetricEJA(RealMatrixEuclideanJordanAlgebra, KnownRankEJA): In theory, our "field" can be any subfield of the reals:: - sage: RealSymmetricEJA(2, AA) - Euclidean Jordan algebra of dimension 3 over Algebraic Real Field + sage: RealSymmetricEJA(2, RDF) + Euclidean Jordan algebra of dimension 3 over Real Double Field sage: RealSymmetricEJA(2, RR) Euclidean Jordan algebra of dimension 3 over Real Field with 53 bits of precision @@ -1415,7 +1391,7 @@ class RealSymmetricEJA(RealMatrixEuclideanJordanAlgebra, KnownRankEJA): return 4 # Dimension 10 - def __init__(self, n, field=QQ, **kwargs): + def __init__(self, n, field=AA, **kwargs): basis = self._denormalized_basis(n, field) super(RealSymmetricEJA, self).__init__(field, basis, n, **kwargs) @@ -1435,7 +1411,7 @@ class ComplexMatrixEuclideanJordanAlgebra(MatrixEuclideanJordanAlgebra): EXAMPLES:: - sage: F = QuadraticField(-1, 'i') + sage: F = QuadraticField(-1, 'I') sage: x1 = F(4 - 2*i) sage: x2 = F(1 + 2*i) sage: x3 = F(-i) @@ -1455,7 +1431,7 @@ class ComplexMatrixEuclideanJordanAlgebra(MatrixEuclideanJordanAlgebra): sage: set_random_seed() sage: n_max = ComplexMatrixEuclideanJordanAlgebra._max_test_case_size() sage: n = ZZ.random_element(n_max) - sage: F = QuadraticField(-1, 'i') + sage: F = QuadraticField(-1, 'I') sage: X = random_matrix(F, n) sage: Y = random_matrix(F, n) sage: Xe = ComplexMatrixEuclideanJordanAlgebra.real_embed(X) @@ -1498,15 +1474,15 @@ class ComplexMatrixEuclideanJordanAlgebra(MatrixEuclideanJordanAlgebra): ....: [ 9, 10, 11, 12], ....: [-10, 9, -12, 11] ]) sage: ComplexMatrixEuclideanJordanAlgebra.real_unembed(A) - [ 2*i + 1 4*i + 3] - [ 10*i + 9 12*i + 11] + [ 2*I + 1 4*I + 3] + [ 10*I + 9 12*I + 11] TESTS: Unembedding is the inverse of embedding:: sage: set_random_seed() - sage: F = QuadraticField(-1, 'i') + sage: F = QuadraticField(-1, 'I') sage: M = random_matrix(F, 3) sage: Me = ComplexMatrixEuclideanJordanAlgebra.real_embed(M) sage: ComplexMatrixEuclideanJordanAlgebra.real_unembed(Me) == M @@ -1524,7 +1500,12 @@ class ComplexMatrixEuclideanJordanAlgebra(MatrixEuclideanJordanAlgebra): field = M.base_ring() R = PolynomialRing(field, 'z') z = R.gen() - F = field.extension(z**2 + 1, 'i', embedding=CLF(-1).sqrt()) + if field is AA: + # Sage doesn't know how to embed AA into QQbar, i.e. how + # to adjoin sqrt(-1) to AA. + F = QQbar + else: + F = field.extension(z**2 + 1, 'I', embedding=CLF(-1).sqrt()) i = F.gen() # Go top-left to bottom-right (reading order), converting every @@ -1565,7 +1546,7 @@ class ComplexMatrixEuclideanJordanAlgebra(MatrixEuclideanJordanAlgebra): sage: Ye = y.natural_representation() sage: X = ComplexHermitianEJA.real_unembed(Xe) sage: Y = ComplexHermitianEJA.real_unembed(Ye) - sage: expected = (X*Y).trace().vector()[0] + sage: expected = (X*Y).trace().real() sage: actual = ComplexHermitianEJA.natural_inner_product(Xe,Ye) sage: actual == expected True @@ -1589,8 +1570,8 @@ class ComplexHermitianEJA(ComplexMatrixEuclideanJordanAlgebra, KnownRankEJA): In theory, our "field" can be any subfield of the reals:: - sage: ComplexHermitianEJA(2, AA) - Euclidean Jordan algebra of dimension 4 over Algebraic Real Field + sage: ComplexHermitianEJA(2, RDF) + Euclidean Jordan algebra of dimension 4 over Real Double Field sage: ComplexHermitianEJA(2, RR) Euclidean Jordan algebra of dimension 4 over Real Field with 53 bits of precision @@ -1700,7 +1681,7 @@ class ComplexHermitianEJA(ComplexMatrixEuclideanJordanAlgebra, KnownRankEJA): return ( s.change_ring(field) for s in S ) - def __init__(self, n, field=QQ, **kwargs): + def __init__(self, n, field=AA, **kwargs): basis = self._denormalized_basis(n,field) super(ComplexHermitianEJA,self).__init__(field, basis, n, **kwargs) @@ -1752,7 +1733,7 @@ class QuaternionMatrixEuclideanJordanAlgebra(MatrixEuclideanJordanAlgebra): if M.ncols() != n: raise ValueError("the matrix 'M' must be square") - F = QuadraticField(-1, 'i') + F = QuadraticField(-1, 'I') i = F.gen() blocks = [] @@ -1828,10 +1809,10 @@ class QuaternionMatrixEuclideanJordanAlgebra(MatrixEuclideanJordanAlgebra): raise ValueError('bad on-diagonal submatrix') if submat[0,1] != -submat[1,0].conjugate(): raise ValueError('bad off-diagonal submatrix') - z = submat[0,0].vector()[0] # real part - z += submat[0,0].vector()[1]*i # imag part - z += submat[0,1].vector()[0]*j # real part - z += submat[0,1].vector()[1]*k # imag part + z = submat[0,0].real() + z += submat[0,0].imag()*i + z += submat[0,1].real()*j + z += submat[0,1].imag()*k elements.append(z) return matrix(Q, n/4, elements) @@ -1884,8 +1865,8 @@ class QuaternionHermitianEJA(QuaternionMatrixEuclideanJordanAlgebra, In theory, our "field" can be any subfield of the reals:: - sage: QuaternionHermitianEJA(2, AA) - Euclidean Jordan algebra of dimension 6 over Algebraic Real Field + sage: QuaternionHermitianEJA(2, RDF) + Euclidean Jordan algebra of dimension 6 over Real Double Field sage: QuaternionHermitianEJA(2, RR) Euclidean Jordan algebra of dimension 6 over Real Field with 53 bits of precision @@ -1996,7 +1977,7 @@ class QuaternionHermitianEJA(QuaternionMatrixEuclideanJordanAlgebra, return ( s.change_ring(field) for s in S ) - def __init__(self, n, field=QQ, **kwargs): + def __init__(self, n, field=AA, **kwargs): basis = self._denormalized_basis(n,field) super(QuaternionHermitianEJA,self).__init__(field, basis, n, **kwargs) @@ -2056,7 +2037,7 @@ class BilinearFormEJA(FiniteDimensionalEuclideanJordanAlgebra, KnownRankEJA): sage: actual == expected True """ - def __init__(self, n, field=QQ, B=None, **kwargs): + def __init__(self, n, field=AA, B=None, **kwargs): if B is None: self._B = matrix.identity(field, max(0,n-1)) else: @@ -2169,7 +2150,7 @@ class JordanSpinEJA(BilinearFormEJA): True """ - def __init__(self, n, field=QQ, **kwargs): + def __init__(self, n, field=AA, **kwargs): # This is a special case of the BilinearFormEJA with the identity # matrix as its bilinear form. return super(JordanSpinEJA, self).__init__(n, field, **kwargs) @@ -2199,12 +2180,12 @@ class TrivialEJA(FiniteDimensionalEuclideanJordanAlgebra, KnownRankEJA): sage: J.one().norm() 0 sage: J.one().subalgebra_generated_by() - Euclidean Jordan algebra of dimension 0 over Rational Field + Euclidean Jordan algebra of dimension 0 over Algebraic Real Field sage: J.rank() 0 """ - def __init__(self, field=QQ, **kwargs): + def __init__(self, field=AA, **kwargs): mult_table = [] fdeja = super(TrivialEJA, self) # The rank is zero using my definition, namely the dimension of the