Skip to content

Answer a polynomial with its roots, not with one per starting point - #686

Merged
Happypig375 merged 1 commit into
ASC-Community:masterfrom
Rafael-SOWNet:fix/duplicate-numeric-roots
Aug 4, 2026
Merged

Happypig375 merged 1 commit into
ASC-Community:masterfrom
Rafael-SOWNet:fix/duplicate-numeric-roots

Conversation

@Rafael-SOWNet

Copy link
Copy Markdown
Member
"x^5 + 3*x + 1".SolveEquation("x")   // master: 28 roots
"x^6 + x + 1".SolveEquation("x")     // master: 23 roots

Four of those 28:

-0.83907243306660750
-0.83907243306660761
-0.83907243306660773
-0.83907243306660784

They are one root. The numeric search starts from a grid and iterates in double precision, so the same root reached from different starting points comes back agreeing to about sixteen significant digits and differing after that — and each starting point contributed an answer of its own. The rounding that was already there only zeroes components near zero, so it collapses a stray 1e-19 imaginary part but has nothing to say about these.

What it does now

Candidates closer together than the iteration can tell apart are one root, and the one kept from each group is whichever leaves the equation closest to zero.

The threshold is measured. Candidates for a single root lie within 1e-15 of each other relative to their size; the nearest two genuinely distinct roots over the twelve polynomials I tried are 0.42 apart. The threshold is 1e-13 — a hundred times above the noise, and twelve orders below the closest two roots that were ever told apart.

polynomial before after
x^5 + 3x + 1 28 5
x^6 + x + 1 23 6
x^9 - x - 1 33 9
x^5 - 5x + 2 19 5

Every one of the twelve now comes back with exactly as many roots as its degree, and every root satisfies its equation. The polynomials solved exactly, by radicals rather than by iteration — x^5 - 1, x^8 - 1, x^7 - 2 — never had the problem and are untouched.

One existing test changed

FromStringTest.TestFormula8 asserted which of the two roots of x^2 + 1 a HashSet hands back first. That was never the solver's to decide, and it changed here: the candidates are now ordered before being collapsed, so that which one is compared against which does not depend on the order the grid produced them in. The test now asks for both roots, which is what it was checking.

Verification

19 new tests, 9 of which fail without the change. 4015 unit tests and 127 F# tests on both target frameworks, none failing. 117-problem self-verifying corpus at 101/117 with nothing wrong, in error or timing out; 1320 property checks over 151 expressions, none failing.

No issue is filed for this — I found it while checking the answers of #685.

…#unreported)

x^5 + 3x + 1 came back with 28 roots and x^6 + x + 1 with 23. The numeric search
starts from a grid, and the same root reached from different starting points
comes back agreeing to about sixteen significant digits and differing after that,
so each starting point contributed an answer of its own. Four of the 28 were
-0.83907243306660750, -0.83907243306660761, -0.83907243306660773 and
-0.83907243306660784.

Candidates closer together than the iteration can tell apart are now one root,
and the one kept from each group is whichever leaves the equation closest to
zero. The room between the two cases is wide: candidates for one root lie within
1e-15 of each other relative to their size, while the nearest two distinct roots
over the twelve polynomials tried are 0.42 apart. The threshold is 1e-13, a
hundred times above the noise and twelve orders below the closest two roots that
were ever told apart.

Each of those twelve now comes back with exactly as many roots as its degree,
and every one of them satisfies its equation.

TestFormula8 asserted which of the two roots of x^2+1 a HashSet hands back first.
That was never the solver's to decide and it changed here, because the
candidates are now ordered before being collapsed so that which one is compared
against which does not depend on the order the grid produced them in. The test
now asks for both roots, which is what it was checking.

19 tests, 9 of which fail without the change. 4015 in all, 127 F#, corpus 101/117
with nothing wrong, and 1320 property checks.
@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 96.66667% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 80.41%. Comparing base (90c00a8) to head (6f3050a).
⚠️ Report is 56 commits behind head on master.

Files with missing lines Patch % Lines
...ontinuous/Solvers/NumericalSolving/NewtonSolver.cs 96.66% 1 Missing ⚠️
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #686      +/-   ##
==========================================
- Coverage   80.99%   80.41%   -0.59%     
==========================================
  Files         155      156       +1     
  Lines       13687    12923     -764     
  Branches     1957     2123     +166     
==========================================
- Hits        11086    10392     -694     
+ Misses       1990     1923      -67     
+ Partials      611      608       -3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Happypig375
Happypig375 merged commit 4756cf9 into ASC-Community:master Aug 4, 2026
24 checks passed
@Rafael-SOWNet
Rafael-SOWNet deleted the fix/duplicate-numeric-roots branch August 4, 2026 20:49
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.

3 participants