]> gitweb.michael.orlitzky.com - sage.d.git/blob - mjo/matrix_algebra.py
5b8b267f5120a4a319f77777885b19731437f518
[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 (i,j,e1) = mon1
236 (k,l,e2) = mon2
237 if j == k:
238 return self.monomial((i,l,e1*e2))
239 else:
240 return self.zero()
241
242 def from_list(self, entries):
243 r"""
244 Construct an element of this algebra from a list of lists of
245 entries.
246
247 SETUP::
248
249 sage: from mjo.matrix_algebra import MatrixAlgebra
250
251 EXAMPLES::
252
253 sage: A = MatrixAlgebra(QQbar, ZZ, 2)
254 sage: A.from_list([[0,I],[-I,0]])
255 +----+---+
256 | 0 | I |
257 +----+---+
258 | -I | 0 |
259 +----+---+
260
261 """
262 nrows = len(entries)
263 ncols = 0
264 if nrows > 0:
265 ncols = len(entries[0])
266
267 if (not all( len(r) == ncols for r in entries )) or (ncols != nrows):
268 raise ValueError("list must be square")
269
270 def convert(e_ij):
271 if e_ij in self.entry_algebra():
272 # Don't re-create an element if it already lives where
273 # it should!
274 return e_ij
275
276 try:
277 # This branch works with e.g. QQbar, where no
278 # to/from_vector() methods are available.
279 return self.entry_algebra()(e_ij)
280 except TypeError:
281 # We have to pass through vectors to convert from the
282 # given entry algebra to ours. Otherwise we can fail to
283 # convert an element of (for example) Octonions(QQ) to
284 # Octonions(AA).
285 return self.entry_algebra().from_vector(e_ij.to_vector())
286
287 return sum( (self.monomial( (i,j, convert(entries[i][j])) )
288 for i in range(nrows)
289 for j in range(ncols) ),
290 self.zero() )
291
292 def _element_constructor_(self, elt):
293 if elt in self:
294 return self
295 else:
296 return self.from_list(elt)
297
298
299 class HurwitzMatrixAlgebraElement(MatrixAlgebraElement):
300 def is_hermitian(self):
301 r"""
302
303 SETUP::
304
305 sage: from mjo.matrix_algebra import HurwitzMatrixAlgebra
306
307 EXAMPLES::
308
309 sage: A = HurwitzMatrixAlgebra(QQbar, ZZ, 2)
310 sage: M = A([ [ 0,I],
311 ....: [-I,0] ])
312 sage: M.is_hermitian()
313 True
314
315 """
316 return all( self[i,j] == self[j,i].conjugate()
317 for i in range(self.nrows())
318 for j in range(self.ncols()) )
319
320
321 class HurwitzMatrixAlgebra(MatrixAlgebra):
322 Element = HurwitzMatrixAlgebraElement