A set-builder's predicate is expanded like a function argument, so its condition escapes the binder — and a predicate that holds everywhere throws #878
Description
Found while fixing §2/§3 of #876, whose fix reaches the crash below from a new spelling. Both defects are on master at 363edfa3 with no help from that work. .NET 10, default settings.
1. A predicate's condition escapes its binder
{ x : 1/x = 0 }.Simplify() -> { } provided not x = 0
The x named in the condition is not the x the set ranges over — it is bound by the set-builder, and the condition has been lifted outside it. As an expression this is a scope error: whatever x means outside the set, it is not the one being quantified.
The cause is that ConditionalSet.InnerSimplify delegates to ExpandOnTwoAndTArguments with the predicate as an ordinary argument (Evaluation.Omni.Classes.cs:49). That helper lifts a Providedf out of an argument and puts it on the whole expression (Evaluation.Classes.cs:159-160), which is right for a function — arguments are values — and wrong for a node that binds a variable. The same applies to its Piecewise, Matrix and FiniteSet expansion.
The answer is also weaker than it needs to be. Membership is the predicate holding, so a predicate that is False where its condition holds and undefined where it does not admits nothing at all, and the set is {} with no condition to place anywhere.
2. A predicate that holds everywhere throws
{ x : 1 = 1 }.Simplify() -> AngouriBugException: The given domain is not presented in those possible
{ x : x = x }.Simplify() -> the same
{ x : x / x = 1 }.Simplify() -> the same
For a true predicate InnerSimplify returns the node's Codomain, ConditionalSet.Codomain is Domain.Any (Domains.Classes.cs:326), and SpecialSet.Create has no case for Any — it throws (Entity.Omni.Classes.cs:524). So that branch has never worked for a set-builder as parsed: every one whose predicate reduces to True throws out of Simplify instead of answering.
AngouriBugException asks the caller to report a bug, so this is not a wrong answer a user can work around — it is an abort on valid input.
There is no set that names every value a symbol could take, which is why Domain.Any has no SpecialSet. Leaving the set as written is available and honest; naming the universal set is a separate question.
3. TryContains declines what it can decide
("{ x : x = a and x > a }").TryContains("a", out _) -> false, once the predicate carries a condition
Same reasoning as §1 in a second place (Entity.Omni.Classes.cs:440): the predicate at x := a is False for real a and undefined for the rest of the plane, so a is not a member anywhere and membership is decided — but the code reads only EvaluableBoolean and gives up on a Providedf.
This one is currently latent: it takes a predicate whose reduction carries a condition to reach it, which #876's fix makes common.
What I am not claiming
That Providedf-hoisting is wrong for every binder in the library — I checked ConditionalSet and did not audit Sumf, Productf, Integralf or Derivativef for the same shape. §1's cause is read off Evaluation.Classes.cs:159-160 and confirmed by the printed result, not by instrumenting the pass.
Activity
Read a set-builder's predicate inside its binder (#878)
Happypig375 commentedon Aug 11, 2026
It might be reasonable to define a universal set that contains all complex, boolean, tensors etc. The problem is how the solution to
x - x = 0can include complexes and tensors but not booleans.Carry the condition an order comparison is decided under (#876), and …
Rafael-SOWNet commentedon Aug 16, 2026
Re-measured on
masterat2d128f3c. Both defects are fixed, by #879 (35f4ae5a, "…and read a set-builder's predicate inside its binder (#878)").{ x : 1/x = 0 }{ } provided not x = 0{ }{ x : 1 = 1 }AngouriBugException{ x : True }{ x : x = x }AngouriBugException{ x : True }{ x : x / x = 1 }AngouriBugException{ x : not x = 0 }§1 is fixed in the stronger of the two ways this issue offered: the condition does not escape the binder and the empty answer carries no condition at all, since a predicate that is false where its condition holds admits nothing. §2 no longer throws, and
{ x : x / x = 1 }keeps its condition inside the binder, which is the part that shows the fix is about scope rather than about suppressing the exception.@Happypig375's question is not resolved by that, and I do not think it is this issue's.
{ x : 1 = 1 }currently answers{ x : True }— a set-builder whose predicate isTrue— rather than naming a universal set. That is a well-formed answer and no longer a crash, so nothing is broken by leaving it; but it is also not the universal set you were asking about, and the tension you named is real: a universal set that contains booleans makesx - x = 0answer wrongly, and one that does not is not universal.That reads to me like its own proposal rather than a leftover of this bug. Happy to file it with your framing if you want it tracked separately; otherwise closing this seems right, since both measured defects are gone.
Happypig375 commentedon Aug 16, 2026
open the issue
Add sum and product, the first operators that bind a variable over a …
Rafael-SOWNet commentedon Aug 21, 2026
@Happypig375 — opened as #996, with your framing quoted and the state of
Domain.Anymeasured as it stands today.Re-measured all three sections on
masterat5211ccd6, which is nine commits past the check above. §3 was the one my earlier comment did not cover; it is fixed too.{ x : 1/x = 0 }{ } provided not x = 0{ }{ x : 1 = 1 }AngouriBugException{ x : True }{ x : x = x }AngouriBugException{ x : True }{ x : x / x = 1 }AngouriBugException{ x : not x = 0 }a in { x : x = a and x > a }§3's fix is in
ConditionalSet.TryContains: a predicate that isFalsewhere its condition holds and has no truth value where it does not is not membership either way, so membership is decided even though the condition is not. The control cases still behave —3 in { x : x > 2 }decides yes,1 in { x : x > 2 }decides no, andy in { x : x > 2 }stays undecided rather than being answered.Nothing measured in this issue is still open, and the design question it raised is now tracked in #996, so this can close.
One thing found while re-measuring and filed separately as #995:
SpecialSet.TryContainsanswers withMayContain's deliberately permissive result, so[1, 2] in BB,[1, 2] in ZZand[1, 2] in RRare allTrue— a matrix is a member of every special set at once,ZZ /\ BBincluded. Different node, different cause, so it is not part of this one.Rafael-SOWNet commentedon Aug 27, 2026
Re-measured on
masterata2edfc33during a pre-release sweep. Both defects are gone, and §1came out as the answer this issue argued for rather than merely stopping the leak.
1. The predicate's condition no longer escapes its binder
Not
{ } provided not x = 0. This issue makes the case that the stronger answer is the rightone — "membership is the predicate holding, so a predicate that is False where its condition
holds and undefined where it does not admits nothing at all, and the set is
{}with nocondition to place anywhere" — and that is what it gives.
2. A predicate that holds everywhere no longer throws
{ x : 1 = 1 }.Simplify(){ x : True }{ x : x = x }.Simplify(){ x : True }{ x : x / x = 1 }.Simplify(){ x : not x = 0 }No
AngouriBugException. The third keeps the condition inside the binder, where it belongs.Worth noting the class rather than just the instance: "
AngouriBugExceptionreachable fromordinary input" was also live in five other public entry points until today —
SumAll,MultiplyAll,Sumf.Sum,Mulf.MultiplyandMathS.Vectoron an emptysequence (#1028, #1096). An exception whose message asks the caller to report a bug against this
repository is worth grepping for whenever one turns up.
Closing is yours — flagging it because a release is being prepared and this reads as an open
crash report while it is not one any more.
Rafael-SOWNet commentedon Aug 31, 2026
Measured on
masterat9d9b21e4, .NET 10, default settings. All three sections, on the issue's own inputs.§1 — a predicate's condition escapes its binder. Fixed, and with the answer §1 argued for rather than a relocated condition:
Membership is the predicate holding, and a predicate that is
Falsewhere its condition holds admits nothing — so the set is empty and there is no condition left to place anywhere.§2 — a predicate that holds everywhere throws. Fixed; no
AngouriBugException:And it takes the option the issue named as available and honest — the set is left as written, rather than a universal
SpecialSetbeing invented for it. Whether there is a universal set stays #996's question, unprejudiced.{ x : x / x = 1 }is the nicest of the three: it keeps the one real exclusion instead of collapsing toTrue.§3 —
TryContainsdeclines what it can decide. Fixed:The predicate at
x := areduces toFalse provided a in RR—Falsewhere its condition holds and undefined elsewhere — so membership is decided, and it is now read that way rather than given up on for not being anEvaluableBoolean.Closing as fixed.