"""
from sage.categories.finite_dimensional_algebras_with_basis import FiniteDimensionalAlgebrasWithBasis
-from sage.categories.morphism import SetMorphism
-from sage.modules.vector_space_morphism import VectorSpaceMorphism
+from sage.categories.map import Map
from sage.structure.element import is_Matrix
from sage.structure.category_object import normalize_names
from sage.algebras.finite_dimensional_algebras.finite_dimensional_algebra_element import FiniteDimensionalAlgebraElement
-class FiniteDimensionalEuclideanJordanAlgebraOperator(VectorSpaceMorphism):
+class FiniteDimensionalEuclideanJordanAlgebraOperator(Map):
def __init__(self, domain_eja, codomain_eja, mat):
- # We save these so that we can output them as part of our
- # text representation. Overriding the domain/codomain methods
- # doesn't work because the EJAs aren't (directly) vector spaces.
- self._domain_eja = domain_eja
- self._codomain_eja = codomain_eja
-
- # Otherwise, we just feed everything to the vector space morphism
- # constructor.
- V = domain_eja.vector_space()
- W = codomain_eja.vector_space()
- homspace = V.Hom(W)
- VectorSpaceMorphism.__init__(self, homspace, mat)
-
-
- def __call__(self, x):
+ if not (
+ isinstance(domain_eja, FiniteDimensionalEuclideanJordanAlgebra) and
+ isinstance(codomain_eja, FiniteDimensionalEuclideanJordanAlgebra) ):
+ raise ValueError('(co)domains must be finite-dimensional Euclidean '
+ 'Jordan algebras')
+
+ F = domain_eja.base_ring()
+ if not (F == codomain_eja.base_ring()):
+ raise ValueError("domain and codomain must have the same base ring")
+
+ # We need to supply something here to avoid getting the
+ # default Homset of the parent FiniteDimensionalAlgebra class,
+ # which messes up e.g. equality testing. We use FreeModules(F)
+ # instead of VectorSpaces(F) because our characteristic polynomial
+ # algorithm will need to F to be a polynomial ring at some point.
+ # When F is a field, FreeModules(F) returns VectorSpaces(F) anyway.
+ parent = Hom(domain_eja, codomain_eja, FreeModules(F))
+
+ # The Map initializer will set our parent to a homset, which
+ # is explicitly NOT what we want, because these ain't algebra
+ # homomorphisms.
+ super(FiniteDimensionalEuclideanJordanAlgebraOperator,self).__init__(parent)
+
+ # Keep a matrix around to do all of the real work. It would
+ # be nice if we could use a VectorSpaceMorphism instead, but
+ # those use row vectors that we don't want to accidentally
+ # expose to our users.
+ self._matrix = mat
+
+
+ def _call_(self, x):
"""
Allow this operator to be called only on elements of an EJA.
True
"""
- # Overriding the single-underscore _call_ didn't work?
- if x not in self._domain_eja:
- raise ValueError("argument does not live in the operator's domain")
- return self._codomain_eja(self.matrix()*x.vector())
+ return self.codomain()(self.matrix()*x.vector())
- def _repr_(self):
- r"""
-
- A text representation of this linear operator on a Euclidean
- Jordan Algebra.
-
- EXAMPLES::
-
- sage: J = JordanSpinEJA(2)
- sage: id = identity_matrix(J.base_ring(), J.dimension())
- sage: FiniteDimensionalEuclideanJordanAlgebraOperator(J,J,id)
- Linear operator between finite-dimensional Euclidean Jordan
- algebras represented by the matrix:
- [1 0]
- [0 1]
- Domain: Euclidean Jordan algebra of degree 2 over Rational Field
- Codomain: Euclidean Jordan algebra of degree 2 over Rational Field
-
- """
- msg = ("Linear operator between finite-dimensional Euclidean Jordan "
- "algebras represented by the matrix:\n",
- "{!r}\n",
- "Domain: {}\n",
- "Codomain: {}")
- return ''.join(msg).format(self.matrix(),
- self._domain_eja,
- self._codomain_eja)
-
-
- def __add__(self, other):
+ def _add_(self, other):
"""
Add the ``other`` EJA operator to this one.
sage: f + g
Traceback (most recent call last):
...
- ValueError: operator (co)domains must match
+ TypeError: unsupported operand parent(s) for +: ...
+
+ """
+ return FiniteDimensionalEuclideanJordanAlgebraOperator(
+ self.domain(),
+ self.codomain(),
+ self.matrix() + other.matrix())
+
+
+ def _composition_(self, other, homset):
+ """
+ Compose two EJA operators to get another one (and NOT a formal
+ composite object) back.
+
+ EXAMPLES::
+
+ sage: J1 = JordanSpinEJA(3)
+ sage: J2 = RealCartesianProductEJA(2)
+ sage: J3 = RealSymmetricEJA(1)
+ sage: mat1 = matrix(QQ, [[1,2,3],
+ ....: [4,5,6]])
+ sage: mat2 = matrix(QQ, [[7,8]])
+ sage: g = FiniteDimensionalEuclideanJordanAlgebraOperator(J1,
+ ....: J2,
+ ....: mat1)
+ sage: f = FiniteDimensionalEuclideanJordanAlgebraOperator(J2,
+ ....: J3,
+ ....: mat2)
+ sage: f*g
+ Linear operator between finite-dimensional Euclidean Jordan
+ algebras represented by the matrix:
+ [39 54 69]
+ Domain: Euclidean Jordan algebra of degree 3 over Rational Field
+ Codomain: Euclidean Jordan algebra of degree 1 over Rational Field
"""
- if not (self._domain_eja == other._domain_eja and
- self._codomain_eja == other._codomain_eja):
- raise ValueError("operator (co)domains must match")
return FiniteDimensionalEuclideanJordanAlgebraOperator(
- self._domain_eja,
- self._codomain_eja,
- VectorSpaceMorphism.__add__(self,other))
+ other.domain(),
+ self.codomain(),
+ self.matrix()*other.matrix())
+
+ def __eq__(self, other):
+ if self.domain() != other.domain():
+ return False
+ if self.codomain() != other.codomain():
+ return False
+ if self.matrix() != other.matrix():
+ return False
+ return True
def __invert__(self):
"""
"""
return FiniteDimensionalEuclideanJordanAlgebraOperator(
- self._codomain_eja,
- self._domain_eja,
- VectorSpaceMorphism.__invert__(self))
+ self.codomain(),
+ self.domain(),
+ ~self.matrix())
+
def __mul__(self, other):
"""
- Compose this EJA operator with the ``other`` one, or scale it by
- an element of its base ring.
+ Compose two EJA operators, or scale myself by an element of the
+ ambient vector space.
+
+ We need to override the real ``__mul__`` function to prevent the
+ coercion framework from throwing an error when it fails to convert
+ a base ring element into a morphism.
+
+ EXAMPLES:
+
+ We can scale an operator on a rational algebra by a rational number::
+
+ sage: J = RealSymmetricEJA(2)
+ sage: e0,e1,e2 = J.gens()
+ sage: x = 2*e0 + 4*e1 + 16*e2
+ sage: x.operator()
+ Linear operator between finite-dimensional Euclidean Jordan algebras
+ represented by the matrix:
+ [ 2 4 0]
+ [ 2 9 2]
+ [ 0 4 16]
+ Domain: Euclidean Jordan algebra of degree 3 over Rational Field
+ Codomain: Euclidean Jordan algebra of degree 3 over Rational Field
+ sage: x.operator()*(1/2)
+ Linear operator between finite-dimensional Euclidean Jordan algebras
+ represented by the matrix:
+ [ 1 2 0]
+ [ 1 9/2 1]
+ [ 0 2 8]
+ Domain: Euclidean Jordan algebra of degree 3 over Rational Field
+ Codomain: Euclidean Jordan algebra of degree 3 over Rational Field
+
"""
- if other in self._codomain_eja.base_ring():
+ if other in self.codomain().base_ring():
return FiniteDimensionalEuclideanJordanAlgebraOperator(
- self._domain_eja,
- self._codomain_eja,
- self._matrix*other)
+ self.domain(),
+ self.codomain(),
+ self.matrix()*other)
- if not (self._domain_eja == other._codomain_eja):
- raise ValueError("operator (co)domains must be compatible")
-
- return FiniteDimensionalEuclideanJordanAlgebraOperator(
- other._domain_eja,
- self._codomain_eja,
- VectorSpaceMorphism.__mul__(self,other))
+ # This should eventually delegate to _composition_ after performing
+ # some sanity checks for us.
+ mor = super(FiniteDimensionalEuclideanJordanAlgebraOperator,self)
+ return mor.__mul__(other)
- def __neg__(self):
+ def _neg_(self):
"""
Negate this EJA operator.
"""
return FiniteDimensionalEuclideanJordanAlgebraOperator(
- self._domain_eja,
- self._codomain_eja,
- VectorSpaceMorphism.__neg__(self))
+ self.domain(),
+ self.codomain(),
+ -self.matrix())
def __pow__(self, n):
cols = self.domain().dimension()
mat = matrix.identity(self.base_ring(), rows, cols)
else:
- mat = VectorSpaceMorphism.__pow__(self,n)
+ mat = self.matrix()**n
return FiniteDimensionalEuclideanJordanAlgebraOperator(
- self._domain_eja,
- self._codomain_eja,
+ self.domain(),
+ self.codomain(),
mat)
- def __sub__(self, other):
+
+ def _repr_(self):
+ r"""
+
+ A text representation of this linear operator on a Euclidean
+ Jordan Algebra.
+
+ EXAMPLES::
+
+ sage: J = JordanSpinEJA(2)
+ sage: id = identity_matrix(J.base_ring(), J.dimension())
+ sage: FiniteDimensionalEuclideanJordanAlgebraOperator(J,J,id)
+ Linear operator between finite-dimensional Euclidean Jordan
+ algebras represented by the matrix:
+ [1 0]
+ [0 1]
+ Domain: Euclidean Jordan algebra of degree 2 over Rational Field
+ Codomain: Euclidean Jordan algebra of degree 2 over Rational Field
+
+ """
+ msg = ("Linear operator between finite-dimensional Euclidean Jordan "
+ "algebras represented by the matrix:\n",
+ "{!r}\n",
+ "Domain: {}\n",
+ "Codomain: {}")
+ return ''.join(msg).format(self.matrix(),
+ self.domain(),
+ self.codomain())
+
+
+ def _sub_(self, other):
"""
Subtract ``other`` from this EJA operator.
+
+ EXAMPLES::
+
+ sage: J = RealSymmetricEJA(2)
+ sage: id = identity_matrix(J.base_ring(),J.dimension())
+ sage: f = FiniteDimensionalEuclideanJordanAlgebraOperator(J,J,id)
+ sage: f - (f*2)
+ Linear operator between finite-dimensional Euclidean Jordan
+ algebras represented by the matrix:
+ [-1 0 0]
+ [ 0 -1 0]
+ [ 0 0 -1]
+ Domain: Euclidean Jordan algebra of degree 3 over Rational Field
+ Codomain: Euclidean Jordan algebra of degree 3 over Rational Field
+
"""
return (self + (-other))
+ def matrix(self):
+ """
+ Return the matrix representation of this operator with respect
+ to the default bases of its (co)domain.
+
+ EXAMPLES::
+
+ sage: J = RealSymmetricEJA(2)
+ sage: mat = matrix(J.base_ring(), J.dimension(), range(9))
+ sage: f = FiniteDimensionalEuclideanJordanAlgebraOperator(J,J,mat)
+ sage: f.matrix()
+ [0 1 2]
+ [3 4 5]
+ [6 7 8]
+
+ """
+ return self._matrix
+
+
class FiniteDimensionalEuclideanJordanAlgebra(FiniteDimensionalAlgebra):
@staticmethod
def __classcall_private__(cls,
sage: set_random_seed()
sage: x = random_eja().random_element()
- sage: x.operator_matrix()*x.vector() == (x^2).vector()
+ sage: x.operator()(x) == (x^2)
True
A few examples of power-associativity::
sage: x = random_eja().random_element()
sage: m = ZZ.random_element(0,10)
sage: n = ZZ.random_element(0,10)
- sage: Lxm = (x^m).operator_matrix()
- sage: Lxn = (x^n).operator_matrix()
+ sage: Lxm = (x^m).operator()
+ sage: Lxn = (x^n).operator()
sage: Lxm*Lxn == Lxn*Lxm
True
"""
- A = self.parent()
if n == 0:
- return A.one()
+ return self.parent().one()
elif n == 1:
return self
else:
- return A( (self.operator_matrix()**(n-1))*self.vector() )
+ return (self.operator()**(n-1))(self)
def apply_univariate_polynomial(self, p):
sage: lhs == rhs
True
+ Test the first polarization identity from my notes, Koecher Chapter
+ III, or from Baes (2.3)::
+
+ sage: set_random_seed()
+ sage: J = random_eja()
+ sage: x = J.random_element()
+ sage: y = J.random_element()
+ sage: Lx = x.operator()
+ sage: Ly = y.operator()
+ sage: Lxx = (x*x).operator()
+ sage: Lxy = (x*y).operator()
+ sage: bool(2*Lx*Lxy + Ly*Lxx == 2*Lxy*Lx + Lxx*Ly)
+ True
+
+ Test the second polarization identity from my notes or from
+ Baes (2.4)::
+
+ sage: set_random_seed()
+ sage: J = random_eja()
+ sage: x = J.random_element()
+ sage: y = J.random_element()
+ sage: z = J.random_element()
+ sage: Lx = x.operator()
+ sage: Ly = y.operator()
+ sage: Lz = z.operator()
+ sage: Lzy = (z*y).operator()
+ sage: Lxy = (x*y).operator()
+ sage: Lxz = (x*z).operator()
+ sage: bool(Lx*Lzy + Lz*Lxy + Ly*Lxz == Lzy*Lx + Lxy*Lz + Lxz*Ly)
+ True
+
+ Test the third polarization identity from my notes or from
+ Baes (2.5)::
+
+ sage: set_random_seed()
+ sage: J = random_eja()
+ sage: u = J.random_element()
+ sage: y = J.random_element()
+ sage: z = J.random_element()
+ sage: Lu = u.operator()
+ sage: Ly = y.operator()
+ sage: Lz = z.operator()
+ sage: Lzy = (z*y).operator()
+ sage: Luy = (u*y).operator()
+ sage: Luz = (u*z).operator()
+ sage: Luyz = (u*(y*z)).operator()
+ sage: lhs = Lu*Lzy + Lz*Luy + Ly*Luz
+ sage: rhs = Luyz + Ly*Lu*Lz + Lz*Lu*Ly
+ sage: bool(lhs == rhs)
+ True
+
"""
if not other in self.parent():
raise TypeError("'other' must live in the same algebra")
- A = self.operator_matrix()
- B = other.operator_matrix()
+ A = self.operator()
+ B = other.operator()
return (A*B == B*A)
Our parent class defines ``left_matrix`` and ``matrix``
methods whose names are misleading. We don't want them.
"""
- raise NotImplementedError("use operator_matrix() instead")
+ raise NotImplementedError("use operator().matrix() instead")
matrix = left_matrix
# We get back a symbolic polynomial in 'x' but want a real
# polynomial in 't'.
- p_of_x = elt.operator_matrix().minimal_polynomial()
+ p_of_x = elt.operator().matrix().minimal_polynomial()
return p_of_x.change_variable_name('t')
"""
P = self.parent()
+ fda_elt = FiniteDimensionalAlgebraElement(P, self)
return FiniteDimensionalEuclideanJordanAlgebraOperator(
- P,P,
- self.operator_matrix() )
-
-
-
- def operator_matrix(self):
- """
- Return the matrix that represents left- (or right-)
- multiplication by this element in the parent algebra.
-
- We implement this ourselves to work around the fact that
- our parent class represents everything with row vectors.
-
- EXAMPLES:
-
- Test the first polarization identity from my notes, Koecher Chapter
- III, or from Baes (2.3)::
-
- sage: set_random_seed()
- sage: J = random_eja()
- sage: x = J.random_element()
- sage: y = J.random_element()
- sage: Lx = x.operator_matrix()
- sage: Ly = y.operator_matrix()
- sage: Lxx = (x*x).operator_matrix()
- sage: Lxy = (x*y).operator_matrix()
- sage: bool(2*Lx*Lxy + Ly*Lxx == 2*Lxy*Lx + Lxx*Ly)
- True
-
- Test the second polarization identity from my notes or from
- Baes (2.4)::
-
- sage: set_random_seed()
- sage: J = random_eja()
- sage: x = J.random_element()
- sage: y = J.random_element()
- sage: z = J.random_element()
- sage: Lx = x.operator_matrix()
- sage: Ly = y.operator_matrix()
- sage: Lz = z.operator_matrix()
- sage: Lzy = (z*y).operator_matrix()
- sage: Lxy = (x*y).operator_matrix()
- sage: Lxz = (x*z).operator_matrix()
- sage: bool(Lx*Lzy + Lz*Lxy + Ly*Lxz == Lzy*Lx + Lxy*Lz + Lxz*Ly)
- True
-
- Test the third polarization identity from my notes or from
- Baes (2.5)::
-
- sage: set_random_seed()
- sage: J = random_eja()
- sage: u = J.random_element()
- sage: y = J.random_element()
- sage: z = J.random_element()
- sage: Lu = u.operator_matrix()
- sage: Ly = y.operator_matrix()
- sage: Lz = z.operator_matrix()
- sage: Lzy = (z*y).operator_matrix()
- sage: Luy = (u*y).operator_matrix()
- sage: Luz = (u*z).operator_matrix()
- sage: Luyz = (u*(y*z)).operator_matrix()
- sage: lhs = Lu*Lzy + Lz*Luy + Ly*Luz
- sage: rhs = Luyz + Ly*Lu*Lz + Lz*Lu*Ly
- sage: bool(lhs == rhs)
- True
-
- Ensure that our operator's ``matrix`` method agrees with
- this implementation::
-
- sage: set_random_seed()
- sage: J = random_eja()
- sage: x = J.random_element()
- sage: x.operator().matrix() == x.operator_matrix()
- True
-
- """
- fda_elt = FiniteDimensionalAlgebraElement(self.parent(), self)
- return fda_elt.matrix().transpose()
+ P,
+ P,
+ fda_elt.matrix().transpose() )
def quadratic_representation(self, other=None):
sage: x.subalgebra_generated_by().is_associative()
True
- Squaring in the subalgebra should be the same thing as
- squaring in the superalgebra::
+ Squaring in the subalgebra should work the same as in
+ the superalgebra::
sage: set_random_seed()
sage: x = random_eja().random_element()
sage: u = x.subalgebra_generated_by().random_element()
- sage: u.operator_matrix()*u.vector() == (u**2).vector()
+ sage: u.operator()(u) == u^2
True
"""
s = 0
minimal_dim = V.dimension()
for i in xrange(1, V.dimension()):
- this_dim = (u**i).operator_matrix().image().dimension()
+ this_dim = (u**i).operator().matrix().image().dimension()
if this_dim < minimal_dim:
minimal_dim = this_dim
s = i
# Beware, solve_right() means that we're using COLUMN vectors.
# Our FiniteDimensionalAlgebraElement superclass uses rows.
u_next = u**(s+1)
- A = u_next.operator_matrix()
+ A = u_next.operator().matrix()
c_coordinates = A.solve_right(u_next.vector())
# Now c_coordinates is the idempotent we want, but it's in