# Programming with Decision Procedures - PowerPoint PPT Presentation Download Presentation Programming with Decision Procedures

Programming with Decision Procedures Download Presentation ## Programming with Decision Procedures

- - - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - - -
##### Presentation Transcript

1. Programming with Decision Procedures Philippe Suter ÉcolePolytechniqueFédérale de Lausanne, Switzerland

2. What’s a Decision Procedure? • 30’000 ft view, known uses

3. This Talk • High-level plan; from decision procedures to applications.

4. Verification of functional programs proof counterexample (input, trace) t1 = Node(Leaf, 5, Node(Leaf,1,Leaf)) t2 = Leaf z = true in BST.remove(Tree, Int)

5. sealedabstractclass Tree caseclass Node(left: Tree, value: Int, right: Tree) extends Tree caseclass Leaf() extends Tree object BST { def add(tree: Tree, element: Int): Tree = tree match { case Leaf() ⇒ Node(Leaf(), element, Leaf()) case Node(l, v, r) if v > element ⇒ Node(add(l, element), v, r) case Node(l, v, r) if v < element ⇒ Node(l, v, add(r, element)) case Node(l, v, r) if v == element ⇒ tree } ensuring (result ≠ Leaf()) } (tree = Node(l, v, r) ∧ v > element ∧ result ≠ Leaf()) ⇒ Node(result, v, r) ≠ Leaf() We know how to generate verification conditions for functional programs

6. Proving verification conditions D.C. Oppen, Reasoning about Recursively Defined Data Structures, POPL ’78 G. Nelson, D.C. Oppen, Simplification by Cooperating Decision Procedure, TOPLAS ’79 (tree = Node(l, v, r) ∧ v > element ∧ result ≠ Leaf()) ⇒ Node(result, v, r) ≠ Leaf() Previous work gives decision procedures that can handle certain verification conditions

7. sealedabstractclass Tree caseclass Node(left: Tree, value: Int, right: Tree) extends Tree caseclass Leaf() extends Tree object BST { def add(tree: Tree, element: Int): Tree = tree match { case Leaf() ⇒ Node(Leaf(), element, Leaf()) case Node(l, v, r) if v > element ⇒ Node(add(l, element), v, r) case Node(l, v, r) if v < element ⇒ Node(l, v, add(r, element)) case Node(l, v, r) if v == element ⇒ tree } ensuring (content(result) == content(tree) ∪ { element }) def content(tree: Tree) : Set[Int] = tree match { case Leaf() ⇒ ∅ case Node(l, v, r) ⇒ content(l) ∪ { v } ∪ content(r) } }

8. Complex verification condition Set Expressions t1 = Node(t2, e1, t3) ∧ content(t4) = content(t2) ∪ { e2 } ∧ content(Node(t4, e1, t3)) ≠ content(t1) ∪ { e2 } ? where def content(tree: Tree) : Set[Int] = tree match { case Leaf() ⇒ ∅ case Node(l, v, r) ⇒ content(l) ∪ { v } ∪ content(r) } Recursive Function Algebraic Data Types

9. Our contribution Decision procedures for extensions of algebraic data types with certain recursive functions

10. Formulas we aim to prove Quantifier-free Formula t1 = Node(t2, e1, t3) ∧ content(t4) = content(t2) ∪ { e2 } ∧ content(Node(t4, e1, t3)) ≠ content(t1) ∪ { e2 } where def content(tree: Tree) : Set[Int] = tree match { case Leaf() ⇒ ∅ case Node(l, v, r) ⇒ content(l) ∪ { v } ∪ content(r) } Generalized Fold Function Domain with a Decidable Theory

11. General form of our recursive functions empty : C combine : (C, E, C) → C def content(tree: Tree) : Set[Int] = tree match { case Leaf() ⇒ ∅ case Node(l, v, r) ⇒ content(l) ∪ { v } ∪ content(r) } defα(tree: Tree) : C = tree match { case Leaf() ⇒ empty case Node(l, v, r) ⇒ combine(α(l), v, α(r)) }

12. How do we prove such formulas? Quantifier-free Formula t1 = Node(t2, e1, t3) ∧ content(t4) = content(t2) ∪ { e2 } ∧ content(Node(t4, e1, t3)) ≠ content(t1) ∪ { e2 } where def content(tree: Tree) : Set[Int] = tree match { case Leaf() ⇒ ∅ case Node(l, v, r) ⇒ content(l) ∪ { v } ∪ content(r) } Generalized Fold Function Domain with a Decidable Theory

13. Separate the Conjuncts t1 = Node(t2, e1, t3) ∧ content(t4) = content(t2) ∪ { e2 } ∧ content(Node(t4, e1, t3)) ≠ content(t1) ∪ { e2 } t1 = Node(t2, e1, t3) ∧ t5 = Node(t4, e1, t3) ∧ c4 = c2 ∪ { e2 } ∧ c5 ≠ c1 ∪ { e2 } ∧ c1 = content(t1) ∧ … ∧ c5 = content(t5)

14. 1 t1 t4 t1 4 = 1 t2 2 t4 7 t3 0 t5 = ∪ 1 ∪ ∪ ∪ 1 4 = 4 α(t1) 4 ∪ ∪ ∪ α(t4) c2 7 7 2 t2 α(t2) 2 2 = content c4 = ∪ ∅ c3 ∅ 0 ∅ t3 α(t3) 0 c4 = { 4 } ∪ { 2 } ∪ ∅ ∪ c3 ∪ c2 t5 α(t5) ∅

15. Overview of the decision procedure tree constraints from the input formula set constraints from the input formula mappings from the input formula c4 = c2 ∪ { e2 } ∧ c5 ≠ c1 ∪ { e2 } t1 = Node(t2, e1, t3) t5 = Node(t4, e1, t3) ci = content(ti), i ∈ { 1, …, 5 } ∧ c4 = c2 ∪ { e2 } c5 ≠ c1 ∪ { e2 } c1 = c2 ∪ { e1 } ∪ c3 c5 = c4 ∪ { e1 } ∪ c3 ∧ ∧ ∧ c1 = c2 ∪ { e1 } ∪ c3 c5 = c4 ∪ { e1 } ∪ c3 ∧ resulting formula additional derived constraints The resulting formula is in the decidable theory of sets Decision Procedure for Sets

16. A verifier based on such procedure val c1 = content(t1) val c2 = content(t2) if (t1 ≠ t2) { if (c1 == ∅) { assert(c2 ≠ ∅) x = c2.chooseElement } } c1 = content(t1) ∧ c2 = content(t2) ∧ t1 ≠ t2 ∧ c1 = ∅ ∧ c2 = ∅ Warning: possible assertion violation

17. Source of incompleteness c1 = content(t1) ∧ c2 = content(t2) ∧ t1 ≠ t2 ∧ c1 = ∅ ∧ c2 = ∅ … t1 ≠ t2 ! c1 = ∅ ∧ c2 = ∅ ∅ Models for the formula in the logic of sets must not contradict the disequalities over trees

18. How to make the algorithm complete • Case analysis for each tree variable: • is it Leaf ? • Is it not Leaf ? c1 = content(t1) ∧ c2 = content(t2) ∧ t1 ≠ t2 ∧ c1 = ∅ ∧ c2 = ∅ ∧ t1 = Leaf ∧ t2 = Node(t3, e, t4) ∧ t1 = Leaf ∧ t2 = Leaf ∧ t1 = Node(t3, e1, t4) ∧ t2 = Node(t5, e2, t6) ∧ t1 Node(t3, e, t4) ∧ t2 = Leaf This gives a complete decision procedure for the content function that maps to sets

19. What about other content functions? • Tree content abstraction, as a: • Set • Multiset • List • Tree size, height, min • Invariants (sortedness,…)

20. Choice of trees is constrained by sets tree constraints from the input formula set constraints from the input formula mappings from the input formula c4 = c2 ∪ { e2 } ∧ c5 ≠ c1 ∪ { e2 } t1 = Node(t2, e1, t3) t5 = Node(t4, e1, t3) ci = content(ti), i ∈ { 1, …, 5 } ∧ c4 = c2 ∪ { e2 } c5 ≠ c1 ∪ { e2 } c1 = c2 ∪ { e1 } ∪ c3 c5 = c4 ∪ { e1 } ∪ c3 ∧ ∧ ∧ c1 = c2 ∪ { e1 } ∪ c3 c5 = c4 ∪ { e1 } ∪ c3 ∧ resulting formula additional derived constraints Decision Procedure for Sets

21. Inverse images • When we have a model for c1, c2, … how can we pick distinct values for t1, t2,… ? ⇔ ti ∈ content-1 (ci) ci = content(ti) α α-1 The cardinality of α-1 (ci) is what matters.

22. ‘Surjectivity’ of set abstraction content-1 ∅ 5 5 1 content-1 { 1, 5 } 5 1 1 1 … 5 |content-1(∅)| = 1 |content-1({1, 5})| = ∞

23. In-order traversal 2 inorder- [ 1, 2, 4, 7 ] 1 7 4

24. ‘Surjectivity’ of in-order traversal inorder-1 [ ] 5 1 [ 1, 5 ] inorder-1 1 5 |inorder-1(list)| = (number of trees of size n = length(list))

25. More trees map to longer lists |inorder-1(list)| … … length(list)

26. - - a finite set of shapes Sp a closed formula Mp in the collection theory such that Mp(c) implies |α-1(c)| > p Pick p sufficiently large. Guess which trees have a problematic shape. Guess their shape and their elements. By construction values for all other trees can be found. An abstraction function α (e.g. content, inorder) is sufficiently surjective if and only if, for each number p > 0, there exist, computable as a function of p: such that, for every term t, Mp(α(t)) or š(t) in Sp.

27. Generalization of the Independence of Disequations Lemma • For a conjunction of ndisequalities over tree terms, if for each term we can pick a value from a set of trees of size at least n+1, then we can pick values that satisfy all disequalities. We can make sure there will be sufficiently many trees to choose from.

28. Scope of our result - Examples • Tree content abstraction, as a: • Set • Multiset • List • Tree size, height, min • Invariants (sortedness,…) • [Kuncak,Rinard’07] • [Piskac,Kuncak’08] • [Plandowski’04] • [Papadimitriou’81] • [Nelson,Oppen’79]

29. defcontent(tree: Tree) : Set[Int] = tree match{ caseLeaf() ⇒ Set.empty caseNode(l, v, r) ⇒ (content(l) ++ content(r)) + v } defallEven(list: List) : Boolean = list match{ caseNil() ⇒ true caseCons(x, xs) ⇒ (x % 2 == 0) && allEven(list) } defmax(tree: Tree) : Option[Int]= tree match{ caseLeaf() ⇒ None caseNode(l, v, r) ⇒ Some(max(l), max(r)) match{ case(None, None) ⇒ Some(v) case(Some(lm), None)⇒ max(lm, v) case(None, Some(rm)) ⇒ max(rm, v) case(Some(lm), Some(rm))⇒ (max(lm, rm), v) })} …as well as any catamorphism that maps to a finite domain!

30. Sufficiently SurjectivityHolds in Practice Theorem: For every sufficiently surjective abstraction our procedure is complete. Theorem: The following abstractions are sufficiently surjective: set content, multiset content, list (any-order), tree height, tree size, minimum, sortedness A complete decision procedure for all these cases!

31. Sufficiently Surjectivity Holds in Practice « » Mike Whalen (University of Minnesota & Rockwell Collins) [ ] K. Slind, D. Hardin, M. Whalen, T.H. Pham, The Guardol Language and Verification System, TACAS 2012 In the context of a project to reason about a domain specific language called Guardolfor XML message processing applications […the]procedure has allowed us to prove a number of interesting things […], so I'm extremely grateful for it.

32. Decision Procedures for Recursive Abstractions Functions • Reasoning about functional programs reduces to proving formulas • Decision procedures always find a proof or a counterexample • Previous decision procedures handle recursion-free formulas • We introduced decision procedures for formulas with recursive fold functions

33. Leon(Online) • A verifier for Scala programs. • The programming and specification languages are the same purely functional subset. definsert(e: Int, t: Tree) = t match{ caseLeaf ⇒ Node(Leaf, e, Leaf) caseNode(l, v, r) ife < v ⇒ Node(insert(e, l), v, r) caseNode(l, v, r) ife > v ⇒ Node(l, v, insert(e, r)) case_ ⇒ t } ensuring( res ⇒ content(res) == content(t) + e) defcontent(t: Tree) = t match{ caseLeaf ⇒Set.empty caseNode(l, v, r) ⇒ (content(l) ++ content(r)) + e }

34. …of quantifier-free formulas in a decidable base theory… Satisfiability ModuloRecursive Programs …pure, total, deterministic, first-order and terminating on all inputs…

35. Satisfiability Modulo Recursive Programs • Semi-decidable problem of great interest: • applications in verification, testing, theorem proving, constraint logic programming, … • Our algorithm always finds counter-examples when they exist, and will also find assume/guarantee style proofs. • It is a decision procedure for infinitely and sufficiently surjectiveabstraction functions. • We implemented it as a part of a verification system for a purely functional subset of Scala.

36. Proving/Disproving Modulo Recursive Programs

37. Proving by Inlining defsize(lst: List) = lstmatch{ caseNil ⇒ 0 caseCons(_, _) ⇒ 1 } ensuring(res ⇒ res ≥ 0) Convert pattern-matching, negate. (if(lst = Nil) 0 else1) < 0 Unsatisfiable.

38. Proving by Inlining defsize(lst: List) = lstmatch{ caseNil ⇒ 0 caseCons(x, xs) ⇒ 1 + size(xs) } ensuring(res ⇒ res ≥ 0) Convert pattern-matching, negate, treat functions as uninterpreted. (if(lst = Nil) 0 else1 + size(lst.tail)) < 0 Satisfiable. (e.g. for size(Nil) = -2) Inline post-condition. (if(lst = Nil) 0 else1 + size(lst.tail)) < 0 ∧ size(lst.tail) ≥ 0 Unsatisfiable.

39. Disproving by Inlining defsize(lst: List) = lstmatch{ caseNil ⇒ -1 caseCons(x, xs) ⇒ 1 + size(xs) } defprop(lst: List) = { size(lst) ≥ 0 } ensuring(res ⇒ res) Negate, treat functions as uninterpreted. Satisfiable. (e.g. lst = , size() = -1) size(lst) < 0 Inline definition of size(lst). size(lst) < 0 ∧ size(lst) = (if(lst = Nil) -1 else 1 + size(lst.tail)) Satisfiable. (e.g. lst = [0,1], size() = -2) size(lst) < 0 ∧ size(lst) = (if(lst = Nil) -1 else 1 + size(lst.tail)) ∧ size(lst.tail) = (if(lst.tail= Nil) -1 else 1 + size(lst.tail.tail)) Satisfiable. (e.g. lst = [0,1,2], …)

40. Disproving with Inlining size(lst) size(lst.tail) ? size(lst.tail.tail) ? … ? There are always unknown branches in the evaluation tree. We can never be sure that there exists no smaller solution.

41. Branch Rewriting size(lst) = if(lst = Nil) { -1 } else{ 1 + size(lst.tail) } size(lst) = ite1 ∧ p1⇔ lst = Nil ∧ p1 ⇒ ite1 = -1 ∧ ¬p1⇒ ite1 = 1 + size(lst.tail) … ∧ p1 size(lst) • Satisfiable assignments do not depend on unknown values. • Unsatisfiable answers have two possible meanings. ? p1 ¬p1

42. Algorithm Some literals in B may be implied by φ: no need to unroll what they guard. (φ, B) = unroll(φ, _) while(true) { solve(φ∧ B) match{ case“SAT” ⇒return“SAT” case“UNSAT” ⇒ solve(φ) match{ case“UNSAT” ⇒return“UNSAT” case“SAT” ⇒ (φ, B) = unroll(φ, B) } } } Inlining must be fair “I’m feeling lucky” Inlines some postconditions and bodies of function applications that were guarded by a literal in B, returns the new formula and new set of guards B.

43. Completeness for Counter-Examples • Let a1,… an be the free variables of φ, and c1,…,cnbe a counter-example to φ. • Let T be the evaluation tree of φ[a1→c1, …, an→cn]. • Eventually, the algorithm will reach a point where T is covered. T : ? ? ? ?

44. Sufficiently Surjective Abstractions • The algorithm terminates, and is thus a decision procedure for the case of infinitely and sufficientlysurjective abstraction functions. defα(tree: Tree) : C = tree match{ case Leaf() ⇒ empty case Node(l, v, r) ⇒ combine(α(l), v, α(r)) }

45. A Tale of Two Techniques φ≡… α(t) … ∨ψ(α(t)) t≡ • For catamorphisms, unrolling α corresponds to enumerating potential counter-examples by the size of their tree. • When all required trees have been considered, a formula at least as strong as ψ(α(t))is derived by the solver. t ≡ t≡ t≡ t≡

46. Verification System

47. (Some) Experimental Results Functional correctness properties of data structures: red-black trees implement a set and maintain height invariants, associative list has read-over-write property, insertion sort returns a sorted list of identical content, amortized queue implements a balanced queue, etc.