Skip to content

Factor was throwing the content away with an irreducibility proof - #1059

Merged
Rafael-SOWNet merged 1 commit into
masterfrom
fix/factor-keeps-the-content-when-the-rest-is-irreducible
Aug 25, 2026
Merged

Rafael-SOWNet merged 1 commit into
masterfrom
fix/factor-keeps-the-content-when-the-rest-is-irreducible

Conversation

@Rafael-SOWNet

Copy link
Copy Markdown
Member

Kronecker's substitution does not merely fail to find a factorisation — it proves there is none. A splitting of the polynomial into two parts of positive degree in the main variable maps to a splitting of the one-variable image, because the substitution is a ring homomorphism on the monomials that can appear; and every splitting of the image is one of the subsets the recombination tries. So where nothing recombines, nothing exists.

That proof was being reported as null, and the content went out with it.

before now
Factor("x * y + y * z", "x") null y * (x + z)
Factor("a * x + a * y + a * z", "x") null a * (x + y + z)
Factor("x ^ 2 * y - y ^ 3", "x") null y * (x + y) * (x - y)
Factor("x ^ 2 + y ^ 2", "x") null x ^ 2 + y ^ 2
Factor("x * y + z", "x") null x * y + z
Factor("x ^ 2 - a", "x") null x ^ 2 - a
Factor("x + y", "x") null x + y

The first three are the real loss: the content had already been taken out and the factorisation assembled, and then the single-factor rest threw the whole thing away. The rest are a contract that disagreed with itself — the one-variable path has always answered Factor("x ^ 2 + 1", "x") with x ^ 2 + 1.

The proof has a precondition, and it is now checked

DivideExact answers null both for "does not divide" and for "ran out of room" — a term count past its budget, a monomial past the degree bound, the step limit — and only the first is evidence. Drawing "irreducible" from the second would be stating exhaustion as a mathematical fact.

So it says which, through a new overload, and the irreducibility claim is withheld where any trial division was cut short. Where FromImage cannot read a candidate back the recombination now refuses rather than skipping it, because a skipped candidate is a subset that was never tried and the proof is over all of them.

The documentation was two PRs stale

The XML doc on MathS.Polynomials.Factor still read "Univariate only. A polynomial in more than one variable is refused rather than answered", with a worked example asserting

Console.WriteLine(MathS.Polynomials.Factor("x * y + y", "x") is null);
// True             -- more than one variable, so it declines

False since #1053, and it is public API documentation — nothing in the suite reads it, and the docsamples harness checks the wiki and the website rather than XML comments. Rewritten and every printed value in it re-measured.

The existing FactorisationRefusesRatherThanReturningTheInput theory kept three rows that are now proofs rather than refusals; those moved to the new theory, and the test keeps the rows where a refusal is still right — not a polynomial, and past the degree bound.

Full suite: Failed: 0, Passed: 8591, Skipped: 14, Total: 8605.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Bjumi5K7fg8yx6UK1mZTQd

…with it

Kronecker's substitution does not merely fail to find a factorisation -- it
proves there is none. A splitting of the polynomial into two parts of positive
degree in the main variable maps to a splitting of the one-variable image,
because the substitution is a ring homomorphism on the monomials that can
appear, and every splitting of the image is one of the subsets the
recombination tries. So where nothing recombines, nothing exists.

That proof was being reported as null, and the content went with it:

    x * y + y * z          was null, is y * (x + z)
    a * x + a * y + a * z  was null, is a * (x + y + z)
    x ^ 2 * y - y ^ 3      was null, is y * (x + y) * (x - y)

The content had already been taken out and the factorisation assembled; the
single-factor rest then threw the whole thing away. It also disagreed with the
one-variable path, where Factor("x ^ 2 + 1", "x") has always been x ^ 2 + 1,
so x ^ 2 + y ^ 2, x ^ 2 - a and x * y + z now answer the same way.

The proof has a precondition, and it is now checked rather than assumed.
DivideExact answers null both for "does not divide" and for "ran out of room",
and only the first is evidence -- so it says which, and the irreducibility
claim is withheld where a division was cut short by a term or degree budget.
Where FromImage cannot read a candidate back the recombination now refuses
instead of skipping it silently, since a skipped candidate is a subset that was
never tried and the proof is over all of them.

The XML documentation on Factor still said "Univariate only", with a worked
example asserting that Factor("x * y + y", "x") is null. That has been false
since #1053 and is public API documentation, which nothing in the suite or in
the docsamples harness reads.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bjumi5K7fg8yx6UK1mZTQd
@Rafael-SOWNet
Rafael-SOWNet merged commit 000e14f into master Aug 25, 2026
31 checks passed
Rafael-SOWNet added a commit that referenced this pull request Aug 26, 2026
…ot reach (#1087)

`Factor` in several variables is Kronecker's substitution, and its one-variable image has
degree `Π (d_i + 1) - 1` -- a product, so it leaves the one-variable factoriser's reach
after very few variables. `x2 + y2 + z2 + w2 + 1` needs an image of degree 80 against a
ceiling of 32, and the answer was `null`.

Evaluating instead of substituting gives an image of degree `d_main`, whatever the
variable count. That image cannot be lifted back to a factorisation without Hensel
lifting, which is still outstanding -- but it does not need to be for one conclusion.
Substitution is a ring homomorphism, so `f = g*h` gives `f(x,a) = g(x,a)*h(x,a)`; degrees
in the main variable add, so if the image kept the total then neither part lost any. **An
irreducible image of full degree is therefore a proof that its source is irreducible**,
and since #1059 that is an answer rather than a refusal.

One direction only, and the tests are mostly about the direction it does not claim. A
reducible image says nothing at all -- `x2 + y` is irreducible and its image at y = -4 is
`(x-2)(x+2)` -- so several points are tried and `x7 - y7` is still refused. A factor free
of the main variable is invisible to an image in that variable, so the content is computed
and anything but a constant declines, or `y * (x + 1)` would be certified on an image of
`2x + 2`.

The `true` half is the one nothing downstream can check: a factorisation is verified by
division and "there is no factorisation" is not. So seventeen of the twenty-three tests
assert that a certificate is *not* issued, and every case in them is written as a product
or is a difference of like powers -- reducible by construction, whatever a factoriser
would say. The count in the positive test is what stops all of that passing vacuously: a
certificate that always declined would satisfy every one of them.

Asked second, after the substitution refuses, because the substitution is the one that can
actually factor and this only ever says that there is nothing to find.

This is the first step of Hensel lifting with an evaluation homomorphism -- choosing a
point whose image keeps the degree -- used for the conclusion that needs no lifting. The
lifting itself is unchanged and still outstanding on #746 tier 1.


Claude-Session: https://claude.ai/code/session_01Bjumi5K7fg8yx6UK1mZTQd

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants