Macaulay2 Engine
Loading...
Searching...
No Matches
matrix-kbasis.cpp
Go to the documentation of this file.
1
33
34#include <stddef.h> // for NULL
35#include <vector> // for vector
36
37#include "ExponentVector.hpp" // for exponents, exponents_t
38#include "interface/m2-mem.h" // for freemem
39#include "engine-includes.hpp" // for M2_arrayint, M2_arrayint_struct
40#include "error.h" // for ERROR
41#include "freemod.hpp" // for FreeModule
42#include "int-bag.hpp" // for Bag
43#include "interrupted.hpp" // for system_interrupted
44#include "matrix-con.hpp" // for MatrixConstructor
45#include "matrix.hpp" // for Matrix
46#include "monideal.hpp" // for MonomialIdeal
47#include "monoid.hpp" // for Monoid, ALLOCATE_EXPONENTS, EXPONENT_...
48#include "newdelete.hpp" // for newarray_atomic, newarray_atomic_clear
49#include "polyring.hpp" // for PolynomialRing
50#include "ring.hpp" // for Ring
51#include "ringelem.hpp" // for ring_elem, vec
52#include "style.hpp" // for EQ
53#include "util.hpp" // for M2_arrayint_to_stdvector
54
69class KBasis
70{
71 // A class for construction of
72 // (a) kbasis of a module, in a given degree
73 // (b) kbasis of a module, which is finite
74 // (c) kbasis of a map
75 private:
77 const Monoid *D;
78 const Monoid *M;
79
81
83
85 std::vector<int> mHeftVector; // length is D->n_vars(), or less.
86 // Dot product with a degree of a variable
87 // in 'mVariables' will give a non-negative value.
88
90 // var_wts[i] is the (mHeftVector . deg(mVariables[i]-th variable))
91 int *var_wts;
92 std::vector<int> mVariables;
95 int limit; // if >= 0, then stop after that number.
96
97 const int *lo_degree; // if non-null, the lowest degree to collect, of length
98 // mHeftVector.size()
99 const int *hi_degree; // if non-null, the highest degree to collect, of
100 // length mHeftVector.size()
101
102 // In the singly graded case: collect every monomial whose weight lies >=
103 // weight of
104 // lo_degree (kb_target_lo_weight), and <= weight of hi_degree
105 // (kb_target_hi_weight).
106 // (resp -infty, infty, if lo_degree resp hi_degree is null). (NO: that's not
107 // right (!), because ordering of the weights might
108 // be the reverse of the ordering of the degrees, if the heft vector is
109 // negative.)
110 //
111 // in multi-graded case, we can only collect one degree, or the entire module.
112 // so: lo_degree and hi_degree must be the same (null, or same degree vector).
113
114 int *kb_exp; // exponent vector being constructed recursively
115 int kb_exp_weight; // weight of this exponent vector
116
117 int kb_target_lo_weight; // only valid if lo_degree is not null
118 int kb_target_hi_weight; // only valid if hi_degree is not null
119
120 int *kb_target_multidegree; // in multigraded case this is not null, and is
121 // the
122 // degree vector which is our target.
123 int *kb_exp_multidegree; // used in recursion, and also to unpack the
124 // multidegree even in the singly graded case
125
127
128 int *kb_mon;
129
131
132 bool kb_error; // set if ERROR has been called, e.g. if a full basis of a non
133 // 0-diml module is asked for
134
135 void insert();
136 bool try_insert_full();
137 bool try_insert_sg();
138 bool try_insert_mg();
139 bool backtrack(int &curr);
140 bool backtrack_mg(int &curr);
141 void basis0_full();
143 void basis0_multi_graded();
144
145 KBasis(const Matrix *bottom,
146 const int *lo_degree,
147 const int *hi_degree,
148 std::vector<int> heftvec,
149 std::vector<int> varlist,
150 bool do_truncation,
151 int limit);
152
154 void compute();
155
156 Matrix *value() { return (kb_error ? nullptr : mat.to_matrix()); }
157
158 public:
159 static Matrix *k_basis(const Matrix *bottom,
162 std::vector<int> heftvec,
163 std::vector<int> varlist,
164 bool do_truncation,
165 int limit);
166};
167
168KBasis::KBasis(const Matrix *bottom,
169 const int *lo_degree0,
170 const int *hi_degree0,
171 std::vector<int> heftvec,
172 std::vector<int> varlist,
173 bool do_truncation0,
174 int limit0)
175 : bottom_matrix(bottom),
176 mHeftVector(heftvec),
177 mVariables(varlist),
178 do_truncation(do_truncation0),
179 weight_has_zeros(false),
180 limit(limit0),
181 lo_degree(lo_degree0),
182 hi_degree(hi_degree0),
183 kb_error(false)
184{
185 P = bottom->get_ring()->cast_to_PolynomialRing();
186 M = P->getMonoid();
187 D = P->get_degree_ring()->getMonoid();
188
189 if (lo_degree == nullptr && hi_degree == nullptr) { computation_type = KB_FULL; }
190 else if (mHeftVector.size() == 1) { computation_type = KB_SINGLE; }
191 else { computation_type = KB_MULTI; }
192
193 // Compute the (non-negative) weights of each of the variables in
194 // 'mVariables'.
195
196 var_wts = newarray_atomic(int, mVariables.size());
197 var_degs = newarray_atomic(int, mVariables.size() * mHeftVector.size());
198 int *exp =
199 newarray_atomic(int, D->n_vars()); // used to hold exponent vectors
200 int next = 0;
201 for (int i = 0; i < mVariables.size(); i++, next += mHeftVector.size())
202 {
203 int v = mVariables[i];
204 D->to_expvector(M->degree_of_var(v), exp);
206 if (var_wts[i] == 0) weight_has_zeros = true;
207 exponents::copy(mHeftVector.size(), exp, var_degs + next);
208 }
209 freemem(exp);
210
211 // Set the recursion variables
212 kb_exp = newarray_atomic_clear(int, P->n_vars());
213 kb_exp_weight = 0;
214
215 if (lo_degree != nullptr)
218 if (hi_degree != nullptr)
221
222 if (lo_degree && hi_degree && mHeftVector.size() == 1 && mHeftVector[0] < 0)
223 {
224 int t = kb_target_lo_weight;
227 }
228
229 kb_mon = M->make_one();
230
231 mat = MatrixConstructor(bottom->rows(), 0);
232 kb_exp_multidegree = D->make_one();
233
234 if (mHeftVector.size() > 1 && lo_degree != nullptr)
235 {
236 kb_target_multidegree = D->make_one();
238 }
239 else { kb_target_multidegree = nullptr; }
240}
241
243{
244 // We have a new basis element
245
246 M->from_expvector(kb_exp, kb_mon);
247 ring_elem r = P->make_flat_term(P->getCoefficients()->one(), kb_mon);
248 vec v = P->make_vec(kb_comp, r);
249 mat.append(v);
250 if (limit > 0) limit--;
251}
252
253inline bool KBasis::backtrack(int &curr)
254{
255 // if we are the end, decrease the last entry to 0
256 if (curr == mVariables.size() - 1)
257 {
258 kb_exp_weight -= var_wts[curr] * kb_exp[mVariables[curr]];
259 kb_exp[mVariables[curr]] = 0;
260 do {
261 curr--;
262 } while (curr >= 0 && kb_exp[mVariables[curr]] == 0);
263 }
264 if (curr < 0) return false;
265 kb_exp[mVariables[curr]]--;
266 kb_exp_weight -= var_wts[curr];
267 curr++;
268 return true;
269}
270
272{
273 Bag *b;
274 if (kb_monideal->search_expvector(kb_exp, b)) return false;
275 insert();
276 return true;
277}
278
280{
281 // insert the all zeros vector
282 if (!try_insert_full()) return;
283 if (mVariables.size() == 0) return;
284
285 int curr = 0;
286 do {
287 int vcurr = mVariables[curr];
288 // increase the curr index until we reach a limit
289 do {
290 if (limit == 0 || system_interrupted()) return;
291 kb_exp[vcurr]++;
292 kb_exp_weight += var_wts[curr];
293 } while (try_insert_full());
294 } while (backtrack(curr));
295}
296
298{
299 Bag *b;
300 if (kb_monideal->search_expvector(kb_exp, b)) return false;
302 {
303 if (do_truncation) insert();
304 return false;
305 }
307 return true;
308}
309
311{
312 // insert the all zeros vector
313 if (!try_insert_sg()) return;
314 if (mVariables.size() == 0) return;
315 int curr = 0;
316 do {
317 int vcurr = mVariables[curr];
318 // increase the curr index until we reach a limit
319 do {
320 if (limit == 0 || system_interrupted()) return;
321 kb_exp[vcurr]++;
322 kb_exp_weight += var_wts[curr];
323 } while (try_insert_sg());
324 } while (backtrack(curr));
325}
326
327inline bool KBasis::backtrack_mg(int &curr)
328{
329 int vcurr = -1;
330 // if we are the end, decrease the last entry to 0
331 if (curr == mVariables.size() - 1)
332 {
333 vcurr = mVariables[curr];
334 kb_exp_weight -= var_wts[curr] * kb_exp[vcurr];
337 var_degs + (mHeftVector.size() * curr),
338 -kb_exp[vcurr],
340 kb_exp[vcurr] = 0;
341 do {
342 curr--;
343 } while (curr >= 0 && kb_exp[mVariables[curr]] == 0);
344 }
345 if (curr < 0) return false;
346 vcurr = mVariables[curr];
347 kb_exp[vcurr]--;
348 kb_exp_weight -= var_wts[curr];
351 var_degs + (mHeftVector.size() * curr),
353 curr++;
354 return true;
355}
356
358{
359 Bag *b;
360 if (kb_monideal->search_expvector(kb_exp, b)) return false;
362 {
363 if (do_truncation) insert();
364 return false;
365 }
367 {
371 insert();
372 }
373 return true;
374}
375
377{
378 // insert the all zeros vector
379 if (!try_insert_mg()) return;
380 if (mVariables.size() == 0) return;
381
382 int curr = 0;
383 do {
384 int vcurr = mVariables[curr];
385 // increase the curr index until we reach a limit
386 do {
387 if (limit == 0 || system_interrupted()) return;
388 kb_exp[vcurr]++;
389 kb_exp_weight += var_wts[curr];
392 var_degs + (mHeftVector.size() * curr),
394 } while (try_insert_mg());
395 } while (backtrack_mg(curr));
396}
397
399 std::vector<int> varlist)
400{
401 // returns true iff all the variables in varlist have some pure power in M
402 std::vector<int> lcms = M2_arrayint_to_stdvector<int>(M->lcm());
404 for (int i = 0; i < lcms.size(); i++) exp[i] = 0;
405 for (int i = 0; i < varlist.size(); i++)
406 {
407 Bag *b;
408 int v = varlist[i];
409 exp[v] = lcms[v];
410 if (!M->search_expvector(exp, b)) return false;
411 exp[v] = 0;
412 }
413 return true;
414}
415
417// Only the lead monomials of the two matrices 'this' and 'bottom' are
418// considered. Thus, you must perform the required GB's elsewhere.
419// Find a basis for (image this)/(image bottom) in degree d.
420// If 'd' is NULL, first check that (image this)/(image bottom) has
421// finite dimension, and if so, return a basis.
422// If 'd' is not NULL, it is an element of the degree monoid.
423{
424 if (limit == 0) return;
425 std::vector<int> zero_vars;
427 {
428 for (int i = 0; i < mVariables.size(); i++)
429 {
430 if (var_wts[i] == 0) { zero_vars.push_back(i); }
431 }
432 }
433
434 for (int i = 0; i < bottom_matrix->n_rows(); i++)
435 {
436 if (system_interrupted()) return;
437 kb_comp = i;
438
439 // Make the monomial ideal: this should contain only
440 // monomials involving 'mVariables'.
441 kb_monideal = bottom_matrix->make_monideal(i, true);
442 // the true means: over ZZ, don't consider monomials with non-unit lead
443 // coeffs
444
445 if (kb_monideal->is_one()) continue;
446 if (hi_degree == nullptr)
447 {
448 // check here that kb_monideal is 0-dimensional
449 // (at least for the variables being used):
451 {
452 kb_error = true;
453 ERROR("module given is not finite over the base");
454 return;
455 }
456 }
457 else if (zero_vars.size() > 0)
458 {
459 // if we have any variables with zero degrees, then kb_monideal needs
460 // to be 0-dimensional in those variables.
461 if (!all_have_pure_powers(kb_monideal, zero_vars))
462 {
463 kb_error = true;
464 ERROR(
465 "module given is not finite over the zero-degree variables");
466 return;
467 }
468 }
469
470 const int *component_degree = bottom_matrix->rows()->degree(i);
471 D->to_expvector(component_degree, kb_exp_multidegree);
474
475 // Do the recursion
476 switch (computation_type)
477 {
478 case KB_FULL:
479 basis0_full();
480 break;
481 case KB_SINGLE:
483 break;
484 case KB_MULTI:
486 break;
487 }
488 }
489}
490
491Matrix /* or null */ *KBasis::k_basis(const Matrix *bottom,
494 std::vector<int> heftvec,
495 std::vector<int> varlist,
496 bool do_truncation,
497 int limit)
498{
499 // There are essentially 3 situations:
500 // (a) basis(M) -- lo_degree and hi_degree are not given
501 // in this case, only need that for each variable in 'varlist',
502 // some power is an initial term of 'bottom' (for each row of 'bottom').
503 // heft is not used here, or considered.
504 // (b) basis(lo,hi,M) -- case when the ring is singly-graded
505 // one of lo and hi must be given. (otherwise we are in case (a) above)
506 // In this case, heft is a list with one element in it.
507 // Assume: heft * deg(x) > 0, for all x in 'varlist'.
508 // In this situation: we use kb_target_lo_heft, kb_target_hi_heft
509 // (c) basis(d, d, M) -- ring is multi-graded
510 // ASSUME: deg_d(x) . heft > 0 for all vars 'x' in 'varlist'
511 // where deg_d(x) consists of the first #d components of deg(x)
512 // ASSUME: 1 <= #d <= degreeRank of the ring
513 // use kb_target_multidegree, kb_target_lo_heft
514 // and kb_exp_multidegree (of length #d).
515 // if do_truncation, then any generator with heft > kb_target_lo_heft is
516 // placed in the resulting matrix
517 //
518 // Further assumptions:
519 // 1 <= #heft <= degreeRank P
520 // #heft = #lo_degree = #hi_degree, if these are not 0.
521 //
522 //
523 // Do some checks first, return 0 if not good.
524 const PolynomialRing *P = bottom->get_ring()->cast_to_PolynomialRing();
525 if (P == nullptr) return Matrix::identity(bottom->rows());
526
527 const PolynomialRing *D = P->get_degree_ring();
528 const int *lo = lo_degree->len > 0 ? lo_degree->array : nullptr;
529 const int *hi = hi_degree->len > 0 ? hi_degree->array : nullptr;
530
531 if (heftvec.size() > D->n_vars())
532 {
533 ERROR("expected heft vector of length <= %d", D->n_vars());
534 return nullptr;
535 }
536
537 if (lo && heftvec.size() != lo_degree->len)
538 {
539 ERROR("expected degrees of length %d", heftvec.size());
540 return nullptr;
541 }
542
543 if (hi && heftvec.size() != hi_degree->len)
544 {
545 ERROR("expected degrees of length %d", heftvec.size());
546 return nullptr;
547 }
548
549 // If heftvec.size() is > 1, and both lo and hi are non-null,
550 // they need to be the same
551 if (heftvec.size() > 1 && lo && hi)
552 for (int i = 0; i < heftvec.size(); i++)
553 if (lo_degree->array[i] != hi_degree->array[i])
554 {
555 ERROR("expected degree bounds to be equal");
556 return nullptr;
557 }
558
559 KBasis KB(bottom, lo, hi, heftvec, varlist, do_truncation, limit);
560
561 // If either a low degree, or high degree is given, then we require a
562 // non-negative heft vector:
563 if (lo || hi)
564 for (int i = 0; i < varlist.size(); i++)
565 if (KB.var_wts[i] < 0)
566 {
567 ERROR(
568 "basis: computation requires a heft form non-negative on the "
569 "degrees of the variables");
570 return nullptr;
571 }
572 // This next line will happen if both lo,hi degrees are given, and they are
573 // different. This can only be the singly generated case, and in that case
574 // the degrees are in the wrong order, so return with 0 basis.
575 if (lo != nullptr && hi != nullptr && lo[0] > hi[0]) return KB.value();
576
577 KB.compute();
578 if (system_interrupted()) return nullptr;
579 return KB.value();
580}
581
583 M2_arrayint hi_degree,
584 M2_arrayint heftvec,
585 M2_arrayint varlist,
586 bool do_truncation,
587 int limit) const
588{
589 auto heft = M2_arrayint_to_stdvector<int>(heftvec);
590 auto vars = M2_arrayint_to_stdvector<int>(varlist);
591 return KBasis::k_basis(
592 this, lo_degree, hi_degree, heft, vars, do_truncation, limit);
593}
594
595// Local Variables:
596// compile-command: "make -C $M2BUILDDIR/Macaulay2/e matrix-kbasis.o "
597// indent-tabs-mode: nil
598// End:
exponents::Exponents exponents_t
Dense exponent-vector template [e_0, ..., e_{nvars-1}] for monomial operations.
static Exponent weight(int nvars, ConstExponents a, const std::vector< Exponent > &wts)
static void divide(int nvars, ConstExponents a, ConstExponents b, Exponents result)
static void mult(int nvars, ConstExponents a, ConstExponents b, Exponents result)
static void copy(int nvars, ConstExponents a, Exponents result)
static void multpower(int nvars, ConstExponents a, ConstExponents b, const Exponent n, Exponents result)
static int lex_compare(int nvars, ConstExponents a, ConstExponents b)
const Monoid * M
int * kb_exp_multidegree
const Matrix * bottom_matrix
std::vector< int > mVariables
void insert()
bool try_insert_mg()
const Monoid * D
MonomialIdeal * kb_monideal
const int * lo_degree
bool backtrack_mg(int &curr)
MatrixConstructor mat
bool do_truncation
int * var_degs
bool try_insert_full()
const int * hi_degree
bool backtrack(int &curr)
const PolynomialRing * P
enum KBasis::@037164154004220152265360251131226313056117246356 computation_type
int kb_target_hi_weight
std::vector< int > mHeftVector
int kb_target_lo_weight
int * kb_target_multidegree
static Matrix * k_basis(const Matrix *bottom, M2_arrayint lo_degree, M2_arrayint hi_degree, std::vector< int > heftvec, std::vector< int > varlist, bool do_truncation, int limit)
int kb_exp_weight
Matrix * value()
int * var_wts
void basis0_singly_graded()
void compute()
bool weight_has_zeros
void basis0_full()
KBasis(const Matrix *bottom, const int *lo_degree, const int *hi_degree, std::vector< int > heftvec, std::vector< int > varlist, bool do_truncation, int limit)
void basis0_multi_graded()
bool try_insert_sg()
const Ring * get_ring() const
Definition matrix.hpp:134
Matrix(const FreeModule *rows, const FreeModule *cols, const_monomial degree_shift, VECTOR(vec) &entries)
Definition matrix.cpp:32
const FreeModule * rows() const
Definition matrix.hpp:144
const Matrix * basis(M2_arrayint lo_degree, M2_arrayint hi_degree, M2_arrayint wt, M2_arrayint vars, bool do_truncation, int limit) const
Mutable builder used to assemble an immutable Matrix one column (or one term) at a time.
Engine-side commutative monomial monoid: variable names, ordering, multidegree machinery,...
Definition monoid.hpp:89
int search_expvector(const_exponents m, Bag *&b) const
Definition monideal.cpp:214
M2_arrayint lcm() const
Definition monideal.cpp:819
Engine-side monomial ideal: a decision tree of Nmi_nodes storing the (typically minimal) generators b...
Definition monideal.hpp:136
Abstract base for the engine's polynomial-ring hierarchy.
Definition polyring.hpp:96
virtual const PolynomialRing * cast_to_PolynomialRing() const
Definition ring.hpp:243
Engine-wide include prelude — a single point of truth for portability shims.
Engine error-reporting primitives: ERROR, INTERNAL_ERROR, error, error_message.
#define Matrix
Definition factory.cpp:14
FreeModule — finite-rank free module R^n, the type-level anchor for every Matrix.
int_bag Bag
Definition int-bag.hpp:70
int_bag / Bag — minimal (payload, varpower monomial) carrier shared by monomial-ideal code.
bool system_interrupted()
system_interrupted() — thread-safe polling predicate for Ctrl+C handling.
void freemem(void *s)
Definition m2-mem.cpp:103
const int ERROR
Definition m2-mem.cpp:55
Engine-wide GC allocator surface (getmem / getmem_atomic) and debug-allocation trap.
MatrixConstructor — the mutable builder that produces an immutable Matrix.
static bool all_have_pure_powers(const MonomialIdeal *M, std::vector< int > varlist)
Matrix — the engine's immutable homomorphism F -> G between free modules.
MonomialIdeal — exponent-vector-only representation of an ideal generated by monomials.
#define ALLOCATE_EXPONENTS(byte_len)
Definition monoid.hpp:62
#define EXPONENT_BYTE_SIZE(nvars)
Definition monoid.hpp:63
Monoid — variable count, naming, grading, and monomial order of a polynomial ring.
#define newarray_atomic_clear(T, len)
Definition newdelete.hpp:93
#define newarray_atomic(T, len)
Definition newdelete.hpp:91
our_new_delete — per-class opt-in routing of new / delete through bdwgc.
PolynomialRing — abstract polynomial-ring base, the engine's most-reused class.
Ring — the legacy abstract base class for every coefficient and polynomial ring.
ring_elem — the universal value type carried by every Ring* in the engine.
const int EQ
Definition style.hpp:40
Engine-wide stylistic constants: LT / EQ / GT codes, INTSIZE, GEOHEAP_SIZE.
std::vector< T > M2_arrayint_to_stdvector(M2_arrayint arr)
Definition util.hpp:96
Conversion helpers between M2 boundary types and standard C++ containers.