# as well as a subspace W of V spanned by those (vectorized)
# basis elements. The W-coordinates are the coefficients that
# we see in things like x = 1*b1 + 2*b2.
- vector_basis = basis
degree = 0
if n > 0:
# its own set of non-ambient coordinates (in terms of the
# supplied basis).
vector_basis = tuple( V(_all2list(b)) for b in basis )
- W = V.span_of_basis( vector_basis, check=check_axioms)
+
+ # Save the span of our matrix basis (when written out as long
+ # vectors) because otherwise we'll have to reconstruct it
+ # every time we want to coerce a matrix into the algebra.
+ self._matrix_span = V.span_of_basis( vector_basis, check=check_axioms)
if orthonormalize:
- # 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.
+ # Now "self._matrix_span" is the vector space of our
+ # algebra coordinates. The variables "X1", "X2",... refer
+ # to the entries of vectors in self._matrix_span. Thus to
+ # convert back and forth between the orthonormal
+ # coordinates and the given ones, we need to stick the
+ # original basis in self._matrix_span.
U = V.span_of_basis( deortho_vector_basis, check=check_axioms)
- self._deortho_matrix = matrix( U.coordinate_vector(q)
- for q in vector_basis )
+ self._deortho_matrix = matrix.column( U.coordinate_vector(q)
+ for q in vector_basis )
# Now we actually compute the multiplication and inner-product
# The jordan product returns a matrixy answer, so we
# have to convert it to the algebra coordinates.
elt = jordan_product(q_i, q_j)
- elt = W.coordinate_vector(V(_all2list(elt)))
+ elt = self._matrix_span.coordinate_vector(V(_all2list(elt)))
self._multiplication_table[i][j] = self.from_vector(elt)
if not orthonormalize:
def _element_constructor_(self, elt):
"""
- Construct an element of this algebra from its vector or matrix
- representation.
+ Construct an element of this algebra or a subalgebra from its
+ EJA element, vector, or matrix representation.
This gets called only after the parent element _call_ method
fails to find a coercion for the argument.
sage: J( (J1.matrix_basis()[1], J2.matrix_basis()[2]) )
b1 + b5
+ Subalgebra elements are embedded into the superalgebra::
+
+ sage: J = JordanSpinEJA(3)
+ sage: J.one()
+ b0
+ sage: x = sum(J.gens())
+ sage: A = x.subalgebra_generated_by()
+ sage: J(A.one())
+ b0
+
TESTS:
Ensure that we can convert any element back and forth
Traceback (most recent call last):
...
ValueError: not an element of this algebra
+
"""
msg = "not an element of this algebra"
if elt in self.base_ring():
# that the integer 3 belongs to the space of 2-by-2 matrices.
raise ValueError(msg)
+ if hasattr(elt, 'superalgebra_element'):
+ # Handle subalgebra elements
+ if elt.parent().superalgebra() == self:
+ return elt.superalgebra_element()
+
try:
# Try to convert a vector into a column-matrix...
elt = elt.column()
# is that we're already converting everything to long vectors,
# and that strategy works for tuples as well.
#
- # We pass check=False because the matrix basis is "guaranteed"
- # to be linearly independent... right? Ha ha.
- elt = _all2list(elt)
- V = VectorSpace(self.base_ring(), len(elt))
- W = V.span_of_basis( (V(_all2list(s)) for s in self.matrix_basis()),
- check=False)
+ elt = self._matrix_span.ambient_vector_space()(_all2list(elt))
try:
- coords = W.coordinate_vector(V(elt))
+ coords = self._matrix_span.coordinate_vector(elt)
except ArithmeticError: # vector is not in free module
raise ValueError(msg)
def random_instance(cls, max_dimension=None, *args, **kwargs):
"""
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.
+ is less than or equal to the lesser of ``max_dimension`` and
+ the value returned by ``_max_random_instance_dimension()``. If
+ the dimension bound is omitted, then only the
+ ``_max_random_instance_dimension()`` is used as a bound.
This method should be implemented in each subclass.
+
+ SETUP::
+
+ sage: from mjo.eja.eja_algebra import ConcreteEJA
+
+ TESTS:
+
+ Both the class bound and the ``max_dimension`` argument are upper
+ bounds on the dimension of the algebra returned::
+
+ sage: from sage.misc.prandom import choice
+ sage: eja_class = choice(ConcreteEJA.__subclasses__())
+ sage: class_max_d = eja_class._max_random_instance_dimension()
+ sage: J = eja_class.random_instance(max_dimension=20,
+ ....: field=QQ,
+ ....: orthonormalize=False)
+ sage: J.dimension() <= class_max_d
+ True
+ sage: J = eja_class.random_instance(max_dimension=2,
+ ....: field=QQ,
+ ....: orthonormalize=False)
+ sage: J.dimension() <= 2
+ True
+
"""
from sage.misc.prandom import choice
eja_class = choice(cls.__subclasses__())
# These all bubble up to the RationalBasisEJA superclass
# constructor, so any (kw)args valid there are also valid
# here.
- return eja_class.random_instance(*args, **kwargs)
+ return eja_class.random_instance(max_dimension, *args, **kwargs)
class MatrixEJA(FiniteDimensionalEJA):
return ZZ(int(ZZ(8*max_dimension + 1).sqrt()/2 - 1/2))
@classmethod
- def random_instance(cls, max_dimension=None, **kwargs):
+ def random_instance(cls, max_dimension=None, *args, **kwargs):
"""
Return a random instance of this type of algebra.
"""
- 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)
+ class_max_d = cls._max_random_instance_dimension()
+ if (max_dimension is None or max_dimension > class_max_d):
+ max_dimension = class_max_d
+ max_size = cls._max_random_instance_size(max_dimension)
+ n = ZZ.random_element(max_size + 1)
return cls(n, **kwargs)
def __init__(self, n, field=AA, **kwargs):
return ZZ(int(ZZ(max_dimension).sqrt()))
@classmethod
- def random_instance(cls, max_dimension=None, **kwargs):
+ def random_instance(cls, max_dimension=None, *args, **kwargs):
"""
Return a random instance of this type of algebra.
"""
- if max_dimension is None:
- max_dimension = cls._max_random_instance_dimension()
- n = ZZ.random_element(cls._max_random_instance_size(max_dimension) + 1)
+ class_max_d = cls._max_random_instance_dimension()
+ if (max_dimension is None or max_dimension > class_max_d):
+ max_dimension = class_max_d
+ max_size = cls._max_random_instance_size(max_dimension)
+ n = ZZ.random_element(max_size + 1)
return cls(n, **kwargs)
return ZZ(int(ZZ(8*max_dimension + 1).sqrt()/4 + 1/4))
@classmethod
- def random_instance(cls, max_dimension=None, **kwargs):
+ def random_instance(cls, max_dimension=None, *args, **kwargs):
"""
Return a random instance of this type of algebra.
"""
- if max_dimension is None:
- max_dimension = cls._max_random_instance_dimension()
- n = ZZ.random_element(cls._max_random_instance_size(max_dimension) + 1)
+ class_max_d = cls._max_random_instance_dimension()
+ if (max_dimension is None or max_dimension > class_max_d):
+ max_dimension = class_max_d
+ max_size = cls._max_random_instance_size(max_dimension)
+ n = ZZ.random_element(max_size + 1)
return cls(n, **kwargs)
class OctonionHermitianEJA(MatrixEJA, RationalBasisEJA, ConcreteEJA):
return 0
@classmethod
- def random_instance(cls, max_dimension=None, **kwargs):
+ def random_instance(cls, max_dimension=None, *args, **kwargs):
"""
Return a random instance of this type of algebra.
"""
- if max_dimension is None:
- max_dimension = cls._max_random_instance_dimension()
- n = ZZ.random_element(cls._max_random_instance_size(max_dimension) + 1)
+ class_max_d = cls._max_random_instance_dimension()
+ if (max_dimension is None or max_dimension > class_max_d):
+ max_dimension = class_max_d
+ max_size = cls._max_random_instance_size(max_dimension)
+ n = ZZ.random_element(max_size + 1)
return cls(n, **kwargs)
def __init__(self, n, field=AA, **kwargs):
return max_dimension
@classmethod
- def random_instance(cls, max_dimension=None, **kwargs):
+ def random_instance(cls, max_dimension=None, *args, **kwargs):
"""
Return a random instance of this type of algebra.
"""
- if max_dimension is None:
- max_dimension = cls._max_random_instance_dimension()
- n = ZZ.random_element(cls._max_random_instance_size(max_dimension) + 1)
+ class_max_d = cls._max_random_instance_dimension()
+ if (max_dimension is None or max_dimension > class_max_d):
+ max_dimension = class_max_d
+ max_size = cls._max_random_instance_size(max_dimension)
+ n = ZZ.random_element(max_size + 1)
return cls(n, **kwargs)
return max_dimension
@classmethod
- def random_instance(cls, max_dimension=None, **kwargs):
+ def random_instance(cls, max_dimension=None, *args, **kwargs):
"""
Return a random instance of this algebra.
"""
- if max_dimension is None:
- max_dimension = cls._max_random_instance_dimension()
- n = ZZ.random_element(cls._max_random_instance_size(max_dimension) + 1)
+ class_max_d = cls._max_random_instance_dimension()
+ if (max_dimension is None or max_dimension > class_max_d):
+ max_dimension = class_max_d
+ max_size = cls._max_random_instance_size(max_dimension)
+ n = ZZ.random_element(max_size + 1)
+
if n.is_zero():
B = matrix.identity(ZZ, n)
return cls(B, **kwargs)
alpha = ZZ.zero()
while alpha.is_zero():
alpha = ZZ.random_element().abs()
+
B22 = M.transpose()*M + alpha*I
from sage.matrix.special import block_matrix
super().__init__(B, *args, **kwargs)
@classmethod
- def random_instance(cls, max_dimension=None, **kwargs):
+ def random_instance(cls, max_dimension=None, *args, **kwargs):
"""
Return a random instance of this type of algebra.
Needed here to override the implementation for ``BilinearFormEJA``.
"""
- if max_dimension is None:
- max_dimension = cls._max_random_instance_dimension()
- n = ZZ.random_element(cls._max_random_instance_size(max_dimension) + 1)
+ class_max_d = cls._max_random_instance_dimension()
+ if (max_dimension is None or max_dimension > class_max_d):
+ max_dimension = class_max_d
+ max_size = cls._max_random_instance_size(max_dimension)
+ n = ZZ.random_element(max_size + 1)
return cls(n, **kwargs)
self.one.set_cache( self.zero() )
@classmethod
- def random_instance(cls, **kwargs):
+ def random_instance(cls, max_dimension=None, *args, **kwargs):
# We don't take a "size" argument so the superclass method is
- # inappropriate for us.
+ # inappropriate for us. The ``max_dimension`` argument is
+ # included so that if this method is called generically with a
+ # ``max_dimension=<whatever>`` argument, we don't try to pass
+ # it on to the algebra constructor.
return cls(**kwargs)
RationalBasisEJA.CartesianProduct = RationalBasisCartesianProductEJA
def random_eja(max_dimension=None, *args, **kwargs):
+ r"""
+
+ SETUP::
+
+ sage: from mjo.eja.eja_algebra import random_eja
+
+ TESTS::
+
+ sage: set_random_seed()
+ sage: n = ZZ.random_element(1,5)
+ sage: J = random_eja(max_dimension=n, field=QQ, orthonormalize=False)
+ sage: J.dimension() <= n
+ True
+
+ """
# 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: