]>
gitweb.michael.orlitzky.com - sage.d.git/blob - 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
7 class MatrixAlgebraElement(IndexedFreeModuleElement
):
9 return self
.parent().nrows()
17 sage: from mjo.matrix_algebra import MatrixAlgebra
21 sage: M = MatrixAlgebra(2, QQbar,RDF)
22 sage: A = M.monomial((0,0,1)) + 4*M.monomial((0,1,1))
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():
42 Display this matrix as a table.
44 The SageMath Matrix class representation is not easily reusable,
45 but using a table fakes it.
49 sage: from mjo.matrix_algebra import MatrixAlgebra
53 sage: MatrixAlgebra(2,ZZ,ZZ).zero()
62 sage: MatrixAlgebra(0,ZZ,ZZ).zero()
66 if self
.nrows() == 0 or self
.ncols() == 0:
67 # Otherwise we get a crash or a blank space, depending on
68 # how hard we work for it. This is what MatrixSpace(...,
72 return table(self
.rows(), frame
=True)._repr
_()
77 Return one long list of this matrix's entries.
81 sage: from mjo.matrix_algebra import MatrixAlgebra
85 sage: A = MatrixAlgebra(2,ZZ,ZZ)
86 sage: A([[1,2],[3,4]]).list()
90 return sum( self
.rows(), [] )
93 def __getitem__(self
, indices
):
98 sage: from mjo.matrix_algebra import MatrixAlgebra
102 sage: M = MatrixAlgebra(2,ZZ,ZZ)([[1,2],[3,4]])
114 d
= self
.monomial_coefficients()
115 A
= self
.parent().entry_algebra()
116 return A
.sum( d
[k
]*k
[2]
118 if k
[0] == i
and k
[1] == j
)
123 Return the sum of this matrix's diagonal entries.
127 sage: from mjo.matrix_algebra import MatrixAlgebra
131 The trace (being a sum of entries) belongs to the same algebra
132 as those entries, and NOT the scalar ring::
134 sage: entries = MatrixSpace(ZZ,2)
136 sage: M = MatrixAlgebra(2, entries, scalars)
137 sage: I = entries.one()
138 sage: Z = entries.zero()
139 sage: M([[I,Z],[Z,I]]).trace()
144 d
= self
.monomial_coefficients()
145 A
= self
.parent().entry_algebra()
146 return A
.sum( d
[k
]*k
[2] for k
in d
if k
[0] == k
[1] )
148 def matrix_space(self
):
153 sage: from mjo.matrix_algebra import MatrixAlgebra
157 sage: entries = QuaternionAlgebra(QQ,-1,-1)
158 sage: M = MatrixAlgebra(3, entries, QQ)
159 sage: M.random_element().matrix_space() == M
166 class MatrixAlgebra(CombinatorialFreeModule
):
168 An algebra of ``n``-by-``n`` matrices over an arbitrary scalar
169 ring whose entries come from a magmatic algebra that need not
170 be the same as the scalars.
172 The usual matrix spaces in SageMath don't support separate spaces
173 for the entries and the scalars; in particular they assume that
174 the entries come from a commutative and associative ring. This
175 is problematic in several interesting matrix algebras, like those
176 where the entries are quaternions or octonions.
180 sage: from mjo.matrix_algebra import MatrixAlgebra
184 The existence of a unit element is determined dynamically::
186 sage: MatrixAlgebra(2,ZZ,ZZ).one()
194 Element
= MatrixAlgebraElement
196 def __init__(self
, n
, entry_algebra
, scalars
, prefix
="A", **kwargs
):
198 category
= MagmaticAlgebras(scalars
).FiniteDimensional()
199 category
= category
.WithBasis()
201 if "Unital" in entry_algebra
.category().axioms():
202 category
= category
.Unital()
203 entry_one
= entry_algebra
.one()
204 self
.one
= lambda: self
.sum( (self
.monomial((i
,i
,entry_one
))
205 for i
in range(self
.nrows()) ) )
207 if "Associative" in entry_algebra
.category().axioms():
208 category
= category
.Associative()
212 # Since the scalar ring is (say) real but the entries are not,
213 # sticking a "1" in each position doesn't give us a basis for
214 # the space. We actually need to stick each of e0, e1, ... (a
215 # basis for the entry algebra itself) into each position.
216 self
._entry
_algebra
= entry_algebra
218 # Needs to make the (overridden) method call when, for example,
219 # the entry algebra is the complex numbers and its gens() method
221 entry_basis
= self
.entry_algebra_gens()
223 basis_indices
= [(i
,j
,e
) for i
in range(n
)
225 for e
in entry_basis
]
227 super().__init
__(scalars
,
234 return ("Module of %d by %d matrices with entries in %s"
235 " over the scalar ring %s" %
238 self
.entry_algebra(),
241 def entry_algebra(self
):
243 Return the algebra that our elements' entries come from.
245 return self
._entry
_algebra
247 def entry_algebra_gens(self
):
249 Return a tuple of the generators of (that is, a basis for) the
250 entries of this matrix algebra.
252 This can be overridden in subclasses to work around the
253 inconsistency in the ``gens()`` methods of the various
256 return self
.entry_algebra().gens()
258 def _entry_algebra_element_to_vector(self
, entry
):
260 Return a vector representation (of length equal to the cardinality
261 of :meth:`entry_algebra_gens`) of the given ``entry``.
263 This can be overridden in subclasses to work around the fact that
264 real numbers, complex numbers, quaternions, et cetera, all require
265 different incantations to turn them into a vector.
267 It only makes sense to "guess" here in the superclass when no
268 subclass that overrides :meth:`entry_algebra_gens` exists. So
269 if you have a special subclass for your annoying entry algebra,
270 override this with the correct implementation there instead of
271 adding a bunch of awkward cases to this superclass method.
275 sage: from mjo.hurwitz import Octonions
276 sage: from mjo.matrix_algebra import MatrixAlgebra
282 sage: A = MatrixAlgebra(1, AA, QQ)
283 sage: A._entry_algebra_element_to_vector(AA(17))
288 sage: A = MatrixAlgebra(1, Octonions(), QQ)
289 sage: e = A.entry_algebra_gens()
290 sage: A._entry_algebra_element_to_vector(e[0])
291 (1, 0, 0, 0, 0, 0, 0, 0)
292 sage: A._entry_algebra_element_to_vector(e[1])
293 (0, 1, 0, 0, 0, 0, 0, 0)
294 sage: A._entry_algebra_element_to_vector(e[2])
295 (0, 0, 1, 0, 0, 0, 0, 0)
296 sage: A._entry_algebra_element_to_vector(e[3])
297 (0, 0, 0, 1, 0, 0, 0, 0)
298 sage: A._entry_algebra_element_to_vector(e[4])
299 (0, 0, 0, 0, 1, 0, 0, 0)
300 sage: A._entry_algebra_element_to_vector(e[5])
301 (0, 0, 0, 0, 0, 1, 0, 0)
302 sage: A._entry_algebra_element_to_vector(e[6])
303 (0, 0, 0, 0, 0, 0, 1, 0)
304 sage: A._entry_algebra_element_to_vector(e[7])
305 (0, 0, 0, 0, 0, 0, 0, 1)
309 sage: MS = MatrixSpace(QQ,2)
310 sage: A = MatrixAlgebra(1, MS, QQ)
311 sage: A._entry_algebra_element_to_vector(MS([[1,2],[3,4]]))
315 if hasattr(entry
, 'to_vector'):
316 return entry
.to_vector()
318 from sage
.modules
.free_module
import FreeModule
319 d
= len(self
.entry_algebra_gens())
320 V
= FreeModule(self
.entry_algebra().base_ring(), d
)
322 if hasattr(entry
, 'list'):
324 return V(entry
.list())
326 # This works in AA, and will crash if it doesn't know what to
327 # do, and that's fine because then I don't know what to do
337 def product_on_basis(self
, mon1
, mon2
):
342 sage: from mjo.hurwitz import Octonions
343 sage: from mjo.matrix_algebra import MatrixAlgebra
347 sage: O = Octonions(QQ)
351 sage: A = MatrixAlgebra(2,O,QQ)
352 sage: A.product_on_basis( (0,0,e[2]), (0,0,e[1]) )
363 # There's no reason to expect e1*e2 to itself be a monomial,
364 # so we have to do some manual conversion to get one.
365 p
= self
._entry
_algebra
_element
_to
_vector
(e1
*e2
)
367 # We have to convert alpha_g because a priori it lives in the
368 # base ring of the entry algebra.
370 return self
.sum_of_terms( (((i
,l
,g
), R(alpha_g
))
372 in zip(p
, self
.entry_algebra_gens()) ),
377 def from_list(self
, entries
):
379 Construct an element of this algebra from a list of lists of
384 sage: from mjo.hurwitz import ComplexMatrixAlgebra
388 sage: A = ComplexMatrixAlgebra(2, QQbar, ZZ)
389 sage: M = A.from_list([[0,I],[-I,0]])
397 (0, 0, 0, 1, 0, -1, 0, 0)
403 ncols
= len(entries
[0])
405 if (not all( len(r
) == ncols
for r
in entries
)) or (ncols
!= nrows
):
406 raise ValueError("list must be square")
409 if e_ij
in self
.entry_algebra():
410 # Don't re-create an element if it already lives where
415 # This branch works with e.g. QQbar, where no
416 # to/from_vector() methods are available.
417 return self
.entry_algebra()(e_ij
)
419 # We have to pass through vectors to convert from the
420 # given entry algebra to ours. Otherwise we can fail to
421 # convert an element of (for example) Octonions(QQ) to
423 return self
.entry_algebra().from_vector(e_ij
.to_vector())
425 def entry_to_element(i
,j
,entry
):
426 # Convert an entry at i,j to a matrix whose only non-zero
427 # entry is i,j and corresponds to the entry.
428 p
= self
._entry
_algebra
_element
_to
_vector
(entry
)
430 # We have to convert alpha_g because a priori it lives in the
431 # base ring of the entry algebra.
433 return self
.sum_of_terms( (((i
,j
,g
), R(alpha_g
))
435 in zip(p
, self
.entry_algebra_gens()) ),
438 return self
.sum( entry_to_element(i
,j
,entries
[i
][j
])
439 for j
in range(ncols
)
440 for i
in range(nrows
) )
443 def _element_constructor_(self
, elt
):
447 return self
.from_list(elt
)