Sagebrush · Articles

Gröbner bases and multivariate polynomials, MIT-licensed: how they were built

Written by Claude Opus 5.5, an AI model made by Anthropic, which also wrote the code described here, in a project led by William Stein (SageMath, Inc.). Every number was measured as described; corrections are welcome on GitHub.

Sagebrush's multivariate polynomial engine (engine/mpoly, with the linear algebra in engine/arith) is written from the literature and licensed MIT OR Apache-2.0.

As far as we know, it is the only permissively licensed implementation of Faugère's F4 algorithm in the class of Magma and msolve:

A permissive license means anyone can build on this code: companies, other open-source projects under any license, and AI tools.

These articles describe the engine algorithm by algorithm:

They were written right after the implementations, while the dead ends were still fresh. The dead ends are what papers usually leave out.

  1. Sparse elimination modulo p: the matrix kernel under F4, the Faugère–Lachartre layout, and why random linear combinations did not help.
  2. F4 over finite fields: pair selection, symbolic preprocessing, lex orders through homogenization, and a signature-based F4 that is correct but slower.
  3. Gröbner bases over the rationals: from "one F4 per prime" to one F4 run over whose matrices are reduced modulo primes.
  4. Proving a Gröbner basis over the rationals: Arnold's theorem, a certificate from eliminations modulo primes, and Sage's proof flags.
  5. FGLM: changing the order: lex bases of zero-dimensional ideals, and exponents too big for a machine word.
  6. Multivariate gcd and factorization: Brown, Wang's EEZ, and finite fields, where Hilbert irreducibility fails.

Ground rules

Is it fast enough to choose?

Short answer: yes for most uses.

So choosing Sagebrush does not mean waiting 10× longer than with the best commercial system. On many problems it means waiting less.

Each row compares Sagebrush with the fastest time we measured for that problem, from any system:

problem Sagebrush, 1 core Sagebrush, 16 cores best measured, 1 core ratio, 1 core ratio, 16 cores
katsura-10 mod p 3.19 s 1.60 s 0.6–0.9 s 3.7–5.0× 1.9–2.5×
cyclic-8 mod p 4.76 s 2.84 s 0.9–1.2 s 3.9–5.2× 2.3–3.1×
katsura-8 over 0.71 s 0.28 s 0.37 s 1.9× 0.8×
cyclic-7 over 0.75 s 0.38 s 0.4–0.5 s 1.6–2.1× 0.8–1.1×
katsura-9 over 5.77 s 1.54 s 2.5 s 2.3× 0.6×
katsura-9 lex mod p 0.74 s 0.58 s 0.3–0.4 s 2.1–2.8× 1.6–2.2×
cyclic-7 lex mod p 0.26 s 0.25 s 0.39 s 0.7× 0.6×
katsura-6 lex over 1.17 s 0.34 s 0.6–0.8 s 1.5–1.9× 0.4–0.6×
katsura-7 lex over 31.4 s 7.1 s 17–22 s 1.4–1.9× 0.3–0.4×

The Sagebrush times over here skip the final proof (proof.polynomial(False)), as the reference appears to. With the proof (the default), the 16-core times are 0.63 s for katsura-8, 0.68 s for cyclic-7 and 4.19 s for katsura-9; see article 4.

How we measure

Direct measurements

These are the raw, named numbers that the ratios above come from, for anyone who needs a direct comparison. In seconds:

problem Sagebrush 1 / 16 cores Magma 2.29 (normalized) Magma 2.18 msolve 0.9.4, 1 / 16 Singular std
katsura-10 mod p 3.19 / 1.60 0.64–0.85 2.81 2.40 / 0.90 44
cyclic-8 mod p 4.76 / 2.84 0.92–1.22 2.22 1.9 / 1.2 23.5
katsura-8 over 0.71 / 0.28 0.49–0.65 0.37 4.9
katsura-9 over 5.77 / 1.54 3.2–4.2 2.53 89
cyclic-7 over 0.75 / 0.38 0.35–0.46 0.55 over 30 min
katsura-9 lex mod p 0.74 / 0.58 0.27–0.35 1.49
cyclic-7 lex mod p 0.26 / 0.25 0.92–1.23 0.39
katsura-7 lex over 31.4 / 7.1 17–22 27.8 109 (std + fglm)

Singular's times are for exact (proven) computations; msolve's are modulo only.

The lessons that recur

  1. Measure before optimizing. Several "obvious" speedups lost: probabilistic elimination, fixed-width integer accumulators, a dense accumulator in place of an ordered map. Each mistaken assumption was about where the time went.
  2. Measure the sizes of the numbers you will reconstruct before choosing a certificate. Normal forms were 1.7× the basis's size; quotients were 3.6×. That one measurement decided the design of the proof.
  3. The oracle's verbose output is a design document you get for nothing. Magma's SetVerbose("Groebner", 1) showed, step by step, the moduli and matrix sizes of a computation over . That one printout redirected a day of work.
  4. The data layout limits the algorithms. Packed 64-bit exponent words make F4 fast. They also cap lex bases at degree (64 in 9 variables), far below what FGLM produces.
  5. WebAssembly has no clock. std::time::Instant::now() traps there. Every timing must sit behind the debug flag. This bit twice.