]> gitweb.michael.orlitzky.com - sage.d.git/blob - mjo/hurwitz.py
eja: rename operator_inner_product -> operator_trace inner_product.
[sage.d.git] / mjo / hurwitz.py
1 from sage.misc.cachefunc import cached_method
2 from sage.combinat.free_module import CombinatorialFreeModule
3 from sage.modules.with_basis.indexed_element import IndexedFreeModuleElement
4 from sage.rings.all import AA
5
6 from mjo.matrix_algebra import MatrixAlgebra, MatrixAlgebraElement
7
8 class Octonion(IndexedFreeModuleElement):
9 def conjugate(self):
10 r"""
11 SETUP::
12
13 sage: from mjo.hurwitz import Octonions
14
15 EXAMPLES::
16
17 sage: O = Octonions()
18 sage: x = sum(O.gens())
19 sage: x.conjugate()
20 e0 - e1 - e2 - e3 - e4 - e5 - e6 - e7
21
22 TESTS::
23
24 Conjugating twice gets you the original element::
25
26 sage: O = Octonions()
27 sage: x = O.random_element()
28 sage: x.conjugate().conjugate() == x
29 True
30
31 """
32 from sage.rings.all import ZZ
33 from sage.matrix.matrix_space import MatrixSpace
34 C = MatrixSpace(ZZ,8).diagonal_matrix((1,-1,-1,-1,-1,-1,-1,-1))
35 return self.parent().from_vector(C*self.to_vector())
36
37 def real(self):
38 r"""
39 Return the real part of this octonion.
40
41 The real part of an octonion is its projection onto the span
42 of the first generator. In other words, the "first dimension"
43 is real and the others are imaginary.
44
45 SETUP::
46
47 sage: from mjo.hurwitz import Octonions
48
49 EXAMPLES::
50
51 sage: O = Octonions()
52 sage: x = sum(O.gens())
53 sage: x.real()
54 e0
55
56 TESTS:
57
58 This method is idempotent::
59
60 sage: O = Octonions()
61 sage: x = O.random_element()
62 sage: x.real().real() == x.real()
63 True
64
65 """
66 return (self + self.conjugate())/2
67
68 def imag(self):
69 r"""
70 Return the imaginary part of this octonion.
71
72 The imaginary part of an octonion is its projection onto the
73 orthogonal complement of the span of the first generator. In
74 other words, the "first dimension" is real and the others are
75 imaginary.
76
77 SETUP::
78
79 sage: from mjo.hurwitz import Octonions
80
81 EXAMPLES::
82
83 sage: O = Octonions()
84 sage: x = sum(O.gens())
85 sage: x.imag()
86 e1 + e2 + e3 + e4 + e5 + e6 + e7
87
88 TESTS:
89
90 This method is idempotent::
91
92 sage: O = Octonions()
93 sage: x = O.random_element()
94 sage: x.imag().imag() == x.imag()
95 True
96
97 """
98 return (self - self.conjugate())/2
99
100 def _norm_squared(self):
101 return (self*self.conjugate()).coefficient(0)
102
103 def norm(self):
104 r"""
105 Return the norm of this octonion.
106
107 SETUP::
108
109 sage: from mjo.hurwitz import Octonions
110
111 EXAMPLES::
112
113 sage: O = Octonions()
114 sage: O.one().norm()
115 1
116
117 TESTS:
118
119 The norm is nonnegative and belongs to the base field::
120
121 sage: O = Octonions()
122 sage: n = O.random_element().norm()
123 sage: n >= 0 and n in O.base_ring()
124 True
125
126 The norm is homogeneous::
127
128 sage: O = Octonions()
129 sage: x = O.random_element()
130 sage: alpha = O.base_ring().random_element()
131 sage: (alpha*x).norm() == alpha.abs()*x.norm()
132 True
133
134 """
135 return self._norm_squared().sqrt()
136
137 # The absolute value notation is typically used for complex numbers...
138 # and norm() isn't supported in AA, so this lets us use abs() in all
139 # of the division algebras we need.
140 abs = norm
141
142 def inverse(self):
143 r"""
144 Return the inverse of this element if it exists.
145
146 SETUP::
147
148 sage: from mjo.hurwitz import Octonions
149
150 EXAMPLES::
151
152 sage: O = Octonions()
153 sage: x = sum(O.gens())
154 sage: x*x.inverse() == O.one()
155 True
156
157 ::
158
159 sage: O = Octonions()
160 sage: O.one().inverse() == O.one()
161 True
162
163 TESTS::
164
165 sage: O = Octonions()
166 sage: x = O.random_element()
167 sage: x.is_zero() or ( x*x.inverse() == O.one() )
168 True
169
170 """
171 if self.is_zero():
172 raise ValueError("zero is not invertible")
173 return self.conjugate()/self._norm_squared()
174
175
176
177 class Octonions(CombinatorialFreeModule):
178 r"""
179 SETUP::
180
181 sage: from mjo.hurwitz import Octonions
182
183 EXAMPLES::
184
185 sage: Octonions()
186 Octonion algebra with base ring Algebraic Real Field
187 sage: Octonions(field=QQ)
188 Octonion algebra with base ring Rational Field
189
190 """
191 def __init__(self,
192 field=AA,
193 prefix="e"):
194
195 # Not associative, not commutative
196 from sage.categories.magmatic_algebras import MagmaticAlgebras
197 category = MagmaticAlgebras(field).FiniteDimensional()
198 category = category.WithBasis().Unital()
199
200 super().__init__(field,
201 range(8),
202 element_class=Octonion,
203 category=category,
204 prefix=prefix,
205 bracket=False)
206
207 # The product of each basis element is plus/minus another
208 # basis element that can simply be looked up on
209 # https://en.wikipedia.org/wiki/Octonion
210 e0, e1, e2, e3, e4, e5, e6, e7 = self.gens()
211 self._multiplication_table = (
212 (e0, e1, e2, e3, e4, e5, e6, e7),
213 (e1,-e0, e3,-e2, e5,-e4,-e7, e6),
214 (e2,-e3,-e0, e1, e6, e7,-e4,-e5),
215 (e3, e2,-e1,-e0, e7,-e6, e5,-e4),
216 (e4,-e5,-e6,-e7,-e0, e1, e2, e3),
217 (e5, e4,-e7, e6,-e1,-e0,-e3, e2),
218 (e6, e7, e4,-e5,-e2, e3,-e0,-e1),
219 (e7,-e6, e5, e4,-e3,-e2, e1,-e0),
220 )
221
222 def product_on_basis(self, i, j):
223 return self._multiplication_table[i][j]
224
225 def one_basis(self):
226 r"""
227 Return the monomial index (basis element) corresponding to the
228 octonion unit element.
229
230 SETUP::
231
232 sage: from mjo.hurwitz import Octonions
233
234 TESTS:
235
236 This gives the correct unit element::
237
238 sage: O = Octonions()
239 sage: x = O.random_element()
240 sage: x*O.one() == x and O.one()*x == x
241 True
242
243 """
244 return 0
245
246 def _repr_(self):
247 return ("Octonion algebra with base ring %s" % self.base_ring())
248
249 def multiplication_table(self):
250 """
251 Return a visual representation of this algebra's multiplication
252 table (on basis elements).
253
254 SETUP::
255
256 sage: from mjo.hurwitz import Octonions
257
258 EXAMPLES:
259
260 The multiplication table is what Wikipedia says it is::
261
262 sage: Octonions().multiplication_table()
263 +----++----+-----+-----+-----+-----+-----+-----+-----+
264 | * || e0 | e1 | e2 | e3 | e4 | e5 | e6 | e7 |
265 +====++====+=====+=====+=====+=====+=====+=====+=====+
266 | e0 || e0 | e1 | e2 | e3 | e4 | e5 | e6 | e7 |
267 +----++----+-----+-----+-----+-----+-----+-----+-----+
268 | e1 || e1 | -e0 | e3 | -e2 | e5 | -e4 | -e7 | e6 |
269 +----++----+-----+-----+-----+-----+-----+-----+-----+
270 | e2 || e2 | -e3 | -e0 | e1 | e6 | e7 | -e4 | -e5 |
271 +----++----+-----+-----+-----+-----+-----+-----+-----+
272 | e3 || e3 | e2 | -e1 | -e0 | e7 | -e6 | e5 | -e4 |
273 +----++----+-----+-----+-----+-----+-----+-----+-----+
274 | e4 || e4 | -e5 | -e6 | -e7 | -e0 | e1 | e2 | e3 |
275 +----++----+-----+-----+-----+-----+-----+-----+-----+
276 | e5 || e5 | e4 | -e7 | e6 | -e1 | -e0 | -e3 | e2 |
277 +----++----+-----+-----+-----+-----+-----+-----+-----+
278 | e6 || e6 | e7 | e4 | -e5 | -e2 | e3 | -e0 | -e1 |
279 +----++----+-----+-----+-----+-----+-----+-----+-----+
280 | e7 || e7 | -e6 | e5 | e4 | -e3 | -e2 | e1 | -e0 |
281 +----++----+-----+-----+-----+-----+-----+-----+-----+
282
283 """
284 n = self.dimension()
285 # Prepend the header row.
286 M = [["*"] + list(self.gens())]
287
288 # And to each subsequent row, prepend an entry that belongs to
289 # the left-side "header column."
290 M += [ [self.monomial(i)] + [ self.monomial(i)*self.monomial(j)
291 for j in range(n) ]
292 for i in range(n) ]
293
294 from sage.misc.table import table
295 return table(M, header_row=True, header_column=True, frame=True)
296
297
298
299
300
301 class HurwitzMatrixAlgebraElement(MatrixAlgebraElement):
302 def conjugate(self):
303 r"""
304 Return the entrywise conjugate of this matrix.
305
306 SETUP::
307
308 sage: from mjo.hurwitz import ComplexMatrixAlgebra
309
310 EXAMPLES::
311
312 sage: A = ComplexMatrixAlgebra(2, QQbar, ZZ)
313 sage: M = A([ [ I, 1 + 2*I],
314 ....: [ 3*I, 4*I] ])
315 sage: M.conjugate()
316 +------+----------+
317 | -I | -2*I + 1 |
318 +------+----------+
319 | -3*I | -4*I |
320 +------+----------+
321
322 ::
323
324 sage: A = ComplexMatrixAlgebra(2, QQbar, QQ)
325 sage: M = A([ [ 1, 2],
326 ....: [ 3, 4] ])
327 sage: M.conjugate() == M
328 True
329 sage: M.to_vector()
330 (1, 0, 2, 0, 3, 0, 4, 0)
331
332 """
333 d = self.monomial_coefficients()
334 A = self.parent()
335 new_terms = ( A._conjugate_term((k,v)) for (k,v) in d.items() )
336 return self.parent().sum_of_terms(new_terms)
337
338 def conjugate_transpose(self):
339 r"""
340 Return the conjugate-transpose of this matrix.
341
342 SETUP::
343
344 sage: from mjo.hurwitz import ComplexMatrixAlgebra
345
346 EXAMPLES::
347
348 sage: A = ComplexMatrixAlgebra(2, QQbar, ZZ)
349 sage: M = A([ [ I, 2*I],
350 ....: [ 3*I, 4*I] ])
351 sage: M.conjugate_transpose()
352 +------+------+
353 | -I | -3*I |
354 +------+------+
355 | -2*I | -4*I |
356 +------+------+
357 sage: M.conjugate_transpose().to_vector()
358 (0, -1, 0, -3, 0, -2, 0, -4)
359
360 """
361 d = self.monomial_coefficients()
362 A = self.parent()
363 new_terms = ( A._conjugate_term( ((k[1],k[0],k[2]), v) )
364 for (k,v) in d.items() )
365 return self.parent().sum_of_terms(new_terms)
366
367 def is_hermitian(self):
368 r"""
369
370 SETUP::
371
372 sage: from mjo.hurwitz import (ComplexMatrixAlgebra,
373 ....: HurwitzMatrixAlgebra)
374
375 EXAMPLES::
376
377 sage: A = ComplexMatrixAlgebra(2, QQbar, ZZ)
378 sage: M = A([ [ 0,I],
379 ....: [-I,0] ])
380 sage: M.is_hermitian()
381 True
382
383 ::
384
385 sage: A = ComplexMatrixAlgebra(2, QQbar, ZZ)
386 sage: M = A([ [ 0,0],
387 ....: [-I,0] ])
388 sage: M.is_hermitian()
389 False
390
391 ::
392
393 sage: A = HurwitzMatrixAlgebra(2, AA, QQ)
394 sage: M = A([ [1, 1],
395 ....: [1, 1] ])
396 sage: M.is_hermitian()
397 True
398
399 """
400 # A tiny bit faster than checking equality with the conjugate
401 # transpose.
402 return all( self[i,j] == self[j,i].conjugate()
403 for i in range(self.nrows())
404 for j in range(i+1) )
405
406
407 def is_skew_symmetric(self):
408 r"""
409 Return whether or not this matrix is skew-symmetric.
410
411 SETUP::
412
413 sage: from mjo.hurwitz import (ComplexMatrixAlgebra,
414 ....: HurwitzMatrixAlgebra)
415
416 EXAMPLES::
417
418 sage: A = ComplexMatrixAlgebra(2, QQbar, ZZ)
419 sage: M = A([ [ 0,I],
420 ....: [-I,1] ])
421 sage: M.is_skew_symmetric()
422 False
423
424 ::
425
426 sage: A = ComplexMatrixAlgebra(2, QQbar, ZZ)
427 sage: M = A([ [ 0, 1+I],
428 ....: [-1-I, 0] ])
429 sage: M.is_skew_symmetric()
430 True
431
432 ::
433
434 sage: A = HurwitzMatrixAlgebra(2, AA, QQ)
435 sage: M = A([ [1, 1],
436 ....: [1, 1] ])
437 sage: M.is_skew_symmetric()
438 False
439
440 ::
441
442 sage: A = ComplexMatrixAlgebra(2, QQbar, ZZ)
443 sage: M = A([ [2*I , 1 + I],
444 ....: [-1 + I, -2*I] ])
445 sage: M.is_skew_symmetric()
446 False
447
448 """
449 # A tiny bit faster than checking equality with the negation
450 # of the transpose.
451 return all( self[i,j] == -self[j,i]
452 for i in range(self.nrows())
453 for j in range(i+1) )
454
455
456 class HurwitzMatrixAlgebra(MatrixAlgebra):
457 r"""
458 A class of matrix algebras whose entries come from a Hurwitz
459 algebra.
460
461 For our purposes, we consider "a Hurwitz" algebra to be the real
462 or complex numbers, the quaternions, or the octonions. These are
463 typically also referred to as the Euclidean Hurwitz algebras, or
464 the normed division algebras.
465
466 By the Cayley-Dickson construction, each Hurwitz algebra is an
467 algebra over the real numbers, so we restrict the scalar field in
468 this case to be real. This also allows us to more accurately
469 produce the generators of the matrix algebra.
470 """
471 Element = HurwitzMatrixAlgebraElement
472
473 def __init__(self, n, entry_algebra, scalars, **kwargs):
474 from sage.rings.all import RR
475 if not scalars.is_subring(RR):
476 # Not perfect, but it's what we're using.
477 raise ValueError("scalar field is not real")
478
479 super().__init__(n, entry_algebra, scalars, **kwargs)
480
481
482 @staticmethod
483 def _conjugate_term(t):
484 r"""
485 Conjugate the given ``(index, coefficient)`` term, returning
486 another such term.
487
488 Given a term ``((i,j,e), c)``, it's straightforward to
489 conjugate the entry ``e``, but if ``e``-conjugate is ``-e``,
490 then the resulting ``((i,j,-e), c)`` is not a term, since
491 ``(i,j,-e)`` is not a monomial index! So when we build a sum
492 of these conjugates we can wind up with a nonsense object.
493
494 This function handles the case where ``e``-conjugate is
495 ``-e``, but nothing more complicated. Thus it makes sense in
496 Hurwitz matrix algebras, but not more generally.
497
498 SETUP::
499
500 sage: from mjo.hurwitz import ComplexMatrixAlgebra
501
502 EXAMPLES::
503
504 sage: A = ComplexMatrixAlgebra(2, QQbar, ZZ)
505 sage: M = A([ [ I, 1 + 2*I],
506 ....: [ 3*I, 4*I] ])
507 sage: t = list(M.monomial_coefficients().items())[1]
508 sage: t
509 ((1, 0, I), 3)
510 sage: A._conjugate_term(t)
511 ((1, 0, I), -3)
512
513 """
514 if t[0][2].conjugate() == t[0][2]:
515 return t
516 else:
517 return (t[0], -t[1])
518
519
520 def entry_algebra_gens(self):
521 r"""
522 Return a tuple of the generators of (that is, a basis for) the
523 entries of this matrix algebra.
524
525 This works around the inconsistency in the ``gens()`` methods
526 of the real/complex numbers, quaternions, and octonions.
527
528 SETUP::
529
530 sage: from mjo.hurwitz import Octonions, HurwitzMatrixAlgebra
531
532 EXAMPLES:
533
534 The inclusion of the unit element is inconsistent across
535 (subalgebras of) Hurwitz algebras::
536
537 sage: AA.gens()
538 (1,)
539 sage: QQbar.gens()
540 (I,)
541 sage: QuaternionAlgebra(AA,1,-1).gens()
542 [i, j, k]
543 sage: Octonions().gens()
544 (e0, e1, e2, e3, e4, e5, e6, e7)
545
546 The unit element is always returned by this method, so the
547 sets of generators have cartinality 1,2,4, and 8 as you'd
548 expect::
549
550 sage: HurwitzMatrixAlgebra(2, AA, AA).entry_algebra_gens()
551 (1,)
552 sage: HurwitzMatrixAlgebra(2, QQbar, AA).entry_algebra_gens()
553 (1, I)
554 sage: Q = QuaternionAlgebra(AA,-1,-1)
555 sage: HurwitzMatrixAlgebra(2, Q, AA).entry_algebra_gens()
556 (1, i, j, k)
557 sage: O = Octonions()
558 sage: HurwitzMatrixAlgebra(2, O, AA).entry_algebra_gens()
559 (e0, e1, e2, e3, e4, e5, e6, e7)
560
561 """
562 gs = self.entry_algebra().gens()
563 one = self.entry_algebra().one()
564 if one in gs:
565 return gs
566 else:
567 return (one,) + tuple(gs)
568
569
570
571 class OctonionMatrixAlgebra(HurwitzMatrixAlgebra):
572 r"""
573 The algebra of ``n``-by-``n`` matrices with octonion entries over
574 (a subfield of) the real numbers.
575
576 The usual matrix spaces in SageMath don't support octonion entries
577 because they assume that the entries of the matrix come from a
578 commutative and associative ring, and the octonions are neither.
579
580 SETUP::
581
582 sage: from mjo.hurwitz import Octonions, OctonionMatrixAlgebra
583
584 EXAMPLES::
585
586 sage: OctonionMatrixAlgebra(3)
587 Module of 3 by 3 matrices with entries in Octonion algebra with base
588 ring Algebraic Real Field over the scalar ring Algebraic Real Field
589
590 ::
591
592 sage: OctonionMatrixAlgebra(3,scalars=QQ)
593 Module of 3 by 3 matrices with entries in Octonion algebra with
594 base ring Rational Field over the scalar ring Rational Field
595
596 ::
597
598 sage: O = Octonions(RR)
599 sage: A = OctonionMatrixAlgebra(1,O)
600 sage: A
601 Module of 1 by 1 matrices with entries in Octonion algebra with
602 base ring Real Field with 53 bits of precision over the scalar
603 ring Algebraic Real Field
604 sage: A.one()
605 +---------------------+
606 | 1.00000000000000*e0 |
607 +---------------------+
608 sage: A.gens()
609 (+---------------------+
610 | 1.00000000000000*e0 |
611 +---------------------+,
612 +---------------------+
613 | 1.00000000000000*e1 |
614 +---------------------+,
615 +---------------------+
616 | 1.00000000000000*e2 |
617 +---------------------+,
618 +---------------------+
619 | 1.00000000000000*e3 |
620 +---------------------+,
621 +---------------------+
622 | 1.00000000000000*e4 |
623 +---------------------+,
624 +---------------------+
625 | 1.00000000000000*e5 |
626 +---------------------+,
627 +---------------------+
628 | 1.00000000000000*e6 |
629 +---------------------+,
630 +---------------------+
631 | 1.00000000000000*e7 |
632 +---------------------+)
633
634 ::
635
636 sage: A = OctonionMatrixAlgebra(2)
637 sage: e0,e1,e2,e3,e4,e5,e6,e7 = A.entry_algebra().gens()
638 sage: A([ [e0+e4, e1+e5],
639 ....: [e2-e6, e3-e7] ])
640 +---------+---------+
641 | e0 + e4 | e1 + e5 |
642 +---------+---------+
643 | e2 - e6 | e3 - e7 |
644 +---------+---------+
645
646 ::
647
648 sage: A1 = OctonionMatrixAlgebra(1,scalars=QQ)
649 sage: A2 = OctonionMatrixAlgebra(1,scalars=QQ)
650 sage: cartesian_product([A1,A2])
651 Module of 1 by 1 matrices with entries in Octonion algebra with
652 base ring Rational Field over the scalar ring Rational Field (+)
653 Module of 1 by 1 matrices with entries in Octonion algebra with
654 base ring Rational Field over the scalar ring Rational Field
655
656 TESTS::
657
658 sage: A = OctonionMatrixAlgebra(ZZ.random_element(10))
659 sage: x = A.random_element()
660 sage: x*A.one() == x and A.one()*x == x
661 True
662
663 """
664 def __init__(self, n, entry_algebra=None, scalars=AA, **kwargs):
665 if entry_algebra is None:
666 entry_algebra = Octonions(field=scalars)
667 super().__init__(n,
668 entry_algebra,
669 scalars,
670 **kwargs)
671
672 class QuaternionMatrixAlgebra(HurwitzMatrixAlgebra):
673 r"""
674 The algebra of ``n``-by-``n`` matrices with quaternion entries over
675 (a subfield of) the real numbers.
676
677 The usual matrix spaces in SageMath don't support quaternion entries
678 because they assume that the entries of the matrix come from a
679 commutative ring, and the quaternions are not commutative.
680
681 SETUP::
682
683 sage: from mjo.hurwitz import QuaternionMatrixAlgebra
684
685 EXAMPLES::
686
687 sage: QuaternionMatrixAlgebra(3)
688 Module of 3 by 3 matrices with entries in Quaternion
689 Algebra (-1, -1) with base ring Algebraic Real Field
690 over the scalar ring Algebraic Real Field
691
692 ::
693
694 sage: QuaternionMatrixAlgebra(3,scalars=QQ)
695 Module of 3 by 3 matrices with entries in Quaternion
696 Algebra (-1, -1) with base ring Rational Field over
697 the scalar ring Rational Field
698
699 ::
700
701 sage: Q = QuaternionAlgebra(RDF, -1, -1)
702 sage: A = QuaternionMatrixAlgebra(1,Q)
703 sage: A
704 Module of 1 by 1 matrices with entries in Quaternion Algebra
705 (-1.0, -1.0) with base ring Real Double Field over the scalar
706 ring Algebraic Real Field
707 sage: A.one()
708 +-----+
709 | 1.0 |
710 +-----+
711 sage: A.gens()
712 (+-----+
713 | 1.0 |
714 +-----+,
715 +---+
716 | i |
717 +---+,
718 +---+
719 | j |
720 +---+,
721 +---+
722 | k |
723 +---+)
724
725 ::
726
727 sage: A = QuaternionMatrixAlgebra(2)
728 sage: i,j,k = A.entry_algebra().gens()
729 sage: A([ [1+i, j-2],
730 ....: [k, k+j] ])
731 +-------+--------+
732 | 1 + i | -2 + j |
733 +-------+--------+
734 | k | j + k |
735 +-------+--------+
736
737 ::
738
739 sage: A1 = QuaternionMatrixAlgebra(1,scalars=QQ)
740 sage: A2 = QuaternionMatrixAlgebra(2,scalars=QQ)
741 sage: cartesian_product([A1,A2])
742 Module of 1 by 1 matrices with entries in Quaternion Algebra
743 (-1, -1) with base ring Rational Field over the scalar ring
744 Rational Field (+) Module of 2 by 2 matrices with entries in
745 Quaternion Algebra (-1, -1) with base ring Rational Field over
746 the scalar ring Rational Field
747
748 TESTS::
749
750 sage: A = QuaternionMatrixAlgebra(ZZ.random_element(10))
751 sage: x = A.random_element()
752 sage: x*A.one() == x and A.one()*x == x
753 True
754
755 """
756 def __init__(self, n, entry_algebra=None, scalars=AA, **kwargs):
757 if entry_algebra is None:
758 # The -1,-1 gives us the "usual" definition of quaternion
759 from sage.algebras.quatalg.quaternion_algebra import (
760 QuaternionAlgebra
761 )
762 entry_algebra = QuaternionAlgebra(scalars,-1,-1)
763 super().__init__(n, entry_algebra, scalars, **kwargs)
764
765 def _entry_algebra_element_to_vector(self, entry):
766 r"""
767
768 SETUP::
769
770 sage: from mjo.hurwitz import QuaternionMatrixAlgebra
771
772 EXAMPLES::
773
774 sage: A = QuaternionMatrixAlgebra(2)
775 sage: u = A.entry_algebra().one()
776 sage: A._entry_algebra_element_to_vector(u)
777 (1, 0, 0, 0)
778 sage: i,j,k = A.entry_algebra().gens()
779 sage: A._entry_algebra_element_to_vector(i)
780 (0, 1, 0, 0)
781 sage: A._entry_algebra_element_to_vector(j)
782 (0, 0, 1, 0)
783 sage: A._entry_algebra_element_to_vector(k)
784 (0, 0, 0, 1)
785
786 """
787 from sage.modules.free_module import FreeModule
788 d = len(self.entry_algebra_gens())
789 V = FreeModule(self.entry_algebra().base_ring(), d)
790 return V(entry.coefficient_tuple())
791
792 class ComplexMatrixAlgebra(HurwitzMatrixAlgebra):
793 r"""
794 The algebra of ``n``-by-``n`` matrices with complex entries over
795 (a subfield of) the real numbers.
796
797 These differ from the usual complex matrix spaces in SageMath
798 because the scalar field is real (and not assumed to be the same
799 as the space from which the entries are drawn). The space of
800 `1`-by-`1` complex matrices will have dimension two, for example.
801
802 SETUP::
803
804 sage: from mjo.hurwitz import ComplexMatrixAlgebra
805
806 EXAMPLES::
807
808 sage: ComplexMatrixAlgebra(3)
809 Module of 3 by 3 matrices with entries in Algebraic Field
810 over the scalar ring Algebraic Real Field
811
812 ::
813
814 sage: ComplexMatrixAlgebra(3,scalars=QQ)
815 Module of 3 by 3 matrices with entries in Algebraic Field
816 over the scalar ring Rational Field
817
818 ::
819
820 sage: A = ComplexMatrixAlgebra(1,CC)
821 sage: A
822 Module of 1 by 1 matrices with entries in Complex Field with
823 53 bits of precision over the scalar ring Algebraic Real Field
824 sage: A.one()
825 +------------------+
826 | 1.00000000000000 |
827 +------------------+
828 sage: A.gens()
829 (+------------------+
830 | 1.00000000000000 |
831 +------------------+,
832 +--------------------+
833 | 1.00000000000000*I |
834 +--------------------+)
835
836 ::
837
838 sage: A = ComplexMatrixAlgebra(2)
839 sage: (I,) = A.entry_algebra().gens()
840 sage: A([ [1+I, 1],
841 ....: [-1, -I] ])
842 +---------+------+
843 | 1 + 1*I | 1 |
844 +---------+------+
845 | -1 | -1*I |
846 +---------+------+
847
848 ::
849
850 sage: A1 = ComplexMatrixAlgebra(1,scalars=QQ)
851 sage: A2 = ComplexMatrixAlgebra(2,scalars=QQ)
852 sage: cartesian_product([A1,A2])
853 Module of 1 by 1 matrices with entries in Algebraic Field over
854 the scalar ring Rational Field (+) Module of 2 by 2 matrices with
855 entries in Algebraic Field over the scalar ring Rational Field
856
857 TESTS::
858
859 sage: A = ComplexMatrixAlgebra(ZZ.random_element(10))
860 sage: x = A.random_element()
861 sage: x*A.one() == x and A.one()*x == x
862 True
863
864 """
865 def __init__(self, n, entry_algebra=None, scalars=AA, **kwargs):
866 if entry_algebra is None:
867 from sage.rings.all import QQbar
868 entry_algebra = QQbar
869 super().__init__(n, entry_algebra, scalars, **kwargs)
870
871 def _entry_algebra_element_to_vector(self, entry):
872 r"""
873
874 SETUP::
875
876 sage: from mjo.hurwitz import ComplexMatrixAlgebra
877
878 EXAMPLES::
879
880 sage: A = ComplexMatrixAlgebra(2, QQbar, QQ)
881 sage: A._entry_algebra_element_to_vector(QQbar(1))
882 (1, 0)
883 sage: A._entry_algebra_element_to_vector(QQbar(I))
884 (0, 1)
885
886 """
887 from sage.modules.free_module import FreeModule
888 d = len(self.entry_algebra_gens())
889 V = FreeModule(self.entry_algebra().base_ring(), d)
890 return V((entry.real(), entry.imag()))