From b79cc4cdab146d7272c7e308ea9ce0b349eaaf38 Mon Sep 17 00:00:00 2001 From: jodavies Date: Thu, 9 Oct 2025 10:29:41 +0100 Subject: [PATCH] flint: perf: use mpoly for sparse univariate computations This improves performance for things like "gcd_(1-x^20,1-x^1000000)" since FLINT poly has a dense representation whereas mpoly is sparse. "Real life" benchmarks such as forcer or minceex are unchanged, they have dense polynomials. --- sources/flintinterface.cc | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/sources/flintinterface.cc b/sources/flintinterface.cc index 0c4fa78c..4c435877 100644 --- a/sources/flintinterface.cc +++ b/sources/flintinterface.cc @@ -37,6 +37,13 @@ static_assert(sizeof(slong) == sizeof(int64_t), "flint interface expects slong i #] Types */ +/* + * FLINT's univariate poly has a dense representation. For sufficiently sparse polynomials it is + * faster to use mpoly instead, which is sparse. For a density <= this threshold we switch, where + * the density defined as is "number of terms" / "maximum degree". + */ +#define UNIVARIATE_DENSITY_THR 0.02f + /* #[ flint::cleanup : */ @@ -1061,7 +1068,9 @@ flint::var_map_t flint::get_variables(const vector &es, const bool with const bool sort_vars) { int32_t num_vars = 0; - // To be used if we sort by highest degree, as the polu code does. + // We count the total number of terms to determine "density". + uint32_t num_terms = 0; + // To be used if we sort by highest degree, as the poly code does. vector degrees; var_map_t var_map; @@ -1071,8 +1080,10 @@ flint::var_map_t flint::get_variables(const vector &es, const bool with // fast notation if ( *e == -SNUMBER ) { + num_terms++; } else if ( *e == -SYMBOL ) { + num_terms++; if ( !var_map.count(e[1]) ) { var_map[e[1]] = num_vars++; degrees.push_back(1); @@ -1087,6 +1098,7 @@ flint::var_map_t flint::get_variables(const vector &es, const bool with } else { for ( WORD i = with_arghead ? ARGHEAD:0; with_arghead ? i < e[0]:e[i] != 0; i += e[i] ) { + num_terms++; if ( i+1 < i+e[i]-ABS(e[i+e[i]-1]) && e[i+1] != SYMBOL ) { MLOCK(ErrorMessageLock); MesPrint("ERROR: polynomials and polyratfuns must contain symbols only"); @@ -1152,6 +1164,16 @@ flint::var_map_t flint::get_variables(const vector &es, const bool with } } + if ( var_map.size() == 1 ) { + // In the univariate case, if the polynomials are sufficiently sparse force the use of the + // multivariate routines, which use a sparse representation, by adding a dummy map entry. + if ( (float)num_terms <= UNIVARIATE_DENSITY_THR * (float)degrees[0] ) { + // -1 will never be a symbol code. Built-in symbols from 0 to 19, and 20 is the first + // user symbol. + var_map[-1] = num_vars; + } + } + return var_map; } /*