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

Add parameter to advance_proof to split into subproofs after enough branches have been created #638

Open
wants to merge 54 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
eb9be59
Add option to split into subproofs when a branching threshold is reached
nwatson22 Sep 6, 2023
ca4156c
Merge remote-tracking branch 'origin/master' into noah/subproof-split
nwatson22 Sep 6, 2023
c1ce995
Make max_iterations disabled by default
nwatson22 Sep 6, 2023
5d6cfb3
Add test for subproof delegation
nwatson22 Sep 6, 2023
5aee4b1
Merge 5d6cfb3d11cf4b010f3bb640e9137a46cdf48ad9 into 465cca1f37317807b…
nwatson22 Sep 6, 2023
9379310
Set Version: 0.1.434
Sep 6, 2023
0a09655
Merge 9379310c36e0f607550124cada5dc7c478881c46 into 465cca1f37317807b…
nwatson22 Sep 6, 2023
efe6ceb
Set Version: 0.1.435
Sep 6, 2023
43953f4
Fix formatting and typing
nwatson22 Sep 6, 2023
ec88901
Merge branch 'noah/subproof-split' of https://github.com/runtimeverif…
nwatson22 Sep 6, 2023
6166076
Merge master into branch
nwatson22 Sep 6, 2023
dd18600
Add new APRProof-level node status for nodes that have been subproofed
nwatson22 Sep 7, 2023
caac8e9
Merge branch 'master' into noah/subproof-split
nwatson22 Sep 7, 2023
d4a66b4
Merge caac8e91e480d3d57593c61ab64456b1e2485989 into e87862c070793908b…
nwatson22 Sep 7, 2023
5a768d4
Set Version: 0.1.437
Sep 7, 2023
0d12594
Add custom function arg to determine IDs of generated subproofs
nwatson22 Sep 13, 2023
d259f23
Make branch extraction more intelligent
nwatson22 Sep 13, 2023
51d7729
Merge d259f23fe282348402f9fd018d567959460004d0 into d19598b275c2ee741…
nwatson22 Sep 13, 2023
2863769
Set Version: 0.1.440
Sep 13, 2023
2826395
Do not immediately apply simplifications, only count non-bottom nodes
nwatson22 Sep 13, 2023
6de4680
Merge branch 'noah/extract-branches-fix' of https://github.com/runtim…
nwatson22 Sep 13, 2023
07574aa
Use kast_simplify
nwatson22 Sep 13, 2023
f8f3249
Merge master into branch, add proof parameter to generate_subproof_name
nwatson22 Sep 15, 2023
8eec373
Merge branch 'master' into noah/subproof-split
nwatson22 Sep 15, 2023
62fe105
Merge 8eec373e0e226b3c619906eef045af48f9eb8007 into da0e4eae92248661b…
nwatson22 Sep 15, 2023
6abe235
Set Version: 0.1.443
Sep 15, 2023
433df12
Merge master into branch
nwatson22 Sep 15, 2023
39703aa
Merge 433df12918490de5578bdd56205b94116b013e69 into 31ca5378c8cb2109c…
nwatson22 Sep 15, 2023
5bf02dd
Set Version: 0.1.444
Sep 15, 2023
8b267aa
Fix subproof_nodes not being saved/loaded
nwatson22 Sep 16, 2023
bac8047
Merge branch 'noah/subproof-split' of https://github.com/runtimeverif…
nwatson22 Sep 16, 2023
a84c6f0
Merge branch 'master' into noah/subproof-split
nwatson22 Sep 18, 2023
c451595
Merge a84c6f014700f96471704aad746fe517bc6b7444 into eee504ee653696c20…
nwatson22 Sep 18, 2023
9628585
Set Version: 0.1.446
Sep 18, 2023
11072b5
Fix is_pending name conflict
nwatson22 Sep 18, 2023
286b04e
Update src/pyk/kcfg/show.py
nwatson22 Sep 18, 2023
88dfe83
Fix formatting
nwatson22 Sep 18, 2023
e60ba7a
Merge branch 'noah/subproof-split' of https://github.com/runtimeverif…
nwatson22 Sep 18, 2023
0d95e58
fix test
nwatson22 Sep 19, 2023
c5bf0c2
Merge branch 'master' into noah/subproof-split
nwatson22 Sep 20, 2023
c3f0d05
Merge c5bf0c2082f954dc30098b028b280e2103a9deb4 into abed8298b54de758d…
nwatson22 Sep 20, 2023
2a99cfc
Set Version: 0.1.448
Sep 20, 2023
b4d0c8a
Add generate_subproof_name to APRBMCProof
nwatson22 Sep 21, 2023
78bfded
Merge branch 'noah/subproof-split' of https://github.com/runtimeverif…
nwatson22 Sep 21, 2023
b63fe59
Make subproof loading more lazy
nwatson22 Sep 22, 2023
c6e8aea
Fix infinite recursion
nwatson22 Sep 22, 2023
fe2849b
Fix _subproofs map key error
nwatson22 Sep 22, 2023
fa20b9b
Remove guard on create_node
nwatson22 Sep 22, 2023
a315547
readd guard on create_node
nwatson22 Sep 22, 2023
d7e8a78
Merge remote-tracking branch 'origin/master' into noah/subproof-split
nwatson22 Sep 25, 2023
51d3778
Merge d7e8a783927af84b7e551f30f741049d5b249188 into ca97bb1caf81ddbb7…
nwatson22 Sep 25, 2023
694f2a5
Set Version: 0.1.449
Sep 25, 2023
5c188fd
Make add_node private
nwatson22 Sep 25, 2023
a2b386a
Merge branch 'noah/subproof-split' of https://github.com/runtimeverif…
nwatson22 Sep 25, 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.447
0.1.448
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.447"
version = "0.1.448"
description = ""
authors = [
"Runtime Verification, Inc. <[email protected]>",
Expand Down
2 changes: 2 additions & 0 deletions src/pyk/kcfg/kcfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,8 @@ def create_node(self, cterm: CTerm) -> Node:
term = cterm.kast
term = remove_source_attributes(term)
cterm = CTerm.from_kast(term)
while self._node_id in self._nodes:
self._node_id += 1
nwatson22 marked this conversation as resolved.
Show resolved Hide resolved
node = KCFG.Node(self._node_id, cterm)
self._node_id += 1
self._nodes[node.id] = node
Expand Down
4 changes: 4 additions & 0 deletions src/pyk/proof/proof.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,10 @@ def failed(self) -> bool:
def passed(self) -> bool:
return self.status == ProofStatus.PASSED

@property
def is_proof_pending(self) -> bool:
return self.status == ProofStatus.PENDING

@property
def dict(self) -> dict[str, Any]:
return {
Expand Down
84 changes: 82 additions & 2 deletions src/pyk/proof/reachability.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from .proof import CompositeSummary, Proof, ProofStatus

if TYPE_CHECKING:
from collections.abc import Iterable, Mapping
from collections.abc import Callable, Iterable, Mapping
from pathlib import Path
from typing import Any, Final, TypeVar

Expand All @@ -43,10 +43,12 @@ class APRProof(Proof, KCFGExploration):
"""

node_refutations: dict[int, RefutationProof] # TODO _node_refutatations
subproof_nodes: set[int]
init: int
target: int
logs: dict[int, tuple[LogEntry, ...]]
circularity: bool
generate_subproof_name: Callable[[APRProof, int], str] | None
failure_info: APRFailureInfo | None

def __init__(
Expand All @@ -58,10 +60,12 @@ def __init__(
target: NodeIdLike,
logs: dict[int, tuple[LogEntry, ...]],
proof_dir: Path | None = None,
subproof_nodes: Iterable[int] | None = None,
node_refutations: dict[int, str] | None = None,
subproof_ids: Iterable[str] = (),
circularity: bool = False,
admitted: bool = False,
generate_subproof_name: Callable[[APRProof, int], str] | None = None,
):
Proof.__init__(self, id, proof_dir=proof_dir, subproof_ids=subproof_ids, admitted=admitted)
KCFGExploration.__init__(self, kcfg, terminal)
Expand All @@ -73,11 +77,14 @@ def __init__(
self.circularity = circularity
self.node_refutations = {}
self.kcfg.cfg_dir = self.proof_subdir / 'kcfg' if self.proof_subdir else None
self.generate_subproof_name = generate_subproof_name

if self.proof_dir is not None and self.proof_subdir is not None:
ensure_dir_path(self.proof_dir)
ensure_dir_path(self.proof_subdir)

self.subproof_nodes = set(subproof_nodes) if subproof_nodes is not None else set()

if node_refutations is not None:
refutations_not_in_subprroofs = set(node_refutations.values()).difference(
set(subproof_ids if subproof_ids else [])
Expand All @@ -103,11 +110,19 @@ def is_refuted(self, node_id: NodeIdLike) -> bool:
return self.kcfg._resolve(node_id) in self.node_refutations.keys()

def is_pending(self, node_id: NodeIdLike) -> bool:
return self.is_explorable(node_id) and not self.is_target(node_id) and not self.is_refuted(node_id)
return (
self.is_explorable(node_id)
and not self.is_target(node_id)
and not self.is_refuted(node_id)
and not self.is_subproof_node(node_id)
)

def is_init(self, node_id: NodeIdLike) -> bool:
return self.kcfg._resolve(node_id) == self.kcfg._resolve(self.init)

def is_subproof_node(self, node_id: NodeIdLike) -> bool:
return self.kcfg._resolve(node_id) in self.subproof_nodes

def is_target(self, node_id: NodeIdLike) -> bool:
return self.kcfg._resolve(node_id) == self.kcfg._resolve(self.target)

Expand All @@ -117,9 +132,13 @@ 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.is_subproof_node(node_id)
and not self.kcfg.is_vacuous(node_id)
)

def add_subproof_node(self, node_id: int) -> None:
self.subproof_nodes.add(node_id)

def shortest_path_to(self, node_id: NodeIdLike) -> tuple[KCFG.Successor, ...]:
spb = self.kcfg.shortest_path_between(self.init, node_id)
assert spb is not None
Expand Down Expand Up @@ -158,6 +177,7 @@ def from_dict(cls: type[APRProof], dct: Mapping[str, Any], proof_dir: Path | Non
id = dct['id']
circularity = dct.get('circularity', False)
admitted = dct.get('admitted', False)
subproof_nodes = dct['subproof_nodes'] if 'subproof_nodes' in dct else []
subproof_ids = dct['subproof_ids'] if 'subproof_ids' in dct else []
node_refutations: dict[int, str] = {}
if 'node_refutation' in dct:
Expand All @@ -177,6 +197,7 @@ def from_dict(cls: type[APRProof], dct: Mapping[str, Any], proof_dir: Path | Non
circularity=circularity,
admitted=admitted,
proof_dir=proof_dir,
subproof_nodes=subproof_nodes,
subproof_ids=subproof_ids,
node_refutations=node_refutations,
)
Expand Down Expand Up @@ -231,6 +252,7 @@ def dict(self) -> dict[str, Any]:
dct['terminal'] = sorted(self._terminal)
dct['init'] = self.init
dct['target'] = self.target
dct['subproof_nodes'] = sorted(self.subproof_nodes)
dct['node_refutations'] = {node_id: proof.id for (node_id, proof) in self.node_refutations.items()}
dct['circularity'] = self.circularity
logs = {int(k): [l.to_dict() for l in ls] for k, ls in self.logs.items()}
Expand All @@ -253,6 +275,7 @@ def summary(self) -> CompositeSummary:
len(self.kcfg.stuck),
len(self._terminal),
len(self.node_refutations),
len(self.subproof_nodes),
len(self.subproof_ids),
),
*subproofs_summaries,
Expand All @@ -271,6 +294,7 @@ def read_proof_data(proof_dir: Path, id: str) -> APRProof:
kcfg = KCFG.read_cfg_data(cfg_dir, id)
init = int(proof_dict['init'])
target = int(proof_dict['target'])
subproof_nodes = proof_dict['subproof_nodes']
circularity = bool(proof_dict['circularity'])
admitted = bool(proof_dict['admitted'])
terminal = proof_dict['terminal']
Expand All @@ -284,6 +308,7 @@ def read_proof_data(proof_dir: Path, id: str) -> APRProof:
terminal=terminal,
init=init,
target=target,
subproof_nodes=subproof_nodes,
logs=logs,
circularity=circularity,
admitted=admitted,
Expand All @@ -307,6 +332,7 @@ def write_proof_data(self) -> None:
dct['type'] = 'APRProof'
dct['init'] = self.kcfg._resolve(self.init)
dct['target'] = self.kcfg._resolve(self.target)
dct['subproof_nodes'] = sorted(self.subproof_nodes)
dct['terminal'] = sorted(self._terminal)
dct['node_refutations'] = {
self.kcfg._resolve(node_id): proof.id for (node_id, proof) in self.node_refutations.items()
Expand Down Expand Up @@ -337,10 +363,12 @@ def __init__(
bmc_depth: int,
bounded: Iterable[int] | None = None,
proof_dir: Path | None = None,
subproof_nodes: Iterable[int] | None = None,
subproof_ids: Iterable[str] = (),
node_refutations: dict[int, str] | None = None,
circularity: bool = False,
admitted: bool = False,
generate_subproof_name: Callable[[APRProof, int], str] | None = None,
):
super().__init__(
id,
Expand All @@ -350,10 +378,12 @@ def __init__(
target,
logs,
proof_dir=proof_dir,
subproof_nodes=subproof_nodes,
subproof_ids=subproof_ids,
node_refutations=node_refutations,
circularity=circularity,
admitted=admitted,
generate_subproof_name=generate_subproof_name,
)
self.bmc_depth = bmc_depth
self._bounded = set(bounded) if bounded is not None else set()
Expand All @@ -367,6 +397,7 @@ def read_proof_data(proof_dir: Path, id: str) -> APRBMCProof:
kcfg = KCFG.read_cfg_data(cfg_dir, id)
init = int(proof_dict['init'])
target = int(proof_dict['target'])
subproof_nodes = proof_dict['subproof_nodes']
circularity = bool(proof_dict['circularity'])
terminal = proof_dict['terminal']
admitted = bool(proof_dict['admitted'])
Expand All @@ -382,6 +413,7 @@ def read_proof_data(proof_dir: Path, id: str) -> APRBMCProof:
terminal=terminal,
init=init,
target=target,
subproof_nodes=subproof_nodes,
logs=logs,
circularity=circularity,
admitted=admitted,
Expand All @@ -407,6 +439,7 @@ def write_proof_data(self) -> None:
dct['type'] = 'APRBMCProof'
dct['init'] = self.kcfg._resolve(self.init)
dct['target'] = self.kcfg._resolve(self.target)
dct['subproof_nodes'] = sorted(self.subproof_nodes)
dct['node_refutations'] = {
self.kcfg._resolve(node_id): proof.id for (node_id, proof) in self.node_refutations.items()
}
Expand Down Expand Up @@ -522,6 +555,7 @@ def summary(self) -> CompositeSummary:
len(self.kcfg.stuck),
len(self._terminal),
len(self.node_refutations),
len(self.subproof_nodes),
len(self._bounded),
len(self.subproof_ids),
),
Expand Down Expand Up @@ -635,13 +669,23 @@ def advance_proof(
cut_point_rules: Iterable[str] = (),
terminal_rules: Iterable[str] = (),
fail_fast: bool = False,
max_branches: int | None = None,
) -> None:
iterations = 0

self._check_all_terminals()

while self.proof.pending:
self.proof.write_proof_data()

if max_branches is not None and len(self.proof.pending) > max_branches:
_LOGGER.info(
f'Reached max_branches={max_branches} bound on number of pending nodes. Nodes {self.proof.pending} will be turned into subproofs.'
)
for pending_node in self.proof.pending:
self.delegate_to_subproof(pending_node)
break
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can delegate_to_subproof result in new pending nodes for self.proof?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It shouldn't create any new nodes in self.proof, no.


if fail_fast and self.proof.failed:
_LOGGER.warning(
f'Terminating proof early because fail_fast is set {self.proof.id}, failing nodes: {[nd.id for nd in self.proof.failing]}'
Expand Down Expand Up @@ -672,6 +716,38 @@ def advance_proof(

self.proof.write_proof_data()

def delegate_to_subproof(self, node: KCFG.Node) -> None:
_LOGGER.info(f'Delegating node {node.id} to a new proof.')
subproof = self.construct_node_subproof(node)
self.proof.add_subproof(subproof)
self.proof.add_subproof_node(node.id)

def construct_node_subproof(self, node: KCFG.Node) -> APRProof:
kcfg = KCFG(self.proof.proof_dir)
target_node = self.proof.kcfg.node(self.proof.target)

node_cterm, _ = self.kcfg_explore.cterm_simplify(node.cterm)
(
target_node_cterm,
_,
) = self.kcfg_explore.cterm_simplify(target_node.cterm)

new_init = kcfg.create_node(node_cterm)
new_target = kcfg.create_node(target_node_cterm)

assert self.proof.generate_subproof_name is not None

return APRProof(
id=self.proof.generate_subproof_name(self.proof, node.id),
kcfg=kcfg,
terminal=[],
init=new_init.id,
target=new_target.id,
logs={},
proof_dir=self.proof.proof_dir,
generate_subproof_name=self.proof.generate_subproof_name,
)

def refute_node(self, node: KCFG.Node) -> RefutationProof | None:
_LOGGER.info(f'Attempting to refute node {node.id}')
refutation = self.construct_node_refutation(node)
Expand Down Expand Up @@ -757,6 +833,7 @@ class APRSummary(ProofSummary):
stuck: int
terminal: int
refuted: int
subproof_nodes: int
subproofs: int

@property
Expand All @@ -772,6 +849,7 @@ def lines(self) -> list[str]:
f' stuck: {self.stuck}',
f' terminal: {self.terminal}',
f' refuted: {self.refuted}',
f' subproof_nodes: {self.subproof_nodes}',
f'Subproofs: {self.subproofs}',
]

Expand Down Expand Up @@ -935,6 +1013,7 @@ class APRBMCSummary(ProofSummary):
stuck: int
terminal: int
refuted: int
subproof_nodes: int
bounded: int
subproofs: int

Expand All @@ -950,6 +1029,7 @@ def lines(self) -> list[str]:
f' stuck: {self.stuck}',
f' terminal: {self.terminal}',
f' refuted: {self.refuted}',
f' subproof_nodes: {self.subproof_nodes}',
f' bounded: {self.bounded}',
f'Subproofs: {self.subproofs}',
]
2 changes: 2 additions & 0 deletions src/pyk/proof/show.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ def node_attrs(self, kcfg: KCFG, node: KCFG.Node) -> list[str]:
attrs.append('target')
if self.proof.is_pending(node.id):
attrs.append('pending')
if self.proof.is_subproof_node(node.id):
attrs.append('subproof')
if self.proof.is_terminal(node.id):
attrs.append('terminal')
if 'stuck' in attrs:
Expand Down
35 changes: 35 additions & 0 deletions src/tests/integration/kcfg/test_imp.py
Original file line number Diff line number Diff line change
Expand Up @@ -1242,6 +1242,41 @@ def test_anti_unify_forget_values(

assert anti_unifier.kast == expected_anti_unifier.kast

def test_delegate_to_subproof(
self,
kcfg_explore: KCFGExplore,
proof_dir: Path,
kprove: KProve,
) -> None:
claim = single(
kprove.get_claims(
K_FILES / 'imp-simple-spec.k',
spec_module_name='IMP-SIMPLE-SPEC',
claim_labels=['IMP-SIMPLE-SPEC.failing-if'],
)
)

proof = APRProof.from_claim(
kprove.definition,
claim,
logs={},
proof_dir=proof_dir,
generate_subproof_name=(lambda proof, node_id: str(node_id)),
)

prover = APRProver(
proof,
kcfg_explore=kcfg_explore,
)

assert len(list(proof.subproofs)) == 0
assert len(proof.pending) == 1

prover.advance_proof(max_branches=1)

assert len(list(proof.subproofs)) == 2
assert len(proof.pending) == 0

def test_anti_unify_keep_values(
self,
kcfg_explore: KCFGExplore,
Expand Down
Loading