From d8d2b63ef14579e48cebbb52e8f4e08b1768fb80 Mon Sep 17 00:00:00 2001 From: Weijia Wang <9713184+wegank@users.noreply.github.com> Date: Thu, 1 Oct 2026 22:21:46 +0200 Subject: [PATCH 1/2] Add option to return full Groebner basis under elimination order over QQ Over the rationals, msolve only lifted the elements of the modular Groebner basis which do not involve the eliminated variables, hence it returned the basis of the elimination ideal only. Some algorithms, e.g. Suzuki-Sato's algorithm for comprehensive Groebner systems, need the full Groebner basis w.r.t. the elimination order. The new --elim-full-basis command line option, combined with -e, makes the multi-modular lifting keep all elements of the basis (and all variables in their leading monomials) in the learning and application phases, and outputs the full basis together with the elimination monomial order in the header. The default behaviour is unchanged. While at it, the application phase now rejects primes for which the basis has fewer elements than expected before reading their lengths. Closes #339. Co-Authored-By: Claude Opus 5.5 --- Makefile.am | 1 + README.md | 3 ++ doc/msolve-tutorial.tex | 31 +++++++++++++++++-- input_files/elim-full-qq.ms | 5 +++ output_files/elim-full-qq.g2.e1.res | 17 +++++++++++ output_files/elim-full-qq.g2.e2.res | 22 ++++++++++++++ src/msolve/duplicate.c | 2 +- src/msolve/lifting-gb.c | 47 +++++++++++++++++++++++------ src/msolve/main.c | 20 ++++++++++-- src/msolve/msolve-data.h | 5 +++ src/msolve/msolve.c | 2 ++ src/neogb/data.h | 3 ++ test/diff/diff_elim-full-qq.sh | 29 ++++++++++++++++++ 13 files changed, 172 insertions(+), 15 deletions(-) create mode 100644 input_files/elim-full-qq.ms create mode 100644 output_files/elim-full-qq.g2.e1.res create mode 100644 output_files/elim-full-qq.g2.e2.res create mode 100755 test/diff/diff_elim-full-qq.sh diff --git a/Makefile.am b/Makefile.am index 41023691..1935804c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -27,6 +27,7 @@ checkdiff = test/diff/diff_bug-2nd-prime-bad.sh \ test/diff/diff_eco6-qq.sh \ test/diff/diff_elim-31.sh \ test/diff/diff_elim-qq.sh \ + test/diff/diff_elim-full-qq.sh \ test/diff/diff_empty-elim-qq.sh \ test/diff/diff_f4sat-31.sh \ test/diff/diff_f4sat-byone-31.sh \ diff --git a/README.md b/README.md index f3612103..e32f93d2 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,9 @@ thanks to the `-e` flag. The following command ``` will perform the Groebner basis computation eliminating the first variable. More generally, using `-e k` will eliminate the first `k` variables. +Over the rationals, only the basis of the elimination ideal is returned +by default; add the `--elim-full-basis` flag to get the full Groebner +basis w.r.t. the elimination order. # Solving over the real numbers diff --git a/doc/msolve-tutorial.tex b/doc/msolve-tutorial.tex index 277358ff..c67ecdc7 100644 --- a/doc/msolve-tutorial.tex +++ b/doc/msolve-tutorial.tex @@ -631,8 +631,35 @@ \section{Computing Gr\"obner bases}\label{sec:grobner} where we see that the first $6$ polynomials are only in $w,x,y,z$, which corresponds to the elimination of the variable $t$. When the input coefficients lie in the field of rational numbers (hence, characteristic $0$), -the returned Gr\"obner basis is the one of the {\em elimination ideal}, i.e. they -have partial degree $0$ in the variables to eliminate. +the returned Gr\"obner basis is by default the one of the {\em elimination +ideal}, i.e. only the polynomials which have partial degree $0$ in the variables +to eliminate are returned. The full Gr\"obner basis with respect to the +elimination order can be obtained by adding the \verb+--elim-full-basis+ flag. +For instance, on the same system with rational coefficients, +\begin{tcolorbox} + \begin{verbatim} + ./msolve -e 1 --elim-full-basis -g 2 -f in.ms -o out.ms + \end{verbatim} +\end{tcolorbox} +returns +\begin{tcolorbox} + \begin{lstlisting} +#Reduced Groebner basis data +#--- +#field characteristic: 0 +#variable order: t, w, x, y, z +#monomial order: eliminating first variable, blocks: graded reverse lexicographical +#length of basis: 7 elements sorted by increasing leading monomials +#--- +[w*y^3-x*z^3, +x^4, +w*x^3, +w^2*x^2, +w^3*x, +w^4, +t*z-1]: + \end{lstlisting} +\end{tcolorbox} More generally, using \verb+-e k+ will eliminate the $k$ first variables. Thus, diff --git a/input_files/elim-full-qq.ms b/input_files/elim-full-qq.ms new file mode 100644 index 00000000..85046950 --- /dev/null +++ b/input_files/elim-full-qq.ms @@ -0,0 +1,5 @@ +x, y, a, b +0 +a*x^2+b*y-1/3, +b*x*y-a+2, +x^3-7/5*y^2+a*b diff --git a/output_files/elim-full-qq.g2.e1.res b/output_files/elim-full-qq.g2.e1.res new file mode 100644 index 00000000..87e0718b --- /dev/null +++ b/output_files/elim-full-qq.g2.e1.res @@ -0,0 +1,17 @@ +#Reduced Groebner basis data +#--- +#field characteristic: 0 +#variable order: x, y, a, b +#monomial order: eliminating first variable, blocks: graded reverse lexicographical +#length of basis: 10 elements sorted by increasing leading monomials +#--- +[21*y^3*a*b-15*y*a^2*b^2+15*y*a*b-30*y*b-5*a+10, +63*y^2*a^3-45*a^4*b-126*y^2*a^2+90*a^3*b+15*y^2*b^2+45*a^3-180*a^2-5*y*b+180*a, +3*y^3*b^3-y^2*b^2+3*a^3-12*a^2+12*a, +756*y^2*a^2*b^2-540*a^3*b^3-90*y^2*b^4-378*y^2*a^2*b-21*y^3*b^2+270*a^3*b^2+15*y*a*b^3+756*y^2*a*b+30*y*b^3-126*y*a^2-270*a^2*b-15*y*b^2-1080*a*b^2+252*y*a+1080*a*b-1075*b, +21*y^4*b^2-15*y^2*a*b^3+126*y^2*a^2-90*a^3*b+15*y^2*b^2-252*y^2*a+180*a^2*b+90*a^2-5*y*b-360*a+360, +2646*y^4*a^2-1350*a^4*b^2-7560*y^2*a^2*b+5400*a^3*b^2+900*y^2*b^3+3780*y^2*a^2+105*y^3*b-75*y*a*b^2-7560*y^2*a-5400*a^2*b-300*y*b^2+1350*a^2+75*y*b+10800*a*b-5400*a+5375, +15*y*a^2*b^4-7*y^2*a*b^2-15*y*a*b^3+21*a^4+30*y*b^3-84*a^3+5*a*b^2+84*a^2-10*b^2, +15*y^2*a*b^4-126*y^2*a^2*b-7*y^3*b^2+90*a^3*b^2-15*y^2*b^3+21*y*a^3+252*y^2*a*b-180*a^2*b^2-84*y*a^2-90*a^2*b+5*y*b^2+84*y*a+360*a*b-360*b, +10800*a^3*b^5+1800*y^2*b^6+900*y^2*b^5-300*y*a*b^5-30240*y^2*a*b^3-5040*y*a^2*b^3+450*y^2*b^4-150*y*a*b^4-600*y*b^5+21168*y*a^4+1890*y^2*a^2*b+105*y^3*b^2-7560*y^2*a*b^2+2520*y*a^2*b^2-1350*a^3*b^2+30380*y^2*b^3-75*y*a*b^3+5400*a^2*b^3+21600*a*b^4-84672*y*a^3-3780*y^2*a*b+1260*y*a^2*b-420*a^3*b+70*y^2*b^2+5040*y*a*b^2+2700*a^2*b^2-10800*a*b^3+85302*y*a^2-210*a^3-2520*y*a*b+3030*a^2*b-20085*y*b^2-5400*a*b^2+21500*b^3-1260*y*a+840*a^2-8760*a*b+10750*b^2-840*a+8735*b, +5*x-21*y^2*a+15*a^2*b-15*a+30]: diff --git a/output_files/elim-full-qq.g2.e2.res b/output_files/elim-full-qq.g2.e2.res new file mode 100644 index 00000000..e6ad07b6 --- /dev/null +++ b/output_files/elim-full-qq.g2.e2.res @@ -0,0 +1,22 @@ +#Reduced Groebner basis data +#--- +#field characteristic: 0 +#variable order: x, y, a, b +#monomial order: eliminating first 2 variables, blocks: graded reverse lexicographical +#length of basis: 15 elements sorted by increasing leading monomials +#--- +[30375*a^6*b^9-91125*a^5*b^8+182250*a^4*b^8-4725*a^5*b^6+91125*a^4*b^7+39690*a^7*b^3-364500*a^3*b^7-83349*a^9-158760*a^6*b^3+14175*a^4*b^5-30375*a^3*b^6+364500*a^2*b^7+666792*a^8-99225*a^6*b^2+158760*a^5*b^3-28350*a^3*b^5+182250*a^2*b^6-1125*a*b^7-2000376*a^7+595350*a^5*b^2-23625*a^3*b^4-364500*a*b^6+2667168*a^6-1190700*a^4*b^2+94500*a^2*b^4+243000*b^6-1333584*a^5+793800*a^3*b^2-94500*a*b^4+175*b^4, +315*y*a^4*b^3-1323*y*a^6+5292*y*a^5-945*y*a^3*b^2-5292*y*a^4+1890*y*a^2*b^2-75*y*b^4-675*a^5*b^5+1350*a^4*b^4-2700*a^3*b^4-675*a^3*b^3+2700*a^2*b^3+105*a^3*b-2700*a*b^3-210*a^2*b+25*b^3, +3969*y*a^6*b+225*y*a*b^6-23814*y*a^5*b+1890*y*a^3*b^3+47628*y*a^4*b-7560*y*a^2*b^3-31752*y*a^3*b+7560*y*a*b^3-35*y*b^3+2025*a^5*b^6-4050*a^4*b^6-4050*a^4*b^5+16200*a^3*b^5-315*a^4*b^3+2025*a^3*b^4-16200*a^2*b^5+1323*a^6+630*a^3*b^3-12150*a^2*b^4-7938*a^5+630*a^3*b^2+24300*a*b^4+15876*a^4-2520*a^2*b^2-16200*b^4-10584*a^3+2520*a*b^2, +1125*y*b^8+83349*y*a^7-735*y*a^3*b^4+9450*y*a^2*b^5-833490*y*a^6+3087*y*a^5*b+59535*y*a^4*b^2+238140*y*a^3*b^3-37800*y*a*b^5+2333772*y*a^5-12348*y*a^4*b-476280*y*a^3*b^2-156555*y*a^2*b^3+4725*y*a*b^4+37800*y*b^5-2000376*y*a^4+12348*y*a^3*b+714420*y*a^2*b^2-4410*y*a*b^3-28350*y*b^4+10125*a^5*b^9-20250*a^4*b^8+42525*a^6*b^5+40500*a^3*b^8-255150*a^5*b^5+10125*a^3*b^7+13230*a^6*b^3-85050*a^5*b^4-40500*a^2*b^7-27783*a^8-52920*a^5*b^3+680400*a^4*b^4+40500*a*b^7+222264*a^7-26460*a^5*b^2+95445*a^4*b^3-1020600*a^3*b^4-375*b^7-666792*a^6+158760*a^4*b^2-425250*a^3*b^3-3150*a^2*b^4+889056*a^5-6615*a^4*b-317520*a^3*b^2+1190700*a^2*b^3+12600*a*b^4-444528*a^4+52920*a^3*b+211435*a^2*b^2-1022175*a*b^3-12600*b^4-79380*a^2*b+490*a*b^2+9450*b^3, +675*y*a^2*b^6+1350*y*a*b^6-47628*y*a^5*b-5670*y*a^3*b^3-1350*y*a*b^5+23814*y*a^5+190512*y*a^4*b-22680*y*a^2*b^3-95256*y*a^4-190512*y*a^3*b+17010*y*a^2*b^2+45255*y*a*b^3+675*y*b^4+95697*y*a^3-34020*y*a*b^2-210*y*b^3-882*y*a^2+210*y*b^2-24300*a^4*b^6+12150*a^4*b^5+48600*a^3*b^5-24300*a^3*b^4-97200*a^2*b^5+945*a^4*b^2+3780*a^3*b^3+24300*a^2*b^4-15876*a^5-1890*a^3*b^2+12150*a^2*b^3+97425*a*b^4+63504*a^4-945*a^3*b-7560*a^2*b^2-48600*a*b^3-97200*b^4-63504*a^3+1890*a^2*b+15120*a*b^2+48375*b^3-35*b, +83349*y*a^8+4725*y*a^3*b^5-666792*y*a^7+59535*y*a^5*b^2-9450*y*a^2*b^5+1125*y*b^7+2000376*y*a^6-357210*y*a^4*b^2-735*y*a^3*b^3+9450*y*a^2*b^4-2664081*y*a^5+714420*y*a^3*b^2-37800*y*a*b^4+1321236*y*a^4-474810*y*a^2*b^2+37800*y*b^4+12348*y*a^3-2940*y*a*b^2+10125*a^5*b^8+42525*a^7*b^5-170100*a^6*b^5-20250*a^4*b^7-85050*a^6*b^4+170100*a^5*b^5+40500*a^3*b^7+510300*a^5*b^4+10125*a^3*b^6+6615*a^6*b^2+42525*a^5*b^3-1020600*a^4*b^4-40500*a^2*b^6-26460*a^5*b^2-340200*a^4*b^3+680400*a^3*b^4+40500*a*b^6-13230*a^5*b+26460*a^4*b^2+1020600*a^3*b^3-375*b^6+79380*a^4*b-1363950*a^2*b^3-158760*a^3*b+693000*a*b^3+105595*a^2*b-12600*b^3+490*a*b, +5*x*b^2-45*y*a^2*b^4+45*y*a*b^3-90*y*b^3+15*a^2*b^3-63*a^4+252*a^3-30*a*b^2-252*a^2+60*b^2, +147*x*a^2-294*x*a-4050*y*a*b^7+142884*y*a^5*b^2-17010*y*a^3*b^4-571536*y*a^4*b^2+102060*y*a^2*b^4+4050*y*b^6+571536*y*a^3*b^2+17010*y*a^2*b^3-135765*y*a*b^4-68040*y*a*b^3+630*y*b^4-2646*y*a^2*b+67725*y*b^3+5292*y*a*b-49*y*b+72900*a^4*b^7-145800*a^3*b^6+291600*a^2*b^6-11340*a^3*b^4+72900*a^2*b^5+47628*a^5*b-291600*a*b^5-23814*a^5-190512*a^4*b+22680*a^2*b^3+290250*b^5+142884*a^4+190512*a^3*b-11340*a^2*b^2-45360*a*b^3-285327*a^3+45360*a*b^2+188748*a^2-45255*b^2+1764*a, +21*y^2*a-5*x-15*a^2*b+15*a-30, +49*y^2*b^2+1350*y*a*b^7-47628*y*a^5*b^2+5670*y*a^3*b^4+190512*y*a^4*b^2-34020*y*a^2*b^4-1350*y*b^6-190512*y*a^3*b^2-5670*y*a^2*b^3+45255*y*a*b^4+22680*y*a*b^3-210*y*b^4+882*y*a^2*b-22575*y*b^3-1764*y*a*b-24300*a^4*b^7+48600*a^3*b^6-97200*a^2*b^6+3780*a^3*b^4-24300*a^2*b^5-15876*a^5*b+97200*a*b^5+7938*a^5+63504*a^4*b-7560*a^2*b^3-96750*b^5-47628*a^4-63504*a^3*b+3780*a^2*b^2+15120*a*b^3+95109*a^3-15120*a*b^2-62916*a^2+15085*b^2-588*a, +x*y*b-a+2, +35*x^2*b-105*x*a*b+210*x*b-225*y*a*b^6+7938*y*a^5*b-31752*y*a^4*b+3780*y*a^2*b^3+225*y*b^5+31752*y*a^3*b-7560*y*a*b^3+35*y*b^3-147*y*a^2+294*y*a+4050*a^4*b^6-8100*a^3*b^5+16200*a^2*b^5-630*a^3*b^3+4050*a^2*b^4+2646*a^5-315*a^3*b^2-16200*a*b^4-10584*a^4+1890*a^2*b^2+16125*b^4+10584*a^3-2520*a*b^2, +3*x^2*a+3*y*b-1, +21*y^3*b+30*x^2-15*y*a*b^2+15*y*b-5, +5*x^3-7*y^2+5*a*b]: diff --git a/src/msolve/duplicate.c b/src/msolve/duplicate.c index 202de6ed..d494eec7 100644 --- a/src/msolve/duplicate.c +++ b/src/msolve/duplicate.c @@ -398,7 +398,7 @@ static inline void duplicate_data_mthread_gbtrace(int nthreads, trace_t **btrace){ - const len_t len = num_gb[0] * (st->nvars - st->nev); + const len_t len = num_gb[0] * lifted_nvars(st); for(int i = 0; i < nthreads; i++){ leadmons_current[i] = (int32_t *)calloc(len, sizeof(int32_t)); diff --git a/src/msolve/lifting-gb.c b/src/msolve/lifting-gb.c index 14f664a1..0784a3a3 100644 --- a/src/msolve/lifting-gb.c +++ b/src/msolve/lifting-gb.c @@ -800,6 +800,17 @@ static inline int32_t compute_num_gb(int32_t *bexp_lm, int32_t len, int nv, int return len; } +/* returns 1 if only the basis of the elimination ideal is lifted, i.e. the + * eliminated variables are dropped, else returns 0 */ +static inline int lift_elim_ideal_only(const md_t *st){ + return st->nev > 0 && !st->elim_full_basis; +} + +/* returns the number of variables of the lifted leading monomials */ +static inline int lifted_nvars(const md_t *st){ + return lift_elim_ideal_only(st) ? st->nvars - st->nev : st->nvars; +} + static int32_t gb_modular_trace_learning(gb_modpoly_t modgbs, int32_t *mgb, int32_t *num_gb, @@ -847,9 +858,11 @@ static int32_t gb_modular_trace_learning(gb_modpoly_t modgbs, leadmons[0] = bexp_lm; int32_t len = bs->lml; - num_gb[0] = compute_num_gb(bexp_lm, len, bht->nv, st->nev); + const int nvl = lifted_nvars(st); + num_gb[0] = compute_num_gb(bexp_lm, len, bht->nv, + lift_elim_ideal_only(st) ? st->nev : 0); int32_t *bexp_lm2 = NULL; - if(st->nev){ + if(lift_elim_ideal_only(st)){ bexp_lm2 = calloc(num_gb[0]*(bht->nv - st->nev), sizeof(int32_t)); for(int32_t i = 0; i < num_gb[0]; i++){ for(int j = 0; j < bht->nv - st->nev; j++){ @@ -872,13 +885,13 @@ static int32_t gb_modular_trace_learning(gb_modpoly_t modgbs, /************************************************/ - int32_t *lens = array_of_lengths(leadmons[0], num_gb[0], bs, bht->nv - st->nev); + int32_t *lens = array_of_lengths(leadmons[0], num_gb[0], bs, nvl); if(truncate_lifting != 0 && truncate_lifting < num_gb[0]){ - gb_modpoly_init(modgbs, 2, lens, bs, bht->nv - st->nev, truncate_lifting, leadmons[0], st); + gb_modpoly_init(modgbs, 2, lens, bs, nvl, truncate_lifting, leadmons[0], st); } else{ - gb_modpoly_init(modgbs, 2, lens, bs, bht->nv - st->nev, num_gb[0], leadmons[0], st); + gb_modpoly_init(modgbs, 2, lens, bs, nvl, num_gb[0], leadmons[0], st); } free(lens); modpgbs_set(modgbs, bs, bht, fc, start, st->nev); @@ -954,6 +967,11 @@ static void gb_modular_trace_application(gb_modpoly_t modgbs, return; } ht_t **bht = &(bs->ht); + if(bs->lml < modgbs->ld){ + bad_primes[0] = 2; + free_basis_and_only_local_hash_table_data(&bs); + return; + } for(len_t i = 0; i < modgbs->ld; i++){ if(modgbs->modpolys[i]->len < bs->hm[bs->lmps[i]][LENGTH]-1){ bad_primes[0] = 2; @@ -967,7 +985,7 @@ static void gb_modular_trace_application(gb_modpoly_t modgbs, } } int32_t lml = bs->lml; - if (st->nev > 0) { + if (lift_elim_ideal_only(st)) { int32_t j = 0; for (len_t i = 0; i < bs->lml; ++i) { if ((*bht)->ev[bs->hm[bs->lmps[i]][OFFSET]][0] == 0) { @@ -986,7 +1004,7 @@ static void gb_modular_trace_application(gb_modpoly_t modgbs, return; } - if(st->nev){ + if(lift_elim_ideal_only(st)){ get_lm_from_bs_trace_elim(bs, bht[0], leadmons_current[0], num_gb[0]); } else{ @@ -994,7 +1012,7 @@ static void gb_modular_trace_application(gb_modpoly_t modgbs, } if(!equal_staircase(leadmons_current[0], leadmons_ori[0], - num_gb[0], num_gb[0], bht[0]->nv - st->nev)){ + num_gb[0], num_gb[0], lifted_nvars(st))){ bad_primes[0] = 1; } @@ -1655,7 +1673,7 @@ gb_modpoly_t *core_groebner_qq( if(!dlinit){ int nb = 0; int32_t *ldeg = array_nbdegrees((*msd->leadmons_ori), msd->num_gb[0], - bs->ht->nv - st->nev, &nb); + lifted_nvars(st), &nb); data_lift_init(dlift, (*modgbsp)->ld, ldeg, nb); choose_coef_to_lift((*modgbsp), dlift); free(ldeg); @@ -2079,6 +2097,7 @@ gb_modpoly_t *groebner_qq( fprintf(ERRSTREAM,"Bad input data, stopped computation.\n"); exit(1); } + md->elim_full_basis = gens->elim_full_basis; initialize_mstrace(msd, md, bs); int err = 0; @@ -2145,7 +2164,15 @@ void print_msolve_gbtrace_qq(data_gens_ff_t *gens, fprintf(ofile, "%s, ", gens->vnames[i]); } fprintf(ofile, "%s\n", gens->vnames[gens->nvars-1]); - fprintf(ofile, "#monomial order: graded reverse lexicographical\n"); + if (gens->elim_full_basis && flags->elim_block_len > 0) { + if (flags->elim_block_len == 1) { + fprintf(ofile, "#monomial order: eliminating first variable, blocks: graded reverse lexicographical\n"); + } else { + fprintf(ofile, "#monomial order: eliminating first %d variables, blocks: graded reverse lexicographical\n", flags->elim_block_len); + } + } else { + fprintf(ofile, "#monomial order: graded reverse lexicographical\n"); + } if ((*modgbsp)->ld == 1) { fprintf(ofile, "#length of basis: 1 element\n"); } else { diff --git a/src/msolve/main.c b/src/msolve/main.c index 7cb72c58..a406bdf2 100644 --- a/src/msolve/main.c +++ b/src/msolve/main.c @@ -135,6 +135,12 @@ static inline void display_help(char *str){ display_option_help_noopt("between 1 and #variables-1, and gives the number of\n"); display_option_help_noopt("eliminated variables. The basis with the first block of\n"); display_option_help_noopt("ELIM variables eliminated is then computed.\n"); + display_option_help_noopt("Over the rationals, only the elements of the basis\n"); + display_option_help_noopt("which do not involve the eliminated variables are\n"); + display_option_help_noopt("returned by default (see --elim-full-basis).\n"); + display_option_help(0, "elim-full-basis", "", "Over the rationals, combined with -e, return the\n"); + display_option_help_noopt("full Groebner basis w.r.t. the elimination order\n"); + display_option_help_noopt("instead of the basis of the elimination ideal only.\n"); display_option_help('l', "linear-algebra", "LIN", "Linear algebra variant to be applied:\n"); display_option_help_noopt(" 1 - exact sparse / dense\n"); display_option_help_noopt(" 2 - exact sparse (default)\n"); @@ -194,6 +200,7 @@ static void getoptions( int32_t *nthreads, int32_t *max_pairs, int32_t *elim_block_len, + int32_t *elim_full_basis, int32_t *linear_algebra, int32_t *use_signatures, int32_t *update_ht, @@ -225,12 +232,14 @@ static void getoptions( see https://cgit.git.savannah.gnu.org/cgit/coreutils.git/tree/src/ls.c */ enum { RANDOM_SEED_OPTION = CHAR_MAX + 1, + ELIM_FULL_BASIS_OPTION, /* Below is the template for the next long option with * no equivalent short option */ /* NEXT_OPTION */ }; struct option long_options[] = { {"elimination", required_argument, NULL, 'e'}, + {"elim-full-basis", no_argument, NULL, ELIM_FULL_BASIS_OPTION}, {"file", required_argument, NULL, 'f'}, {"groebner-basis", required_argument, NULL, 'g'}, {"help", no_argument, NULL, 'h'}, @@ -385,6 +394,9 @@ static void getoptions( case RANDOM_SEED_OPTION: *seed = strtoll(optarg, NULL, 10); break; + case ELIM_FULL_BASIS_OPTION: + *elim_full_basis = 1; + break; /* Below is the template for the next long option with * no equivalent short option */ /* case NEXT_OPTION: */ @@ -483,6 +495,7 @@ int main(int argc, char **argv){ int32_t initial_hts = 17; int32_t max_pairs = 0; int32_t elim_block_len = 0; + int32_t elim_full_basis = 0; int32_t update_ht = 0; int32_t generate_pbm = 0; int32_t reduce_gb = 1; @@ -505,7 +518,7 @@ int main(int argc, char **argv){ files->in_file = NULL; files->out_file = NULL; getoptions(argc, argv, &initial_hts, &nr_threads, &max_pairs, - &elim_block_len, &la_option, &use_signatures, &update_ht, + &elim_block_len, &elim_full_basis, &la_option, &use_signatures, &update_ht, &reduce_gb, &print_gb, &truncate_lifting, &genericity_handling, &unstable_staircase, &saturate, &colon, &normal_form, &normal_form_matrix, &is_gb, &lift_matrix, &get_param, @@ -557,7 +570,10 @@ int main(int argc, char **argv){ gens->rand_linear = 0; gens->random_linear_form = malloc(sizeof(int32_t)*(nr_vars)); - gens->elim = elim_block_len; + /* over QQ, the eliminated variables are dropped from the output + * unless the full basis is requested */ + gens->elim_full_basis = elim_full_basis; + gens->elim = elim_full_basis ? 0 : elim_block_len; if(0 < field_char && field_char < pow(2, 15) && la_option > 2){ if(info_level){ diff --git a/src/msolve/msolve-data.h b/src/msolve/msolve-data.h index 73a6f909..e375fa3e 100644 --- a/src/msolve/msolve-data.h +++ b/src/msolve/msolve-data.h @@ -54,7 +54,12 @@ typedef int64_t bits_t; typedef struct{ nvars_t nvars; + /* number of eliminated variables which are dropped from the output + * (characteristic 0 only) */ nvars_t elim; + /* over QQ with an elimination order: if nonzero, the full basis is + * computed, otherwise only the basis of the elimination ideal */ + int32_t elim_full_basis; int32_t ngens; int32_t nterms; int32_t field_char; diff --git a/src/msolve/msolve.c b/src/msolve/msolve.c index cb82a156..1080fd1d 100644 --- a/src/msolve/msolve.c +++ b/src/msolve/msolve.c @@ -35,6 +35,7 @@ static inline void duplicate_data_mthread_gbtrace(int nthreads, int32_t **leadmons_ori, int32_t **leadmons_current, trace_t **btrace); +static inline int lifted_nvars(const md_t *st); static inline mpz_t *allocate_crt_linear_forms(int nlins, int nv, uint32_t **lineqs_ptr); @@ -333,6 +334,7 @@ data_gens_ff_t *allocate_data_gens() { gens->random_linear_form = NULL; gens->elim = 0; + gens->elim_full_basis = 0; return gens; } diff --git a/src/neogb/data.h b/src/neogb/data.h index 4dc09bf2..ebaaa259 100644 --- a/src/neogb/data.h +++ b/src/neogb/data.h @@ -430,6 +430,9 @@ struct md_t int32_t print_gb; int32_t truncate_lifting; + /* over QQ with an elimination order: if nonzero, the full basis is + * lifted, otherwise only the basis of the elimination ideal */ + int32_t elim_full_basis; /* for f4sat */ uint32_t new_multipliers; diff --git a/test/diff/diff_elim-full-qq.sh b/test/diff/diff_elim-full-qq.sh new file mode 100755 index 00000000..e218b41d --- /dev/null +++ b/test/diff/diff_elim-full-qq.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +file=elim-full-qq + +source test/diff/diff_source.sh + +# full Groebner basis w.r.t. an elimination order over the rationals, +# not only the basis of the elimination ideal +for e in 1 2; do + for t in 1 2; do + for l in 2 44; do + $(pwd)/msolve -f input_files/$file.ms -o test/diff/$file.$e.$t.$l.res \ + --random-seed $seed \ + -e $e --elim-full-basis -g 2 -l $l -t $t + if [ $? -gt 0 ]; then + print_exit 1 + fi + + diff test/diff/$file.$e.$t.$l.res output_files/$file.g2.e$e.res + if [ $? -gt 0 ]; then + print_exit 2 + fi + + rm test/diff/$file.$e.$t.$l.res + done + done +done + +normal_exit From 4cdc192b2f64acbc1ee93437d53afaa9aaba1b7e Mon Sep 17 00:00:00 2001 From: Weijia Wang <9713184+wegank@users.noreply.github.com> Date: Thu, 1 Oct 2026 22:37:58 +0200 Subject: [PATCH 2/2] Register elim-full-qq test files in configure.ac The test script and its input and expected output files have to be listed in AC_CONFIG_LINKS so that they are shipped in the distribution tarball and linked into the build tree, otherwise make distcheck fails. Co-Authored-By: Claude Opus 5.5 --- configure.ac | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/configure.ac b/configure.ac index 77d47f48..68aa46f8 100644 --- a/configure.ac +++ b/configure.ac @@ -170,6 +170,10 @@ AC_CONFIG_LINKS([ output_files/elim-qq.g2.e1.res:output_files/elim-qq.g2.e1.res output_files/elim-qq.g2.e2.res:output_files/elim-qq.g2.e2.res test/diff/diff_elim-qq.sh:test/diff/diff_elim-qq.sh + input_files/elim-full-qq.ms:input_files/elim-full-qq.ms + output_files/elim-full-qq.g2.e1.res:output_files/elim-full-qq.g2.e1.res + output_files/elim-full-qq.g2.e2.res:output_files/elim-full-qq.g2.e2.res + test/diff/diff_elim-full-qq.sh:test/diff/diff_elim-full-qq.sh input_files/empty-elim-qq.ms:input_files/empty-elim-qq.ms output_files/empty-elim-qq.g2.e1.res:output_files/empty-elim-qq.g2.e1.res output_files/empty-elim-qq.g2.e2.res:output_files/empty-elim-qq.g2.e2.res