Path: blob/master/src/sage/matrix/matrix_integer_dense_hnf.py
8815 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, Integer, previous_prime, next_prime, CRT_list, RR1314def max_det_prime(n):15"""16Return the largest prime so that it is reasonably efficienct to17compute modulo that prime with n x n matrices in LinBox.1819INPUT:2021- ``n`` -- a positive integer2223OUTPUT:2425a prime number2627EXAMPLES::2829sage: from sage.matrix.matrix_integer_dense_hnf import max_det_prime30sage: max_det_prime(10000)31838859332sage: max_det_prime(1000)33838859334sage: max_det_prime(10)35838859336"""37# See #14032: LinBox now uses a constant bound of 2^23.38# This is the largest prime less than that bound.39return Integer(8388593)4041def det_from_modp_and_divisor(A, d, p, z_mod, moduli, z_so_far=ZZ(1), N_so_far=ZZ(1)):42"""43This is used for internal purposes for computing determinants44quickly (with the hybrid p-adic / multimodular algorithm).4546INPUT:47A -- a square matrix48d -- a divisor of the determinant of A49p -- a prime50z_mod -- values of det/d (mod ...)51moduli -- the moduli so far52z_so_far -- for a modulus p in the list moduli,53(z_so_far mod p) is the determinant of A modulo p.54N_so_far -- N_so_far is the product over the primes in the list moduli.5556OUTPUT:57A triple (det bound, new z_so_far, new N_so_far).5859EXAMPLES:60sage: a = matrix(ZZ, 3, [6, 1, 2, -56, -2, -1, -11, 2, -3])61sage: factor(a.det())62-1 * 13 * 2963sage: d = 1364sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf65sage: matrix_integer_dense_hnf.det_from_modp_and_divisor(a, d, 97, [], [])66(-377, -29, 97)67sage: a.det()68-37769"""70tm = verbose("Multimodular stage of det calculation -- using p = %s"%p, level=2)71z = A.mod(p).det() / d72z = z.lift()73z_mod.append(z)74moduli.append(p)75z = CRT_list([z_so_far, z], [N_so_far, p])76N = N_so_far*p7778if z > N//2:79z = z - N80verbose("Finished multimodular det for p = %s"%p, tm, level=2)81return (d * z, z, N)8283def det_given_divisor(A, d, proof=True, stabilize=2):84"""85Given a divisor d of the determinant of A, compute the86determinant of A.8788INPUT::8990- ``A`` -- a square integer matrix91- ``d`` -- a nonzero integer that is assumed to divide the determinant of A92- ``proof`` -- bool (default: True) compute det modulo enough primes93so that the determinant is computed provably correctly (via the94Hadamard bound). It would be VERY hard for ``det()`` to fail even95with proof=False.96- ``stabilize`` -- int (default: 2) if proof = False, then compute97the determinant modulo `p` until ``stabilize`` successive modulo98determinant computations stabilize.99100OUTPUT:101102integer -- determinant103104EXAMPLES::105106sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf107sage: a = matrix(ZZ,3,[-1, -1, -1, -20, 4, 1, -1, 1, 2])108sage: matrix_integer_dense_hnf.det_given_divisor(a, 3)109-30110sage: matrix_integer_dense_hnf.det_given_divisor(a, 3, proof=False)111-30112sage: matrix_integer_dense_hnf.det_given_divisor(a, 3, proof=False, stabilize=1)113-30114sage: a.det()115-30116117Here we illustrate proof=False giving a wrong answer::118119sage: p = matrix_integer_dense_hnf.max_det_prime(2)120sage: q = previous_prime(p)121sage: a = matrix(ZZ, 2, [p, 0, 0, q])122sage: p * q12370368442188091124sage: matrix_integer_dense_hnf.det_given_divisor(a, 1, proof=False, stabilize=2)1250126127This still works, because we don't work modulo primes that divide128the determinant bound, which is found using a p-adic algorithm::129130sage: a.det(proof=False, stabilize=2)131703684421880911321333 primes is enough::134135sage: matrix_integer_dense_hnf.det_given_divisor(a, 1, proof=False, stabilize=3)13670368442188091137sage: matrix_integer_dense_hnf.det_given_divisor(a, 1, proof=False, stabilize=5)13870368442188091139sage: matrix_integer_dense_hnf.det_given_divisor(a, 1, proof=True)14070368442188091141142TESTS::143144sage: m = diagonal_matrix(ZZ, 68, [2]*66 + [1,1])145sage: m.det()14673786976294838206464147"""148p = max_det_prime(A.nrows())149z_mod = []150moduli = []151assert d != 0152z_so_far = 1153N_so_far = 1154if proof:155N = 1156B = (2 * 10**A.hadamard_bound()) // d + 1157dd = d158# bad verbose statement, since computing the log overflows!159est = int(RR(B).log() / RR(p).log()) + 1160cnt = 1161verbose("Multimodular det -- need to use about %s primes."%est, level=1)162while N < B:163if d % p != 0:164tm = cputime()165dd, z_so_far, N_so_far = det_from_modp_and_divisor(A, d, p, z_mod, moduli, z_so_far, N_so_far)166N *= p167verbose("computed det mod p=%s which is %s (of about %s)"%(p, cnt, est), tm)168p = previous_prime(p)169cnt += 1170return dd171else:172val = []173while True:174if d % p != 0:175tm = cputime()176dd, z_so_far, N_so_far = det_from_modp_and_divisor(A, d, p, z_mod, moduli, z_so_far, N_so_far)177verbose("computed det mod %s"%p, tm)178val.append(dd)179if len(val) >= stabilize and len(set(val[-stabilize:])) == 1:180return val[-1]181p = previous_prime(p)182183def det_padic(A, proof=True, stabilize=2):184"""185Return the determinant of A, computed using a p-adic/multimodular186algorithm.187188INPUTS:189190- ``A`` -- a square matrix191192- ``proof`` -- boolean193194- ``stabilize`` (default: 2) -- if proof False, number of successive primes so that195CRT det must stabilize.196197EXAMPLES::198199sage: import sage.matrix.matrix_integer_dense_hnf as h200sage: a = matrix(ZZ, 3, [1..9])201sage: h.det_padic(a)2020203sage: a = matrix(ZZ, 3, [1,2,5,-7,8,10,192,5,18])204sage: h.det_padic(a)205-3669206sage: a.determinant(algorithm='ntl')207-3669208"""209if not A.is_square():210raise ValueError("A must be a square matrix")211r = A.rank()212if r < A.nrows():213return ZZ(0)214v = random_matrix(ZZ, A.nrows(), 1)215d = A._solve_right_nonsingular_square(v, check_rank=False).denominator()216return det_given_divisor(A, d, proof=proof, stabilize=stabilize)217218def double_det (A, b, c, proof):219"""220Compute the determinants of the stacked integer matrices221A.stack(b) and A.stack(c).222223INPUT:224A -- an (n-1) x n matrix225b -- an 1 x n matrix226c -- an 1 x n matrix227proof -- whether or not to compute the det modulo enough times228to provably compute the determinant.229230OUTPUT:231a pair of two integers.232233EXAMPLES:234sage: from sage.matrix.matrix_integer_dense_hnf import double_det235sage: A = matrix(ZZ, 2, 3, [1,2,3, 4,-2,5])236sage: b = matrix(ZZ, 1, 3, [1,-2,5])237sage: c = matrix(ZZ, 1, 3, [8,2,10])238sage: A.stack(b).det()239-48240sage: A.stack(c).det()24142242sage: double_det(A, b, c, False)243(-48, 42)244"""245# We use the "two for the price of one" algorithm, which I made up. (William Stein)246247# This is a clever trick! First we transpose everything. Then248# we use that if [A|b]*v = c then [A|c]*w = b with w easy to write down!249# In fact w is got from v by dividing all entries by -v[n], where n is the250# number of rows of v, and *also* dividing the last entry of w by v[n] again.251# See this as an algebra exercise where you have to think of matrix vector252# multiply as "linear combination of columns".253A = A.transpose()254b = b.transpose()255c = c.transpose()256t = verbose('starting double det')257B = A.augment(b)258v = B.solve_right(-c)259260db = det_given_divisor(B, v.denominator(), proof=proof)261262n = v.nrows()263vn = v[n-1,0]264w = (-1/vn)*v265w[n-1] = w[n-1]/vn266dc = det_given_divisor(A.augment(c), w.denominator(), proof=proof)267268verbose('finished double det', t)269270return (db, dc)271272def add_column_fallback(B, a, proof):273"""274Simplistic version of add_column, in case the powerful clever one275fails (e.g., B is singular).276277INPUT:278B -- a square matrix (may be singular)279a -- an n x 1 matrix, where B has n rows280proof -- bool; whether to prove result correct281282OUTPUT:283x -- a vector such that H' = H_B.augment(x) is the HNF of A = B.augment(a).284285EXAMPLES:286sage: B = matrix(ZZ,3, [-1, -1, 1, -3, 8, -2, -1, -1, -1])287sage: a = matrix(ZZ,3,1, [1,2,3])288sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf289sage: matrix_integer_dense_hnf.add_column_fallback(B, a, True)290[-3]291[-7]292[-2]293sage: matrix_integer_dense_hnf.add_column_fallback(B, a, False)294[-3]295[-7]296[-2]297sage: B.augment(a).hermite_form()298[ 1 1 1 -3]299[ 0 11 1 -7]300[ 0 0 2 -2]301"""302tt = verbose('add column fallback...')303W = B.augment(matrix(ZZ,B.nrows(),a.list()))304H, _ = hnf(W, proof)305C = H.matrix_from_columns([H.ncols()-1])306verbose('finished add column fallback', tt)307return C308309def solve_system_with_difficult_last_row(B, a):310"""311Solve B*x = a when the last row of $B$ contains huge entries using312a clever trick that reduces the problem to solve C*x = a where $C$313is $B$ but with the last row replaced by something small, along314with one easy null space computation. The latter are both solved315$p$-adically.316317INPUT:318B -- a square n x n nonsingular matrix with painful big bottom row.319a -- an n x 1 column matrix320OUTPUT:321the unique solution to B*x = a.322323EXAMPLES:324sage: from sage.matrix.matrix_integer_dense_hnf import solve_system_with_difficult_last_row325sage: B = matrix(ZZ, 3, [1,2,4, 3,-4,7, 939082,2930982,132902384098234])326sage: a = matrix(ZZ,3,1, [1,2,5])327sage: z = solve_system_with_difficult_last_row(B, a)328sage: z329[ 106321906985474/132902379815497]330[132902385037291/1329023798154970]331[ -5221794/664511899077485]332sage: B*z333[1]334[2]335[5]336"""337# Here's how:338# 1. We make a copy of B but with the last *nasty* row of B replaced339# by a random very nice row.340C = copy(B)341while True:342C[C.nrows()-1] = random_matrix(ZZ,1,C.ncols()).row(0)343# 2. Then we find the unique solution to C * x = a344try:345x = C.solve_right(a)346except ValueError:347verbose("Try difficult solve again with different random vector")348else:349break350351352# 3. We next delete the last row of B and find a basis vector k353# for the 1-dimensional kernel.354D = B.matrix_from_rows(range(C.nrows()-1))355N = D._rational_kernel_iml()356if N.ncols() != 1:357verbose("Try difficult solve again with different random vector")358return solve_system_with_difficult_last_row(B, a)359360k = N.matrix_from_columns([0])361362# 4. The sought for solution z to B*z = a is some linear combination363#364# z = x + alpha*k365#366# of x and k, where k is the above fixed basis for the kernel of D.367# Setting w to be the last row of B, this column vector z satisfies368#369# w * z = a'370#371# where a' is the last entry of a. Thus372#373# w * (x + alpha*k) = a'374#375# so w * x + alpha*w*k = a'376# so alpha*w*k = a' - w*x.377378w = B[-1] # last row of B379a_prime = a[-1]380lhs = w*k381rhs = a_prime - w * x382383if lhs[0] == 0:384verbose("Try difficult solve again with different random vector")385return solve_system_with_difficult_last_row(B, a)386387alpha = rhs[0] / lhs[0]388z = x + alpha*k389return z390391def add_column(B, H_B, a, proof):392"""393The add column procedure.394395INPUT:396B -- a square matrix (may be singular)397H_B -- the Hermite normal form of B398a -- an n x 1 matrix, where B has n rows399proof -- bool; whether to prove result correct, in case we use fallback method.400401OUTPUT:402x -- a vector such that H' = H_B.augment(x) is the HNF of A = B.augment(a).403404EXAMPLES:405sage: B = matrix(ZZ, 3, 3, [1,2,5, 0,-5,3, 1,1,2])406sage: H_B = B.echelon_form()407sage: a = matrix(ZZ, 3, 1, [1,8,-2])408sage: import sage.matrix.matrix_integer_dense_hnf as hnf409sage: x = hnf.add_column(B, H_B, a, True); x410[18]411[ 3]412[23]413sage: H_B.augment(x)414[ 1 0 17 18]415[ 0 1 3 3]416[ 0 0 18 23]417sage: B.augment(a).echelon_form()418[ 1 0 17 18]419[ 0 1 3 3]420[ 0 0 18 23]421"""422t0 = verbose('starting add_column')423424if B.rank() < B.nrows():425return add_column_fallback(B, a, proof)426else:427z = solve_system_with_difficult_last_row(B, a)428429zd, d = z._clear_denom()430x = H_B * zd431if d != 1:432for i in range(x.nrows()):433x[i,0] = x[i,0]/d434435return x436437def add_row(A, b, pivots, include_zero_rows):438"""439The add row procedure.440441INPUT:442A -- a matrix in Hermite normal form with n column443b -- an n x 1 row matrix444pivots -- sorted list of integers; the pivot positions of A.445446OUTPUT:447H -- the Hermite normal form of A.stack(b).448new_pivots -- the pivot columns of H.449450EXAMPLES:451sage: import sage.matrix.matrix_integer_dense_hnf as hnf452sage: A = matrix(ZZ, 2, 3, [-21, -7, 5, 1,20,-7])453sage: b = matrix(ZZ, 1,3, [-1,1,-1])454sage: hnf.add_row(A, b, A.pivots(), True)455(456[ 1 6 29]457[ 0 7 28]458[ 0 0 46], [0, 1, 2]459)460sage: A.stack(b).echelon_form()461[ 1 6 29]462[ 0 7 28]463[ 0 0 46]464"""465t = verbose('add hnf row')466v = b.row(0)467H, pivs = A._add_row_and_maintain_echelon_form(b.row(0), pivots)468if include_zero_rows and H.nrows() != A.nrows()+1:469H = H.matrix_from_rows(range(A.nrows()+1))470verbose('finished add hnf row', t)471return H, pivs472473def pivots_of_hnf_matrix(H):474"""475Return the pivot columns of a matrix H assumed to be in HNF.476477INPUT:478H -- a matrix that must be HNF479480OUTPUT:481list -- list of pivots482483EXAMPLES:484sage: H = matrix(ZZ, 3, 5, [1, 0, 0, 45, -36, 0, 1, 0, 131, -107, 0, 0, 0, 178, -145]); H485[ 1 0 0 45 -36]486[ 0 1 0 131 -107]487[ 0 0 0 178 -145]488sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf489sage: matrix_integer_dense_hnf.pivots_of_hnf_matrix(H)490[0, 1, 3]491"""492pivots = []493r = -1494for j in xrange(H.ncols()):495# Find first nonzero position (counting from bottom) in the j-th column496for i in reversed(xrange(H.nrows())):497if H[i,j]:498if i > r:499pivots.append(j)500r = i501elif i <= r:502break503return pivots504505def hnf_square(A, proof):506"""507INPUT:508a nonsingular n x n matrix A over the integers.509OUTPUT:510the Hermite normal form of A.511512EXAMPLES:513sage: import sage.matrix.matrix_integer_dense_hnf as hnf514sage: A = matrix(ZZ, 3, [-21, -7, 5, 1,20,-7, -1,1,-1])515sage: hnf.hnf_square(A, False)516[ 1 6 29]517[ 0 7 28]518[ 0 0 46]519sage: A.echelon_form()520[ 1 6 29]521[ 0 7 28]522[ 0 0 46]523"""524n = A.nrows()525m = A.ncols()526if n != m:527raise ValueError("A must be square.")528529# Small cases -- don't use this algorithm530if n <= 3:531return A.echelon_form(algorithm="pari")532533if A.rank() < A.nrows():534raise ValueError("matrix must have full rank")535536537538t = verbose("starting slicings")539B = A.matrix_from_rows(range(m-2)).matrix_from_columns(range(n-1))540c = A.matrix_from_rows([m-2]).matrix_from_columns (range(n-1))541d = A.matrix_from_rows([m-1]).matrix_from_columns (range(n-1))542b = A.matrix_from_columns([n-1]).matrix_from_rows(range(m-2))543verbose("done slicing", t)544545try:546(d1,d2) = double_det (B,c,d, proof=proof)547except (ValueError, ZeroDivisionError), msg:548d1 = B.stack(c).det(proof=proof)549d2 = B.stack(d).det(proof=proof)550551(g,k,l) = d1._xgcd (d2, minimal=True)552553W = B.stack (k*c + l*d)554verbose("submatrix det: g=%s"%g)555CUTOFF = 2**30556if g == 0:557# Big trouble -- matrix isn't invertible558# Since we have no good conditioning code at present,559# in this case we just fall back to using pari.560H = W.echelon_form(algorithm='pari')561elif 2*g > CUTOFF:562# Unlikely that g will be large on even slightly random input563# if it is, we fallback to the traditional algorithm.564# A nasty example is A = n*random_matrix(ZZ,m), where565# this algorithm gets killed. This is not random input though.566f = W.gcd()567g = g / (f**W.nrows())568if 2*g <= CUTOFF:569verbose("Found common factor of %s -- dividing out; get new g = %s"%(f,g))570W0 = (W/f).change_ring(ZZ)571H = W0._hnf_mod(2*g)572H *= f573else:574verbose("Falling back to PARI HNF since input matrix is ill conditioned for p-adic hnf algorithm.")575# We need more clever preconditioning?576# It is important to *not* just do the submatrix, since577# the whole rest of the algorithm will likely be very slow in578# weird cases where the det is large.579# E.g., matrix all of whose rows but 1 are multiplied by some580# fixed scalar n.581raise NotImplementedError("fallback to PARI!")582#H = W.hermite_form(algorithm='pari')583else:584H = W._hnf_mod(2*g)585586x = add_column(W, H, b.stack(matrix(1,1,[k*A[m-2,m-1] + l*A[m-1,m-1]])), proof)587Hprime = H.augment(x)588pivots = pivots_of_hnf_matrix(Hprime)589590Hprime, pivots = add_row(Hprime, A.matrix_from_rows([m-2]), pivots, include_zero_rows=False)591Hprime, pivots = add_row(Hprime, A.matrix_from_rows([m-1]), pivots, include_zero_rows=False)592H = Hprime.matrix_from_rows(range(m))593return H594595def interleave_matrices(A, B, cols1, cols2):596"""597INPUT:598A, B -- matrices with the same number of rows599cols1, cols2 -- disjoint lists of integers600OUTPUT:601construct a new matrix C by sticking the columns602of A at the positions specified by cols1 and the603columns of B at the positions specified by cols2.604605EXAMPLES:606sage: A = matrix(ZZ, 2, [1,2,3,4]); B = matrix(ZZ, 2, [-1,5,2,3])607sage: A608[1 2]609[3 4]610sage: B611[-1 5]612[ 2 3]613sage: import sage.matrix.matrix_integer_dense_hnf as hnf614sage: hnf.interleave_matrices(A, B, [1,3], [0,2])615[-1 1 5 2]616[ 2 3 3 4]617"""618D = A.augment(B)619w = cols1 + cols2620v = [w.index(i) for i in range(len(cols1) + len(cols2))]621return D.matrix_from_columns(v)622623def probable_pivot_rows(A):624"""625Return rows of A that are very likely to be pivots.626627This really finds the pivots of A modulo a random prime.628629INPUT:630A -- a matrix631OUTPUT:632a tuple of integers633634EXAMPLES:635sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf636sage: a = matrix(ZZ,3,[0, -1, -1, 0, -20, 1, 0, 1, 2])637sage: a638[ 0 -1 -1]639[ 0 -20 1]640[ 0 1 2]641sage: matrix_integer_dense_hnf.probable_pivot_rows(a)642(0, 1)643"""644return probable_pivot_columns(A.transpose())645646def probable_pivot_columns(A):647"""648INPUT:649A -- a matrix650OUTPUT:651a tuple of integers652653EXAMPLES:654sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf655sage: a = matrix(ZZ,3,[0, -1, -1, 0, -20, 1, 0, 1, 2])656sage: a657[ 0 -1 -1]658[ 0 -20 1]659[ 0 1 2]660sage: matrix_integer_dense_hnf.probable_pivot_columns(a)661(1, 2)662"""663p = ZZ.random_element(10007, 46000).next_prime()664pivots = A._reduce(p).pivots()665return pivots666667def ones(H, pivots):668"""669Find all 1 pivot columns of the matrix H in Hermite form, along670with the corresponding rows, and also the non 1 pivot columns and671non-pivot rows. Here a 1 pivot column is a pivot column so that672the leading bottom entry is 1.673674INPUT:675H -- matrix in Hermite form676pivots -- list of integers (all pivot positions of H).677678OUTPUT:6794-tuple of integer lists: onecol, onerow, non_oneol, non_onerow680681EXAMPLES:682sage: H = matrix(ZZ, 3, 5, [1, 0, 0, 45, -36, 0, 1, 0, 131, -107, 0, 0, 0, 178, -145]); H683[ 1 0 0 45 -36]684[ 0 1 0 131 -107]685[ 0 0 0 178 -145]686sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf687sage: matrix_integer_dense_hnf.ones(H, [0,1,3])688([0, 1], [0, 1], [2], [2])689"""690# Find the "onecol" pivot columns of H, i.e., the columns691# that contain exactly one "1" entry and all other entries 0.692onecol = []693onerow = []694i = 0695for c in pivots:696if H[i,c] == 1:697onecol.append(c)698onerow.append(i)699i += 1700onecol_set = set(onecol)701non_onerow = [i for i in range(len(pivots)) if i not in onerow]702non_onecol = [i for i in range(H.ncols()) if i not in onecol_set][:len(non_onerow)]703return onecol, onerow, non_onecol, non_onerow704705def extract_ones_data(H, pivots):706"""707Compute ones data and corresponding submatrices of H. This is708used to optimized the add_row function.709710INPUT:711H -- a matrix in HNF712pivots -- list of all pivot column positions of H713714OUTPUT:715C, D, E, onecol, onerow, non_onecol, non_onerow716where onecol, onerow, non_onecol, non_onerow are as for717the ones function, and C, D, E are matrices:718C -- submatrix of all non-onecol columns and onecol rows719D -- all non-onecol columns and other rows720E -- inverse of D721If D isn't invertible or there are 0 or more than 2 non onecols,722then C, D, and E are set to None.723724EXAMPLES:725sage: H = matrix(ZZ, 3, 4, [1, 0, 0, 7, 0, 1, 5, 2, 0, 0, 6, 6])726sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf727sage: matrix_integer_dense_hnf.extract_ones_data(H, [0,1,2])728(729[0]730[5], [6], [1/6], [0, 1], [0, 1], [2], [2]731)732733Here we get None's since the (2,2) position submatrix is not invertible.734sage: H = matrix(ZZ, 3, 5, [1, 0, 0, 45, -36, 0, 1, 0, 131, -107, 0, 0, 0, 178, -145]); H735[ 1 0 0 45 -36]736[ 0 1 0 131 -107]737[ 0 0 0 178 -145]738sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf739sage: matrix_integer_dense_hnf.extract_ones_data(H, [0,1,3])740(None, None, None, [0, 1], [0, 1], [2], [2])741"""742onecol, onerow, non_onecol, non_onerow = ones(H, pivots)743verbose('extract_ones -- got submatrix of size %s'%len(non_onecol))744if len(non_onecol) in [1,2]:745# Extract submatrix of all non-onecol columns and onecol rows746C = H.matrix_from_rows_and_columns(onerow, non_onecol)747# Extract submatrix of all non-onecol columns and other rows748D = H.matrix_from_rows_and_columns(non_onerow, non_onecol).transpose()749tt = verbose("extract ones -- INVERT %s x %s"%(len(non_onerow), len(non_onecol)), level=1)750try:751E = D**(-1)752except ZeroDivisionError:753C = D = E = None754verbose("done inverting", tt, level=1)755return C, D, E, onecol, onerow, non_onecol, non_onerow756else:757return None, None, None, onecol, onerow, non_onecol, non_onerow758759def is_in_hnf_form(H, pivots):760"""761Return True precisely if the matrix H is in Hermite normal form762with given pivot columns.763764INPUT:765H -- matrix766pivots -- sorted list of integers767768OUTPUT:769bool -- True or False770771EXAMPLES:772sage: a = matrix(ZZ,3,5,[-2, -6, -3, -17, -1, 2, -1, -1, -2, -1, -2, -2, -6, 9, 2])773sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf774sage: matrix_integer_dense_hnf.is_in_hnf_form(a,range(3))775False776sage: e = a.hermite_form(); p = a.pivots()777sage: matrix_integer_dense_hnf.is_in_hnf_form(e, p)778True779"""780tt = verbose('testing if matrix is in HNF')781r = 0782pivots_set = set(pivots)783for j in xrange(H.ncols()):784if j in pivots_set:785for i in xrange(r+1, H.nrows()):786if H[i,j]:787verbose('not HNF because nonzeros below pivot position',tt)788return False789for i in xrange(r):790if H[i,j] < 0 or H[i,j] >= H[r,j]:791verbose('not HNF because negative or too big above pivot position',tt)792return False793r += 1794else:795for i in xrange(r,H.nrows()):796if H[i,j]:797verbose('not HNF nonzero in wrong place in nonpivot column',tt)798return False799verbose('done verifying in HNF -- yes', tt)800return True801802def probable_hnf(A, include_zero_rows, proof):803"""804Return the HNF of A or raise an exception if something involving805the randomized nature of the algorithm goes wrong along the way.806Calling this function again a few times should result it in it807working, at least if proof=True.808809INPUT:810A -- a matrix811include_zero_rows -- bool812proof -- bool813814OUTPUT:815the Hermite normal form of A.816cols -- pivot columns817818EXAMPLES:819sage: a = matrix(ZZ,4,3,[-1, -1, -1, -20, 4, 1, -1, 1, 2,1,2,3])820sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf821sage: matrix_integer_dense_hnf.probable_hnf(a, True, True)822(823[1 0 0]824[0 1 0]825[0 0 1]826[0 0 0], [0, 1, 2]827)828sage: matrix_integer_dense_hnf.probable_hnf(a, False, True)829(830[1 0 0]831[0 1 0]832[0 0 1], [0, 1, 2]833)834sage: matrix_integer_dense_hnf.probable_hnf(a, False, False)835(836[1 0 0]837[0 1 0]838[0 0 1], [0, 1, 2]839)840"""841# Find left-most full rank submatrix by working modulo a prime842rows = list(probable_pivot_rows(A))843B = A.matrix_from_rows(rows)844cols = list(probable_pivot_columns(B))845C = B.matrix_from_columns(cols)846# Now C is a submatrix of A that has full rank and is square.847848# We compute the HNF of C, which is a square nonsingular matrix.849try:850H = hnf_square(C, proof=proof)851except NotImplementedError:852# raise853# this signals that we must fallback to pari854verbose("generic random modular HNF algorithm failed -- we fall back to PARI")855H = A.hermite_form(algorithm='pari', include_zero_rows=include_zero_rows, proof=proof)856return H, H.pivots()857858# The transformation matrix to HNF is the unique859# matrix U such that U * C = H, i.e., U = H*C^(-1).860861if len(cols) < B.ncols():862# We compute the HNF of B by multiplying the matrix D863# got from the columns not in C by U:864# We want to compute X = U*D. But U = H*C^(-1),865# so X = U*D = H*C^(-1)*D.866# So C*H^(-1)*X = D867868# find y s.t C*y = D869# H^(-1)*X = y ===> X = H*y870#871cols_set = set(cols)872cols2 = [i for i in range(B.ncols()) if not i in cols_set]873D = B.matrix_from_columns(cols2)874Y = C.solve_right(D)875H2 = H*Y876H2 = H2.change_ring(ZZ)877878# The HNF of B is got by assembling together879# the matrices H and H2.880H = interleave_matrices(H, H2, cols, cols2)881882pivots = pivots_of_hnf_matrix(H)883884# Now H is the HNF of the matrix B.885# Finally we add all remaining rows of A to H using886# the add_row function.887888C, D, E, onecol, onerow, non_onecol, non_onerow = extract_ones_data(H, cols)889if not proof and len(non_onecol) == 0:890# Identity matrix -- done891verbose("hnf -- got identity matrix -- early abort (0)")892if include_zero_rows: H = pad_zeros(H, A.nrows())893return H, pivots894895rows_set = set(rows)896for i in range(A.nrows()):897if not i in rows_set:898v = A.matrix_from_rows([i])899if v == 0: continue900if E is None:901H, pivots = add_row(H, v, pivots, include_zero_rows=False)902C, D, E, onecol, onerow, non_onecol, non_onerow = extract_ones_data(H, pivots)903if not proof and len(non_onecol) == 0:904# Identity matrix -- done905verbose("hnf -- got identity matrix -- early abort (1)")906if include_zero_rows: H = pad_zeros(H, A.nrows())907return H, pivots908else:909z = A.matrix_from_rows_and_columns([i], non_onecol)910w = A.matrix_from_rows_and_columns([i], onecol)911tt = verbose("checking denom (%s x %s)"%(D.nrows(), D.ncols()))912Y = (z - w*C).transpose()913k = E*Y914verbose("done checking denom",tt)915if k.denominator() != 1:916H, pivots = add_row(H, v, pivots, include_zero_rows=False)917D = H.matrix_from_rows_and_columns(non_onerow, non_onecol).transpose()918nn = ones(H, pivots)919if not proof and len(nn[2]) == 0:920verbose("hnf -- got identity matrix -- early abort (2)")921if include_zero_rows: H = pad_zeros(H, A.nrows())922return H, pivots923924if include_zero_rows: H = pad_zeros(H, A.nrows())925return H, pivots926927def pad_zeros(A, nrows):928"""929Add zeros to the bottom of A so that the930resulting matrix has nrows.931932INPUT:933A -- a matrix934nrows -- an integer that is at least as big as the number of rows of A.935936OUTPUT:937a matrix with nrows rows.938939EXAMPLES:940sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf941sage: a = matrix(ZZ, 2, 4, [1, 0, 0, 7, 0, 1, 5, 2])942sage: matrix_integer_dense_hnf.pad_zeros(a, 4)943[1 0 0 7]944[0 1 5 2]945[0 0 0 0]946[0 0 0 0]947sage: matrix_integer_dense_hnf.pad_zeros(a, 2)948[1 0 0 7]949[0 1 5 2]950"""951nz = nrows - A.nrows()952if nz == 0:953return A954if nz < 0:955return A.matrix_from_rows(range(nrows))956return A.stack(matrix(ZZ, nz, A.ncols()))957958959def hnf(A, include_zero_rows=True, proof=True):960"""961Return the Hermite Normal Form of a general integer matrix A,962along with the pivot columns.963964INPUT:965A -- an n x m matrix A over the integers.966include_zero_rows -- bool (default: True) whether or not to967include zero rows in the output matrix968proof -- whether or not to prove the result correct.969970OUTPUT:971matrix -- the Hermite normal form of A972pivots -- the pivot column positions of A973974EXAMPLES:975sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf976sage: a = matrix(ZZ,3,5,[-2, -6, -3, -17, -1, 2, -1, -1, -2, -1, -2, -2, -6, 9, 2])977sage: matrix_integer_dense_hnf.hnf(a)978(979[ 2 0 26 -75 -10]980[ 0 1 27 -73 -9]981[ 0 0 37 -106 -13], [0, 1, 2]982)983sage: matrix_integer_dense_hnf.hnf(a.transpose())984(985[1 0 0]986[0 1 0]987[0 0 1]988[0 0 0]989[0 0 0], [0, 1, 2]990)991sage: matrix_integer_dense_hnf.hnf(a.transpose(), include_zero_rows=False)992(993[1 0 0]994[0 1 0]995[0 0 1], [0, 1, 2]996)997"""998if A.nrows() <= 1:999np = A.nonzero_positions()1000if len(np) == 0:1001pivots = []1002if not include_zero_rows:1003A = A.new_matrix(0) # 0 rows1004else:1005i,j = np[0]1006if A[i,j] < 0:1007A = -A1008pivots = [j]1009return A, pivots10101011if proof == False:1012H, pivots = probable_hnf(A, include_zero_rows = include_zero_rows, proof=False)1013if not include_zero_rows and len(pivots) > H.nrows():1014return H.matrix_from_rows(range(len(pivots))), pivots10151016while True:1017try:1018H, pivots = probable_hnf(A, include_zero_rows = include_zero_rows, proof=True)1019except (AssertionError, ZeroDivisionError, TypeError):1020raise1021#verbose("Assertion occurred when computing HNF; guessed pivot columns likely wrong.")1022#continue1023else:1024if is_in_hnf_form(H, pivots):1025if not include_zero_rows and len(pivots) > H.nrows():1026H = H.matrix_from_rows(range(len(pivots)))1027return H, pivots1028verbose("After attempt the return matrix is not in HNF form since pivots must have been wrong. We try again.")10291030def hnf_with_transformation(A, proof=True):1031"""1032Compute the HNF H of A along with a transformation matrix U1033such that U*A = H. Also return the pivots of H.10341035INPUT:1036A -- an n x m matrix A over the integers.1037proof -- whether or not to prove the result correct.10381039OUTPUT:1040matrix -- the Hermite normal form H of A1041U -- a unimodular matrix such that U * A = H1042pivots -- the pivot column positions of A10431044EXAMPLES:1045sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf1046sage: A = matrix(ZZ, 2, [1, -5, -10, 1, 3, 197]); A1047[ 1 -5 -10]1048[ 1 3 197]1049sage: H, U, pivots = matrix_integer_dense_hnf.hnf_with_transformation(A)1050sage: H1051[ 1 3 197]1052[ 0 8 207]1053sage: U1054[ 0 1]1055[-1 1]1056sage: U*A1057[ 1 3 197]1058[ 0 8 207]1059"""1060# All we do is augment the input matrix with the identity matrix of the appropriate rank on the right.1061C = A.augment(identity_matrix(ZZ, A.nrows()))1062H, pivots = hnf(C, include_zero_rows=True, proof=proof)1063U = H.matrix_from_columns(range(A.ncols(), H.ncols()))1064H2 = H.matrix_from_columns(range(A.ncols()))1065return H2, U, pivots10661067def hnf_with_transformation_tests(n=10, m=5, trials=10):1068"""1069Use this to randomly test that hnf with transformation matrix1070is working.10711072EXAMPLES:1073sage: from sage.matrix.matrix_integer_dense_hnf import hnf_with_transformation_tests1074sage: hnf_with_transformation_tests(n=15,m=10, trials=10)10750 1 2 3 4 5 6 7 8 91076"""1077import sys1078for i in range(trials):1079print i,1080sys.stdout.flush()1081a = random_matrix(ZZ, n, m)1082w = hnf_with_transformation(a)1083assert w[0] == w[1]*a1084w = hnf_with_transformation(a, proof=False)1085assert w[0] == w[1]*a108610871088#################################################################################################1089# Code for testing and benchmarking1090#################################################################################################1091def benchmark_hnf(nrange, bits=4):1092"""1093Run benchmark program.10941095EXAMPLES:1096sage: import sage.matrix.matrix_integer_dense_hnf as hnf1097sage: hnf.benchmark_hnf([50,100],32)1098('sage', 50, 32, ...),1099('sage', 100, 32, ...),1100"""1101b = 2**bits1102for n in nrange:1103a = random_matrix(ZZ, n, x=-b,y=b)1104t = cputime()1105h,_ = hnf(a, proof=False)1106tm = cputime(t)1107print '%s,'%(('sage', n, bits, tm),)11081109def benchmark_magma_hnf(nrange, bits=4):1110"""1111EXAMPLES:1112sage: import sage.matrix.matrix_integer_dense_hnf as hnf1113sage: hnf.benchmark_magma_hnf([50,100],32) # optional - magma1114('magma', 50, 32, ...),1115('magma', 100, 32, ...),1116"""1117from sage.interfaces.all import magma1118b = 2**bits1119for n in nrange:1120a = magma('MatrixAlgebra(IntegerRing(),%s)![Random(%s,%s) : i in [1..%s]]'%(n,-b,b,n**2))1121t = magma.cputime()1122h = a.EchelonForm()1123tm = magma.cputime(t)1124print '%s,'%(('magma', n, bits, tm),)112511261127global sanity1128def sanity_checks(times=50, n=8, m=5, proof=True, stabilize=2, check_using_magma = True):1129"""1130Run random sanity checks on the modular p-adic HNF with tall and wide matrices1131both dense and sparse.11321133INPUT:1134times -- number of times to randomly try matrices with each shape1135n -- number of rows1136m -- number of columns1137proof -- test with proof true1138stabilize -- parameter to pass to hnf algorithm when proof is False1139check_using_magma -- if True use Magma instead of PARI to1140check correctness of computed HNF's.1141Since PARI's HNF is buggy and slow (as of11422008-02-16 non-pivot entries sometimes1143aren't normalized to be nonnegative) the1144default is Magma.11451146EXAMPLES:1147sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf1148sage: matrix_integer_dense_hnf.sanity_checks(times=5, check_using_magma=False)1149small 8 x 511500 1 2 3 4 (done)1151big 8 x 511520 1 2 3 4 (done)1153small 5 x 811540 1 2 3 4 (done)1155big 5 x 811560 1 2 3 4 (done)1157sparse 8 x 511580 1 2 3 4 (done)1159sparse 5 x 811600 1 2 3 4 (done)1161ill conditioned -- 1000*A -- 8 x 511620 1 2 3 4 (done)1163ill conditioned -- 1000*A but one row -- 8 x 511640 1 2 3 4 (done)1165"""1166import sys1167def __do_check(v):1168"""1169This is used internally by the sanity check code.1170"""1171for i,a in enumerate(v):1172global sanity1173sanity = a1174print i,1175sys.stdout.flush()1176if check_using_magma:1177if magma(hnf(a)[0]) != magma(a).EchelonForm():1178print "bug computing hnf of a matrix"1179print 'a = matrix(ZZ, %s, %s, %s)'%(a.nrows(), a.ncols(), a.list())1180return1181else:1182if hnf(a)[0] != a.echelon_form('pari'):1183print "bug computing hnf of a matrix"1184print 'a = matrix(ZZ, %s, %s, %s)'%(a.nrows(), a.ncols(), a.list())1185return1186print " (done)"11871188print "small %s x %s"%(n,m)1189__do_check([random_matrix(ZZ, n, m, x=-1,y=1) for _ in range(times)])1190print "big %s x %s"%(n,m)1191__do_check([random_matrix(ZZ, n, m, x=-2^32,y=2^32) for _ in range(times)])11921193print "small %s x %s"%(m,n)1194__do_check([random_matrix(ZZ, m, n, x=-1,y=1) for _ in range(times)])1195print "big %s x %s"%(m,n)1196__do_check([random_matrix(ZZ, m, n, x=-2^32,y=2^32) for _ in range(times)])11971198print "sparse %s x %s"%(n,m)1199__do_check([random_matrix(ZZ, n, m, density=0.1) for _ in range(times)])1200print "sparse %s x %s"%(m,n)1201__do_check([random_matrix(ZZ, m, n, density=0.1) for _ in range(times)])12021203print "ill conditioned -- 1000*A -- %s x %s"%(n,m)1204__do_check([1000*random_matrix(ZZ, n, m, x=-1,y=1) for _ in range(times)])12051206print "ill conditioned -- 1000*A but one row -- %s x %s"%(n,m)1207v = []1208for _ in range(times):1209a = 1000*random_matrix(ZZ, n, m, x=-1,y=1)1210a[a.nrows()-1] = a[a.nrows()-1]/10001211v.append(a)1212__do_check(v)1213121412151216121712181219