Repository navigation
Hold excluded middle whichever operand carries the negation (#876) - #877
Merged
Merged
Conversation
`or` is commutative and this reduction was not:
(not (x < 0) or (x < 0)).Simplify() -> True
((x < 0) or not (x < 0)).Simplify() -> x < 0 or x >= 0
There was one excluded-middle rule, Orf(Notf(a), a), matching the negation on the left
operand only, and no mirror of it anywhere in the file. So the same proposition had two
answers depending on which side it was written on.
A bare variable hid it: `p or not p` and `not p or p` both give True, because the boolean
minimiser reduces expressions over boolean variables whichever way round they are. It takes
an operand the minimiser does not treat as an atom -- a comparison -- to see the hole, which
is why this survived. It reproduces on <, >, <=, = and in:
(x < 0) or not (x < 0) was x < 0 or x >= 0 now True
(x > 0) or not (x > 0) was x > 0 or x <= 0 now True
(x <= 0) or not (x <= 0) was x <= 0 or x > 0 now True
(a = b) or not (a = b) was a = b or not a = b now True
(x in RR) or not (x in RR) was x in RR or not x in RR now True
The `=` case is what rules out the obvious explanation. For `<` the negation is rewritten to
`>=` before the disjunction is looked at, which would destroy the pattern on its own; for `=`
there is no such rewrite, the shape Orf(a, Notf(a)) is intact, and it still did not reduce.
The missing mirror is the whole cause.
`and` is not affected: it has no contradiction rule on either side, so it is symmetric. Where
`(x < 0) and not (x < 0)` reduces to False it is comparison reasoning about x < 0 and x >= 0
being unsatisfiable, not this rule, and it already worked both ways round.
This does not touch the soundness half of #876, which stays open. Excluded middle needs the
proposition to have a truth value, and over the default complex codomain `i < 0` is NaN, so
the left-handed form was already answering True where the honest value is NaN. This change
makes that reachable from one more spelling rather than introducing it; the fix wants the
rules to read MathS.Settings.Codomain, which nothing outside the limit machinery does yet.
DomainCondition is not the mechanism for it -- it records singularities, not where an order
comparison is defined.
Verified: 6227 C# tests and 130 F# tests pass, 0 fail. propcheck 1340 checks 0 failures,
simpsweep 62778 point comparisons 0 disagreements, rootcheck 596 cases 0 incomplete and
0 unsound, casbench 117/119 with 0 wrong, 0 error and 0 timeout, every verdict and answer
in coverage.md byte-identical. boolmin unchanged at 6/9.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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.
Fixes §1 of #876.
oris commutative and this reduction was not:There was one excluded-middle rule,
Orf(Notf(a), a), matching the negation on the left operand only, with no mirror anywhere in the file. The same proposition therefore had two answers depending on which side it was written on.Why it survived this long
A bare variable hides it —
p or not pandnot p or pboth giveTrue, because the boolean minimiser reduces expressions over boolean variables whichever way round they are. It takes an operand the minimiser does not treat as an atom — a comparison — to expose the hole:(x < 0) or not (x < 0)x < 0 or x >= 0True(x > 0) or not (x > 0)x > 0 or x <= 0True(x <= 0) or not (x <= 0)x <= 0 or x > 0True(a = b) or not (a = b)a = b or not a = bTrue(x in RR) or not (x in RR)x in RR or not x in RRTrueThe
=row is what rules out the obvious explanation. For<, the negation is rewritten to>=before the disjunction is looked at, which would destroy the pattern on its own — so that looked like the cause. For=there is no such rewrite, the shapeOrf(a, Notf(a))is intact, and it still did not reduce. The missing mirror is the whole cause.andis unaffected: it has no contradiction rule on either side, so it is symmetric. Where(x < 0) and not (x < 0)givesFalsethat is comparison reasoning aboutx < 0andx >= 0being unsatisfiable, and it already worked both ways round.What this deliberately does not fix
The soundness half of #876 stays open. Excluded middle needs the proposition to have a truth value, and over the default complex codomain
i < 0isNaN— so the left-handed form was already answeringTruewhere the honest value isNaN. This change makes that reachable from one more spelling rather than introducing it.The real fix wants these rules to read
MathS.Settings.Codomain, which nothing outside the limit machinery does yet. Worth recording:DomainConditionis not the mechanism, despite the neighbouring rules using it — it records singularities (division by zero, log poles), not where an order comparison is defined.Verification
propcheck1340 checks, 0 failuressimpsweep62778 point comparisons over 10463 expressions, 0 disagreementsrootcheck596 cases, 0 incomplete, 0 unsoundcasbench117/119, 0 wrong, 0 error, 0 timeout — every verdict and answer incoverage.mdbyte-identical, only timings movedboolminunchanged at 6/9🤖 Generated with Claude Code