Skip to content

Over the reals, answer an exponential equation with real roots only - #2433

Open
Rafael-SOWNet wants to merge 1 commit into
masterfrom
feature/a-root-over-the-reals-is-real
Open

Rafael-SOWNet wants to merge 1 commit into
masterfrom
feature/a-root-over-the-reals-is-real

Conversation

@Rafael-SOWNet

Copy link
Copy Markdown
Member

Closes #2425.

Under MathS.Settings.Codomain.Set(Domain.Real), an exponential equation could answer a root that is not real. e^x + 1 = 0 answered { ln(-1) }, and ln(-1) is iπ.

There were two causes:

  • The inversion ignored the codomain. Powf.InvertNode turned a^u = v into log(a, v) for every v. The real codomain is applied only inside particular solvers (the logarithm, Lambert W and modulus solvers), and the exponential inversion was not one of them.
  • The replacement search assumed its variable was real. It solved the variable that stands for a repeated subtree under the caller's codomain, so over the reals it treated that variable as real. That is false where the subtree is something like i x. It is now solved over the complex numbers in that case.

Over the reals, the inversion now answers no root where the base is a positive real, the exponent is real-valued, and the value is decidedly not a positive real. That is decided by value, from a 40-digit enclosure, never by the printed form. Anything the check cannot decide keeps its root as before. Over the complex numbers, 31 equations give output identical character for character to master's.

equation, over RR 2.6.0 and master this branch true
e^x + 1 = 0 { ln(-1) } { } { }
e^x - 2e^(-x) - 1 = 0 { ln(-1), ln(2) } { ln(2) } { ln(2) }
2^x = -3 { log(2, -3) } { } { }
3 e^x + 5 = 0 { ln(-5/3) } { } { }
e^(2x) + e^x - 2 = 0 { 0, ln(-2) } { 0 } { 0 }
e^x + e^(-x) = 1 { ln((1∓sqrt(-3))/2) } { } { }
e^x - e^(-x) = 3 { ln((3-sqrt13)/2), ln((3+sqrt13)/2) } { ln((3+sqrt13)/2) } the same
e^x = -1 + i { ln(-1+i) } { } { }
e^(x^2) = -1 { ±sqrt(ln(-1)) } { } { }
e^x - 2 = 0, e^(2x) - 3e^x + 2 = 0, ln(x) = 2, e^x = y unchanged unchanged

The rows are in BREAKING-CHANGES.md, measured on the 2.6.0 package. Not changed here, and still not filtered by the real codomain: polynomial roots (x^2 = -1 gives { i, -i }) and trigonometric ones (sin(x) = 2).

Checked on this branch:

  • Tests: UnitTests 19370 passed, plus the MCP, Terminal, InteractiveWrapper and F# wrapper suites, and the AOT smoke test. The new tests check, for each root, its count, that its value is real, and that substituting it leaves a residual under 1e-30; and that the complex codomain still answers iπ.
  • Harnesses: BoundCheck, RootCheck, CasBench, PropCheck, SimpSweep, RuleCheck, CanonCheck and Confluence all pass.

🤖 Generated with Claude Code

A positive base to a real power is positive, so over the reals a^u = v has a
root only for a positive v. Powf's inversion answered log(a, v) for every v, and
ln(-1), which is i pi, stayed a node that nothing read as non-real. Under the
real codomain the inversion now has no root where an enclosure of v's value
shows it is not a positive real; a v it cannot separate from the positive
reals, or a symbolic one, keeps its root as before.

The replacement search reads its replacement as real, which holds only where
the subtree it stands for is real at every real x. One that is not, such as
i x, is solved over the complex numbers, so e^(i x) = -1 keeps its root pi;
one that is, such as x^2, and has no real root, leaves x with none:
e^(x^2) = -1 is { }.

The complex codomain is unchanged. BREAKING-CHANGES.md rows measured on the
2.6.0 package and on this branch.

Closes #2425.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov

codecov Bot commented Nov 13, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 73.91304% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.81%. Comparing base (2eea2e9) to head (87b8f00).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
...nuous/Solvers/EquationSolver/InvertNode.Classes.cs 50.00% 3 Missing and 3 partials ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #2433      +/-   ##
==========================================
- Coverage   86.81%   86.81%   -0.01%     
==========================================
  Files         312      312              
  Lines       69551    69573      +22     
  Branches    20316    20325       +9     
==========================================
+ Hits        60383    60401      +18     
- Misses       4932     4933       +1     
- Partials     4236     4239       +3     
Flag Coverage Δ
cpp 37.77% <ø> (ø)
csharp 87.35% <73.91%> (-0.01%) ⬇️
fsharp 32.97% <ø> (ø)
interactive 39.81% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...Solvers/EquationSolver/AnalyticalEquationSolver.cs 96.25% <100.00%> (+0.25%) ⬆️
...nuous/Solvers/EquationSolver/InvertNode.Classes.cs 61.61% <50.00%> (-0.52%) ⬇️

... and 4 files with indirect coverage changes

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

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

Over the reals, an exponential equation answers ln(-1) as a root

1 participant