Nikolaj Bjørner
Microsoft Research
nbjorner@microsoft.com |
Research areas around SMT proofs
SMT proofs in a nutshell
A brief summary of proof efforts for SMT
A sample of proof formats
Proofs for EUF
Proofs for Arithmetic
Other proofs
Standards?
Directions
Connecting SMT solvers to internal/external verifiers
Dealing with large surface areas of SMT theories
How detailed should proofs be?
Use proofs for profiling, learning, debugging
Proofs for simplification vs. search
Pre-processing simplification
Proofs from CDCL(T) search
Theory solver offers two main services
Both cases rely on establishing -tautologies
tautologies are justified using theory specific rules
tautologies can be added to propositional RUP proofs as trusted assumptions
EUF - we show how to justify EUF tautologies found by congruence closure
Arithmetic - we illustrate extracting Farkas proofs
Other theories - many rely on instantiating axioms.
SMT solvers combine multiple -solvers into a single solver.
Example: EUF + Arithmetic
For proof generation, we have a nested scenario:
The CDCL solver by default logs -clauses after false unit literals have been removed
Steps required to verify -clause :
T-solvers share and propagate equalities
Each internal -propagation is logged as a lemma.
Assumed and propagated equalities are not known by CDCL(T) solver.
Equality reasoning leaks to -solvers
Variant of dependent type theory with built-ins for exceptions
First described in 2002, then several 2008, 2013, ..
Noteworthy:
Pre-processing
A general contextual rewrite functor
So far unexplored in this work:
Wide set of theories
Unsupported only: Floats, sets, sequences
Target a variety of backends
Pre-processing proofs
Trusted base of proof rules, see SMT Workshop 2022 paper.
Proof rules for linear arithmetic, arrays, ADTs, EUF.
Proof rules formulated mild extension of SMT3
Self-contained checker
(assume a0 (exists ((x A)) (f x)))
(anchor :step t1 :args (:= x vr))
(step t1.t1 (cl (= x vr)) :rule cong)
(step t1.t2 (cl (= (f x) (f vr))) :rule cong)
(step t1 (cl (= (exists ((x A)) (f x))
(exists ((vr A)) (f vr)))) :rule bind)
(step t2 (cl (not (= (exists ((vr A)) (f x))
(exists ((vr A)) (f vr)))) (not (exists ((vr A)) (f x)))
(exists ((vr A)) (f vr))) :rule equiv_pos1)
(step t3 (cl (exists ((vr A)) (f vr))) :premises (a0 t1 t2) :rule resolution)
(define-fun X () A (choice ((vr A)) (f vr)))
(step t4 (cl (= (exists ((vr A)) (f vr)) (f X))) :rule sko_ex)
(step t5 (cl (not (= (exists ((vr A)) (f vr)) (f X)))
(not (exists ((vr A)) (f vr))) (f X)) :rule equiv_pos1)
(step t6 (cl (f X)) :premises (t3 t4 t5) :rule resolution)
(set-option :proof true)
(declare-fun f (Int) Int)
(declare-const x Int)
(assert (or (= (f (f (f x))) x) (= (f (f x)) x)))
(assert (not (= (f (f (f (f (f (f x)))))) x)))
(check-sat)
(get-proof)
((proof
(let ((?x25 (f x)))
(let ((?x26 (f ?x25)))
(let ((?x27 (f ?x26)))
(let ((?x32 (f ?x27)))
(let ((?x33 (f ?x32)))
(let ((?x34 (f ?x33)))
(let (($x35 (= ?x34 x)))
(let (($x29 (= ?x26 x)))
(let (($x28 (= ?x27 x)))
(let ((@x47 (hypothesis $x28)))
(let ((@x53 (monotonicity (monotonicity (monotonicity @x47 (= ?x32 ?x25)) (= ?x33 ?x26)) (= ?x34 ?x27))))
(let (($x36 (not $x35)))
(let ((@x37 (asserted $x36)))
(let ((@x57 (lemma (unit-resolution @x37 (trans* @x53 @x47 $x35) false) (not $x28))))
(let (($x30 (or $x28 $x29)))
(let ((@x39 (rewrite (= $x30 $x30))))
(let ((@x58 (unit-resolution (mp (mp (mp (asserted $x30) @x39 $x30) @x39 $x30) @x39 $x30) @x57 $x29)))
(let ((@x64 (trans* (monotonicity (monotonicity @x58 (= ?x27 ?x25)) (= ?x32 ?x26)) @x58 (= ?x32 x))))
(let ((@x69 (trans* (monotonicity (monotonicity @x64 (= ?x33 ?x25)) (= ?x34 ?x26)) @x58 $x35)))
(unit-resolution @x37 @x69 false))))))))))))))))))))))
Pretty printed from Python:
assume(Or(f(f(f(x))) == x, f(f(x)) == x))
assume(Not(f(f(f(f(f(f(x)))))) == x))
infer(rup, Not(f(f(f(f(f(f(x)))))) == x))
infer(euf(Not(f(f(f(f(f(f(x)))))) == x),
f(f(x)) == x,
cc(f(x) == f(f(f(x)))),
cc(f(f(f(f(x)))) == f(f(x))),
cc(f(f(f(f(f(x))))) == f(x)),
cc(f(f(f(f(f(f(x)))))) == f(f(f(f(x)))))),
Or(Not(f(f(x)) == x), f(f(f(f(f(f(x)))))) == x))
infer(rup, Not(f(f(x)) == x))
infer(rup, f(f(f(x))) == x)
infer(euf(Not(f(f(f(f(f(f(x)))))) == x),
f(f(f(x))) == x,
cc(f(x) == f(f(f(f(x))))),
cc(f(f(f(f(f(x))))) == f(f(x))),
cc(f(f(f(f(f(f(x)))))) == f(f(f(x))))),
f(f(f(f(f(f(x)))))) == x)
infer(rup, False)
From SMTLIB:
(declare-fun f (Int) Int)
(define-const $24 Int (f x))
(define-const $25 Int (f $24))
(define-const $26 Int (f $25))
(define-const $27 Bool (= $26 x))
(define-const $28 Bool (= $25 x))
(assume $27 $28)
(define-const $30 Int (f $26))
(define-const $31 Int (f $30))
(define-const $32 Int (f $31))
(define-const $33 Bool (= $32 x))
(assume (not $33))
(declare-fun rup () Proof)
(infer (not $33) rup)
(declare-fun euf (Bool Bool Proof Proof Proof Proof) Proof)
(declare-fun cc (Bool) Proof)
(define-const $42 Bool (= $32 $30))
(define-const $43 Proof (cc $42))
(define-const $40 Bool (= $31 $24))
(define-const $41 Proof (cc $40))
(define-const $38 Bool (= $30 $25))
(define-const $39 Proof (cc $38))
(define-const $36 Bool (= $24 $26))
(define-const $37 Proof (cc $36))
(define-const $34 Bool (not $33))
(define-const $44 Proof (euf $34 $28 $37 $39 $41 $43))
(infer (not $28) $33 $44)
(infer (not $28) rup)
(infer $27 rup)
(declare-fun euf (Bool Bool Proof Proof Proof) Proof)
(define-const $49 Bool (= $32 $26))
(define-const $50 Proof (cc $49))
(define-const $47 Bool (= $31 $25))
(define-const $48 Proof (cc $47))
(define-const $45 Bool (= $24 $30))
(define-const $46 Proof (cc $45))
(define-const $51 Proof (euf $34 $27 $46 $48 $50))
(infer $33 $51)
(infer rup)
The empty theory of first-order logic.
merge()
Roots: n1 := find(n1), r2 := find(n2)
assume (r1 != r2)
Erase: for each p in r1.P such that p.cg == p:
erase from table
Update root: r1.find := r2
Justify: ....
Insert: for each p in r1.P:
p.cg = insert p in etable
if p.cg == p:
append p to r2.P
else
add (p.cg == p) to "to_merge"
unmerge():
Erase: for each p in r2.P added from r1.P:
erase p from table
Unjustify: ....
Revert root: r1.find := r1
Insert: for each p in r1.P:
insert p if n was cc root before merge
condition for being cc root before merge:
p.cg == p or !congruent(p, p.cg)
congruent(p,q) := roots of p.ts = roots of q.ts
A justification is a reason for merging two nodes. There are two possible reasons for merging nodes:
NB: is justified recursively by justifying .
Invariant: Every non-root node points to a linked list of justifications leading to the root
NB The linked list does not follow direction of union-find.
Missed justifications:
egg [13] keeps track of potential extra paths to find short proofs.
for SMT: data-structure overhead vs. amortized effect of backtracking?
Fine-grained proofs - pro/cons:
A coarse-grained proof object (N. Shankar chat @ FLoC):
(set-option :sat.euf true)
(set-option :sat.smt.proof eufproof.smt2)
(declare-fun f (Int) Int)
(declare-const x Int)
(assert (or (= (f (f (f x))) x) (= (f (f x)) x)))
(assert (not (= (f (f (f (f (f (f x)))))) x)))
(check-sat)
assume(Or(f(f(f(x))) == x, f(f(x)) == x))
assume(Not(f(f(f(f(f(f(x)))))) == x))
infer(rup, Not(f(f(f(f(f(f(x)))))) == x))
infer(euf(Not(f(f(f(f(f(f(x)))))) == x),
f(f(x)) == x,
cc(f(x) == f(f(f(x)))),
cc(f(f(f(f(x)))) == f(f(x))),
cc(f(f(f(f(f(x))))) == f(x)),
cc(f(f(f(f(f(f(x)))))) == f(f(f(f(x)))))),
Or(Not(f(f(x)) == x), f(f(f(f(f(f(x)))))) == x))
infer(rup, Not(f(f(x)) == x))
infer(rup, f(f(f(x))) == x)
infer(euf(Not(f(f(f(f(f(f(x)))))) == x),
f(f(f(x))) == x,
cc(f(x) == f(f(f(f(x))))),
cc(f(f(f(f(f(x))))) == f(f(x))),
cc(f(f(f(f(f(f(x)))))) == f(f(f(x))))),
f(f(f(f(f(f(x)))))) == x)
infer(rup, False)
SMT solving based on Dual Simplex maintain a tableau of the form:
where are basic and are non-basic variables.
x, y = Reals('x y')
solve([x >= 0, Or(x + y <= 2, x + 2*y >= 6),
Or(x + y >= 2, x + 2*y > 4)])
Introduce slacks to define terms
Formula using slacks:
Only bounds (e.g., ) are asserted during search.
A tableau is infeasible if there is a row such that
(We omit the symmetric case for upper bounds, )
The explanation for infeasibility are the literals or , respectively or .
Slack variables are unfolded into their definitions.
The justification are the coefficients
Dual simplex infeasibility
Bounds propagation
Equality propagation
Properties of div, mod, rem, to_int
Cuts
GCD tests
Non-linear arithmetic
(farkas c1 (<=? g1 g1') c2 (<=? g2 g2') ... cn (<=? gn gn'))
(bound c1 (<=? g1 g1') c2 (<=? g2 g2') ... cn (<=? gn gn') (not (<= e1 e2)))
(implied-eq c0 c1 (<=? g1 g1') c2 (<=? g2 g2') ... cn (<=? gn gn') (not (= e1 e2)))
where <=? is <=, <, > >=, =, and their negations; and are integer constants.
(bound c1 (<=? g1 g1') (<=? g2 g2') ... (<=? gn gn') (not (<= e1 e2)))
(implied-eq c0 c1 (<=? g1 g1') c2 (<=? g2 g2') ... cn (<=? gn gn') (not (= e1 e2)))
Check:
Use Skolem functions, not fresh constants [18], when instantiating existentials
(=> (forall ((x I)) (= (A i) (B i))) (= A B))
is instantiated as
A = B or select(A, diff(A,B)) != select(B, diff(A, B))
Use Skolem functions to replace extended operators
(assert (contains a b))
Is rewritten into
(assert (= a (str.++ (contains.left a b) b (contains.right a b))))
(choose ((x T)) (p x))
Used in proof generating systems (veriT, SMTInterpol).
SMTLIB Google Group (Hoenicke, Reynolds, others active)
Some diversity in formats generated by provers and not clear the format from provers should/can be aligned.
(farkas c1 (<=? g1 g1') c2 (<=? g2 g2') ... cn (<=? gn gn'))
where <=? is <=, <, > >=, =, and their negations; and are integer constants.
Define normalization function:
ci * (>= gi gi') -> -ci * (<= gi gi')
ci * (< gi gi') -> ci * (<= (+ 1 gi) gi') if gi is Int
ci * (> gi gi') -> -ci * (< gi gi')
ci * (not (<= gi gi')) -> -ci * (< gi gi')
ci * (<= gi gi') -> -ci * (<= gi' gi) if ci < 0
etc
Let be indices of inequalities, equalities, and reduce using the equalities . Then is non-negative if contains a strict inequality, or negative if it only contains weak inequalities.
Tradeoff between generic rules and simpler rules.
Unclear if added complexity of checking generic rules provides return on investment.
A proof checker could map to nucleus of SMTInterpol format before validating premises.
Model transformers, dual to proofs
Pre and in-processing proofs
Sustainable proofs
Beyond the power of regular resolution
Proofs for