Repository navigation
Do not name a root that denies how it was found (#964) - #1025
Merged
Merged
Conversation
"derivative(y, x) + y - x".SolveEquation("y") answered { x }. Substituting
that back, with this library's own Substitute, gives derivative(x, x) + x - x,
which is 1: the set named a member that is not a root.
The derivative went to zero because y is not x, and every calculus operator
reads a name it does not bind the same way -- limit(y, x, 0) is y,
integral(y, x) is x * y + C, sum(y, k, 1, 3) is 3 * y. That is a decision
about the name, and the root the solver then returns says the name stands
for an expression in x. The answer denies the step that produced it.
"derivative(y, x) + y - x".SolveEquation("y") { x }
"derivative(y, x) + y ^ 2 - x".SolveEquation("y") { sqrt(x), -sqrt(x) }
"integral(y, x) + y - x".SolveEquation("y") { -(C + -x) / (x + 1) }
"limit(y, x, 0) + y - x".SolveEquation("y") { x / 2 }
"sum(y, k, 1, 3) + y - k".SolveEquation("y") { k / 4 }
All five now come back as the condition itself. The empty set would say
there is no such y, which is a second false claim in place of the first;
the condition says only that this was not settled, which is true. Solving
it is #746 item 48, a differential-equation solver, and is not here.
A root free of the name the operator is taken over denies nothing and is
returned as before: derivative(y * x, x) + y - 1 is still { 1/2 }, and
derivative(y ^ 2, y) - 2 is { 1 }, where the unknown is the name the
derivative binds and no independence is claimed of it.
The set builder had the same reading of its own bound name:
{ y : derivative(y, x) + y - x = 0 } simplified to { y : y - x = 0 }, which
is { x }. A binder over y makes y range over values, and expressions in x
are among them, so that simplification settled a condition written to stay
open -- and it is what the answer above would have collapsed into. Guarded
where the predicate is simplified.
Not touched: the equality half of `<=` and `>=` goes through the same
solver, but the inequality half is answered by AnalyticalInequalitySolver
under the same assumption, so guarding one of the two would give a set that
is honest in half of itself. That needs its own change.
Suite 7549 passed, 0 failed, 14 skipped -- the 16 added here on top of the
7533 of the merge base. F# wrapper 134 passed. Both sides of every value
above measured on a build: 6b93b40 and this tree.
Claude-Session: https://claude.ai/code/session_01Bjumi5K7fg8yx6UK1mZTQd
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sources/.editorconfig pins the header per directory rather than globally -- bumping the default would make IDE0073 fail on the 367 files that carry the 2022 one -- so a new file needs its own scope or it ships with a copyright four years out of date. The two other branches that add files append their scopes to the same place, so this costs whichever of the three merges later one append conflict, and all three entries survive it.
This was referenced Aug 23, 2026
BREAKING-CHANGES.md and Sources/.editorconfig are both append-only in practice, so the merge keeps both sides -- except that a section heading is not an append: unioning left two '## Unreleased' headings, and the entries now sit under one.
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 #964. Roadmap #746 item 3 — a live wrong answer.
What was wrong
"derivative(y, x) + y - x".SolveEquation("y")answered{ x }. Substituting that back — with this library's ownSubstitute— givesderivative(x, x) + x - x, which is1. The set named a member that is not a root.The derivative went to zero because
yis notx, and every calculus operator reads a name it does not bind the same way:limit(y, x, 0)isy,integral(y, x)isx * y + C,sum(y, k, 1, 3)is3 * y. Each is a decision about the namey. The root the solver then returns says that name stands for an expression inx, so the answer denies the step that produced it.Measured on
6b93b401, the merge base:"derivative(y, x) + y - x".SolveEquation("y"){ x }("derivative(y, x) + y - x = 0").Solve("y"){ x }"derivative(y, x) + y ^ 2 - x".SolveEquation("y"){ sqrt(x), -sqrt(x) }, leavingx ^ (-1/2) / 2and-1/2 * x ^ (-1/2)"integral(y, x) + y - x".SolveEquation("y"){ -(C + -x) / (x + 1) }"limit(y, x, 0) + y - x".SolveEquation("y"){ x / 2 }"sum(y, k, 1, 3) + y - k".SolveEquation("y"){ k / 4 }"{ y : derivative(y, x) + y - x = 0 }".ToEntity().Simplify(){ y : y - x = 0 }So it is not one equation and not one operator: every binder in the language, and a quadratic in the unknown as readily as a linear one.
Why declining, and which spelling
Solving these needs a differential-equation solver — #746 item 48 — and this is not that. So the answer has to be a refusal, and a refusal has two spellings that are not equally honest:
y, which is false;All six now come back as
ConditionalSet—{ y : derivative(y, x) + y - x = 0 }and so on.AnalyticalEquationSolveralready answers this way for #278, so it is the established spelling here rather than a new one.A root that does not mention the name the operator is taken over denies nothing, and is returned as before. Nothing that answered correctly stops answering:
"derivative(y * x, x) + y - 1".SolveEquation("y")is{ 1/2 }"derivative(y ^ 2, y) - 2".SolveEquation("y")is{ 1 }— there the unknown is the name the derivative binds"limit(y * x, x, 2) - 4".SolveEquation("y")is{ 2 }"sum(y, k, 1, 3) - 6".SolveEquation("y")is{ 2 }The set builder had it too
{ y : derivative(y, x) + y - x = 0 }simplified to{ y : y - x = 0 }, which is{ x }— the same wrong answer, and the thing the honest answer above would have collapsed into on its way out throughEntity.Solve. A binder overymakesyrange over values, and expressions inxare among them, so that reading is not available under it.ConditionalSet.InnerSimplifynow leaves such a predicate alone.That is one shared predicate,
CalculusOperator.NamesAssumedFreeOf, used at both sites.What is deliberately not touched
The equality half of
<=and>=goes through the same solver, but the inequality half is answered byAnalyticalInequalitySolverunder the same assumption —(derivative(y, x) + y - x <= 0).Solve("y")is{ x } \/ (-oo; -x / (-1)), and both halves are wrong. Guarding one of the two would give a set that is honest in half of itself, so it needs its own change rather than half of this one.Measured
6b93b401and this tree.BREAKING-CHANGES.mdcarries all of it under a newUnreleasedsection.The new tests are in
Sources/Tests/UnitTests/Algebra/SolveTest/CalculusEquationTest.cs. The first of them is the property, not the form: every member of a finite answer is substituted back and required to simplify to zero. It is what fails on the merge base, withx is not a root of derivative(y, x) + y - x; it leaves 1.🤖 Generated with Claude Code
https://claude.ai/code/session_01Bjumi5K7fg8yx6UK1mZTQd