]> gitweb.michael.orlitzky.com - sage.d.git/blob - mjo/hurwitz.py
c213283099c2464316994a9d0fec7fef1f31c284
[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 entries = [ [ self[i,j].conjugate()
324 for j in range(self.ncols())]
325 for i in range(self.nrows()) ]
326 return self.parent()._element_constructor_(entries)
327
328 def conjugate_transpose(self):
329 r"""
330 Return the conjugate-transpose of this matrix.
331
332 SETUP::
333
334 sage: from mjo.hurwitz import ComplexMatrixAlgebra
335
336 EXAMPLES::
337
338 sage: A = ComplexMatrixAlgebra(2, QQbar, ZZ)
339 sage: M = A([ [ I, 2*I],
340 ....: [ 3*I, 4*I] ])
341 sage: M.conjugate_transpose()
342 +------+------+
343 | -I | -3*I |
344 +------+------+
345 | -2*I | -4*I |
346 +------+------+
347 sage: M.conjugate_transpose().to_vector()
348 (0, -1, 0, -3, 0, -2, 0, -4)
349
350 """
351 entries = [ [ self[j,i].conjugate()
352 for j in range(self.ncols())]
353 for i in range(self.nrows()) ]
354 return self.parent()._element_constructor_(entries)
355
356 def is_hermitian(self):
357 r"""
358
359 SETUP::
360
361 sage: from mjo.hurwitz import (ComplexMatrixAlgebra,
362 ....: HurwitzMatrixAlgebra)
363
364 EXAMPLES::
365
366 sage: A = ComplexMatrixAlgebra(2, QQbar, ZZ)
367 sage: M = A([ [ 0,I],
368 ....: [-I,0] ])
369 sage: M.is_hermitian()
370 True
371
372 ::
373
374 sage: A = HurwitzMatrixAlgebra(2, AA, QQ)
375 sage: M = A([ [1, 1],
376 ....: [1, 1] ])
377 sage: M.is_hermitian()
378 True
379
380 """
381 # A tiny bit faster than checking equality with the conjugate
382 # transpose.
383 return all( self[i,j] == self[j,i].conjugate()
384 for i in range(self.nrows())
385 for j in range(self.ncols()) )
386
387
388 def is_skew_hermitian(self):
389 r"""
390
391 SETUP::
392
393 sage: from mjo.hurwitz import (ComplexMatrixAlgebra,
394 ....: HurwitzMatrixAlgebra)
395
396 EXAMPLES::
397
398 sage: A = ComplexMatrixAlgebra(2, QQbar, ZZ)
399 sage: M = A([ [ 0,I],
400 ....: [-I,1] ])
401 sage: M.is_skew_hermitian()
402 False
403
404 ::
405
406 sage: A = HurwitzMatrixAlgebra(2, AA, QQ)
407 sage: M = A([ [1, 1],
408 ....: [1, 1] ])
409 sage: M.is_skew_hermitian()
410 False
411
412 ::
413
414 sage: A = ComplexMatrixAlgebra(2, QQbar, ZZ)
415 sage: M = A([ [2*I , 1 + I],
416 ....: [-1 + I, -2*I] ])
417 sage: M.is_skew_hermitian()
418 True
419
420 """
421 # A tiny bit faster than checking equality with the conjugate
422 # transpose.
423 return all( self[i,j] == -self[j,i].conjugate()
424 for i in range(self.nrows())
425 for j in range(self.ncols()) )
426
427
428 class HurwitzMatrixAlgebra(MatrixAlgebra):
429 r"""
430 A class of matrix algebras whose entries come from a Hurwitz
431 algebra.
432
433 For our purposes, we consider "a Hurwitz" algebra to be the real
434 or complex numbers, the quaternions, or the octonions. These are
435 typically also referred to as the Euclidean Hurwitz algebras, or
436 the normed division algebras.
437
438 By the Cayley-Dickson construction, each Hurwitz algebra is an
439 algebra over the real numbers, so we restrict the scalar field in
440 this case to be real. This also allows us to more accurately
441 produce the generators of the matrix algebra.
442 """
443 Element = HurwitzMatrixAlgebraElement
444
445 def __init__(self, n, entry_algebra, scalars, **kwargs):
446 from sage.rings.all import RR
447 if not scalars.is_subring(RR):
448 # Not perfect, but it's what we're using.
449 raise ValueError("scalar field is not real")
450
451 super().__init__(n, entry_algebra, scalars, **kwargs)
452
453 def entry_algebra_gens(self):
454 r"""
455 Return a tuple of the generators of (that is, a basis for) the
456 entries of this matrix algebra.
457
458 This works around the inconsistency in the ``gens()`` methods
459 of the real/complex numbers, quaternions, and octonions.
460
461 SETUP::
462
463 sage: from mjo.hurwitz import Octonions, HurwitzMatrixAlgebra
464
465 EXAMPLES:
466
467 The inclusion of the unit element is inconsistent across
468 (subalgebras of) Hurwitz algebras::
469
470 sage: AA.gens()
471 (1,)
472 sage: QQbar.gens()
473 (I,)
474 sage: QuaternionAlgebra(AA,1,-1).gens()
475 [i, j, k]
476 sage: Octonions().gens()
477 (e0, e1, e2, e3, e4, e5, e6, e7)
478
479 The unit element is always returned by this method, so the
480 sets of generators have cartinality 1,2,4, and 8 as you'd
481 expect::
482
483 sage: HurwitzMatrixAlgebra(2, AA, AA).entry_algebra_gens()
484 (1,)
485 sage: HurwitzMatrixAlgebra(2, QQbar, AA).entry_algebra_gens()
486 (1, I)
487 sage: Q = QuaternionAlgebra(AA,-1,-1)
488 sage: HurwitzMatrixAlgebra(2, Q, AA).entry_algebra_gens()
489 (1, i, j, k)
490 sage: O = Octonions()
491 sage: HurwitzMatrixAlgebra(2, O, AA).entry_algebra_gens()
492 (e0, e1, e2, e3, e4, e5, e6, e7)
493
494 """
495 gs = self.entry_algebra().gens()
496 one = self.entry_algebra().one()
497 if one in gs:
498 return gs
499 else:
500 return (one,) + tuple(gs)
501
502
503
504 class OctonionMatrixAlgebra(HurwitzMatrixAlgebra):
505 r"""
506 The algebra of ``n``-by-``n`` matrices with octonion entries over
507 (a subfield of) the real numbers.
508
509 The usual matrix spaces in SageMath don't support octonion entries
510 because they assume that the entries of the matrix come from a
511 commutative and associative ring, and the octonions are neither.
512
513 SETUP::
514
515 sage: from mjo.hurwitz import Octonions, OctonionMatrixAlgebra
516
517 EXAMPLES::
518
519 sage: OctonionMatrixAlgebra(3)
520 Module of 3 by 3 matrices with entries in Octonion algebra with base
521 ring Algebraic Real Field over the scalar ring Algebraic Real Field
522
523 ::
524
525 sage: OctonionMatrixAlgebra(3,scalars=QQ)
526 Module of 3 by 3 matrices with entries in Octonion algebra with
527 base ring Rational Field over the scalar ring Rational Field
528
529 ::
530
531 sage: O = Octonions(RR)
532 sage: A = OctonionMatrixAlgebra(1,O)
533 sage: A
534 Module of 1 by 1 matrices with entries in Octonion algebra with
535 base ring Real Field with 53 bits of precision over the scalar
536 ring Algebraic Real Field
537 sage: A.one()
538 +---------------------+
539 | 1.00000000000000*e0 |
540 +---------------------+
541 sage: A.gens()
542 (+---------------------+
543 | 1.00000000000000*e0 |
544 +---------------------+,
545 +---------------------+
546 | 1.00000000000000*e1 |
547 +---------------------+,
548 +---------------------+
549 | 1.00000000000000*e2 |
550 +---------------------+,
551 +---------------------+
552 | 1.00000000000000*e3 |
553 +---------------------+,
554 +---------------------+
555 | 1.00000000000000*e4 |
556 +---------------------+,
557 +---------------------+
558 | 1.00000000000000*e5 |
559 +---------------------+,
560 +---------------------+
561 | 1.00000000000000*e6 |
562 +---------------------+,
563 +---------------------+
564 | 1.00000000000000*e7 |
565 +---------------------+)
566
567 ::
568
569 sage: A = OctonionMatrixAlgebra(2)
570 sage: e0,e1,e2,e3,e4,e5,e6,e7 = A.entry_algebra().gens()
571 sage: A([ [e0+e4, e1+e5],
572 ....: [e2-e6, e3-e7] ])
573 +---------+---------+
574 | e0 + e4 | e1 + e5 |
575 +---------+---------+
576 | e2 - e6 | e3 - e7 |
577 +---------+---------+
578
579 ::
580
581 sage: A1 = OctonionMatrixAlgebra(1,scalars=QQ)
582 sage: A2 = OctonionMatrixAlgebra(1,scalars=QQ)
583 sage: cartesian_product([A1,A2])
584 Module of 1 by 1 matrices with entries in Octonion algebra with
585 base ring Rational Field over the scalar ring Rational Field (+)
586 Module of 1 by 1 matrices with entries in Octonion algebra with
587 base ring Rational Field over the scalar ring Rational Field
588
589 TESTS::
590
591 sage: A = OctonionMatrixAlgebra(ZZ.random_element(10))
592 sage: x = A.random_element()
593 sage: x*A.one() == x and A.one()*x == x
594 True
595
596 """
597 def __init__(self, n, entry_algebra=None, scalars=AA, **kwargs):
598 if entry_algebra is None:
599 entry_algebra = Octonions(field=scalars)
600 super().__init__(n,
601 entry_algebra,
602 scalars,
603 **kwargs)
604
605 class QuaternionMatrixAlgebra(HurwitzMatrixAlgebra):
606 r"""
607 The algebra of ``n``-by-``n`` matrices with quaternion entries over
608 (a subfield of) the real numbers.
609
610 The usual matrix spaces in SageMath don't support quaternion entries
611 because they assume that the entries of the matrix come from a
612 commutative ring, and the quaternions are not commutative.
613
614 SETUP::
615
616 sage: from mjo.hurwitz import QuaternionMatrixAlgebra
617
618 EXAMPLES::
619
620 sage: QuaternionMatrixAlgebra(3)
621 Module of 3 by 3 matrices with entries in Quaternion
622 Algebra (-1, -1) with base ring Algebraic Real Field
623 over the scalar ring Algebraic Real Field
624
625 ::
626
627 sage: QuaternionMatrixAlgebra(3,scalars=QQ)
628 Module of 3 by 3 matrices with entries in Quaternion
629 Algebra (-1, -1) with base ring Rational Field over
630 the scalar ring Rational Field
631
632 ::
633
634 sage: Q = QuaternionAlgebra(RDF, -1, -1)
635 sage: A = QuaternionMatrixAlgebra(1,Q)
636 sage: A
637 Module of 1 by 1 matrices with entries in Quaternion Algebra
638 (-1.0, -1.0) with base ring Real Double Field over the scalar
639 ring Algebraic Real Field
640 sage: A.one()
641 +-----+
642 | 1.0 |
643 +-----+
644 sage: A.gens()
645 (+-----+
646 | 1.0 |
647 +-----+,
648 +---+
649 | i |
650 +---+,
651 +---+
652 | j |
653 +---+,
654 +---+
655 | k |
656 +---+)
657
658 ::
659
660 sage: A = QuaternionMatrixAlgebra(2)
661 sage: i,j,k = A.entry_algebra().gens()
662 sage: A([ [1+i, j-2],
663 ....: [k, k+j] ])
664 +-------+--------+
665 | 1 + i | -2 + j |
666 +-------+--------+
667 | k | j + k |
668 +-------+--------+
669
670 ::
671
672 sage: A1 = QuaternionMatrixAlgebra(1,scalars=QQ)
673 sage: A2 = QuaternionMatrixAlgebra(2,scalars=QQ)
674 sage: cartesian_product([A1,A2])
675 Module of 1 by 1 matrices with entries in Quaternion Algebra
676 (-1, -1) with base ring Rational Field over the scalar ring
677 Rational Field (+) Module of 2 by 2 matrices with entries in
678 Quaternion Algebra (-1, -1) with base ring Rational Field over
679 the scalar ring Rational Field
680
681 TESTS::
682
683 sage: A = QuaternionMatrixAlgebra(ZZ.random_element(10))
684 sage: x = A.random_element()
685 sage: x*A.one() == x and A.one()*x == x
686 True
687
688 """
689 def __init__(self, n, entry_algebra=None, scalars=AA, **kwargs):
690 if entry_algebra is None:
691 # The -1,-1 gives us the "usual" definition of quaternion
692 from sage.algebras.quatalg.quaternion_algebra import (
693 QuaternionAlgebra
694 )
695 entry_algebra = QuaternionAlgebra(scalars,-1,-1)
696 super().__init__(n, entry_algebra, scalars, **kwargs)
697
698 def _entry_algebra_element_to_vector(self, entry):
699 r"""
700
701 SETUP::
702
703 sage: from mjo.hurwitz import QuaternionMatrixAlgebra
704
705 EXAMPLES::
706
707 sage: A = QuaternionMatrixAlgebra(2)
708 sage: u = A.entry_algebra().one()
709 sage: A._entry_algebra_element_to_vector(u)
710 (1, 0, 0, 0)
711 sage: i,j,k = A.entry_algebra().gens()
712 sage: A._entry_algebra_element_to_vector(i)
713 (0, 1, 0, 0)
714 sage: A._entry_algebra_element_to_vector(j)
715 (0, 0, 1, 0)
716 sage: A._entry_algebra_element_to_vector(k)
717 (0, 0, 0, 1)
718
719 """
720 from sage.modules.free_module import FreeModule
721 d = len(self.entry_algebra_gens())
722 V = FreeModule(self.entry_algebra().base_ring(), d)
723 return V(entry.coefficient_tuple())
724
725 class ComplexMatrixAlgebra(HurwitzMatrixAlgebra):
726 r"""
727 The algebra of ``n``-by-``n`` matrices with complex entries over
728 (a subfield of) the real numbers.
729
730 These differ from the usual complex matrix spaces in SageMath
731 because the scalar field is real (and not assumed to be the same
732 as the space from which the entries are drawn). The space of
733 `1`-by-`1` complex matrices will have dimension two, for example.
734
735 SETUP::
736
737 sage: from mjo.hurwitz import ComplexMatrixAlgebra
738
739 EXAMPLES::
740
741 sage: ComplexMatrixAlgebra(3)
742 Module of 3 by 3 matrices with entries in Algebraic Field
743 over the scalar ring Algebraic Real Field
744
745 ::
746
747 sage: ComplexMatrixAlgebra(3,scalars=QQ)
748 Module of 3 by 3 matrices with entries in Algebraic Field
749 over the scalar ring Rational Field
750
751 ::
752
753 sage: A = ComplexMatrixAlgebra(1,CC)
754 sage: A
755 Module of 1 by 1 matrices with entries in Complex Field with
756 53 bits of precision over the scalar ring Algebraic Real Field
757 sage: A.one()
758 +------------------+
759 | 1.00000000000000 |
760 +------------------+
761 sage: A.gens()
762 (+------------------+
763 | 1.00000000000000 |
764 +------------------+,
765 +--------------------+
766 | 1.00000000000000*I |
767 +--------------------+)
768
769 ::
770
771 sage: A = ComplexMatrixAlgebra(2)
772 sage: (I,) = A.entry_algebra().gens()
773 sage: A([ [1+I, 1],
774 ....: [-1, -I] ])
775 +---------+------+
776 | 1 + 1*I | 1 |
777 +---------+------+
778 | -1 | -1*I |
779 +---------+------+
780
781 ::
782
783 sage: A1 = ComplexMatrixAlgebra(1,scalars=QQ)
784 sage: A2 = ComplexMatrixAlgebra(2,scalars=QQ)
785 sage: cartesian_product([A1,A2])
786 Module of 1 by 1 matrices with entries in Algebraic Field over
787 the scalar ring Rational Field (+) Module of 2 by 2 matrices with
788 entries in Algebraic Field over the scalar ring Rational Field
789
790 TESTS::
791
792 sage: A = ComplexMatrixAlgebra(ZZ.random_element(10))
793 sage: x = A.random_element()
794 sage: x*A.one() == x and A.one()*x == x
795 True
796
797 """
798 def __init__(self, n, entry_algebra=None, scalars=AA, **kwargs):
799 if entry_algebra is None:
800 from sage.rings.all import QQbar
801 entry_algebra = QQbar
802 super().__init__(n, entry_algebra, scalars, **kwargs)
803
804 def _entry_algebra_element_to_vector(self, entry):
805 r"""
806
807 SETUP::
808
809 sage: from mjo.hurwitz import ComplexMatrixAlgebra
810
811 EXAMPLES::
812
813 sage: A = ComplexMatrixAlgebra(2, QQbar, QQ)
814 sage: A._entry_algebra_element_to_vector(QQbar(1))
815 (1, 0)
816 sage: A._entry_algebra_element_to_vector(QQbar(I))
817 (0, 1)
818
819 """
820 from sage.modules.free_module import FreeModule
821 d = len(self.entry_algebra_gens())
822 V = FreeModule(self.entry_algebra().base_ring(), d)
823 return V((entry.real(), entry.imag()))