Path: blob/master/sage/matrix/matrix_integer_dense_hnf.py
4056 views
"""1Modular algorithm to compute Hermite normal forms of integer matrices.23AUTHORS:4-- Clement Pernet and William Stein (2008-02-07): initial version5"""67from copy import copy89from sage.misc.misc import verbose, cputime10from sage.matrix.constructor import random_matrix, matrix, matrix, identity_matrix1112from sage.rings.all import ZZ, previous_prime, next_prime, CRT_list, RR13import math1415def max_det_prime(n):16"""17Return the largest prime so that it is reasonably efficiency to18compute modulo that prime with n x n matrices in LinBox.1920INPUT:21n -- a positive integer2223OUTPUT:24a prime number2526EXAMPLES:27sage: from sage.matrix.matrix_integer_dense_hnf import max_det_prime28sage: max_det_prime(10000)2952430930sage: max_det_prime(1000)31209716932sage: max_det_prime(10)331677725934"""35k = int(26 - math.ceil(math.log(n)*0.7213475205))36return next_prime(2**k)3738def det_from_modp_and_divisor(A, d, p, z_mod, moduli, z_so_far=ZZ(1), N_so_far=ZZ(1)):39"""40This is used for internal purposes for computing determinants41quickly (with the hybrid p-adic / multimodular algorithm).4243INPUT:44A -- a square matrix45d -- a divisor of the determinant of A46p -- a prime47z_mod -- values of det/d (mod ...)48moduli -- the moduli so far49z_so_far -- for a modulus p in the list moduli,50(z_so_far mod p) is the determinant of A modulo p.51N_so_far -- N_so_far is the product over the primes in the list moduli.5253OUTPUT:54A triple (det bound, new z_so_far, new N_so_far).5556EXAMPLES:57sage: a = matrix(ZZ, 3, [6, 1, 2, -56, -2, -1, -11, 2, -3])58sage: factor(a.det())59-1 * 13 * 2960sage: d = 1361sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf62sage: matrix_integer_dense_hnf.det_from_modp_and_divisor(a, d, 97, [], [])63(-377, -29, 97)64sage: a.det()65-37766"""67tm = verbose("Multimodular stage of det calculation -- using p = %s"%p, level=2)68z = A._linbox_modn_det(p) / d69z = z.lift()70z_mod.append(z)71moduli.append(p)72z = CRT_list([z_so_far, z], [N_so_far, p])73N = N_so_far*p7475if z > N//2:76z = z - N77verbose("Finished multimodular det for p = %s"%p, tm, level=2)78return (d * z, z, N)7980def det_given_divisor(A, d, proof=True, stabilize=2):81"""82Given a divisor d of the determinant of A, compute the83determinant of A.8485INPUT:86A -- a square integer matrix87d -- a nonzero integer that is assumed to divide the determinant of A88proof -- bool (default True) compute det modulo enough primes89so that the determinant is computed provably correctly90(via the Hadamard bound). It would be VERY hard for91det to fail even with proof=False.92stabilize -- int (default: 2) if proof = False, then compute det93mod p until stabilize successive modulo det computations94stabilize.9596OUTPUT:97integer -- determinant9899EXAMPLES:100sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf101sage: a = matrix(ZZ,3,[-1, -1, -1, -20, 4, 1, -1, 1, 2])102sage: matrix_integer_dense_hnf.det_given_divisor(a, 3)103-30104sage: matrix_integer_dense_hnf.det_given_divisor(a, 3, proof=False)105-30106sage: matrix_integer_dense_hnf.det_given_divisor(a, 3, proof=False, stabilize=1)107-30108sage: a.det()109-30110111Here we illustrate proof=False giving a wrong answer:112sage: p = matrix_integer_dense_hnf.max_det_prime(2)113sage: q = previous_prime(p)114sage: a = matrix(ZZ, 2, [p, 0, 0, q])115sage: p * q1161125899772623531117sage: matrix_integer_dense_hnf.det_given_divisor(a, 1, proof=False, stabilize=2)1180119120This still works, because we don't work modulo primes that divide121the determinant bound, which is found using a p-adic algorithm.122sage: a.det(proof=False, stabilize=2)12311258997726235311241251263 primes is enough:127sage: matrix_integer_dense_hnf.det_given_divisor(a, 1, proof=False, stabilize=3)1281125899772623531129sage: matrix_integer_dense_hnf.det_given_divisor(a, 1, proof=False, stabilize=5)1301125899772623531131sage: matrix_integer_dense_hnf.det_given_divisor(a, 1, proof=True)1321125899772623531133134TESTS:135sage: m = diagonal_matrix(ZZ, 68, [2]*66 + [1,1])136sage: m.det()13773786976294838206464138"""139p = max_det_prime(A.nrows())140z_mod = []141moduli = []142assert d != 0143z_so_far = 1144N_so_far = 1145if proof:146N = 1147B = (2 * 10**A.hadamard_bound()) // d + 1148dd = d149# bad verbose statement, since computing the log overflows!150est = int(RR(B).log() / RR(p).log()) + 1151cnt = 1152verbose("Multimodular det -- need to use about %s primes."%est, level=1)153while N < B:154if d % p != 0:155tm = cputime()156dd, z_so_far, N_so_far = det_from_modp_and_divisor(A, d, p, z_mod, moduli, z_so_far, N_so_far)157N *= p158verbose("computed det mod p=%s which is %s (of about %s)"%(p, cnt, est), tm)159p = previous_prime(p)160cnt += 1161return dd162else:163val = []164while True:165if d % p != 0:166tm = cputime()167dd, z_so_far, N_so_far = det_from_modp_and_divisor(A, d, p, z_mod, moduli, z_so_far, N_so_far)168verbose("computed det mod %s"%p, tm)169val.append(dd)170if len(val) >= stabilize and len(set(val[-stabilize:])) == 1:171return val[-1]172p = previous_prime(p)173174def det_padic(A, proof=True, stabilize=2):175"""176Return the determinant of A, computed using a p-adic/multimodular177algorithm.178179INPUTS:180A -- a square matrix181proof -- boolean182stabilize (default: 2) -- if proof False, number of successive primes so that183CRT det must stabilize.184185EXAMPLES:186sage: import sage.matrix.matrix_integer_dense_hnf as h187sage: a = matrix(ZZ, 3, [1..9])188sage: h.det_padic(a)1890190sage: a = matrix(ZZ, 3, [1,2,5,-7,8,10,192,5,18])191sage: h.det_padic(a)192-3669193sage: a.determinant(algorithm='ntl')194-3669195"""196if not A.is_square():197raise ValueError("A must be a square matrix")198r = A.rank()199if r < A.nrows():200return ZZ(0)201v = random_matrix(ZZ, A.nrows(), 1)202d = A._solve_right_nonsingular_square(v, check_rank=False).denominator()203return det_given_divisor(A, d, proof=proof, stabilize=stabilize)204205def double_det (A, b, c, proof):206"""207Compute the determinants of the stacked integer matrices208A.stack(b) and A.stack(c).209210INPUT:211A -- an (n-1) x n matrix212b -- an 1 x n matrix213c -- an 1 x n matrix214proof -- whether or not to compute the det modulo enough times215to provably compute the determinant.216217OUTPUT:218a pair of two integers.219220EXAMPLES:221sage: from sage.matrix.matrix_integer_dense_hnf import double_det222sage: A = matrix(ZZ, 2, 3, [1,2,3, 4,-2,5])223sage: b = matrix(ZZ, 1, 3, [1,-2,5])224sage: c = matrix(ZZ, 1, 3, [8,2,10])225sage: A.stack(b).det()226-48227sage: A.stack(c).det()22842229sage: double_det(A, b, c, False)230(-48, 42)231"""232# We use the "two for the price of one" algorithm, which I made up. (William Stein)233234# This is a clever trick! First we transpose everything. Then235# we use that if [A|b]*v = c then [A|c]*w = b with w easy to write down!236# In fact w is got from v by dividing all entries by -v[n], where n is the237# number of rows of v, and *also* dividing the last entry of w by v[n] again.238# See this as an algebra exercise where you have to think of matrix vector239# multiply as "linear combination of columns".240A = A.transpose()241b = b.transpose()242c = c.transpose()243t = verbose('starting double det')244B = A.augment(b)245v = B.solve_right(-c)246247db = det_given_divisor(B, v.denominator(), proof=proof)248249n = v.nrows()250vn = v[n-1,0]251w = (-1/vn)*v252w[n-1] = w[n-1]/vn253dc = det_given_divisor(A.augment(c), w.denominator(), proof=proof)254255verbose('finished double det', t)256257return (db, dc)258259def add_column_fallback(B, a, proof):260"""261Simplistic version of add_column, in case the powerful clever one262fails (e.g., B is singular).263264INPUT:265B -- a square matrix (may be singular)266a -- an n x 1 matrix, where B has n rows267proof -- bool; whether to prove result correct268269OUTPUT:270x -- a vector such that H' = H_B.augment(x) is the HNF of A = B.augment(a).271272EXAMPLES:273sage: B = matrix(ZZ,3, [-1, -1, 1, -3, 8, -2, -1, -1, -1])274sage: a = matrix(ZZ,3,1, [1,2,3])275sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf276sage: matrix_integer_dense_hnf.add_column_fallback(B, a, True)277[-3]278[-7]279[-2]280sage: matrix_integer_dense_hnf.add_column_fallback(B, a, False)281[-3]282[-7]283[-2]284sage: B.augment(a).hermite_form()285[ 1 1 1 -3]286[ 0 11 1 -7]287[ 0 0 2 -2]288"""289tt = verbose('add column fallback...')290W = B.augment(matrix(ZZ,B.nrows(),a.list()))291H, _ = hnf(W, proof)292C = H.matrix_from_columns([H.ncols()-1])293verbose('finished add column fallback', tt)294return C295296def solve_system_with_difficult_last_row(B, a):297"""298Solve B*x = a when the last row of $B$ contains huge entries using299a clever trick that reduces the problem to solve C*x = a where $C$300is $B$ but with the last row replaced by something small, along301with one easy null space computation. The latter are both solved302$p$-adically.303304INPUT:305B -- a square n x n nonsingular matrix with painful big bottom row.306a -- an n x 1 column matrix307OUTPUT:308the unique solution to B*x = a.309310EXAMPLES:311sage: from sage.matrix.matrix_integer_dense_hnf import solve_system_with_difficult_last_row312sage: B = matrix(ZZ, 3, [1,2,4, 3,-4,7, 939082,2930982,132902384098234])313sage: a = matrix(ZZ,3,1, [1,2,5])314sage: z = solve_system_with_difficult_last_row(B, a)315sage: z316[ 106321906985474/132902379815497]317[132902385037291/1329023798154970]318[ -5221794/664511899077485]319sage: B*z320[1]321[2]322[5]323"""324# Here's how:325# 1. We make a copy of B but with the last *nasty* row of B replaced326# by a random very nice row.327C = copy(B)328while True:329C[C.nrows()-1] = random_matrix(ZZ,1,C.ncols()).row(0)330# 2. Then we find the unique solution to C * x = a331try:332x = C.solve_right(a)333except ValueError:334verbose("Try difficult solve again with different random vector")335else:336break337338339# 3. We next delete the last row of B and find a basis vector k340# for the 1-dimensional kernel.341D = B.matrix_from_rows(range(C.nrows()-1))342N = D._rational_kernel_iml()343if N.ncols() != 1:344verbose("Try difficult solve again with different random vector")345return solve_system_with_difficult_last_row(B, a)346347k = N.matrix_from_columns([0])348349# 4. The sought for solution z to B*z = a is some linear combination350#351# z = x + alpha*k352#353# of x and k, where k is the above fixed basis for the kernel of D.354# Setting w to be the last row of B, this column vector z satisfies355#356# w * z = a'357#358# where a' is the last entry of a. Thus359#360# w * (x + alpha*k) = a'361#362# so w * x + alpha*w*k = a'363# so alpha*w*k = a' - w*x.364365w = B[-1] # last row of B366a_prime = a[-1]367lhs = w*k368rhs = a_prime - w * x369370if lhs[0] == 0:371verbose("Try difficult solve again with different random vector")372return solve_system_with_difficult_last_row(B, a)373374alpha = rhs[0] / lhs[0]375z = x + alpha*k376return z377378def add_column(B, H_B, a, proof):379"""380The add column procedure.381382INPUT:383B -- a square matrix (may be singular)384H_B -- the Hermite normal form of B385a -- an n x 1 matrix, where B has n rows386proof -- bool; whether to prove result correct, in case we use fallback method.387388OUTPUT:389x -- a vector such that H' = H_B.augment(x) is the HNF of A = B.augment(a).390391EXAMPLES:392sage: B = matrix(ZZ, 3, 3, [1,2,5, 0,-5,3, 1,1,2])393sage: H_B = B.echelon_form()394sage: a = matrix(ZZ, 3, 1, [1,8,-2])395sage: import sage.matrix.matrix_integer_dense_hnf as hnf396sage: x = hnf.add_column(B, H_B, a, True); x397[18]398[ 3]399[23]400sage: H_B.augment(x)401[ 1 0 17 18]402[ 0 1 3 3]403[ 0 0 18 23]404sage: B.augment(a).echelon_form()405[ 1 0 17 18]406[ 0 1 3 3]407[ 0 0 18 23]408"""409t0 = verbose('starting add_column')410411if B.rank() < B.nrows():412return add_column_fallback(B, a, proof)413else:414z = solve_system_with_difficult_last_row(B, a)415416zd, d = z._clear_denom()417x = H_B * zd418if d != 1:419for i in range(x.nrows()):420x[i,0] = x[i,0]/d421422return x423424def add_row(A, b, pivots, include_zero_rows):425"""426The add row procedure.427428INPUT:429A -- a matrix in Hermite normal form with n column430b -- an n x 1 row matrix431pivots -- sorted list of integers; the pivot positions of A.432433OUTPUT:434H -- the Hermite normal form of A.stack(b).435new_pivots -- the pivot columns of H.436437EXAMPLES:438sage: import sage.matrix.matrix_integer_dense_hnf as hnf439sage: A = matrix(ZZ, 2, 3, [-21, -7, 5, 1,20,-7])440sage: b = matrix(ZZ, 1,3, [-1,1,-1])441sage: hnf.add_row(A, b, A.pivots(), True)442(443[ 1 6 29]444[ 0 7 28]445[ 0 0 46], [0, 1, 2]446)447sage: A.stack(b).echelon_form()448[ 1 6 29]449[ 0 7 28]450[ 0 0 46]451"""452t = verbose('add hnf row')453v = b.row(0)454H, pivs = A._add_row_and_maintain_echelon_form(b.row(0), pivots)455if include_zero_rows and H.nrows() != A.nrows()+1:456H = H.matrix_from_rows(range(A.nrows()+1))457verbose('finished add hnf row', t)458return H, pivs459460def pivots_of_hnf_matrix(H):461"""462Return the pivot columns of a matrix H assumed to be in HNF.463464INPUT:465H -- a matrix that must be HNF466467OUTPUT:468list -- list of pivots469470EXAMPLES:471sage: H = matrix(ZZ, 3, 5, [1, 0, 0, 45, -36, 0, 1, 0, 131, -107, 0, 0, 0, 178, -145]); H472[ 1 0 0 45 -36]473[ 0 1 0 131 -107]474[ 0 0 0 178 -145]475sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf476sage: matrix_integer_dense_hnf.pivots_of_hnf_matrix(H)477[0, 1, 3]478"""479pivots = []480r = -1481for j in xrange(H.ncols()):482# Find first nonzero position (counting from bottom) in the j-th column483for i in reversed(xrange(H.nrows())):484if H[i,j]:485if i > r:486pivots.append(j)487r = i488elif i <= r:489break490return pivots491492def hnf_square(A, proof):493"""494INPUT:495a nonsingular n x n matrix A over the integers.496OUTPUT:497the Hermite normal form of A.498499EXAMPLES:500sage: import sage.matrix.matrix_integer_dense_hnf as hnf501sage: A = matrix(ZZ, 3, [-21, -7, 5, 1,20,-7, -1,1,-1])502sage: hnf.hnf_square(A, False)503[ 1 6 29]504[ 0 7 28]505[ 0 0 46]506sage: A.echelon_form()507[ 1 6 29]508[ 0 7 28]509[ 0 0 46]510"""511n = A.nrows()512m = A.ncols()513if n != m:514raise ValueError("A must be square.")515516# Small cases -- don't use this algorithm517if n <= 3:518return A.echelon_form(algorithm="pari")519520if A.rank() < A.nrows():521raise ValueError("matrix must have full rank")522523524525t = verbose("starting slicings")526B = A.matrix_from_rows(range(m-2)).matrix_from_columns(range(n-1))527c = A.matrix_from_rows([m-2]).matrix_from_columns (range(n-1))528d = A.matrix_from_rows([m-1]).matrix_from_columns (range(n-1))529b = A.matrix_from_columns([n-1]).matrix_from_rows(range(m-2))530verbose("done slicing", t)531532try:533(d1,d2) = double_det (B,c,d, proof=proof)534except (ValueError, ZeroDivisionError), msg:535d1 = B.stack(c).det(proof=proof)536d2 = B.stack(d).det(proof=proof)537538(g,k,l) = d1._xgcd (d2, minimal=True)539540W = B.stack (k*c + l*d)541verbose("submatrix det: g=%s"%g)542CUTOFF = 2**30543if g == 0:544# Big trouble -- matrix isn't invertible545# Since we have no good conditioning code at present,546# in this case we just fall back to using pari.547H = W.echelon_form(algorithm='pari')548elif 2*g > CUTOFF:549# Unlikely that g will be large on even slightly random input550# if it is, we fallback to the traditional algorithm.551# A nasty example is A = n*random_matrix(ZZ,m), where552# this algorithm gets killed. This is not random input though.553f = W.gcd()554g = g / (f**W.nrows())555if 2*g <= CUTOFF:556verbose("Found common factor of %s -- dividing out; get new g = %s"%(f,g))557W0 = (W/f).change_ring(ZZ)558H = W0._hnf_mod(2*g)559H *= f560else:561verbose("Falling back to PARI HNF since input matrix is ill conditioned for p-adic hnf algorithm.")562# We need more clever preconditioning?563# It is important to *not* just do the submatrix, since564# the whole rest of the algorithm will likely be very slow in565# weird cases where the det is large.566# E.g., matrix all of whose rows but 1 are multiplied by some567# fixed scalar n.568raise NotImplementedError("fallback to PARI!")569#H = W.hermite_form(algorithm='pari')570else:571H = W._hnf_mod(2*g)572573x = add_column(W, H, b.stack(matrix(1,1,[k*A[m-2,m-1] + l*A[m-1,m-1]])), proof)574Hprime = H.augment(x)575pivots = pivots_of_hnf_matrix(Hprime)576577Hprime, pivots = add_row(Hprime, A.matrix_from_rows([m-2]), pivots, include_zero_rows=False)578Hprime, pivots = add_row(Hprime, A.matrix_from_rows([m-1]), pivots, include_zero_rows=False)579H = Hprime.matrix_from_rows(range(m))580return H581582def interleave_matrices(A, B, cols1, cols2):583"""584INPUT:585A, B -- matrices with the same number of rows586cols1, cols2 -- disjoint lists of integers587OUTPUT:588construct a new matrix C by sticking the columns589of A at the positions specified by cols1 and the590columns of B at the positions specified by cols2.591592EXAMPLES:593sage: A = matrix(ZZ, 2, [1,2,3,4]); B = matrix(ZZ, 2, [-1,5,2,3])594sage: A595[1 2]596[3 4]597sage: B598[-1 5]599[ 2 3]600sage: import sage.matrix.matrix_integer_dense_hnf as hnf601sage: hnf.interleave_matrices(A, B, [1,3], [0,2])602[-1 1 5 2]603[ 2 3 3 4]604"""605D = A.augment(B)606w = cols1 + cols2607v = [w.index(i) for i in range(len(cols1) + len(cols2))]608return D.matrix_from_columns(v)609610def probable_pivot_rows(A):611"""612Return rows of A that are very likely to be pivots.613614This really finds the pivots of A modulo a random prime.615616INPUT:617A -- a matrix618OUTPUT:619a tuple of integers620621EXAMPLES:622sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf623sage: a = matrix(ZZ,3,[0, -1, -1, 0, -20, 1, 0, 1, 2])624sage: a625[ 0 -1 -1]626[ 0 -20 1]627[ 0 1 2]628sage: matrix_integer_dense_hnf.probable_pivot_rows(a)629(0, 1)630"""631return probable_pivot_columns(A.transpose())632633def probable_pivot_columns(A):634"""635INPUT:636A -- a matrix637OUTPUT:638a tuple of integers639640EXAMPLES:641sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf642sage: a = matrix(ZZ,3,[0, -1, -1, 0, -20, 1, 0, 1, 2])643sage: a644[ 0 -1 -1]645[ 0 -20 1]646[ 0 1 2]647sage: matrix_integer_dense_hnf.probable_pivot_columns(a)648(1, 2)649"""650p = ZZ.random_element(10007, 46000).next_prime()651pivots = A._reduce(p).pivots()652return pivots653654def ones(H, pivots):655"""656Find all 1 pivot columns of the matrix H in Hermite form, along657with the corresponding rows, and also the non 1 pivot columns and658non-pivot rows. Here a 1 pivot column is a pivot column so that659the leading bottom entry is 1.660661INPUT:662H -- matrix in Hermite form663pivots -- list of integers (all pivot positions of H).664665OUTPUT:6664-tuple of integer lists: onecol, onerow, non_oneol, non_onerow667668EXAMPLES:669sage: H = matrix(ZZ, 3, 5, [1, 0, 0, 45, -36, 0, 1, 0, 131, -107, 0, 0, 0, 178, -145]); H670[ 1 0 0 45 -36]671[ 0 1 0 131 -107]672[ 0 0 0 178 -145]673sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf674sage: matrix_integer_dense_hnf.ones(H, [0,1,3])675([0, 1], [0, 1], [2], [2])676"""677# Find the "onecol" pivot columns of H, i.e., the columns678# that contain exactly one "1" entry and all other entries 0.679onecol = []680onerow = []681i = 0682for c in pivots:683if H[i,c] == 1:684onecol.append(c)685onerow.append(i)686i += 1687onecol_set = set(onecol)688non_onerow = [i for i in range(len(pivots)) if i not in onerow]689non_onecol = [i for i in range(H.ncols()) if i not in onecol_set][:len(non_onerow)]690return onecol, onerow, non_onecol, non_onerow691692def extract_ones_data(H, pivots):693"""694Compute ones data and corresponding submatrices of H. This is695used to optimized the add_row function.696697INPUT:698H -- a matrix in HNF699pivots -- list of all pivot column positions of H700701OUTPUT:702C, D, E, onecol, onerow, non_onecol, non_onerow703where onecol, onerow, non_onecol, non_onerow are as for704the ones function, and C, D, E are matrices:705C -- submatrix of all non-onecol columns and onecol rows706D -- all non-onecol columns and other rows707E -- inverse of D708If D isn't invertible or there are 0 or more than 2 non onecols,709then C, D, and E are set to None.710711EXAMPLES:712sage: H = matrix(ZZ, 3, 4, [1, 0, 0, 7, 0, 1, 5, 2, 0, 0, 6, 6])713sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf714sage: matrix_integer_dense_hnf.extract_ones_data(H, [0,1,2])715(716[0]717[5], [6], [1/6], [0, 1], [0, 1], [2], [2]718)719720Here we get None's since the (2,2) position submatrix is not invertible.721sage: H = matrix(ZZ, 3, 5, [1, 0, 0, 45, -36, 0, 1, 0, 131, -107, 0, 0, 0, 178, -145]); H722[ 1 0 0 45 -36]723[ 0 1 0 131 -107]724[ 0 0 0 178 -145]725sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf726sage: matrix_integer_dense_hnf.extract_ones_data(H, [0,1,3])727(None, None, None, [0, 1], [0, 1], [2], [2])728"""729onecol, onerow, non_onecol, non_onerow = ones(H, pivots)730verbose('extract_ones -- got submatrix of size %s'%len(non_onecol))731if len(non_onecol) in [1,2]:732# Extract submatrix of all non-onecol columns and onecol rows733C = H.matrix_from_rows_and_columns(onerow, non_onecol)734# Extract submatrix of all non-onecol columns and other rows735D = H.matrix_from_rows_and_columns(non_onerow, non_onecol).transpose()736tt = verbose("extract ones -- INVERT %s x %s"%(len(non_onerow), len(non_onecol)), level=1)737try:738E = D**(-1)739except ZeroDivisionError:740C = D = E = None741verbose("done inverting", tt, level=1)742return C, D, E, onecol, onerow, non_onecol, non_onerow743else:744return None, None, None, onecol, onerow, non_onecol, non_onerow745746def is_in_hnf_form(H, pivots):747"""748Return True precisely if the matrix H is in Hermite normal form749with given pivot columns.750751INPUT:752H -- matrix753pivots -- sorted list of integers754755OUTPUT:756bool -- True or False757758EXAMPLES:759sage: a = matrix(ZZ,3,5,[-2, -6, -3, -17, -1, 2, -1, -1, -2, -1, -2, -2, -6, 9, 2])760sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf761sage: matrix_integer_dense_hnf.is_in_hnf_form(a,range(3))762False763sage: e = a.hermite_form(); p = a.pivots()764sage: matrix_integer_dense_hnf.is_in_hnf_form(e, p)765True766"""767tt = verbose('testing if matrix is in HNF')768r = 0769pivots_set = set(pivots)770for j in xrange(H.ncols()):771if j in pivots_set:772for i in xrange(r+1, H.nrows()):773if H[i,j]:774verbose('not HNF because nonzeros below pivot position',tt)775return False776for i in xrange(r):777if H[i,j] < 0 or H[i,j] >= H[r,j]:778verbose('not HNF because negative or too big above pivot position',tt)779return False780r += 1781else:782for i in xrange(r,H.nrows()):783if H[i,j]:784verbose('not HNF nonzero in wrong place in nonpivot column',tt)785return False786verbose('done verifying in HNF -- yes', tt)787return True788789def probable_hnf(A, include_zero_rows, proof):790"""791Return the HNF of A or raise an exception if something involving792the randomized nature of the algorithm goes wrong along the way.793Calling this function again a few times should result it in it794working, at least if proof=True.795796INPUT:797A -- a matrix798include_zero_rows -- bool799proof -- bool800801OUTPUT:802the Hermite normal form of A.803cols -- pivot columns804805EXAMPLES:806sage: a = matrix(ZZ,4,3,[-1, -1, -1, -20, 4, 1, -1, 1, 2,1,2,3])807sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf808sage: matrix_integer_dense_hnf.probable_hnf(a, True, True)809(810[1 0 0]811[0 1 0]812[0 0 1]813[0 0 0], [0, 1, 2]814)815sage: matrix_integer_dense_hnf.probable_hnf(a, False, True)816(817[1 0 0]818[0 1 0]819[0 0 1], [0, 1, 2]820)821sage: matrix_integer_dense_hnf.probable_hnf(a, False, False)822(823[1 0 0]824[0 1 0]825[0 0 1], [0, 1, 2]826)827"""828# Find left-most full rank submatrix by working modulo a prime829rows = list(probable_pivot_rows(A))830B = A.matrix_from_rows(rows)831cols = list(probable_pivot_columns(B))832C = B.matrix_from_columns(cols)833# Now C is a submatrix of A that has full rank and is square.834835# We compute the HNF of C, which is a square nonsingular matrix.836try:837H = hnf_square(C, proof=proof)838except NotImplementedError:839# raise840# this signals that we must fallback to pari841verbose("generic random modular HNF algorithm failed -- we fall back to PARI")842H = A.hermite_form(algorithm='pari', include_zero_rows=include_zero_rows, proof=proof)843return H, H.pivots()844845# The transformation matrix to HNF is the unique846# matrix U such that U * C = H, i.e., U = H*C^(-1).847848if len(cols) < B.ncols():849# We compute the HNF of B by multiplying the matrix D850# got from the columns not in C by U:851# We want to compute X = U*D. But U = H*C^(-1),852# so X = U*D = H*C^(-1)*D.853# So C*H^(-1)*X = D854855# find y s.t C*y = D856# H^(-1)*X = y ===> X = H*y857#858cols_set = set(cols)859cols2 = [i for i in range(B.ncols()) if not i in cols_set]860D = B.matrix_from_columns(cols2)861Y = C.solve_right(D)862H2 = H*Y863H2 = H2.change_ring(ZZ)864865# The HNF of B is got by assembling together866# the matrices H and H2.867H = interleave_matrices(H, H2, cols, cols2)868869pivots = pivots_of_hnf_matrix(H)870871# Now H is the HNF of the matrix B.872# Finally we add all remaining rows of A to H using873# the add_row function.874875C, D, E, onecol, onerow, non_onecol, non_onerow = extract_ones_data(H, cols)876if not proof and len(non_onecol) == 0:877# Identity matrix -- done878verbose("hnf -- got identity matrix -- early abort (0)")879if include_zero_rows: H = pad_zeros(H, A.nrows())880return H, pivots881882rows_set = set(rows)883for i in range(A.nrows()):884if not i in rows_set:885v = A.matrix_from_rows([i])886if v == 0: continue887if E is None:888H, pivots = add_row(H, v, pivots, include_zero_rows=False)889C, D, E, onecol, onerow, non_onecol, non_onerow = extract_ones_data(H, pivots)890if not proof and len(non_onecol) == 0:891# Identity matrix -- done892verbose("hnf -- got identity matrix -- early abort (1)")893if include_zero_rows: H = pad_zeros(H, A.nrows())894return H, pivots895else:896z = A.matrix_from_rows_and_columns([i], non_onecol)897w = A.matrix_from_rows_and_columns([i], onecol)898tt = verbose("checking denom (%s x %s)"%(D.nrows(), D.ncols()))899Y = (z - w*C).transpose()900k = E*Y901verbose("done checking denom",tt)902if k.denominator() != 1:903H, pivots = add_row(H, v, pivots, include_zero_rows=False)904D = H.matrix_from_rows_and_columns(non_onerow, non_onecol).transpose()905nn = ones(H, pivots)906if not proof and len(nn[2]) == 0:907verbose("hnf -- got identity matrix -- early abort (2)")908if include_zero_rows: H = pad_zeros(H, A.nrows())909return H, pivots910911if include_zero_rows: H = pad_zeros(H, A.nrows())912return H, pivots913914def pad_zeros(A, nrows):915"""916Add zeros to the bottom of A so that the917resulting matrix has nrows.918919INPUT:920A -- a matrix921nrows -- an integer that is at least as big as the number of rows of A.922923OUTPUT:924a matrix with nrows rows.925926EXAMPLES:927sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf928sage: a = matrix(ZZ, 2, 4, [1, 0, 0, 7, 0, 1, 5, 2])929sage: matrix_integer_dense_hnf.pad_zeros(a, 4)930[1 0 0 7]931[0 1 5 2]932[0 0 0 0]933[0 0 0 0]934sage: matrix_integer_dense_hnf.pad_zeros(a, 2)935[1 0 0 7]936[0 1 5 2]937"""938nz = nrows - A.nrows()939if nz == 0:940return A941if nz < 0:942return A.matrix_from_rows(range(nrows))943return A.stack(matrix(ZZ, nz, A.ncols()))944945946def hnf(A, include_zero_rows=True, proof=True):947"""948Return the Hermite Normal Form of a general integer matrix A,949along with the pivot columns.950951INPUT:952A -- an n x m matrix A over the integers.953include_zero_rows -- bool (default: True) whether or not to954include zero rows in the output matrix955proof -- whether or not to prove the result correct.956957OUTPUT:958matrix -- the Hermite normal form of A959pivots -- the pivot column positions of A960961EXAMPLES:962sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf963sage: a = matrix(ZZ,3,5,[-2, -6, -3, -17, -1, 2, -1, -1, -2, -1, -2, -2, -6, 9, 2])964sage: matrix_integer_dense_hnf.hnf(a)965(966[ 2 0 26 -75 -10]967[ 0 1 27 -73 -9]968[ 0 0 37 -106 -13], [0, 1, 2]969)970sage: matrix_integer_dense_hnf.hnf(a.transpose())971(972[1 0 0]973[0 1 0]974[0 0 1]975[0 0 0]976[0 0 0], [0, 1, 2]977)978sage: matrix_integer_dense_hnf.hnf(a.transpose(), include_zero_rows=False)979(980[1 0 0]981[0 1 0]982[0 0 1], [0, 1, 2]983)984"""985if A.nrows() <= 1:986np = A.nonzero_positions()987if len(np) == 0:988pivots = []989if not include_zero_rows:990A = A.new_matrix(0) # 0 rows991else:992i,j = np[0]993if A[i,j] < 0:994A = -A995pivots = [j]996return A, pivots997998if proof == False:999H, pivots = probable_hnf(A, include_zero_rows = include_zero_rows, proof=False)1000if not include_zero_rows and len(pivots) > H.nrows():1001return H.matrix_from_rows(range(len(pivots))), pivots10021003while True:1004try:1005H, pivots = probable_hnf(A, include_zero_rows = include_zero_rows, proof=True)1006except (AssertionError, ZeroDivisionError, TypeError):1007raise1008#verbose("Assertion occurred when computing HNF; guessed pivot columns likely wrong.")1009#continue1010else:1011if is_in_hnf_form(H, pivots):1012if not include_zero_rows and len(pivots) > H.nrows():1013H = H.matrix_from_rows(range(len(pivots)))1014return H, pivots1015verbose("After attempt the return matrix is not in HNF form since pivots must have been wrong. We try again.")10161017def hnf_with_transformation(A, proof=True):1018"""1019Compute the HNF H of A along with a transformation matrix U1020such that U*A = H. Also return the pivots of H.10211022INPUT:1023A -- an n x m matrix A over the integers.1024proof -- whether or not to prove the result correct.10251026OUTPUT:1027matrix -- the Hermite normal form H of A1028U -- a unimodular matrix such that U * A = H1029pivots -- the pivot column positions of A10301031EXAMPLES:1032sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf1033sage: A = matrix(ZZ, 2, [1, -5, -10, 1, 3, 197]); A1034[ 1 -5 -10]1035[ 1 3 197]1036sage: H, U, pivots = matrix_integer_dense_hnf.hnf_with_transformation(A)1037sage: H1038[ 1 3 197]1039[ 0 8 207]1040sage: U1041[ 0 1]1042[-1 1]1043sage: U*A1044[ 1 3 197]1045[ 0 8 207]1046"""1047# All we do is augment the input matrix with the identity matrix of the appropriate rank on the right.1048C = A.augment(identity_matrix(ZZ, A.nrows()))1049H, pivots = hnf(C, include_zero_rows=True, proof=proof)1050U = H.matrix_from_columns(range(A.ncols(), H.ncols()))1051H2 = H.matrix_from_columns(range(A.ncols()))1052return H2, U, pivots10531054def hnf_with_transformation_tests(n=10, m=5, trials=10):1055"""1056Use this to randomly test that hnf with transformation matrix1057is working.10581059EXAMPLES:1060sage: from sage.matrix.matrix_integer_dense_hnf import hnf_with_transformation_tests1061sage: hnf_with_transformation_tests(n=15,m=10, trials=10)10620 1 2 3 4 5 6 7 8 91063"""1064import sys1065for i in range(trials):1066print i,1067sys.stdout.flush()1068a = random_matrix(ZZ, n, m)1069w = hnf_with_transformation(a)1070assert w[0] == w[1]*a1071w = hnf_with_transformation(a, proof=False)1072assert w[0] == w[1]*a107310741075#################################################################################################1076# Code for testing and benchmarking1077#################################################################################################1078def benchmark_hnf(nrange, bits=4):1079"""1080Run benchmark program.10811082EXAMPLES:1083sage: import sage.matrix.matrix_integer_dense_hnf as hnf1084sage: hnf.benchmark_hnf([50,100],32)1085('sage', 50, 32, ...),1086('sage', 100, 32, ...),1087"""1088b = 2**bits1089for n in nrange:1090a = random_matrix(ZZ, n, x=-b,y=b)1091t = cputime()1092h,_ = hnf(a, proof=False)1093tm = cputime(t)1094print '%s,'%(('sage', n, bits, tm),)10951096def benchmark_magma_hnf(nrange, bits=4):1097"""1098EXAMPLES:1099sage: import sage.matrix.matrix_integer_dense_hnf as hnf1100sage: hnf.benchmark_magma_hnf([50,100],32) # optional - magma1101('magma', 50, 32, ...),1102('magma', 100, 32, ...),1103"""1104from sage.interfaces.all import magma1105b = 2**bits1106for n in nrange:1107a = magma('MatrixAlgebra(IntegerRing(),%s)![Random(%s,%s) : i in [1..%s]]'%(n,-b,b,n**2))1108t = magma.cputime()1109h = a.EchelonForm()1110tm = magma.cputime(t)1111print '%s,'%(('magma', n, bits, tm),)111211131114global sanity1115def sanity_checks(times=50, n=8, m=5, proof=True, stabilize=2, check_using_magma = True):1116"""1117Run random sanity checks on the modular p-adic HNF with tall and wide matrices1118both dense and sparse.11191120INPUT:1121times -- number of times to randomly try matrices with each shape1122n -- number of rows1123m -- number of columns1124proof -- test with proof true1125stabilize -- parameter to pass to hnf algorithm when proof is False1126check_using_magma -- if True use Magma instead of PARI to1127check correctness of computed HNF's.1128Since PARI's HNF is buggy and slow (as of11292008-02-16 non-pivot entries sometimes1130aren't normalized to be nonnegative) the1131default is Magma.11321133EXAMPLES:1134sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf1135sage: matrix_integer_dense_hnf.sanity_checks(times=5, check_using_magma=False)1136small 8 x 511370 1 2 3 4 (done)1138big 8 x 511390 1 2 3 4 (done)1140small 5 x 811410 1 2 3 4 (done)1142big 5 x 811430 1 2 3 4 (done)1144sparse 8 x 511450 1 2 3 4 (done)1146sparse 5 x 811470 1 2 3 4 (done)1148ill conditioned -- 1000*A -- 8 x 511490 1 2 3 4 (done)1150ill conditioned -- 1000*A but one row -- 8 x 511510 1 2 3 4 (done)1152"""1153import sys1154def __do_check(v):1155"""1156This is used internally by the sanity check code.1157"""1158for i,a in enumerate(v):1159global sanity1160sanity = a1161print i,1162sys.stdout.flush()1163if check_using_magma:1164if magma(hnf(a)[0]) != magma(a).EchelonForm():1165print "bug computing hnf of a matrix"1166print 'a = matrix(ZZ, %s, %s, %s)'%(a.nrows(), a.ncols(), a.list())1167return1168else:1169if hnf(a)[0] != a.echelon_form('pari'):1170print "bug computing hnf of a matrix"1171print 'a = matrix(ZZ, %s, %s, %s)'%(a.nrows(), a.ncols(), a.list())1172return1173print " (done)"11741175print "small %s x %s"%(n,m)1176__do_check([random_matrix(ZZ, n, m, x=-1,y=1) for _ in range(times)])1177print "big %s x %s"%(n,m)1178__do_check([random_matrix(ZZ, n, m, x=-2^32,y=2^32) for _ in range(times)])11791180print "small %s x %s"%(m,n)1181__do_check([random_matrix(ZZ, m, n, x=-1,y=1) for _ in range(times)])1182print "big %s x %s"%(m,n)1183__do_check([random_matrix(ZZ, m, n, x=-2^32,y=2^32) for _ in range(times)])11841185print "sparse %s x %s"%(n,m)1186__do_check([random_matrix(ZZ, n, m, density=0.1) for _ in range(times)])1187print "sparse %s x %s"%(m,n)1188__do_check([random_matrix(ZZ, m, n, density=0.1) for _ in range(times)])11891190print "ill conditioned -- 1000*A -- %s x %s"%(n,m)1191__do_check([1000*random_matrix(ZZ, n, m, x=-1,y=1) for _ in range(times)])11921193print "ill conditioned -- 1000*A but one row -- %s x %s"%(n,m)1194v = []1195for _ in range(times):1196a = 1000*random_matrix(ZZ, n, m, x=-1,y=1)1197a[a.nrows()-1] = a[a.nrows()-1]/10001198v.append(a)1199__do_check(v)1200120112021203120412051206