fix(inspect): choose selectors that share one condition together
This commit is contained in:
@@ -1481,13 +1481,48 @@ class TestConditioningSemantics(unittest.TestCase):
|
||||
[(parity.POS, parity.POS), (WOLF, WOLF)],
|
||||
)
|
||||
# Independent selectors on each role multiply.
|
||||
source["7"] = _if_else(["12", 1], ["10", 0], ["14", 0])
|
||||
source["7"] = _if_else(["16", 1], ["10", 0], ["14", 0])
|
||||
source["14"] = _encoded(parity.NEG)
|
||||
source["16"] = _image_exists()
|
||||
source["3"]["inputs"]["negative"] = ["7", 0]
|
||||
candidates = _inspect(source)["candidates"]
|
||||
self.assertEqual(len(candidates), 4)
|
||||
self.assertEqual(len({_sides(item) for item in candidates}), 4)
|
||||
|
||||
def test_selectors_sharing_one_condition_choose_together(self):
|
||||
extra = {
|
||||
"10": _encoded("pos A"),
|
||||
"11": _encoded("pos B"),
|
||||
"14": _encoded("neg A"),
|
||||
"15": _encoded("neg B"),
|
||||
"12": _image_exists(),
|
||||
"16": _image_exists(),
|
||||
}
|
||||
source = _with_positive(_if_else(["12", 1], ["10", 0], ["11", 0]), extra)
|
||||
source["7"] = _if_else(["12", 1], ["14", 0], ["15", 0])
|
||||
result = _inspect(source)
|
||||
self.assertEqual(result["state"], "ambiguous")
|
||||
# One runtime value drives both selectors: never "pos A" with "neg B".
|
||||
self.assertEqual(
|
||||
[_sides(item) for item in result["candidates"]],
|
||||
[("pos A", "neg A"), ("pos B", "neg B")],
|
||||
)
|
||||
# Another output slot of the same node is another value.
|
||||
source["7"]["inputs"]["boolean"] = ["12", 0]
|
||||
self.assertEqual(len(_inspect(source)["candidates"]), 4)
|
||||
# Two independent conditions give the full cross product.
|
||||
source["7"]["inputs"]["boolean"] = ["16", 1]
|
||||
candidates = _inspect(source)["candidates"]
|
||||
self.assertEqual(
|
||||
sorted(_sides(item) for item in candidates),
|
||||
[
|
||||
("pos A", "neg A"),
|
||||
("pos A", "neg B"),
|
||||
("pos B", "neg A"),
|
||||
("pos B", "neg B"),
|
||||
],
|
||||
)
|
||||
|
||||
def test_diverging_branch_that_cannot_be_traced_is_never_guessed(self):
|
||||
extra = {
|
||||
"10": _encoded(parity.POS),
|
||||
@@ -1505,7 +1540,11 @@ class TestConditioningSemantics(unittest.TestCase):
|
||||
for index in range(6):
|
||||
extra[f"a{index}"] = _encoded(f"left {index}")
|
||||
extra[f"b{index}"] = _encoded(f"right {index}")
|
||||
extra[f"s{index}"] = _if_else(["12", 1], [f"a{index}", 0], [f"b{index}", 0])
|
||||
# Each selector reads its own condition: independent choices.
|
||||
extra[f"e{index}"] = _image_exists()
|
||||
extra[f"s{index}"] = _if_else(
|
||||
[f"e{index}", 1], [f"a{index}", 0], [f"b{index}", 0]
|
||||
)
|
||||
extra[f"c{index}"] = {
|
||||
"class_type": "ConditioningCombine",
|
||||
"inputs": {
|
||||
|
||||
@@ -171,9 +171,11 @@ class _Diverging(Exception):
|
||||
again once per choice, so each candidate is internally consistent.
|
||||
"""
|
||||
|
||||
def __init__(self, node_id: str) -> None:
|
||||
super().__init__(node_id)
|
||||
self.node_id = node_id
|
||||
def __init__(self, key: Tuple[str, int]) -> None:
|
||||
super().__init__(key)
|
||||
# The condition output (node ID, slot): one runtime value, so every
|
||||
# selector it drives is forced together.
|
||||
self.key = key
|
||||
|
||||
|
||||
def _node_ref(node_id: str, class_type: Any) -> dict:
|
||||
@@ -599,8 +601,8 @@ class _GraphResolver:
|
||||
choice: Optional[bool] = condition
|
||||
elif not _link(condition):
|
||||
raise _Unresolved("Malformed selector condition", "malformed")
|
||||
elif node_id in self.choices:
|
||||
choice = self.choices[node_id]
|
||||
elif (str(condition[0]), condition[1]) in self.choices:
|
||||
choice = self.choices[(str(condition[0]), condition[1])]
|
||||
self.tainted = True
|
||||
else:
|
||||
choice = None
|
||||
@@ -614,7 +616,7 @@ class _GraphResolver:
|
||||
for label in ("true", "false")
|
||||
]
|
||||
if parts[0][0] != parts[1][0]:
|
||||
raise _Diverging(node_id)
|
||||
raise _Diverging((str(condition[0]), condition[1]))
|
||||
return self.merged(node_id, 0, parts)
|
||||
|
||||
def merged(self, node_id: str, slot: int, parts: list) -> Tuple[str, list, str]:
|
||||
@@ -939,7 +941,7 @@ class _GraphResolver:
|
||||
if len(results) + len(pending) + 2 > _MAX_BRANCH_CANDIDATES:
|
||||
raise _Unresolved("Too many selector branches", "work-limit")
|
||||
pending.extend(
|
||||
{**self.choices, diverging.node_id: choice}
|
||||
{**self.choices, diverging.key: choice}
|
||||
for choice in (True, False)
|
||||
)
|
||||
except _Unresolved as error:
|
||||
|
||||
Reference in New Issue
Block a user