MathS.Polynomials.Factor returns a product that does not equal the polynomial it factored
when the numeric content is a bare constant and the polynomial has more than one variable.
Measured on master at 69e40108:
| input |
Factor(·, "x") |
(answer - input).Simplify() |
4 * x^2 - 4 * y^2 |
(x + y) * (x - y) |
-3 * x ^ 2 + 3 * y ^ 2 |
2 * x^2 - 2 * y^2 |
(x + y) * (x - y) |
y ^ 2 - x ^ 2 |
3 * x * y + 3 * y |
y * 3 * (x + 1) |
0 |
5 * x^2 * y - 5 * y |
y * 5 * (x + 1) * (x - 1) |
0 |
2 * x^2 - 2 |
2 * (x + 1) * (x - 1) |
0 |
The factor 4 is simply gone. This is a wrong answer rather than an untidy one: the contract of
a factorisation is that multiplying the factors gives back the input, and here it does not.
Where it comes from
KroneckerFactorization.Factor documents its result as "each of positive degree in main" — so
a constant content is deliberately not among the factors it returns, and the caller has to put
it back. MathS.Polynomials.Kronecker assembles the returned factors into a product and returns
it without ever doing so.
The rows that come out right take a different path. 3 * x * y + 3 * y has content 3 * y, which
is not a constant, so FactorAfterTakingOutTheContent handles it and reinstates the content
itself. 2 * x^2 - 2 is univariate and never reaches the substitution. Only "multivariate and
the content is a pure number" lands in the gap.
Why it matters more than the row count suggests
Everything else in this layer is checked by exact division — a candidate that does not divide the
input is refused, which is what makes a refusal the worst it can do. That check is on the
factors; nothing checks the assembled product against the input, so a constant lost during
assembly is lost silently.
Found while wiring the polynomial layer into Entity.Factorize for #1018, where the wrong answer
would have reached a much more used API. Worth fixing before that lands.
MathS.Polynomials.Factorreturns a product that does not equal the polynomial it factoredwhen the numeric content is a bare constant and the polynomial has more than one variable.
Measured on
masterat69e40108:Factor(·, "x")(answer - input).Simplify()4 * x^2 - 4 * y^2(x + y) * (x - y)-3 * x ^ 2 + 3 * y ^ 22 * x^2 - 2 * y^2(x + y) * (x - y)y ^ 2 - x ^ 23 * x * y + 3 * yy * 3 * (x + 1)05 * x^2 * y - 5 * yy * 5 * (x + 1) * (x - 1)02 * x^2 - 22 * (x + 1) * (x - 1)0The factor
4is simply gone. This is a wrong answer rather than an untidy one: the contract ofa factorisation is that multiplying the factors gives back the input, and here it does not.
Where it comes from
KroneckerFactorization.Factordocuments its result as "each of positive degree inmain" — soa constant content is deliberately not among the factors it returns, and the caller has to put
it back.
MathS.Polynomials.Kroneckerassembles the returned factors into a product and returnsit without ever doing so.
The rows that come out right take a different path.
3 * x * y + 3 * yhas content3 * y, whichis not a constant, so
FactorAfterTakingOutTheContenthandles it and reinstates the contentitself.
2 * x^2 - 2is univariate and never reaches the substitution. Only "multivariate andthe content is a pure number" lands in the gap.
Why it matters more than the row count suggests
Everything else in this layer is checked by exact division — a candidate that does not divide the
input is refused, which is what makes a refusal the worst it can do. That check is on the
factors; nothing checks the assembled product against the input, so a constant lost during
assembly is lost silently.
Found while wiring the polynomial layer into
Entity.Factorizefor #1018, where the wrong answerwould have reached a much more used API. Worth fixing before that lands.