The ``field`` we're given must be real with ``check_field=True``::
- sage: JordanSpinEJA(2,QQbar)
+ sage: JordanSpinEJA(2, QQbar)
Traceback (most recent call last):
...
ValueError: scalar field is not real
+ sage: JordanSpinEJA(2, QQbar, check_field=False)
+ Euclidean Jordan algebra of dimension 2 over Algebraic Field
The multiplication table must be square with ``check_axioms=True``::
# element's ring because the basis space might be an algebraic
# closure whereas the base ring of the 3-by-3 identity matrix
# could be QQ instead of QQbar.
+ #
+ # We pass check=False because the matrix basis is "guaranteed"
+ # to be linearly independent... right? Ha ha.
V = VectorSpace(self.base_ring(), elt.nrows()*elt.ncols())
- W = V.span_of_basis( _mat2vec(s) for s in self.matrix_basis() )
+ W = V.span_of_basis( (_mat2vec(s) for s in self.matrix_basis()),
+ check=False)
try:
coords = W.coordinate_vector(_mat2vec(elt))
check_field=True,
check_axioms=True):
+ if check_field:
+ # Abuse the check_field parameter to check that the entries of
+ # out basis (in ambient coordinates) are in the field QQ.
+ if not all( all(b_i in QQ for b_i in b.list()) for b in basis ):
+ raise TypeError("basis not rational")
+
n = len(basis)
vector_basis = basis
# deorthonormalized basis. These can be used later to
# construct a deorthonormalized copy of this algebra over QQ
# in which several operations are much faster.
- self._deortho_multiplication_table = None
- self._deortho_inner_product_table = None
+ self._rational_algebra = None
if orthonormalize:
- # Compute the deorthonormalized tables before we orthonormalize
- # the given basis.
- W = V.span_of_basis( vector_basis )
+ if self.base_ring() is not QQ:
+ # There's no point in constructing the extra algebra if this
+ # one is already rational. If the original basis is rational
+ # but normalization would make it irrational, then this whole
+ # constructor will just fail anyway as it tries to stick an
+ # irrational number into a rational algebra.
+ #
+ # Note: the same Jordan and inner-products work here,
+ # because they are necessarily defined with respect to
+ # ambient coordinates and not any particular basis.
+ self._rational_algebra = RationalBasisEuclideanJordanAlgebra(
+ QQ,
+ basis,
+ jordan_product,
+ inner_product,
+ orthonormalize=False,
+ prefix=prefix,
+ category=category,
+ check_field=False,
+ check_axioms=False)
- # TODO: use symmetry
- self._deortho_multiplication_table = [ [0 for j in range(n)]
- for i in range(n) ]
- self._deortho_inner_product_table = [ [0 for j in range(n)]
- for i in range(n) ]
+ # Compute the deorthonormalized tables before we orthonormalize
+ # the given basis. The "check" parameter here guarantees that
+ # the basis is linearly-independent.
+ W = V.span_of_basis( vector_basis, check=check_axioms)
# Note: the Jordan and inner-products are defined in terms
# of the ambient basis. It's important that their arguments
# table is in terms of vectors
elt = _mat2vec(elt)
- # TODO: use symmetry
- elt = W.coordinate_vector(elt)
- self._deortho_multiplication_table[i][j] = elt
- self._deortho_multiplication_table[j][i] = elt
- self._deortho_inner_product_table[i][j] = ip
- self._deortho_inner_product_table[j][i] = ip
-
- if self._deortho_multiplication_table is not None:
- self._deortho_multiplication_table = tuple(map(tuple, self._deortho_multiplication_table))
- if self._deortho_inner_product_table is not None:
- self._deortho_inner_product_table = tuple(map(tuple, self._deortho_inner_product_table))
-
# We overwrite the name "vector_basis" in a second, but never modify it
# in place, to this effectively makes a copy of it.
deortho_vector_basis = vector_basis
else:
vector_basis = gram_schmidt(vector_basis, inner_product)
- W = V.span_of_basis( vector_basis )
-
# Normalize the "matrix" basis, too!
basis = vector_basis
if basis_is_matrices:
basis = tuple( map(_vec2mat,basis) )
- W = V.span_of_basis( vector_basis )
+ W = V.span_of_basis( vector_basis, check=check_axioms)
# Now "W" is the vector space of our algebra coordinates. The
# variables "X1", "X2",... refer to the entries of vectors in
# W. Thus to convert back and forth between the orthonormal
# coordinates and the given ones, we need to stick the original
# basis in W.
- U = V.span_of_basis( deortho_vector_basis )
+ U = V.span_of_basis( deortho_vector_basis, check=check_axioms)
self._deortho_matrix = matrix( U.coordinate_vector(q)
for q in vector_basis )
- # TODO: use symmetry
- mult_table = [ [0 for j in range(n)] for i in range(n) ]
- ip_table = [ [0 for j in range(n)] for i in range(n) ]
+ # If the superclass constructor is going to verify the
+ # symmetry of this table, it has better at least be
+ # square...
+ if check_axioms:
+ mult_table = [ [0 for j in range(n)] for i in range(n) ]
+ ip_table = [ [0 for j in range(n)] for i in range(n) ]
+ else:
+ mult_table = [ [0 for j in range(i+1)] for i in range(n) ]
+ ip_table = [ [0 for j in range(i+1)] for i in range(n) ]
# Note: the Jordan and inner-products are defined in terms
# of the ambient basis. It's important that their arguments
# table is in terms of vectors
elt = _mat2vec(elt)
- # TODO: use symmetry
elt = W.coordinate_vector(elt)
mult_table[i][j] = elt
- mult_table[j][i] = elt
ip_table[i][j] = ip
- ip_table[j][i] = ip
+ if check_axioms:
+ # The tables are square if we're verifying that they
+ # are commutative.
+ mult_table[j][i] = elt
+ ip_table[j][i] = ip
if basis_is_matrices:
for m in basis:
Algebraic Real Field
"""
- if self.base_ring() is QQ:
+ if self.base_ring() is QQ or self._rational_algebra is None:
# There's no need to construct *another* algebra over the
- # rationals if this one is already over the rationals.
+ # rationals if this one is already over the
+ # rationals. Likewise, if we never orthonormalized our
+ # basis, we might as well just use the given one.
superclass = super(RationalBasisEuclideanJordanAlgebra, self)
return superclass._charpoly_coefficients()
# Do the computation over the rationals. The answer will be
# the same, because all we've done is a change of basis.
- J = FiniteDimensionalEuclideanJordanAlgebra(QQ,
- self._deortho_multiplication_table,
- self._deortho_inner_product_table)
-
- # Change back from QQ to our real base ring
+ # Then, change back from QQ to our real base ring
a = ( a_i.change_ring(self.base_ring())
- for a_i in J._charpoly_coefficients() )
+ for a_i in self._rational_algebra._charpoly_coefficients() )
# Now convert the coordinate variables back to the
# deorthonormalized ones.
else:
Sij = Eij + Eij.transpose()
S.append(Sij)
- return S
+ return tuple(S)
@staticmethod
def inner_product(x,y):
return x.inner_product(y)
+ # Don't orthonormalize because our basis is already
+ # orthonormal with respect to our inner-product. But also
+ # don't pass check_field=False here, because the user can pass
+ # in a field!
super(HadamardEJA, self).__init__(field,
basis,
jordan_product,
inner_product,
+ orthonormalize=False,
+ check_axioms=False,
**kwargs)
self.rank.set_cache(n)
"""
def __init__(self, n, field=AA, **kwargs):
- # This is a special case of the BilinearFormEJA with the identity
- # matrix as its bilinear form.
+ # This is a special case of the BilinearFormEJA with the
+ # identity matrix as its bilinear form.
B = matrix.identity(field, n)
- super(JordanSpinEJA, self).__init__(B, field, **kwargs)
+
+ # Don't orthonormalize because our basis is already
+ # orthonormal with respect to our inner-product. But
+ # also don't pass check_field=False here, because the
+ # user can pass in a field!
+ super(JordanSpinEJA, self).__init__(B,
+ field,
+ orthonormalize=False,
+ check_axioms=False,
+ **kwargs)
@staticmethod
def _max_random_instance_size():
n2 = J2.dimension()
n = n1+n2
V = VectorSpace(field, n)
- mult_table = [ [ V.zero() for j in range(n) ]
+ mult_table = [ [ V.zero() for j in range(i+1) ]
for i in range(n) ]
for i in range(n1):
- for j in range(n1):
+ for j in range(i+1):
p = (J1.monomial(i)*J1.monomial(j)).to_vector()
mult_table[i][j] = V(p.list() + [field.zero()]*n2)
for i in range(n2):
- for j in range(n2):
+ for j in range(i+1):
p = (J2.monomial(i)*J2.monomial(j)).to_vector()
mult_table[n1+i][n1+j] = V([field.zero()]*n1 + p.list())
# TODO: build the IP table here from the two constituent IP
# matrices (it'll be block diagonal, I think).
- ip_table = None
+ ip_table = [ [ field.zero() for j in range(i+1) ]
+ for i in range(n) ]
super(DirectSumEJA, self).__init__(field,
mult_table,
ip_table,