Macaulay2 Engine
Loading...
Searching...
No Matches
hermite.cpp
Go to the documentation of this file.
1// Copyright 1996-2005 Michael E. Stillman
2
3#include "style.hpp"
4#include "hermite.hpp"
5#include "text-io.hpp"
6#include "matrix-con.hpp"
7
8extern RingZZ *globalZZ;
9
10static long nallocs_hm_elem = 0;
11// static long highwater_hm_elem = 0;
12static long nfree_hm_elem = 0;
13
15// The computation is complete up through this degree.
16{
17 if (status() == COMP_DONE) return 0;
18 return -1;
19}
20
22{
23 hm_elem *result = new hm_elem;
25 mpz_init(result->lead);
26 if ((*gens)[i] == nullptr)
27 {
28 result->f = nullptr;
29 }
30 else
31 {
32 mpz_abs(result->lead, (*gens)[i]->coeff.get_mpz());
33 result->f = globalZZ->copy_vec(gens->elem(i));
34 }
35 if (i < n_comps_per_syz)
36 result->fsyz = globalZZ->e_sub_i(i);
37 else
38 result->fsyz = nullptr;
39 result->next = nullptr;
40 return result;
41}
43{
44 if (p->f == nullptr)
45 {
46 if (p->fsyz != nullptr && collect_syz) syz_list.push_back(p->fsyz);
47 mpz_clear(p->lead);
48 delete p;
50 }
51 else
52 {
53 int i = p->f->comp;
54 p->next = initial[i];
55 initial[i] = p;
56 }
57}
58
59HermiteComputation::HermiteComputation(const Matrix *m, int collsyz, int nsyz)
60 : row(m->n_rows() - 1),
61 gens(m),
62 GB_list(nullptr),
63 n_gb(0),
64 collect_syz(collsyz)
65{
66 int i;
67
68 for (i = 0; i < m->n_rows(); i++) initial.push_back(nullptr);
69
70 if (nsyz < 0 || nsyz > m->n_cols()) nsyz = m->n_cols();
71 n_comps_per_syz = nsyz;
72 Fsyz = m->cols()->sub_space(nsyz);
73
74 for (i = 0; i < m->n_cols(); i++)
75 {
76 hm_elem *p = new_gen(i);
77 insert(p);
78 }
79}
80
82{
83 mpz_clear(p->lead);
84 globalZZ->remove_vec(p->f);
85 globalZZ->remove_vec(p->fsyz);
86 delete p;
87 p = nullptr;
88}
89
91{
92 // I am pretty sure the logic is as follows here (MES):
93 // the GB_list contains all of the remaining hm_elems's
94 // the 'next' field for these is the GB_list list.
95 // the initial[i] list first contains all elements input that
96 // have i as their lead term.
97 // but after computing this component, initial[i] is first set to nullptr,
98 // then to the lone GB element with this component.
99 // AND this element is on the GB_list.
100 // Upshot: to remove all of the hn_elem's: 2 choices:
101 // (1) just delete elems on GB_list, and not on initials lists.
102 // (2) just delete the first element on each element list.
103 //
104 //
105
106 // Remove the Groebner basis:
107
108 while (GB_list != nullptr)
109 {
110 hm_elem *tmp = GB_list;
111 GB_list = tmp->next;
112 remove_hm_elem(tmp);
113 }
114}
115
117{
118 int c = mpz_cmp(f->lead, g->lead);
119 if (c < 0) return -1;
120 if (c > 0) return 1;
121 return 0;
122}
123
125{
126 if (g == nullptr) return f;
127 if (f == nullptr) return g;
128 hm_elem head;
129 hm_elem *result = &head;
130 hm_elem *h;
131 while (1) switch (compare_elems(f, g))
132 {
133 case 1:
134 result->next = g;
135 result = result->next;
136 g = g->next;
137 if (g == nullptr)
138 {
139 result->next = f;
140 return head.next;
141 }
142 break;
143 case 0:
144 // In this case, we remove one, and re-insert:
145 if (mpz_cmp(f->f->coeff.get_mpz(), g->f->coeff.get_mpz()) == 0)
146 {
147 vec f1 = globalZZ->copy_vec(f->f);
148 vec f2 = globalZZ->copy_vec(f->fsyz);
149 globalZZ->subtract_vec_to(g->f, f1);
150 globalZZ->subtract_vec_to(g->fsyz, f2);
151 }
152 else
153 {
154 vec f1 = globalZZ->copy_vec(f->f);
155 vec f2 = globalZZ->copy_vec(f->fsyz);
156 globalZZ->add_vec_to(g->f, f1);
157 globalZZ->add_vec_to(g->fsyz, f2);
158 }
159
160 h = g;
161 // We need to reset the lead term
162 if (g->f != nullptr) mpz_abs(h->lead, g->f->coeff.get_mpz());
163 g = g->next;
164 insert(h);
165 if (g == nullptr)
166 {
167 result->next = f;
168 return head.next;
169 }
170 // Now fall through to merge f into the result:
171 [[fallthrough]];
172 case -1:
173 result->next = f;
174 result = result->next;
175 f = f->next;
176 if (f == nullptr)
177 {
178 result->next = g;
179 return head.next;
180 }
181 break;
182 }
183}
184
186{
187 // Sort in ascending absolute value of lead term
188 if (p == nullptr || p->next == nullptr) return;
189 hm_elem *p1 = nullptr;
190 hm_elem *p2 = nullptr;
191 while (p != nullptr)
192 {
193 hm_elem *tmp = p;
194 p = p->next;
195 tmp->next = p1;
196 p1 = tmp;
197
198 if (p == nullptr) break;
199 tmp = p;
200 p = p->next;
201 tmp->next = p2;
202 p2 = tmp;
203 }
204
205 sort(p1);
206 sort(p2);
207 p = merge(p1, p2);
208}
209
211{
212 // compute (u,v) s.t. u lead(p) + v lead(q) = gcd
213 // set p <- u*p + v*q;
214 // set q <- lead(q)/gcd * p - lead(p)/gcd * q
215 // DOn't forget to also reset the 'lead' fields!
216 ring_elem u, v;
217 ring_elem g = globalZZ->gcd_extended(p->f->coeff, q->f->coeff, u, v);
218 ring_elem a = globalZZ->divide(q->f->coeff, g); // exact
219 ring_elem b = globalZZ->divide(p->f->coeff, g); // exact
220 globalZZ->negate_to(b);
221
222 vec p1 = globalZZ->mult_vec(u, p->f);
223 vec p2 = globalZZ->mult_vec(v, q->f);
224 globalZZ->add_vec_to(p1, p2);
225
226 vec syz1 = globalZZ->mult_vec(u, p->fsyz);
227 vec syz2 = globalZZ->mult_vec(v, q->fsyz);
228 globalZZ->add_vec_to(syz1, syz2);
229
230 vec q1 = globalZZ->mult_vec(a, p->f);
231 vec q2 = globalZZ->mult_vec(b, q->f);
232 globalZZ->add_vec_to(q1, q2);
233
234 vec qsyz1 = globalZZ->mult_vec(a, p->fsyz);
235 vec qsyz2 = globalZZ->mult_vec(b, q->fsyz);
236 globalZZ->add_vec_to(qsyz1, qsyz2);
237
238 globalZZ->remove_vec(p->f);
239 globalZZ->remove_vec(q->f);
240 globalZZ->remove_vec(p->fsyz);
241 globalZZ->remove_vec(q->fsyz);
242 globalZZ->remove(a);
243 globalZZ->remove(b);
244 globalZZ->remove(g);
245 globalZZ->remove(u);
246 globalZZ->remove(v);
247
248 // Now that the arithmetic has been done, put back into 'p', 'q':
249 p->f = p1;
250 p->fsyz = syz1;
251 mpz_set(p->lead, p->f->coeff.get_mpz());
252
253 q->f = q1;
254 q->fsyz = qsyz1;
255 if (q->f != nullptr) mpz_abs(q->lead, q->f->coeff.get_mpz());
256
257 insert(q);
258}
259
261{
262 // ngb = stop[0]
263 // nsyz = stop[1]
264 // npairs = stop[2]
265 if (status() == COMP_DONE) return;
266 for (; row >= 0; row--)
267 {
268 hm_elem *p = initial[row];
269 if (p == nullptr) continue;
270 initial[row] = nullptr;
271 sort(p); // This can remove elements, inserting them back
272 while (p != nullptr && p->next != nullptr)
273 {
274 hm_elem *pnext = p->next->next;
275 reduce(p, p->next); // replaces p1, and re-inserts p2.
276 p->next = pnext;
277 }
278 // At this point, 'p' is the only remaining element with this lead term
279 // So insert it into GB_list
280 p->next = GB_list;
281 GB_list = p;
282 n_gb++;
283 }
284 // At this point, we are done, so reset initial[...] (it is all NULL right
285 // now)
286
287 // Now let's auto reduce the result...
288 // We will reduce the GB elems
289 // one by one, placing them back into the 'initial' table.
290 // We use the following: the lead components of elements of GB_list are in
291 // increasing order
292
293 for (hm_elem *p = GB_list; p != nullptr; p = p->next)
294 {
295 if (!globalZZ->is_positive(p->f->coeff))
296 {
297 vec f = globalZZ->negate_vec(p->f);
298 vec fsyz = globalZZ->negate_vec(p->fsyz);
299 globalZZ->remove_vec(p->f);
300 globalZZ->remove_vec(p->fsyz);
301 p->f = f;
302 p->fsyz = fsyz;
303 }
304 gb_reduce(p->f, p->fsyz);
305 initial[p->f->comp] = p;
306 }
307
308 // for (hm_elem *p = GB_list; p != 0; p = p->next)
309 // initial[p->f->comp] = p;
311}
312
313/*************************
314 ** Top level interface **
315 *************************/
316
317const Matrix /* or null */ *HermiteComputation::get_gb()
318{
319 MatrixConstructor mat(gens->rows(), 0);
320 for (hm_elem *p = GB_list; p != nullptr; p = p->next)
321 mat.append(globalZZ->copy_vec(p->f));
322 return mat.to_matrix();
323}
324
326{
327 // return the minimal generators (or as minimal as possible?)
328 return get_gb();
329}
330
332{
333 MatrixConstructor mat(Fsyz, 0);
334 for (hm_elem *p = GB_list; p != nullptr; p = p->next)
335 mat.append(globalZZ->copy_vec(p->fsyz));
336 return mat.to_matrix();
337}
338
340{
341 MatrixConstructor mat(Fsyz, 0);
342 for (int i = 0; i < syz_list.size(); i++)
343 mat.append(globalZZ->copy_vec(syz_list[i]));
344 return mat.to_matrix();
345}
346
347const Matrix /* or null */ *HermiteComputation::get_initial(int nparts)
348{
349 (void) nparts;
350 MatrixConstructor mat(gens->rows(), 0);
351 for (hm_elem *p = GB_list; p != nullptr; p = p->next)
352 {
353 vec v = p->f;
354 mat.append(globalZZ->make_vec(v->comp, v->coeff));
355 }
356 return mat.to_matrix();
357}
358
360/* This displays statistical information, and depends on the
361 M2_gbTrace value */
362{
363 o << newline;
364 for (int i = 0; i < gens->n_rows(); i++)
365 if (initial[i] != nullptr)
366 {
367 o << "--- component " << i << " -----" << newline;
368 for (hm_elem *p = initial[i]; p != nullptr; p = p->next)
369 {
370 bignum_text_out(o, p->lead);
371 o << " ## ";
372 globalZZ->vec_text_out(o, p->f);
373 o << " ## ";
374 globalZZ->vec_text_out(o, p->fsyz);
375 o << newline;
376 // If the computation is done, this is the GB we are displaying
377 // but we only want the first element in each list.
378 if (status() == COMP_DONE) break;
379 }
380 }
381 o << newline << "--- syzygies ---" << newline;
382 for (int i = 0; i < syz_list.size(); i++)
383 globalZZ->vec_text_out(o, syz_list[i]);
384 o << newline;
385}
386
388{
389 // Reduce f so that each of its terms are < corresponding initial term
390 // (in absolute value).
391 vecterm head;
392 vecterm *result = &head;
393 head.next = nullptr;
394 while (f != nullptr)
395 {
396 int x = f->comp;
397 hm_elem *h = initial[x];
398 if (h != nullptr)
399 {
400 ring_elem v;
401 ring_elem rem =
402 globalZZ->remainderAndQuotient(f->coeff, h->f->coeff, v);
403 bool do_reduce = !globalZZ->is_zero(v);
404 if (do_reduce)
405 {
406 v = globalZZ->negate(v);
407 vec g = globalZZ->mult_vec(v, h->f);
408 globalZZ->add_vec_to(f, g);
409 }
410 if (globalZZ->is_zero(rem)) continue;
411 }
412 // The lead term stays
413 result->next = f;
414 f = f->next;
415 result = result->next;
416 result->next = nullptr;
417 continue;
418 }
419
420 f = head.next;
421}
422
423void HermiteComputation::gb_reduce(vec &f, vec &fsyz) const
424{
425 // Reduce f so that each of its terms are < corresponding initial term
426 // (in absolute value).
427 vecterm head;
428 vecterm *result = &head;
429 head.next = nullptr;
430 while (f != nullptr)
431 {
432 int x = f->comp;
433 hm_elem *h = initial[x];
434 if (h != nullptr)
435 {
436 ring_elem v;
437 ring_elem rem =
438 globalZZ->remainderAndQuotient(f->coeff, h->f->coeff, v);
439 bool do_reduce = !globalZZ->is_zero(v);
440 if (do_reduce)
441 {
442 v = globalZZ->negate(v);
443 vec g = globalZZ->mult_vec(v, h->f);
444 globalZZ->add_vec_to(f, g);
445 vec gsyz = globalZZ->mult_vec(v, h->fsyz);
446 globalZZ->add_vec_to(fsyz, gsyz);
447 }
448 if (globalZZ->is_zero(rem)) continue;
449 }
450 // The lead term stays
451 result->next = f;
452 f = f->next;
453 result = result->next;
454 result->next = nullptr;
455 continue;
456 }
457
458 f = head.next;
459}
460
462 const Matrix *m)
463{
464 if (m->get_ring() != globalZZ)
465 {
466 ERROR("expected matrix over ZZ");
467 return nullptr;
468 }
469 if (m->n_rows() != gens->rows()->rank())
470 {
471 ERROR("expected matrices to have same number of rows");
472 return nullptr;
473 }
474 MatrixConstructor mat_remainder(m->rows(), m->cols(), m->degree_shift());
475 for (int i = 0; i < m->n_cols(); i++)
476 {
477 vec f = globalZZ->copy_vec(m->elem(i));
478
479 gb_reduce(f);
480 mat_remainder.set_column(i, f);
481 }
482 return mat_remainder.to_matrix();
483}
484
486 const Matrix *m,
487 const Matrix /* or null */ **result_remainder,
488 const Matrix /* or null */ **result_quotient)
489{
490 if (m->get_ring() != globalZZ)
491 {
492 ERROR("expected matrix over ZZ");
493 *result_remainder = nullptr;
494 *result_quotient = nullptr;
495 return false;
496 }
497 if (m->n_rows() != gens->rows()->rank())
498 {
499 ERROR("expected matrices to have same number of rows");
500 *result_remainder = nullptr;
501 *result_quotient = nullptr;
502 return false;
503 }
504 MatrixConstructor mat_remainder(m->rows(), m->cols(), m->degree_shift());
505 MatrixConstructor mat_quotient(Fsyz, m->cols(), nullptr);
506 bool all_zeroes = true;
507 for (int i = 0; i < m->n_cols(); i++)
508 {
509 vec f = globalZZ->copy_vec(m->elem(i));
510 vec fsyz = nullptr;
511
512 gb_reduce(f, fsyz);
513 if (f != nullptr) all_zeroes = false;
514
515 globalZZ->negate_vec_to(fsyz);
516 mat_remainder.set_column(i, f);
517 mat_quotient.set_column(i, fsyz);
518 }
519 *result_remainder = mat_remainder.to_matrix();
520 *result_quotient = mat_quotient.to_matrix();
521 return all_zeroes;
522}
523
525// Return -1 if every column of 'm' reduces to zero.
526// Otherwise return the index of the first column that
527// does not reduce to zero.
528{
529 if (m->get_ring() != globalZZ)
530 {
531 ERROR("expected matrix over ZZ");
532 return -1;
533 }
534 // Reduce each column of m one by one.
535 for (int i = 0; i < m->n_cols(); i++)
536 {
537 vec f = globalZZ->copy_vec(m->elem(i));
538 gb_reduce(f);
539 if (f != nullptr)
540 {
541 globalZZ->remove_vec(f);
542 return i;
543 }
544 }
545 return -1;
546}
547
548// Local Variables:
549// compile-command: "make -C $M2BUILDDIR/Macaulay2/e "
550// indent-tabs-mode: nil
551// End:
enum ComputationStatusCode status() const
Definition comp.hpp:100
enum ComputationStatusCode set_status(enum ComputationStatusCode)
Definition comp.cpp:66
FreeModule * sub_space(int n) const
Definition freemod.cpp:197
HermiteComputation(const Matrix *m, int collect_syz, int n_syz)
Definition hermite.cpp:59
virtual const Matrix * matrix_remainder(const Matrix *m)
Definition hermite.cpp:461
hm_elem * new_gen(int i)
Definition hermite.cpp:21
virtual int complete_thru_degree() const
Definition hermite.cpp:14
void remove_hm_elem(hm_elem *&p)
Definition hermite.cpp:81
virtual void text_out(buffer &o) const
Definition hermite.cpp:359
virtual const Matrix * get_initial(int nparts)
Definition hermite.cpp:347
virtual void start_computation()
Definition hermite.cpp:260
virtual int contains(const Matrix *m)
Definition hermite.cpp:524
const FreeModule * Fsyz
Definition hermite.hpp:64
void reduce(hm_elem *&p, hm_elem *q)
Definition hermite.cpp:210
void insert(hm_elem *p)
Definition hermite.cpp:42
virtual const Matrix * get_syzygies()
Definition hermite.cpp:339
const Matrix * gens
Definition hermite.hpp:61
void gb_reduce(vec &f) const
Definition hermite.cpp:387
int compare_elems(hm_elem *f, hm_elem *g) const
Definition hermite.cpp:116
hm_elem * merge(hm_elem *f, hm_elem *g)
Definition hermite.cpp:124
virtual const Matrix * get_gb()
Definition hermite.cpp:317
hm_elem * GB_list
Definition hermite.hpp:63
virtual const Matrix * get_change()
Definition hermite.cpp:331
virtual M2_bool matrix_lift(const Matrix *m, const Matrix **result_remainder, const Matrix **result_quotient)
Definition hermite.cpp:485
virtual const Matrix * get_mingens()
Definition hermite.cpp:325
void sort(hm_elem *&p)
Definition hermite.cpp:185
const_monomial degree_shift() const
Definition matrix.hpp:149
const Ring * get_ring() const
Definition matrix.hpp:134
ring_elem elem(int i, int j) const
Definition matrix.cpp:307
int n_cols() const
Definition matrix.hpp:147
int n_rows() const
Definition matrix.hpp:146
const FreeModule * rows() const
Definition matrix.hpp:144
const FreeModule * cols() const
Definition matrix.hpp:145
Matrix * to_matrix()
void append(vec v)
void set_column(int c, vec v)
Mutable builder used to assemble an immutable Matrix one column (or one term) at a time.
Engine-side ring of integers, backed by GMP mpz_ptr elements.
Definition ZZ.hpp:77
@ COMP_DONE
Definition computation.h:60
#define Matrix
Definition factory.cpp:14
RingZZ * globalZZ
Definition relem.cpp:13
int p
int p1
static long nfree_hm_elem
Definition hermite.cpp:12
static long nallocs_hm_elem
Definition hermite.cpp:10
HermiteComputation — Hermite normal form over ZZ, the ZZ-analogue of GaussElimComputation.
const int ERROR
Definition m2-mem.cpp:55
VALGRIND_MAKE_MEM_DEFINED & result(result)
char newline[]
Definition m2-types.cpp:49
char M2_bool
Definition m2-types.h:82
MatrixConstructor — the mutable builder that produces an immutable Matrix.
volatile int x
vec f
Definition hermite.hpp:46
mpz_t lead
Definition hermite.hpp:45
hm_elem * next
Definition hermite.hpp:44
vec fsyz
Definition hermite.hpp:47
Engine-wide stylistic constants: LT / EQ / GT codes, INTSIZE, GEOHEAP_SIZE.
void bignum_text_out(buffer &o, mpz_srcptr a)
Definition text-io.cpp:12
Text-formatting helpers layered on buffer: bignum print, line wrapping, M2_gbTrace-gated emit.