Domain.Any is a codomain and not the universal set (#996) - #1126
Merged
Conversation
#996 asked whether there should be a universal set and whether Domain.Any is it. Measured against the repository rather than argued: every one of the twelve nodes that declare Any means "this node imposes no restriction", the enum is an order with Any at the top and narrowing is what reads it, and the five domains that name a set are exactly the five SpecialSet.Create answers. Any is a codomain. A mathematical set that constrains nothing is already { x : True }, which parses, prints, round-trips, compares and decides membership -- so there is no AA, and nothing in the repository asked for one. Three code paths said otherwise, and each is corrected: - solve(a implies b) took the complement inside the *statement node's* codomain, which is Boolean for every implication, so `(x = 1) implies (x = 2)` was answered `{ 2 } \/ BB` -- truth values in the solution set of a numeric question. A TODO on the line asked for a universal set to subtract from. Neither is needed: "the values of x where a does not hold" is `{ x : not a }`, which names no universe at all. That is #996's answer -- what the solver wanted was the difference, and the difference is expressible without the universe. - the (-oo; +oo) rule asked SpecialSet.Create for the set of an interval's codomain, which threw NotSufficientlySupportedException out of `solve` on input a caller can write. The interval is now left as written where its codomain names no set. Its data twin declined the same case only because MatchedRule.Build swallows what a replacement throws -- the right answer arrived at by an exception -- so the condition says it instead. - the implicit Domain -> Entity conversion is partial and now documents that it is, rather than reading as "a domain is a set". The enum's own comment said "the domain of all values (might be removed in the future)", which is the reading this issue exists to rule out. It now says what Any is, what it is not, and where the unconstrained *set* lives; the ambient MathS.Settings.Codomain is documented as separate from both. Syntax.md's claim that Any cannot be written was left stale by #1048 and is corrected with it. `solve x in (x - x = 0)` is CC, unchanged, and CC still decides against a truth value and a matrix -- which is what makes it an answer rather than a universal set. 30 new tests pin the boundary and 2 more pin the page; three of them fail against the previous behaviour. 9159 passed, 0 failed.
This was referenced Aug 31, 2026
Rafael-SOWNet
added a commit
that referenced
this pull request
Aug 31, 2026
StatementSolver.Solve had arms for equality, the connectives, the four
comparisons, membership, `provided` and `piecewise` -- and none for `not`, so
every negation fell through to `Set.Empty`. The empty set is a positive claim,
*no x satisfies this*, and it was false of every one of them:
not (x = 1) { }
not (x > 1) { }
not (x in RR) { }
not not (x = 1) { }
not (x > 1 or x < -1) { }
This is the defect #1036 fixed for equations, left standing for negation.
The negation is now pushed inward as far as there is an arm for it, and named
as a set-builder where there is not:
not (x = 1) { x : not x = 1 }
not (x > 1) (-oo; 1]
not (x in RR) { x : not x in RR }
not not (x = 1) { 1 }
not (x > 1 or x < -1) [-1; 1]
Inward is unambiguous *here* in a way it is not in the simplifier, which is why
it is done in the solver and not as a rule: this switch has arms for the
connectives and for the comparisons and none for `not`, so inward is the
direction that reaches one. A negated comparison is a comparison, and
RewriteRules.InequalityEquality is where that is already written down -- asking
it rather than restating it keeps the two from drifting.
What no arm reaches is answered as written rather than as nothing, which is the
shape #1126 introduced for implications: `{ x : not x in RR }` names the
non-real complex numbers exactly.
26 tests, asserting which values the answers admit rather than the shape they
are written in; 21 of the 26 fail against the previous behaviour.
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.
Answers #996 — should there be a
universal set, and is
Domain.Anyit? — from the repository rather than from the question, andcorrects the three places that had already answered it the other way.
The conclusion
Domain.Anyis a codomain. It is not a set, and there is no universal set to add.Three things in the code say so, none of them the comment on the enum member:
Any—Variable,Matrix,ConditionalSet,Lambda, the set operators, the providers — means this node imposes no restriction. That is astatement about a node, not a claim that a collection contains every value.
Codomain < Domain.Complexfor "narrower than the complex plane", andDomainConditionInnarrows a node whose codomain is wider than the reading.
Anyis the top of that order, whichmakes it the identity for narrowing — not a set of everything.
SpecialSet.Createanswers.Anythrows,and every internal caller answers it before asking.
And the thing a universal set would be for is already there:
{ x : True }parses, prints,round-trips, compares up to its bound name, and decides membership for a number, a truth value and
a matrix alike. Set-difference, intersection, union and
Filterall work on it. Nothing in therepository asked for
AA, so none is introduced.solve x in (x - x = 0)isCC, unchanged — andCCstill decides againsttrueand against[1, 2](#995), which is what makes it an answer rather than a universal set in disguise.What was wrong
1.
solve(a implies b)put truth values in the solution set of a numeric question.The arm took the complement inside
expr.Codomain— the statement node's codomain, which isBooleanfor everyImpliesf. ATODObeside it asked for a universal set to subtract from.Neither the codomain nor a universal set was needed. The values of
xwhereadoes not holdis
{ x : not a }, which names no universe at all and is right whateverxranges over. That is#996's answer: what the solver wanted was the difference, and the difference is expressible
without the universe — which is the case against adding one.
It does not make the implication solver complete.
Solve(b, x)is still empty wherebdoes notmention
x, soA implies Trueis{ A : not A }rather thanBB— as it was before, where theanswer was
BB \ { True }. What stops is answering with a set the question was never asked over.2. An unbounded interval widened to
Anythrew out ofsolve.(-oo; +oo)becomes the domain it is an interval of; widened toAnythere is no such domain, soit is now left as written. Its data twin in
MatchedRulesdeclined the same case only becauseMatchedRule.Buildswallows what a replacement throws — the right answer arrived at by anexception — so the condition says it instead.
3. The implicit
Domain→Entityconversion read as "a domain is a set".Kept for compatibility, now documented as partial: it is the map from the five domains that name a
set, and it throws for
Anyrather than inventing one.Documentation
The enum member said "the domain of all values (might be removed in the future)" — the reading
this issue exists to rule out, and an open question in a place that looks like an answer. It now
says what
Anyis, what it is not, that the unconstrained set is{ x : True }, and that theambient
MathS.Settings.Codomainis a third thing again: a parameter of the question, not aproperty of an expression and not a solution set.
Syntax.mdstill saidAnycannot be written, which #1048 made false. Corrected, along with thedomain(...)entry, and the Sets table now records that there is no universal set and no literalfor one.
The SymPy exporter maps a set-builder's
AnytoS.UniversalSet. That stays — it is what SymPyprints a
ConditionSetwithout a third argument over — with a comment that it is a choice aboutthe target language, since SymPy has a universal set and this library does not, and an exporter
to Lean or SMT picks differently.
Tests
32 new tests, in
DomainAnyIsNotAUniversalSetTestandSyntaxDocumentedTest. They pin theinvariant rather than the implementation:
{ x : True }as the unconstrained set (membership,round-trip, stability, bound-name independence), the five domains converting to sets and
Anynot,no literal for
AAorUU,Anyas a codomain keyword and not a set literal,x - x = 0answering the domain it was asked in and that domain excluding a truth value and a matrix, an
implication asserting no domain, and the widened interval. Three of them fail against the previous
behaviour; the rest would fail the day a universal set is added by accident.
Measured
casbench116/119, 0 wrong, 0 error, 0 timeout.crashcheck1834 cases, 0 crashed, 0 unexpected.rulecheck30 sets, 17532 applications, 0 value changes, 0 never settle.confluenceSetOperator0 nodes with several arms, 0 disagreeing.BREAKING-CHANGES.md, both measured on a build.Deliberately not in this PR
AnalyticalEquationSolveranswers a tautology with a hardcodedMathS.Sets.Crather than theambient
MathS.Settings.Codomain, sosolve x in (x - x = 0)isCCeven underCodomain.Set(Domain.Real). Conservative and correct at the default; making the tautology answerfollow the ambient reading is a solver change with its own blast radius.
Solvehas noNotfarm, sonot (x = 1)answers{ }— the empty set as a positive claim,which is the defect Giving up on a resource is spelled the same as a mathematical negative #1036 fixed for equations, unfixed for negation. Filed as Solve answers every negation with the empty set, because the statement solver has no arm for not #1127.
Neither is a domain-versus-set confusion, which is what this one is about.