Macaulay2 Engine
Loading...
Searching...
No Matches
LLL.cpp
Go to the documentation of this file.
1// Copyright 1998 Michael E. Stillman
2
3#include "LLL.hpp"
4
5#include "relem.hpp"
6#include "matrix.hpp"
7#include "text-io.hpp"
8#include "interrupted.hpp"
9
11{
12 // Makes sure that 1/4 < num/den <= 1
13 // Assumed: num, den are elements of ZZ.
14 mpz_srcptr a = num.get_mpz();
15 mpz_srcptr b = den.get_mpz();
16 if (mpz_sgn(a) < 0) return false;
17 if (mpz_sgn(b) < 0) return false;
18 if (mpz_cmp(a, b) > 0) return false; // return false if a>b.
19 mpz_t c;
20 mpz_init(c);
21 mpz_mul_2exp(c, a, 2); // c = 4*a
22 int cmp = mpz_cmp(b, c);
23 mpz_clear(c);
24 if (cmp >= 0) return false;
25 return true;
26}
27
29 gmp_QQ threshold,
30 MutableMatrix *&LLLstate)
31{
32 // First check m: should be a matrix over globalZZ.
33 if (A == nullptr || A->get_ring() != globalZZ)
34 {
35 ERROR("LLL only defined for matrices over ZZ");
36 return false;
37 }
38
39 ring_elem num = globalZZ->from_int(mpq_numref(threshold));
40 ring_elem den = globalZZ->from_int(mpq_denref(threshold));
41 // Check that 'threshold' is in range, and set the numerator, denom
42 if (!checkThreshold(num, den))
43 {
44 ERROR("LLL threshold should be in the range (1/4, 1]");
45 globalZZ->remove(num);
46 globalZZ->remove(den);
47 return false;
48 }
49
50 // LLLstate has n+4 columns, and n rows.
51 // First n columns: LLLstate(i,i) = D#i
52 // LLLstate(i,j) = lambda#(j,i) for
53 // Last four columns just have entries in row 0:
54 // The entries are: k, kmax, alphaTop, alphaBottom: all are ZZ values.
55
56 size_t n = A->n_cols();
57 LLLstate = MutableMatrix::zero_matrix(globalZZ, n, n + 4, A->is_dense());
58 if (n > 0)
59 {
60 LLLstate->set_entry(0, n, globalZZ->from_long(1)); // k := 2
61 // LLLstate->set_entry(0,n+1,globalZZ->from_long(0)); // kmax := 1
62 LLLstate->set_entry(
63 0, n + 2, num); // Set threshold numerator, denominator
64 LLLstate->set_entry(0, n + 3, den);
65 ring_elem dot;
66 A->dot_product(0, 0, dot);
67 LLLstate->set_entry(0, 0, dot); // D#1 := dot(A,0,0)
68 }
69 return true;
70}
71
73 int k,
74 ring_elem alphaTop,
75 ring_elem alphaBottom)
76{
77 // Test:alphaBottom * (D#(k-2) * D#k + lambda#(k,k-1)^2) <
78 // alphaTop * D#(k-1)^2
79 ring_elem D2, D1, D, L;
80 mpz_t a, b;
81 lambda->get_entry(k - 1, k - 1, D1);
82 lambda->get_entry(k, k, D);
83 bool Lnotzero = lambda->get_entry(k - 1, k, L);
84 if (k == 1)
85 mpz_init_set(a, D.get_mpz());
86 else
87 {
88 mpz_init(a);
89 lambda->get_entry(k - 2, k - 2, D2);
90 mpz_mul(a, D2.get_mpz(), D.get_mpz());
91 }
92 mpz_init(b);
93 if (Lnotzero)
94 {
95 mpz_mul(b, L.get_mpz(), L.get_mpz());
96 mpz_add(a, a, b);
97 }
98 mpz_mul(a, a, alphaBottom.get_mpz()); // This is the LHS.
99
100 mpz_mul(b, D1.get_mpz(), D1.get_mpz());
101 mpz_mul(b, alphaTop.get_mpz(), b); // RHS
102 int cmp = mpz_cmp(a, b);
103 mpz_clear(a);
104 mpz_clear(b);
105 return (cmp < 0);
106}
107
109 int ell,
110 MutableMatrix *A,
111 MutableMatrix *Achange, // can be NULL
112 MutableMatrix *lambda)
113{
114 // set q = ...
115 // negate q.
116 ring_elem Dl, mkl, q;
117 if (!lambda->get_entry(ell, k, mkl)) return;
118 lambda->get_entry(ell, ell, Dl);
119 mpz_srcptr a = mkl.get_mpz();
120 mpz_srcptr b = Dl.get_mpz(); // b = D#ell
121 mpz_t c, d;
122 mpz_init(c);
123 mpz_init(d);
124 mpz_mul_2exp(c, a, 1); // c = 2*lambda#(k,ell)
125 mpz_abs(d, c); // d = abs(2*lambda#(k,ell)
126 mpz_add(c, c, b); // c = 2*lambda#(k,ell) + D#ell
127 mpz_mul_2exp(d, b, 1); // d = 2*D#ell
128 mpz_fdiv_q(c, c, d); // c = (almost) final q
129 mpz_neg(c, c);
130 q = ring_elem(c);
131
132 // A->addColumnMultiple(ell,q,k);
133 // lambda->addColumnMultiple(ell,q,k);
134
135 A->column_op(k, q, ell);
136 if (Achange) Achange->column_op(k, q, ell);
137 lambda->column_op(k, q, ell);
138
139 mpz_clear(c);
140 mpz_clear(d);
141}
142
144 int kmax,
145 MutableMatrix *A,
146 MutableMatrix *Achange, // can be NULL
147 MutableMatrix *lambda)
148{
149 int i;
150 mpz_t a, b, B, C1, C2, D, D1, lam;
151 ring_elem rD1, rD, rlam;
152
153 A->interchange_columns(k, k - 1);
154 if (Achange) Achange->interchange_columns(k, k - 1);
155
156 mpz_init(a);
157 mpz_init(b);
158 mpz_init(B);
159 mpz_init(C1);
160 mpz_init(C2);
161
162 lambda->get_entry(k - 1, k - 1, rD1);
163 lambda->get_entry(k, k, rD);
164 mpz_init_set(D1, rD1.get_mpz());
165 mpz_init_set(D, rD.get_mpz());
166
167 if (lambda->get_entry(k - 1, k, rlam))
168 mpz_init_set(lam, rlam.get_mpz());
169 else
170 mpz_init(lam);
171
172 // Interchange both of these columns, except for these three terms:
173 if (k >= 2)
174 {
175 lambda->interchange_columns(k, k - 1);
176 lambda->set_entry(k - 1, k, globalZZ->from_int(lam));
177 lambda->set_entry(k, k, globalZZ->from_int(D));
178 lambda->set_entry(k, k - 1, globalZZ->from_long(0));
179 // (k-1,k-1) is set below.
180 }
181
182 // B := (D#(k-2) * D#k + lam^2) // D#(k-1);
183 if (k == 1)
184 mpz_set(a, D);
185 else
186 {
187 ring_elem rD2;
188 lambda->get_entry(k - 2, k - 2, rD2);
189 mpz_mul(a, rD2.get_mpz(), D);
190 }
191 mpz_mul(b, lam, lam);
192 mpz_add(a, a, b);
193 mpz_fdiv_q(B, a, D1);
194 lambda->set_entry(k - 1, k - 1, globalZZ->from_int(B));
195
196 // scan(k+1..C.kmax, i-> (
197 // t := lambda#(i,k);
198 // lambda#(i,k) = (D#k * lambda#(i,k-1) - lam * t) // D#(k-1);
199 // lambda#(i,k-1) = (B*t + lam*lambda#(i,k))//(D#k);));
200 for (i = k + 1; i <= kmax; i++)
201 {
202 ring_elem s, t;
203 bool s_notzero = lambda->get_entry(k - 1, i, s);
204 bool t_notzero = lambda->get_entry(k, i, t);
205 if (s_notzero)
206 mpz_mul(a, D, s.get_mpz());
207 else
208 mpz_set_ui(a, 0);
209 // lambda#(i,k) = (D#k * lambda#(i,k-1) - lam * t) // D#(k-1);
210 if (t_notzero)
211 mpz_mul(b, lam, t.get_mpz());
212 else
213 mpz_set_ui(b, 0);
214 mpz_sub(a, a, b);
215 mpz_fdiv_q(C1, a, D1);
216
217 // lambda#(i,k-1) = (B*t + lam*lambda#(i,k))//(D#k);));
218 mpz_mul(b, lam, C1);
219 if (t_notzero)
220 mpz_mul(a, B, t.get_mpz());
221 else
222 mpz_set_ui(a, 0);
223
224 mpz_add(a, a, b);
225 mpz_fdiv_q(C2, a, D);
226
227 lambda->set_entry(
228 k, i, globalZZ->from_int(C1)); // These two lines will remove t,s.
229 lambda->set_entry(k - 1, i, globalZZ->from_int(C2));
230 }
231 mpz_clear(a);
232 mpz_clear(b);
233 mpz_clear(B);
234 mpz_clear(C1);
235 mpz_clear(C2);
236 mpz_clear(D1);
237 mpz_clear(D);
238 mpz_clear(lam);
239}
240
242 MutableMatrix *Achange,
243 MutableMatrix *LLLstate,
244 int nsteps)
245{
246 size_t n = A->n_cols();
247 if (n == 0) return COMP_DONE;
248
249 // Extract the state from LLLstate:
250 int k, kmax;
251 ring_elem a, alphaTop, alphaBottom;
252 buffer o;
253
254 if (LLLstate->get_entry(0, n, a))
255 {
256 std::pair<bool, long> res = globalZZ->coerceToLongInteger(a);
257 assert(res.first);
258 k = static_cast<int>(res.second);
259 }
260 else
261 k = 0;
262
263 if (LLLstate->get_entry(0, n + 1, a))
264 {
265 std::pair<bool, long> res = globalZZ->coerceToLongInteger(a);
266 assert(res.first);
267 kmax = static_cast<int>(res.second);
268 }
269 else
270 kmax = 0;
271
272 LLLstate->get_entry(0, n + 2, alphaTop); // Don't free alphaTop!
273 LLLstate->get_entry(0, n + 3, alphaBottom);
274
275 while (k < n && nsteps != 0 && !system_interrupted())
276 {
277 if (M2_gbTrace >= 1)
278 {
279 o.reset();
280 o << ".";
281 if (M2_gbTrace >= 2) o << k;
282 if (nsteps % 20 == 0) o << newline;
283 emit(o.str());
284 }
285 nsteps--;
286
287 if (k > kmax)
288 {
289 if (M2_gbTrace == 1)
290 {
291 o.reset();
292 o << "." << k;
293 if (nsteps % 20 == 0) o << newline;
294 emit(o.str());
295 }
296
297 kmax = k;
298 for (int j = 0; j <= k; j++)
299 {
300 ring_elem u;
301 A->dot_product(k, j, u);
302 for (int i = 0; i <= j - 1; i++)
303 {
304 // u = (D#i * u - lambda#(k,i) * lambda#(j,i)) // D#(i-1)
305 ring_elem Di, mki, mji, Di1;
306 LLLstate->get_entry(i, i, Di);
307 globalZZ->mult_to(u, Di);
308 if (LLLstate->get_entry(i, k, mki) &&
309 LLLstate->get_entry(i, j, mji))
310 {
311 ring_elem t1 = globalZZ->mult(mki, mji);
312 globalZZ->subtract_to(u, t1);
313 }
314 if (i > 0)
315 {
316 LLLstate->get_entry(
317 i - 1, i - 1, Di1); // Cannot be zero!!
318 ring_elem t1 = globalZZ->divide(u, Di1);
319 globalZZ->remove(u);
320 u = t1;
321 }
322 }
323 // At this point we have our element:
324 LLLstate->set_entry(j, k, u);
325 if (j == k && globalZZ->is_zero(u))
326 {
327 ERROR("LLL vectors not independent");
328 return COMP_ERROR;
329 }
330 }
331 } // end of the k>kmax initialization
332 REDI(k, k - 1, A, Achange, LLLstate);
333 if (Lovasz(LLLstate, k, alphaTop, alphaBottom))
334 {
335 SWAPI(k, kmax, A, Achange, LLLstate);
336 k--;
337 if (k == 0) k = 1;
338 }
339 else
340 {
341 for (int ell = k - 2; ell >= 0; ell--)
342 REDI(k, ell, A, Achange, LLLstate);
343 k++;
344 }
345 }
346
347 // Before returning, reset k,kmax:
348 LLLstate->set_entry(0, n, globalZZ->from_long(k));
349 LLLstate->set_entry(0, n + 1, globalZZ->from_long(kmax));
350
351 if (k >= n) return COMP_DONE;
352 if (nsteps == 0) return COMP_DONE_STEPS;
353 return COMP_INTERRUPTED;
354}
355
357 MutableMatrix *Achange, // can be NULL
358 gmp_QQ threshold)
359{
360 MutableMatrix *LLLstate;
361 if (!initializeLLL(A, threshold, LLLstate)) return false;
362 int ret = doLLL(A, Achange, LLLstate);
363 if (ret != COMP_DONE)
364 {
365 freemem(LLLstate);
366 return false;
367 }
368 return true;
369}
370
371// Local Variables:
372// compile-command: "make -C $M2BUILDDIR/Macaulay2/e "
373// indent-tabs-mode: nil
374// End:
Lenstra-Lenstra-Lovász integer lattice basis reduction, in place on a MutableMatrix.
static void SWAPI(int k, int kmax, MutableMatrix *A, MutableMatrix *Achange, MutableMatrix *lambda)
Definition LLL.cpp:143
static bool LLL(MutableMatrix *M, MutableMatrix *U, gmp_QQ threshold)
Definition LLL.cpp:356
static int doLLL(MutableMatrix *A, MutableMatrix *Achange, MutableMatrix *LLLstate, int nsteps=-1)
Definition LLL.cpp:241
static bool checkThreshold(ring_elem num, ring_elem den)
Definition LLL.cpp:10
static bool Lovasz(MutableMatrix *lambda, int k, ring_elem alphaTop, ring_elem alphaBottom)
Definition LLL.cpp:72
static bool initializeLLL(const MutableMatrix *A, gmp_QQ threshold, MutableMatrix *&LLLstate)
Definition LLL.cpp:28
static void REDI(int k, int ell, MutableMatrix *A, MutableMatrix *Achange, MutableMatrix *lambda)
Definition LLL.cpp:108
virtual bool dot_product(size_t i, size_t j, ring_elem &result) const =0
virtual size_t n_cols() const =0
virtual bool interchange_columns(size_t i, size_t j)=0
virtual bool get_entry(size_t r, size_t c, ring_elem &result) const =0
static MutableMatrix * zero_matrix(const Ring *R, size_t nrows, size_t ncols, bool dense)
Definition mat.cpp:54
virtual bool is_dense() const =0
virtual bool column_op(size_t i, ring_elem r, size_t j)=0
virtual const Ring * get_ring() const =0
virtual bool set_entry(size_t r, size_t c, const ring_elem a)=0
Abstract base class for mutable matrices over an arbitrary engine Ring, the in-place counterpart of t...
Definition mat.hpp:79
char * str()
Definition buffer.hpp:72
void reset()
Definition buffer.hpp:69
@ COMP_DONE_STEPS
Definition computation.h:69
@ COMP_DONE
Definition computation.h:60
@ COMP_ERROR
Definition computation.h:56
@ COMP_INTERRUPTED
Definition computation.h:57
RingZZ * globalZZ
Definition relem.cpp:13
bool system_interrupted()
system_interrupted() — thread-safe polling predicate for Ctrl+C handling.
void freemem(void *s)
Definition m2-mem.cpp:103
void size_t s
Definition m2-mem.cpp:271
const int ERROR
Definition m2-mem.cpp:55
char newline[]
Definition m2-types.cpp:49
int M2_gbTrace
Definition m2-types.cpp:52
mpq_srcptr gmp_QQ
Definition m2-types.h:145
Matrix — the engine's immutable homomorphism F -> G between free modules.
RingElement — tagged (Ring*, ring_elem) pair, the engine's universal element type.
void emit(const char *s)
Definition text-io.cpp:41
Text-formatting helpers layered on buffer: bignum print, line wrapping, M2_gbTrace-gated emit.
mpz_srcptr get_mpz() const
Definition ringelem.hpp:127