Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/matrix/matrix_integer_dense_hnf.py
8815 views
1
"""
2
Modular algorithm to compute Hermite normal forms of integer matrices.
3
4
AUTHORS:
5
-- Clement Pernet and William Stein (2008-02-07): initial version
6
"""
7
8
from copy import copy
9
10
from sage.misc.misc import verbose, cputime
11
from sage.matrix.constructor import random_matrix, matrix, matrix, identity_matrix
12
13
from sage.rings.all import ZZ, Integer, previous_prime, next_prime, CRT_list, RR
14
15
def max_det_prime(n):
16
"""
17
Return the largest prime so that it is reasonably efficienct to
18
compute modulo that prime with n x n matrices in LinBox.
19
20
INPUT:
21
22
- ``n`` -- a positive integer
23
24
OUTPUT:
25
26
a prime number
27
28
EXAMPLES::
29
30
sage: from sage.matrix.matrix_integer_dense_hnf import max_det_prime
31
sage: max_det_prime(10000)
32
8388593
33
sage: max_det_prime(1000)
34
8388593
35
sage: max_det_prime(10)
36
8388593
37
"""
38
# See #14032: LinBox now uses a constant bound of 2^23.
39
# This is the largest prime less than that bound.
40
return Integer(8388593)
41
42
def det_from_modp_and_divisor(A, d, p, z_mod, moduli, z_so_far=ZZ(1), N_so_far=ZZ(1)):
43
"""
44
This is used for internal purposes for computing determinants
45
quickly (with the hybrid p-adic / multimodular algorithm).
46
47
INPUT:
48
A -- a square matrix
49
d -- a divisor of the determinant of A
50
p -- a prime
51
z_mod -- values of det/d (mod ...)
52
moduli -- the moduli so far
53
z_so_far -- for a modulus p in the list moduli,
54
(z_so_far mod p) is the determinant of A modulo p.
55
N_so_far -- N_so_far is the product over the primes in the list moduli.
56
57
OUTPUT:
58
A triple (det bound, new z_so_far, new N_so_far).
59
60
EXAMPLES:
61
sage: a = matrix(ZZ, 3, [6, 1, 2, -56, -2, -1, -11, 2, -3])
62
sage: factor(a.det())
63
-1 * 13 * 29
64
sage: d = 13
65
sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf
66
sage: matrix_integer_dense_hnf.det_from_modp_and_divisor(a, d, 97, [], [])
67
(-377, -29, 97)
68
sage: a.det()
69
-377
70
"""
71
tm = verbose("Multimodular stage of det calculation -- using p = %s"%p, level=2)
72
z = A.mod(p).det() / d
73
z = z.lift()
74
z_mod.append(z)
75
moduli.append(p)
76
z = CRT_list([z_so_far, z], [N_so_far, p])
77
N = N_so_far*p
78
79
if z > N//2:
80
z = z - N
81
verbose("Finished multimodular det for p = %s"%p, tm, level=2)
82
return (d * z, z, N)
83
84
def det_given_divisor(A, d, proof=True, stabilize=2):
85
"""
86
Given a divisor d of the determinant of A, compute the
87
determinant of A.
88
89
INPUT::
90
91
- ``A`` -- a square integer matrix
92
- ``d`` -- a nonzero integer that is assumed to divide the determinant of A
93
- ``proof`` -- bool (default: True) compute det modulo enough primes
94
so that the determinant is computed provably correctly (via the
95
Hadamard bound). It would be VERY hard for ``det()`` to fail even
96
with proof=False.
97
- ``stabilize`` -- int (default: 2) if proof = False, then compute
98
the determinant modulo `p` until ``stabilize`` successive modulo
99
determinant computations stabilize.
100
101
OUTPUT:
102
103
integer -- determinant
104
105
EXAMPLES::
106
107
sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf
108
sage: a = matrix(ZZ,3,[-1, -1, -1, -20, 4, 1, -1, 1, 2])
109
sage: matrix_integer_dense_hnf.det_given_divisor(a, 3)
110
-30
111
sage: matrix_integer_dense_hnf.det_given_divisor(a, 3, proof=False)
112
-30
113
sage: matrix_integer_dense_hnf.det_given_divisor(a, 3, proof=False, stabilize=1)
114
-30
115
sage: a.det()
116
-30
117
118
Here we illustrate proof=False giving a wrong answer::
119
120
sage: p = matrix_integer_dense_hnf.max_det_prime(2)
121
sage: q = previous_prime(p)
122
sage: a = matrix(ZZ, 2, [p, 0, 0, q])
123
sage: p * q
124
70368442188091
125
sage: matrix_integer_dense_hnf.det_given_divisor(a, 1, proof=False, stabilize=2)
126
0
127
128
This still works, because we don't work modulo primes that divide
129
the determinant bound, which is found using a p-adic algorithm::
130
131
sage: a.det(proof=False, stabilize=2)
132
70368442188091
133
134
3 primes is enough::
135
136
sage: matrix_integer_dense_hnf.det_given_divisor(a, 1, proof=False, stabilize=3)
137
70368442188091
138
sage: matrix_integer_dense_hnf.det_given_divisor(a, 1, proof=False, stabilize=5)
139
70368442188091
140
sage: matrix_integer_dense_hnf.det_given_divisor(a, 1, proof=True)
141
70368442188091
142
143
TESTS::
144
145
sage: m = diagonal_matrix(ZZ, 68, [2]*66 + [1,1])
146
sage: m.det()
147
73786976294838206464
148
"""
149
p = max_det_prime(A.nrows())
150
z_mod = []
151
moduli = []
152
assert d != 0
153
z_so_far = 1
154
N_so_far = 1
155
if proof:
156
N = 1
157
B = (2 * 10**A.hadamard_bound()) // d + 1
158
dd = d
159
# bad verbose statement, since computing the log overflows!
160
est = int(RR(B).log() / RR(p).log()) + 1
161
cnt = 1
162
verbose("Multimodular det -- need to use about %s primes."%est, level=1)
163
while N < B:
164
if d % p != 0:
165
tm = cputime()
166
dd, z_so_far, N_so_far = det_from_modp_and_divisor(A, d, p, z_mod, moduli, z_so_far, N_so_far)
167
N *= p
168
verbose("computed det mod p=%s which is %s (of about %s)"%(p, cnt, est), tm)
169
p = previous_prime(p)
170
cnt += 1
171
return dd
172
else:
173
val = []
174
while True:
175
if d % p != 0:
176
tm = cputime()
177
dd, z_so_far, N_so_far = det_from_modp_and_divisor(A, d, p, z_mod, moduli, z_so_far, N_so_far)
178
verbose("computed det mod %s"%p, tm)
179
val.append(dd)
180
if len(val) >= stabilize and len(set(val[-stabilize:])) == 1:
181
return val[-1]
182
p = previous_prime(p)
183
184
def det_padic(A, proof=True, stabilize=2):
185
"""
186
Return the determinant of A, computed using a p-adic/multimodular
187
algorithm.
188
189
INPUTS:
190
191
- ``A`` -- a square matrix
192
193
- ``proof`` -- boolean
194
195
- ``stabilize`` (default: 2) -- if proof False, number of successive primes so that
196
CRT det must stabilize.
197
198
EXAMPLES::
199
200
sage: import sage.matrix.matrix_integer_dense_hnf as h
201
sage: a = matrix(ZZ, 3, [1..9])
202
sage: h.det_padic(a)
203
0
204
sage: a = matrix(ZZ, 3, [1,2,5,-7,8,10,192,5,18])
205
sage: h.det_padic(a)
206
-3669
207
sage: a.determinant(algorithm='ntl')
208
-3669
209
"""
210
if not A.is_square():
211
raise ValueError("A must be a square matrix")
212
r = A.rank()
213
if r < A.nrows():
214
return ZZ(0)
215
v = random_matrix(ZZ, A.nrows(), 1)
216
d = A._solve_right_nonsingular_square(v, check_rank=False).denominator()
217
return det_given_divisor(A, d, proof=proof, stabilize=stabilize)
218
219
def double_det (A, b, c, proof):
220
"""
221
Compute the determinants of the stacked integer matrices
222
A.stack(b) and A.stack(c).
223
224
INPUT:
225
A -- an (n-1) x n matrix
226
b -- an 1 x n matrix
227
c -- an 1 x n matrix
228
proof -- whether or not to compute the det modulo enough times
229
to provably compute the determinant.
230
231
OUTPUT:
232
a pair of two integers.
233
234
EXAMPLES:
235
sage: from sage.matrix.matrix_integer_dense_hnf import double_det
236
sage: A = matrix(ZZ, 2, 3, [1,2,3, 4,-2,5])
237
sage: b = matrix(ZZ, 1, 3, [1,-2,5])
238
sage: c = matrix(ZZ, 1, 3, [8,2,10])
239
sage: A.stack(b).det()
240
-48
241
sage: A.stack(c).det()
242
42
243
sage: double_det(A, b, c, False)
244
(-48, 42)
245
"""
246
# We use the "two for the price of one" algorithm, which I made up. (William Stein)
247
248
# This is a clever trick! First we transpose everything. Then
249
# we use that if [A|b]*v = c then [A|c]*w = b with w easy to write down!
250
# In fact w is got from v by dividing all entries by -v[n], where n is the
251
# number of rows of v, and *also* dividing the last entry of w by v[n] again.
252
# See this as an algebra exercise where you have to think of matrix vector
253
# multiply as "linear combination of columns".
254
A = A.transpose()
255
b = b.transpose()
256
c = c.transpose()
257
t = verbose('starting double det')
258
B = A.augment(b)
259
v = B.solve_right(-c)
260
261
db = det_given_divisor(B, v.denominator(), proof=proof)
262
263
n = v.nrows()
264
vn = v[n-1,0]
265
w = (-1/vn)*v
266
w[n-1] = w[n-1]/vn
267
dc = det_given_divisor(A.augment(c), w.denominator(), proof=proof)
268
269
verbose('finished double det', t)
270
271
return (db, dc)
272
273
def add_column_fallback(B, a, proof):
274
"""
275
Simplistic version of add_column, in case the powerful clever one
276
fails (e.g., B is singular).
277
278
INPUT:
279
B -- a square matrix (may be singular)
280
a -- an n x 1 matrix, where B has n rows
281
proof -- bool; whether to prove result correct
282
283
OUTPUT:
284
x -- a vector such that H' = H_B.augment(x) is the HNF of A = B.augment(a).
285
286
EXAMPLES:
287
sage: B = matrix(ZZ,3, [-1, -1, 1, -3, 8, -2, -1, -1, -1])
288
sage: a = matrix(ZZ,3,1, [1,2,3])
289
sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf
290
sage: matrix_integer_dense_hnf.add_column_fallback(B, a, True)
291
[-3]
292
[-7]
293
[-2]
294
sage: matrix_integer_dense_hnf.add_column_fallback(B, a, False)
295
[-3]
296
[-7]
297
[-2]
298
sage: B.augment(a).hermite_form()
299
[ 1 1 1 -3]
300
[ 0 11 1 -7]
301
[ 0 0 2 -2]
302
"""
303
tt = verbose('add column fallback...')
304
W = B.augment(matrix(ZZ,B.nrows(),a.list()))
305
H, _ = hnf(W, proof)
306
C = H.matrix_from_columns([H.ncols()-1])
307
verbose('finished add column fallback', tt)
308
return C
309
310
def solve_system_with_difficult_last_row(B, a):
311
"""
312
Solve B*x = a when the last row of $B$ contains huge entries using
313
a clever trick that reduces the problem to solve C*x = a where $C$
314
is $B$ but with the last row replaced by something small, along
315
with one easy null space computation. The latter are both solved
316
$p$-adically.
317
318
INPUT:
319
B -- a square n x n nonsingular matrix with painful big bottom row.
320
a -- an n x 1 column matrix
321
OUTPUT:
322
the unique solution to B*x = a.
323
324
EXAMPLES:
325
sage: from sage.matrix.matrix_integer_dense_hnf import solve_system_with_difficult_last_row
326
sage: B = matrix(ZZ, 3, [1,2,4, 3,-4,7, 939082,2930982,132902384098234])
327
sage: a = matrix(ZZ,3,1, [1,2,5])
328
sage: z = solve_system_with_difficult_last_row(B, a)
329
sage: z
330
[ 106321906985474/132902379815497]
331
[132902385037291/1329023798154970]
332
[ -5221794/664511899077485]
333
sage: B*z
334
[1]
335
[2]
336
[5]
337
"""
338
# Here's how:
339
# 1. We make a copy of B but with the last *nasty* row of B replaced
340
# by a random very nice row.
341
C = copy(B)
342
while True:
343
C[C.nrows()-1] = random_matrix(ZZ,1,C.ncols()).row(0)
344
# 2. Then we find the unique solution to C * x = a
345
try:
346
x = C.solve_right(a)
347
except ValueError:
348
verbose("Try difficult solve again with different random vector")
349
else:
350
break
351
352
353
# 3. We next delete the last row of B and find a basis vector k
354
# for the 1-dimensional kernel.
355
D = B.matrix_from_rows(range(C.nrows()-1))
356
N = D._rational_kernel_iml()
357
if N.ncols() != 1:
358
verbose("Try difficult solve again with different random vector")
359
return solve_system_with_difficult_last_row(B, a)
360
361
k = N.matrix_from_columns([0])
362
363
# 4. The sought for solution z to B*z = a is some linear combination
364
#
365
# z = x + alpha*k
366
#
367
# of x and k, where k is the above fixed basis for the kernel of D.
368
# Setting w to be the last row of B, this column vector z satisfies
369
#
370
# w * z = a'
371
#
372
# where a' is the last entry of a. Thus
373
#
374
# w * (x + alpha*k) = a'
375
#
376
# so w * x + alpha*w*k = a'
377
# so alpha*w*k = a' - w*x.
378
379
w = B[-1] # last row of B
380
a_prime = a[-1]
381
lhs = w*k
382
rhs = a_prime - w * x
383
384
if lhs[0] == 0:
385
verbose("Try difficult solve again with different random vector")
386
return solve_system_with_difficult_last_row(B, a)
387
388
alpha = rhs[0] / lhs[0]
389
z = x + alpha*k
390
return z
391
392
def add_column(B, H_B, a, proof):
393
"""
394
The add column procedure.
395
396
INPUT:
397
B -- a square matrix (may be singular)
398
H_B -- the Hermite normal form of B
399
a -- an n x 1 matrix, where B has n rows
400
proof -- bool; whether to prove result correct, in case we use fallback method.
401
402
OUTPUT:
403
x -- a vector such that H' = H_B.augment(x) is the HNF of A = B.augment(a).
404
405
EXAMPLES:
406
sage: B = matrix(ZZ, 3, 3, [1,2,5, 0,-5,3, 1,1,2])
407
sage: H_B = B.echelon_form()
408
sage: a = matrix(ZZ, 3, 1, [1,8,-2])
409
sage: import sage.matrix.matrix_integer_dense_hnf as hnf
410
sage: x = hnf.add_column(B, H_B, a, True); x
411
[18]
412
[ 3]
413
[23]
414
sage: H_B.augment(x)
415
[ 1 0 17 18]
416
[ 0 1 3 3]
417
[ 0 0 18 23]
418
sage: B.augment(a).echelon_form()
419
[ 1 0 17 18]
420
[ 0 1 3 3]
421
[ 0 0 18 23]
422
"""
423
t0 = verbose('starting add_column')
424
425
if B.rank() < B.nrows():
426
return add_column_fallback(B, a, proof)
427
else:
428
z = solve_system_with_difficult_last_row(B, a)
429
430
zd, d = z._clear_denom()
431
x = H_B * zd
432
if d != 1:
433
for i in range(x.nrows()):
434
x[i,0] = x[i,0]/d
435
436
return x
437
438
def add_row(A, b, pivots, include_zero_rows):
439
"""
440
The add row procedure.
441
442
INPUT:
443
A -- a matrix in Hermite normal form with n column
444
b -- an n x 1 row matrix
445
pivots -- sorted list of integers; the pivot positions of A.
446
447
OUTPUT:
448
H -- the Hermite normal form of A.stack(b).
449
new_pivots -- the pivot columns of H.
450
451
EXAMPLES:
452
sage: import sage.matrix.matrix_integer_dense_hnf as hnf
453
sage: A = matrix(ZZ, 2, 3, [-21, -7, 5, 1,20,-7])
454
sage: b = matrix(ZZ, 1,3, [-1,1,-1])
455
sage: hnf.add_row(A, b, A.pivots(), True)
456
(
457
[ 1 6 29]
458
[ 0 7 28]
459
[ 0 0 46], [0, 1, 2]
460
)
461
sage: A.stack(b).echelon_form()
462
[ 1 6 29]
463
[ 0 7 28]
464
[ 0 0 46]
465
"""
466
t = verbose('add hnf row')
467
v = b.row(0)
468
H, pivs = A._add_row_and_maintain_echelon_form(b.row(0), pivots)
469
if include_zero_rows and H.nrows() != A.nrows()+1:
470
H = H.matrix_from_rows(range(A.nrows()+1))
471
verbose('finished add hnf row', t)
472
return H, pivs
473
474
def pivots_of_hnf_matrix(H):
475
"""
476
Return the pivot columns of a matrix H assumed to be in HNF.
477
478
INPUT:
479
H -- a matrix that must be HNF
480
481
OUTPUT:
482
list -- list of pivots
483
484
EXAMPLES:
485
sage: H = matrix(ZZ, 3, 5, [1, 0, 0, 45, -36, 0, 1, 0, 131, -107, 0, 0, 0, 178, -145]); H
486
[ 1 0 0 45 -36]
487
[ 0 1 0 131 -107]
488
[ 0 0 0 178 -145]
489
sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf
490
sage: matrix_integer_dense_hnf.pivots_of_hnf_matrix(H)
491
[0, 1, 3]
492
"""
493
pivots = []
494
r = -1
495
for j in xrange(H.ncols()):
496
# Find first nonzero position (counting from bottom) in the j-th column
497
for i in reversed(xrange(H.nrows())):
498
if H[i,j]:
499
if i > r:
500
pivots.append(j)
501
r = i
502
elif i <= r:
503
break
504
return pivots
505
506
def hnf_square(A, proof):
507
"""
508
INPUT:
509
a nonsingular n x n matrix A over the integers.
510
OUTPUT:
511
the Hermite normal form of A.
512
513
EXAMPLES:
514
sage: import sage.matrix.matrix_integer_dense_hnf as hnf
515
sage: A = matrix(ZZ, 3, [-21, -7, 5, 1,20,-7, -1,1,-1])
516
sage: hnf.hnf_square(A, False)
517
[ 1 6 29]
518
[ 0 7 28]
519
[ 0 0 46]
520
sage: A.echelon_form()
521
[ 1 6 29]
522
[ 0 7 28]
523
[ 0 0 46]
524
"""
525
n = A.nrows()
526
m = A.ncols()
527
if n != m:
528
raise ValueError("A must be square.")
529
530
# Small cases -- don't use this algorithm
531
if n <= 3:
532
return A.echelon_form(algorithm="pari")
533
534
if A.rank() < A.nrows():
535
raise ValueError("matrix must have full rank")
536
537
538
539
t = verbose("starting slicings")
540
B = A.matrix_from_rows(range(m-2)).matrix_from_columns(range(n-1))
541
c = A.matrix_from_rows([m-2]).matrix_from_columns (range(n-1))
542
d = A.matrix_from_rows([m-1]).matrix_from_columns (range(n-1))
543
b = A.matrix_from_columns([n-1]).matrix_from_rows(range(m-2))
544
verbose("done slicing", t)
545
546
try:
547
(d1,d2) = double_det (B,c,d, proof=proof)
548
except (ValueError, ZeroDivisionError), msg:
549
d1 = B.stack(c).det(proof=proof)
550
d2 = B.stack(d).det(proof=proof)
551
552
(g,k,l) = d1._xgcd (d2, minimal=True)
553
554
W = B.stack (k*c + l*d)
555
verbose("submatrix det: g=%s"%g)
556
CUTOFF = 2**30
557
if g == 0:
558
# Big trouble -- matrix isn't invertible
559
# Since we have no good conditioning code at present,
560
# in this case we just fall back to using pari.
561
H = W.echelon_form(algorithm='pari')
562
elif 2*g > CUTOFF:
563
# Unlikely that g will be large on even slightly random input
564
# if it is, we fallback to the traditional algorithm.
565
# A nasty example is A = n*random_matrix(ZZ,m), where
566
# this algorithm gets killed. This is not random input though.
567
f = W.gcd()
568
g = g / (f**W.nrows())
569
if 2*g <= CUTOFF:
570
verbose("Found common factor of %s -- dividing out; get new g = %s"%(f,g))
571
W0 = (W/f).change_ring(ZZ)
572
H = W0._hnf_mod(2*g)
573
H *= f
574
else:
575
verbose("Falling back to PARI HNF since input matrix is ill conditioned for p-adic hnf algorithm.")
576
# We need more clever preconditioning?
577
# It is important to *not* just do the submatrix, since
578
# the whole rest of the algorithm will likely be very slow in
579
# weird cases where the det is large.
580
# E.g., matrix all of whose rows but 1 are multiplied by some
581
# fixed scalar n.
582
raise NotImplementedError("fallback to PARI!")
583
#H = W.hermite_form(algorithm='pari')
584
else:
585
H = W._hnf_mod(2*g)
586
587
x = add_column(W, H, b.stack(matrix(1,1,[k*A[m-2,m-1] + l*A[m-1,m-1]])), proof)
588
Hprime = H.augment(x)
589
pivots = pivots_of_hnf_matrix(Hprime)
590
591
Hprime, pivots = add_row(Hprime, A.matrix_from_rows([m-2]), pivots, include_zero_rows=False)
592
Hprime, pivots = add_row(Hprime, A.matrix_from_rows([m-1]), pivots, include_zero_rows=False)
593
H = Hprime.matrix_from_rows(range(m))
594
return H
595
596
def interleave_matrices(A, B, cols1, cols2):
597
"""
598
INPUT:
599
A, B -- matrices with the same number of rows
600
cols1, cols2 -- disjoint lists of integers
601
OUTPUT:
602
construct a new matrix C by sticking the columns
603
of A at the positions specified by cols1 and the
604
columns of B at the positions specified by cols2.
605
606
EXAMPLES:
607
sage: A = matrix(ZZ, 2, [1,2,3,4]); B = matrix(ZZ, 2, [-1,5,2,3])
608
sage: A
609
[1 2]
610
[3 4]
611
sage: B
612
[-1 5]
613
[ 2 3]
614
sage: import sage.matrix.matrix_integer_dense_hnf as hnf
615
sage: hnf.interleave_matrices(A, B, [1,3], [0,2])
616
[-1 1 5 2]
617
[ 2 3 3 4]
618
"""
619
D = A.augment(B)
620
w = cols1 + cols2
621
v = [w.index(i) for i in range(len(cols1) + len(cols2))]
622
return D.matrix_from_columns(v)
623
624
def probable_pivot_rows(A):
625
"""
626
Return rows of A that are very likely to be pivots.
627
628
This really finds the pivots of A modulo a random prime.
629
630
INPUT:
631
A -- a matrix
632
OUTPUT:
633
a tuple of integers
634
635
EXAMPLES:
636
sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf
637
sage: a = matrix(ZZ,3,[0, -1, -1, 0, -20, 1, 0, 1, 2])
638
sage: a
639
[ 0 -1 -1]
640
[ 0 -20 1]
641
[ 0 1 2]
642
sage: matrix_integer_dense_hnf.probable_pivot_rows(a)
643
(0, 1)
644
"""
645
return probable_pivot_columns(A.transpose())
646
647
def probable_pivot_columns(A):
648
"""
649
INPUT:
650
A -- a matrix
651
OUTPUT:
652
a tuple of integers
653
654
EXAMPLES:
655
sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf
656
sage: a = matrix(ZZ,3,[0, -1, -1, 0, -20, 1, 0, 1, 2])
657
sage: a
658
[ 0 -1 -1]
659
[ 0 -20 1]
660
[ 0 1 2]
661
sage: matrix_integer_dense_hnf.probable_pivot_columns(a)
662
(1, 2)
663
"""
664
p = ZZ.random_element(10007, 46000).next_prime()
665
pivots = A._reduce(p).pivots()
666
return pivots
667
668
def ones(H, pivots):
669
"""
670
Find all 1 pivot columns of the matrix H in Hermite form, along
671
with the corresponding rows, and also the non 1 pivot columns and
672
non-pivot rows. Here a 1 pivot column is a pivot column so that
673
the leading bottom entry is 1.
674
675
INPUT:
676
H -- matrix in Hermite form
677
pivots -- list of integers (all pivot positions of H).
678
679
OUTPUT:
680
4-tuple of integer lists: onecol, onerow, non_oneol, non_onerow
681
682
EXAMPLES:
683
sage: H = matrix(ZZ, 3, 5, [1, 0, 0, 45, -36, 0, 1, 0, 131, -107, 0, 0, 0, 178, -145]); H
684
[ 1 0 0 45 -36]
685
[ 0 1 0 131 -107]
686
[ 0 0 0 178 -145]
687
sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf
688
sage: matrix_integer_dense_hnf.ones(H, [0,1,3])
689
([0, 1], [0, 1], [2], [2])
690
"""
691
# Find the "onecol" pivot columns of H, i.e., the columns
692
# that contain exactly one "1" entry and all other entries 0.
693
onecol = []
694
onerow = []
695
i = 0
696
for c in pivots:
697
if H[i,c] == 1:
698
onecol.append(c)
699
onerow.append(i)
700
i += 1
701
onecol_set = set(onecol)
702
non_onerow = [i for i in range(len(pivots)) if i not in onerow]
703
non_onecol = [i for i in range(H.ncols()) if i not in onecol_set][:len(non_onerow)]
704
return onecol, onerow, non_onecol, non_onerow
705
706
def extract_ones_data(H, pivots):
707
"""
708
Compute ones data and corresponding submatrices of H. This is
709
used to optimized the add_row function.
710
711
INPUT:
712
H -- a matrix in HNF
713
pivots -- list of all pivot column positions of H
714
715
OUTPUT:
716
C, D, E, onecol, onerow, non_onecol, non_onerow
717
where onecol, onerow, non_onecol, non_onerow are as for
718
the ones function, and C, D, E are matrices:
719
C -- submatrix of all non-onecol columns and onecol rows
720
D -- all non-onecol columns and other rows
721
E -- inverse of D
722
If D isn't invertible or there are 0 or more than 2 non onecols,
723
then C, D, and E are set to None.
724
725
EXAMPLES:
726
sage: H = matrix(ZZ, 3, 4, [1, 0, 0, 7, 0, 1, 5, 2, 0, 0, 6, 6])
727
sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf
728
sage: matrix_integer_dense_hnf.extract_ones_data(H, [0,1,2])
729
(
730
[0]
731
[5], [6], [1/6], [0, 1], [0, 1], [2], [2]
732
)
733
734
Here we get None's since the (2,2) position submatrix is not invertible.
735
sage: H = matrix(ZZ, 3, 5, [1, 0, 0, 45, -36, 0, 1, 0, 131, -107, 0, 0, 0, 178, -145]); H
736
[ 1 0 0 45 -36]
737
[ 0 1 0 131 -107]
738
[ 0 0 0 178 -145]
739
sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf
740
sage: matrix_integer_dense_hnf.extract_ones_data(H, [0,1,3])
741
(None, None, None, [0, 1], [0, 1], [2], [2])
742
"""
743
onecol, onerow, non_onecol, non_onerow = ones(H, pivots)
744
verbose('extract_ones -- got submatrix of size %s'%len(non_onecol))
745
if len(non_onecol) in [1,2]:
746
# Extract submatrix of all non-onecol columns and onecol rows
747
C = H.matrix_from_rows_and_columns(onerow, non_onecol)
748
# Extract submatrix of all non-onecol columns and other rows
749
D = H.matrix_from_rows_and_columns(non_onerow, non_onecol).transpose()
750
tt = verbose("extract ones -- INVERT %s x %s"%(len(non_onerow), len(non_onecol)), level=1)
751
try:
752
E = D**(-1)
753
except ZeroDivisionError:
754
C = D = E = None
755
verbose("done inverting", tt, level=1)
756
return C, D, E, onecol, onerow, non_onecol, non_onerow
757
else:
758
return None, None, None, onecol, onerow, non_onecol, non_onerow
759
760
def is_in_hnf_form(H, pivots):
761
"""
762
Return True precisely if the matrix H is in Hermite normal form
763
with given pivot columns.
764
765
INPUT:
766
H -- matrix
767
pivots -- sorted list of integers
768
769
OUTPUT:
770
bool -- True or False
771
772
EXAMPLES:
773
sage: a = matrix(ZZ,3,5,[-2, -6, -3, -17, -1, 2, -1, -1, -2, -1, -2, -2, -6, 9, 2])
774
sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf
775
sage: matrix_integer_dense_hnf.is_in_hnf_form(a,range(3))
776
False
777
sage: e = a.hermite_form(); p = a.pivots()
778
sage: matrix_integer_dense_hnf.is_in_hnf_form(e, p)
779
True
780
"""
781
tt = verbose('testing if matrix is in HNF')
782
r = 0
783
pivots_set = set(pivots)
784
for j in xrange(H.ncols()):
785
if j in pivots_set:
786
for i in xrange(r+1, H.nrows()):
787
if H[i,j]:
788
verbose('not HNF because nonzeros below pivot position',tt)
789
return False
790
for i in xrange(r):
791
if H[i,j] < 0 or H[i,j] >= H[r,j]:
792
verbose('not HNF because negative or too big above pivot position',tt)
793
return False
794
r += 1
795
else:
796
for i in xrange(r,H.nrows()):
797
if H[i,j]:
798
verbose('not HNF nonzero in wrong place in nonpivot column',tt)
799
return False
800
verbose('done verifying in HNF -- yes', tt)
801
return True
802
803
def probable_hnf(A, include_zero_rows, proof):
804
"""
805
Return the HNF of A or raise an exception if something involving
806
the randomized nature of the algorithm goes wrong along the way.
807
Calling this function again a few times should result it in it
808
working, at least if proof=True.
809
810
INPUT:
811
A -- a matrix
812
include_zero_rows -- bool
813
proof -- bool
814
815
OUTPUT:
816
the Hermite normal form of A.
817
cols -- pivot columns
818
819
EXAMPLES:
820
sage: a = matrix(ZZ,4,3,[-1, -1, -1, -20, 4, 1, -1, 1, 2,1,2,3])
821
sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf
822
sage: matrix_integer_dense_hnf.probable_hnf(a, True, True)
823
(
824
[1 0 0]
825
[0 1 0]
826
[0 0 1]
827
[0 0 0], [0, 1, 2]
828
)
829
sage: matrix_integer_dense_hnf.probable_hnf(a, False, True)
830
(
831
[1 0 0]
832
[0 1 0]
833
[0 0 1], [0, 1, 2]
834
)
835
sage: matrix_integer_dense_hnf.probable_hnf(a, False, False)
836
(
837
[1 0 0]
838
[0 1 0]
839
[0 0 1], [0, 1, 2]
840
)
841
"""
842
# Find left-most full rank submatrix by working modulo a prime
843
rows = list(probable_pivot_rows(A))
844
B = A.matrix_from_rows(rows)
845
cols = list(probable_pivot_columns(B))
846
C = B.matrix_from_columns(cols)
847
# Now C is a submatrix of A that has full rank and is square.
848
849
# We compute the HNF of C, which is a square nonsingular matrix.
850
try:
851
H = hnf_square(C, proof=proof)
852
except NotImplementedError:
853
# raise
854
# this signals that we must fallback to pari
855
verbose("generic random modular HNF algorithm failed -- we fall back to PARI")
856
H = A.hermite_form(algorithm='pari', include_zero_rows=include_zero_rows, proof=proof)
857
return H, H.pivots()
858
859
# The transformation matrix to HNF is the unique
860
# matrix U such that U * C = H, i.e., U = H*C^(-1).
861
862
if len(cols) < B.ncols():
863
# We compute the HNF of B by multiplying the matrix D
864
# got from the columns not in C by U:
865
# We want to compute X = U*D. But U = H*C^(-1),
866
# so X = U*D = H*C^(-1)*D.
867
# So C*H^(-1)*X = D
868
869
# find y s.t C*y = D
870
# H^(-1)*X = y ===> X = H*y
871
#
872
cols_set = set(cols)
873
cols2 = [i for i in range(B.ncols()) if not i in cols_set]
874
D = B.matrix_from_columns(cols2)
875
Y = C.solve_right(D)
876
H2 = H*Y
877
H2 = H2.change_ring(ZZ)
878
879
# The HNF of B is got by assembling together
880
# the matrices H and H2.
881
H = interleave_matrices(H, H2, cols, cols2)
882
883
pivots = pivots_of_hnf_matrix(H)
884
885
# Now H is the HNF of the matrix B.
886
# Finally we add all remaining rows of A to H using
887
# the add_row function.
888
889
C, D, E, onecol, onerow, non_onecol, non_onerow = extract_ones_data(H, cols)
890
if not proof and len(non_onecol) == 0:
891
# Identity matrix -- done
892
verbose("hnf -- got identity matrix -- early abort (0)")
893
if include_zero_rows: H = pad_zeros(H, A.nrows())
894
return H, pivots
895
896
rows_set = set(rows)
897
for i in range(A.nrows()):
898
if not i in rows_set:
899
v = A.matrix_from_rows([i])
900
if v == 0: continue
901
if E is None:
902
H, pivots = add_row(H, v, pivots, include_zero_rows=False)
903
C, D, E, onecol, onerow, non_onecol, non_onerow = extract_ones_data(H, pivots)
904
if not proof and len(non_onecol) == 0:
905
# Identity matrix -- done
906
verbose("hnf -- got identity matrix -- early abort (1)")
907
if include_zero_rows: H = pad_zeros(H, A.nrows())
908
return H, pivots
909
else:
910
z = A.matrix_from_rows_and_columns([i], non_onecol)
911
w = A.matrix_from_rows_and_columns([i], onecol)
912
tt = verbose("checking denom (%s x %s)"%(D.nrows(), D.ncols()))
913
Y = (z - w*C).transpose()
914
k = E*Y
915
verbose("done checking denom",tt)
916
if k.denominator() != 1:
917
H, pivots = add_row(H, v, pivots, include_zero_rows=False)
918
D = H.matrix_from_rows_and_columns(non_onerow, non_onecol).transpose()
919
nn = ones(H, pivots)
920
if not proof and len(nn[2]) == 0:
921
verbose("hnf -- got identity matrix -- early abort (2)")
922
if include_zero_rows: H = pad_zeros(H, A.nrows())
923
return H, pivots
924
925
if include_zero_rows: H = pad_zeros(H, A.nrows())
926
return H, pivots
927
928
def pad_zeros(A, nrows):
929
"""
930
Add zeros to the bottom of A so that the
931
resulting matrix has nrows.
932
933
INPUT:
934
A -- a matrix
935
nrows -- an integer that is at least as big as the number of rows of A.
936
937
OUTPUT:
938
a matrix with nrows rows.
939
940
EXAMPLES:
941
sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf
942
sage: a = matrix(ZZ, 2, 4, [1, 0, 0, 7, 0, 1, 5, 2])
943
sage: matrix_integer_dense_hnf.pad_zeros(a, 4)
944
[1 0 0 7]
945
[0 1 5 2]
946
[0 0 0 0]
947
[0 0 0 0]
948
sage: matrix_integer_dense_hnf.pad_zeros(a, 2)
949
[1 0 0 7]
950
[0 1 5 2]
951
"""
952
nz = nrows - A.nrows()
953
if nz == 0:
954
return A
955
if nz < 0:
956
return A.matrix_from_rows(range(nrows))
957
return A.stack(matrix(ZZ, nz, A.ncols()))
958
959
960
def hnf(A, include_zero_rows=True, proof=True):
961
"""
962
Return the Hermite Normal Form of a general integer matrix A,
963
along with the pivot columns.
964
965
INPUT:
966
A -- an n x m matrix A over the integers.
967
include_zero_rows -- bool (default: True) whether or not to
968
include zero rows in the output matrix
969
proof -- whether or not to prove the result correct.
970
971
OUTPUT:
972
matrix -- the Hermite normal form of A
973
pivots -- the pivot column positions of A
974
975
EXAMPLES:
976
sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf
977
sage: a = matrix(ZZ,3,5,[-2, -6, -3, -17, -1, 2, -1, -1, -2, -1, -2, -2, -6, 9, 2])
978
sage: matrix_integer_dense_hnf.hnf(a)
979
(
980
[ 2 0 26 -75 -10]
981
[ 0 1 27 -73 -9]
982
[ 0 0 37 -106 -13], [0, 1, 2]
983
)
984
sage: matrix_integer_dense_hnf.hnf(a.transpose())
985
(
986
[1 0 0]
987
[0 1 0]
988
[0 0 1]
989
[0 0 0]
990
[0 0 0], [0, 1, 2]
991
)
992
sage: matrix_integer_dense_hnf.hnf(a.transpose(), include_zero_rows=False)
993
(
994
[1 0 0]
995
[0 1 0]
996
[0 0 1], [0, 1, 2]
997
)
998
"""
999
if A.nrows() <= 1:
1000
np = A.nonzero_positions()
1001
if len(np) == 0:
1002
pivots = []
1003
if not include_zero_rows:
1004
A = A.new_matrix(0) # 0 rows
1005
else:
1006
i,j = np[0]
1007
if A[i,j] < 0:
1008
A = -A
1009
pivots = [j]
1010
return A, pivots
1011
1012
if proof == False:
1013
H, pivots = probable_hnf(A, include_zero_rows = include_zero_rows, proof=False)
1014
if not include_zero_rows and len(pivots) > H.nrows():
1015
return H.matrix_from_rows(range(len(pivots))), pivots
1016
1017
while True:
1018
try:
1019
H, pivots = probable_hnf(A, include_zero_rows = include_zero_rows, proof=True)
1020
except (AssertionError, ZeroDivisionError, TypeError):
1021
raise
1022
#verbose("Assertion occurred when computing HNF; guessed pivot columns likely wrong.")
1023
#continue
1024
else:
1025
if is_in_hnf_form(H, pivots):
1026
if not include_zero_rows and len(pivots) > H.nrows():
1027
H = H.matrix_from_rows(range(len(pivots)))
1028
return H, pivots
1029
verbose("After attempt the return matrix is not in HNF form since pivots must have been wrong. We try again.")
1030
1031
def hnf_with_transformation(A, proof=True):
1032
"""
1033
Compute the HNF H of A along with a transformation matrix U
1034
such that U*A = H. Also return the pivots of H.
1035
1036
INPUT:
1037
A -- an n x m matrix A over the integers.
1038
proof -- whether or not to prove the result correct.
1039
1040
OUTPUT:
1041
matrix -- the Hermite normal form H of A
1042
U -- a unimodular matrix such that U * A = H
1043
pivots -- the pivot column positions of A
1044
1045
EXAMPLES:
1046
sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf
1047
sage: A = matrix(ZZ, 2, [1, -5, -10, 1, 3, 197]); A
1048
[ 1 -5 -10]
1049
[ 1 3 197]
1050
sage: H, U, pivots = matrix_integer_dense_hnf.hnf_with_transformation(A)
1051
sage: H
1052
[ 1 3 197]
1053
[ 0 8 207]
1054
sage: U
1055
[ 0 1]
1056
[-1 1]
1057
sage: U*A
1058
[ 1 3 197]
1059
[ 0 8 207]
1060
"""
1061
# All we do is augment the input matrix with the identity matrix of the appropriate rank on the right.
1062
C = A.augment(identity_matrix(ZZ, A.nrows()))
1063
H, pivots = hnf(C, include_zero_rows=True, proof=proof)
1064
U = H.matrix_from_columns(range(A.ncols(), H.ncols()))
1065
H2 = H.matrix_from_columns(range(A.ncols()))
1066
return H2, U, pivots
1067
1068
def hnf_with_transformation_tests(n=10, m=5, trials=10):
1069
"""
1070
Use this to randomly test that hnf with transformation matrix
1071
is working.
1072
1073
EXAMPLES:
1074
sage: from sage.matrix.matrix_integer_dense_hnf import hnf_with_transformation_tests
1075
sage: hnf_with_transformation_tests(n=15,m=10, trials=10)
1076
0 1 2 3 4 5 6 7 8 9
1077
"""
1078
import sys
1079
for i in range(trials):
1080
print i,
1081
sys.stdout.flush()
1082
a = random_matrix(ZZ, n, m)
1083
w = hnf_with_transformation(a)
1084
assert w[0] == w[1]*a
1085
w = hnf_with_transformation(a, proof=False)
1086
assert w[0] == w[1]*a
1087
1088
1089
#################################################################################################
1090
# Code for testing and benchmarking
1091
#################################################################################################
1092
def benchmark_hnf(nrange, bits=4):
1093
"""
1094
Run benchmark program.
1095
1096
EXAMPLES:
1097
sage: import sage.matrix.matrix_integer_dense_hnf as hnf
1098
sage: hnf.benchmark_hnf([50,100],32)
1099
('sage', 50, 32, ...),
1100
('sage', 100, 32, ...),
1101
"""
1102
b = 2**bits
1103
for n in nrange:
1104
a = random_matrix(ZZ, n, x=-b,y=b)
1105
t = cputime()
1106
h,_ = hnf(a, proof=False)
1107
tm = cputime(t)
1108
print '%s,'%(('sage', n, bits, tm),)
1109
1110
def benchmark_magma_hnf(nrange, bits=4):
1111
"""
1112
EXAMPLES:
1113
sage: import sage.matrix.matrix_integer_dense_hnf as hnf
1114
sage: hnf.benchmark_magma_hnf([50,100],32) # optional - magma
1115
('magma', 50, 32, ...),
1116
('magma', 100, 32, ...),
1117
"""
1118
from sage.interfaces.all import magma
1119
b = 2**bits
1120
for n in nrange:
1121
a = magma('MatrixAlgebra(IntegerRing(),%s)![Random(%s,%s) : i in [1..%s]]'%(n,-b,b,n**2))
1122
t = magma.cputime()
1123
h = a.EchelonForm()
1124
tm = magma.cputime(t)
1125
print '%s,'%(('magma', n, bits, tm),)
1126
1127
1128
global sanity
1129
def sanity_checks(times=50, n=8, m=5, proof=True, stabilize=2, check_using_magma = True):
1130
"""
1131
Run random sanity checks on the modular p-adic HNF with tall and wide matrices
1132
both dense and sparse.
1133
1134
INPUT:
1135
times -- number of times to randomly try matrices with each shape
1136
n -- number of rows
1137
m -- number of columns
1138
proof -- test with proof true
1139
stabilize -- parameter to pass to hnf algorithm when proof is False
1140
check_using_magma -- if True use Magma instead of PARI to
1141
check correctness of computed HNF's.
1142
Since PARI's HNF is buggy and slow (as of
1143
2008-02-16 non-pivot entries sometimes
1144
aren't normalized to be nonnegative) the
1145
default is Magma.
1146
1147
EXAMPLES:
1148
sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf
1149
sage: matrix_integer_dense_hnf.sanity_checks(times=5, check_using_magma=False)
1150
small 8 x 5
1151
0 1 2 3 4 (done)
1152
big 8 x 5
1153
0 1 2 3 4 (done)
1154
small 5 x 8
1155
0 1 2 3 4 (done)
1156
big 5 x 8
1157
0 1 2 3 4 (done)
1158
sparse 8 x 5
1159
0 1 2 3 4 (done)
1160
sparse 5 x 8
1161
0 1 2 3 4 (done)
1162
ill conditioned -- 1000*A -- 8 x 5
1163
0 1 2 3 4 (done)
1164
ill conditioned -- 1000*A but one row -- 8 x 5
1165
0 1 2 3 4 (done)
1166
"""
1167
import sys
1168
def __do_check(v):
1169
"""
1170
This is used internally by the sanity check code.
1171
"""
1172
for i,a in enumerate(v):
1173
global sanity
1174
sanity = a
1175
print i,
1176
sys.stdout.flush()
1177
if check_using_magma:
1178
if magma(hnf(a)[0]) != magma(a).EchelonForm():
1179
print "bug computing hnf of a matrix"
1180
print 'a = matrix(ZZ, %s, %s, %s)'%(a.nrows(), a.ncols(), a.list())
1181
return
1182
else:
1183
if hnf(a)[0] != a.echelon_form('pari'):
1184
print "bug computing hnf of a matrix"
1185
print 'a = matrix(ZZ, %s, %s, %s)'%(a.nrows(), a.ncols(), a.list())
1186
return
1187
print " (done)"
1188
1189
print "small %s x %s"%(n,m)
1190
__do_check([random_matrix(ZZ, n, m, x=-1,y=1) for _ in range(times)])
1191
print "big %s x %s"%(n,m)
1192
__do_check([random_matrix(ZZ, n, m, x=-2^32,y=2^32) for _ in range(times)])
1193
1194
print "small %s x %s"%(m,n)
1195
__do_check([random_matrix(ZZ, m, n, x=-1,y=1) for _ in range(times)])
1196
print "big %s x %s"%(m,n)
1197
__do_check([random_matrix(ZZ, m, n, x=-2^32,y=2^32) for _ in range(times)])
1198
1199
print "sparse %s x %s"%(n,m)
1200
__do_check([random_matrix(ZZ, n, m, density=0.1) for _ in range(times)])
1201
print "sparse %s x %s"%(m,n)
1202
__do_check([random_matrix(ZZ, m, n, density=0.1) for _ in range(times)])
1203
1204
print "ill conditioned -- 1000*A -- %s x %s"%(n,m)
1205
__do_check([1000*random_matrix(ZZ, n, m, x=-1,y=1) for _ in range(times)])
1206
1207
print "ill conditioned -- 1000*A but one row -- %s x %s"%(n,m)
1208
v = []
1209
for _ in range(times):
1210
a = 1000*random_matrix(ZZ, n, m, x=-1,y=1)
1211
a[a.nrows()-1] = a[a.nrows()-1]/1000
1212
v.append(a)
1213
__do_check(v)
1214
1215
1216
1217
1218
1219