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