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