Skip to content
Open
Changes from all commits
Commits
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
52 changes: 41 additions & 11 deletions verification-algorithm.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ immutable class ProofState:
It could be a tuple of all the elements, or it could be implemented as a manual linked list, as
a pair of one element and a pointer to the remainder of the list.
Invariant: all the WorkItems with a non-empty `backtrack_hint` are above all the pairs with None.
This is because every push onto the workstack has a non-None `backtrack_hint`.
This is because every push onto the workstack when backtracking has a non-None `backtrack_hint`.

Comment thread
jyoo980 marked this conversation as resolved.
Possible fields for ProofState:

Expand All @@ -121,6 +121,10 @@ immutable class WorkItem:
* `hint`: str
Each hint is text provided to the LLM to guide it in the backtracking process; its value is
populated by the `hint` field in the `BacktrackToCallee` class.
* `assume_without_verification`: bool (default: False)
When True, the spec generated for this function is assumed without running the verifier.
Set when backtracking to an external callee — its spec is regenerated with the hint but
assumed rather than verified.

immutable class SpecConversation:

Expand Down Expand Up @@ -173,26 +177,52 @@ def step(proofstate: ProofState) -> List[ProofState]:
Let `top_fn` be the function on the top of the input's workstack.
The output ProofState has a smaller workstack if top_fn was successfully verified or if top_fn's
spec will be assumed.
The output ProofState has a larger workstack (representing backtracking) if `top_fn` was not
The output ProofState has a larger workstack (representing backtracking) if `top_fn` was not
verified or assumed.
"""
(fn, backtrack_hint): WorkItem = proofstate.workstack.top();
work_item: WorkItem = proofstate.workstack.top()
fn, backtrack_hint, assume_without_verification = work_item
new_speccs: List[SpecConversation] = generate_and_repair_spec(fn, backtrack_hint)
Comment thread
jyoo980 marked this conversation as resolved.
pruned_speccs = prune_heuristically(fn, candidate_speccs, proofstate)
pruned_speccs = prune_heuristically(fn, new_speccs, proofstate)

Comment thread
jyoo980 marked this conversation as resolved.
# If flagged to assume without verification (e.g., an external callee pushed during
# backtracking), skip the verifier and set next_step directly.
if assume_without_verification:
for specc in pruned_speccs:
specc.next_step = ASSUME_SPEC_AS_IS
else:
# next_step may already be set by generate_and_repair_spec (e.g., for specs that verified
# during repair, or for recursive functions). If not yet set, verify and query the LLM.
for specc in pruned_speccs:
if not specc.next_step:
vresult = call_verifier(fn, specc, proofstate)
if is_success(vresult):
specc.next_step = ACCEPT_VERIFIED_SPEC
else:
specc = call_llm_for_next_step(spec_conversation=specc, proof_state=proofstate)

Comment thread
jyoo980 marked this conversation as resolved.
result = []
for specc in pruned_speccs:
# Produce a new ProofState from `specc.spec` and `specc.conversation`.
# Produce a new ProofState from `specc.spec` and `specc.next_step`.

# Updating the specs is important even if backtracking will occur.
next_proofstate_specs = proofstate.specs | {fn: specc.spec}

last_llm_response = specc.conversation.last()
if last_llm_response contains "backtrack":
next_proofstate_workstack =
proofstate.workstack + [WorkItem(last_llm_response.get_function(), last_llm_response)]
else:
next_proofstate_workstack = proofstate.workstack[:-1] # pop off fn
match specc.next_step:
case ACCEPT_VERIFIED_SPEC | ASSUME_SPEC_AS_IS:
next_proofstate_workstack = proofstate.workstack[:-1] # pop fn
case BACKTRACK_TO_CALLEE(callee_name, hint):
callee = function_graph.get_function_or_none(callee_name)
if not callee or fn.is_external_function:
# Backtracking is not possible (callee missing, or caller is external).
# Assume the failing spec and pop fn.
next_proofstate_workstack = proofstate.workstack[:-1]
else:
# Push callee; if it's external its spec will be assumed rather than verified.
next_proofstate_workstack = proofstate.workstack + [
WorkItem(callee, hint, assume_without_verification=callee.is_external_function)
]

next_proofstate = ProofState(next_proofstate_specs, next_proofstate_workstack)
result.append(next_proofstate)
return result
Expand Down