140 likes | 313 Views
Automatic verification of summations. K. Rustan M. Leino. IFIP WG 2.3 meeting 46 Sydney, Australia 11 January 2007. Goal: prove the following program.
E N D
Automatic verification of summations K. Rustan M. Leino IFIP WG 2.3 meeting 46Sydney, Australia11 January 2007
Goal: prove the following program { 0 ≤ N }s := 0; n := 0;while n < Ninvariant 0 ≤ n ≤ N s = (Σi | 0 ≤ i < n :: a[i])do s := s + a[n]; n := n + 1end{ s = (Σi | 0 ≤ i < N :: a[i]) } … automatically, using a VC generator and an SMT solver
Need feedback on • Related work • More clever encoding • Some completeness argument/thoughts • Decision procedure to fit into an SMT solver • More examples • useful ones • contrived ones
Arrays • x := a[i] is treated as x := select(a, i) • a[i] := x is treated as a := store(a, i, x) • wp(a[i] := 5; assert a[k] = 12, true )=wp(a[i] := 5, select(a, k) = 12 )=select(store(a, i, 5), k) = 12
Assignment-free form • a[i] := 5; assert a[k] = 12is rewritten into: • assume a1 = store(a0, i, 5);assert select(a1, k) = 12whose wpis: • a1 = store(a0, i, 5) select(a1, k) = 12
Example wp( havoc b;assume (n :: n ≠ i b[n] = a[n]) b[i] = 5; a := b;assert a[k] = 12, true ) = (n :: n ≠ i b1[n] = a0[n]) b1[i] = 5 a2 = b1 select(a2, k) = 12
Term sets • All equalities and congruences are represented explicitly, but other derived facts may not be • Examples: • given: x ≤ y, y ≤ x also represents: x = y • given: x ≤ y, y ≤ z may not represent: x ≤ z • given: x = 3, y = x+1 may not represent: y = 4
Quantifiers • Instantiation via e-graph matching • A matching pattern (trigger) is a set of terms that together mention all the bound variables, and none of which is just a bound variable by itself • Examples: • (x :: { f(x) } 0 ≤ f(x)) • (x,y :: { g(x,y) } f(x) < g(x,y))
More examples • (x,y :: { f(x), f(y) } x ≤ y f(x) ≤ f(y)) • (x :: { f(x) } x ≠ null f(x) ≤ f(next(x))) • (x :: { f(next(x)) } x ≠ null f(x) ≤ f(next(x))) • (x :: { f(x+1) } f(x) ≤ f(x+1)) • (x,y,z :: { x*(y+z) } x*(y+z) = x*y + x*z) • (x,y :: { P(x,y) } x = y P(x,y) = 10) • (x :: { P(x,x) } P(x,x) = 10)
sum0 (rendered in BoogiePL) var a: [int]int; // map from int to int procedure Sum(N: int) returns (s: int) requires 0 <= N; ensures s == qsum(0, N, a); { var n: int; entry: n := 0; s := 0; goto Head; Head: assert 0 <= n && n <= N && s == qsum(0, n, a); // loop invariant goto Body, Done; Body: assume n < N; // loop guard s := s + a[n]; n := n + 1; goto Head; Done: assume !(n < N); // negation of loop guard return; } functionqsum(lo: int, hi: int, A: [int]int) returns (int); axiom (forall lo: int, hi: int, A: [int]int :: { qsum(lo, hi, A) } hi <= lo ==> qsum(lo, hi, A) == 0); axiom (forall lo: int, hi: int, A: [int]int :: { qsum(lo, hi+1, A) } lo <= hi ==> qsum(lo, hi+1, A) == qsum(lo, hi, A) + A[hi]); This program (and the ones on the following slides) verifies with Boogie, using Simplify as the underlying SMT solver
sum1 var a: [int]int; procedure Sum(N: int) returns (s: int) requires 0 <= N; modifies a; ensures s == qsum(0, N, old(a)); { var n: int; entry: n := 0; s := 0; goto Head; Head: assert 0 <= n && n <= N && s == qsum(0, n, a); // loop invariant assert (foralli: int :: 0 <= i && i < N ==> a[i] == old(a)[i]); goto Body, Done; Body: assume n < N; // loop guard s := s + a[n]; a[-2] := s; // assignment outside a[0,..N] n := n + 1; goto Head; Done: assume !(n < N); // negation of loop guard return; } functionqsum(lo: int, hi: int, A: [int]int) returns (int); axiom (forall lo: int, hi: int, A: [int]int :: { qsum(lo, hi, A) } hi <= lo ==> qsum(lo, hi, A) == 0); axiom (forall lo: int, hi: int, A: [int]int :: { qsum(lo, hi+1, A) } lo <= hi ==> qsum(lo, hi+1, A) == qsum(lo, hi, A) + A[hi]); axiom (forall lo: int, hi: int, A: [int]int, B: [int]int :: { qsum(lo, hi, A), qsum(lo, hi, B) } (forall j: int :: lo <= j && j < hi ==> A[j] == B[j]) ==> qsum(lo, hi, A) == qsum(lo, hi, B));
inc.bpl var a: [int]int; procedure Inc(j: int, N: int, x: int) requires 0 <= j && j < N; modifies a; ensuresqsum(0, N, a) == old(qsum(0, N, a)) + x; { entry: a[j] := a[j] + x; return; } functionqsum(lo: int, hi: int, A: [int]int) returns (int); axiom (forall lo: int, hi: int, A: [int]int :: { qsum(lo, hi, A) } hi <= lo ==> qsum(lo, hi, A) == 0); axiom (forall lo: int, hi: int, A: [int]int :: { qsum(lo, hi+1, A) } lo <= hi ==> qsum(lo, hi+1, A) == qsum(lo, hi, A) + A[hi]); axiom (forall lo: int, hi: int, A: [int]int, B: [int]int :: { qsum(lo, hi, A), qsum(lo, hi, B) } (forall j: int :: lo <= j && j < hi ==> A[j] == B[j]) ==> qsum(lo, hi, A) == qsum(lo, hi, B)); axiom (forall lo: int, hi: int, k: int, A: [int]int, B: [int]int :: { qsum(lo, hi, A), qsum(lo, hi, B), A[k] } (forall j: int :: lo <= j && j < k ==> A[j] == B[j]) && (forall j: int :: k < j && j < hi ==> A[j] == B[j]) ==> qsum(lo, hi, A) - A[k] == qsum(lo, hi, B) - B[k]);
swap.bpl var a: [int]int; procedure Swap(i: int, j: int, N: int) requires 0 <= i && i < N; requires 0 <= j && j < N; modifies a; ensuresqsum(0, N, a) == old(qsum(0, N, a)); { vartmp: int; entry: tmp := a[i]; a[i] := a[j]; assertqsum(0, N, a) == qsum(0, N, a); a[j] := tmp; return; } functionqsum(lo: int, hi: int, A: [int]int) returns (int); axiom (forall lo: int, hi: int, A: [int]int :: { qsum(lo, hi, A) } hi <= lo ==> qsum(lo, hi, A) == 0); axiom (forall lo: int, hi: int, A: [int]int :: { qsum(lo, hi+1, A) } lo <= hi ==> qsum(lo, hi+1, A) == qsum(lo, hi, A) + A[hi]); axiom (forall lo: int, hi: int, A: [int]int, B: [int]int :: { qsum(lo, hi, A), qsum(lo, hi, B) } (forall j: int :: lo <= j && j < hi ==> A[j] == B[j]) ==> qsum(lo, hi, A) == qsum(lo, hi, B)); axiom (forall lo: int, hi: int, k: int, A: [int]int, B: [int]int :: { qsum(lo, hi, A), qsum(lo, hi, B), A[k] } (forall j: int :: lo <= j && j < k ==> A[j] == B[j]) && (forall j: int :: k < j && j < hi ==> A[j] == B[j]) ==> qsum(lo, hi, A) - A[k] == qsum(lo, hi, B) - B[k]);