Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sage
Path: blob/develop/src/sage/matrix/matrix_gap.pyx
4081 views
1
r"""
2
Wrappers on GAP matrices
3
"""
4
# ****************************************************************************
5
# Copyright (C) 2017 Vincent Delecroix <[email protected]>
6
#
7
# This program is free software: you can redistribute it and/or modify
8
# it under the terms of the GNU General Public License as published by
9
# the Free Software Foundation, either version 2 of the License, or
10
# (at your option) any later version.
11
# https://www.gnu.org/licenses/
12
# ****************************************************************************
13
from sage.categories.fields import Fields
14
from sage.libs.gap.libgap import libgap
15
from sage.structure.element cimport Matrix
16
from sage.matrix.args cimport MatrixArgs_init
17
18
19
cdef class Matrix_gap(Matrix_dense):
20
r"""
21
A Sage matrix wrapper over a GAP matrix.
22
23
EXAMPLES::
24
25
sage: M = MatrixSpace(ZZ, 2, implementation='gap')
26
sage: m1 = M([1, 0, 2, -3])
27
sage: m2 = M([2, 2, 5, -1])
28
sage: type(m1)
29
<class 'sage.matrix.matrix_gap.Matrix_gap'>
30
31
sage: m1 * m2
32
[ 2 2]
33
[-11 7]
34
sage: type(m1 * m2)
35
<class 'sage.matrix.matrix_gap.Matrix_gap'>
36
37
sage: M = MatrixSpace(QQ, 5, 3, implementation='gap')
38
sage: m = M(range(15))
39
sage: m.left_kernel()
40
Vector space of degree 5 and dimension 3 over Rational Field
41
Basis matrix:
42
[ 1 0 0 -4 3]
43
[ 0 1 0 -3 2]
44
[ 0 0 1 -2 1]
45
46
sage: M = MatrixSpace(ZZ, 10, implementation='gap')
47
sage: m = M(range(100))
48
sage: m.transpose().parent() is M
49
True
50
51
sage: # needs sage.rings.number_field
52
sage: UCF = UniversalCyclotomicField()
53
sage: M = MatrixSpace(UCF, 3, implementation='gap')
54
sage: m = M([UCF.zeta(i) for i in range(1,10)])
55
sage: m
56
[ 1 -1 E(3)]
57
[ E(4) E(5) -E(3)^2]
58
[ E(7) E(8) -E(9)^4 - E(9)^7]
59
sage: (m^2)[1,2]
60
E(180)^32 - E(180)^33 + E(180)^68 - E(180)^69 + E(180)^104 - E(180)^141 - E(180)^156 + E(180)^176 - E(180)^177
61
62
TESTS::
63
64
sage: rings = [ZZ, QQ, UniversalCyclotomicField(), GF(2), GF(3)]
65
sage: rings += [UniversalCyclotomicField()] # needs sage.rings.number_field
66
sage: for ring in rings:
67
....: M = MatrixSpace(ring, 2, implementation='gap')
68
....: TestSuite(M).run(skip=['_test_construction'])
69
....: M = MatrixSpace(ring, 2, 3, implementation='gap')
70
....: TestSuite(M).run(skip=['_test_construction'])
71
"""
72
def __init__(self, parent, entries=None, copy=None, bint coerce=True):
73
r"""
74
INPUT:
75
76
- ``parent`` -- a matrix space
77
78
- ``entries`` -- see :func:`matrix`
79
80
- ``copy`` -- ignored (for backwards compatibility)
81
82
- ``coerce`` -- if ``True`` (the default), convert elements to the
83
base ring before passing them to GAP. If ``False``, pass the
84
elements to GAP as given.
85
86
TESTS::
87
88
sage: M = MatrixSpace(ZZ, 2, implementation='gap')
89
sage: M(0)
90
[0 0]
91
[0 0]
92
sage: M(1)
93
[1 0]
94
[0 1]
95
sage: M(2)
96
[2 0]
97
[0 2]
98
sage: type(M(0))
99
<class 'sage.matrix.matrix_gap.Matrix_gap'>
100
sage: type(M(1))
101
<class 'sage.matrix.matrix_gap.Matrix_gap'>
102
sage: type(M(2))
103
<class 'sage.matrix.matrix_gap.Matrix_gap'>
104
105
sage: M = MatrixSpace(QQ, 2, 3, implementation='gap')
106
sage: M(0)
107
[0 0 0]
108
[0 0 0]
109
sage: M(1)
110
Traceback (most recent call last):
111
...
112
TypeError: nonzero scalar matrix must be square
113
sage: MatrixSpace(QQ, 1, 2, implementation='gap')(0)
114
[0 0]
115
sage: MatrixSpace(QQ, 2, 1, implementation='gap')(0)
116
[0]
117
[0]
118
"""
119
ma = MatrixArgs_init(parent, entries)
120
it = ma.iter(coerce)
121
cdef list mat = []
122
cdef long i, j
123
for i in range(ma.nrows):
124
row = [next(it) for j in range(ma.ncols)]
125
mat.append(row)
126
self._libgap = libgap(mat)
127
128
cdef Matrix_gap _new(self, Py_ssize_t nrows, Py_ssize_t ncols):
129
if nrows == self._nrows and ncols == self._ncols:
130
P = self._parent
131
else:
132
P = self.matrix_space(nrows, ncols)
133
134
return Matrix_gap.__new__(Matrix_gap, P)
135
136
def __copy__(self):
137
r"""
138
TESTS::
139
140
sage: M = MatrixSpace(QQ, 2, implementation='gap')
141
sage: m1 = M([1,2,0,3])
142
sage: m2 = m1.__copy__()
143
sage: m2
144
[1 2]
145
[0 3]
146
sage: m1[0,1] = -2
147
sage: m1
148
[ 1 -2]
149
[ 0 3]
150
sage: m2
151
[1 2]
152
[0 3]
153
"""
154
cdef Matrix_gap M = self._new(self._nrows, self._ncols)
155
M._libgap = self._libgap.deepcopy(1)
156
return M
157
158
def __reduce__(self):
159
r"""
160
TESTS::
161
162
sage: M = MatrixSpace(ZZ, 2, implementation='gap')
163
sage: m = M([1,2,1,2])
164
sage: loads(dumps(m)) == m
165
True
166
"""
167
return self._parent, (self.list(),)
168
169
cpdef GapElement gap(self):
170
r"""
171
Return the underlying gap object.
172
173
EXAMPLES::
174
175
sage: M = MatrixSpace(ZZ, 2, implementation='gap')
176
sage: m = M([1,2,2,1]).gap()
177
sage: m
178
[ [ 1, 2 ], [ 2, 1 ] ]
179
sage: type(m)
180
<class 'sage.libs.gap.element.GapElement_List'>
181
182
sage: m.MatrixAutomorphisms()
183
Group([ (1,2) ])
184
"""
185
return self._libgap
186
187
cdef get_unsafe(self, Py_ssize_t i, Py_ssize_t j):
188
return self._base_ring(self._libgap[i,j])
189
190
cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, object x):
191
r"""
192
TESTS::
193
194
sage: M = MatrixSpace(ZZ, 2, implementation='gap')
195
sage: m = M(0)
196
sage: m[0,1] = 13
197
sage: m
198
[ 0 13]
199
[ 0 0]
200
sage: m[1,0] = -1/2
201
Traceback (most recent call last):
202
...
203
TypeError: no conversion of this rational to integer
204
"""
205
self._libgap[i,j] = x
206
207
cdef copy_from_unsafe(self, Py_ssize_t iDst, Py_ssize_t jDst, src, Py_ssize_t iSrc, Py_ssize_t jSrc):
208
r"""
209
Copy the ``(iSrc, jSrc)`` entry of ``src`` into the ``(iDst, jDst)``
210
entry of ``self``.
211
212
INPUT:
213
214
- ``iDst`` - the row to be copied to in ``self``.
215
- ``jDst`` - the column to be copied to in ``self``.
216
- ``src`` - the matrix to copy from. Should be a Matrix_gap with the
217
same base ring as ``self``.
218
- ``iSrc`` - the row to be copied from in ``src``.
219
- ``jSrc`` - the column to be copied from in ``src``.
220
221
TESTS::
222
223
sage: M = MatrixSpace(ZZ, 3, 4, implementation='gap')(range(12))
224
sage: M
225
[ 0 1 2 3]
226
[ 4 5 6 7]
227
[ 8 9 10 11]
228
sage: M.transpose()
229
[ 0 4 8]
230
[ 1 5 9]
231
[ 2 6 10]
232
[ 3 7 11]
233
sage: M.matrix_from_rows([0,2])
234
[ 0 1 2 3]
235
[ 8 9 10 11]
236
sage: M.matrix_from_columns([1,3])
237
[ 1 3]
238
[ 5 7]
239
[ 9 11]
240
sage: M.matrix_from_rows_and_columns([1,2],[0,3])
241
[ 4 7]
242
[ 8 11]
243
"""
244
cdef Matrix_gap _src = <Matrix_gap>src
245
self._libgap[iDst,jDst] = _src._libgap[iSrc,jSrc]
246
247
cpdef _richcmp_(self, other, int op):
248
r"""
249
Compare ``self`` and ``right``.
250
251
EXAMPLES::
252
253
sage: M = MatrixSpace(ZZ, 2, implementation='gap')
254
sage: m1 = M([1,2,3,4])
255
sage: m2 = M([1,2,3,4])
256
sage: m3 = M([1,2,0,4])
257
sage: m1 == m2
258
True
259
sage: m1 != m2
260
False
261
sage: m1 == m3
262
False
263
sage: m1 != m3
264
True
265
266
sage: M = MatrixSpace(QQ, 2, implementation='gap')
267
sage: m1 = M([1/2, 1/3, 2, -5])
268
sage: m2 = M([1/2, 1/3, 2, -5])
269
sage: m3 = M([1/2, 0, 2, -5])
270
sage: m1 == m2
271
True
272
sage: m1 != m2
273
False
274
sage: m1 == m3
275
False
276
sage: m1 != m3
277
True
278
279
sage: # needs sage.rings.number_field
280
sage: UCF = UniversalCyclotomicField()
281
sage: M = MatrixSpace(UCF, 2, implementation='gap')
282
sage: m1 = M([E(2), E(3), 0, E(4)])
283
sage: m2 = M([E(2), E(3), 0, E(4)])
284
sage: m3 = M([E(2), E(3), 0, E(5)])
285
sage: m1 == m2
286
True
287
sage: m1 != m2
288
False
289
sage: m1 == m3
290
False
291
sage: m1 != m3
292
True
293
"""
294
return (<Matrix_gap> self)._libgap._richcmp_((<Matrix_gap> other)._libgap, op)
295
296
def __neg__(self):
297
r"""
298
TESTS::
299
300
sage: M = MatrixSpace(ZZ, 2, 3, implementation='gap')
301
sage: m = M([1, -1, 3, 2, -5, 1])
302
sage: -m
303
[-1 1 -3]
304
[-2 5 -1]
305
"""
306
cdef Matrix_gap M = self._new(self._nrows, self._ncols)
307
M._libgap = self._libgap.AdditiveInverse()
308
return M
309
310
def __invert__(self):
311
r"""
312
TESTS::
313
314
sage: M = MatrixSpace(QQ, 2, implementation='gap')
315
sage: ~M([4,2,2,2])
316
[ 1/2 -1/2]
317
[-1/2 1]
318
"""
319
cdef Matrix_gap M
320
if self._base_ring in Fields():
321
M = self._new(self._nrows, self._ncols)
322
M._libgap = self._libgap.Inverse()
323
return M
324
return Matrix_dense.__invert__(self)
325
326
cpdef _add_(left, right):
327
r"""
328
TESTS::
329
330
sage: M = MatrixSpace(ZZ, 2, 3, implementation='gap')
331
sage: M([1,2,3,4,3,2]) + M([1,1,1,1,1,1]) == M([2,3,4,5,4,3])
332
True
333
"""
334
cdef Matrix_gap cleft = <Matrix_gap> left
335
cdef Matrix_gap ans = cleft._new(cleft._nrows, cleft._ncols)
336
ans._libgap = left._libgap + (<Matrix_gap> right)._libgap
337
return ans
338
339
cpdef _sub_(left, right):
340
r"""
341
TESTS::
342
343
sage: M = MatrixSpace(ZZ, 2, 3, implementation='gap')
344
sage: M([1,2,3,4,3,2]) - M([1,1,1,1,1,1]) == M([0,1,2,3,2,1])
345
True
346
"""
347
cdef Matrix_gap cleft = <Matrix_gap> left
348
cdef Matrix_gap ans = cleft._new(cleft._nrows, cleft._ncols)
349
ans._libgap = left._libgap - (<Matrix_gap> right)._libgap
350
return ans
351
352
cdef Matrix _matrix_times_matrix_(left, Matrix right):
353
r"""
354
TESTS::
355
356
sage: M = MatrixSpace(QQ, 2, implementation='gap')
357
sage: m1 = M([1,2,-4,3])
358
sage: m2 = M([-1,1,1,-1])
359
sage: m1 * m2
360
[ 1 -1]
361
[ 7 -7]
362
"""
363
if left._ncols != right._nrows:
364
raise IndexError("Number of columns of self must equal number of rows of right.")
365
cdef Matrix_gap M = left._new(left._nrows, right._ncols)
366
M._libgap = <Matrix_gap> ((<Matrix_gap> left)._libgap * (<Matrix_gap> right)._libgap)
367
return M
368
369
def transpose(self):
370
r"""
371
Return the transpose of this matrix.
372
373
EXAMPLES::
374
375
sage: M = MatrixSpace(QQ, 2, implementation='gap')
376
sage: M([4,2,23,52]).transpose()
377
[ 4 23]
378
[ 2 52]
379
380
sage: M = MatrixSpace(QQ, 1, 3, implementation='gap')
381
sage: M([4,2,52]).transpose()
382
[ 4]
383
[ 2]
384
[52]
385
386
TESTS::
387
388
sage: M = MatrixSpace(QQ, 2, 3, implementation='gap')
389
sage: m = M(range(6))
390
sage: m.subdivide([1],[2])
391
sage: m
392
[0 1|2]
393
[---+-]
394
[3 4|5]
395
sage: m.transpose()
396
[0|3]
397
[1|4]
398
[-+-]
399
[2|5]
400
401
sage: M = MatrixSpace(QQ, 0, 2, implementation='gap')
402
sage: m = M([])
403
sage: m.subdivide([],[1])
404
sage: m.subdivisions()
405
([], [1])
406
sage: m.transpose().subdivisions()
407
([1], [])
408
409
sage: M = MatrixSpace(QQ, 2, 0, implementation='gap')
410
sage: m = M([])
411
sage: m.subdivide([1],[])
412
sage: m.subdivisions()
413
([1], [])
414
sage: m.transpose().subdivisions()
415
([], [1])
416
"""
417
cdef Matrix_gap M
418
M = self._new(self._ncols, self._nrows)
419
M._libgap = self._libgap.TransposedMat()
420
if self._subdivisions is not None:
421
row_divs, col_divs = self.subdivisions()
422
M.subdivide(col_divs, row_divs)
423
return M
424
425
def determinant(self):
426
r"""
427
Return the determinant of this matrix.
428
429
EXAMPLES::
430
431
sage: M = MatrixSpace(ZZ, 2, implementation='gap')
432
sage: M([2, 1, 1, 1]).determinant()
433
1
434
sage: M([2, 1, 3, 3]).determinant()
435
3
436
437
TESTS::
438
439
sage: M = MatrixSpace(ZZ, 1, implementation='gap')
440
sage: parent(M(1).determinant())
441
Integer Ring
442
443
sage: M = MatrixSpace(QQ, 1, implementation='gap')
444
sage: parent(M(1).determinant())
445
Rational Field
446
447
sage: # needs sage.rings.number_field
448
sage: M = MatrixSpace(UniversalCyclotomicField(), 1, implementation='gap')
449
sage: parent(M(1).determinant())
450
Universal Cyclotomic Field
451
"""
452
return self._base_ring(self._libgap.DeterminantMat())
453
454
def trace(self):
455
r"""
456
Return the trace of this matrix.
457
458
EXAMPLES::
459
460
sage: M = MatrixSpace(ZZ, 2, implementation='gap')
461
sage: M([2, 1, 1, 1]).trace()
462
3
463
sage: M([2, 1, 3, 3]).trace()
464
5
465
466
TESTS::
467
468
sage: M = MatrixSpace(ZZ, 1, implementation='gap')
469
sage: parent(M(1).trace())
470
Integer Ring
471
472
sage: M = MatrixSpace(QQ, 1, implementation='gap')
473
sage: parent(M(1).trace())
474
Rational Field
475
476
sage: # needs sage.rings.number_field
477
sage: M = MatrixSpace(UniversalCyclotomicField(), 1, implementation='gap')
478
sage: parent(M(1).trace())
479
Universal Cyclotomic Field
480
"""
481
return self._base_ring(self._libgap.TraceMat())
482
483
def rank(self):
484
r"""
485
Return the rank of this matrix.
486
487
EXAMPLES::
488
489
sage: M = MatrixSpace(ZZ, 2, implementation='gap')
490
sage: M([2, 1, 1, 1]).rank()
491
2
492
sage: M([2, 1, 4, 2]).rank()
493
1
494
"""
495
return int(self._libgap.RankMat())
496
497
def minpoly(self, var='x', **kwds):
498
"""
499
Compute the minimal polynomial.
500
501
EXAMPLES::
502
503
sage: M = MatrixSpace(ZZ, 2, implementation='gap')
504
sage: M([0, 1, -1, -1]).minpoly()
505
x^2 + x + 1
506
"""
507
po = self._libgap.MinimalPolynomial().sage()
508
return po.change_variable_name(var)
509
510
minimal_polynomial = minpoly
511
512
def elementary_divisors(self):
513
"""
514
Return the list of elementary divisors of this matrix.
515
516
EXAMPLES::
517
518
sage: M = MatrixSpace(ZZ, 5, implementation='gap')
519
sage: T = M([(i+1)**(j+1) for i in range(5) for j in range(5)])
520
sage: T.elementary_divisors()
521
[1, 2, 6, 24, 120]
522
"""
523
return [self._base_ring(u)
524
for u in self._libgap.ElementaryDivisorsMat()]
525
526