A factorisation multiplies back to what it factored (#1092) - #1093
Merged
Conversation
`MathS.Polynomials.Factor("4 * x^2 - 4 * y^2", "x")` returned `(x + y) * (x - y)`. The 4
is gone, and the difference from the input is `-3x^2 + 3y^2` -- a factorisation that is
not equal to what it factored, which is the one property a factorisation has.
`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. `Kronecker` assembled them into a product and never did. Only multivariate
polynomials whose content is a *pure number* land in the gap: a content with a variable in
it goes through `FactorAfterTakingOutTheContent`, which reinstates it, and a univariate
polynomial never reaches the substitution at all.
Recovered by dividing the polynomial by the assembled product rather than by tracking the
content separately -- the same discipline as everything else here. A quotient that is
missing or is not a constant means the product does not account for the polynomial, and
then there is no answer to give.
**Why nothing caught it.** Every candidate in this layer is checked by exact division,
which is what makes a refusal the worst it can do -- but that check is on the individual
*factors*. Nothing compared the assembled product against the input, so a constant lost
during assembly was lost silently. The new test asserts exactly that property, on value
rather than on spelling, and fails 6 of its 11 cases without this change.
Found while wiring the polynomial layer into `Entity.Factorize` for #1018, where the wrong
answer would have reached a far more used API.
Rafael-SOWNet
force-pushed
the
fix/factor-keeps-its-content
branch
from
August 27, 2026 14:47
79324fa to
f1fe4c8
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1092.
The
4is gone, and(answer - input).Simplify()is-3 * x ^ 2 + 3 * y ^ 2. That is afactorisation which is not equal to what it factored — the one property a factorisation has.
Factor("4 * x2 - 4 * y2", "x")(x + y) * (x - y)4 * (x + y) * (x - y)Factor("2 * x2 - 2 * y2", "x")(x + y) * (x - y)2 * (x + y) * (x - y)Factor("6 * x4 - 6 * y4", "x")66 * (x + y) * (x ^ 2 + y ^ 2) * (x - y)Factor("3 * x * y + 3 * y", "x")y * 3 * (x + 1)Factor("2 * x2 - 2", "x")2 * (x + 1) * (x - 1)Measured on a build of each side.
Where it came from
KroneckerFactorization.Factordocuments its result as "each of positive degree inmain" — aconstant content is deliberately not among the factors it returns, and the caller has to
reinstate it.
MathS.Polynomials.Kroneckerassembled them into a product and never did.Only "multivariate and the content is a pure number" lands in the gap. A content with a
variable in it (
3 * x * y + 3 * y→ content3 * y) goes throughFactorAfterTakingOutTheContent, which reinstates it; a univariate polynomial never reaches thesubstitution.
The content is recovered by dividing the polynomial by the assembled product — the same
discipline as the rest of this layer. A quotient that is missing or is not a constant means the
product does not account for the polynomial, and then there is no answer to give.
Why nothing caught it
Every candidate here is checked by exact division, which is what makes a refusal the worst this
layer can do. But that check is on the individual factors. Nothing ever compared the
assembled product against the input, so a constant lost during assembly was lost silently.
AFactorisationMultipliesBackToWhatItFactoredasserts exactly that property, on value ratherthan on spelling — the only way to catch it. It fails 6 of its 11 cases without this change.
Found while wiring the polynomial layer into
Entity.Factorizefor #1018, where this wronganswer would have reached a far more used API. That is why this is a separate PR and goes first.
Full suite: 8747 passed, 0 failed.
🤖 Generated with Claude Code
https://claude.ai/code/session_01Bjumi5K7fg8yx6UK1mZTQd