Gröbner basis project
Codebase for research into Gröbner basis computation
f4_reduction.cpp
1 #ifndef __F4_REDUCTION_CPP__
2 #define __F4_REDUCTION_CPP__
3 
4 /*****************************************************************************\
5 * This file is part of DynGB. *
6 * *
7 * DynGB is free software: you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation, either version 2 of the License, or *
10 * (at your option) any later version. *
11 * *
12 * Foobar is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU General Public License *
18 * along with DynGB. If not, see <http://www.gnu.org/licenses/>. *
19 \*****************************************************************************/
20 
21 #include "f4_reduction.hpp"
22 #include "algorithm_buchberger_basic.hpp"
23 
24 #include <thread>
25 using std::thread;
26 
27 void find_position(
28  list<Monomial *>::iterator & ti,
29  list<Monomial *>::iterator stop,
30  list<Abstract_Polynomial *>::iterator & pi,
31  const Monomial & u
32 ) {
33  while (ti != stop and **ti > u) {
34  ++ti;
35  ++pi;
36  }
37 }
38 
40  const list<Critical_Pair_Basic *> & P,
41  const list<Abstract_Polynomial *> & B
42 ) : G(B), Rx(P.front()->first()->base_ring()) {
43  cols = 0;
44  A = nullptr;
45  R_build.clear();
46  M_build.clear();
47  strategies.clear();
48  auto mi = M_build.begin();
49  auto ri = R_build.begin();
50  mord = P.front()->first()->monomial_ordering();
51  for (auto p : P) {
52  strategies.push_back(new Poly_Sugar_Data(p->first()));
53  if (strategies.back() != nullptr) {
54  strategies.back()->at_generation_tasks(p->first_multiplier());
55  if (p->second() != nullptr)
56  strategies.back()->pre_reduction_tasks(p->second_multiplier().log(), *(p->second()));
57  }
58  add_monomials(mi, ri, p->first(), p->first_multiplier(), true);
59  if (p->second() != nullptr) {
60  *ri = const_cast<Abstract_Polynomial *>(p->second());
61  ++ri; ++mi;
62  add_monomials(mi, ri, p->second(), p->second_multiplier());
63  }
64  }
65  // for each monomial, find an appropriate reducer
66  mi = M_build.begin();
67  ri = R_build.begin();
68  while (mi != M_build.end()) {
69  auto g = G.begin();
70  bool found = ((*ri) != nullptr);
71  while (not found and g != G.end()) {
72  if ((**mi).divisible_by((*g)->leading_monomial())) {
73  found = true;
74  *ri = *g;
75  Monomial u(**mi);
76  u /= (*g)->leading_monomial();
77  add_monomials(mi, ri, *g, u);
78  g = G.end();
79  }
80  ++g;
81  }
82  ++mi;
83  ++ri;
84  }
85  initialize_many(P);
86 }
87 
88 void F4_Reduction_Data::initialize_many(const list<Critical_Pair_Basic *> & P) {
89  cols = M_build.size();
90  Abstract_Polynomial * p = const_cast<Abstract_Polynomial *>(P.front()->first());
91  Prime_Field & F = const_cast<Prime_Field &>(p->ground_field());
92  rows = P.size();
93  heads.resize(rows);
94  nonzero_entries.resize(rows);
95  for (unsigned i = 0; i < rows; ++i)
96  heads[i] = 0;
97  A = (Prime_Field_Element *)malloc(
98  sizeof(Prime_Field_Element)*cols*rows*P.size()
99  );
100  for (unsigned i = 0; i < cols*rows; ++i)
101  A[i].assign(0, &F);
102  M.clear();
103  for (auto mi = M_build.begin(); mi != M_build.end(); ++mi)
104  M.push_back(*mi);
105  strategies.resize(P.size());
106  for (unsigned i = 0; i < strategies.size(); ++i)
107  strategies[i] = nullptr;
108  unsigned row = 0;
109  for (auto cp : P) {
110  auto p = cp->first();
111  auto t = cp->first_multiplier();
112  auto pi = p->new_iterator();
113  nonzero_entries[row] = 0;
114  auto mi = M_build.begin();
115  bool head_found = false;
116  for (unsigned i = 0; i < cols; ++i) {
117  if ((not pi->fellOff()) and M[i]->like_multiple(pi->currMonomial(), t)) {
118  *(A + row*cols + i) = pi->currCoeff();
119  pi->moveRight();
120  ++nonzero_entries[row];
121  if (not head_found) {
122  heads[row] = i;
123  head_found = true;
124  }
125  }
126  ++mi;
127  }
128  delete pi;
129  strategies[row] = new Poly_Sugar_Data(cp->first());
130  delete cp;
131  ++row;
132  }
133  R.resize(R_build.size());
134  unsigned i = 0;
135  for (Abstract_Polynomial * r : R_build)
136  R[i++] = r;
137 }
138 
140  list<Monomial *>::iterator & t1,
141  list<Abstract_Polynomial *>::iterator & r1,
142  const Abstract_Polynomial *g,
143  const Monomial & u,
144  bool new_row
145 ) {
146  NVAR_TYPE n = g->leading_monomial().num_vars();
147  if (new_row) {
148  t1 = M_build.begin();
149  r1 = R_build.begin();
150  Monomial * t = new Monomial(g->leading_monomial());
151  (*t) *= u;
152  find_position(t1, M_build.end(), r1, *t);
153  if (t1 == M_build.end()) {
154  M_build.push_back(t);
155  R_build.push_back(nullptr);
156  t1 = M_build.begin();
157  r1 = R_build.begin();
158  auto t2(t1);
159  ++t2;
160  while (t2 != M_build.end()) {
161  ++t1; ++r1;
162  ++t2;
163  }
164  } else if (**t1 != *t) {
165  M_build.insert(t1, t);
166  R_build.insert(r1, nullptr);
167  --t1; --r1;
168  }
169  }
170  auto t2(t1); auto r2(r1);
171  Polynomial_Iterator * pi = g->new_iterator();
172  pi->moveRight();
173  while (not (pi->fellOff())) {
174  Monomial * t = new Monomial(pi->currMonomial());
175  (*t) *= u;
176  find_position(t2, M_build.end(), r2, *t);
177  if (t2 == M_build.end()) {
178  M_build.push_back(t);
179  R_build.push_back(nullptr);
180  } else if (**t2 != *t) {
181  M_build.insert(t2, t);
182  R_build.insert(r2, nullptr);
183  }
184  pi->moveRight();
185  }
186  delete pi;
187 }
188 
190  free(A);
191  for (auto strat : strategies) {
192  if (strat != nullptr)
193  delete strat;
194  }
195 }
196 
197 void F4_Reduction_Data::print_matrix(bool show_data) {
198  if (show_data) {
199  for (auto m : M)
200  cout << *m << ", ";
201  cout << endl;
202  }
203  for (unsigned i = 0; i < rows; ++i) {
204  cout << "( ";
205  for (unsigned j = 0; j < cols; ++j) {
206  cout << A[i*cols + j];
207  if (j < cols - 1)
208  cout << ", ";
209  }
210  cout << ")\n";
211  }
212 }
213 
215  for (unsigned i = 0; i < cols; ++i) {
216  cout << *(M[i]) << " to be reduced by ";
217  if (R[i] == nullptr)
218  cout << "none\n";
219  else
220  cout << R[i]->leading_monomial() << endl;
221  }
222 }
223 
225  bool is_zero_so_far = true;
226  for (unsigned i = 0; is_zero_so_far and i < rows; ++i)
227  is_zero_so_far = is_zero_so_far and (nonzero_entries[i] == 0);
228  return is_zero_so_far;
229 }
230 
231 void F4_Reduction_Data::reduce_my_rows(const set<unsigned> & my_rows) {
232  NVAR_TYPE n = M[0]->num_vars();
233  EXP_TYPE * u = new EXP_TYPE[n]; // exponents of multiplier
234  for (unsigned k : my_rows) {
235  // loop through our terms
236  for (unsigned i = heads[k]; i < cols; ++i) {
237  // do we need to reduce this term, and can we?
238  if ((not A[k*cols + i].is_zero()) and R[i] != nullptr) {
239  // get reducer for this monomial
240  const Abstract_Polynomial * g = R[i];
241  Polynomial_Iterator * gi = g->new_iterator();
242  // determine multiplier
243  for (NVAR_TYPE k = 0; k < n; ++k)
244  u[k] = (*M[i])[k] - (gi->currMonomial())[k];
245  // determine reduction coefficient
246  Prime_Field_Element a(A[k*cols + i]*gi->currCoeff().inverse());
247  // loop through g's terms
248  // by construction, monomials of u*g[i] should already appear in M,
249  // so the line marked *** SHOULD NOT need a guard
250  unsigned j = i;
251  while (not gi->fellOff()) {
252  const Monomial & t = gi->currMonomial();
253  while (not M[j]->like_multiple(u, t)) ++j;
254  bool was_zero = A[k*cols + j].is_zero();
255  A[k*cols + j] -= a*gi->currCoeff();
256  if (was_zero and not A[k*cols + j].is_zero())
257  ++nonzero_entries[k];
258  else if (not was_zero and A[k*cols + j].is_zero())
259  --nonzero_entries[k];
260  ++j;
261  gi->moveRight();
262  }
263  if (strategies[k] != nullptr)
264  strategies[k]->pre_reduction_tasks(u, *g);
265  advance_head(k);
266  //while (heads[i] < cols and A[heads[i] + i*cols].is_zero()) ++heads[i];
267  delete gi;
268  }
269  }
270  }
271  delete [] u;
272 }
273 
275  unsigned cores = std::thread::hardware_concurrency();
276  unsigned num_threads = (cores < rows) ? cores : rows;
277  set<unsigned> * thread_rows = new set<unsigned>[num_threads];
278  // loop through rows
279  for (unsigned k = 0; k < rows; ++k)
280  thread_rows[k % num_threads].insert(k);
281  thread * workers = new thread[num_threads];
282  for (unsigned c = 0; c < num_threads; ++c)
283  workers[c] = thread(
284  &F4_Reduction_Data::reduce_my_rows, this, std::cref(thread_rows[c])
285  );
286  for (unsigned c = 0; c < num_threads; ++c)
287  workers[c].join();
288  delete [] workers;
289  delete [] thread_rows;
290 }
291 
293  for (unsigned i = 0; i < rows; ++i) {
294  if (nonzero_entries[i] > 0) {
295  for (unsigned j = 0; j < rows; ++j) {
296  if (j != i) {
297  auto c = heads[i];
298  if (not A[j*cols + c].is_zero()) {
299  auto a = A[j*cols + c];
300  a *= -A[i*cols + c].inverse();
301  while (c < cols) {
302  if (not A[i*cols + c].is_zero()) {
303  bool was_zero = A[j*cols + c].is_zero();
304  A[j*cols + c] += a*A[i*cols + c];
305  if (was_zero and not A[j*cols + c].is_zero())
306  ++nonzero_entries[j];
307  else if (not was_zero and A[j*cols + c].is_zero())
308  --nonzero_entries[j];
309  }
310  ++c;
311  }
312  if (heads[i] == heads[j])
313  advance_head(j);
314  }
315  }
316  }
317  }
318  }
319 }
320 
321 vector<Constant_Polynomial *> F4_Reduction_Data::finalize() {
322  vector<Constant_Polynomial *> result;
323  NVAR_TYPE n = M[0]->num_vars();
324  for (unsigned i = 0; i < rows; ++i) {
325  if (nonzero_entries[i] == 0) {
326  delete strategies[i];
327  strategies[i] = nullptr;
328  } else {
329  Monomial * M_final = (Monomial *)malloc(sizeof(Monomial)*nonzero_entries[i]);
330  Prime_Field_Element * A_final
332  unsigned k = 0;
333  Prime_Field_Element scale(A[i*cols + heads[i]].inverse(), A[i*cols + heads[i]].field());
334  for (unsigned j = heads[i]; k < nonzero_entries[i] and j < cols; ++j) {
335  if (not A[i*cols + j].is_zero()) {
336  A_final[k] = A[i*cols + j]*scale;
337  M_final[k].common_initialization();
338  M_final[k].initialize_exponents(n);
339  M_final[k].set_monomial_ordering(mord);
340  M_final[k] = *M[j];
341  ++k;
342  }
343  }
344  result.push_back(new Constant_Polynomial(
345  nonzero_entries[i],
346  Rx,
347  M_final, A_final,
348  mord
349  ));
350  result.back()->set_strategy(strategies[i]);
351  strategies[i] = nullptr;
352  free(M_final);
353  free(A_final);
354  }
355  }
356  return result;
357 }
358 
359 list<Constant_Polynomial *> f4_control(const list<Abstract_Polynomial *> &F) {
360  time_t start_f4 = time(nullptr);
361  cout << "computation started at " << asctime(localtime(&start_f4)) << endl;
362  unsigned number_of_spolys = 0;
363  list<Abstract_Polynomial *> G;
364  list<Critical_Pair_Basic *> P;
365  // set up basis with generators
366  for (Abstract_Polynomial * fo : F)
367  {
369  f->set_strategy(new Poly_Sugar_Data(f));
371  P.push_back(new Critical_Pair_Basic(f, StrategyFlags::SUGAR_STRATEGY));
372  }
373  // main loop
374  bool verbose = false;
375  bool very_verbose = false;
376  list<Critical_Pair_Basic *> Pnew;
377  while (not P.empty()) {
380  Critical_Pair_Basic * p = P.front();
381  report_front_pair(p, StrategyFlags::SUGAR_STRATEGY);
382  cout << "\tdegree: " << p->lcm().total_degree(0) << endl;
383  P.pop_front();
384  Pnew.push_back(p);
385  DEG_TYPE mindeg = p->lcm().total_degree();
386  for (auto pi = P.begin(); pi != P.end(); ++pi) {
387  p = *pi;
388  if (p->lcm().total_degree() < mindeg) {
389  for (auto qi = Pnew.begin(); qi != Pnew.end(); ++qi)
390  P.push_front(*qi);
391  Pnew.clear();
392  mindeg = p->lcm().total_degree();
393  report_front_pair(p, StrategyFlags::SUGAR_STRATEGY);
394  cout << "\tdegree: " << p->lcm().total_degree(0) << endl;
395  Pnew.push_back(p);
396  auto qi = pi;
397  ++qi;
398  P.erase(pi);
399  pi = qi;
400  }
401  else if (p->lcm().total_degree() == mindeg) {
402  report_front_pair(p, StrategyFlags::SUGAR_STRATEGY);
403  Pnew.push_back(p);
404  P.erase(pi);
405  }
406  }
407  // make s-poly
408  F4_Reduction_Data s(Pnew, G);
409  number_of_spolys += Pnew.size();
410  Pnew.clear();
411  if (not s.is_zero()) {
412  s.reduce_by_old();
413  s.reduce_by_new();
414  }
415  if (s.is_zero()) {
416  cout << "\tmatrix reduced to zero\n";
417  // delete s;
418  } else {
419  vector<Constant_Polynomial *> R = s.finalize();
420  for (auto r : R) {
421  cout << "\tadded " << r->leading_monomial() << endl;
422  very_verbose = false;
423  if (very_verbose) { cout << "\tadded "; r->println(); }
424  gm_update(P, G, r, StrategyFlags::SUGAR_STRATEGY);
425  }
426  }
427  }
428  cout << number_of_spolys << " s-polynomials computed and reduced\n";
429  // cleanup
430  cout << G.size() << " polynomials before interreduction\n";
431  //check_correctness(G, strategy);
432  G = reduce_basis(G);
433  cout << G.size() << " polynomials after interreduction\n";
434  list<Constant_Polynomial *> B;
435  for (Abstract_Polynomial * g : G)
436  B.push_back(new Constant_Polynomial(*g));
437  time_t end_f4 = time(nullptr);
438  double duration = difftime(end_f4, start_f4);
439  cout << "computation ended at " << asctime(localtime(&end_f4)) << endl;
440  cout << "parallel f4 took " << duration << " seconds\n";
441  return B;
442 }
443 
444 #endif
DEG_TYPE total_degree(NVAR_TYPE m=0) const
Sum of exponents of the first m variables.
Definition: monomial.cpp:190
The general class of a polynomial.
Definition: polynomial.hpp:101
unsigned rows
number of rows in the polynomial
vector< Monomial * > M
monomials for each column
A Constant_Polynomial is a polynomial that should not change.
bool is_zero()
returns true iff all the entries are 0
vector< Abstract_Polynomial * > R
finalized list of indices of reducers for the corresponding monomials of f
void sort_pairs_by_strategy(list< T *> &P)
Applies the strategy to find the “smallest” critical pair.
virtual Polynomial_Iterator * new_iterator() const =0
An iterator that poses no risk of modifying the polynomial.
virtual void moveRight()=0
Moves right in the polynomial, to the next smaller monomial.
bool is_zero() const
Is this the additive identity?
Definition: fields.hpp:180
virtual const Prime_Field_Element & currCoeff() const =0
Reports the coefficient at the current position.
void gm_update(list< Critical_Pair_Basic *> &P, list< Abstract_Polynomial *> &G, Abstract_Polynomial *r, StrategyFlags strategy)
Implementation of Gebauer-Moeller algorithm. Based on description in Becker and Weispfenning (1993)...
list< Monomial * > M_build
monomials while building
void print_matrix(bool show_data=false)
prints the matrix
Information necessary for a field modulo a prime.
Definition: fields.hpp:49
void set_monomial_ordering(const Monomial_Ordering *mord)
sets the Monomial_Ordering associated with this Monomial
Definition: monomial.cpp:211
virtual Monomial & leading_monomial() const =0
leading monomial – call after sort_by_order()!
vector< Poly_Sugar_Data * > strategies
strategy data for each polynomial
const Monomial & lcm() const
lcm of leading monomials of polynomials
void reduce_my_rows(const set< unsigned > &)
reduces the specified set of rows, suitable for multithreading
void advance_head(unsigned i)
advances head; useful after a computation
list< Abstract_Polynomial * > R_build
indices of reducers for the corresponding elements of M
Prime_Field & ground_field()
ground field – all coefficients should be in this field
Definition: polynomial.cpp:27
void reduce_by_old()
reduces polynomials
virtual void at_generation_tasks()
hook called while first generating polynomial
Definition: strategies.hpp:88
~F4_Reduction_Data()
releases space for the matrix and deletes any strategies not already set to nullptr ...
list< Abstract_Polynomial * > reduce_basis(list< Abstract_Polynomial *>G)
Remove redundant polynomials from G.
vector< Constant_Polynomial * > finalize()
converts this to a Constant_Polynomial and returns the result
NVAR_TYPE num_vars() const
number of variables
Definition: monomial.hpp:130
virtual bool fellOff() const =0
unsigned cols
number of columns in the polynomial
Implementation of monomials.
Definition: monomial.hpp:69
Element of a field of prime characteristic.
Definition: fields.hpp:137
F4_Reduction_Data(const list< Critical_Pair_Basic *> &P, const list< Abstract_Polynomial *> &B)
encapsulation of one step of the F4 algorithm for the polynomials indicated by P and B ...
polynomial-related data for a sugar strategy
const Monomial_Ordering * mord
how the monomials are ordered
void initialize_exponents(NVAR_TYPE number_of_vars)
allocates memory for exponents This is useful when you want to allocate an array of monomials...
Definition: monomial.cpp:65
void set_strategy(Poly_Strategy_Data *psd)
sets the polynomial’s strategy to psd
Definition: polynomial.cpp:36
void add_monomials(list< Monomial *>::iterator &ti, list< Abstract_Polynomial *>::iterator &ri, const Abstract_Polynomial *g, const Monomial &u, bool new_row=false)
adds monomials of to M_build
Used to iterate through a polynomial.
Definition: polynomial.hpp:224
void initialize_many(const list< Critical_Pair_Basic *> &P)
creates the matrix
COEF_TYPE inverse() const
Returns the multiplicative inverse of this.
Definition: fields.cpp:84
virtual const Monomial & currMonomial() const =0
Reports the monomial at the current position.
void list_reducers()
lists the reducers selected for each column, in order
list< Constant_Polynomial * > f4_control(const list< Abstract_Polynomial *> &F)
equivalent to buchberger(), but for Faugère’s F4 algorithm
void reduce_by_new()
reduces polynomials
Implementation of Faugère’s F4 algorithm.
Controls the creation of s-polynomials.
Prime_Field_Element * A
coefficient data
void report_critical_pairs(const list< T *>P, bool verbose=false)
A brief report on the number of critical pairs. If verbose is true, also lists them.
Polynomial_Ring & Rx
polynomial ring
vector< unsigned > nonzero_entries
number of nonzero entries of each row of A
vector< unsigned > heads
head of the polynomial (location of leading term)
void common_initialization(const Monomial_Ordering *ord=nullptr)
things all Monomial initializers must do
Definition: monomial.hpp:74
const list< Abstract_Polynomial * > & G
current basis of ideal
virtual Poly_Strategy_Data * strategy() const
strategy related information
Definition: polynomial.hpp:144