Macaulay2 Engine
Loading...
Searching...
No Matches
NCGroebner.cpp
Go to the documentation of this file.
2
3#include "NCAlgebras/FreeAlgebra.hpp" // for FreeAlgebra
4#include "NCAlgebras/FreeMonoid.hpp" // for FreeMonoid, operator<<, FreeM...
5#include "NCAlgebras/NCReduction.hpp" // for PolynomialHeap, getHeapType
6#include "NCAlgebras/OverlapTable.hpp" // for OverlapTable, operator<<
7#include "NCAlgebras/Word.hpp" // for Word
8#include "NCAlgebras/WordTable.hpp" // for Overlap, WordTable
9#include "buffer.hpp" // for buffer
10#include "interface/m2-types.h" // for M2_gbTrace, newline
11#include "myalloc.hpp" // for operator<<, AllocLogger
12#include "ring.hpp" // for Ring
13#include "ringelem.hpp" // for ring_elem
14#include "text-io.hpp" // for emit_line, emit
15
16#include <deque> // for deque
17#include <tuple> // for get, make_tuple
18#include <utility> // for pair
19
21 const ConstPolyList& input,
22 int hardDegreeLimit,
23 int strategy
24 )
25 : mFreeAlgebra(A),
26 mInput(input),
29 mHardDegreeLimit(hardDegreeLimit)
30{
31 if (M2_gbTrace >= 1)
32 {
33 buffer o;
34 o << "[NCGB] reduction heap: "
35 << getHeapName(getHeapType(strategy))
36 << newline;
37 emit_line(o.str());
38 }
39 Word tmpWord;
40 // process input polynomials
41 mIsGraded = true;
42 for (auto i = 0; i < mInput.size(); ++i)
43 {
44 auto d = freeAlgebra().heft_degree(*mInput[i]);
45 mGeneratorDegrees.push_back(d.first);
46 if (not d.second)
47 mIsGraded = false;
48 tmpWord = freeAlgebra().lead_word(*mInput[i]);
49 mOverlapTable.insert(d.first, // previously: freeAlgebra().monoid().wordHeft(tmpWord),
50 true,
51 std::make_tuple(i,-1,-1,true));
52 }
53 if (M2_gbTrace >= 1)
54 {
55 buffer o;
56 o << "[NCGB] input is " << (mIsGraded ? "homogeneous" : "inhomogeneous") << newline;
57 emit_line(o.str());
58 }
59}
60
61void NCGroebner::compute(int softDegreeLimit)
62{
63 if (mIsGraded)
64 computeHomogeneous(softDegreeLimit);
65 else
66 computeInhomogeneous(softDegreeLimit);
67}
68
69void NCGroebner::computeHomogeneous(int softDegreeLimit)
70{
71 size_t n_spairs = 0;
72 while (!mOverlapTable.isFinished(softDegreeLimit))
73 {
74 auto degSet = mOverlapTable.nextDegreeOverlaps();
75 auto toBeProcessed = degSet.second;
76 if (M2_gbTrace >= 1)
77 {
78 buffer o;
79 o << "[" << degSet.first << "](" << toBeProcessed->size() << ")";
80 emit(o.str());
81 }
82 while(!toBeProcessed->empty())
83 {
84 auto overlap = toBeProcessed->front();
85 //if this is a `real' overlap, and the overlap is not necessary, then move
86 //on to the next overlap.
87 if (std::get<1>(overlap) != -1 && !isOverlapNecessary(overlap))
88 {
89 toBeProcessed->pop_front();
90 if (M2_gbTrace >= 3)
91 {
92 std::cout << "Reduction avoided using 2nd criterion." << std::endl;
93 std::cout << "table after pop:";
94 mOverlapTable.dump(std::cout,true);
95 }
96 // TODO: is this logic correct? Is the overlap actually skipped?
97 continue;
98 }
99 auto overlapPoly = createOverlapPoly(overlap);
100
101 n_spairs++;
102 auto redOverlapPoly = twoSidedReduction(overlapPoly);
103 delete overlapPoly;
104
105 if (!freeAlgebra().is_zero(*redOverlapPoly))
106 {
107 addToGroebnerBasis(redOverlapPoly);
109 updateOverlaps(redOverlapPoly);
110 }
111 else
112 {
113 // if reduction is zero
114 if (M2_gbTrace >= 4)
115 {
116 std::cout << "Overlap " << overlap << " reduced to zero."
117 << std::endl;
118 }
119 }
120 toBeProcessed->pop_front();
121 }
122 // remove the lowest degree overlaps from the overlap table
123 mOverlapTable.removeLowestDegree();
124 }
125 if (M2_gbTrace >= 1)
126 {
127 buffer o;
128 o << "[NCGB] number of spair reductions: " << n_spairs;
129 emit_line(o.str());
130 }
131}
132
133void NCGroebner::computeInhomogeneous(int softDegreeLimit)
134{
135 size_t n_spairs = 0;
136 while (!mOverlapTable.isFinished(softDegreeLimit))
137 {
138 auto degSet = mOverlapTable.nextDegreeOverlaps();
139 auto toBeProcessed = degSet.second;
140 if (M2_gbTrace >= 1)
141 {
142 buffer o;
143 o << "[" << degSet.first << "](" << toBeProcessed->size() << ")";
144 emit(o.str());
145 }
146 while(!toBeProcessed->empty())
147 {
148 auto overlap = toBeProcessed->front();
149 //if this is a `real' overlap, and the overlap is not necessary, then move
150 //on to the next overlap.
151 if (std::get<1>(overlap) != -1 && !isOverlapNecessary(overlap))
152 {
153 toBeProcessed->pop_front();
154 if (M2_gbTrace >= 2)
155 {
156 std::cout << "Reduction avoided using 2nd criterion." << std::endl;
157 std::cout << "table after pop:";
158 mOverlapTable.dump(std::cout,true);
159 }
160 // TODO: is this logic correct? Is the overlap actually skipped?
161 continue;
162 }
163 auto overlapPoly = createOverlapPoly(overlap);
164
165 n_spairs++;
166 auto redOverlapPoly = twoSidedReduction(overlapPoly);
167 delete overlapPoly;
168
169 if (!freeAlgebra().is_zero(*redOverlapPoly))
170 {
171 addToGroebnerBasis(redOverlapPoly);
173 updateOverlaps(redOverlapPoly);
174 }
175 else
176 {
177 // if reduction is zero
178 if (M2_gbTrace >= 4)
179 {
180 std::cout << "Overlap " << overlap << " reduced to zero."
181 << std::endl;
182 }
183 }
184 toBeProcessed->pop_front();
185 }
186 // remove the lowest degree overlaps from the overlap table
187 mOverlapTable.removeLowestDegree();
188 }
189 if (M2_gbTrace >= 1)
190 {
191 buffer o;
192 o << "[NCGB] number of spair reductions: " << n_spairs;
193 emit_line(o.str());
194 }
195}
196
198{
199 // add in auto-reduction here?
201 mGroebner.push_back(toAdd);
202}
203
205{
206 if (mGroebner.size() <= 1) return;
207 const Poly& lastPoly = *(mGroebner[mGroebner.size()-1]);
208 Monom leadMon = lastPoly.cbegin().monom();
209 for (auto fPtr = mGroebner.begin(); fPtr != mGroebner.end() - 1; ++fPtr)
210 {
211 ring_elem foundCoeff = getCoeffOfMonom(**fPtr,leadMon);
212 if (!freeAlgebra().coefficientRing()->is_zero(foundCoeff))
213 {
214 Poly* result = new Poly;
215 freeAlgebra().subtractScalarMultipleOf(*result,**fPtr,lastPoly,foundCoeff);
216 freeAlgebra().swap(**fPtr,*result);
217 }
218 }
219}
220
222{
223 for (auto t = f.cbegin(); t != f.cend(); ++t)
224 {
225 if (freeAlgebra().monoid().isEqual(t.monom(),m))
226 return t.coeff();
227 }
228 return freeAlgebra().coefficientRing()->zero();
229}
230
232{
233 std::vector<Overlap> newOverlaps;
234 Word newLeadWord = freeAlgebra().lead_word(*toAdd);
235
236 // the word table insert places the right overlaps into newOverlaps
237 mWordTable.insert(newLeadWord,newOverlaps);
238 insertNewOverlaps(newOverlaps);
239
240 newOverlaps.clear();
241 // this function finds the left overlaps with the most recently
242 // inserted word.
243 mWordTable.leftOverlaps(newOverlaps);
244 insertNewOverlaps(newOverlaps);
245}
246
248{
249 //return reinterpret_cast<const ConstPolyList&>(mGroebner);
250 return mGroebner;
251}
252
253// new version of reduction code which uses a heap structure
254auto NCGroebner::twoSidedReduction(const Poly* reducee) const -> Poly*
255{
256 // stats for benchmarking, debug only...
257 size_t loop_count = 0;
258 size_t nterms = 0; // number of terms added in to the heap
259
260 // easy access to variables in the class
261 const FreeAlgebra& A{ freeAlgebra() };
262 const PolyList& reducers{ currentValue() };
263 const WordTable& W{ mWordTable };
264 //const SuffixTree& W{ mWordTable };
265
266 // pair will be (i,j) where the ith word in wordtable appears in word in position j
267 std::pair<int,int> subwordPos;
268 Word leftWord, rightWord;
269
270 Poly* remainder = new Poly;
271 Poly tmp; // temp polynomial for seeing what is being added to heap.
272
273 mHeap->clear();
274 nterms += reducee->numTerms();
275 mHeap->addPolynomial(*reducee);
276
278
279 while (not mHeap->isZero())
280 {
281 loop_count++;
282
283 // Find (left, right, index) s.t. left*reducers[index]*right == leadMonomial(reduceeSoFar).
284 Word reduceeLeadWord;
285 std::pair<Monom, ring_elem> LT { mHeap->viewLeadTerm() };
286
287 A.monoid().wordFromMonom(reduceeLeadWord, LT.first);
288
289 if (W.subword(reduceeLeadWord,subwordPos))
290 {
291 // If there is one, perform reduceeSoFar -= coef * left * reducers[index] * right
292 A.monoid().wordPrefixFromMonom(leftWord, LT.first, subwordPos.second);
293 A.monoid().wordSuffixFromMonom(rightWord, LT.first, W[subwordPos.first].size()+subwordPos.second);
294
295 ring_elem c = A.coefficientRing()->negate(LT.second);
296 ring_elem d = reducers[subwordPos.first]->cbegin().coeff();
297 // TODO: Check to see if d is a unit before inverting.
298 auto coeffNeeded = A.coefficientRing()->divide(c,d);
299
300 A.clear(tmp);
302 *reducers[subwordPos.first],
303 coeffNeeded,
304 leftWord,
305 rightWord);
306 nterms += tmp.numTerms();
307 mHeap->addPolynomial(tmp);
308 }
309 else
310 {
311 // If none, copy that term to the remainder (use add_to_end)
312 // and subtract that term
313 A.add_to_end(*remainder, LT.second, LT.first);
314 mHeap->removeLeadTerm();
315 }
316 }
317 if (M2_gbTrace >= 5)
318 {
319 std::cout << "reduction: " << "#steps: " << loop_count << " " << FreeMonoidLogger() << std::endl;
320 std::cout << " " << "#terms: " << nterms << std::endl;
321 std::cout << " " << AllocLogger() << std::endl;
322 }
323 return remainder;
324}
325
327{
329 for (auto i = reducees.cbegin(); i != reducees.cend(); ++i)
330 result.push_back(twoSidedReduction(*i));
331 return result;
332}
333
335{
336 // this function clears out mWordTable, places mInput in mGroebner,
337 // and builds the word table.
338 mWordTable.clear();
339 for (auto& f : mInput)
340 {
341 Poly *fCopy = new Poly;
342 freeAlgebra().copy(*fCopy,*f);
343 mGroebner.push_back(fCopy);
344 auto i = f->cbegin();
345 Word tmp;
346 freeAlgebra().monoid().wordFromMonom(tmp,i.monom());
347 mWordTable.insert(tmp);
348 }
349}
350
352 const PolyList& polyList,
353 int polyIndex1,
354 int overlapIndex,
355 int polyIndex2) -> Poly*
356{
357 // here, polyIndex1 and 2 are indices into polyList, and overlapIndex
358 // is the index where the overlap starts in the polynomial pointed in
359 // by *polyIndex1*.
360 Poly* result = new Poly;
361 Poly tmp1, tmp2;
363 prefix = A.lead_word_prefix(*polyList[polyIndex1], overlapIndex);
364 suffix = A.lead_word_suffix(*polyList[polyIndex2], *(polyList[polyIndex1]->cbegin().monom().begin()) - A.monoid().numWeights() - 1 - overlapIndex);
365 A.mult_by_term_right(tmp1, *polyList[polyIndex1], A.coefficientRing()->from_long(1), suffix);
366 A.mult_by_term_left(tmp2, *polyList[polyIndex2], A.coefficientRing()->from_long(1), prefix);
367 A.subtract(*result, tmp1, tmp2);
368 return result;
369}
370
372{
373 if (std::get<1>(overlap) == -1)
374 {
375 // if we are here, then 'overlap' represents a generator
376 const Poly* f = mInput[std::get<0>(overlap)];
377 Poly * result = new Poly;
378 freeAlgebra().copy(*result, *f);
379 return result;
380 }
381 else return createOverlapPoly(freeAlgebra(),
382 mGroebner,
383 std::get<0>(overlap),
384 std::get<1>(overlap),
385 std::get<2>(overlap));
386}
387
388auto NCGroebner::createOverlapLeadWord(Poly& wordAsPoly, Overlap o) const -> void
389{
390 auto A = freeAlgebra();
391 Poly tmp;
392 Word prefix = A.lead_word_prefix(*mGroebner[std::get<0>(o)], std::get<1>(o));
393 A.lead_term_as_poly(tmp, *mGroebner[std::get<2>(o)]);
394 A.mult_by_term_left(wordAsPoly, tmp, A.coefficientRing()->from_long(1), prefix);
395}
396
397// not what we need - need an overlapDegree function
399{
400 Word tmp = freeAlgebra().lead_word(*mGroebner[std::get<2>(o)]);
401 return std::get<1>(o) + tmp.size();
402}
403
405// overlap: of a pair of words v, w, v = a*s, w = s*b, returns the
406// heft degree of a*s*b.
407// o = triple (index of left GB element, pos, index of right GB element,
408// pos is the location in left GB element where s starts.
409{
410 Word tmpL = freeAlgebra().lead_word(*mGroebner[std::get<0>(o)]);
411 Word tmpR = freeAlgebra().lead_word(*mGroebner[std::get<2>(o)]);
412 int len_of_s = tmpL.size() - std::get<1>(o);
413 return freeAlgebra().monoid().wordHeft(tmpL) +
414 freeAlgebra().monoid().wordHeft(tmpR, len_of_s);
415}
416
417auto NCGroebner::printOverlapData(std::ostream& o, Overlap overlap) const -> void
418{
419 buffer b1,b2;
420 freeAlgebra().elem_text_out(b1,*mGroebner[std::get<0>(overlap)], true, true, true);
421 freeAlgebra().elem_text_out(b2,*mGroebner[std::get<2>(overlap)], true, true, true);
422 o << "Left Poly : " << b1.str() << std::endl;
423 o << "Overlap Pos : " << std::get<1>(overlap) << std::endl;
424 o << "Right Poly : " << b2.str() << std::endl;
425}
426
427auto NCGroebner::displayGroebnerBasis(std::ostream& o) const -> void
428{
429 o << "Current GB:" << std::endl;
430 for (auto f : mGroebner)
431 {
432 buffer b1;
433 freeAlgebra().elem_text_out(b1,*f, true, true, true);
434 o << b1.str() << std::endl;
435 }
436}
437
438auto NCGroebner::insertNewOverlaps(std::vector<Overlap>& newOverlaps) -> void
439{
440 for (auto newOverlap : newOverlaps)
441 {
442 // check to see if the overlap is necessary before insertion
443 // FM: not sure if we should do this here, or in the loop.
444 // std::cout << "Checking overlap: " << newOverlap << std::endl;
445 // printOverlapData(std::cout, newOverlap);
446 if (isOverlapNecessary(newOverlap))
447 {
448 mOverlapTable.insert(overlapHeft(newOverlap),
449 false,
450 newOverlap);
451 }
452 else
453 {
454 if (M2_gbTrace >= 3)
455 {
456 std::cout << "Reduction avoided using 2nd criterion." << std::endl;
457 }
458 }
459 // std::cout << "Overlap check complete." << std::endl;
460 }
461}
462
464{
465 // this function tests if the lead word of the overlap polynomial
466 // of o is a multiple of another pattern in the word table.
467
468 // need to be careful, however, since an overlap lead word is trivially
469 // a multiple of the words used to build it. These possibilities must be discarded
470 bool retval;
471
472 auto A = freeAlgebra();
473 Poly tmp;
474 Word w;
475
477 w = A.lead_word(tmp);
478 retval = !mWordTable.isNontrivialSuperword(w, std::get<0>(o), std::get<2>(o));
479 return retval;
480}
481
482// Local Variables:
483// compile-command: "make -C $M2BUILDDIR/Macaulay2/e "
484// indent-tabs-mode: nil
485// End:
Free associative algebra k<x_1,...,x_n> over an arbitrary coefficient ring.
FreeMonoid — monoid of length-prefixed non-commutative words with weight-vector prefix.
NCGroebner — Buchberger-style two-sided Gröbner basis driver over a FreeAlgebra.
std::unique_ptr< PolynomialHeap > makePolynomialHeap(HeapType type, const FreeAlgebra &F)
std::string getHeapName(HeapType type)
HeapType getHeapType(int strategy)
PolynomialHeap abstract interface — batched-subtraction heap for non-commutative reduction.
std::tuple< int, int, int, bool > Overlap
OverlapTable — degree-sorted queue of pending word overlaps for non-commutative GB drivers.
gc_vector< const Poly * > ConstPolyList
Polynomial< CoefficientRingType > Poly
gc_vector< Poly * > PolyList
Word prefix(const Word vec, int lengthOfPrefix)
Word suffix(const Word vec, int indexOfSuffix)
Word and WordWithData — non-owning views over the flat-int encoding of a non-commutative word.
WordTable / WordWithDataTable — leading-word indices for non-commutative Gröbner basis lookup.
Append-only GC-backed byte buffer used throughout the engine for text output.
Process-wide allocation counter used by StatsAllocator for debugging and benchmarking.
Definition myalloc.hpp:63
const Ring * coefficientRing() const
void add_to_end(Poly &f, const Poly &g) const
void makeMonicInPlace(Poly &f) const
std::pair< int, bool > heft_degree(const Poly &f) const
void subtractScalarMultipleOf(Poly &result, const Poly &f, const Poly &g, ring_elem coeff) const
void clear(Poly &f) const
Word lead_word(const Poly &f) const
const FreeMonoid & monoid() const
void mult_by_term_left_and_right(Poly &result, const Poly &f, const ring_elem c, const Monom leftM, const Monom rightM) const
void swap(Poly &f, Poly &g) const
Free associative algebra over a coefficient ring: the non-commutative analogue of PolynomialRing.
void wordFromMonom(Word &result, const Monom &m) const
void wordPrefixFromMonom(Word &result, const Monom &m, int endIndex) const
void wordSuffixFromMonom(Word &result, const Monom &m, int beginIndex) const
static void reset()
Static counter for non-commutative monomial comparisons.
auto overlapWordLength(Overlap o) const -> int
void computeInhomogeneous(int softDegreeLimit)
auto twoSidedReduction(const ConstPolyList &reducees) const -> ConstPolyList
std::unique_ptr< PolynomialHeap > mHeap
void updateOverlaps(const Poly *toAdd)
NCGroebner(const FreeAlgebra &A, const ConstPolyList &input, int hardDegreeLimit, int strategy)
std::vector< int > mGeneratorDegrees
void autoreduceByLastElement()
const FreeAlgebra & mFreeAlgebra
void compute(int softDegreeLimit)
int mHardDegreeLimit
int mTopComputedDegree
auto insertNewOverlaps(std::vector< Overlap > &newOverlaps) -> void
auto isOverlapNecessary(Overlap o) const -> bool
auto overlapHeft(Overlap o) const -> int
void computeHomogeneous(int softDegreeLimit)
const PolyList & currentValue() const
auto createOverlapLeadWord(Poly &wordAsPoly, Overlap o) const -> void
OverlapTable mOverlapTable
static auto createOverlapPoly(const FreeAlgebra &A, const PolyList &polyList, int polyIndex1, int polyIndex2, int overlapIndex) -> Poly *
PolyList mGroebner
auto displayGroebnerBasis(std::ostream &o) const -> void
const FreeAlgebra & freeAlgebra() const
WordTable mWordTable
auto printOverlapData(std::ostream &o, Overlap overlap) const -> void
ring_elem getCoeffOfMonom(const Poly &f, const Monom &m)
const ConstPolyList mInput
auto initReductionOnly() -> void
void addToGroebnerBasis(Poly *toAdd)
virtual ring_elem divide(const ring_elem f, const ring_elem g) const =0
ring_elem zero() const
Definition ring.hpp:359
virtual ring_elem negate(const ring_elem f) const =0
int size() const
Definition Word.hpp:74
Non-owning view of a non-commutative word: [begin, end) of int variable indices.
Definition Word.hpp:56
bool subword(Word word, std::pair< int, int > &output) const
Index of Words (non-commutative monomials) with subword, prefix/suffix, and overlap lookup used by th...
Definition WordTable.hpp:89
int size()
Definition buffer.hpp:70
char * str()
Definition buffer.hpp:72
VALGRIND_MAKE_MEM_DEFINED & result(result)
char newline[]
Definition m2-types.cpp:49
int M2_gbTrace
Definition m2-types.cpp:52
Engine-to-interpreter type vocabulary across the C++ / .dd boundary.
AllocLogger / StatsAllocator — single-threaded debug/benchmark instrumentation.
Ring — the legacy abstract base class for every coefficient and polynomial ring.
TermIterator< Nterm > begin(Nterm *ptr)
Definition ringelem.cpp:4
ring_elem — the universal value type carried by every Ring* in the engine.
Non-owning view onto a [length, degree, v1, v2, ..., vn] packed monomial in some externally managed b...
const int LT
Definition style.hpp:39
void emit_line(const char *s)
Definition text-io.cpp:47
void emit(const char *s)
Definition text-io.cpp:41
Text-formatting helpers layered on buffer: bignum print, line wrapping, M2_gbTrace-gated emit.