Skip to content
This repository was archived by the owner on Apr 25, 2024. It is now read-only.

Add support for vacuous nodes #587

Merged
merged 36 commits into from
Sep 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
2617d43
VacuousResult
anvacaru Aug 8, 2023
4d51252
add set for vacuous nodes in the kcfg
anvacaru Aug 8, 2023
06219b2
support vacuous in ARPBMCProof and update Proof summaries
anvacaru Aug 8, 2023
1edf843
Merge 06219b2f3e60c3d260905a08a16fe66d880658b2 into 376b83f6499e1b38b…
anvacaru Aug 8, 2023
3eb585d
Set Version: 0.1.405
rv-auditor Aug 8, 2023
d061436
format
anvacaru Aug 8, 2023
e02e170
add code review suggestions
anvacaru Aug 9, 2023
61db89f
Merge branch 'master' into anvacaru/vacuous-2
anvacaru Aug 9, 2023
405e72c
Merge 61db89f2e5f46ecaf226b8cf3c8340c88cd6300d into 42763aec420089647…
anvacaru Aug 9, 2023
7d68bdc
Set Version: 0.1.407
rv-auditor Aug 9, 2023
c36bc9d
Merge branch 'master' into anvacaru/vacuous-2
anvacaru Aug 10, 2023
a3b3f3d
Merge c36bc9d705fb6c01cbe24be2280652ae6a9d1d67 into 7232884d501bb8d7d…
anvacaru Aug 10, 2023
497316b
Set Version: 0.1.409
rv-auditor Aug 10, 2023
a0cec40
Merge remote-tracking branch 'origin/master' into anvacaru/vacuous-2
anvacaru Aug 10, 2023
ebcbc3c
Merge a0cec4073b7ea496eb0017c3d05795aca7dee208 into 97a9a21bb10f163e0…
anvacaru Aug 10, 2023
7ad3fad
Set Version: 0.1.412
rv-auditor Aug 10, 2023
c0c16dd
set EXECUTE_BOOSTER_TEST_DATA
anvacaru Aug 11, 2023
4b95415
Merge branch 'master' into anvacaru/vacuous-2
anvacaru Aug 16, 2023
b99c1ab
Merge 4b95415aecc1dfff54d20459081b6102cf169fa5 into 3dbb978c37d9e006e…
anvacaru Aug 16, 2023
8461621
Set Version: 0.1.416
rv-auditor Aug 16, 2023
773adb3
Merge remote-tracking branch 'origin/master' into anvacaru/vacuous-2
anvacaru Aug 31, 2023
f769485
Merge 773adb31ab04575d44f4318fe5474309e577aaee into a18d76df9419194ac…
anvacaru Sep 1, 2023
ab6abb7
Set Version: 0.1.432
rv-auditor Sep 1, 2023
41ba653
update is_explorable and is_failing
anvacaru Sep 1, 2023
adcf3bd
Revert "set EXECUTE_BOOSTER_TEST_DATA"
anvacaru Sep 1, 2023
5885e4b
adding vacuous check in check_terminal
Sep 7, 2023
2b4ca7c
Merge branch 'master' into anvacaru/vacuous-2
PetarMax Sep 8, 2023
cc6d05d
Merge 2b4ca7c9eb155d4866d712d775d500ed99f75498 into e87862c070793908b…
anvacaru Sep 8, 2023
be3f7e9
Set Version: 0.1.437
rv-auditor Sep 8, 2023
208de95
reverting vacuous check
Sep 8, 2023
088bf96
filtering bottom states out of returned cterms
Sep 8, 2023
03e2164
pruning bottom states in pyk
Sep 8, 2023
f276342
Merge branch 'master' into anvacaru/vacuous-2
anvacaru Sep 8, 2023
1dc71b8
Merge f276342f5c0c0ee45d1795bca62b6917c892bb95 into 390af0a1978c4edb9…
anvacaru Sep 8, 2023
4417c25
Set Version: 0.1.438
rv-auditor Sep 8, 2023
89f6251
update kore-rpc-test
anvacaru Sep 8, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package/version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.1.437
0.1.438
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"

[tool.poetry]
name = "pyk"
version = "0.1.437"
version = "0.1.438"
description = ""
authors = [
"Runtime Verification, Inc. <[email protected]>",
Expand Down
7 changes: 6 additions & 1 deletion src/pyk/kcfg/exploration.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,12 @@ def is_terminal(self, node_id: NodeIdLike) -> bool:

# Explorable node recogniser
def is_explorable(self, node_id: NodeIdLike) -> bool:
return self.kcfg.is_leaf(node_id) and not self.is_terminal(node_id) and not self.kcfg.is_stuck(node_id)
return (
self.kcfg.is_leaf(node_id)
and not self.is_terminal(node_id)
and not self.kcfg.is_stuck(node_id)
and not self.kcfg.is_vacuous(node_id)
)

#
# Collectors
Expand Down
23 changes: 15 additions & 8 deletions src/pyk/kcfg/explore.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def cterm_execute(
cut_point_rules: Iterable[str] | None = None,
terminal_rules: Iterable[str] | None = None,
module_name: str | None = None,
) -> tuple[int, CTerm, list[CTerm], tuple[LogEntry, ...]]:
) -> tuple[bool, int, CTerm, list[CTerm], tuple[LogEntry, ...]]:
_LOGGER.debug(f'Executing: {cterm}')
kore = self.kprint.kast_to_kore(cterm.kast, GENERATED_TOP_CELL)
er = self._kore_client.execute(
Expand All @@ -89,19 +89,20 @@ def cterm_execute(
log_successful_simplifications=self._trace_rewrites,
log_failed_simplifications=self._trace_rewrites,
)
_is_vacuous = er.reason is StopReason.VACUOUS
depth = er.depth
next_state = CTerm.from_kast(self.kprint.kore_to_kast(er.state.kore))
_next_states = er.next_states if er.next_states is not None else []
next_states = [CTerm.from_kast(self.kprint.kore_to_kast(ns.kore)) for ns in _next_states]
next_states = [cterm for cterm in next_states if not cterm.is_bottom]
if len(next_states) == 1 and len(next_states) < len(_next_states):
return depth + 1, next_states[0], [], er.logs
return _is_vacuous, depth + 1, next_states[0], [], er.logs
elif len(next_states) == 1:
if er.reason == StopReason.CUT_POINT_RULE:
return depth, next_state, next_states, er.logs
return _is_vacuous, depth, next_state, next_states, er.logs
else:
next_states = []
return depth, next_state, next_states, er.logs
return _is_vacuous, depth, next_state, next_states, er.logs

def cterm_simplify(self, cterm: CTerm) -> tuple[CTerm, tuple[LogEntry, ...]]:
_LOGGER.debug(f'Simplifying: {cterm}')
Expand Down Expand Up @@ -294,7 +295,7 @@ def step(
if len(successors) != 0 and type(successors[0]) is KCFG.Split:
raise ValueError(f'Cannot take step from split node {self.id}: {shorten_hashes(node.id)}')
_LOGGER.info(f'Taking {depth} steps from node {self.id}: {shorten_hashes(node.id)}')
actual_depth, cterm, next_cterms, next_node_logs = self.cterm_execute(
_, actual_depth, cterm, next_cterms, next_node_logs = self.cterm_execute(
node.cterm, depth=depth, module_name=module_name
)
if actual_depth != depth:
Expand Down Expand Up @@ -368,6 +369,8 @@ def extend(
raise ValueError(f'Cannot extend non-leaf node {self.id}: {node.id}')
if kcfg.is_stuck(node.id):
raise ValueError(f'Cannot extend stuck node {self.id}: {node.id}')
if kcfg.is_vacuous(node.id):
raise ValueError(f'Cannot extend vacuous node {self.id}: {node.id}')
if kcfg_exploration.is_terminal(node.id):
raise ValueError(f'Cannot extend terminal node {self.id}: {node.id}')

Expand All @@ -384,7 +387,7 @@ def extend(
return

_LOGGER.info(f'Extending KCFG from node {self.id}: {shorten_hashes(node.id)}')
depth, cterm, next_cterms, next_node_logs = self.cterm_execute(
_is_vacuous, depth, cterm, next_cterms, next_node_logs = self.cterm_execute(
node.cterm,
depth=execute_depth,
cut_point_rules=cut_point_rules,
Expand All @@ -403,8 +406,12 @@ def extend(

# Stuck
elif len(next_cterms) == 0:
kcfg.add_stuck(node.id)
_LOGGER.info(f'Found stuck node {self.id}: {shorten_hashes(node.id)}')
if _is_vacuous:
kcfg.add_vacuous(node.id)
_LOGGER.warning(f'Found vacuous node {self.id}: {shorten_hashes(node.id)}')
else:
kcfg.add_stuck(node.id)
_LOGGER.info(f'Found stuck node {self.id}: {shorten_hashes(node.id)}')

# Cut Rule
elif len(next_cterms) == 1:
Expand Down
34 changes: 34 additions & 0 deletions src/pyk/kcfg/kcfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ def edges(self) -> tuple[KCFG.Edge, ...]:
_covers: dict[int, dict[int, Cover]]
_splits: dict[int, Split]
_ndbranches: dict[int, NDBranch]
_vacuous: set[int]
_stuck: set[int]
_aliases: dict[str, int]
_lock: RLock
Expand All @@ -219,6 +220,7 @@ def __init__(self, cfg_dir: Path | None = None) -> None:
self._covers = {}
self._splits = {}
self._ndbranches = {}
self._vacuous = set()
self._stuck = set()
self._aliases = {}
self._lock = RLock()
Expand Down Expand Up @@ -262,6 +264,10 @@ def nodes(self) -> list[Node]:
def root(self) -> list[Node]:
return [node for node in self.nodes if self.is_root(node.id)]

@property
def vacuous(self) -> list[Node]:
return [node for node in self.nodes if node.id in self._vacuous]

@property
def stuck(self) -> list[Node]:
return [node for node in self.nodes if node.id in self._stuck]
Expand Down Expand Up @@ -315,6 +321,7 @@ def to_dict(self) -> dict[str, Any]:
splits = [split.to_dict() for split in self.splits()]
ndbranches = [ndbranch.to_dict() for ndbranch in self.ndbranches()]

vacuous = sorted(self._vacuous)
stuck = sorted(self._stuck)
aliases = dict(sorted(self._aliases.items()))

Expand All @@ -325,6 +332,7 @@ def to_dict(self) -> dict[str, Any]:
'covers': covers,
'splits': splits,
'ndbranches': ndbranches,
'vacuous': vacuous,
'stuck': stuck,
'aliases': aliases,
}
Expand Down Expand Up @@ -356,6 +364,9 @@ def from_dict(dct: Mapping[str, Any]) -> KCFG:
csubst = CSubst.from_dict(cover_dict['csubst'])
cfg.create_cover(source_id, target_id, csubst=csubst)

for vacuous_id in dct.get('vacuous') or []:
cfg.add_vacuous(vacuous_id)

for stuck_id in dct.get('stuck') or []:
cfg.add_stuck(stuck_id)

Expand Down Expand Up @@ -470,6 +481,7 @@ def remove_node(self, node_id: NodeIdLike) -> None:
if not self._covers[source_id]:
self._covers.pop(source_id)

self._vacuous.discard(node_id)
self._stuck.discard(node_id)

self._splits = {k: s for k, s in self._splits.items() if k != node_id and node_id not in s.target_ids}
Expand Down Expand Up @@ -636,6 +648,19 @@ def edge_likes(self, *, source_id: NodeIdLike | None = None, target_id: NodeIdLi
'List[KCFG.EdgeLike]', self.covers(source_id=source_id, target_id=target_id)
)

def add_vacuous(self, node_id: NodeIdLike) -> None:
self._vacuous.add(self._resolve(node_id))

def remove_vacuous(self, node_id: NodeIdLike) -> None:
node_id = self._resolve(node_id)
if node_id not in self._vacuous:
raise ValueError(f'Node is not vacuous: {node_id}')
self._vacuous.remove(node_id)

def discard_vacuous(self, node_id: NodeIdLike) -> None:
node_id = self._resolve(node_id)
self._vacuous.discard(node_id)

def add_stuck(self, node_id: NodeIdLike) -> None:
self._stuck.add(self._resolve(node_id))

Expand Down Expand Up @@ -723,6 +748,10 @@ def is_root(self, node_id: NodeIdLike) -> bool:
node_id = self._resolve(node_id)
return len(self.predecessors(node_id)) == 0

def is_vacuous(self, node_id: NodeIdLike) -> bool:
node_id = self._resolve(node_id)
return node_id in self._vacuous

def is_stuck(self, node_id: NodeIdLike) -> bool:
node_id = self._resolve(node_id)
return node_id in self._stuck
Expand Down Expand Up @@ -888,6 +917,7 @@ def write_cfg_data(self) -> None:
splits = [split.to_dict() for split in self.splits()]
ndbranches = [ndbranch.to_dict() for ndbranch in self.ndbranches()]

vacuous = sorted(self._vacuous)
stuck = sorted(self._stuck)
aliases = dict(sorted(self._aliases.items()))
dct: dict[str, list[int] | int | dict[str, int] | list[dict[str, Any]]] = {}
Expand All @@ -897,6 +927,7 @@ def write_cfg_data(self) -> None:
dct['covers'] = covers
dct['splits'] = splits
dct['ndbranches'] = ndbranches
dct['vacuous'] = vacuous
dct['stuck'] = stuck
dct['aliases'] = aliases
cfg_json.write_text(json.dumps(dct))
Expand Down Expand Up @@ -964,6 +995,9 @@ def read_cfg_data(cfg_dir: Path, id: str) -> KCFG:
csubst = CSubst.from_dict(cover_dict['csubst'])
cfg.create_cover(source_id, target_id, csubst=csubst)

for vacuous_id in dct.get('vacuous') or []:
cfg.add_vacuous(vacuous_id)

for stuck_id in dct.get('stuck') or []:
cfg.add_stuck(stuck_id)

Expand Down
2 changes: 2 additions & 0 deletions src/pyk/kcfg/show.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ def node_attrs(self, kcfg: KCFG, node: KCFG.Node) -> list[str]:
attrs.append('root')
if kcfg.is_stuck(node.id):
attrs.append('stuck')
if kcfg.is_vacuous(node.id):
attrs.append('vacuous')
if kcfg.is_leaf(node.id):
attrs.append('leaf')
if kcfg.is_split(node.id):
Expand Down
24 changes: 24 additions & 0 deletions src/pyk/kore/rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,7 @@ class StopReason(str, Enum):
BRANCHING = 'branching'
CUT_POINT_RULE = 'cut-point-rule'
TERMINAL_RULE = 'terminal-rule'
VACUOUS = 'vacuous'


@final
Expand Down Expand Up @@ -493,6 +494,7 @@ class ExecuteResult(ABC): # noqa: B024
StopReason.BRANCHING: 'BranchingResult',
StopReason.CUT_POINT_RULE: 'CutPointResult',
StopReason.TERMINAL_RULE: 'TerminalResult',
StopReason.VACUOUS: 'VacuousResult',
}

reason: ClassVar[StopReason]
Expand Down Expand Up @@ -625,6 +627,28 @@ def from_dict(cls: type[TerminalResult], dct: Mapping[str, Any]) -> TerminalResu
return TerminalResult(state=State.from_dict(dct['state']), depth=dct['depth'], rule=dct['rule'], logs=logs)


@final
@dataclass(frozen=True)
class VacuousResult(ExecuteResult):
reason = StopReason.VACUOUS
next_states = None
rule = None

state: State
depth: int
logs: tuple[LogEntry, ...]

@classmethod
def from_dict(cls: type[VacuousResult], dct: Mapping[str, Any]) -> VacuousResult:
cls._check_reason(dct)
logs = tuple(LogEntry.from_dict(l) for l in dct['logs']) if 'logs' in dct else ()
return VacuousResult(
state=State.from_dict(dct['state']),
depth=dct['depth'],
logs=logs,
)


@final
@dataclass(frozen=True)
class ImpliesResult:
Expand Down
7 changes: 7 additions & 0 deletions src/pyk/proof/reachability.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ def is_failing(self, node_id: NodeIdLike) -> bool:
and not self.is_explorable(node_id)
and not self.is_target(node_id)
and not self.is_refuted(node_id)
and not self.kcfg.is_vacuous(node_id)
)

def shortest_path_to(self, node_id: NodeIdLike) -> tuple[KCFG.Successor, ...]:
Expand Down Expand Up @@ -246,6 +247,7 @@ def summary(self) -> CompositeSummary:
len(self.kcfg.nodes),
len(self.pending),
len(self.failing),
len(self.kcfg.vacuous),
len(self.kcfg.stuck),
len(self._terminal),
len(self.node_refutations),
Expand Down Expand Up @@ -514,6 +516,7 @@ def summary(self) -> CompositeSummary:
len(self.kcfg.nodes),
len(self.pending),
len(self.failing),
len(self.kcfg.vacuous),
len(self.kcfg.stuck),
len(self._terminal),
len(self.node_refutations),
Expand Down Expand Up @@ -739,6 +742,7 @@ class APRSummary(ProofSummary):
nodes: int
pending: int
failing: int
vacuous: int
stuck: int
terminal: int
refuted: int
Expand All @@ -753,6 +757,7 @@ def lines(self) -> list[str]:
f' nodes: {self.nodes}',
f' pending: {self.pending}',
f' failing: {self.failing}',
f' vacuous: {self.vacuous}',
f' stuck: {self.stuck}',
f' terminal: {self.terminal}',
f' refuted: {self.refuted}',
Expand Down Expand Up @@ -913,6 +918,7 @@ class APRBMCSummary(ProofSummary):
nodes: int
pending: int
failing: int
vacuous: int
stuck: int
terminal: int
refuted: int
Expand All @@ -927,6 +933,7 @@ def lines(self) -> list[str]:
f' nodes: {self.nodes}',
f' pending: {self.pending}',
f' failing: {self.failing}',
f' vacuous: {self.vacuous}',
f' stuck: {self.stuck}',
f' terminal: {self.terminal}',
f' refuted: {self.refuted}',
Expand Down
3 changes: 3 additions & 0 deletions src/tests/integration/k-files/kore-rpc-test.k
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
module KORE-RPC-TEST-SYNTAX
imports INT-SYNTAX
imports BOOL-SYNTAX
endmodule


module KORE-RPC-TEST
imports KORE-RPC-TEST-SYNTAX
imports INT
imports BOOL

rule [r01]: 0 => 1
rule [r12]: 1 => 2
rule [r23]: 2 => 3
rule [r24]: 2 => 4
rule [r34]: 3 => 4 ensures false
rule [r45]: 4 => 5
rule [r56]: 5 => 6

Expand Down
2 changes: 1 addition & 1 deletion src/tests/integration/kcfg/test_cell_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ def test_execute(
expected_k, _, _ = expected_post

# When
actual_depth, actual_post_term, _, _logs = kcfg_explore.cterm_execute(
_, actual_depth, actual_post_term, _, _logs = kcfg_explore.cterm_execute(
self.config(kcfg_explore.kprint, k, aacounts, accounts), depth=depth
)
actual_k = kcfg_explore.kprint.pretty_print(actual_post_term.cell('K_CELL'))
Expand Down
2 changes: 1 addition & 1 deletion src/tests/integration/kcfg/test_imp.py
Original file line number Diff line number Diff line change
Expand Up @@ -780,7 +780,7 @@ def test_execute(
expected_k, expected_state = expected_post

# When
actual_depth, actual_post_term, actual_next_terms, _logs = kcfg_explore.cterm_execute(
_, actual_depth, actual_post_term, actual_next_terms, _logs = kcfg_explore.cterm_execute(
self.config(kcfg_explore.kprint, k, state), depth=depth
)
actual_k = kcfg_explore.kprint.pretty_print(actual_post_term.cell('K_CELL'))
Expand Down
6 changes: 3 additions & 3 deletions src/tests/integration/kcfg/test_multiple_definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def test_execute(
kcfg_explore: KCFGExplore,
test_id: str,
) -> None:
split_depth, split_post_term, split_next_terms, _logs = kcfg_explore.cterm_execute(self.config(), depth=1)
_, split_depth, split_post_term, split_next_terms, _logs = kcfg_explore.cterm_execute(self.config(), depth=1)

split_k = kcfg_explore.kprint.pretty_print(split_post_term.cell('K_CELL'))
split_next_k = [kcfg_explore.kprint.pretty_print(split_post_term.cell('K_CELL')) for term in split_next_terms]
Expand All @@ -57,13 +57,13 @@ def test_execute(
'a ( X:KItem )',
] == split_next_k

step_1_depth, step_1_post_term, step_1_next_terms, _logs = kcfg_explore.cterm_execute(
_, step_1_depth, step_1_post_term, step_1_next_terms, _logs = kcfg_explore.cterm_execute(
split_next_terms[0], depth=1
)
step_1_k = kcfg_explore.kprint.pretty_print(step_1_post_term.cell('K_CELL'))
assert 'c' == step_1_k

step_2_depth, step_2_post_term, step_2_next_terms, _logs = kcfg_explore.cterm_execute(
_, step_2_depth, step_2_post_term, step_2_next_terms, _logs = kcfg_explore.cterm_execute(
split_next_terms[1], depth=1
)
step_2_k = kcfg_explore.kprint.pretty_print(step_1_post_term.cell('K_CELL'))
Expand Down
Loading