Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/matrix/matrix_integer_dense_saturation.py
4046 views
1
"""
2
Saturation over ZZ
3
"""
4
5
from sage.rings.all import ZZ, gcd, binomial, GF
6
from sage.matrix.constructor import identity_matrix, random_matrix
7
from sage.misc.misc import verbose
8
from sage.misc.randstate import current_randstate
9
import matrix_integer_dense_hnf
10
from copy import copy
11
12
13
def p_saturation(A, p, proof=True):
14
"""
15
INPUT:
16
A -- a matrix over ZZ
17
p -- a prime
18
proof -- bool (default: True)
19
20
OUTPUT:
21
The p-saturation of the matrix A, i.e., a new matrix in Hermite form
22
whose row span a ZZ-module that is p-saturated.
23
24
EXAMPLES:
25
sage: from sage.matrix.matrix_integer_dense_saturation import p_saturation
26
sage: A = matrix(ZZ, 2, 2, [3,2,3,4]); B = matrix(ZZ, 2,3,[1,2,3,4,5,6])
27
sage: A.det()
28
6
29
sage: C = A*B; C
30
[11 16 21]
31
[19 26 33]
32
sage: C2 = p_saturation(C, 2); C2
33
[ 1 8 15]
34
[ 0 9 18]
35
sage: C2.index_in_saturation()
36
9
37
sage: C3 = p_saturation(C, 3); C3
38
[ 1 0 -1]
39
[ 0 2 4]
40
sage: C3.index_in_saturation()
41
2
42
"""
43
tm = verbose("%s-saturating a %sx%s matrix"%(p, A.nrows(), A.ncols()))
44
H = A.hermite_form(include_zero_rows=False, proof=proof)
45
while True:
46
if p == 2:
47
A = H.change_ring(GF(p))
48
else:
49
try:
50
# Faster than change_ring
51
A = H._reduce(p)
52
except OverflowError:
53
# fall back to generic GF(p) matrices
54
A = H.change_ring(GF(p))
55
assert A.nrows() <= A.ncols()
56
K = A.kernel()
57
if K.dimension() == 0:
58
verbose("done saturating", tm)
59
return H
60
B = K.basis_matrix().lift()
61
C = ((B * H) / p).change_ring(ZZ)
62
H = H.stack(C).hermite_form(include_zero_rows=False, proof=proof)
63
verbose("done saturating", tm)
64
65
def random_sublist_of_size(k, n):
66
"""
67
INPUT:
68
k -- an integer
69
n -- an integer
70
71
OUTPUT:
72
a randomly chosen sublist of range(k) of size n.
73
74
EXAMPLES:
75
sage: import sage.matrix.matrix_integer_dense_saturation as s
76
sage: s.random_sublist_of_size(10,3)
77
[0, 1, 5]
78
sage: s.random_sublist_of_size(10,7)
79
[0, 1, 3, 4, 5, 7, 8]
80
"""
81
if n > k:
82
raise ValueError, "n must be <= len(v)"
83
if n == k:
84
return range(k)
85
if n >= k//2+5:
86
# use complement
87
w = random_sublist_of_size(k, k-n)
88
m = set(w)
89
w = [z for z in xrange(k) if z not in m]
90
assert(len(w)) == n
91
return w
92
93
randrange = current_randstate().python_random().randrange
94
95
w = set([])
96
while len(w) < n:
97
z = randrange(k)
98
if not z in w:
99
w.add(z)
100
w = list(w)
101
w.sort()
102
return w
103
104
def solve_system_with_difficult_last_row(B, A):
105
"""
106
Solve the matrix equation B*Z = A when the last row of $B$
107
contains huge entries.
108
109
INPUT:
110
B -- a square n x n nonsingular matrix with painful big bottom row.
111
A -- an n x k matrix.
112
OUTPUT:
113
the unique solution to B*Z = A.
114
115
EXAMPLES:
116
sage: from sage.matrix.matrix_integer_dense_saturation import solve_system_with_difficult_last_row
117
sage: B = matrix(ZZ, 3, [1,2,3, 3,-1,2,939239082,39202803080,2939028038402834]); A = matrix(ZZ,3,2,[1,2,4,3,-1,0])
118
sage: X = solve_system_with_difficult_last_row(B, A); X
119
[ 290668794698843/226075992027744 468068726971/409557956572]
120
[-226078357385539/1582531944194208 1228691305937/2866905696004]
121
[ 2365357795/1582531944194208 -17436221/2866905696004]
122
sage: B*X == A
123
True
124
"""
125
# See the comments in the function of the same name in matrix_integer_dense_hnf.py.
126
# This function is just a generalization of that one to A a matrix.
127
C = copy(B)
128
while True:
129
C[C.nrows()-1] = random_matrix(ZZ,1,C.ncols()).row(0)
130
try:
131
X = C.solve_right(A)
132
except ValueError:
133
verbose("Try difficult solve again with different random vector")
134
else:
135
break
136
D = B.matrix_from_rows(range(C.nrows()-1))
137
N = D._rational_kernel_iml()
138
if N.ncols() != 1:
139
verbose("Difficult solve quickly failed. Using direct approach.")
140
return B.solve_right(A)
141
142
tm = verbose("Recover correct linear combinations")
143
k = N.matrix_from_columns([0])
144
145
# The sought for solution Z to B*Z = A is some linear combination
146
# Z = X + alpha*k
147
# Let w be the last row of B; then Z satisfies
148
# w * Z = A'
149
# where A' is the last row of A. Thus
150
# w * (X + alpha*k) = A'
151
# so w * X + alpha*w*k = A'
152
# so alpha*w*k = A' - w*X.
153
w = B[-1] # last row of B
154
A_prime = A[-1] # last row of A
155
lhs = w*k
156
rhs = A_prime - w * X
157
158
if lhs[0] == 0:
159
verbose("Difficult solve quickly failed. Using direct approach.")
160
return B.solve_right(A)
161
162
for i in range(X.ncols()):
163
alpha = rhs[i] / lhs[0]
164
X.set_column(i, (X.matrix_from_columns([i]) + alpha*k).list())
165
verbose("Done getting linear combinations.", tm)
166
return X
167
168
def saturation(A, proof=True, p=0, max_dets=5):
169
"""
170
Compute a saturation matrix of A.
171
172
INPUT:
173
A -- a matrix over ZZ
174
proof -- bool (default: True)
175
p -- int (default: 0); if not 0
176
only guarantees that output is p-saturated
177
max_dets -- int (default: 4) max number of dets of
178
submatrices to compute.
179
180
OUTPUT:
181
matrix -- saturation of the matrix A.
182
183
EXAMPLES:
184
sage: from sage.matrix.matrix_integer_dense_saturation import saturation
185
sage: A = matrix(ZZ, 2, 2, [3,2,3,4]); B = matrix(ZZ, 2,3,[1,2,3,4,5,6]); C = A*B
186
sage: C
187
[11 16 21]
188
[19 26 33]
189
sage: C.index_in_saturation()
190
18
191
sage: S = saturation(C); S
192
[11 16 21]
193
[-2 -3 -4]
194
sage: S.index_in_saturation()
195
1
196
sage: saturation(C, proof=False)
197
[11 16 21]
198
[-2 -3 -4]
199
sage: saturation(C, p=2)
200
[11 16 21]
201
[-2 -3 -4]
202
sage: saturation(C, p=2, max_dets=1)
203
[11 16 21]
204
[-2 -3 -4]
205
"""
206
# Find a submatrix of full rank and instead saturate that matrix.
207
r = A.rank()
208
if A.is_square() and r == A.nrows():
209
return identity_matrix(ZZ, r)
210
if A.nrows() > r:
211
P = []
212
while len(P) < r:
213
P = matrix_integer_dense_hnf.probable_pivot_rows(A)
214
A = A.matrix_from_rows(P)
215
216
# Factor out all common factors from all rows, just in case.
217
A = copy(A)
218
A._factor_out_common_factors_from_each_row()
219
220
if A.nrows() <= 1:
221
return A
222
223
A, zero_cols = A._delete_zero_columns()
224
225
if max_dets > 0:
226
# Take the GCD of at most num_dets randomly chosen determinants.
227
nr = A.nrows(); nc = A.ncols()
228
d = 0
229
trials = min(binomial(nc, nr), max_dets)
230
already_tried = []
231
while len(already_tried) < trials:
232
v = random_sublist_of_size(nc, nr)
233
tm = verbose('saturation -- checking det condition on submatrix')
234
d = gcd(d, A.matrix_from_columns(v).determinant(proof=proof))
235
verbose('saturation -- got det down to %s'%d, tm)
236
if gcd(d, p) == 1:
237
return A._insert_zero_columns(zero_cols)
238
already_tried.append(v)
239
240
if gcd(d, p) == 1:
241
# already p-saturated
242
return A._insert_zero_columns(zero_cols)
243
244
# Factor and p-saturate at each p.
245
# This is not a good algorithm, because all the HNF's in it are really slow!
246
#
247
#tm = verbose('factoring gcd %s of determinants'%d)
248
#limit = 2**31-1
249
#F = d.factor(limit = limit)
250
#D = [p for p, e in F if p <= limit]
251
#B = [n for n, e in F if n > limit] # all big factors -- there will only be at most one
252
#assert len(B) <= 1
253
#C = B[0]
254
#for p in D:
255
# A = p_saturation(A, p=p, proof=proof)
256
257
# This is a really simple but powerful algorithm.
258
# FACT: If A is a matrix of full rank, then hnf(transpose(A))^(-1)*A is a saturation of A.
259
# To make this practical we use solve_system_with_difficult_last_row, since the
260
# last column of HNF's are typically the only really big ones.
261
B = A.transpose().hermite_form(include_zero_rows=False, proof=proof)
262
B = B.transpose()
263
264
# Now compute B^(-1) * A
265
C = solve_system_with_difficult_last_row(B, A)
266
return C.change_ring(ZZ)._insert_zero_columns(zero_cols)
267
268
def index_in_saturation(A, proof=True):
269
r"""
270
The index of A in its saturation.
271
272
INPUT::
273
274
- ``A`` -- matrix over `\ZZ`
275
276
- ``proof`` -- boolean (``True`` or ``False``)
277
278
OUTPUT:
279
280
An integer
281
282
EXAMPLES::
283
284
sage: from sage.matrix.matrix_integer_dense_saturation import index_in_saturation
285
sage: A = matrix(ZZ, 2, 2, [3,2,3,4]); B = matrix(ZZ, 2,3,[1,2,3,4,5,6]); C = A*B; C
286
[11 16 21]
287
[19 26 33]
288
sage: index_in_saturation(C)
289
18
290
sage: W = C.row_space()
291
sage: S = W.saturation()
292
sage: W.index_in(S)
293
18
294
295
For any zero matrix the index in its saturation is 1 (see :trac:`13034`)::
296
297
sage: m = matrix(ZZ, 3)
298
sage: m
299
[0 0 0]
300
[0 0 0]
301
[0 0 0]
302
sage: m.index_in_saturation()
303
1
304
sage: m = matrix(ZZ, 2, 3)
305
sage: m
306
[0 0 0]
307
[0 0 0]
308
sage: m.index_in_saturation()
309
1
310
311
TESTS::
312
313
sage: zero = matrix(ZZ, [[]])
314
sage: zero.index_in_saturation()
315
1
316
"""
317
r = A.rank()
318
if r == 0:
319
return ZZ(1)
320
if r < A.nrows():
321
A = A.hermite_form(proof=proof, include_zero_rows=False)
322
if A.is_square():
323
return abs(A.determinant(proof=proof))
324
A = A.transpose()
325
A = A.hermite_form(proof=proof,include_zero_rows=False)
326
return abs(A.determinant(proof=proof))
327
328
329
330