We've already completed one TODO by adding is_self_adjoint() for
operators. Another is completed by deciding that I don't want to drop
natural representations for non-matrix algebras. But I do want to
rename them, to "matrix representations," because that's what they
are.
6. Pass already_echelonized (default: False) and echelon_basis
(default: None) into the subalgebra constructor. The value of
already_echelonized can be passed to V.span_of_basis() to save
6. Pass already_echelonized (default: False) and echelon_basis
(default: None) into the subalgebra constructor. The value of
already_echelonized can be passed to V.span_of_basis() to save
- some time, and usinf e.g. FreeModule_submodule_with_basis_field
+ some time, and using e.g. FreeModule_submodule_with_basis_field
we may somehow be able to pass the echelon basis straight in to
save time.
we may somehow be able to pass the echelon basis straight in to
save time.
9. Compute the scalar in the general natural_inner_product() for
matrices, so no overrides are necessary.
9. Compute the scalar in the general natural_inner_product() for
matrices, so no overrides are necessary.
-10. Eliminate "natural representation" for non-matrix algebras.
-
-11. The main EJA element constructor is happy to convert between
+10. The main EJA element constructor is happy to convert between
e.g. HadamardEJA(3) and JordanSpinEJA(3).
e.g. HadamardEJA(3) and JordanSpinEJA(3).
+
+11. Figure out if CombinatorialFreeModule's use of IndexedGenerators
+ can be used to replace the matrix_basis().
mult_table,
prefix='e',
category=None,
mult_table,
prefix='e',
category=None,
check_field=True,
check_axioms=True):
"""
check_field=True,
check_axioms=True):
"""
if not all( len(l) == n for l in mult_table ):
raise ValueError("multiplication table is not square")
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()
if category is None:
category = MagmaticAlgebras(field).FiniteDimensional()
def _element_constructor_(self, elt):
"""
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
representation.
This gets called only after the parent element _call_ method
TESTS:
Ensure that we can convert any element of the two non-matrix
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: set_random_seed()
sage: J = HadamardEJA.random_instance()
sage: x = J.random_element()
sage: J(x.to_vector().column()) == x
True
sage: x = J.random_element()
sage: J(x.to_vector().column()) == x
True
"""
msg = "not an element of this algebra"
if elt == 0:
"""
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)
# 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
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.
# 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))
try:
coords = W.coordinate_vector(_mat2vec(elt))
return table(M, header_row=True, header_column=True, frame=True)
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.
sage: J = RealSymmetricEJA(2)
sage: J.basis()
Finite family {0: e0, 1: e1, 2: e2}
sage: J = RealSymmetricEJA(2)
sage: J.basis()
Finite family {0: e0, 1: e1, 2: e2}
- sage: J.natural_basis()
(
[1 0] [ 0 0.7071067811865475?] [0 0]
[0 0], [0.7071067811865475? 0], [0 1]
(
[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 = JordanSpinEJA(2)
sage: J.basis()
Finite family {0: e0, 1: e1}
- sage: J.natural_basis()
- 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 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`
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)
"""
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 MatrixSpace(self.base_ring(), self.dimension(), 1)
else:
- return self._natural_basis[0].matrix_space()
+ return self._matrix_basis[0].matrix_space()
Vector space of degree 6 and dimension 2...
sage: J1
Euclidean Jordan algebra of dimension 3...
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'
[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]
[ 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
[ 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()
- 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
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
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
sage: set_random_seed()
sage: J = ConcreteEuclideanJordanAlgebra.random_instance()
sage: x = J.random_element()
sage: x.operator().is_self_adjoint()
True
field = field.extension(p, 'sqrt2', embedding=RLF(2).sqrt())
basis = tuple( s.change_ring(field) for s in basis )
self._basis_normalizers = tuple(
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.
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.
# 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],
super(MatrixEuclideanJordanAlgebra, self).__init__(field,
mult_table,
super(MatrixEuclideanJordanAlgebra, self).__init__(field,
mult_table,
**kwargs)
if algebra_dim == 0:
**kwargs)
if algebra_dim == 0:
# entries in a nice field already. Just compute the thing.
return super(MatrixEuclideanJordanAlgebra, self)._charpoly_coefficients()
# 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.
self._basis_normalizers) )
# Do this over the rationals and convert back at the end.
raise NotImplementedError
@classmethod
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()
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: 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
sage: expected = (X*Y + Y*X)/2
sage: actual == expected
True
- 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::
its real embedding.
SETUP::
sage: set_random_seed()
sage: J = ComplexHermitianEJA.random_instance()
sage: x,y = J.random_elements(2)
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: 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
"""
sage: actual == expected
True
"""
- return RealMatrixEuclideanJordanAlgebra.natural_inner_product(X,Y)/2
+ return RealMatrixEuclideanJordanAlgebra.matrix_inner_product(X,Y)/2
class ComplexHermitianEJA(ComplexMatrixEuclideanJordanAlgebra,
class ComplexHermitianEJA(ComplexMatrixEuclideanJordanAlgebra,
sage: set_random_seed()
sage: J = ComplexHermitianEJA.random_instance()
sage: x,y = J.random_elements(2)
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
sage: expected = (X*Y + Y*X)/2
sage: actual == expected
True
- 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::
its real embedding.
SETUP::
sage: set_random_seed()
sage: J = QuaternionHermitianEJA.random_instance()
sage: x,y = J.random_elements(2)
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: 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
"""
sage: actual == expected
True
"""
- return RealMatrixEuclideanJordanAlgebra.natural_inner_product(X,Y)/4
+ return RealMatrixEuclideanJordanAlgebra.matrix_inner_product(X,Y)/4
class QuaternionHermitianEJA(QuaternionMatrixEuclideanJordanAlgebra,
class QuaternionHermitianEJA(QuaternionMatrixEuclideanJordanAlgebra,
sage: set_random_seed()
sage: J = QuaternionHermitianEJA.random_instance()
sage: x,y = J.random_elements(2)
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
sage: expected = (X*Y + Y*X)/2
sage: actual == expected
True
- def natural_representation(self):
- Return a more-natural representation of this element.
+ Return an (often more natural) representation of this element as a
+ matrix.
- 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" representation of this element as a Hermitian
- matrix, if it has one. If not, you get the usual representation.
+ Every finite-dimensional Euclidean Jordan Algebra is a direct
+ sum of five simple algebras, four of which comprise Hermitian
+ matrices. This method returns a "natural" matrix
+ representation of this element as either a Hermitian matrix or
+ column vector.
sage: J = ComplexHermitianEJA(3)
sage: J.one()
e0 + e3 + e8
sage: J = ComplexHermitianEJA(3)
sage: J.one()
e0 + e3 + e8
- sage: J.one().natural_representation()
+ sage: J.one().to_matrix()
[1 0 0 0 0 0]
[0 1 0 0 0 0]
[0 0 1 0 0 0]
[1 0 0 0 0 0]
[0 1 0 0 0 0]
[0 0 1 0 0 0]
sage: J = QuaternionHermitianEJA(3)
sage: J.one()
e0 + e5 + e14
sage: J = QuaternionHermitianEJA(3)
sage: J.one()
e0 + e5 + e14
- sage: J.one().natural_representation()
+ sage: J.one().to_matrix()
[1 0 0 0 0 0 0 0 0 0 0 0]
[0 1 0 0 0 0 0 0 0 0 0 0]
[0 0 1 0 0 0 0 0 0 0 0 0]
[1 0 0 0 0 0 0 0 0 0 0 0]
[0 1 0 0 0 0 0 0 0 0 0 0]
[0 0 1 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 1 0 0]
[0 0 0 0 0 0 0 0 0 0 1 0]
[0 0 0 0 0 0 0 0 0 0 0 1]
[0 0 0 0 0 0 0 0 0 1 0 0]
[0 0 0 0 0 0 0 0 0 0 1 0]
[0 0 0 0 0 0 0 0 0 0 0 1]
- B = self.parent().natural_basis()
- W = self.parent().natural_basis_space()
+ B = self.parent().matrix_basis()
+ W = self.parent().matrix_space()
# This is just a manual "from_vector()", but of course
# matrix spaces aren't vector spaces in sage, so they
# don't have a from_vector() method.
# This is just a manual "from_vector()", but of course
# matrix spaces aren't vector spaces in sage, so they
# don't have a from_vector() method.
- return W.linear_combination(zip(B,self.to_vector()))
+ return W.linear_combination( zip(B, self.to_vector()) )
- The natural representation of an element in the subalgebra is
- the same as its natural representation in the superalgebra::
+ The matrix representation of an element in the subalgebra is
+ the same as its matrix representation in the superalgebra::
sage: set_random_seed()
sage: A = random_eja().random_element().subalgebra_generated_by()
sage: y = A.random_element()
sage: set_random_seed()
sage: A = random_eja().random_element().subalgebra_generated_by()
sage: y = A.random_element()
- sage: actual = y.natural_representation()
- sage: expected = y.superalgebra_element().natural_representation()
+ sage: actual = y.to_matrix()
+ sage: expected = y.superalgebra_element().to_matrix()
sage: actual == expected
True
sage: actual == expected
True
sage: E22 = matrix(AA, [ [0,0],
....: [0,1] ])
sage: K1 = FiniteDimensionalEuclideanJordanSubalgebra(J, (J(E11),))
sage: E22 = matrix(AA, [ [0,0],
....: [0,1] ])
sage: K1 = FiniteDimensionalEuclideanJordanSubalgebra(J, (J(E11),))
- sage: K1.one().natural_representation()
+ sage: K1.one().to_matrix()
[1 0]
[0 0]
sage: K2 = FiniteDimensionalEuclideanJordanSubalgebra(J, (J(E22),))
[1 0]
[0 0]
sage: K2 = FiniteDimensionalEuclideanJordanSubalgebra(J, (J(E22),))
- sage: K2.one().natural_representation()
+ sage: K2.one().to_matrix()
mult_table[i][j] = W.coordinate_vector(product_vector)
self._inner_product_matrix = matrix(field, ip_table)
mult_table[i][j] = W.coordinate_vector(product_vector)
self._inner_product_matrix = matrix(field, ip_table)
- natural_basis = tuple( b.natural_representation() for b in basis )
+ matrix_basis = tuple( b.to_matrix() for b in basis )
mult_table,
prefix=prefix,
category=category,
mult_table,
prefix=prefix,
category=category,
- natural_basis=natural_basis,
+ matrix_basis=matrix_basis,
check_field=False,
check_axioms=check_axioms)
check_field=False,
check_axioms=check_axioms)
- def natural_basis_space(self):
+ def matrix_space(self):
- Return the natural basis space of this algebra, which is identical
- to that of its superalgebra.
+ Return the matrix space of this algebra, which is identical to
+ that of its superalgebra.
- This is correct "by definition," and avoids a mismatch when the
- subalgebra is trivial (with no natural basis to infer anything
- from) and the parent is not.
+ This is correct "by definition," and avoids a mismatch when
+ the subalgebra is trivial (with no matrix basis elements to
+ infer anything from) and the parent is not.
- return self.superalgebra().natural_basis_space()
+ return self.superalgebra().matrix_space()