Path: blob/master/sage/matrix/matrix_integer_dense_saturation.py
4046 views
"""1Saturation over ZZ2"""34from sage.rings.all import ZZ, gcd, binomial, GF5from sage.matrix.constructor import identity_matrix, random_matrix6from sage.misc.misc import verbose7from sage.misc.randstate import current_randstate8import matrix_integer_dense_hnf9from copy import copy101112def p_saturation(A, p, proof=True):13"""14INPUT:15A -- a matrix over ZZ16p -- a prime17proof -- bool (default: True)1819OUTPUT:20The p-saturation of the matrix A, i.e., a new matrix in Hermite form21whose row span a ZZ-module that is p-saturated.2223EXAMPLES:24sage: from sage.matrix.matrix_integer_dense_saturation import p_saturation25sage: A = matrix(ZZ, 2, 2, [3,2,3,4]); B = matrix(ZZ, 2,3,[1,2,3,4,5,6])26sage: A.det()27628sage: C = A*B; C29[11 16 21]30[19 26 33]31sage: C2 = p_saturation(C, 2); C232[ 1 8 15]33[ 0 9 18]34sage: C2.index_in_saturation()35936sage: C3 = p_saturation(C, 3); C337[ 1 0 -1]38[ 0 2 4]39sage: C3.index_in_saturation()40241"""42tm = verbose("%s-saturating a %sx%s matrix"%(p, A.nrows(), A.ncols()))43H = A.hermite_form(include_zero_rows=False, proof=proof)44while True:45if p == 2:46A = H.change_ring(GF(p))47else:48try:49# Faster than change_ring50A = H._reduce(p)51except OverflowError:52# fall back to generic GF(p) matrices53A = H.change_ring(GF(p))54assert A.nrows() <= A.ncols()55K = A.kernel()56if K.dimension() == 0:57verbose("done saturating", tm)58return H59B = K.basis_matrix().lift()60C = ((B * H) / p).change_ring(ZZ)61H = H.stack(C).hermite_form(include_zero_rows=False, proof=proof)62verbose("done saturating", tm)6364def random_sublist_of_size(k, n):65"""66INPUT:67k -- an integer68n -- an integer6970OUTPUT:71a randomly chosen sublist of range(k) of size n.7273EXAMPLES:74sage: import sage.matrix.matrix_integer_dense_saturation as s75sage: s.random_sublist_of_size(10,3)76[0, 1, 5]77sage: s.random_sublist_of_size(10,7)78[0, 1, 3, 4, 5, 7, 8]79"""80if n > k:81raise ValueError, "n must be <= len(v)"82if n == k:83return range(k)84if n >= k//2+5:85# use complement86w = random_sublist_of_size(k, k-n)87m = set(w)88w = [z for z in xrange(k) if z not in m]89assert(len(w)) == n90return w9192randrange = current_randstate().python_random().randrange9394w = set([])95while len(w) < n:96z = randrange(k)97if not z in w:98w.add(z)99w = list(w)100w.sort()101return w102103def solve_system_with_difficult_last_row(B, A):104"""105Solve the matrix equation B*Z = A when the last row of $B$106contains huge entries.107108INPUT:109B -- a square n x n nonsingular matrix with painful big bottom row.110A -- an n x k matrix.111OUTPUT:112the unique solution to B*Z = A.113114EXAMPLES:115sage: from sage.matrix.matrix_integer_dense_saturation import solve_system_with_difficult_last_row116sage: B = matrix(ZZ, 3, [1,2,3, 3,-1,2,939239082,39202803080,2939028038402834]); A = matrix(ZZ,3,2,[1,2,4,3,-1,0])117sage: X = solve_system_with_difficult_last_row(B, A); X118[ 290668794698843/226075992027744 468068726971/409557956572]119[-226078357385539/1582531944194208 1228691305937/2866905696004]120[ 2365357795/1582531944194208 -17436221/2866905696004]121sage: B*X == A122True123"""124# See the comments in the function of the same name in matrix_integer_dense_hnf.py.125# This function is just a generalization of that one to A a matrix.126C = copy(B)127while True:128C[C.nrows()-1] = random_matrix(ZZ,1,C.ncols()).row(0)129try:130X = C.solve_right(A)131except ValueError:132verbose("Try difficult solve again with different random vector")133else:134break135D = B.matrix_from_rows(range(C.nrows()-1))136N = D._rational_kernel_iml()137if N.ncols() != 1:138verbose("Difficult solve quickly failed. Using direct approach.")139return B.solve_right(A)140141tm = verbose("Recover correct linear combinations")142k = N.matrix_from_columns([0])143144# The sought for solution Z to B*Z = A is some linear combination145# Z = X + alpha*k146# Let w be the last row of B; then Z satisfies147# w * Z = A'148# where A' is the last row of A. Thus149# w * (X + alpha*k) = A'150# so w * X + alpha*w*k = A'151# so alpha*w*k = A' - w*X.152w = B[-1] # last row of B153A_prime = A[-1] # last row of A154lhs = w*k155rhs = A_prime - w * X156157if lhs[0] == 0:158verbose("Difficult solve quickly failed. Using direct approach.")159return B.solve_right(A)160161for i in range(X.ncols()):162alpha = rhs[i] / lhs[0]163X.set_column(i, (X.matrix_from_columns([i]) + alpha*k).list())164verbose("Done getting linear combinations.", tm)165return X166167def saturation(A, proof=True, p=0, max_dets=5):168"""169Compute a saturation matrix of A.170171INPUT:172A -- a matrix over ZZ173proof -- bool (default: True)174p -- int (default: 0); if not 0175only guarantees that output is p-saturated176max_dets -- int (default: 4) max number of dets of177submatrices to compute.178179OUTPUT:180matrix -- saturation of the matrix A.181182EXAMPLES:183sage: from sage.matrix.matrix_integer_dense_saturation import saturation184sage: A = matrix(ZZ, 2, 2, [3,2,3,4]); B = matrix(ZZ, 2,3,[1,2,3,4,5,6]); C = A*B185sage: C186[11 16 21]187[19 26 33]188sage: C.index_in_saturation()18918190sage: S = saturation(C); S191[11 16 21]192[-2 -3 -4]193sage: S.index_in_saturation()1941195sage: saturation(C, proof=False)196[11 16 21]197[-2 -3 -4]198sage: saturation(C, p=2)199[11 16 21]200[-2 -3 -4]201sage: saturation(C, p=2, max_dets=1)202[11 16 21]203[-2 -3 -4]204"""205# Find a submatrix of full rank and instead saturate that matrix.206r = A.rank()207if A.is_square() and r == A.nrows():208return identity_matrix(ZZ, r)209if A.nrows() > r:210P = []211while len(P) < r:212P = matrix_integer_dense_hnf.probable_pivot_rows(A)213A = A.matrix_from_rows(P)214215# Factor out all common factors from all rows, just in case.216A = copy(A)217A._factor_out_common_factors_from_each_row()218219if A.nrows() <= 1:220return A221222A, zero_cols = A._delete_zero_columns()223224if max_dets > 0:225# Take the GCD of at most num_dets randomly chosen determinants.226nr = A.nrows(); nc = A.ncols()227d = 0228trials = min(binomial(nc, nr), max_dets)229already_tried = []230while len(already_tried) < trials:231v = random_sublist_of_size(nc, nr)232tm = verbose('saturation -- checking det condition on submatrix')233d = gcd(d, A.matrix_from_columns(v).determinant(proof=proof))234verbose('saturation -- got det down to %s'%d, tm)235if gcd(d, p) == 1:236return A._insert_zero_columns(zero_cols)237already_tried.append(v)238239if gcd(d, p) == 1:240# already p-saturated241return A._insert_zero_columns(zero_cols)242243# Factor and p-saturate at each p.244# This is not a good algorithm, because all the HNF's in it are really slow!245#246#tm = verbose('factoring gcd %s of determinants'%d)247#limit = 2**31-1248#F = d.factor(limit = limit)249#D = [p for p, e in F if p <= limit]250#B = [n for n, e in F if n > limit] # all big factors -- there will only be at most one251#assert len(B) <= 1252#C = B[0]253#for p in D:254# A = p_saturation(A, p=p, proof=proof)255256# This is a really simple but powerful algorithm.257# FACT: If A is a matrix of full rank, then hnf(transpose(A))^(-1)*A is a saturation of A.258# To make this practical we use solve_system_with_difficult_last_row, since the259# last column of HNF's are typically the only really big ones.260B = A.transpose().hermite_form(include_zero_rows=False, proof=proof)261B = B.transpose()262263# Now compute B^(-1) * A264C = solve_system_with_difficult_last_row(B, A)265return C.change_ring(ZZ)._insert_zero_columns(zero_cols)266267def index_in_saturation(A, proof=True):268r"""269The index of A in its saturation.270271INPUT::272273- ``A`` -- matrix over `\ZZ`274275- ``proof`` -- boolean (``True`` or ``False``)276277OUTPUT:278279An integer280281EXAMPLES::282283sage: from sage.matrix.matrix_integer_dense_saturation import index_in_saturation284sage: A = matrix(ZZ, 2, 2, [3,2,3,4]); B = matrix(ZZ, 2,3,[1,2,3,4,5,6]); C = A*B; C285[11 16 21]286[19 26 33]287sage: index_in_saturation(C)28818289sage: W = C.row_space()290sage: S = W.saturation()291sage: W.index_in(S)29218293294For any zero matrix the index in its saturation is 1 (see :trac:`13034`)::295296sage: m = matrix(ZZ, 3)297sage: m298[0 0 0]299[0 0 0]300[0 0 0]301sage: m.index_in_saturation()3021303sage: m = matrix(ZZ, 2, 3)304sage: m305[0 0 0]306[0 0 0]307sage: m.index_in_saturation()3081309310TESTS::311312sage: zero = matrix(ZZ, [[]])313sage: zero.index_in_saturation()3141315"""316r = A.rank()317if r == 0:318return ZZ(1)319if r < A.nrows():320A = A.hermite_form(proof=proof, include_zero_rows=False)321if A.is_square():322return abs(A.determinant(proof=proof))323A = A.transpose()324A = A.hermite_form(proof=proof,include_zero_rows=False)325return abs(A.determinant(proof=proof))326327328329330