Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/matrix/matrix_integer_2x2.pyx
4049 views
1
"""
2
Two by two matrices over the integers.
3
"""
4
5
include "../ext/interrupt.pxi"
6
include "../ext/stdsage.pxi"
7
include "../ext/python_list.pxi"
8
include "../ext/python_number.pxi"
9
include "../ext/python_ref.pxi"
10
11
from sage.rings.all import polygen, QQ,ZZ
12
from sage.rings.integer cimport Integer
13
from sage.structure.element cimport ModuleElement, Element
14
from sage.structure.mutability cimport Mutability
15
16
cimport matrix_dense
17
import matrix_dense
18
19
cimport matrix
20
21
from matrix_space import MatrixSpace
22
from sage.misc.lazy_attribute import lazy_attribute
23
24
class MatrixSpace_ZZ_2x2_class(MatrixSpace):
25
"""
26
Return a space of 2x2 matrices over ZZ, whose elements are of
27
type sage.matrix.matrix_integer_2x2.Matrix_integer_2x2 instead of
28
sage.matrix.matrix_integer_dense.Matrix_integer_dense.
29
30
NOTE: This class exists only for quickly creating and testing
31
elements of this type. Once these become the default for 2x2
32
matrices over ZZ, this class should be removed.
33
34
EXAMPLES::
35
36
sage: sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()
37
Space of 2x2 integer matrices
38
39
By trac ticket #12290, it is a unique parent::
40
41
sage: from sage.matrix.matrix_integer_2x2 import MatrixSpace_ZZ_2x2
42
sage: MatrixSpace_ZZ_2x2() is MatrixSpace_ZZ_2x2()
43
True
44
sage: M = MatrixSpace_ZZ_2x2()
45
sage: loads(dumps(M)) is M
46
True
47
48
"""
49
@staticmethod
50
def __classcall__(cls):
51
"""
52
EXAMPLES::
53
54
sage: from sage.matrix.matrix_integer_2x2 import MatrixSpace_ZZ_2x2
55
sage: MatrixSpace_ZZ_2x2() is MatrixSpace_ZZ_2x2() # indirect doctest
56
True
57
58
"""
59
return super(MatrixSpace,cls).__classcall__(cls)
60
61
def __init__(self):
62
"""
63
EXAMPLES::
64
65
sage: sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()
66
Space of 2x2 integer matrices
67
"""
68
MatrixSpace.__init__(self, ZZ, 2, 2, False)
69
70
def _repr_(self):
71
"""
72
EXAMPLES::
73
74
sage: sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()
75
Space of 2x2 integer matrices
76
"""
77
return "Space of 2x2 integer matrices"
78
79
def _get_matrix_class(self):
80
"""
81
EXAMPLES::
82
83
sage: A = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()
84
sage: A._get_matrix_class()
85
<type 'sage.matrix.matrix_integer_2x2.Matrix_integer_2x2'>
86
"""
87
return Matrix_integer_2x2
88
89
ZZ_2x2_parent = MatrixSpace_ZZ_2x2_class()
90
def MatrixSpace_ZZ_2x2():
91
"""
92
Return the space of 2x2 integer matrices. (This function
93
exists to maintain uniqueness of parents.)
94
95
EXAMPLES::
96
97
sage: M = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()
98
sage: M
99
Space of 2x2 integer matrices
100
sage: M is sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()
101
True
102
"""
103
return ZZ_2x2_parent
104
105
106
cdef class Matrix_integer_2x2(matrix_dense.Matrix_dense):
107
r"""
108
The \class{Matrix_integer_2x2} class derives from
109
\class{Matrix_dense}, and defines fast functionality for 2x2
110
matrices over the integers. Matrices are represented by four gmp
111
integer fields: a, b, c, and d.
112
113
EXAMPLES::
114
115
sage: MS = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()
116
sage: m = MS([1,2,3,4]) ; m
117
[1 2]
118
[3 4]
119
sage: TestSuite(m).run()
120
"""
121
########################################################################
122
# LEVEL 1 functionality
123
# x * __cinit__
124
# x * __init__
125
# x * __dealloc__
126
# x * set_unsafe
127
# x * get_unsafe
128
# x * def _pickle
129
# x * def _unpickle
130
########################################################################
131
132
def __cinit__(self):
133
mpz_init(self.a)
134
mpz_init(self.b)
135
mpz_init(self.c)
136
mpz_init(self.d)
137
self._entries = &self.a
138
139
def __init__(self, parent, entries, copy, coerce):
140
"""
141
EXAMPLES::
142
143
sage: MS = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()
144
sage: MS()
145
[0 0]
146
[0 0]
147
sage: MS(5)
148
[5 0]
149
[0 5]
150
sage: MS([3,4,5,6])
151
[3 4]
152
[5 6]
153
sage: MS([11,3])
154
Traceback (most recent call last):
155
...
156
TypeError: cannot construct an element of
157
Space of 2x2 integer matrices from [11, 3]!
158
"""
159
matrix.Matrix.__init__(self, parent)
160
161
cdef Py_ssize_t i, n
162
163
if entries is None:
164
entries = 0
165
166
if not isinstance(entries, list):
167
try:
168
x = ZZ(entries)
169
is_list = 0
170
except TypeError:
171
if hasattr(entries, "list"):
172
entries = entries.list()
173
is_list = 1
174
else:
175
try:
176
entries = list(entries)
177
is_list = 1
178
except TypeError:
179
raise TypeError("entries must be coercible to a list or the base ring")
180
181
else:
182
is_list = 1
183
184
if is_list:
185
186
if len(entries) != self._nrows * self._ncols:
187
raise TypeError("entries has the wrong length")
188
189
if coerce:
190
mpz_set(self.a, (<Integer>ZZ(entries[0])).value)
191
mpz_set(self.b, (<Integer>ZZ(entries[1])).value)
192
mpz_set(self.c, (<Integer>ZZ(entries[2])).value)
193
mpz_set(self.d, (<Integer>ZZ(entries[3])).value)
194
else:
195
mpz_set(self.a, (<Integer>entries[0]).value)
196
mpz_set(self.b, (<Integer>entries[1]).value)
197
mpz_set(self.c, (<Integer>entries[2]).value)
198
mpz_set(self.d, (<Integer>entries[3]).value)
199
200
else:
201
202
mpz_set(self.a, (<Integer>x).value)
203
mpz_set_si(self.b, 0)
204
mpz_set_si(self.c, 0)
205
mpz_set(self.d, (<Integer>x).value)
206
207
def __dealloc__(self):
208
mpz_clear(self.a)
209
mpz_clear(self.b)
210
mpz_clear(self.c)
211
mpz_clear(self.d)
212
213
def testit(self):
214
print "testing",self._base_ring
215
216
cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, value):
217
mpz_set(self._entries[(i << 1) | j], (<Integer>value).value)
218
219
cdef get_unsafe(self, Py_ssize_t i, Py_ssize_t j):
220
cdef Integer z
221
z = Integer.__new__(Integer)
222
mpz_set(z.value, self._entries[(i << 1) | j])
223
return z
224
225
def _pickle(self):
226
"""
227
EXAMPLES:
228
sage: M = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()
229
sage: m = M([9,8,7,6])
230
sage: m == loads(dumps(m))
231
True
232
"""
233
return self.list(), 0
234
235
def _unpickle(self, data, int version):
236
"""
237
EXAMPLES::
238
239
sage: M = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()
240
sage: m = M(5)
241
sage: m == loads(dumps(m))
242
True
243
"""
244
if version == 0:
245
mpz_set(self.a, (<Integer>ZZ(data[0])).value)
246
mpz_set(self.b, (<Integer>ZZ(data[1])).value)
247
mpz_set(self.c, (<Integer>ZZ(data[2])).value)
248
mpz_set(self.d, (<Integer>ZZ(data[3])).value)
249
else:
250
raise RuntimeError("unknown matrix version")
251
252
def __richcmp__(matrix.Matrix self, right, int op):
253
"""
254
EXAMPLES::
255
256
sage: M = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()
257
sage: m = M([1,2,3,4])
258
sage: n = M([-1,2,3,4])
259
sage: m < n
260
False
261
sage: m > n
262
True
263
sage: m == m
264
True
265
"""
266
return self._richcmp(right, op)
267
268
def __hash__(self):
269
"""
270
Return a hash of self.
271
272
EXAMPLES::
273
274
sage: M = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()
275
sage: m = M([1,2,3,4])
276
sage: m.set_immutable()
277
sage: m.__hash__()
278
8
279
"""
280
return self._hash()
281
282
def __iter__(self):
283
"""
284
Return an iterator over the entries of self.
285
286
EXAMPLES::
287
288
sage: M = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()
289
sage: m = M([1,2,3,4])
290
sage: m.__iter__()
291
<listiterator object at ...>
292
"""
293
return iter(self.list())
294
295
cdef Matrix_integer_2x2 _new_c(self):
296
cdef Matrix_integer_2x2 x
297
x = <Matrix_integer_2x2> Matrix_integer_2x2.__new__(Matrix_integer_2x2, self._parent, None, False, False)
298
x._parent = self._parent
299
x._nrows = 2
300
x._ncols = 2
301
return x
302
303
304
########################################################################
305
# LEVEL 2 functionality
306
# x * cdef _add_
307
# * cdef _mul_
308
# * cdef _cmp_c_impl
309
# x * __neg__
310
# x * __invert__
311
# x * __copy__
312
# x * _multiply_classical
313
# x * _list -- copy of the list of underlying elements
314
# * _dict -- copy of the sparse dictionary of underlying elements
315
########################################################################
316
317
def __copy__(self):
318
"""
319
Return a copy of self.
320
321
EXAMPLES::
322
323
sage: M = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()
324
sage: m = M([1,3,4,5])
325
sage: n = copy(m)
326
sage: n == m
327
True
328
sage: n is m
329
False
330
sage: m.is_mutable()
331
True
332
sage: n.is_mutable()
333
True
334
335
The following test against a bug fixed in trac ticket #12290::
336
337
sage: n.base_ring()
338
Integer Ring
339
340
"""
341
cdef Matrix_integer_2x2 x
342
x = self._new_c()
343
mpz_set(x.a, self.a)
344
mpz_set(x.b, self.b)
345
mpz_set(x.c ,self.c)
346
mpz_set(x.d, self.d)
347
x._mutability = Mutability(False)
348
x._base_ring = self._base_ring
349
if self._subdivisions is not None:
350
x.subdivide(*self.subdivisions())
351
return x
352
353
cpdef ModuleElement _add_(left, ModuleElement right):
354
"""
355
EXAMPLES::
356
357
sage: M = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()
358
sage: M([8,7,6,5]) + M([4,5,6,7])
359
[12 12]
360
[12 12]
361
"""
362
cdef Matrix_integer_2x2 A
363
A = left._new_c()
364
mpz_add(A.a, left.a, (<Matrix_integer_2x2>right).a)
365
mpz_add(A.b, left.b, (<Matrix_integer_2x2>right).b)
366
mpz_add(A.c, left.c, (<Matrix_integer_2x2>right).c)
367
mpz_add(A.d, left.d, (<Matrix_integer_2x2>right).d)
368
return A
369
370
cdef int _cmp_c_impl(left, Element right) except -2:
371
"""
372
EXAMPLES::
373
374
sage: M = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()
375
sage: m = M([4,3,7,9])
376
sage: n = M([3,3,7,9])
377
sage: m < n
378
False
379
sage: m > n
380
True
381
sage: m == m
382
True
383
sage: m == n
384
False
385
"""
386
return mpz_cmp(left.a, (<Matrix_integer_2x2>right).a) or \
387
mpz_cmp(left.b, (<Matrix_integer_2x2>right).b) or \
388
mpz_cmp(left.c, (<Matrix_integer_2x2>right).c) or \
389
mpz_cmp(left.d, (<Matrix_integer_2x2>right).d)
390
391
def __neg__(self):
392
"""
393
Return the additive inverse of self.
394
395
EXAMPLES::
396
397
sage: M = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()
398
sage: m = M([1,-1,-1,1])
399
sage: -m
400
[-1 1]
401
[ 1 -1]
402
"""
403
cdef Matrix_integer_2x2 A
404
A = self._new_c()
405
mpz_neg(A.a, self.a)
406
mpz_neg(A.b, self.b)
407
mpz_neg(A.c, self.c)
408
mpz_neg(A.d, self.d)
409
return A
410
411
def __invert__(self):
412
"""
413
Return the inverse of self, as a 2x2 matrix over QQ.
414
415
EXAMPLES::
416
417
sage: M = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()
418
sage: m = M([2,0,0,2])
419
sage: m^-1
420
[1/2 0]
421
[ 0 1/2]
422
sage: type(m^-1)
423
<type 'sage.matrix.matrix_rational_dense.Matrix_rational_dense'>
424
"""
425
MS = self._matrix_(QQ).parent()
426
D = self.determinant()
427
return MS([self.get_unsafe(1,1)/D, -self.get_unsafe(0,1)/D, -self.get_unsafe(1,0)/D, self.get_unsafe(0,0)/D], coerce=False, copy=False)
428
429
def __invert__unit(self):
430
"""
431
If self is a unit, i.e. if its determinant is 1 or -1, return
432
the inverse of self as a 2x2 matrix over ZZ. If not, raise a
433
ZeroDivisionError.
434
435
EXAMPLES::
436
437
sage: M = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()
438
sage: m = M([1,8,3,25])
439
sage: m.__invert__unit()
440
[25 -8]
441
[-3 1]
442
sage: type(m.__invert__unit())
443
<type 'sage.matrix.matrix_integer_2x2.Matrix_integer_2x2'>
444
sage: m^-1
445
[25 -8]
446
[-3 1]
447
sage: type(m^-1)
448
<type 'sage.matrix.matrix_rational_dense.Matrix_rational_dense'>
449
sage: m.__invert__unit() == m^-1
450
True
451
sage: M([2,0,0,2]).__invert__unit()
452
Traceback (most recent call last):
453
...
454
ZeroDivisionError: Not a unit!
455
"""
456
cdef Matrix_integer_2x2 A
457
cdef Integer D
458
D = self.determinant()
459
if D.is_one():
460
A = self._new_c()
461
mpz_set(A.a, self.d)
462
mpz_neg(A.b, self.b)
463
mpz_neg(A.c, self.c)
464
mpz_set(A.d, self.a)
465
return A
466
467
elif D.is_unit():
468
A = self._new_c()
469
mpz_neg(A.a, self.d)
470
mpz_set(A.b, self.b)
471
mpz_set(A.c, self.c)
472
mpz_neg(A.d, self.a)
473
return A
474
475
else:
476
raise ZeroDivisionError("Not a unit!")
477
_invert_unit = __invert__unit
478
479
def _multiply_classical(left, matrix.Matrix _right):
480
"""
481
Multiply the matrices left and right using the classical $O(n^3)$
482
algorithm.
483
484
EXAMPLES::
485
486
sage: M = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()
487
sage: m = M([9,7,6,4])
488
sage: n = M([7,1,3,0])
489
sage: m*n
490
[84 9]
491
[54 6]
492
sage: m._multiply_classical(n)
493
[84 9]
494
[54 6]
495
"""
496
cdef Matrix_integer_2x2 A, right
497
cdef mpz_t tmp
498
mpz_init(tmp)
499
500
right = _right
501
A = left._new_c()
502
503
mpz_mul(A.a, left.a, right.a)
504
mpz_mul(tmp, left.b, right.c)
505
mpz_add(A.a, A.a, tmp)
506
507
mpz_mul(A.b, left.a, right.b)
508
mpz_mul(tmp, left.b, right.d)
509
mpz_add(A.b, A.b, tmp)
510
511
mpz_mul(A.c, left.c, right.a)
512
mpz_mul(tmp, left.d, right.c)
513
mpz_add(A.c, A.c, tmp)
514
515
mpz_mul(A.d, left.c, right.b)
516
mpz_mul(tmp, left.d, right.d)
517
mpz_add(A.d, A.d, tmp)
518
519
mpz_clear(tmp)
520
return A
521
522
def _list(self):
523
"""
524
EXAMPLES::
525
526
sage: M = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()
527
sage: M([8,6,0,8])._list()
528
[8, 6, 0, 8]
529
"""
530
return [self.get_unsafe(0,0), self.get_unsafe(0,1),
531
self.get_unsafe(1,0), self.get_unsafe(1,1)]
532
533
########################################################################
534
# LEVEL 3 functionality (Optional)
535
# x * cdef _sub_
536
# x * __deepcopy__
537
# * Matrix windows -- only if you need strassen for that base
538
# * Other functions (list them here):
539
# x * determinant
540
# x * charpoly
541
########################################################################
542
543
cpdef ModuleElement _sub_(left, ModuleElement right):
544
"""
545
EXAMPLES::
546
547
sage: M = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()
548
sage: M([3,4,5,6]) - M([1,2,3,4])
549
[2 2]
550
[2 2]
551
"""
552
cdef Matrix_integer_2x2 A
553
A = left._new_c()
554
mpz_sub(A.a, left.a, (<Matrix_integer_2x2>right).a)
555
mpz_sub(A.b, left.b, (<Matrix_integer_2x2>right).b)
556
mpz_sub(A.c, left.c, (<Matrix_integer_2x2>right).c)
557
mpz_sub(A.d, left.d, (<Matrix_integer_2x2>right).d)
558
return A
559
560
def determinant(self):
561
"""
562
Return the determinant of self, which is just ad-bc.
563
564
EXAMPLES::
565
566
sage: M = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()
567
sage: M([9,0,8,7]).determinant()
568
63
569
"""
570
cdef Integer z
571
cdef mpz_t tmp
572
mpz_init(tmp)
573
z = Integer.__new__(Integer)
574
mpz_mul(z.value, self.a, self.d)
575
mpz_mul(tmp, self.b, self.c)
576
mpz_sub(z.value, z.value, tmp)
577
mpz_clear(tmp)
578
return z
579
580
def trace(self):
581
"""
582
Return the trace of self, which is just a+d.
583
584
EXAMPLES::
585
586
sage: M = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()
587
sage: M([9,0,8,7]).trace()
588
16
589
"""
590
cdef Integer z = PY_NEW(Integer)
591
mpz_add(z.value, self._entries[0], self._entries[3])
592
return z
593
594
def charpoly(self, var='x'):
595
"""
596
Return the charpoly of self as a polynomial in var. Since self
597
is 2x2, this is just var^2 - self.trace() * var +
598
self.determinant().
599
600
EXAMPLES::
601
602
sage: M = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()
603
sage: M([9,0,8,7]).charpoly()
604
x^2 - 16*x + 63
605
sage: M(range(4)).charpoly()
606
x^2 - 3*x - 2
607
sage: M(range(4)).charpoly('t')
608
t^2 - 3*t - 2
609
"""
610
t = polygen(ZZ,name=var)
611
return t*t - t*self.trace() + self.determinant()
612
613
def __deepcopy__(self, *args, **kwds):
614
"""
615
EXAMPLES::
616
617
sage: M = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()
618
sage: m = M([5,5,5,5])
619
sage: n = deepcopy(m)
620
sage: n == m
621
True
622
sage: n is m
623
False
624
sage: m[0,0] == n[0,0]
625
True
626
sage: m[0,0] is n[0,0]
627
False
628
"""
629
return self.__copy__()
630
631
632
#######################################################################
633
634