]> gitweb.michael.orlitzky.com - sage.d.git/blob - mjo/matrix_algebra.py
a67a9b4a1c0692d31aceffa3097343aa0df99dae
[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 SETUP::
163
164 sage: from mjo.matrix_algebra import MatrixAlgebra
165
166 EXAMPLES::
167
168 The existence of a unit element is determined dynamically::
169
170 sage: MatrixAlgebra(ZZ,ZZ,2).one()
171 +---+---+
172 | 1 | 0 |
173 +---+---+
174 | 0 | 1 |
175 +---+---+
176
177 """
178 Element = MatrixAlgebraElement
179
180 def __init__(self, entry_algebra, scalars, n, prefix="A", **kwargs):
181
182 category = MagmaticAlgebras(scalars).FiniteDimensional()
183 category = category.WithBasis()
184
185 if "Unital" in entry_algebra.category().axioms():
186 category = category.Unital()
187 entry_one = entry_algebra.one()
188 self.one = lambda: sum( (self.monomial((i,i,entry_one))
189 for i in range(self.nrows()) ),
190 self.zero() )
191
192 if "Associative" in entry_algebra.category().axioms():
193 category = category.Associative()
194
195 self._nrows = n
196
197 # Since the scalar ring is real but the entries are not,
198 # sticking a "1" in each position doesn't give us a basis for
199 # the space. We actually need to stick each of e0, e1, ... (a
200 # basis for the entry algebra itself) into each position.
201 I = range(n)
202 J = range(n)
203 self._entry_algebra = entry_algebra
204 entry_basis = entry_algebra.gens()
205
206 basis_indices = [(i,j,e) for i in range(n)
207 for j in range(n)
208 for e in entry_algebra.gens()]
209
210 super().__init__(scalars,
211 basis_indices,
212 category=category,
213 prefix=prefix,
214 bracket='(')
215
216 def _repr_(self):
217 return ("Module of %d by %d matrices with entries in %s"
218 " over the scalar ring %s" %
219 (self.nrows(),
220 self.ncols(),
221 self.entry_algebra(),
222 self.base_ring()) )
223
224 def entry_algebra(self):
225 r"""
226 Return the algebra that our elements' entries come from.
227 """
228 return self._entry_algebra
229
230 def nrows(self):
231 return self._nrows
232 ncols = nrows
233
234 def product_on_basis(self, mon1, mon2):
235 r"""
236
237 SETUP::
238
239 sage: from mjo.octonions import Octonions
240 sage: from mjo.matrix_algebra import MatrixAlgebra
241
242 TESTS::
243
244 sage: O = Octonions(QQ)
245 sage: e = O.gens()
246 sage: e[2]*e[1]
247 -e3
248 sage: A = MatrixAlgebra(O,QQ,2)
249 sage: A.product_on_basis( (0,0,e[2]), (0,0,e[1]) )
250 +-----+---+
251 | -e3 | 0 |
252 +-----+---+
253 | 0 | 0 |
254 +-----+---+
255
256 """
257 (i,j,e1) = mon1
258 (k,l,e2) = mon2
259 if j == k:
260 # If e1*e2 has a negative sign in front of it,
261 # then (i,l,e1*e2) won't be a monomial!
262 p = e1*e2
263 if (i,l,p) in self.indices():
264 return self.monomial((i,l,p))
265 else:
266 return -self.monomial((i,l,-p))
267 else:
268 return self.zero()
269
270 def from_list(self, entries):
271 r"""
272 Construct an element of this algebra from a list of lists of
273 entries.
274
275 SETUP::
276
277 sage: from mjo.matrix_algebra import MatrixAlgebra
278
279 EXAMPLES::
280
281 sage: A = MatrixAlgebra(QQbar, ZZ, 2)
282 sage: A.from_list([[0,I],[-I,0]])
283 +----+---+
284 | 0 | I |
285 +----+---+
286 | -I | 0 |
287 +----+---+
288
289 """
290 nrows = len(entries)
291 ncols = 0
292 if nrows > 0:
293 ncols = len(entries[0])
294
295 if (not all( len(r) == ncols for r in entries )) or (ncols != nrows):
296 raise ValueError("list must be square")
297
298 def convert(e_ij):
299 if e_ij in self.entry_algebra():
300 # Don't re-create an element if it already lives where
301 # it should!
302 return e_ij
303
304 try:
305 # This branch works with e.g. QQbar, where no
306 # to/from_vector() methods are available.
307 return self.entry_algebra()(e_ij)
308 except TypeError:
309 # We have to pass through vectors to convert from the
310 # given entry algebra to ours. Otherwise we can fail to
311 # convert an element of (for example) Octonions(QQ) to
312 # Octonions(AA).
313 return self.entry_algebra().from_vector(e_ij.to_vector())
314
315 return sum( (self.monomial( (i,j, convert(entries[i][j])) )
316 for i in range(nrows)
317 for j in range(ncols) ),
318 self.zero() )
319
320 def _element_constructor_(self, elt):
321 if elt in self:
322 return self
323 else:
324 return self.from_list(elt)
325
326
327 class HurwitzMatrixAlgebraElement(MatrixAlgebraElement):
328 def is_hermitian(self):
329 r"""
330
331 SETUP::
332
333 sage: from mjo.matrix_algebra import HurwitzMatrixAlgebra
334
335 EXAMPLES::
336
337 sage: A = HurwitzMatrixAlgebra(QQbar, ZZ, 2)
338 sage: M = A([ [ 0,I],
339 ....: [-I,0] ])
340 sage: M.is_hermitian()
341 True
342
343 """
344 return all( self[i,j] == self[j,i].conjugate()
345 for i in range(self.nrows())
346 for j in range(self.ncols()) )
347
348
349 class HurwitzMatrixAlgebra(MatrixAlgebra):
350 Element = HurwitzMatrixAlgebraElement