Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reusing backend information to construct KCFGs more efficiently #4590

Merged
merged 31 commits into from
Aug 19, 2024
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
5630763
different approach
PetarMax Aug 15, 2024
6ff9f37
no changes to .toml
PetarMax Aug 15, 2024
cb9e98c
reverting changes
PetarMax Aug 15, 2024
00d519e
adding predecessor to APRProofStep
PetarMax Aug 15, 2024
f2158a8
correction
PetarMax Aug 15, 2024
733d089
overhaul
PetarMax Aug 15, 2024
33fcde3
Merge branch 'develop' into petar/streamlining-branching
PetarMax Aug 15, 2024
869e7b2
formatting correction
PetarMax Aug 15, 2024
b20e4c6
better logging message
PetarMax Aug 15, 2024
a60b914
further messages
PetarMax Aug 15, 2024
45fd80b
minor further messages
PetarMax Aug 16, 2024
0018b2a
moving logic to APRProof
PetarMax Aug 16, 2024
b0bc50e
corrections
PetarMax Aug 16, 2024
8d4ab1f
documentation and circularity precision
PetarMax Aug 16, 2024
69b3c98
Merge branch 'develop' into petar/streamlining-branching
PetarMax Aug 16, 2024
e865465
second attempt at understanding circularities
PetarMax Aug 16, 2024
12b0fc0
additional precision re circular rules
PetarMax Aug 16, 2024
5ce87e8
correction
PetarMax Aug 16, 2024
68ba0b1
more corrections
PetarMax Aug 16, 2024
3321f48
comments
PetarMax Aug 16, 2024
9c86d7e
formatting
PetarMax Aug 16, 2024
207e325
even more formatting
PetarMax Aug 16, 2024
c19f63d
removing always_check_subsumption, more comments
PetarMax Aug 16, 2024
4d695ad
correction
PetarMax Aug 16, 2024
d3d3cae
Final correction
PetarMax Aug 17, 2024
4d54908
Update pyk/src/pyk/kcfg/explore.py
PetarMax Aug 17, 2024
a661700
Update pyk/src/pyk/kcfg/explore.py
PetarMax Aug 17, 2024
852fba4
minor corrections
PetarMax Aug 19, 2024
76e9f1e
refactoring proof results
PetarMax Aug 19, 2024
c3e6e8d
Update pyk/src/pyk/proof/reachability.py
PetarMax Aug 19, 2024
e60d2be
Update semantics.py
PetarMax Aug 19, 2024
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
59 changes: 24 additions & 35 deletions pyk/src/pyk/kcfg/explore.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,19 +218,15 @@ def extend_cterm(
cut_point_rules: Iterable[str] = (),
terminal_rules: Iterable[str] = (),
module_name: str | None = None,
) -> KCFGExtendResult:
def log(message: str, *, warning: bool = False) -> None:
_LOGGER.log(logging.WARNING if warning else logging.INFO, f'Extend result for {self.id}: {message}')
) -> list[KCFGExtendResult]:
PetarMax marked this conversation as resolved.
Show resolved Hide resolved

custom_step_result = self.kcfg_semantics.custom_step(_cterm)
if custom_step_result is not None:
log(f'custom step node: {node_id}')
return custom_step_result
return [custom_step_result]

abstract_cterm = self.kcfg_semantics.abstract_node(_cterm)
if _cterm != abstract_cterm:
log(f'abstraction node: {node_id}')
return Abstract(abstract_cterm)
return [Abstract(abstract_cterm)]

cterm, next_states, depth, vacuous, next_node_logs = self.cterm_symbolic.execute(
_cterm,
Expand All @@ -240,33 +236,27 @@ def log(message: str, *, warning: bool = False) -> None:
module_name=module_name,
)

extend_results: list[KCFGExtendResult] = []

# Basic block
if depth > 0:
log(f'basic block at depth {depth}: {node_id}')
return Step(cterm, depth, next_node_logs, self._extract_rule_labels(next_node_logs))
extend_results.append(Step(cterm, depth, next_node_logs, self._extract_rule_labels(next_node_logs)))

# Stuck or vacuous
if not next_states:
if vacuous:
log(f'vacuous node: {node_id}', warning=True)
return Vacuous()
log(f'stuck node: {node_id}')
return Stuck()

extend_results.append(Vacuous())
elif depth == 0:
extend_results.append(Stuck())
# Cut rule
if len(next_states) == 1:
log(f'cut-rule basic block at depth {depth}: {node_id}')
return Step(
next_states[0].state,
1,
next_node_logs,
self._extract_rule_labels(next_node_logs),
cut=True,
)

elif len(next_states) == 1:
if not self.kcfg_semantics.can_make_custom_step(cterm):
(next_node_logs, rules) = (
((), []) if extend_results else (next_node_logs, self._extract_rule_labels(next_node_logs))
)
extend_results.append(Step(next_states[0].state, 1, next_node_logs, rules, cut=True, info='cut-rule'))
# Branch
assert len(next_states) > 1
if all(branch_constraint for _, branch_constraint in next_states):
elif all(branch_constraint for _, branch_constraint in next_states):
branch_preds = [flatten_label('#And', not_none(rule_predicate)) for _, rule_predicate in next_states]
common_preds = list(
unique(
Expand All @@ -277,15 +267,14 @@ def log(message: str, *, warning: bool = False) -> None:
)
)
branches = [mlAnd(pred for pred in branch_pred if pred not in common_preds) for branch_pred in branch_preds]
if common_preds:
log(
f'Common predicates found in branches: {[self.pretty_print(ml_pred_to_bool(cp)) for cp in common_preds]}'
)
constraint_strs = [self.pretty_print(ml_pred_to_bool(bc)) for bc in branches]
log(f'{len(branches)} branches: {node_id} -> {constraint_strs}')
return Branch(branches)
info = f'{[self.pretty_print(ml_pred_to_bool(bc)) for bc in branches]}'
extend_results.append(Branch(branches, info=info))
else:
# NDBranch
log(f'{len(next_states)} non-deterministic branches: {node_id}')
next_cterms = [cterm for cterm, _ in next_states]
return NDBranch(next_cterms, next_node_logs, self._extract_rule_labels(next_node_logs))
(next_node_logs, rules) = (
((), []) if extend_results else (next_node_logs, self._extract_rule_labels(next_node_logs))
)
extend_results.append(NDBranch(next_cterms, next_node_logs, rules))

return extend_results
24 changes: 21 additions & 3 deletions pyk/src/pyk/kcfg/kcfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -509,30 +509,45 @@ def extend(
node: KCFG.Node,
logs: dict[int, tuple[LogEntry, ...]],
) -> None:

def log(message: str, *, warning: bool = False) -> None:
result_info = extend_result.info if type(extend_result) is Step or type(extend_result) is Branch else ''
result_info_message = f': {result_info}' if result_info else ''
_LOGGER.log(
logging.WARNING if warning else logging.INFO,
f'Extending current KCFG with the following: {message}{result_info_message}',
)

match extend_result:
case Vacuous():
self.add_vacuous(node.id)
log(f'vacuous node: {node.id}', warning=True)

case Stuck():
self.add_stuck(node.id)
log(f'stuck node: {node.id}')

case Abstract(cterm):
new_node = self.create_node(cterm)
self.create_cover(node.id, new_node.id)
log(f'abstraction node: {node.id}')

case Step(cterm, depth, next_node_logs, rule_labels, _):
next_node = self.create_node(cterm)
logs[next_node.id] = next_node_logs
self.create_edge(node.id, next_node.id, depth, rules=rule_labels)
log(f'basic block at depth {depth}: {node.id} --> {next_node.id}')

case Branch(constraints, _):
self.split_on_constraints(node.id, constraints)
case Branch(branches, _):
branch_node_ids = self.split_on_constraints(node.id, branches)
log(f'{len(branches)} branches: {node.id} --> {branch_node_ids}')

case NDBranch(cterms, next_node_logs, rule_labels):
next_ids = [self.create_node(cterm).id for cterm in cterms]
for i in next_ids:
logs[i] = next_node_logs
self.create_ndbranch(node.id, next_ids, rules=rule_labels)
log(f'{len(cterms)} non-deterministic branches: {node.id} --> {next_ids}')

case _:
raise AssertionError()
Expand Down Expand Up @@ -1333,17 +1348,20 @@ class Step(KCFGExtendResult):
logs: tuple[LogEntry, ...]
rule_labels: list[str]
cut: bool = field(default=False)
info: str = field(default='')


@final
@dataclass(frozen=True)
class Branch(KCFGExtendResult):
constraints: tuple[KInner, ...]
heuristic: bool
info: str = field(default='')

def __init__(self, constraints: Iterable[KInner], *, heuristic: bool = False):
def __init__(self, constraints: Iterable[KInner], *, heuristic: bool = False, info: str = ''):
object.__setattr__(self, 'constraints', tuple(constraints))
object.__setattr__(self, 'heuristic', heuristic)
object.__setattr__(self, 'info', info)


@final
Expand Down
16 changes: 16 additions & 0 deletions pyk/src/pyk/kcfg/semantics.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,28 @@ class KCFGSemantics(ABC):
@abstractmethod
def is_terminal(self, c: CTerm) -> bool: ...

"""Checks whether or not a given CTerm is terminal."""
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
"""Checks whether or not a given CTerm is terminal."""
"""Check whether or not a given ``CTerm`` is terminal."""


@abstractmethod
def abstract_node(self, c: CTerm) -> CTerm: ...

"""Implements an abstraction mechanism."""

@abstractmethod
def same_loop(self, c1: CTerm, c2: CTerm) -> bool: ...

"""Checks whether or not the two given CTerms represent the same loop head."""

@abstractmethod
def can_make_custom_step(self, c: CTerm) -> bool: ...
tothtamas28 marked this conversation as resolved.
Show resolved Hide resolved

"""Checks whether or not the semantics can make a custom step from a given CTerm."""

@abstractmethod
def custom_step(self, c: CTerm) -> KCFGExtendResult | None: ...

"""Implements a custom semantic step."""


class DefaultSemantics(KCFGSemantics):
def is_terminal(self, c: CTerm) -> bool:
Expand All @@ -32,5 +45,8 @@ def abstract_node(self, c: CTerm) -> CTerm:
def same_loop(self, c1: CTerm, c2: CTerm) -> bool:
return False

def can_make_custom_step(self, c: CTerm) -> bool:
return False

def custom_step(self, c: CTerm) -> KCFGExtendResult | None:
return None
Loading
Loading