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