Skip to content

fix(tla): enumerate memberships in a disjunctive Init conjunct (Prisoner under-exploration) - #190

Merged
zoratu merged 1 commit into
mainfrom
fix/init-disjunctive-conjunct-enumeration
Jul 20, 2026
Merged

fix(tla): enumerate memberships in a disjunctive Init conjunct (Prisoner under-exploration)#190
zoratu merged 1 commit into
mainfrom
fix/init-disjunctive-conjunct-enumeration

Conversation

@zoratu

@zoratu zoratu commented Jul 20, 2026

Copy link
Copy Markdown
Owner

An Init that is a conjunction with a disjunctive conjunct/\ A /\ (\/ B1 \/ B2) /\ C — dropped the disjunct branches' initial states.

evaluate_init_states handled a whole-body top-level disjunction, but not a disjunction nested as one conjunct of a conjunctive Init. The clause path evaluated the \/ conjunct as a boolean guard, and with an unbound state variable that reads FALSE (light \in {"off","on"} with light unbound is ModelValue("light") \in {..} = false), so the branch's membership/equality alternatives were never enumerated.

Prisoner{Solo,}LightUnknown (Prisoners_Single_Switch) lost the \/ Light_Unknown /\ light \in {"off","on"} alternative → 1 initial state instead of 2 → under-exploration:

  • PrisonerSoloLightUnknown: ours 2 → 4 (== TLC)
  • PrisonerLightUnknown: ours 30 → 62 (== TLC)

Fix

After merged_clauses rejoins any split disjunction, if a clause is a top-level disjunction, distribute the Init over it — (A /\ B1 /\ C) \/ (A /\ B2 /\ C) — and recurse each alternative (reusing the existing whole-body-disjunction path).

  • Distributing over merged_clauses (not a raw split_top_level(_, "/\\"), which shreds a disjunct's inner /\ guards like Light_Unknown /\ …) keeps each disjunction whole.
  • A disjunct with a false constant guard (~Light_Unknown when Light_Unknown = TRUE) yields zero states via the existing guard check, so the union is exact.
  • Recursion terminates: each level removes one disjunctive conjunct.

Validation

  • PrisonerSoloLightUnknown 4 == TLC, PrisonerLightUnknown 62 == TLC.
  • scripts/diff_tlc.sh: 20/20 — adds InitDisjunctConjunct (guarded disjunctive Init conjunct, 2 == TLC; also covers the unbounded-union case).
  • cargo test --release: 1486 passed / 0 failed.

…ner under-exploration)

An Init that is a conjunction with a disjunctive conjunct —
`/\ A /\ (\/ B1 \/ B2) /\ C` — dropped the disjunct branches' initial
states. `evaluate_init_states` handled a whole-body top-level disjunction
but not a disjunction nested as ONE conjunct: the clause path evaluated the
`\/` conjunct as a boolean guard, and with an unbound state variable that
reads FALSE (`light \in {"off","on"}` with `light` unbound is
`ModelValue("light") \in {..}` = false), so the branch's membership/equality
alternatives were never enumerated. Prisoner{Solo,}LightUnknown lost the
`\/ Light_Unknown /\ light \in {"off","on"}` alternative, producing 1 initial
state instead of 2 → under-exploration (Solo 2 vs 4, Light 30 vs 62).

Fix: after `merged_clauses` rejoins any split disjunction, if a clause is a
top-level disjunction, DISTRIBUTE the Init over it —
`(A /\ B1 /\ C) \/ (A /\ B2 /\ C)` — and recurse each alternative (reusing
the whole-body-disjunction path). Distributing over `merged_clauses` (not a
raw `split_top_level(_, "/\\")`, which shreds a disjunct's inner `/\` guards)
keeps each disjunction whole. A disjunct with a false constant guard
(`~Light_Unknown` when `Light_Unknown = TRUE`) yields zero states via the
existing guard check, so the union is exact; recursion terminates (each level
removes one disjunctive conjunct).

Result: PrisonerSoloLightUnknown 4 == TLC, PrisonerLightUnknown 62 == TLC.
Gate: diff_tlc 20/20 (adds InitDisjunctConjunct, 2 == TLC), cargo test
--release 1486 passed / 0 failed.
@zoratu
zoratu merged commit 6a1e86c into main Jul 20, 2026
6 checks passed
@zoratu
zoratu deleted the fix/init-disjunctive-conjunct-enumeration branch July 20, 2026 07:04
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