mult_table,
prefix='e',
category=None,
- natural_basis=None,
+ matrix_basis=None,
check_field=True,
check_axioms=True):
"""
if not all( len(l) == n for l in mult_table ):
raise ValueError("multiplication table is not square")
- self._natural_basis = natural_basis
+ self._matrix_basis = matrix_basis
if category is None:
category = MagmaticAlgebras(field).FiniteDimensional()
def _element_constructor_(self, elt):
"""
- Construct an element of this algebra from its natural
+ Construct an element of this algebra from its vector or matrix
representation.
This gets called only after the parent element _call_ method
TESTS:
Ensure that we can convert any element of the two non-matrix
- simple algebras (whose natural representations are their usual
- vector representations) back and forth faithfully::
+ simple algebras (whose matrix representations are columns)
+ back and forth faithfully::
sage: set_random_seed()
sage: J = HadamardEJA.random_instance()
sage: x = J.random_element()
sage: J(x.to_vector().column()) == x
True
-
"""
msg = "not an element of this algebra"
if elt == 0:
# that the integer 3 belongs to the space of 2-by-2 matrices.
raise ValueError(msg)
- natural_basis = self.natural_basis()
- basis_space = natural_basis[0].matrix_space()
- if elt not in basis_space:
+ if elt not in self.matrix_space():
raise ValueError(msg)
# Thanks for nothing! Matrix spaces aren't vector spaces in
- # Sage, so we have to figure out its natural-basis coordinates
+ # Sage, so we have to figure out its matrix-basis coordinates
# ourselves. We use the basis space's ring instead of the
# 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.
- V = VectorSpace(basis_space.base_ring(), elt.nrows()*elt.ncols())
- W = V.span_of_basis( _mat2vec(s) for s in natural_basis )
+ V = VectorSpace(self.base_ring(), elt.nrows()*elt.ncols())
+ W = V.span_of_basis( _mat2vec(s) for s in self.matrix_basis() )
try:
coords = W.coordinate_vector(_mat2vec(elt))
return table(M, header_row=True, header_column=True, frame=True)
- def natural_basis(self):
+ def matrix_basis(self):
"""
- Return a more-natural representation of this algebra's basis.
+ Return an (often more natural) representation of this algebras
+ basis as an ordered tuple of matrices.
+
+ Every finite-dimensional Euclidean Jordan Algebra is a, up to
+ Jordan isomorphism, a direct sum of five simple
+ algebras---four of which comprise Hermitian matrices. And the
+ last type of algebra can of course be thought of as `n`-by-`1`
+ column matrices (ambiguusly called column vectors) to avoid
+ special cases. As a result, matrices (and column vectors) are
+ a natural representation format for Euclidean Jordan algebra
+ elements.
- Every finite-dimensional Euclidean Jordan Algebra is a direct
- sum of five simple algebras, four of which comprise Hermitian
- matrices. This method returns the original "natural" basis
- for our underlying vector space. (Typically, the natural basis
- is used to construct the multiplication table in the first place.)
+ But, when we construct an algebra from a basis of matrices,
+ those matrix representations are lost in favor of coordinate
+ vectors *with respect to* that basis. We could eventually
+ convert back if we tried hard enough, but having the original
+ representations handy is valuable enough that we simply store
+ them and return them from this method.
- Note that this will always return a matrix. The standard basis
- in `R^n` will be returned as `n`-by-`1` column matrices.
+ Why implement this for non-matrix algebras? Avoiding special
+ cases for the :class:`BilinearFormEJA` pays with simplicity in
+ its own right. But mainly, we would like to be able to assume
+ that elements of a :class:`DirectSumEJA` can be displayed
+ nicely, without having to have special classes for direct sums
+ one of whose components was a matrix algebra.
SETUP::
sage: J = RealSymmetricEJA(2)
sage: J.basis()
Finite family {0: e0, 1: e1, 2: e2}
- sage: J.natural_basis()
+ sage: J.matrix_basis()
(
[1 0] [ 0 0.7071067811865475?] [0 0]
[0 0], [0.7071067811865475? 0], [0 1]
sage: J = JordanSpinEJA(2)
sage: J.basis()
Finite family {0: e0, 1: e1}
- sage: J.natural_basis()
+ sage: J.matrix_basis()
(
[1] [0]
[0], [1]
)
-
"""
- if self._natural_basis is None:
- M = self.natural_basis_space()
+ if self._matrix_basis is None:
+ M = self.matrix_space()
return tuple( M(b.to_vector()) for b in self.basis() )
else:
- return self._natural_basis
+ return self._matrix_basis
- def natural_basis_space(self):
+ def matrix_space(self):
"""
- Return the matrix space in which this algebra's natural basis
- elements live.
+ Return the matrix space in which this algebra's elements live, if
+ we think of them as matrices (including column vectors of the
+ appropriate size).
Generally this will be an `n`-by-`1` column-vector space,
except when the algebra is trivial. There it's `n`-by-`n`
- (where `n` is zero), to ensure that two elements of the
- natural basis space (empty matrices) can be multiplied.
+ (where `n` is zero), to ensure that two elements of the matrix
+ space (empty matrices) can be multiplied.
+
+ Matrix algebras override this with something more useful.
"""
if self.is_trivial():
return MatrixSpace(self.base_ring(), 0)
- elif self._natural_basis is None or len(self._natural_basis) == 0:
+ elif self._matrix_basis is None or len(self._matrix_basis) == 0:
return MatrixSpace(self.base_ring(), self.dimension(), 1)
else:
- return self._natural_basis[0].matrix_space()
+ return self._matrix_basis[0].matrix_space()
@cached_method
Vector space of degree 6 and dimension 2...
sage: J1
Euclidean Jordan algebra of dimension 3...
- sage: J0.one().natural_representation()
+ sage: J0.one().to_matrix()
[0 0 0]
[0 0 0]
[0 0 1]
sage: orig_df = AA.options.display_format
sage: AA.options.display_format = 'radical'
- sage: J.from_vector(J5.basis()[0]).natural_representation()
+ sage: J.from_vector(J5.basis()[0]).to_matrix()
[ 0 0 1/2*sqrt(2)]
[ 0 0 0]
[1/2*sqrt(2) 0 0]
- sage: J.from_vector(J5.basis()[1]).natural_representation()
+ sage: J.from_vector(J5.basis()[1]).to_matrix()
[ 0 0 0]
[ 0 0 1/2*sqrt(2)]
[ 0 1/2*sqrt(2) 0]
sage: AA.options.display_format = orig_df
- sage: J1.one().natural_representation()
+ sage: J1.one().to_matrix()
[1 0 0]
[0 1 0]
[0 0 0]
TESTS:
- Our natural basis is normalized with respect to the natural inner
- product unless we specify otherwise::
+ Our basis is normalized with respect to the algebra's inner
+ product, unless we specify otherwise::
sage: set_random_seed()
sage: J = ConcreteEuclideanJordanAlgebra.random_instance()
sage: all( b.norm() == 1 for b in J.gens() )
True
- Since our natural basis is normalized with respect to the natural
- inner product, and since we know that this algebra is an EJA, any
+ Since our basis is orthonormal with respect to the algebra's inner
+ product, and since we know that this algebra is an EJA, any
left-multiplication operator's matrix will be symmetric because
- natural->EJA basis representation is an isometry and within the EJA
- the operator is self-adjoint by the Jordan axiom::
+ natural->EJA basis representation is an isometry and within the
+ EJA the operator is self-adjoint by the Jordan axiom::
sage: set_random_seed()
sage: J = ConcreteEuclideanJordanAlgebra.random_instance()
sage: x = J.random_element()
sage: x.operator().is_self_adjoint()
True
-
"""
@staticmethod
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 )
+ ~(self.matrix_inner_product(s,s).sqrt()) for s in basis )
basis = tuple(s*c for (s,c) in zip(basis,self._basis_normalizers))
# Now compute the multiplication and inner product tables.
# HACK: ignore the error here if we don't need the
# inner product (as is the case when we construct
# a dummy QQ-algebra for fast charpoly coefficients.
- ip_table[i][j] = self.natural_inner_product(basis[i],
+ ip_table[i][j] = self.matrix_inner_product(basis[i],
basis[j])
except:
pass
super(MatrixEuclideanJordanAlgebra, self).__init__(field,
mult_table,
- natural_basis=basis,
+ matrix_basis=basis,
**kwargs)
if algebra_dim == 0:
# entries in a nice field already. Just compute the thing.
return super(MatrixEuclideanJordanAlgebra, self)._charpoly_coefficients()
- basis = ( (b/n) for (b,n) in zip(self.natural_basis(),
+ basis = ( (b/n) for (b,n) in zip(self.matrix_basis(),
self._basis_normalizers) )
# Do this over the rationals and convert back at the end.
raise NotImplementedError
@classmethod
- def natural_inner_product(cls,X,Y):
+ def matrix_inner_product(cls,X,Y):
Xu = cls.real_unembed(X)
Yu = cls.real_unembed(Y)
tr = (Xu*Yu).trace()
sage: set_random_seed()
sage: J = RealSymmetricEJA.random_instance()
sage: x,y = J.random_elements(2)
- sage: actual = (x*y).natural_representation()
- sage: X = x.natural_representation()
- sage: Y = y.natural_representation()
+ sage: actual = (x*y).to_matrix()
+ sage: X = x.to_matrix()
+ sage: Y = y.to_matrix()
sage: expected = (X*Y + Y*X)/2
sage: actual == expected
True
@classmethod
- def natural_inner_product(cls,X,Y):
+ def matrix_inner_product(cls,X,Y):
"""
- Compute a natural inner product in this algebra directly from
+ Compute a matrix inner product in this algebra directly from
its real embedding.
SETUP::
sage: set_random_seed()
sage: J = ComplexHermitianEJA.random_instance()
sage: x,y = J.random_elements(2)
- sage: Xe = x.natural_representation()
- sage: Ye = y.natural_representation()
+ sage: Xe = x.to_matrix()
+ sage: Ye = y.to_matrix()
sage: X = ComplexHermitianEJA.real_unembed(Xe)
sage: Y = ComplexHermitianEJA.real_unembed(Ye)
sage: expected = (X*Y).trace().real()
- sage: actual = ComplexHermitianEJA.natural_inner_product(Xe,Ye)
+ sage: actual = ComplexHermitianEJA.matrix_inner_product(Xe,Ye)
sage: actual == expected
True
"""
- return RealMatrixEuclideanJordanAlgebra.natural_inner_product(X,Y)/2
+ return RealMatrixEuclideanJordanAlgebra.matrix_inner_product(X,Y)/2
class ComplexHermitianEJA(ComplexMatrixEuclideanJordanAlgebra,
sage: set_random_seed()
sage: J = ComplexHermitianEJA.random_instance()
sage: x,y = J.random_elements(2)
- sage: actual = (x*y).natural_representation()
- sage: X = x.natural_representation()
- sage: Y = y.natural_representation()
+ sage: actual = (x*y).to_matrix()
+ sage: X = x.to_matrix()
+ sage: Y = y.to_matrix()
sage: expected = (X*Y + Y*X)/2
sage: actual == expected
True
@classmethod
- def natural_inner_product(cls,X,Y):
+ def matrix_inner_product(cls,X,Y):
"""
- Compute a natural inner product in this algebra directly from
+ Compute a matrix inner product in this algebra directly from
its real embedding.
SETUP::
sage: set_random_seed()
sage: J = QuaternionHermitianEJA.random_instance()
sage: x,y = J.random_elements(2)
- sage: Xe = x.natural_representation()
- sage: Ye = y.natural_representation()
+ sage: Xe = x.to_matrix()
+ sage: Ye = y.to_matrix()
sage: X = QuaternionHermitianEJA.real_unembed(Xe)
sage: Y = QuaternionHermitianEJA.real_unembed(Ye)
sage: expected = (X*Y).trace().coefficient_tuple()[0]
- sage: actual = QuaternionHermitianEJA.natural_inner_product(Xe,Ye)
+ sage: actual = QuaternionHermitianEJA.matrix_inner_product(Xe,Ye)
sage: actual == expected
True
"""
- return RealMatrixEuclideanJordanAlgebra.natural_inner_product(X,Y)/4
+ return RealMatrixEuclideanJordanAlgebra.matrix_inner_product(X,Y)/4
class QuaternionHermitianEJA(QuaternionMatrixEuclideanJordanAlgebra,
sage: set_random_seed()
sage: J = QuaternionHermitianEJA.random_instance()
sage: x,y = J.random_elements(2)
- sage: actual = (x*y).natural_representation()
- sage: X = x.natural_representation()
- sage: Y = y.natural_representation()
+ sage: actual = (x*y).to_matrix()
+ sage: X = x.to_matrix()
+ sage: Y = y.to_matrix()
sage: expected = (X*Y + Y*X)/2
sage: actual == expected
True