Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/matrix/matrix_cyclo_dense.pyx
4072 views
1
"""
2
Matrices over Cyclotomic Fields
3
4
The underlying matrix for a Matrix_cyclo_dense object is stored as
5
follows: given an n x m matrix over a cyclotomic field of degree d, we
6
store a d x (nm) matrix over QQ, each column of which corresponds to
7
an element of the original matrix. This can be retrieved via the
8
_rational_matrix method. Here is an example illustrating this:
9
10
EXAMPLES::
11
12
sage: F.<zeta> = CyclotomicField(5)
13
sage: M = Matrix(F, 2, 3, [zeta, 3, zeta**4+5, (zeta+1)**4, 0, 1])
14
sage: M
15
[ zeta 3 -zeta^3 - zeta^2 - zeta + 4]
16
[3*zeta^3 + 5*zeta^2 + 3*zeta 0 1]
17
18
sage: M._rational_matrix()
19
[ 0 3 4 0 0 1]
20
[ 1 0 -1 3 0 0]
21
[ 0 0 -1 5 0 0]
22
[ 0 0 -1 3 0 0]
23
24
25
AUTHORS:
26
* William Stein
27
* Craig Citro
28
"""
29
30
######################################################################
31
# Copyright (C) 2008 William Stein
32
# Distributed under the terms of the GNU General Public License (GPL)
33
# The full text of the GPL is available at:
34
# http://www.gnu.org/licenses/
35
######################################################################
36
37
include "../ext/interrupt.pxi"
38
# include "../ext/stdsage.pxi"
39
include "../ext/cdefs.pxi"
40
include "../ext/gmp.pxi"
41
include "../ext/random.pxi"
42
include "../libs/ntl/decl.pxi"
43
44
from sage.structure.element cimport ModuleElement, RingElement, Element, Vector
45
from sage.misc.randstate cimport randstate, current_randstate
46
47
from constructor import matrix
48
from matrix_space import MatrixSpace
49
from matrix cimport Matrix
50
import matrix_dense
51
from matrix_integer_dense import _lift_crt
52
from sage.structure.element cimport Matrix as baseMatrix
53
from misc import matrix_integer_dense_rational_reconstruction
54
from sage.ext.multi_modular import MAX_MODULUS
55
56
from sage.rings.rational_field import QQ
57
from sage.rings.integer_ring import ZZ
58
from sage.rings.arith import previous_prime, binomial
59
from sage.rings.all import RealNumber
60
from sage.rings.integer cimport Integer
61
from sage.rings.rational cimport Rational
62
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
63
from sage.rings.number_field.number_field import NumberField_quadratic
64
from sage.rings.number_field.number_field_element cimport NumberFieldElement
65
from sage.rings.number_field.number_field_element_quadratic cimport NumberFieldElement_quadratic
66
67
from sage.structure.proof.proof import get_flag as get_proof_flag
68
from sage.misc.misc import verbose
69
import math
70
71
# parameters for tuning
72
echelon_primes_increment = 15
73
echelon_verbose_level = 1
74
75
76
cdef class Matrix_cyclo_dense(matrix_dense.Matrix_dense):
77
########################################################################
78
# LEVEL 1 functionality
79
# x * __cinit__
80
# x * __dealloc__ (not needed)
81
# x * __init__
82
# x * set_unsafe
83
# x * get_unsafe
84
# x * _pickle
85
# x * _unpickle
86
########################################################################
87
88
def __cinit__(self, parent, entries, coerce, copy):
89
"""
90
Create a new dense cyclotomic matrix.
91
92
INPUT:
93
parent -- a matrix space over a cyclotomic field
94
entries -- a list of entries or scalar
95
coerce -- bool; if true entries are coerced to base ring
96
copy -- bool; ignored due to underlying data structure
97
98
EXAMPLES::
99
100
sage: from sage.matrix.matrix_cyclo_dense import Matrix_cyclo_dense
101
sage: A = Matrix_cyclo_dense.__new__(Matrix_cyclo_dense, MatrixSpace(CyclotomicField(3),2), [0,1,2,3], True, True)
102
sage: type(A)
103
<type 'sage.matrix.matrix_cyclo_dense.Matrix_cyclo_dense'>
104
105
Note that the entries of A haven't even been set yet above; that doesn't
106
happen until init is called::
107
108
sage: A[0,0]
109
Traceback (most recent call last):
110
...
111
ValueError: matrix entries not yet initialized
112
"""
113
Matrix.__init__(self, parent)
114
self._degree = self._base_ring.degree()
115
self._n = int(self._base_ring._n())
116
117
# This is not necessary, since we do not (yet) explicitly allocate
118
# any memory.
119
#def __dealloc__(self):
120
# pass
121
122
def __init__(self, parent, entries=None, copy=True, coerce=True):
123
"""
124
Initialize a newly created cyclotomic matrix.
125
126
INPUT:
127
128
- ``parent`` -- a matrix space over a cyclotomic field
129
130
- ``entries`` -- a list of entries or scalar
131
132
- ``coerce`` -- boolean; if true entries are coerced to base
133
ring
134
135
- ``copy`` -- boolean; ignored due to underlying data
136
structure
137
138
EXAMPLES:
139
140
This function is called implicitly when you create new
141
cyclotomic dense matrices::
142
143
sage: W.<a> = CyclotomicField(100)
144
sage: A = matrix(2, 3, [1, 1/a, 1-a,a, -2/3*a, a^19])
145
sage: A
146
[ 1 -a^39 + a^29 - a^19 + a^9 -a + 1]
147
[ a -2/3*a a^19]
148
sage: TestSuite(A).run()
149
150
TESTS::
151
152
sage: matrix(W, 2, 1, a)
153
Traceback (most recent call last):
154
...
155
TypeError: nonzero scalar matrix must be square
156
157
We call __init__ explicitly below::
158
159
sage: from sage.matrix.matrix_cyclo_dense import Matrix_cyclo_dense
160
sage: A = Matrix_cyclo_dense.__new__(Matrix_cyclo_dense, MatrixSpace(CyclotomicField(3),2), [0,1,2,3], True, True)
161
sage: A.__init__(MatrixSpace(CyclotomicField(3),2), [0,1,2,3], True, True)
162
sage: A
163
[0 1]
164
[2 3]
165
166
"""
167
cdef int i
168
z = None
169
if (entries is None) or (entries == 0):
170
pass
171
elif isinstance(entries, list):
172
# This code could be made much faster using Cython, etc.
173
if coerce:
174
K = parent.base_ring()
175
entries = [K(a) for a in entries]
176
entries = sum([a.list() for a in entries], [])
177
else:
178
K = self._base_ring
179
z = K(entries)
180
entries = 0
181
182
self._n = int(self._base_ring._n())
183
self._matrix = Matrix_rational_dense(MatrixSpace(QQ, self._nrows*self._ncols, self._degree),
184
entries, copy=False, coerce=False).transpose()
185
# This could also be made much faster.
186
if z is not None:
187
if self._nrows != self._ncols:
188
raise TypeError, "nonzero scalar matrix must be square"
189
for i in range(self._nrows):
190
self.set_unsafe(i,i,z)
191
192
cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, value):
193
"""
194
Set the ij-th entry of self.
195
196
WARNING: This function does no bounds checking whatsoever, as
197
the name suggests. It also assumes certain facts about the
198
internal representation of cyclotomic fields. This is intended
199
for internal use only.
200
201
EXAMPLES::
202
203
sage: K.<z> = CyclotomicField(11) ; M = Matrix(K,2,range(4))
204
sage: M[0,1] = z ; M
205
[0 z]
206
[2 3]
207
208
sage: K.<z> = CyclotomicField(3) ; M = Matrix(K,2,range(4))
209
sage: M[1,1] = z+1 ; M
210
[ 0 1]
211
[ 2 z + 1]
212
213
TESTS:
214
215
Since separate code exists for each quadratic field, we need
216
doctests for each.::
217
218
sage: K.<z> = CyclotomicField(4) ; M = Matrix(K,2,range(4))
219
sage: M[1,1] = z+1 ; M
220
[ 0 1]
221
[ 2 z + 1]
222
sage: K.<z> = CyclotomicField(6) ; M = Matrix(K,2,range(4))
223
sage: M[1,1] = z+1 ; M
224
[ 0 1]
225
[ 2 z + 1]
226
"""
227
# NEW FAST VERSION -- makes assumptions about how the
228
# cyclotomic field is implemented.
229
cdef Py_ssize_t k, c
230
cdef NumberFieldElement v
231
cdef mpz_t numer, denom
232
233
# The i,j entry is the (i * self._ncols + j)'th column.
234
c = i * self._ncols + j
235
236
if PY_TYPE_CHECK_EXACT(value, NumberFieldElement_quadratic):
237
# Must be coded differently, since elements of
238
# quadratic number fields are stored differently.
239
if self._n == 4:
240
mpz_set(mpq_numref(self._matrix._matrix[0][c]),
241
(<NumberFieldElement_quadratic>value).a)
242
mpz_set(mpq_denref(self._matrix._matrix[0][c]),
243
(<NumberFieldElement_quadratic>value).denom)
244
mpq_canonicalize(self._matrix._matrix[0][c])
245
246
mpz_set(mpq_numref(self._matrix._matrix[1][c]),
247
(<NumberFieldElement_quadratic>value).b)
248
mpz_set(mpq_denref(self._matrix._matrix[1][c]),
249
(<NumberFieldElement_quadratic>value).denom)
250
mpq_canonicalize(self._matrix._matrix[1][c])
251
elif self._n == 3:
252
mpz_set(mpq_numref(self._matrix._matrix[0][c]),
253
(<NumberFieldElement_quadratic>value).a)
254
mpz_add(mpq_numref(self._matrix._matrix[0][c]),
255
mpq_numref(self._matrix._matrix[0][c]),
256
(<NumberFieldElement_quadratic>value).b)
257
mpz_set(mpq_denref(self._matrix._matrix[0][c]),
258
(<NumberFieldElement_quadratic>value).denom)
259
mpq_canonicalize(self._matrix._matrix[0][c])
260
261
mpz_set(mpq_numref(self._matrix._matrix[1][c]),
262
(<NumberFieldElement_quadratic>value).b)
263
mpz_mul_si(mpq_numref(self._matrix._matrix[1][c]),
264
mpq_numref(self._matrix._matrix[1][c]), 2)
265
mpz_set(mpq_denref(self._matrix._matrix[1][c]),
266
(<NumberFieldElement_quadratic>value).denom)
267
mpq_canonicalize(self._matrix._matrix[1][c])
268
else: # self._n is 6
269
mpz_set(mpq_numref(self._matrix._matrix[0][c]),
270
(<NumberFieldElement_quadratic>value).a)
271
mpz_sub(mpq_numref(self._matrix._matrix[0][c]),
272
mpq_numref(self._matrix._matrix[0][c]),
273
(<NumberFieldElement_quadratic>value).b)
274
mpz_set(mpq_denref(self._matrix._matrix[0][c]),
275
(<NumberFieldElement_quadratic>value).denom)
276
mpq_canonicalize(self._matrix._matrix[0][c])
277
278
mpz_set(mpq_numref(self._matrix._matrix[1][c]),
279
(<NumberFieldElement_quadratic>value).b)
280
mpz_mul_si(mpq_numref(self._matrix._matrix[1][c]),
281
mpq_numref(self._matrix._matrix[1][c]), 2)
282
mpz_set(mpq_denref(self._matrix._matrix[1][c]),
283
(<NumberFieldElement_quadratic>value).denom)
284
mpq_canonicalize(self._matrix._matrix[1][c])
285
return
286
287
v = value
288
289
mpz_init(numer)
290
mpz_init(denom)
291
292
v._ntl_denom_as_mpz(&denom)
293
for k from 0 <= k < self._degree:
294
v._ntl_coeff_as_mpz(&numer, k)
295
mpz_set(mpq_numref(self._matrix._matrix[k][c]), numer)
296
mpz_set(mpq_denref(self._matrix._matrix[k][c]), denom)
297
mpq_canonicalize(self._matrix._matrix[k][c])
298
299
mpz_clear(numer)
300
mpz_clear(denom)
301
302
cdef get_unsafe(self, Py_ssize_t i, Py_ssize_t j):
303
"""
304
Get the ij-th of self.
305
306
WARNING: As the name suggests, expect segfaults if i,j are out
307
of bounds!! This is for internal use only.
308
309
EXAMPLES::
310
311
sage: W.<a> = CyclotomicField(5)
312
sage: A = matrix(2, 3, [9939208341, 1/a, 1-a,a, -2/3*a, a^19])
313
314
This implicitly calls get_unsafe::
315
316
sage: A[0,0]
317
9939208341
318
319
TESTS:
320
321
Since separate code exists for each quadratic field, we need
322
doctests for each.::
323
324
sage: K.<z> = CyclotomicField(3) ; M = Matrix(K,2,range(4))
325
sage: M[1,1] = z+1 ; M[1,1]
326
z + 1
327
sage: (M[1,1] - M[0,1])**3
328
1
329
sage: K.<z> = CyclotomicField(4) ; M = Matrix(K,2,range(4))
330
sage: M[1,1] = z+1 ; M[1,1]
331
z + 1
332
sage: (M[1,1] - M[0,1])**4
333
1
334
sage: K.<z> = CyclotomicField(6) ; M = Matrix(K,2,range(4))
335
sage: M[1,1] = z+1 ; M[1,1]
336
z + 1
337
sage: (M[1,1] - M[0,1])**6
338
1
339
"""
340
cdef Py_ssize_t k, c
341
cdef NumberFieldElement x
342
cdef NumberFieldElement_quadratic xq
343
cdef mpz_t denom, quo, tmp
344
cdef ZZ_c coeff
345
346
if self._matrix is None:
347
raise ValueError, "matrix entries not yet initialized"
348
349
c = i * self._ncols + j
350
mpz_init(tmp)
351
352
if self._degree == 2:
353
xq = self._base_ring(0)
354
if self._n == 4:
355
mpz_mul(xq.a, mpq_numref(self._matrix._matrix[0][c]),
356
mpq_denref(self._matrix._matrix[1][c]))
357
mpz_mul(xq.b, mpq_numref(self._matrix._matrix[1][c]),
358
mpq_denref(self._matrix._matrix[0][c]))
359
mpz_mul(xq.denom, mpq_denref(self._matrix._matrix[0][c]),
360
mpq_denref(self._matrix._matrix[1][c]))
361
else: # n is 3 or 6
362
mpz_mul(xq.a, mpq_numref(self._matrix._matrix[0][c]),
363
mpq_denref(self._matrix._matrix[1][c]))
364
mpz_mul_si(xq.a, xq.a, 2)
365
mpz_mul(tmp, mpq_denref(self._matrix._matrix[0][c]),
366
mpq_numref(self._matrix._matrix[1][c]))
367
if self._n == 3:
368
mpz_sub(xq.a, xq.a, tmp)
369
else: # n == 6
370
mpz_add(xq.a, xq.a, tmp)
371
372
mpz_mul(xq.b, mpq_denref(self._matrix._matrix[0][c]),
373
mpq_numref(self._matrix._matrix[1][c]))
374
375
mpz_mul(xq.denom, mpq_denref(self._matrix._matrix[0][c]),
376
mpq_denref(self._matrix._matrix[1][c]))
377
mpz_mul_si(xq.denom, xq.denom, 2)
378
379
xq._reduce_c_()
380
mpz_clear(tmp)
381
return xq
382
383
x = self._base_ring(0)
384
ZZ_construct(&coeff)
385
mpz_init_set_ui(denom, 1)
386
387
# Get the least common multiple of the denominators in
388
# this column.
389
for k from 0 <= k < self._degree:
390
mpz_lcm(denom, denom, mpq_denref(self._matrix._matrix[k][c]))
391
392
for k from 0 <= k < self._degree:
393
# set each entry of x to a*denom/b where a/b is the
394
# k,c entry of _matrix.
395
mpz_mul(tmp, mpq_numref(self._matrix._matrix[k][c]), denom)
396
mpz_divexact(tmp, tmp, mpq_denref(self._matrix._matrix[k][c]))
397
# Now set k-th entry of x's numerator to tmp
398
mpz_to_ZZ(&coeff, &tmp)
399
ZZX_SetCoeff(x.__numerator, k, coeff)
400
401
# Set the denominator of x to denom.
402
mpz_to_ZZ(&x.__denominator, &denom)
403
mpz_clear(denom)
404
mpz_clear(tmp)
405
ZZ_destruct(&coeff)
406
407
return x
408
409
def _pickle(self):
410
"""
411
Used for pickling matrices. This function returns the
412
underlying data and pickle version.
413
414
OUTPUT:
415
data -- output of pickle
416
version -- int
417
418
EXAMPLES::
419
420
sage: K.<z> = CyclotomicField(3)
421
sage: w = matrix(K, 3, 3, [0, -z, -2, -2*z + 2, 2*z, z, z, 1-z, 2+3*z])
422
sage: w._pickle()
423
(('0 0 -2 2 0 0 0 1 2 0 -1 0 -2 2 1 1 -1 3', 0), 0)
424
"""
425
data = self._matrix._pickle()
426
return data, 0
427
428
def _unpickle(self, data, int version):
429
"""
430
Called when unpickling matrices.
431
432
INPUT:
433
data -- a string
434
version -- int
435
436
This modifies self.
437
438
EXAMPLES::
439
440
sage: K.<z> = CyclotomicField(3)
441
sage: w = matrix(K, 3, 3, [0, -z, -2, -2*z + 2, 2*z, z, z, 1-z, 2+3*z])
442
sage: data, version = w._pickle()
443
sage: k = w.parent()(0)
444
sage: k._unpickle(data, version)
445
sage: k == w
446
True
447
"""
448
#self.check_mutability()
449
if version == 0:
450
self._matrix = Matrix_rational_dense(MatrixSpace(QQ, self._degree, self._nrows*self._ncols), None, False, False)
451
self._matrix._unpickle(*data) # data is (data, matrix_QQ_version)
452
else:
453
raise RuntimeError, "unknown matrix version (=%s)"%version
454
455
########################################################################
456
# LEVEL 2 functionality
457
# x * cdef _add_
458
# x * cdef _sub_
459
# * cdef _mul_
460
# x * cdef _lmul_ -- scalar multiplication
461
# x * cdef _cmp_c_impl
462
# x * __neg__
463
# * __invert__
464
# x * __copy__
465
# * _multiply_classical
466
# * _list -- list of underlying elements (need not be a copy)
467
# * _dict -- sparse dictionary of underlying elements (need not be a copy)
468
########################################################################
469
470
cpdef ModuleElement _add_(self, ModuleElement right):
471
"""
472
Return the sum of two dense cyclotomic matrices.
473
474
INPUT:
475
self, right -- dense cyclotomic matrices with the same
476
parents
477
OUTPUT:
478
a dense cyclotomic matrix
479
480
EXAMPLES::
481
482
sage: W.<z> = CyclotomicField(5)
483
sage: A = matrix(2, 3, [1,z,z^2,z^3,z^4,2/3*z]); B = matrix(2, 3, [-1,2*z,3*z^2,5*z+1,z^4,1/3*z])
484
sage: A + B
485
[ 0 3*z 4*z^2]
486
[ z^3 + 5*z + 1 -2*z^3 - 2*z^2 - 2*z - 2 z]
487
488
Verify directly that the above output is correct::
489
490
sage: (A+B).list() == [A.list()[i]+B.list()[i] for i in range(6)]
491
True
492
"""
493
cdef Matrix_cyclo_dense A = Matrix_cyclo_dense.__new__(Matrix_cyclo_dense, self.parent(), None, None, None)
494
# Fix the redundancy here.
495
A._matrix = self._matrix + (<Matrix_cyclo_dense>right)._matrix
496
return A
497
498
cpdef ModuleElement _sub_(self, ModuleElement right):
499
"""
500
Return the difference of two dense cyclotomic matrices.
501
502
INPUT:
503
self, right -- dense cyclotomic matrices with the same
504
parent
505
OUTPUT:
506
a dense cyclotomic matrix
507
508
EXAMPLES::
509
510
sage: W.<z> = CyclotomicField(5)
511
sage: A = matrix(2, 3, [1,z,z^2,z^3,z^4,2/3*z]); B = matrix(2, 3, [-1,2*z,3*z^2,5*z+1,z^4,1/3*z])
512
sage: A - B
513
[ 2 -z -2*z^2]
514
[z^3 - 5*z - 1 0 1/3*z]
515
516
Verify directly that the above output is correct::
517
518
sage: (A-B).list() == [A.list()[i]-B.list()[i] for i in range(6)]
519
True
520
"""
521
cdef Matrix_cyclo_dense A = Matrix_cyclo_dense.__new__(Matrix_cyclo_dense, self.parent(), None, None, None)
522
A._matrix = self._matrix - (<Matrix_cyclo_dense>right)._matrix
523
return A
524
525
cpdef ModuleElement _lmul_(self, RingElement right):
526
"""
527
Multiply a dense cyclotomic matrix by a scalar.
528
529
INPUT:
530
self -- dense cyclotomic matrix
531
right --- scalar in the base cyclotomic field
532
533
EXAMPLES::
534
535
sage: W.<z> = CyclotomicField(5)
536
sage: A = matrix(2, 3, [1,z,z^2,z^3,z^4,2/3*z])
537
sage: (1 + z/3)*A
538
[ 1/3*z + 1 1/3*z^2 + z 1/3*z^3 + z^2]
539
[2/3*z^3 - 1/3*z^2 - 1/3*z - 1/3 -z^3 - z^2 - z - 2/3 2/9*z^2 + 2/3*z]
540
541
Verify directly that the above output is correct::
542
543
sage: ((1+z/3)*A).list() == [(1+z/3)*x for x in A.list()]
544
True
545
"""
546
if right == 1:
547
return self
548
elif right == 0:
549
return self.parent()(0)
550
551
# Create a new matrix object but with the _matrix attribute not initialized:
552
cdef Matrix_cyclo_dense A = Matrix_cyclo_dense.__new__(Matrix_cyclo_dense,
553
self.parent(), None, None, None)
554
555
if right.polynomial().degree() == 0:
556
# multiplication by a rational number
557
A._matrix = self._matrix._lmul_(right)
558
else:
559
# Multiply by nontrivial element of the cyclotomic number field
560
# We do this by finding the matrix of this element, then left
561
# multiplying by it. We have to tweak the matrix some to
562
# get the right basis, etc.
563
T = right.matrix().transpose()
564
A._matrix = T * self._matrix
565
return A
566
567
cdef baseMatrix _matrix_times_matrix_(self, baseMatrix right):
568
"""
569
Return the product of two cyclotomic dense matrices.
570
571
INPUT:
572
self, right -- cyclotomic dense matrices with compatible
573
parents (same base ring, and compatible
574
dimensions for matrix multiplication).
575
576
OUTPUT:
577
cyclotomic dense matrix
578
579
ALGORITHM:
580
Use a multimodular algorithm that involves multiplying the
581
two matrices modulo split primes.
582
583
EXAMPLES::
584
585
sage: W.<z> = CyclotomicField(5)
586
sage: A = matrix(3, 3, [1,z,z^2,z^3,z^4,2/3*z,-3*z,z,2+z]); B = matrix(3, 3, [-1,2*z,3*z^2,5*z+1,z^4,1/3*z,2-z,3-z,5-z])
587
sage: A*B
588
[ -z^3 + 7*z^2 + z - 1 -z^3 + 3*z^2 + 2*z + 1 -z^3 + 25/3*z^2]
589
[-2*z^3 - 5/3*z^2 + 1/3*z + 4 -z^3 - 8/3*z^2 - 2 -2/3*z^2 + 10/3*z + 10/3]
590
[ 4*z^2 + 4*z + 4 -7*z^2 + z + 7 -9*z^3 - 2/3*z^2 + 3*z + 10]
591
592
Verify that the answer above is consistent with what the
593
generic sparse matrix multiply gives (which is a different
594
implementation).::
595
596
sage: A*B == A.sparse_matrix()*B.sparse_matrix()
597
True
598
599
sage: N1 = Matrix(CyclotomicField(6), 1, [1])
600
sage: cf6 = CyclotomicField(6) ; z6 = cf6.0
601
sage: N2 = Matrix(CyclotomicField(6), 1, 5, [0,1,z6,-z6,-z6+1])
602
sage: N1*N2
603
[ 0 1 zeta6 -zeta6 -zeta6 + 1]
604
sage: N1 = Matrix(CyclotomicField(6), 1, [-1])
605
sage: N1*N2
606
[ 0 -1 -zeta6 zeta6 zeta6 - 1]
607
608
Verify that a degenerate case bug reported at trac 5974 is fixed.
609
610
sage: K.<zeta6>=CyclotomicField(6); matrix(K,1,2) * matrix(K,2,[0, 1, 0, -2*zeta6, 0, 0, 1, -2*zeta6 + 1])
611
[0 0 0 0]
612
613
TESTS:
614
615
This is from trac #8666::
616
617
sage: K.<zeta4> = CyclotomicField(4)
618
sage: m = matrix(K, [125])
619
sage: n = matrix(K, [186])
620
sage: m*n
621
[23250]
622
sage: (-m)*n
623
[-23250]
624
"""
625
A, denom_self = self._matrix._clear_denom()
626
B, denom_right = (<Matrix_cyclo_dense>right)._matrix._clear_denom()
627
628
# conservative but correct estimate: 2 is there to account for the
629
# sign of the entries
630
bound = 1 + 2 * A.height() * B.height() * self._ncols
631
632
n = self._base_ring._n()
633
p = previous_prime(MAX_MODULUS)
634
prod = 1
635
v = []
636
while prod <= bound:
637
while (n >= 2 and p % n != 1) or denom_self % p == 0 or denom_right % p == 0:
638
if p == 2:
639
raise RuntimeError, "we ran out of primes in matrix multiplication."
640
p = previous_prime(p)
641
prod *= p
642
Amodp, _ = self._reductions(p)
643
Bmodp, _ = right._reductions(p)
644
_, S = self._reduction_matrix(p)
645
X = Amodp[0]._matrix_from_rows_of_matrices([Amodp[i] * Bmodp[i] for i in range(len(Amodp))])
646
v.append(S*X)
647
p = previous_prime(p)
648
M = matrix(ZZ, self._base_ring.degree(), self._nrows*right.ncols())
649
_lift_crt(M, v)
650
d = denom_self * denom_right
651
if d == 1:
652
M = M.change_ring(QQ)
653
else:
654
M = (1/d)*M
655
cdef Matrix_cyclo_dense C = Matrix_cyclo_dense.__new__(Matrix_cyclo_dense,
656
MatrixSpace(self._base_ring, self._nrows, right.ncols()),
657
None, None, None)
658
C._matrix = M
659
return C
660
661
def __richcmp__(Matrix self, right, int op):
662
"""
663
Compare a matrix with something else. This immediately calls
664
a base class _richcmp.
665
666
EXAMPLES::
667
668
sage: W.<z> = CyclotomicField(5)
669
sage: A = matrix(W, 2, 2, [1,z,-z,1+z/2])
670
671
These implicitly call richcmp::
672
673
sage: A == 5
674
False
675
sage: A < 100
676
True
677
"""
678
return self._richcmp(right, op)
679
680
cdef long _hash(self) except -1:
681
"""
682
Return hash of this matrix.
683
684
EXAMPLES:
685
686
This is called implicitly by the hash function.::
687
688
sage: W.<z> = CyclotomicField(5)
689
sage: A = matrix(W, 2, 2, [1,z,-z,1+z/2])
690
691
The matrix must be immutable.::
692
693
sage: hash(A)
694
Traceback (most recent call last):
695
...
696
TypeError: mutable matrices are unhashable
697
sage: A.set_immutable()
698
699
Yes, this works::
700
701
sage: hash(A)
702
-25
703
"""
704
return self._matrix._hash()
705
706
def __hash__(self):
707
"""
708
Return hash of an immutable matrix. Raise a TypeError if input
709
matrix is mutable.
710
711
EXAMPLES::
712
713
sage: W.<z> = CyclotomicField(5)
714
sage: A = matrix(W, 2, 2, [1,2/3*z+z^2,-z,1+z/2])
715
sage: hash(A)
716
Traceback (most recent call last):
717
...
718
TypeError: mutable matrices are unhashable
719
sage: A.set_immutable()
720
sage: A.__hash__()
721
-18
722
"""
723
if self._mutability._is_immutable:
724
return self._hash()
725
else:
726
raise TypeError, "mutable matrices are unhashable"
727
728
cdef int _cmp_c_impl(self, Element right) except -2:
729
"""
730
Implements comparison of two cyclotomic matrices with
731
identical parents.
732
733
INPUT:
734
self, right -- matrices with same parent
735
OUTPUT:
736
int; either -1, 0, or 1
737
738
EXAMPLES:
739
740
This function is called implicitly when comparisons with matrices
741
are done or the cmp function is used.::
742
743
sage: W.<z> = CyclotomicField(5)
744
sage: A = matrix(W, 2, 2, [1,2/3*z+z^2,-z,1+z/2])
745
sage: cmp(A,A)
746
0
747
sage: cmp(A,2*A)
748
-1
749
sage: cmp(2*A,A)
750
1
751
"""
752
return self._matrix._cmp_c_impl((<Matrix_cyclo_dense>right)._matrix)
753
754
def __copy__(self):
755
"""
756
Make a copy of this matrix.
757
758
EXAMPLES:
759
We create a cyclotomic matrix.::
760
761
sage: W.<z> = CyclotomicField(5)
762
sage: A = matrix(W, 2, 2, [1,2/3*z+z^2,-z,1+z/2])
763
764
We make a copy of A.::
765
sage: C = A.__copy__()
766
767
We make another reference to A.::
768
769
sage: B = A
770
771
Changing this reference changes A itself::
772
773
sage: B[0,0] = 10
774
sage: A[0,0]
775
10
776
777
Changing the copy does not change A.::
778
779
sage: C[0,0] = 20
780
sage: C[0,0]
781
20
782
sage: A[0,0]
783
10
784
"""
785
cdef Matrix_cyclo_dense A = Matrix_cyclo_dense.__new__(Matrix_cyclo_dense, self.parent(), None, None, None)
786
A._matrix = self._matrix.__copy__()
787
return A
788
789
def __neg__(self):
790
"""
791
Return the negative of this matrix.
792
793
OUTPUT:
794
matrix
795
796
EXAMPLES::
797
798
sage: W.<z> = CyclotomicField(5)
799
sage: A = matrix(W, 2, 2, [1,2/3*z+z^2,-z,1+z/2])
800
sage: -A
801
[ -1 -z^2 - 2/3*z]
802
[ z -1/2*z - 1]
803
sage: A.__neg__()
804
[ -1 -z^2 - 2/3*z]
805
[ z -1/2*z - 1]
806
"""
807
cdef Matrix_cyclo_dense A = Matrix_cyclo_dense.__new__(Matrix_cyclo_dense, self.parent(), None, None, None)
808
A._matrix = self._matrix.__neg__()
809
return A
810
811
812
########################################################################
813
# LEVEL 3 functionality (Optional)
814
# * __deepcopy__
815
# * __invert__
816
# * Matrix windows -- only if you need strassen for that base
817
# * Other functions (list them here):
818
# * Specialized echelon form
819
########################################################################
820
def set_immutable(self):
821
"""
822
Change this matrix so that it is immutable.
823
824
EXAMPLES::
825
826
sage: W.<z> = CyclotomicField(5)
827
sage: A = matrix(W, 2, 2, [1,2/3*z+z^2,-z,1+z/2])
828
sage: A[0,0] = 10
829
sage: A.set_immutable()
830
sage: A[0,0] = 20
831
Traceback (most recent call last):
832
...
833
ValueError: matrix is immutable; please change a copy instead (i.e., use copy(M) to change a copy of M).
834
835
Note that there is no function to set a matrix to be mutable
836
again, since such a function would violate the whole point.
837
Instead make a copy, which is always mutable by default.::
838
839
sage: A.set_mutable()
840
Traceback (most recent call last):
841
...
842
AttributeError: 'sage.matrix.matrix_cyclo_dense.Matrix_cyclo_dense' object has no attribute 'set_mutable'
843
sage: B = A.__copy__()
844
sage: B[0,0] = 20
845
sage: B[0,0]
846
20
847
"""
848
self._matrix.set_immutable()
849
matrix_dense.Matrix_dense.set_immutable(self)
850
851
def _rational_matrix(self):
852
"""
853
Return the underlying rational matrix corresponding to self.
854
855
EXAMPLES::
856
857
sage: Matrix(CyclotomicField(7),4,4,range(16))._rational_matrix()
858
[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]
859
[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
860
[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
861
[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
862
[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
863
[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
864
sage: Matrix(CyclotomicField(7),4,4,[CyclotomicField(7).gen(0)**i for i in range(16)])._rational_matrix()
865
[ 1 0 0 0 0 0 -1 1 0 0 0 0 0 -1 1 0]
866
[ 0 1 0 0 0 0 -1 0 1 0 0 0 0 -1 0 1]
867
[ 0 0 1 0 0 0 -1 0 0 1 0 0 0 -1 0 0]
868
[ 0 0 0 1 0 0 -1 0 0 0 1 0 0 -1 0 0]
869
[ 0 0 0 0 1 0 -1 0 0 0 0 1 0 -1 0 0]
870
[ 0 0 0 0 0 1 -1 0 0 0 0 0 1 -1 0 0]
871
"""
872
return self._matrix
873
874
def denominator(self):
875
"""
876
Return the denominator of the entries of this matrix.
877
878
OUTPUT:
879
integer -- the smallest integer d so that d * self has
880
entries in the ring of integers
881
882
EXAMPLES::
883
884
sage: W.<z> = CyclotomicField(5)
885
sage: A = matrix(W, 2, 2, [-2/7,2/3*z+z^2,-z,1+z/19]); A
886
[ -2/7 z^2 + 2/3*z]
887
[ -z 1/19*z + 1]
888
sage: d = A.denominator(); d
889
399
890
"""
891
return self._matrix.denominator()
892
893
def coefficient_bound(self):
894
r"""
895
Return an upper bound for the (complex) absolute values of all
896
entries of self with respect to all embeddings.
897
898
Use \code{self.height()} for a sharper bound.
899
900
This is computed using just the Cauchy-Schwarz inequality, i.e.,
901
we use the fact that
902
$$ \left| \sum_i a_i\zeta^i \right| \leq \sum_i |a_i|, $$
903
as $|\zeta| = 1$.
904
905
EXAMPLES::
906
907
sage: W.<z> = CyclotomicField(5)
908
sage: A = matrix(W, 2, 2, [1+z, 0, 9*z+7, -3 + 4*z]); A
909
[ z + 1 0]
910
[9*z + 7 4*z - 3]
911
sage: A.coefficient_bound()
912
16
913
914
The above bound is just $9 + 7$, coming from the lower left entry.
915
A better bound would be the following::
916
917
sage: (A[1,0]).abs()
918
12.997543663...
919
"""
920
cdef Py_ssize_t i, j
921
922
bound = 0
923
for i from 0 <= i < self._matrix._ncols:
924
925
n = 0
926
for j from 0 <= j < self._matrix._nrows:
927
n += self._matrix[j, i].abs()
928
if bound < n:
929
bound = n
930
931
return bound
932
933
934
def height(self):
935
r"""
936
Return the height of self.
937
938
If we let $a_{ij}$ be the $i,j$ entry of self, then we define
939
the height of self to be
940
$$
941
\max_v \max_{i,j} |a_{ij}|_v,
942
$$
943
where $v$ runs over all complex embeddings of \code{self.base_ring()}.
944
945
EXAMPLES::
946
947
sage: W.<z> = CyclotomicField(5)
948
sage: A = matrix(W, 2, 2, [1+z, 0, 9*z+7, -3 + 4*z]); A
949
[ z + 1 0]
950
[9*z + 7 4*z - 3]
951
sage: A.height()
952
12.997543663...
953
sage: (A[1,0]).abs()
954
12.997543663...
955
"""
956
cdef Py_ssize_t i, j
957
958
emb = self._base_ring.complex_embeddings()
959
960
ht = 0
961
for i from 0 <= i < self._nrows:
962
for j from 0 <= j < self._ncols:
963
t = max([ x.norm().sqrt() for x in [ f(self.get_unsafe(i,j)) for f in emb ] ])
964
if t > ht:
965
ht = t
966
967
return ht
968
969
cdef _randomize_rational_column_unsafe(Matrix_cyclo_dense self,
970
Py_ssize_t col, mpz_t nump1, mpz_t denp1, distribution=None):
971
"""
972
Randomizes all entries in column ``col``. This is a helper method
973
used in the implementation of dense matrices over cyclotomic fields.
974
975
INPUT:
976
977
- ``col`` - Integer, indicating the column; must be coercable to
978
``int``, and this must lie between 0 (inclusive) and
979
``self._ncols`` (exclusive), since no bounds-checking is performed
980
- ``nump1`` - Integer, numerator bound plus one
981
- ``denp1`` - Integer, denominator bound plus one
982
- ``distribution`` - ``None`` or '1/n' (default: ``None``); if '1/n'
983
then ``num_bound``, ``den_bound`` are ignored and numbers are chosen
984
using the GMP function ``mpq_randomize_entry_recip_uniform``
985
- ``nonzero`` - Bool (default: ``False``); whether the new entries
986
are forced to be non-zero
987
988
OUTPUT:
989
990
- None, the matrix is modified in-space
991
992
WARNING:
993
994
This method is quite unsafe. It's called from the method
995
``randomize``, but probably shouldn't be called from another method
996
without first carefully reading the source code!
997
998
TESTS:
999
1000
The following doctests are all indirect::
1001
1002
sage: MS = MatrixSpace(CyclotomicField(10), 4, 4)
1003
sage: A = MS.random_element(); A
1004
[ -2*zeta10^3 + 2*zeta10^2 - zeta10 zeta10^3 + 2*zeta10^2 - zeta10 + 1 0 -2*zeta10^3 + zeta10^2 - 2*zeta10 + 2]
1005
[ 0 -zeta10^3 + 2*zeta10^2 - zeta10 -zeta10^3 + 1 zeta10^3 + zeta10]
1006
[ 1/2*zeta10^2 -2*zeta10^2 + 2 -1/2*zeta10^3 + 1/2*zeta10^2 + 2 2*zeta10^3 - zeta10^2 - 2]
1007
[ 1 zeta10^2 + 2 2*zeta10^2 2*zeta10 - 2]
1008
sage: B = MS.random_element(density=0.5)
1009
sage: B._rational_matrix()
1010
[ 0 0 0 0 1 0 0 2 0 2 0 0 0 0 0 0]
1011
[ 0 0 0 0 0 0 0 0 -1 -2 0 0 0 0 0 2]
1012
[ 0 -1 0 0 -1 0 0 0 0 0 0 0 0 0 -2 -1]
1013
[ 0 0 0 0 0 0 0 2 -1/2 1/2 0 0 0 0 -1 0]
1014
sage: C = MS.random_element(density=0.5, num_bound=20, den_bound=20)
1015
sage: C._rational_matrix()
1016
[ 0 0 8/11 -10/3 -11/7 8 1 -3 0 0 1 0 0 0 0 0]
1017
[ 0 0 -11/17 -3/13 -5/6 17/3 -19/17 -4/5 0 0 9 0 0 0 0 0]
1018
[ 0 0 -11 -3/2 -5/12 8/11 0 -3/19 0 0 -5/6 0 0 0 0 0]
1019
[ 0 0 0 5/8 -5/11 -5/4 6/11 2/3 0 0 -16/11 0 0 0 0 0]
1020
"""
1021
cdef Py_ssize_t i
1022
cdef Matrix_rational_dense mat = self._matrix
1023
1024
sig_on()
1025
if distribution == "1/n":
1026
for i from 0 <= i < mat._nrows:
1027
mpq_randomize_entry_recip_uniform(mat._matrix[i][col])
1028
elif mpz_cmp_si(denp1, 2): # denom is > 1
1029
for i from 0 <= i < mat._nrows:
1030
mpq_randomize_entry(mat._matrix[i][col], nump1, denp1)
1031
else:
1032
for i from 0 <= i < mat._nrows:
1033
mpq_randomize_entry_as_int(mat._matrix[i][col], nump1)
1034
sig_off()
1035
1036
def randomize(self, density=1, num_bound=2, den_bound=2, \
1037
distribution=None, nonzero=False, *args, **kwds):
1038
r"""
1039
Randomize the entries of ``self``.
1040
1041
Choose rational numbers according to ``distribution``, whose
1042
numerators are bounded by ``num_bound`` and whose denominators are
1043
bounded by ``den_bound``.
1044
1045
EXAMPLES::
1046
1047
sage: A = Matrix(CyclotomicField(5),2,2,range(4)) ; A
1048
[0 1]
1049
[2 3]
1050
sage: A.randomize()
1051
sage: A # random output
1052
[ 1/2*zeta5^2 + zeta5 1/2]
1053
[ -zeta5^2 + 2*zeta5 -2*zeta5^3 + 2*zeta5^2 + 2]
1054
"""
1055
# Problem 1:
1056
# We cannot simply call the ``randomize`` code in ``matrix2.pyx`` on
1057
# the underlying matrix, since this is a d x (mn) matrix, where d is
1058
# the degree of the field extension, which leads to an overly dense
1059
# matrix.
1060
#
1061
# Problem 2:
1062
# We cannot simply copy the code from ``matrix2.pyx``, since the
1063
# ``random_element`` method for cyclotomic fields does not support
1064
# the arguments ``num_bound`` and ``den_bound``, which are support by
1065
# the rational field.
1066
#
1067
# Proposed solution:
1068
# Randomly select a proportion of ``density`` of the elements in the
1069
# matrix over the cyclotomic field, that is, this many columns in the
1070
# underlying rational matrix. Then, for each element in that column,
1071
# randomize it to a rational number, applying the arguments
1072
# ``num_bound`` and ``den_bound``.
1073
1074
density = float(density)
1075
if density <= 0:
1076
return
1077
if density > 1:
1078
density = 1
1079
1080
self.check_mutability()
1081
self.clear_cache()
1082
1083
cdef Py_ssize_t col, i, k, num
1084
cdef randstate rstate = current_randstate()
1085
cdef Integer B, C
1086
cdef bint col_is_zero
1087
1088
B = Integer(num_bound+1)
1089
C = Integer(den_bound+1)
1090
1091
if nonzero:
1092
if density >= 1:
1093
for col from 0 <= col < self._matrix._ncols:
1094
col_is_zero = True
1095
while col_is_zero:
1096
self._randomize_rational_column_unsafe(col, B.value, \
1097
C.value, distribution)
1098
# Check whether the new column is non-zero
1099
for i from 0 <= i < self._degree:
1100
if mpq_sgn(self._matrix._matrix[i][col]) != 0:
1101
col_is_zero = False
1102
break
1103
else:
1104
num = int(self._nrows * self._ncols * density)
1105
for k from 0 <= k < num:
1106
col = rstate.c_random() % self._matrix._ncols
1107
col_is_zero = True
1108
while col_is_zero:
1109
self._randomize_rational_column_unsafe(col, B.value, \
1110
C.value, distribution)
1111
# Check whether the new column is non-zero
1112
for i from 0 <= i < self._degree:
1113
if mpq_sgn(self._matrix._matrix[i][col]) != 0:
1114
col_is_zero = False
1115
break
1116
else:
1117
if density >= 1:
1118
for col from 0 <= col < self._matrix._ncols:
1119
self._randomize_rational_column_unsafe(col, B.value, \
1120
C.value, distribution)
1121
else:
1122
num = int(self._nrows * self._ncols * density)
1123
for k from 0 <= k < num:
1124
col = rstate.c_random() % self._matrix._ncols
1125
self._randomize_rational_column_unsafe(col, B.value, \
1126
C.value, distribution)
1127
1128
def _charpoly_bound(self):
1129
"""
1130
Determine a bound for the coefficients of the characteristic
1131
polynomial of self. We use the bound in Lemma 2.2 of:
1132
1133
Dumas, J-G. "Bounds on the coefficients of characteristic
1134
and minimal polynomials." J. Inequal. Pure Appl. Math. 8
1135
(2007), no. 2.
1136
1137
This bound only applies for self._nrows >= 4, so in all
1138
smaller cases, we just use a naive bound.
1139
1140
EXAMPLES::
1141
1142
sage: A = Matrix(CyclotomicField(7),3,3,range(9))
1143
sage: A._charpoly_bound()
1144
2048
1145
sage: A.charpoly()
1146
x^3 - 12*x^2 - 18*x
1147
1148
An example from the above paper, where the bound is sharp::
1149
1150
sage: B = Matrix(CyclotomicField(7), 5,5, [1,1,1,1,1,1,1,-1,-1,-1,1,-1,1,-1,-1,1,-1,-1,1,-1,1,-1,-1,-1,1])
1151
sage: B._charpoly_bound()
1152
80
1153
sage: B.charpoly()
1154
x^5 - 5*x^4 + 40*x^2 - 80*x + 48
1155
"""
1156
cdef Py_ssize_t i
1157
1158
# should we even bother with this check, or just say in
1159
# the docstring that we assume it's square?
1160
if self._nrows != self._ncols:
1161
raise ArithmeticError, "self must be a square matrix"
1162
1163
if self.is_zero():
1164
return 1
1165
1166
B = self.coefficient_bound()
1167
1168
# TODO: should charpoly just hardcode the return value for
1169
# self.nrows() < 4?
1170
1171
# this bound is only valid for n >= 4, use naive bounds
1172
# in other cases.
1173
if self._nrows <= 3:
1174
return max(1, 3*B, 6*B**2, 4*B**3)
1175
1176
# This is an approximation to 2^(5/6*log_2(5) - 2/3*log_2(6))
1177
alpha = RealNumber('1.15799718800731')
1178
# This is 2*e^(1-(2(7\gamma-4))/(13(3-2\gamma))), where \gamma
1179
# is Euler's constant.
1180
delta = RealNumber('5.418236')
1181
# This is an approximation to 1/2. :)
1182
half = RealNumber('0.5')
1183
1184
D = (((1+2*delta*self._nrows*(B**2)).sqrt()-1)/(delta*B**2)).ceil()
1185
1186
# TODO: we don't check anything about overflows anywhere here;
1187
# should we?
1188
1189
# i = 0 case
1190
M = ((self._nrows * B**2)**(self._nrows * half)).ceil()
1191
1192
for i from 1 <= i < D:
1193
val = binomial(self._nrows, i) * \
1194
(((self._nrows-i)*B**2)**((self._nrows-i)*half)).ceil()
1195
if val > M:
1196
M = val
1197
1198
return M
1199
1200
1201
def charpoly(self, var='x', algorithm="multimodular", proof=None):
1202
r"""
1203
Return the characteristic polynomial of self, as a polynomial
1204
over the base ring.
1205
1206
INPUT:
1207
algorithm -- 'multimodular' (default): reduce modulo
1208
primes, compute charpoly mod
1209
p, and lift (very fast)
1210
'pari': use pari (quite slow; comparable to
1211
Magma v2.14 though)
1212
'hessenberg': put matrix in Hessenberg form
1213
(double dog slow)
1214
proof -- bool (default: None) proof flag determined by
1215
global linalg proof.
1216
1217
OUTPUT:
1218
polynomial
1219
1220
EXAMPLES::
1221
1222
sage: K.<z> = CyclotomicField(5)
1223
sage: a = matrix(K, 3, [1,z,1+z^2, z/3,1,2,3,z^2,1-z])
1224
sage: f = a.charpoly(); f
1225
x^3 + (z - 3)*x^2 + (-16/3*z^2 - 2*z)*x - 2/3*z^3 + 16/3*z^2 - 5*z + 5/3
1226
sage: f(a)
1227
[0 0 0]
1228
[0 0 0]
1229
[0 0 0]
1230
sage: a.charpoly(algorithm='pari')
1231
x^3 + (z - 3)*x^2 + (-16/3*z^2 - 2*z)*x - 2/3*z^3 + 16/3*z^2 - 5*z + 5/3
1232
sage: a.charpoly(algorithm='hessenberg')
1233
x^3 + (z - 3)*x^2 + (-16/3*z^2 - 2*z)*x - 2/3*z^3 + 16/3*z^2 - 5*z + 5/3
1234
1235
sage: Matrix(K, 1, [0]).charpoly()
1236
x
1237
sage: Matrix(K, 1, [5]).charpoly(var='y')
1238
y - 5
1239
1240
sage: Matrix(CyclotomicField(13),3).charpoly()
1241
x^3
1242
sage: Matrix(CyclotomicField(13),3).charpoly()[2].parent()
1243
Cyclotomic Field of order 13 and degree 12
1244
1245
TESTS::
1246
1247
sage: Matrix(CyclotomicField(10),0).charpoly()
1248
1
1249
"""
1250
key = 'charpoly-%s-%s'%(algorithm,proof)
1251
f = self.fetch(key)
1252
if f is not None:
1253
return f.change_variable_name(var)
1254
1255
if self.nrows() != self.ncols():
1256
raise TypeError, "self must be square"
1257
1258
if self.is_zero():
1259
R = PolynomialRing(self.base_ring(), name=var)
1260
f = R.gen(0)**self.nrows()
1261
self.cache(key, f)
1262
return f
1263
1264
if self.nrows() == 1:
1265
R = PolynomialRing(self.base_ring(), name=var)
1266
f = R.gen(0) - self[0,0]
1267
self.cache(key, f)
1268
return f
1269
1270
if algorithm == 'multimodular':
1271
f = self._charpoly_multimodular(var, proof=proof)
1272
elif algorithm == 'pari':
1273
f = self._charpoly_over_number_field(var)
1274
elif algorithm == 'hessenberg':
1275
f = self._charpoly_hessenberg(var)
1276
else:
1277
raise ValueError, "unknown algorithm '%s'"%algorithm
1278
self.cache(key, f)
1279
return f
1280
1281
def _charpoly_mod(self, p):
1282
"""
1283
Return the characteristic polynomial of self*denom modulo all
1284
primes over $p$.
1285
1286
This is used internally by the multimodular charpoly algorithm.
1287
1288
INPUT:
1289
p -- a prime that splits completely
1290
1291
OUTPUT:
1292
matrix over GF(p) whose columns correspond to the entries
1293
of all the characteristic polynomials of the reduction of self modulo all
1294
the primes over p.
1295
1296
EXAMPLES::
1297
1298
sage: W.<z> = CyclotomicField(5)
1299
sage: A = matrix(W, 2, 2, [1+z, 0, 9*z+7, -3 + 4*z]); A
1300
[ z + 1 0]
1301
[9*z + 7 4*z - 3]
1302
sage: A._charpoly_mod(11)
1303
[8 2 1]
1304
[1 6 0]
1305
[4 0 0]
1306
[0 0 0]
1307
"""
1308
tm = verbose("Computing characteristic polynomial of cyclotomic matrix modulo %s."%p)
1309
# Reduce self modulo all primes over p
1310
R, denom = self._reductions(p)
1311
# Compute the characteristic polynomial of each reduced matrix
1312
F = [A.charpoly('x') for A in R]
1313
# Put the characteristic polynomials together as the rows of a mod-p matrix
1314
k = R[0].base_ring()
1315
S = matrix(k, len(F), self.nrows()+1, [f.list() for f in F])
1316
# multiply by inverse of reduction matrix to lift
1317
_, L = self._reduction_matrix(p)
1318
X = L * S
1319
# Now the columns of the matrix X define the entries of the
1320
# charpoly modulo p.
1321
verbose("Finished computing charpoly mod %s."%p, tm)
1322
return X
1323
1324
def _charpoly_multimodular(self, var='x', proof=None):
1325
"""
1326
Compute the characteristic polynomial of self using a
1327
multimodular algorithm.
1328
1329
INPUT:
1330
proof -- bool (default: global flag); if False, compute
1331
using primes $p_i$ until the lift modulo all
1332
primes up to $p_i$ is the same as the lift modulo
1333
all primes up to $p_{i+3}$ or the bound is
1334
reached.
1335
1336
EXAMPLES::
1337
1338
sage: K.<z> = CyclotomicField(3)
1339
sage: A = matrix(3, [-z, 2*z + 1, 1/2*z + 2, 1, -1/2, 2*z + 2, -2*z - 2, -2*z - 2, 2*z - 1])
1340
sage: A._charpoly_multimodular()
1341
x^3 + (-z + 3/2)*x^2 + (17/2*z + 9/2)*x - 9/2*z - 23/2
1342
sage: A._charpoly_multimodular('T')
1343
T^3 + (-z + 3/2)*T^2 + (17/2*z + 9/2)*T - 9/2*z - 23/2
1344
sage: A._charpoly_multimodular('T', proof=False)
1345
T^3 + (-z + 3/2)*T^2 + (17/2*z + 9/2)*T - 9/2*z - 23/2
1346
1347
TESTS:
1348
1349
We test a degenerate case::
1350
1351
sage: A = matrix(CyclotomicField(1),2,[1,2,3,4]); A.charpoly()
1352
x^2 - 5*x - 2
1353
"""
1354
cdef Matrix_cyclo_dense A
1355
A = Matrix_cyclo_dense.__new__(Matrix_cyclo_dense, self.parent(),
1356
None, None, None)
1357
1358
proof = get_proof_flag(proof, "linear_algebra")
1359
1360
n = self._base_ring._n()
1361
p = previous_prime(MAX_MODULUS)
1362
prod = 1
1363
v = []
1364
#A, denom = self._matrix._clear_denom()
1365
# TODO: this might be stupidly slow
1366
denom = self._matrix.denominator()
1367
A._matrix = <Matrix_rational_dense>(denom*self._matrix)
1368
bound = A._charpoly_bound()
1369
L_last = 0
1370
while prod <= bound:
1371
while (n >= 2 and p % n != 1) or denom % p == 0:
1372
if p == 2:
1373
raise RuntimeError, "we ran out of primes in multimodular charpoly algorithm."
1374
p = previous_prime(p)
1375
1376
X = A._charpoly_mod(p)
1377
v.append(X)
1378
prod *= p
1379
p = previous_prime(p)
1380
1381
# if we've used enough primes as determined by bound, or
1382
# if we've used 3 primes, we check to see if the result is
1383
# the same.
1384
if prod >= bound or (not proof and (len(v) % 3 == 0)):
1385
M = matrix(ZZ, self._base_ring.degree(), self._nrows+1)
1386
L = _lift_crt(M, v)
1387
if not proof and L == L_last:
1388
break
1389
L_last = L
1390
1391
# Now each column of L encodes a coefficient of the output polynomial,
1392
# with column 0 being the constant coefficient.
1393
K = self.base_ring()
1394
R = K[var]
1395
coeffs = [K(w.list()) for w in L.columns()]
1396
f = R(coeffs)
1397
1398
# Rescale to account for denominator, if necessary
1399
if denom != 1:
1400
x = R.gen()
1401
f = f(x * denom) * (1 / (denom**f.degree()))
1402
1403
return f
1404
1405
1406
def _reductions(self, p):
1407
"""
1408
Compute the reductions modulo all primes over p of denom*self,
1409
where denom is the denominator of self.
1410
1411
INPUT:
1412
p -- a prime that splits completely in the base cyclotomic field.
1413
1414
OUTPUT:
1415
list -- of r distinct matrices modulo p, where r is
1416
the degree of the cyclotomic base field.
1417
denom -- an integer
1418
1419
EXAMPLES::
1420
1421
sage: K.<z> = CyclotomicField(3)
1422
sage: w = matrix(K, 2, 3, [0, -z/5, -2/3, -2*z + 2, 2*z, z])
1423
sage: R, d = w._reductions(7)
1424
sage: R[0]
1425
[0 2 4]
1426
[1 1 4]
1427
sage: R[1]
1428
[0 1 4]
1429
[5 4 2]
1430
sage: d
1431
15
1432
"""
1433
# Get matrix that defines the linear reduction maps modulo
1434
# each prime of the base ring over p.
1435
T, _ = self._reduction_matrix(p)
1436
# Clear denominator and get matrix over the integers suitable
1437
# for reduction.
1438
A, denom = self._matrix._clear_denom()
1439
# Actually reduce the matrix over the integers modulo the
1440
# prime p.
1441
B = A._mod_int(p)
1442
# Now multiply, which computes from B all the reductions of
1443
# self*denom modulo each of the primes over p.
1444
R = T * B
1445
# Finally compute the actual reductions by extracting them
1446
# from R (note that the rows of R define the reductions).
1447
ans = R._matrices_from_rows(self._nrows, self._ncols)
1448
return ans, denom
1449
1450
def _reduction_matrix(self, p):
1451
"""
1452
INPUT:
1453
p -- a prime that splits completely in the base field.
1454
1455
OUTPUT:
1456
-- Matrix over GF(p) whose action from the left
1457
gives the map from O_K to GF(p) x ... x GF(p)
1458
given by reducing modulo all the primes over p.
1459
-- inverse of this matrix
1460
1461
EXAMPLES::
1462
1463
sage: K.<z> = CyclotomicField(3)
1464
sage: w = matrix(K, 2, 3, [0, -z/5, -2/3, -2*z + 2, 2*z, z])
1465
sage: A, B = w._reduction_matrix(7)
1466
sage: A
1467
[1 4]
1468
[1 2]
1469
sage: B
1470
[6 2]
1471
[4 3]
1472
1473
The reduction matrix is used to calculate the reductions mod primes
1474
above p. ::
1475
1476
sage: K.<z> = CyclotomicField(5)
1477
sage: A = matrix(K, 2, 2, [1, z, z^2+1, 5*z^3]); A
1478
[ 1 z]
1479
[z^2 + 1 5*z^3]
1480
sage: T, S = A._reduction_matrix(11)
1481
sage: T * A._rational_matrix().change_ring(GF(11))
1482
[ 1 9 5 4]
1483
[ 1 5 4 9]
1484
[ 1 4 6 1]
1485
[ 1 3 10 3]
1486
1487
The rows of this product are the (flattened) matrices mod each prime above p::
1488
1489
sage: roots = [r for r, e in K.defining_polynomial().change_ring(GF(11)).roots()]; roots
1490
[9, 5, 4, 3]
1491
sage: [r^2+1 for r in roots]
1492
[5, 4, 6, 10]
1493
sage: [5*r^3 for r in roots]
1494
[4, 9, 1, 3]
1495
1496
The reduction matrix is cached::
1497
sage: w._reduction_matrix(7) is w._reduction_matrix(7)
1498
True
1499
"""
1500
cache = self.fetch('reduction_matrices')
1501
if cache is None:
1502
cache = {}
1503
self.cache('reduction_matrices', cache)
1504
try:
1505
return cache[p]
1506
except KeyError:
1507
pass
1508
K = self.base_ring()
1509
phi = K.defining_polynomial()
1510
from sage.rings.all import GF
1511
from constructor import matrix
1512
F = GF(p)
1513
aa = [a for a, _ in phi.change_ring(F).roots()]
1514
n = K.degree()
1515
if len(aa) != n:
1516
raise ValueError, "the prime p (=%s) must split completely but doesn't"%p
1517
T = matrix(F, n)
1518
for i in range(n):
1519
a = aa[i]
1520
b = 1
1521
for j in range(n):
1522
T[i,j] = b
1523
b *= a
1524
T.set_immutable()
1525
ans = (T, T**(-1))
1526
cache[p] = ans
1527
return ans
1528
1529
def echelon_form(self, algorithm='multimodular', height_guess=None):
1530
"""
1531
Find the echelon form of self, using the specified algorithm.
1532
1533
The result is cached for each algorithm separately.
1534
1535
EXAMPLES::
1536
1537
sage: W.<z> = CyclotomicField(3)
1538
sage: A = matrix(W, 2, 3, [1+z, 2/3, 9*z+7, -3 + 4*z, z, -7*z]); A
1539
[ z + 1 2/3 9*z + 7]
1540
[4*z - 3 z -7*z]
1541
sage: A.echelon_form()
1542
[ 1 0 -192/97*z - 361/97]
1543
[ 0 1 1851/97*z + 1272/97]
1544
sage: A.echelon_form(algorithm='classical')
1545
[ 1 0 -192/97*z - 361/97]
1546
[ 0 1 1851/97*z + 1272/97]
1547
1548
We verify that the result is cached and that the caches are separate::
1549
1550
sage: A.echelon_form() is A.echelon_form()
1551
True
1552
sage: A.echelon_form() is A.echelon_form(algorithm='classical')
1553
False
1554
1555
TESTS::
1556
1557
sage: W.<z> = CyclotomicField(13)
1558
sage: A = Matrix(W, 2,3, [10^30*(1-z)^13, 1, 2, 3, 4, z])
1559
sage: B = Matrix(W, 2,3, [(1-z)^13, 1, 2, 3, 4, z])
1560
sage: A.echelon_form() == A.echelon_form('classical') # long time (4s on sage.math, 2011)
1561
True
1562
sage: B.echelon_form() == B.echelon_form('classical')
1563
True
1564
1565
A degenerate case with the degree 1 cyclotomic field::
1566
1567
sage: A = matrix(CyclotomicField(1),2,3,[1,2,3,4,5,6]);
1568
sage: A.echelon_form()
1569
[ 1 0 -1]
1570
[ 0 1 2]
1571
1572
A case that checks the bug in trac #3500. ::
1573
1574
sage: cf4 = CyclotomicField(4) ; z4 = cf4.0
1575
sage: A = Matrix(cf4, 1, 2, [-z4, 1])
1576
sage: A.echelon_form()
1577
[ 1 zeta4]
1578
1579
Verify that matrix on Trac #10281 works::
1580
1581
sage: K.<rho> = CyclotomicField(106)
1582
sage: coeffs = [(18603/107*rho^51 - 11583/107*rho^50 - 19907/107*rho^49 - 13588/107*rho^48 - 8722/107*rho^47 + 2857/107*rho^46 - 19279/107*rho^45 - 16666/107*rho^44 - 11327/107*rho^43 + 3802/107*rho^42 + 18998/107*rho^41 - 10798/107*rho^40 + 16210/107*rho^39 - 13768/107*rho^38 + 15063/107*rho^37 - 14433/107*rho^36 - 19434/107*rho^35 - 12606/107*rho^34 + 3786/107*rho^33 - 17996/107*rho^32 + 12341/107*rho^31 - 15656/107*rho^30 - 19092/107*rho^29 + 8382/107*rho^28 - 18147/107*rho^27 + 14024/107*rho^26 + 18751/107*rho^25 - 8301/107*rho^24 - 20112/107*rho^23 - 14483/107*rho^22 + 4715/107*rho^21 + 20065/107*rho^20 + 15293/107*rho^19 + 10072/107*rho^18 + 4775/107*rho^17 - 953/107*rho^16 - 19782/107*rho^15 - 16020/107*rho^14 + 5633/107*rho^13 - 17618/107*rho^12 - 18187/107*rho^11 + 7492/107*rho^10 + 19165/107*rho^9 - 9988/107*rho^8 - 20042/107*rho^7 + 10109/107*rho^6 - 17677/107*rho^5 - 17723/107*rho^4 - 12489/107*rho^3 - 6321/107*rho^2 - 4082/107*rho - 1378/107, 1, 4*rho + 1), (0, 1, rho + 4)]
1583
sage: m = matrix(2, coeffs)
1584
sage: a = m.echelon_form(algorithm='classical')
1585
sage: b = m.echelon_form(algorithm='multimodular') # long: about 5-10 seconds
1586
sage: a == b # long: depends on above
1587
True
1588
"""
1589
key = 'echelon_form-%s'%algorithm
1590
E = self.fetch(key)
1591
if E is not None:
1592
return E
1593
1594
if self._nrows == 0:
1595
E = self.__copy__()
1596
self.cache(key, E)
1597
self.cache('pivots', ())
1598
return E
1599
1600
if algorithm == 'multimodular':
1601
E = self._echelon_form_multimodular(height_guess=height_guess)
1602
elif algorithm == 'classical':
1603
E = (self*self.denominator())._echelon_classical()
1604
else:
1605
raise ValueError, "unknown algorithm '%s'"%algorithm
1606
1607
self.cache(key, E)
1608
return E
1609
1610
def _echelon_form_multimodular(self, num_primes=10, height_guess=None):
1611
"""
1612
Use a multimodular algorithm to find the echelon form of self.
1613
1614
INPUT:
1615
num_primes -- number of primes to work modulo
1616
height_guess -- guess for the height of the echelon form
1617
of self
1618
1619
OUTPUT:
1620
matrix in reduced row echelon form
1621
1622
EXAMPLES::
1623
1624
sage: W.<z> = CyclotomicField(3)
1625
sage: A = matrix(W, 2, 3, [1+z, 2/3, 9*z+7, -3 + 4*z, z, -7*z]); A
1626
[ z + 1 2/3 9*z + 7]
1627
[4*z - 3 z -7*z]
1628
sage: A._echelon_form_multimodular(10)
1629
[ 1 0 -192/97*z - 361/97]
1630
[ 0 1 1851/97*z + 1272/97]
1631
1632
TESTS:
1633
1634
We test a degenerate case::
1635
1636
sage: A = matrix(CyclotomicField(5),0); A
1637
[]
1638
sage: A._echelon_form_multimodular(10)
1639
[]
1640
sage: A.pivots()
1641
()
1642
1643
sage: A = matrix(CyclotomicField(13), 2, 3, [5, 1, 2, 46307, 46307*4, 46307])
1644
sage: A._echelon_form_multimodular()
1645
[ 1 0 7/19]
1646
[ 0 1 3/19]
1647
"""
1648
cdef int i
1649
cdef Matrix_cyclo_dense res
1650
1651
verbose("entering _echelon_form_multimodular", level=echelon_verbose_level)
1652
1653
denom = self._matrix.denominator()
1654
A = denom * self
1655
1656
# This bound is chosen somewhat arbitrarily. Changing it affects the
1657
# runtime, not the correctness of the result.
1658
if height_guess is None:
1659
height_guess = (A.coefficient_bound()+100)*1000000
1660
1661
# This is all setup to keep track of various data
1662
# in the loop below.
1663
p = previous_prime(MAX_MODULUS)
1664
found = 0
1665
prod = 1
1666
n = self._base_ring._n()
1667
height_bound = self._ncols * height_guess * A.coefficient_bound() + 1
1668
mod_p_ech_ls = []
1669
max_pivots = []
1670
is_square = self._nrows == self._ncols
1671
1672
verbose("using height bound %s"%height_bound, level=echelon_verbose_level)
1673
1674
while True:
1675
# Generate primes to use, and find echelon form
1676
# modulo those primes.
1677
while found < num_primes or prod <= height_bound:
1678
if (n == 1) or p%n == 1:
1679
try:
1680
mod_p_ech, piv_ls = A._echelon_form_one_prime(p)
1681
except ValueError:
1682
# This means that we chose a prime which divides
1683
# the denominator of the echelon form of self, so
1684
# just skip it and continue
1685
p = previous_prime(p)
1686
continue
1687
# if we have the identity, just return it, and
1688
# we're done.
1689
if is_square and len(piv_ls) == self._nrows:
1690
self.cache('pivots', tuple(range(self._nrows)))
1691
return self.parent().identity_matrix()
1692
if piv_ls > max_pivots:
1693
mod_p_ech_ls = [mod_p_ech]
1694
max_pivots = piv_ls
1695
# add this to the list of primes
1696
prod = p
1697
found = 1
1698
elif piv_ls == max_pivots:
1699
mod_p_ech_ls.append(mod_p_ech)
1700
# add this to the list of primes
1701
prod *= p
1702
found += 1
1703
else:
1704
# this means that the rank profile mod this
1705
# prime is worse than those that came before,
1706
# so we just loop
1707
p = previous_prime(p)
1708
continue
1709
1710
p = previous_prime(p)
1711
1712
if found > num_primes:
1713
num_primes = found
1714
1715
verbose("computed echelon form mod %s primes"%num_primes,
1716
level=echelon_verbose_level)
1717
verbose("current product of primes used: %s"%prod,
1718
level=echelon_verbose_level)
1719
1720
# Use CRT to lift back to ZZ
1721
mat_over_ZZ = matrix(ZZ, self._base_ring.degree(), self._nrows * self._ncols)
1722
_lift_crt(mat_over_ZZ, mod_p_ech_ls)
1723
# note: saving the CRT intermediate MultiModularBasis does
1724
# not seem to affect the runtime at all
1725
1726
# Attempt to use rational reconstruction to find
1727
# our echelon form
1728
try:
1729
verbose("attempting rational reconstruction ...", level=echelon_verbose_level)
1730
res = Matrix_cyclo_dense.__new__(Matrix_cyclo_dense, self.parent(),
1731
None, None, None)
1732
res._matrix = <Matrix_rational_dense>matrix_integer_dense_rational_reconstruction(mat_over_ZZ, prod)
1733
1734
except ValueError:
1735
# If a ValueError is raised here, it means that the
1736
# rational reconstruction failed. In this case, add
1737
# on a few more primes, and try again.
1738
1739
num_primes += echelon_primes_increment
1740
verbose("rational reconstruction failed, trying with %s primes"%num_primes, level=echelon_verbose_level)
1741
continue
1742
1743
verbose("rational reconstruction succeeded with %s primes!"%num_primes, level=echelon_verbose_level)
1744
1745
if ((res * res.denominator()).coefficient_bound() *
1746
self.coefficient_bound() * self.ncols()) > prod:
1747
# In this case, we don't know the result to sufficient
1748
# "precision" (here precision is just the modulus,
1749
# prod) to guarantee its correctness, so loop.
1750
1751
num_primes += echelon_primes_increment
1752
verbose("height not sufficient to determine echelon form", level=echelon_verbose_level)
1753
continue
1754
1755
verbose("found echelon form with %s primes, whose product is %s"%(num_primes, prod), level=echelon_verbose_level)
1756
self.cache('pivots', tuple(max_pivots))
1757
return res
1758
1759
def _echelon_form_one_prime(self, p):
1760
"""
1761
Find the echelon form of self mod the primes dividing p. Return
1762
the rational matrix representing this lift. If the pivots of the
1763
reductions mod the primes over p are different, then no such lift
1764
exists, and we raise a ValueError. If this happens, then the
1765
denominator of the echelon form of self is divisible by p. (Note
1766
that the converse need not be true.)
1767
1768
INPUT:
1769
p -- a prime that splits completely in the cyclotomic base field.
1770
1771
OUTPUT:
1772
matrix -- Lift via CRT of the echelon forms of self modulo
1773
each of the primes over p.
1774
tuple -- the tuple of pivots for the echelon form of self mod the
1775
primes dividing p
1776
1777
EXAMPLES::
1778
1779
sage: W.<z> = CyclotomicField(3)
1780
sage: A = matrix(W, 2, 3, [1+z, 2/3, 9*z+7, -3 + 4*z, z, -7*z]); A
1781
[ z + 1 2/3 9*z + 7]
1782
[4*z - 3 z -7*z]
1783
sage: A._echelon_form_one_prime(7)
1784
(
1785
[1 0 4 0 1 2]
1786
[0 0 3 0 0 4], (0, 1)
1787
)
1788
sage: Matrix(W,2,3,[2*z+3,0,1,0,1,0])._echelon_form_one_prime(7)
1789
Traceback (most recent call last):
1790
...
1791
ValueError: echelon form mod 7 not defined
1792
1793
"""
1794
cdef Matrix_cyclo_dense res
1795
cdef int i
1796
1797
# Initialize variables
1798
is_square = self._nrows == self._ncols
1799
ls, denom = self._reductions(p)
1800
1801
# Find our first echelon form, and the associated list
1802
# of pivots
1803
ech_ls = [ls[0].echelon_form()]
1804
pivot_ls = ech_ls[0].pivots()
1805
# If we've found the identity matrix, we're all done.
1806
if self._nrows == self._ncols == len(pivot_ls):
1807
return (self.parent().identity_matrix(), range(self._nrows))
1808
1809
# For each reduction of self (i.e. for each prime of
1810
# self.base_ring() over p), compute the echelon form, and
1811
# keep track of all reductions which have the largest
1812
# number of pivots seen so far.
1813
for i from 1 <= i < len(ls):
1814
ech = ls[i].echelon_form()
1815
1816
# This should only occur when p divides the denominator
1817
# of the echelon form of self.
1818
if ech.pivots() != pivot_ls:
1819
raise ValueError, "echelon form mod %s not defined"%p
1820
1821
ech_ls.append(ech)
1822
1823
# Now, just lift back to ZZ and return it.
1824
1825
# TODO: coercion going on here
1826
reduction = matrix(ZZ, len(ech_ls), self._nrows * self._ncols,
1827
[ [y.lift() for y in E.list()] for E in ech_ls])
1828
1829
# TODO: more coercion happening here
1830
_, Finv = self._reduction_matrix(p)
1831
1832
lifted_matrix = Finv * reduction
1833
1834
return (lifted_matrix, pivot_ls)
1835
1836
1837