from sage.misc.prandom import choice
from sage.misc.table import table
from sage.modules.free_module import FreeModule, VectorSpace
-from sage.rings.integer_ring import ZZ
-from sage.rings.number_field.number_field import NumberField, QuadraticField
-from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
-from sage.rings.rational_field import QQ
-from sage.rings.real_lazy import CLF, RLF
-from sage.structure.element import is_Matrix
-
+from sage.rings.all import (ZZ, QQ, RR, RLF, CLF,
+ PolynomialRing,
+ QuadraticField)
from mjo.eja.eja_element import FiniteDimensionalEuclideanJordanAlgebraElement
from mjo.eja.eja_utils import _mat2vec
rank,
prefix='e',
category=None,
- natural_basis=None):
+ natural_basis=None,
+ check=True):
"""
SETUP::
- sage: from mjo.eja.eja_algebra import random_eja
+ sage: from mjo.eja.eja_algebra import (JordanSpinEJA, random_eja)
EXAMPLES:
sage: x*y == y*x
True
+ TESTS:
+
+ The ``field`` we're given must be real::
+
+ sage: JordanSpinEJA(2,QQbar)
+ Traceback (most recent call last):
+ ...
+ ValueError: field is not real
+
"""
+ if check:
+ if not field.is_subring(RR):
+ # Note: this does return true for the real algebraic
+ # field, and any quadratic field where we've specified
+ # a real embedding.
+ raise ValueError('field is not real')
+
self._rank = rank
self._natural_basis = natural_basis
return self.from_vector(coords)
- @staticmethod
- def _max_test_case_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.
- """
- return 5
-
-
def _repr_(self):
"""
Return a string representation of ``self``.
EXAMPLES:
- Our inner product satisfies the Jordan axiom, which is also
- referred to as "associativity" for a symmetric bilinear form::
+ Our inner product is "associative," which means the following for
+ a symmetric bilinear form::
sage: set_random_seed()
sage: J = random_eja()
"""
return tuple( self.random_element() for idx in xrange(count) )
- @classmethod
- def random_instance(cls, field=QQ, **kwargs):
- """
- Return a random instance of this type of algebra.
-
- In subclasses for algebras that we know how to construct, this
- is a shortcut for constructing test cases and examples.
- """
- if cls is FiniteDimensionalEuclideanJordanAlgebra:
- # Red flag! But in theory we could do this I guess. The
- # only finite-dimensional exceptional EJA is the
- # octononions. So, we could just create an EJA from an
- # associative matrix algebra (generated by a subset of
- # elements) with the symmetric product. Or, we could punt
- # to random_eja() here, override it in our subclasses, and
- # not worry about it.
- raise NotImplementedError
-
- n = ZZ.random_element(cls._max_test_case_size()) + 1
- return cls(n, field, **kwargs)
-
def rank(self):
"""
Element = FiniteDimensionalEuclideanJordanAlgebraElement
-class RealCartesianProductEJA(FiniteDimensionalEuclideanJordanAlgebra):
+class KnownRankEJA(object):
+ """
+ A class for algebras that we actually know we can construct. The
+ main issue is that, for most of our methods to make sense, we need
+ to know the rank of our algebra. Thus we can't simply generate a
+ "random" algebra, or even check that a given basis and product
+ satisfy the axioms; because even if everything looks OK, we wouldn't
+ know the rank we need to actuallty build the thing.
+
+ Not really a subclass of FDEJA because doing that causes method
+ resolution errors, e.g.
+
+ TypeError: Error when calling the metaclass bases
+ Cannot create a consistent method resolution
+ order (MRO) for bases FiniteDimensionalEuclideanJordanAlgebra,
+ KnownRankEJA
+
+ """
+ @staticmethod
+ def _max_test_case_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.
+ """
+ return 5
+
+ @classmethod
+ def random_instance(cls, field=QQ, **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_test_case_size()) + 1
+ return cls(n, field, **kwargs)
+
+
+class RealCartesianProductEJA(FiniteDimensionalEuclideanJordanAlgebra,
+ KnownRankEJA):
"""
Return the Euclidean Jordan Algebra corresponding to the set
`R^n` under the Hadamard product.
return x.to_vector().inner_product(y.to_vector())
-def random_eja():
+def random_eja(field=QQ):
"""
Return a "random" finite-dimensional Euclidean Jordan Algebra.
Euclidean Jordan algebra of dimension...
"""
- classname = choice([RealCartesianProductEJA,
- JordanSpinEJA,
- RealSymmetricEJA,
- ComplexHermitianEJA,
- QuaternionHermitianEJA])
- return classname.random_instance()
+ classname = choice(KnownRankEJA.__subclasses__())
+ return classname.random_instance(field=field)
z = R.gen()
p = z**2 - 2
if p.is_irreducible():
- field = NumberField(p, 'sqrt2', embedding=RLF(2).sqrt())
+ field = field.extension(p, 'sqrt2', embedding=RLF(2).sqrt())
basis = tuple( s.change_ring(field) for s in basis )
self._basis_normalizers = tuple(
~(self.natural_inner_product(s,s).sqrt()) for s in basis )
else:
basis = ( (b/n) for (b,n) in izip(self.natural_basis(),
self._basis_normalizers) )
- field = self.base_ring().base_ring() # yeeeaahhhhhhh
- J = MatrixEuclideanJordanAlgebra(field,
+
+ # Do this over the rationals and convert back at the end.
+ J = MatrixEuclideanJordanAlgebra(QQ,
basis,
self.rank(),
normalize_basis=False)
# p might be missing some vars, have to substitute "optionally"
pairs = izip(x.base_ring().gens(), self._basis_normalizers)
substitutions = { v: v*c for (v,c) in pairs }
- return p.subs(substitutions)
+ result = p.subs(substitutions)
+
+ # The result of "subs" can be either a coefficient-ring
+ # element or a polynomial. Gotta handle both cases.
+ if result in QQ:
+ return self.base_ring()(result)
+ else:
+ return result.change_ring(self.base_ring())
@staticmethod
Xu = cls.real_unembed(X)
Yu = cls.real_unembed(Y)
tr = (Xu*Yu).trace()
+
if tr in RLF:
# It's real already.
return tr
return M
-class RealSymmetricEJA(RealMatrixEuclideanJordanAlgebra):
+class RealSymmetricEJA(RealMatrixEuclideanJordanAlgebra, KnownRankEJA):
"""
The rank-n simple EJA consisting of real symmetric n-by-n
matrices, the usual symmetric Jordan product, and the trace inner
sage: e2*e2
e2
+ 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, RR)
+ Euclidean Jordan algebra of dimension 3 over Real Field with
+ 53 bits of precision
+
TESTS:
The dimension of this algebra is `(n^2 + n) / 2`::
n = M.nrows()
if M.ncols() != n:
raise ValueError("the matrix 'M' must be square")
- field = M.base_ring()
+
+ # We don't need any adjoined elements...
+ field = M.base_ring().base_ring()
+
blocks = []
for z in M.list():
- a = z.vector()[0] # real part, I guess
- b = z.vector()[1] # imag part, I guess
+ a = z.list()[0] # real part, I guess
+ b = z.list()[1] # imag part, I guess
blocks.append(matrix(field, 2, [[a,b],[-b,a]]))
- # We can drop the imaginaries here.
- return matrix.block(field.base_ring(), n, blocks)
+ return matrix.block(field, n, blocks)
@staticmethod
if not n.mod(2).is_zero():
raise ValueError("the matrix 'M' must be a complex embedding")
- field = M.base_ring() # This should already have sqrt2
+ # If "M" was normalized, its base ring might have roots
+ # adjoined and they can stick around after unembedding.
+ field = M.base_ring()
R = PolynomialRing(field, 'z')
z = R.gen()
- F = NumberField(z**2 + 1,'i', embedding=CLF(-1).sqrt())
+ F = field.extension(z**2 + 1, 'i', embedding=CLF(-1).sqrt())
i = F.gen()
# Go top-left to bottom-right (reading order), converting every
return RealMatrixEuclideanJordanAlgebra.natural_inner_product(X,Y)/2
-class ComplexHermitianEJA(ComplexMatrixEuclideanJordanAlgebra):
+class ComplexHermitianEJA(ComplexMatrixEuclideanJordanAlgebra, KnownRankEJA):
"""
The rank-n simple EJA consisting of complex Hermitian n-by-n
matrices over the real numbers, the usual symmetric Jordan product,
sage: from mjo.eja.eja_algebra import ComplexHermitianEJA
+ EXAMPLES:
+
+ 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, RR)
+ Euclidean Jordan algebra of dimension 4 over Real Field with
+ 53 bits of precision
+
TESTS:
The dimension of this algebra is `n^2`::
"""
R = PolynomialRing(field, 'z')
z = R.gen()
- F = NumberField(z**2 + 1, 'I', embedding=CLF(-1).sqrt())
+ F = field.extension(z**2 + 1, 'I')
I = F.gen()
# This is like the symmetric case, but we need to be careful:
if M.ncols() != n:
raise ValueError("the matrix 'M' must be square")
if not n.mod(4).is_zero():
- raise ValueError("the matrix 'M' must be a complex embedding")
+ raise ValueError("the matrix 'M' must be a quaternion embedding")
# Use the base ring of the matrix to ensure that its entries can be
# multiplied by elements of the quaternion algebra.
return RealMatrixEuclideanJordanAlgebra.natural_inner_product(X,Y)/4
-class QuaternionHermitianEJA(QuaternionMatrixEuclideanJordanAlgebra):
+class QuaternionHermitianEJA(QuaternionMatrixEuclideanJordanAlgebra,
+ KnownRankEJA):
"""
The rank-n simple EJA consisting of self-adjoint n-by-n quaternion
matrices, the usual symmetric Jordan product, and the
sage: from mjo.eja.eja_algebra import QuaternionHermitianEJA
+ EXAMPLES:
+
+ 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, RR)
+ Euclidean Jordan algebra of dimension 6 over Real Field with
+ 53 bits of precision
+
TESTS:
The dimension of this algebra is `2*n^2 - n`::
S.append(Sij_J)
Sij_K = cls.real_embed(K*Eij - K*Eij.transpose())
S.append(Sij_K)
- return S
+
+ # Since we embedded these, we can drop back to the "field" that we
+ # started with instead of the quaternion algebra "Q".
+ return ( s.change_ring(field) for s in S )
def __init__(self, n, field=QQ, **kwargs):
super(QuaternionHermitianEJA,self).__init__(field, basis, n, **kwargs)
-class JordanSpinEJA(FiniteDimensionalEuclideanJordanAlgebra):
+class JordanSpinEJA(FiniteDimensionalEuclideanJordanAlgebra, KnownRankEJA):
"""
The rank-2 simple EJA consisting of real vectors ``x=(x0, x_bar)``
with the usual inner product and jordan product ``x*y =