fix: context tracking in bv_decide counter example (#5675)

Closes #5674.
This commit is contained in:
Henrik Böving 2024-10-11 10:57:06 +02:00 committed by GitHub
parent 742ca6afa7
commit e5bbda1c3d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 40 additions and 18 deletions

View file

@ -41,7 +41,7 @@ def lratChecker (cfg : TacticContext) (bvExpr : BVLogicalExpr) : MetaM Expr := d
@[inherit_doc Lean.Parser.Tactic.bvCheck]
def bvCheck (g : MVarId) (cfg : TacticContext) : MetaM Unit := do
let unsatProver : UnsatProver := fun reflectionResult _ => do
let unsatProver : UnsatProver := fun _ reflectionResult _ => do
withTraceNode `sat (fun _ => return "Preparing LRAT reflection term") do
let proof ← lratChecker cfg reflectionResult.bvExpr
return .ok ⟨proof, ""⟩

View file

@ -83,6 +83,10 @@ structure ReflectionResult where
A counter example generated from the bitblaster.
-/
structure CounterExample where
/--
The goal in which to interpret this counter example.
-/
goal : MVarId
/--
The set of unused but potentially relevant hypotheses. Useful for diagnosing spurious counter
examples.
@ -97,7 +101,7 @@ structure UnsatProver.Result where
proof : Expr
lratCert : LratCert
abbrev UnsatProver := ReflectionResult → Std.HashMap Nat (Nat × Expr) →
abbrev UnsatProver := MVarId → ReflectionResult → Std.HashMap Nat (Nat × Expr) →
MetaM (Except CounterExample UnsatProver.Result)
/--
@ -112,8 +116,9 @@ abbrev DiagnosisM : Type → Type := ReaderT CounterExample <| StateRefT Diagnos
namespace DiagnosisM
def run (x : DiagnosisM Unit) (counterExample : CounterExample) : MetaM Diagnosis := do
let (_, issues) ← ReaderT.run x counterExample |>.run {}
return issues
counterExample.goal.withContext do
let (_, issues) ← ReaderT.run x counterExample |>.run {}
return issues
def unusedHyps : DiagnosisM (Std.HashSet FVarId) := do
return (← read).unusedHypotheses
@ -177,7 +182,7 @@ def explainCounterExampleQuality (counterExample : CounterExample) : MetaM Messa
err := err ++ m!"Consider the following assignment:\n"
return err
def lratBitblaster (cfg : TacticContext) (reflectionResult : ReflectionResult)
def lratBitblaster (goal : MVarId) (cfg : TacticContext) (reflectionResult : ReflectionResult)
(atomsAssignment : Std.HashMap Nat (Nat × Expr)) :
MetaM (Except CounterExample UnsatProver.Result) := do
let bvExpr := reflectionResult.bvExpr
@ -206,11 +211,13 @@ def lratBitblaster (cfg : TacticContext) (reflectionResult : ReflectionResult)
match res with
| .ok cert =>
trace[Meta.Tactic.sat] "SAT solver found a proof."
let proof ← cert.toReflectionProof cfg bvExpr ``verifyBVExpr ``unsat_of_verifyBVExpr_eq_true
return .ok ⟨proof, cert⟩
| .error assignment =>
trace[Meta.Tactic.sat] "SAT solver found a counter example."
let equations := reconstructCounterExample map assignment aigSize atomsAssignment
return .error { unusedHypotheses := reflectionResult.unusedHypotheses, equations }
return .error { goal, unusedHypotheses := reflectionResult.unusedHypotheses, equations }
def reflectBV (g : MVarId) : M ReflectionResult := g.withContext do
@ -248,7 +255,7 @@ def closeWithBVReflection (g : MVarId) (unsatProver : UnsatProver) :
let atomsPairs := (← getThe State).atoms.toList.map (fun (expr, ⟨width, ident⟩) => (ident, (width, expr)))
let atomsAssignment := Std.HashMap.ofList atomsPairs
match ← unsatProver reflectionResult atomsAssignment with
match ← unsatProver g reflectionResult atomsAssignment with
| .ok ⟨bvExprUnsat, cert⟩ =>
let proveFalse ← reflectionResult.proveFalse bvExprUnsat
g.assign proveFalse
@ -256,9 +263,9 @@ def closeWithBVReflection (g : MVarId) (unsatProver : UnsatProver) :
| .error counterExample => return .error counterExample
def bvUnsat (g : MVarId) (cfg : TacticContext) : MetaM (Except CounterExample LratCert) := M.run do
let unsatProver : UnsatProver := fun reflectionResult atomsAssignment => do
let unsatProver : UnsatProver := fun g reflectionResult atomsAssignment => do
withTraceNode `bv (fun _ => return "Preparing LRAT reflection term") do
lratBitblaster cfg reflectionResult atomsAssignment
lratBitblaster g cfg reflectionResult atomsAssignment
closeWithBVReflection g unsatProver
/--
@ -289,9 +296,11 @@ def bvDecide (g : MVarId) (cfg : TacticContext) : MetaM Result := do
match ← bvDecide' g cfg with
| .ok result => return result
| .error counterExample =>
let error ← explainCounterExampleQuality counterExample
let folder := fun error (var, value) => error ++ m!"{var} = {value.bv}\n"
throwError counterExample.equations.foldl (init := error) folder
counterExample.goal.withContext do
let error ← explainCounterExampleQuality counterExample
let folder := fun error (var, value) => error ++ m!"{var} = {value.bv}\n"
let errorMessage := counterExample.equations.foldl (init := error) folder
throwError (← addMessageContextFull errorMessage)
@[builtin_tactic Lean.Parser.Tactic.bvDecide]
def evalBvTrace : Tactic := fun

View file

@ -105,7 +105,7 @@ instance : ToExpr LRAT.IntAction where
mkApp3 (mkConst ``LRAT.Action.del [.zero, .zero]) beta alpha (toExpr ids)
toTypeExpr := mkConst ``LRAT.IntAction
def LratCert.ofFile (lratPath : System.FilePath) (trimProofs : Bool) : MetaM LratCert := do
def LratCert.ofFile (lratPath : System.FilePath) (trimProofs : Bool) : CoreM LratCert := do
let proofInput ← IO.FS.readBinFile lratPath
let proof ←
withTraceNode `sat (fun _ => return s!"Parsing LRAT file") do
@ -139,8 +139,8 @@ Run an external SAT solver on the `CNF` to obtain an LRAT proof.
This will obtain an `LratCert` if the formula is UNSAT and throw errors otherwise.
-/
def runExternal (cnf : CNF Nat) (solver : System.FilePath) (lratPath : System.FilePath)
(trimProofs : Bool) (timeout : Nat) (binaryProofs : Bool)
: MetaM (Except (Array (Bool × Nat)) LratCert) := do
(trimProofs : Bool) (timeout : Nat) (binaryProofs : Bool) :
CoreM (Except (Array (Bool × Nat)) LratCert) := do
IO.FS.withTempFile fun cnfHandle cnfPath => do
withTraceNode `sat (fun _ => return "Serializing SAT problem to DIMACS file") do
-- lazyPure to prevent compiler lifting
@ -162,7 +162,7 @@ def runExternal (cnf : CNF Nat) (solver : System.FilePath) (lratPath : System.Fi
/--
Add an auxiliary declaration. Only used to create constants that appear in our reflection proof.
-/
def mkAuxDecl (name : Name) (value type : Expr) : MetaM Unit :=
def mkAuxDecl (name : Name) (value type : Expr) : CoreM Unit :=
addAndCompile <| .defnDecl {
name := name,
levelParams := [],
@ -181,8 +181,7 @@ function together with a correctness theorem for it.
`∀ (b : α) (c : LratCert), verifier b c = true → unsat b`
-/
def LratCert.toReflectionProof [ToExpr α] (cert : LratCert) (cfg : TacticContext) (reflected : α)
(verifier : Name) (unsat_of_verifier_eq_true : Name) :
MetaM Expr := do
(verifier : Name) (unsat_of_verifier_eq_true : Name) : MetaM Expr := do
withTraceNode `sat (fun _ => return "Compiling expr term") do
mkAuxDecl cfg.exprDef (toExpr reflected) (toTypeExpr α)

14
tests/lean/run/5674.lean Normal file
View file

@ -0,0 +1,14 @@
import Std.Tactic.BVDecide
/--
error: The prover found a potentially spurious counterexample:
- The following potentially relevant hypotheses could not be used: [h]
Consider the following assignment:
b = 0x0000000000000000#64
-/
#guard_msgs in
example
(b : BitVec 64)
(h : b.toNat > 0) :
b > 0 := by
bv_decide