verantyx-logic-math / patch_proof.py
kofdai's picture
Initial upload of Verantyx Logic Engine (v1.0)
29b87da verified
from __future__ import annotations
from dataclasses import dataclass
from typing import Any, Dict, List, Optional, Set, Tuple, Callable
@dataclass
class Patch:
add_edges: List[Tuple[int, int]]
remove_edges: List[Tuple[int, int]]
def _edge_set_from_cex(cex: Dict[str, Any]) -> Set[Tuple[int, int]]:
edges = cex.get("edges", []) or []
out: Set[Tuple[int, int]] = set()
for e in edges:
if isinstance(e, (list, tuple)) and len(e) == 2:
a, b = e
if isinstance(a, int) and isinstance(b, int):
out.add((a, b))
return out
def apply_patch_to_counterexample(cex: Dict[str, Any], patch: Patch) -> Dict[str, Any]:
"""
反例モデルに対して edge の add/remove を適用した新しいモデルを返す。
valuation 等はそのまま残す(Phase 11 の目的は構造破壊の確認)。
"""
worlds = int(cex.get("n_worlds") or cex.get("worlds") or 0)
es = _edge_set_from_cex(cex)
for a, b in patch.remove_edges:
es.discard((a, b))
for a, b in patch.add_edges:
# 範囲外は無視(安全)
if 0 <= a < worlds and 0 <= b < worlds:
es.add((a, b))
new_cex = dict(cex)
new_cex["edges"] = [[a, b] for (a, b) in sorted(es)]
return new_cex
# ----------------------------
# Assumption checks (MVP)
# ----------------------------
def check_assumption_satisfied(cex: Dict[str, Any], assumption: str) -> bool:
worlds = int(cex.get("n_worlds") or cex.get("worlds") or 0)
es = _edge_set_from_cex(cex)
if assumption == "assume:reflexive":
return all((i, i) in es for i in range(worlds))
if assumption == "assume:symmetric":
return all((b, a) in es for (a, b) in es)
if assumption == "assume:transitive":
# if (x,y) and (y,z) then (x,z)
for (x, y) in es:
for (y2, z) in es:
if y2 == y and (x, z) not in es:
return False
return True
if assumption == "assume:euclidean":
# if (a,b) and (a,c) then (b,c)
for (a, b) in es:
for (a2, c) in es:
if a2 == a and (b, c) not in es:
return False
return True
# unknown assumption kind => can't verify, treat as False to be conservative
return False
# ----------------------------
# Formula recheck hook
# ----------------------------
def recheck_formula_with_model_search(
model_checker_fn: Callable[[str, Dict[str, Any]], bool],
formula: str,
assumptions: List[str], # Not used for direct model check, but kept for signature compat if needed
patched_cex: Dict[str, Any],
) -> Dict[str, Any]:
"""
Check if patched_cex is STILL a counterexample.
model_checker_fn(formula, model) returns True if model satisfies formula (VALID), False if COUNTEREXAMPLE.
"""
try:
# If model satisfies formula, it is NOT a counterexample anymore.
is_valid_on_model = model_checker_fn(formula, patched_cex)
if is_valid_on_model:
return {
"still_counterexample": False,
"note": "Formula holds on the patched model (counterexample destroyed)."
}
else:
return {
"still_counterexample": True,
"note": "Formula still fails on the patched model."
}
except Exception as e:
return {"still_counterexample": None, "note": f"Error during recheck: {e}"}
def model_search_wrapper(
model_search_fn: Callable,
formula: str,
assumptions: List[str]
) -> Dict[str, Any]:
"""
engineの model_search を呼ぶための薄いラッパ。
返り値形式を統一:
{ "valid": bool, "counterexample": dict|None, "note": str }
"""
# model_search_fn is usually find_counterexample from verifier.py
# find_counterexample returns VerifyResult
res = model_search_fn(formula=formula, assumptions=assumptions)
# Adapt VerifyResult to dict
is_valid = (res.status == "valid")
return {
"valid": is_valid,
"counterexample": res.counterexample,
"note": f"Status: {res.status}. Audit: {res.audit[-1] if res.audit else ''}"
}