Skip to content

Ask whether it is a member, not whether it might be (#995) - #997

Open
Rafael-SOWNet wants to merge 1 commit into
masterfrom
fix/special-set-membership-995
Open

Ask whether it is a member, not whether it might be (#995)#997
Rafael-SOWNet wants to merge 1 commit into
masterfrom
fix/special-set-membership-995

Conversation

@Rafael-SOWNet

Copy link
Copy Markdown
Collaborator

Fixes #995.

The defect

SpecialSet.TryContains decided membership by asking MayContain, and every MayContain is written to be permissive:

public override bool MayContain(Entity entity)
    => entity is Boolean || !entity.IsConstantLeaf;   // Booleans, and the same shape four more times

IsConstantLeaf is Boolean or Number or SpecialSet, so a matrix, a finite set and an interval were all "not ruled out" and therefore members — of every special set, simultaneously:

before after
[1, 2] in BB True False
[1, 2] in ZZ True False
[1, 2] in QQ True False
[1, 2] in RR True False
[1, 2] in CC True False
[1, 2] in (ZZ /\ BB) True False
{ 1, 2 } in RR True False
[0; 1] in RR True False

ZZ and BB share no members, so the old answers contradicted each other and nothing could have relied on all of them.

Why the two disagree

The two names are asking two different questions, and only one implementation existed for both.

MayContainmight this be a member — is right for its other caller. DomainsFunctional.FitsDomainOrNonNumeric guards a codomain, and there the safe direction is to let through whatever has not been ruled out. MayContain is unchanged by this PR.

TryContainsis this a member — hands back a decision in an out bool. Reading the permissive result as the decided one turns "I cannot rule this out" into "yes".

The symbolic guard above it is why this survived: anything containing a variable is declined before MayContain is reached, and every numeric value is a constant leaf, so only the closed non-leaves took the permissive branch. For those the right answer is available — a matrix is not a real number — and it is False, decided rather than unknown.

What did not change

3 in RR      -> decided: yes        i in RR    -> decided: no
true in BB   -> decided: yes        3 in BB    -> decided: no
3/4 in QQ    -> decided: yes        3/4 in ZZ  -> decided: no
x in RR      -> undecided           RR in BB   -> decided: no

Out of scope, and deliberately: +oo in CC and 0/0 in RR are both True today. Those are constant leaves, so they go down the other branch — the same question as the -oo half of the domain-condition discussion, not this one. #995 says so too.

Measured

  • Full suite on this branch: 7377 passed, 0 failed, 14 skipped.
  • casbench: 116/119, 0 wrong, 0 error, 0 timeout — the standing figure.
  • 11 new cases in Sources/Tests/UnitTests/Core/Sets/Contains.cs, covering each special set against a matrix, the intersection, a finite set, an interval, and the leaf answers that must not move.
  • BREAKING-CHANGES.md entry with both values measured on builds — 5211ccd6 for the old ones.

`SpecialSet.TryContains` decided membership by calling `MayContain`, whose
answer is deliberately permissive: every one of the five is written as
`entity is <that kind> || !entity.IsConstantLeaf`. That is what its other
caller wants -- `DomainsFunctional.FitsDomainOrNonNumeric` guards a codomain,
where letting through what has not been ruled out is the safe direction --
but membership is the opposite question, and its `out bool` is a decision.

So everything closed that was not a constant leaf was a member of every
special set at once:

    [1, 2] in BB   ->  True
    [1, 2] in ZZ   ->  True
    [1, 2] in RR   ->  True
    [1, 2] in (ZZ /\ BB)  ->  True
    { 1, 2 } in RR ->  True

The symbolic guard above it is why this went unseen: anything with a variable
is declined before `MayContain` is reached, and every numeric value is a
constant leaf, so only matrices, finite sets and intervals took the permissive
branch -- and for those the answer is available and is False. A matrix is not
a real number, and that is decided rather than unknown.

`MayContain` is unchanged, and so is every answer for a number or a boolean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KbKcbJP266A3EGyQ5kq7Ru
@Rafael-SOWNet

Copy link
Copy Markdown
Collaborator Author

Against the two PRs already open, this conflicts in one file — BREAKING-CHANGES.md, and only in the at-a-glance table. Derived with git merge-tree --write-tree, not assumed:

against conflicts
#991 constant-node-984 BREAKING-CHANGES.md. Entity.Omni.Classes.cs auto-merges — #991 edits ConditionalSet, this edits SpecialSet
#990 fix/bound-name-must-be-symbolic BREAKING-CHANGES.md

Resolution is "keep both row sets"; the one row that goes is #990's derivative(e ^ 2, e) line, superseded by #991's, which carries the alpha-renamed answer.

All three compose. Built master + #990 + #991 + #997 on a throwaway branch and ran the suite: 7466 passed, 0 failed, 14 skipped. Spot-checks on that build:

[1, 2] in RR                  decided: no
[1, 2] in (ZZ /\ BB)          False
3 in { e : e > 0 }            decided: yes
[1, 2] in { e : e > 0 }       undecided   -- a different path, and rightly undecided
derivative(e ^ 2, e)          2 * e

The last two are where the two changes meet: { e : e > 0 } is a set builder over a name #991 made a variable, and its membership goes through ConditionalSet.TryContains, not the one this PR changes — so a matrix against it is still undecided rather than decided False, which is right, since [1, 2] > 0 has no truth value.

Whichever merges last takes the one table conflict; I will do it.

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.

A matrix is a member of every special set at once, because TryContains answers with MayContain's permissive result

1 participant