1 from sage
.matrix
.constructor
import matrix
2 from sage
.categories
.all
import FreeModules
3 from sage
.categories
.map import Map
5 class FiniteDimensionalEuclideanJordanAlgebraOperator(Map
):
6 def __init__(self
, domain_eja
, codomain_eja
, mat
):
8 # isinstance(domain_eja, FiniteDimensionalEuclideanJordanAlgebra) and
9 # isinstance(codomain_eja, FiniteDimensionalEuclideanJordanAlgebra) ):
10 # raise ValueError('(co)domains must be finite-dimensional Euclidean '
13 F
= domain_eja
.base_ring()
14 if not (F
== codomain_eja
.base_ring()):
15 raise ValueError("domain and codomain must have the same base ring")
16 if not (F
== mat
.base_ring()):
17 raise ValueError("domain and matrix must have the same base ring")
19 # We need to supply something here to avoid getting the
20 # default Homset of the parent FiniteDimensionalAlgebra class,
21 # which messes up e.g. equality testing. We use FreeModules(F)
22 # instead of VectorSpaces(F) because our characteristic polynomial
23 # algorithm will need to F to be a polynomial ring at some point.
24 # When F is a field, FreeModules(F) returns VectorSpaces(F) anyway.
25 parent
= domain_eja
.Hom(codomain_eja
, FreeModules(F
))
27 # The Map initializer will set our parent to a homset, which
28 # is explicitly NOT what we want, because these ain't algebra
30 super(FiniteDimensionalEuclideanJordanAlgebraOperator
,self
).__init
__(parent
)
32 # Keep a matrix around to do all of the real work. It would
33 # be nice if we could use a VectorSpaceMorphism instead, but
34 # those use row vectors that we don't want to accidentally
35 # expose to our users.
41 Allow this operator to be called only on elements of an EJA.
45 sage: from mjo.eja.eja_operator import FiniteDimensionalEuclideanJordanAlgebraOperator
46 sage: from mjo.eja.eja_algebra import JordanSpinEJA
50 sage: J = JordanSpinEJA(3)
51 sage: x = J.linear_combination(zip(J.gens(),range(len(J.gens()))))
52 sage: id = identity_matrix(J.base_ring(), J.dimension())
53 sage: f = FiniteDimensionalEuclideanJordanAlgebraOperator(J,J,id)
58 return self
.codomain().from_vector(self
.matrix()*x
.to_vector())
61 def _add_(self
, other
):
63 Add the ``other`` EJA operator to this one.
67 sage: from mjo.eja.eja_operator import FiniteDimensionalEuclideanJordanAlgebraOperator
68 sage: from mjo.eja.eja_algebra import (
70 ....: RealSymmetricEJA )
74 When we add two EJA operators, we get another one back::
76 sage: J = RealSymmetricEJA(2)
77 sage: id = identity_matrix(J.base_ring(), J.dimension())
78 sage: f = FiniteDimensionalEuclideanJordanAlgebraOperator(J,J,id)
79 sage: g = FiniteDimensionalEuclideanJordanAlgebraOperator(J,J,id)
81 Linear operator between finite-dimensional Euclidean Jordan
82 algebras represented by the matrix:
86 Domain: Euclidean Jordan algebra of dimension 3 over...
87 Codomain: Euclidean Jordan algebra of dimension 3 over...
89 If you try to add two identical vector space operators but on
90 different EJAs, that should blow up::
92 sage: J1 = RealSymmetricEJA(2)
93 sage: id1 = identity_matrix(J1.base_ring(), 3)
94 sage: J2 = JordanSpinEJA(3)
95 sage: id2 = identity_matrix(J2.base_ring(), 3)
96 sage: f = FiniteDimensionalEuclideanJordanAlgebraOperator(J1,J1,id1)
97 sage: g = FiniteDimensionalEuclideanJordanAlgebraOperator(J2,J2,id2)
99 Traceback (most recent call last):
101 TypeError: unsupported operand parent(s) for +: ...
104 return FiniteDimensionalEuclideanJordanAlgebraOperator(
107 self
.matrix() + other
.matrix())
110 def _composition_(self
, other
, homset
):
112 Compose two EJA operators to get another one (and NOT a formal
113 composite object) back.
117 sage: from mjo.eja.eja_operator import FiniteDimensionalEuclideanJordanAlgebraOperator
118 sage: from mjo.eja.eja_algebra import (
120 ....: RealCartesianProductEJA,
121 ....: RealSymmetricEJA)
125 sage: J1 = JordanSpinEJA(3)
126 sage: J2 = RealCartesianProductEJA(2)
127 sage: J3 = RealSymmetricEJA(1)
128 sage: mat1 = matrix(QQ, [[1,2,3],
130 sage: mat2 = matrix(QQ, [[7,8]])
131 sage: g = FiniteDimensionalEuclideanJordanAlgebraOperator(J1,
134 sage: f = FiniteDimensionalEuclideanJordanAlgebraOperator(J2,
138 Linear operator between finite-dimensional Euclidean Jordan
139 algebras represented by the matrix:
141 Domain: Euclidean Jordan algebra of dimension 3 over
143 Codomain: Euclidean Jordan algebra of dimension 1 over
147 return FiniteDimensionalEuclideanJordanAlgebraOperator(
150 self
.matrix()*other
.matrix())
153 def __eq__(self
, other
):
154 if self
.domain() != other
.domain():
156 if self
.codomain() != other
.codomain():
158 if self
.matrix() != other
.matrix():
163 def __invert__(self
):
165 Invert this EJA operator.
169 sage: from mjo.eja.eja_operator import FiniteDimensionalEuclideanJordanAlgebraOperator
170 sage: from mjo.eja.eja_algebra import RealSymmetricEJA
174 sage: J = RealSymmetricEJA(2)
175 sage: id = identity_matrix(J.base_ring(), J.dimension())
176 sage: f = FiniteDimensionalEuclideanJordanAlgebraOperator(J,J,id)
178 Linear operator between finite-dimensional Euclidean Jordan
179 algebras represented by the matrix:
183 Domain: Euclidean Jordan algebra of dimension 3 over...
184 Codomain: Euclidean Jordan algebra of dimension 3 over...
187 return FiniteDimensionalEuclideanJordanAlgebraOperator(
193 def __mul__(self
, other
):
195 Compose two EJA operators, or scale myself by an element of the
196 ambient vector space.
198 We need to override the real ``__mul__`` function to prevent the
199 coercion framework from throwing an error when it fails to convert
200 a base ring element into a morphism.
204 sage: from mjo.eja.eja_operator import FiniteDimensionalEuclideanJordanAlgebraOperator
205 sage: from mjo.eja.eja_algebra import RealSymmetricEJA
209 We can scale an operator on a rational algebra by a rational number::
211 sage: J = RealSymmetricEJA(2)
212 sage: e0,e1,e2 = J.gens()
213 sage: x = 2*e0 + 4*e1 + 16*e2
215 Linear operator between finite-dimensional Euclidean Jordan algebras
216 represented by the matrix:
220 Domain: Euclidean Jordan algebra of dimension 3 over...
221 Codomain: Euclidean Jordan algebra of dimension 3 over...
222 sage: x.operator()*(1/2)
223 Linear operator between finite-dimensional Euclidean Jordan algebras
224 represented by the matrix:
228 Domain: Euclidean Jordan algebra of dimension 3 over...
229 Codomain: Euclidean Jordan algebra of dimension 3 over...
233 if other
in self
.codomain().base_ring():
234 return FiniteDimensionalEuclideanJordanAlgebraOperator(
238 except NotImplementedError:
239 # This can happen with certain arguments if the base_ring()
240 # is weird and doesn't know how to test membership.
243 # This should eventually delegate to _composition_ after performing
244 # some sanity checks for us.
245 mor
= super(FiniteDimensionalEuclideanJordanAlgebraOperator
,self
)
246 return mor
.__mul
__(other
)
251 Negate this EJA operator.
255 sage: from mjo.eja.eja_operator import FiniteDimensionalEuclideanJordanAlgebraOperator
256 sage: from mjo.eja.eja_algebra import RealSymmetricEJA
260 sage: J = RealSymmetricEJA(2)
261 sage: id = identity_matrix(J.base_ring(), J.dimension())
262 sage: f = FiniteDimensionalEuclideanJordanAlgebraOperator(J,J,id)
264 Linear operator between finite-dimensional Euclidean Jordan
265 algebras represented by the matrix:
269 Domain: Euclidean Jordan algebra of dimension 3 over...
270 Codomain: Euclidean Jordan algebra of dimension 3 over...
273 return FiniteDimensionalEuclideanJordanAlgebraOperator(
279 def __pow__(self
, n
):
281 Raise this EJA operator to the power ``n``.
285 sage: from mjo.eja.eja_operator import FiniteDimensionalEuclideanJordanAlgebraOperator
286 sage: from mjo.eja.eja_algebra import RealSymmetricEJA
290 Ensure that we get back another EJA operator that can be added,
291 subtracted, et cetera::
293 sage: J = RealSymmetricEJA(2)
294 sage: id = identity_matrix(J.base_ring(), J.dimension())
295 sage: f = FiniteDimensionalEuclideanJordanAlgebraOperator(J,J,id)
296 sage: f^0 + f^1 + f^2
297 Linear operator between finite-dimensional Euclidean Jordan
298 algebras represented by the matrix:
302 Domain: Euclidean Jordan algebra of dimension 3 over...
303 Codomain: Euclidean Jordan algebra of dimension 3 over...
309 # Raising a vector space morphism to the zero power gives
310 # you back a special IdentityMorphism that is useless to us.
311 rows
= self
.codomain().dimension()
312 cols
= self
.domain().dimension()
313 mat
= matrix
.identity(self
.base_ring(), rows
, cols
)
315 mat
= self
.matrix()**n
317 return FiniteDimensionalEuclideanJordanAlgebraOperator(
326 A text representation of this linear operator on a Euclidean
331 sage: from mjo.eja.eja_operator import FiniteDimensionalEuclideanJordanAlgebraOperator
332 sage: from mjo.eja.eja_algebra import JordanSpinEJA
336 sage: J = JordanSpinEJA(2)
337 sage: id = identity_matrix(J.base_ring(), J.dimension())
338 sage: FiniteDimensionalEuclideanJordanAlgebraOperator(J,J,id)
339 Linear operator between finite-dimensional Euclidean Jordan
340 algebras represented by the matrix:
343 Domain: Euclidean Jordan algebra of dimension 2 over
345 Codomain: Euclidean Jordan algebra of dimension 2 over
349 msg
= ("Linear operator between finite-dimensional Euclidean Jordan "
350 "algebras represented by the matrix:\n",
354 return ''.join(msg
).format(self
.matrix(),
359 def _sub_(self
, other
):
361 Subtract ``other`` from this EJA operator.
365 sage: from mjo.eja.eja_operator import FiniteDimensionalEuclideanJordanAlgebraOperator
366 sage: from mjo.eja.eja_algebra import RealSymmetricEJA
370 sage: J = RealSymmetricEJA(2)
371 sage: id = identity_matrix(J.base_ring(),J.dimension())
372 sage: f = FiniteDimensionalEuclideanJordanAlgebraOperator(J,J,id)
374 Linear operator between finite-dimensional Euclidean Jordan
375 algebras represented by the matrix:
379 Domain: Euclidean Jordan algebra of dimension 3 over...
380 Codomain: Euclidean Jordan algebra of dimension 3 over...
383 return (self
+ (-other
))
388 Return the matrix representation of this operator with respect
389 to the default bases of its (co)domain.
393 sage: from mjo.eja.eja_operator import FiniteDimensionalEuclideanJordanAlgebraOperator
394 sage: from mjo.eja.eja_algebra import RealSymmetricEJA
398 sage: J = RealSymmetricEJA(2)
399 sage: mat = matrix(J.base_ring(), J.dimension(), range(9))
400 sage: f = FiniteDimensionalEuclideanJordanAlgebraOperator(J,J,mat)
410 def minimal_polynomial(self
):
412 Return the minimal polynomial of this linear operator,
413 in the variable ``t``.
417 sage: from mjo.eja.eja_operator import FiniteDimensionalEuclideanJordanAlgebraOperator
418 sage: from mjo.eja.eja_algebra import RealSymmetricEJA
422 sage: J = RealSymmetricEJA(3)
423 sage: J.one().operator().minimal_polynomial()
427 # The matrix method returns a polynomial in 'x' but want one in 't'.
428 return self
.matrix().minimal_polynomial().change_variable_name('t')
431 def spectral_decomposition(self
):
433 Return the spectral decomposition of this operator as a list of
434 (eigenvalue, orthogonal projector) pairs.
438 sage: from mjo.eja.eja_algebra import RealSymmetricEJA
442 sage: J = RealSymmetricEJA(4,AA)
443 sage: x = sum(J.gens())
444 sage: A = x.subalgebra_generated_by(orthonormalize_basis=True)
445 sage: L0x = A(x).operator()
446 sage: Ps = [ P*l for (l,P) in L0x.spectral_decomposition() ]
447 sage: Ps[0] + Ps[1] == L0x
451 if not self
.matrix().is_symmetric():
452 raise ValueError('algebra basis is not orthonormal')
454 D
,P
= self
.matrix().jordan_form(subdivide
=False,transformation
=True)
455 eigenvalues
= D
.diagonal()
458 for i
in range(len(us
)):
459 # they won't be normalized, but they have to be
460 # for the spectral theorem to work.
461 us
[i
] = us
[i
]/us
[i
].norm()
462 mat
= us
[i
].column()*us
[i
].row()
463 Pi
= FiniteDimensionalEuclideanJordanAlgebraOperator(
467 projectors
.append(Pi
)
468 return zip(eigenvalues
, projectors
)