]> gitweb.michael.orlitzky.com - sage.d.git/blob - mjo/matrix_algebra.py
matrix_algebra: factor out Hurwitz subclass.
[sage.d.git] / mjo / matrix_algebra.py
1 from sage.misc.table import table
2 from sage.categories.magmatic_algebras import MagmaticAlgebras
3 from sage.misc.cachefunc import cached_method
4 from sage.combinat.free_module import CombinatorialFreeModule
5 from sage.modules.with_basis.indexed_element import IndexedFreeModuleElement
6
7 class MatrixAlgebraElement(IndexedFreeModuleElement):
8 def nrows(self):
9 return self.parent().nrows()
10 ncols = nrows
11
12 @cached_method
13 def rows(self):
14 r"""
15 SETUP::
16
17 sage: from mjo.matrix_algebra import MatrixAlgebra
18
19 EXAMPLES::
20
21 sage: M = MatrixAlgebra(QQbar,RDF,2)
22 sage: A = M.monomial((0,0,1)) + 4*M.monomial((0,1,1))
23 sage: A
24 +-----+-----+
25 | 1.0 | 4.0 |
26 +-----+-----+
27 | 0 | 0 |
28 +-----+-----+
29 sage: A.rows()
30 [[1.0, 4.0], [0, 0]]
31
32 """
33 zero = self.parent().entry_algebra().zero()
34 l = [[zero for j in range(self.ncols())] for i in range(self.nrows())]
35 for (k,v) in self.monomial_coefficients().items():
36 (i,j,e) = k
37 l[i][j] += v*e
38 return l
39
40 def __repr__(self):
41 r"""
42 Display this matrix as a table.
43
44 The SageMath Matrix class representation is not easily reusable,
45 but using a table fakes it.
46
47 SETUP::
48
49 sage: from mjo.matrix_algebra import MatrixAlgebra
50
51 EXAMPLES::
52
53 sage: MatrixAlgebra(ZZ,ZZ,2).zero()
54 +---+---+
55 | 0 | 0 |
56 +---+---+
57 | 0 | 0 |
58 +---+---+
59
60 """
61 return table(self.rows(), frame=True)._repr_()
62
63
64 def list(self):
65 r"""
66 Return one long list of this matrix's entries.
67
68 SETUP::
69
70 sage: from mjo.matrix_algebra import MatrixAlgebra
71
72 EXAMPLES::
73
74 sage: A = MatrixAlgebra(ZZ,ZZ,2)
75 sage: A([[1,2],[3,4]]).list()
76 [1, 2, 3, 4]
77
78 """
79 return sum( self.rows(), [] )
80
81
82 def __getitem__(self, indices):
83 r"""
84
85 SETUP::
86
87 sage: from mjo.matrix_algebra import MatrixAlgebra
88
89 EXAMPLES::
90
91 sage: M = MatrixAlgebra(ZZ,ZZ,2)([[1,2],[3,4]])
92 sage: M[0,0]
93 1
94 sage: M[0,1]
95 2
96 sage: M[1,0]
97 3
98 sage: M[1,1]
99 4
100
101 """
102 i,j = indices
103 return self.rows()[i][j]
104
105 def trace(self):
106 r"""
107 Return the sum of this matrix's diagonal entries.
108
109 SETUP::
110
111 sage: from mjo.matrix_algebra import MatrixAlgebra
112
113 EXAMPLES:
114
115 The trace (being a sum of entries) belongs to the same algebra
116 as those entries, and NOT the scalar ring::
117
118 sage: entries = MatrixSpace(ZZ,2)
119 sage: scalars = ZZ
120 sage: M = MatrixAlgebra(entries, scalars, 2)
121 sage: I = entries.one()
122 sage: Z = entries.zero()
123 sage: M([[I,Z],[Z,I]]).trace()
124 [2 0]
125 [0 2]
126
127 """
128 zero = self.parent().entry_algebra().zero()
129 return sum( (self[i,i] for i in range(self.nrows())), zero )
130
131 def matrix_space(self):
132 r"""
133
134 SETUP::
135
136 sage: from mjo.matrix_algebra import MatrixAlgebra
137
138 TESTS::
139
140 sage: set_random_seed()
141 sage: entries = QuaternionAlgebra(QQ,-1,-1)
142 sage: M = MatrixAlgebra(entries, QQ, 3)
143 sage: M.random_element().matrix_space() == M
144 True
145
146 """
147 return self.parent()
148
149
150 class MatrixAlgebra(CombinatorialFreeModule):
151 r"""
152 An algebra of ``n``-by-``n`` matrices over an arbitrary scalar
153 ring whose entries come from a magmatic algebra that need not
154 be the same as the scalars.
155
156 The usual matrix spaces in SageMath don't support separate spaces
157 for the entries and the scalars; in particular they assume that
158 the entries come from a commutative and associative ring. This
159 is problematic in several interesting matrix algebras, like those
160 where the entries are quaternions or octonions.
161 """
162 Element = MatrixAlgebraElement
163
164 def __init__(self, entry_algebra, scalars, n, prefix="A", **kwargs):
165
166 category = MagmaticAlgebras(scalars).FiniteDimensional()
167 category = category.WithBasis()
168
169 if "Unital" in entry_algebra.category().axioms():
170 category = category.Unital()
171 if "Associative" in entry_algebra.category().axioms():
172 category = category.Associative()
173
174 self._nrows = n
175
176 # Since the scalar ring is real but the entries are not,
177 # sticking a "1" in each position doesn't give us a basis for
178 # the space. We actually need to stick each of e0, e1, ... (a
179 # basis for the entry algebra itself) into each position.
180 I = range(n)
181 J = range(n)
182 self._entry_algebra = entry_algebra
183 entry_basis = entry_algebra.gens()
184
185 basis_indices = [(i,j,e) for i in range(n)
186 for j in range(n)
187 for e in entry_algebra.gens()]
188
189 super().__init__(scalars,
190 basis_indices,
191 category=category,
192 prefix=prefix,
193 bracket='(')
194
195 def _repr_(self):
196 return ("Module of %d by %d matrices with entries in %s"
197 " over the scalar ring %s" %
198 (self.nrows(),
199 self.ncols(),
200 self.entry_algebra(),
201 self.base_ring()) )
202
203 def entry_algebra(self):
204 r"""
205 Return the algebra that our elements' entries come from.
206 """
207 return self._entry_algebra
208
209 def nrows(self):
210 return self._nrows
211 ncols = nrows
212
213 def product_on_basis(self, mon1, mon2):
214 (i,j,e1) = mon1
215 (k,l,e2) = mon2
216 if j == k:
217 return self.monomial((i,l,e1*e2))
218 else:
219 return self.zero()
220
221 def from_list(self, entries):
222 r"""
223 Construct an element of this algebra from a list of lists of
224 entries.
225
226 SETUP::
227
228 sage: from mjo.matrix_algebra import MatrixAlgebra
229
230 EXAMPLES::
231
232 sage: A = MatrixAlgebra(QQbar, ZZ, 2)
233 sage: A.from_list([[0,I],[-I,0]])
234 +----+---+
235 | 0 | I |
236 +----+---+
237 | -I | 0 |
238 +----+---+
239
240 """
241 nrows = len(entries)
242 ncols = 0
243 if nrows > 0:
244 ncols = len(entries[0])
245
246 if (not all( len(r) == ncols for r in entries )) or (ncols != nrows):
247 raise ValueError("list must be square")
248
249 def convert(e_ij):
250 if e_ij in self.entry_algebra():
251 # Don't re-create an element if it already lives where
252 # it should!
253 return e_ij
254
255 try:
256 # This branch works with e.g. QQbar, where no
257 # to/from_vector() methods are available.
258 return self.entry_algebra()(e_ij)
259 except TypeError:
260 # We have to pass through vectors to convert from the
261 # given entry algebra to ours. Otherwise we can fail to
262 # convert an element of (for example) Octonions(QQ) to
263 # Octonions(AA).
264 return self.entry_algebra().from_vector(e_ij.to_vector())
265
266 return sum( (self.monomial( (i,j, convert(entries[i][j])) )
267 for i in range(nrows)
268 for j in range(ncols) ),
269 self.zero() )
270
271 def _element_constructor_(self, elt):
272 if elt in self:
273 return self
274 else:
275 return self.from_list(elt)
276
277
278 class HurwitzMatrixAlgebraElement(MatrixAlgebraElement):
279 def is_hermitian(self):
280 r"""
281
282 SETUP::
283
284 sage: from mjo.matrix_algebra import HurwitzMatrixAlgebra
285
286 EXAMPLES::
287
288 sage: A = HurwitzMatrixAlgebra(QQbar, ZZ, 2)
289 sage: M = A([ [ 0,I],
290 ....: [-I,0] ])
291 sage: M.is_hermitian()
292 True
293
294 """
295 return all( self[i,j] == self[j,i].conjugate()
296 for i in range(self.nrows())
297 for j in range(self.ncols()) )
298
299
300 class HurwitzMatrixAlgebra(MatrixAlgebra):
301 Element = HurwitzMatrixAlgebraElement
302
303 def one(self):
304 r"""
305 SETUP::
306
307 sage: from mjo.matrix_algebra import HurwitzMatrixAlgebra
308
309 """
310 return sum( (self.monomial((i,i,self.entry_algebra().one()))
311 for i in range(self.nrows()) ),
312 self.zero() )
313
314