L'équipe ProVal s'est arrêtée à la fin août 2012, et renaît en une nouvelle équipe
Toccata
Ces pages n'évoluent plus, suivez le lien ci-dessus pour les informations Ã
jour sur notre équipe.
Tree reconstruction from a list of leave depths
Problem 4 of the second verified software competition.
The ZIP file below contains both the source code, the Why3 proof session file and the Coq scripts of the proofs made in Coq. The Why3 source code is then displayed, followed by a summary of the proofs.
Auteurs: Jean-Christophe Filliâtre / Andrei Paskevich
Catégories: Tree Data Structure
Outils: Why3
Références: The 2nd Verified Software Competition
download ZIP archive
(* The 2nd Verified Software Competition (VSTTE 2012) https://sites.google.com/site/vstte2012/compet Problem 4: Tree Reconstruction Build a binary tree from a list of leaf depths, if any This is a purely applicative implementation, using immutable lists from Why3's standard library. *) theory Tree use export int.Int use export list.List use export list.Append type tree = Leaf | Node tree tree (* the list of leaf depths for tree t, if root is at depth d *) function depths (d: int) (t: tree) : list int = match t with | Leaf -> Cons d Nil | Node l r -> depths (d+1) l ++ depths (d+1) r end (* lemmas on depths *) lemma depths_head: forall t: tree, d: int. match depths d t with Cons x _ -> x >= d | Nil -> false end lemma depths_unique: forall t1 t2: tree, d: int, s1 s2: list int. depths d t1 ++ s1 = depths d t2 ++ s2 -> t1 = t2 && s1 = s2 lemma depths_prefix: forall t: tree, d1 d2: int, s1 s2: list int. depths d1 t ++ s1 = depths d2 t ++ s2 -> d1 = d2 lemma depths_prefix_simple: forall t: tree, d1 d2: int. depths d1 t = depths d2 t -> d1 = d2 lemma depths_subtree: forall t1 t2: tree, d1 d2: int, s1: list int. depths d1 t1 ++ s1 = depths d2 t2 -> d1 >= d2 lemma depths_unique2: forall t1 t2: tree, d1 d2: int. depths d1 t1 = depths d2 t2 -> d1 = d2 && t1 = t2 end module TreeReconstruction use export Tree use import list.Length (* termination of build_rec (below) requires a lexicographic order *) predicate lex (x1 x2: (list int, int)) = let s1, d1 = x1 in let s2, d2 = x2 in length s1 < length s2 || length s1 = length s2 && match s1, s2 with | Cons h1 _, Cons h2 _ -> d2 < d1 <= h1 = h2 | _ -> false end exception Failure (* used to signal the algorithm's failure i.e. there is no tree *) let rec build_rec (d: int) (s: list int) : (tree, list int) variant { (s, d) with lex } ensures { let t, s' = result in s = depths d t ++ s' } raises { Failure -> forall t: tree, s' : list int. depths d t ++ s' <> s } = match s with | Nil -> raise Failure | Cons h t -> if h < d then raise Failure; if h = d then (Leaf, t) else let l, s = build_rec (d+1) s in let r, s = build_rec (d+1) s in (Node l r, s) end let build (s: list int) : tree ensures { depths 0 result = s } raises { Failure -> forall t: tree. depths 0 t <> s } = let t, s = build_rec 0 s in match s with | Nil -> t | _ -> raise Failure end end module Harness use import TreeReconstruction let harness () ensures { result = Node Leaf (Node (Node Leaf Leaf) Leaf) } raises { Failure -> false } = build (Cons 1 (Cons 3 (Cons 3 (Cons 2 Nil)))) let harness2 () ensures { false } raises { Failure -> true } = build (Cons 1 (Cons 3 (Cons 2 (Cons 2 Nil)))) end (* A variant implementation proposed by Jayadev Misra Given the input list [x1; x2; ...; xn], we first turn it into the list of pairs [(x1, Leaf); (x2, Leaf); ...; (xn, Leaf)]. Then, repeatedly, we scan this list from left to right, looking for two consecutive pairs (v1, t1) and (v2, t2) with v1 = v2. Then we replace them with the pair (v1-1, Node t1 t2) and we start again. We stop when there is only one pair left (v,t). Then we must have v=0. The implementation below achieves linear complexity using a zipper data structure to traverse the list of pairs. The left list contains the elements already traversed (thus on the left), in reverse order, and the right list contains the elements yet to be traversed. *) (* Proving termination is quite easy and we do it first (though we could, obviously, do it together with proving correctness) *) module ZipperBasedTermination use import Tree use import list.Length use import list.Reverse exception Failure let rec tc (left: list (int, tree)) (right: list (int, tree)) : tree variant { length left + length right, length right } = match left, right with | _, Nil -> raise Failure | Nil, Cons (v, t) Nil -> if v = 0 then t else raise Failure | Nil, Cons (v, t) right' -> tc (Cons (v, t) Nil) right' | Cons (v1, t1) left', Cons (v2, t2) right' -> if v1 = v2 then tc left' (Cons (v1 - 1, Node t1 t2) right') else tc (Cons (v2, t2) left) right' end end (* Now soundness and completeness *) module ZipperBased use import Tree use import list.Length use import list.Reverse (* the following function generalizes function [depths] to a forest, that is a list of pairs (depth, tree) *) function forest_depths (f: list (int, tree)) : list int = match f with | Nil -> Nil | Cons (d, t) r -> depths d t ++ forest_depths r end (* an obvious lemma on [forest_depths] *) lemma forest_depths_append: forall f1 f2: list (int, tree). forest_depths (f1 ++ f2) = forest_depths f1 ++ forest_depths f2 (* to prove completeness, one needs an invariant over the list [left]. The main ingredient is predicate [greedy] below, which states that [d] is distinct from all depths along the left branch of [d1, t1]. *) predicate greedy (d: int) (d1: int) (t1: tree) = d <> d1 /\ match t1 with Leaf -> true | Node l1 _ -> greedy d (d1+1) l1 end (* then we extend it to a list of pairs [(dn,tn); ...; (d2,t2); (d1,t1)] as follows: [greedy d2 d1 t1], [greedy d3 d2 t2], etc. this is inductive predicate [g] *) inductive g (l: list (int, tree)) = | Gnil: g Nil | Gone: forall d: int, t: tree. g (Cons (d, t) Nil) | Gtwo: forall d1 d2: int, t1 t2: tree, l: list (int, tree). greedy d1 d2 t2 -> g (Cons (d1, t1) l) -> g (Cons (d2, t2) (Cons (d1, t1) l)) (* an easy lemma on [g] *) lemma g_append: forall l1 l2: list (int, tree). g (l1 ++ l2) -> g l1 (* key lemma for completeness: whenever we fail because [right] is empty, we have to prove that there is no solution Note: the proof first generalizes the statement as follows: forest_depths ((d1,t1) :: l) <> depths d t + s whenever d < d1 (see the corresponding Coq file) *) lemma right_nil: forall l: list (int, tree). length l >= 2 -> g l -> forall t: tree, d: int. forest_depths (reverse l) <> depths d t (* key lemma for soundness: preservation of the invariant when we move a tree from [right] to [left] *) lemma main_lemma: forall l: list (int, tree), d1 d2: int, t1 t2: tree. d1 <> d2 -> g (Cons (d1, t1) l) -> match t2 with Node l2 _ -> greedy d1 (d2+1) l2 | Leaf -> true end -> g (Cons (d2, t2) (Cons (d1, t1) l)) (* finally, we need a predicate to state that a forest [l] contains only leaves *) predicate only_leaf (l: list (int, tree)) = match l with | Nil -> true | Cons (_, t) r -> t = Leaf /\ only_leaf r end exception Failure let rec tc (left: list (int, tree)) (right: list (int, tree)) : tree requires { (* list [left] satisfies the invariant *) g left /\ (* when [left] has one element, it can't be a solution *) match left with Cons (d1, t1) Nil -> d1 <> 0 \/ right <> Nil | _ -> true end /\ (* apart (possibly) from its head, all elements in [right] are leaves; moreover the left branch of [right]'s head already satisfies invariant [g] when consed to [left] *) match right with | Cons (d2, t2) right' -> only_leaf right' /\ match t2 with Node l2 _ -> g (Cons (d2+1, l2) left) | Leaf -> true end | Nil -> true end } ensures { depths 0 result = forest_depths (reverse left ++ right) } raises { Failure -> forall t: tree. depths 0 t <> forest_depths (reverse left ++ right) } = match left, right with | _, Nil -> raise Failure | Nil, Cons (v, t) Nil -> if v = 0 then t else raise Failure | Nil, Cons (v, t) right' -> tc (Cons (v, t) Nil) right' | Cons (v1, t1) left', Cons (v2, t2) right' -> if v1 = v2 then tc left' (Cons (v1 - 1, Node t1 t2) right') else tc (Cons (v2, t2) left) right' end (* Getting function [build] from [tc] is easy: from the list [x1; x2; ...; xn] we simply build the list of pairs [(x1, Leaf); (x2, Leaf); ...; (xn, Leaf)]. Function [map_leaf] below does this. *) function map_leaf (l: list int) : list (int, tree) = match l with | Nil -> Nil | Cons d r -> Cons (d, Leaf) (map_leaf r) end (* two lemmas on [map_leaf] *) lemma map_leaf_depths: forall l: list int. forest_depths (map_leaf l) = l lemma map_leaf_only_leaf: forall l: list int. only_leaf (map_leaf l) let build (s: list int) ensures { depths 0 result = s } raises { Failure -> forall t: tree. depths 0 t <> s } = tc Nil (map_leaf s) end
Why3 Proof Results for Project "vstte12_tree_reconstruction"
Theory "Tree": fully verified
| Obligations | CVC3 (2.4.1) | Coq (8.3pl4) | depths_head | --- | 0.52 | depths_unique | --- | 0.56 | depths_prefix | --- | 0.70 | depths_prefix_simple | 0.01 | --- | depths_subtree | --- | 1.06 | depths_unique2 | --- | 0.87 |
Theory "WP TreeReconstruction": fully verified
| Obligations | Alt-Ergo (0.94) | CVC3 (2.2) | Coq (8.3pl4) | parameter build_rec | --- | --- | --- |
| split_goal | parameter build_rec | 0.01 | --- | --- | |
| parameter build_rec | --- | 1.38 | --- | ||
| parameter build_rec | 0.03 | --- | --- | ||
| parameter build_rec | --- | --- | --- | ||
| split_goal | parameter build_rec | 0.05 | --- | --- | |
| parameter build_rec | 0.04 | --- | --- | ||
| parameter build_rec | 0.06 | --- | --- | ||
| parameter build_rec | --- | --- | 0.71 | ||
| parameter build_rec | --- | --- | 0.61 | parameter build | --- | --- | --- |
| split_goal | parameter build | 0.01 | --- | --- | |
| parameter build | --- | --- | 0.62 | ||
| exceptional postcondition | --- | --- | 0.53 | ||
Theory "WP Harness": fully verified
| Obligations | Coq (8.3pl4) | parameter harness | --- |
| split_goal | normal postcondition | 0.52 |
| parameter harness | 0.55 | parameter harness2 | 0.72 |
Theory "WP ZipperBasedTermination": fully verified
| Obligations | Alt-Ergo (0.94) | parameter tc | --- |
| split_goal | parameter tc | 0.06 |
| parameter tc | 0.04 | |
| parameter tc | 0.01 | |
| parameter tc | 0.07 | |
| parameter tc | 0.09 | |
| parameter tc | 0.05 | |
| parameter tc | 0.01 | |
Theory "WP ZipperBased": fully verified
| Obligations | Alt-Ergo (0.94) | CVC3 (2.4.1) | Coq (8.3pl4) | Z3 (3.2) | forest_depths_append | --- | --- | 0.54 | --- | g_append | --- | --- | 1.18 | --- | right_nil | --- | --- | 9.96 | --- | main_lemma | --- | --- | 0.79 | --- | parameter tc | --- | --- | --- | --- |
| split_goal | parameter tc | --- | --- | --- | --- | |
| split_goal | parameter tc | --- | 0.12 | --- | --- | |
| parameter tc | --- | 0.03 | --- | --- | ||
| parameter tc | --- | 0.02 | --- | --- | ||
| parameter tc | --- | 0.02 | --- | --- | ||
| parameter tc | --- | 0.86 | --- | --- | ||
| parameter tc | --- | --- | 0.99 | --- | ||
| parameter tc | --- | --- | --- | --- | ||
| split_goal | parameter tc | 6.42 | --- | --- | --- | |
| parameter tc | --- | 0.02 | --- | --- | ||
| parameter tc | --- | 0.02 | --- | --- | ||
| parameter tc | --- | 0.03 | --- | --- | ||
| parameter tc | 0.02 | 0.15 | --- | --- | ||
| parameter tc | 0.02 | 0.08 | --- | --- | ||
| parameter tc | --- | 0.02 | --- | --- | ||
| parameter tc | --- | 0.07 | --- | --- | ||
| parameter tc | --- | --- | --- | --- | ||
| split_goal | parameter tc | --- | 0.12 | --- | --- | |
| parameter tc | --- | 0.02 | --- | --- | ||
| parameter tc | --- | 0.03 | --- | --- | ||
| parameter tc | --- | 0.02 | --- | --- | ||
| parameter tc | --- | --- | 0.61 | --- | ||
| parameter tc | --- | --- | 0.97 | --- | ||
| parameter tc | --- | --- | --- | --- | ||
| split_goal | parameter tc | 13.46 | --- | --- | --- | |
| parameter tc | --- | 0.02 | --- | --- | ||
| parameter tc | --- | 0.06 | --- | --- | ||
| parameter tc | --- | 0.12 | --- | --- | ||
| parameter tc | 0.02 | 0.03 | --- | --- | ||
| parameter tc | 0.02 | 0.03 | --- | 0.02 | ||
| parameter tc | --- | 0.10 | --- | Timeout | ||
| parameter tc | 0.02 | 0.02 | --- | --- | ||
| parameter tc | 0.02 | 0.02 | --- | 0.02 | ||
| parameter tc | --- | 0.15 | --- | --- | map_leaf_depths | --- | --- | 0.53 | --- | map_leaf_only_leaf | --- | --- | 0.53 | --- | parameter build | 0.05 | --- | --- | --- |