Skip to content

Domain.Any is a codomain and not the universal set (#996) - #1126

Merged
Rafael-SOWNet merged 1 commit into
masterfrom
universal-set-is-not-a-codomain-996
Aug 31, 2026
Merged

Domain.Any is a codomain and not the universal set (#996)#1126
Rafael-SOWNet merged 1 commit into
masterfrom
universal-set-is-not-a-codomain-996

Conversation

@Rafael-SOWNet

@Rafael-SOWNet Rafael-SOWNet commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator

Answers #996should there be a
universal set, and is Domain.Any it?
— from the repository rather than from the question, and
corrects the three places that had already answered it the other way.

The conclusion

Domain.Any is 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:

  • Every one of the twelve nodes that declares AnyVariable, Matrix, ConditionalSet,
    Lambda, the set operators, the providers — means this node imposes no restriction. That is a
    statement about a node, not a claim that a collection contains every value.
  • The enum is an order, narrowest to widest, and that is how it is read: evaluation asks
    Codomain < Domain.Complex for "narrower than the complex plane", and DomainConditionIn
    narrows a node whose codomain is wider than the reading. Any is the top of that order, which
    makes it the identity for narrowing — not a set of everything.
  • The five domains that name a set are exactly the five SpecialSet.Create answers. Any throws,
    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 Filter all work on it. Nothing in the
repository asked for AA, so none is introduced.

solve x in (x - x = 0) is CC, unchanged — and CC still decides against true and 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 is
Boolean for every Impliesf. A TODO beside it asked for a universal set to subtract from.

"(x = 1) implies (x = 2)".ToEntity().Solve("x")     { 2 } \/ BB          was
                                                    { x : not x = 1 }    is
"x > 1 implies x > 0".ToEntity().Solve("x")         BB \ (1; +oo) \/ (0; +oo)
                                                    { x : not x > 1 } \/ (0; +oo)
"A implies B".ToEntity().Solve("A")                 BB \ { True }
                                                    { A : not A }

Neither the codomain nor a universal set was needed. The values of x where a does not hold
is { x : not a }, which names no universe at all and is right whatever x ranges 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 where b does not
mention x, so A implies True is { A : not A } rather than BB — as it was before, where the
answer was BB \ { True }. What stops is answering with a set the question was never asked over.

2. An unbounded interval widened to Any threw out of solve.

"domain((-oo; +oo), Any) = RR".ToEntity().Solve("x")
    NotSufficientlySupportedException: There is no special set for domain Any     was
    {  }                                                                          is

(-oo; +oo) becomes the domain it is an interval of; widened to Any there is no such domain, so
it is now left as written. Its data twin in MatchedRules 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.

3. The implicit DomainEntity conversion 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 Any rather 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 Any is, what it is not, that the unconstrained set is { x : True }, and that the
ambient MathS.Settings.Codomain is a third thing again: a parameter of the question, not a
property of an expression and not a solution set.

Syntax.md still said Any cannot be written, which #1048 made false. Corrected, along with the
domain(...) entry, and the Sets table now records that there is no universal set and no literal
for one.

The SymPy exporter maps a set-builder's Any to S.UniversalSet. That stays — it is what SymPy
prints a ConditionSet without a third argument over — with a comment that it is a choice about
the 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 DomainAnyIsNotAUniversalSetTest and SyntaxDocumentedTest. They pin the
invariant rather than the implementation: { x : True } as the unconstrained set (membership,
round-trip, stability, bound-name independence), the five domains converting to sets and Any not,
no literal for AA or UU, Any as a codomain keyword and not a set literal, x - x = 0
answering 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

  • Unit suite 9159 passed, 0 failed, 14 skipped.
  • casbench 116/119, 0 wrong, 0 error, 0 timeout.
  • crashcheck 1834 cases, 0 crashed, 0 unexpected.
  • rulecheck 30 sets, 17532 applications, 0 value changes, 0 never settle.
  • confluence SetOperator 0 nodes with several arms, 0 disagreeing.
  • Two entries in BREAKING-CHANGES.md, both measured on a build.

Deliberately not in this PR

Neither is a domain-versus-set confusion, which is what this one is about.

#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.
@Rafael-SOWNet
Rafael-SOWNet merged commit 82c40ca into master Aug 31, 2026
31 checks passed
@Rafael-SOWNet
Rafael-SOWNet deleted the universal-set-is-not-a-codomain-996 branch August 31, 2026 15:12
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.
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.

1 participant