Skip to content
Snippets Groups Projects
Commit ecd17777 authored by Viktor Kuncak's avatar Viktor Kuncak
Browse files

doc: tutorial now essentially complete. Fixed also the file.

parent 130487b7
No related branches found
No related tags found
No related merge requests found
...@@ -12,6 +12,7 @@ This tutorial shows how to: ...@@ -12,6 +12,7 @@ This tutorial shows how to:
* use sets and recursive function to specify data structures * use sets and recursive function to specify data structures
* synthesize insertion into a sorted list * synthesize insertion into a sorted list
* synthesize sorting on lists * synthesize sorting on lists
* repair an incorrect function
We assume that the user is using the web interface. The We assume that the user is using the web interface. The
functionality is also available (possibly with less functionality is also available (possibly with less
...@@ -461,6 +462,9 @@ order. ...@@ -461,6 +462,9 @@ order.
Insertion into Sorted List Insertion into Sorted List
-------------------------- --------------------------
Consider the following specification of insertion into a sorted list,
which is a building block for an insertion sort.
.. code-block:: scala .. code-block:: scala
def sInsert(x : BigInt, l : List) : List = { def sInsert(x : BigInt, l : List) : List = {
...@@ -473,10 +477,14 @@ Insertion into Sorted List ...@@ -473,10 +477,14 @@ Insertion into Sorted List
} }
} ensuring {(res:List) => isSorted(res)} } ensuring {(res:List) => isSorted(res)}
Leon verifies that the returned list is indeed sorted. Note
how we are again using a recursively defined function to
specify another function.
Being Sorted is Not Enough Being Sorted is Not Enough
-------------------------- --------------------------
A function such as this one is correct. Note, however, that a function such as this one is also correct.
.. code-block:: scala .. code-block:: scala
...@@ -485,7 +493,8 @@ A function such as this one is correct. ...@@ -485,7 +493,8 @@ A function such as this one is correct.
Nil Nil
} ensuring {(res:List) => isSorted(res)} } ensuring {(res:List) => isSorted(res)}
So, our specification may be considered weak. So, our specification may be considered weak, because it does
not say anything about the elements.
Using Size in Specification Using Size in Specification
--------------------------- ---------------------------
...@@ -498,7 +507,7 @@ Consider a stronger additional postcondition property: ...@@ -498,7 +507,7 @@ Consider a stronger additional postcondition property:
Does it hold? If we try to add it, we obtain a counterexample. Does it hold? If we try to add it, we obtain a counterexample.
A correct strengthening, taking into account that the element A correct strengthening, taking into account that the element
may or may not already be in the list, is: may or may not already be in the list, is the following.
.. code-block:: scala .. code-block:: scala
...@@ -523,8 +532,9 @@ of the list. ...@@ -523,8 +532,9 @@ of the list.
} ensuring {(res:List) => } ensuring {(res:List) =>
isSorted(res) && content(res) == content(l) ++ Set(x)} isSorted(res) && content(res) == content(l) ++ Set(x)}
To compute content, in this example we use sets, even though To compute `content`, in this example we use sets (even
it might be better in general to use multisets. though in general it might be better in general to use bags
i.e. multisets).
.. code-block:: scala .. code-block:: scala
...@@ -537,25 +547,28 @@ it might be better in general to use multisets. ...@@ -537,25 +547,28 @@ it might be better in general to use multisets.
Sorting Specification and Running It Sorting Specification and Running It
------------------------------------ ------------------------------------
Specifying sorting is in fact very easy.
.. code-block:: scala .. code-block:: scala
def sortMagic(l : List) = choose{(res:List) => def sortMagic(l : List) = {
isSorted(res) && content(res) == content(l) ???[List]
} } ensuring((res:List) =>
isSorted(res) && content(res) == content(l))
We can execute it. We can execute such a sort.
.. code-block:: scala .. code-block:: scala
def mm = sortMagic(Cons(20, Cons(5, Cons(50, Cons(2, Nil))))) def mm = sortMagic(Cons(20, Cons(5, Cons(50, Cons(2, Nil)))))
obtaining the expected `Cons(2, Cons(5, Cons(20, Cons(50, Nil))))`. obtaining the expected `Cons(2, Cons(5, Cons(20, Cons(50, Nil))))`.
Note that the function actually removes duplicates from the input list.
Synthesizing Sort Synthesizing Sort
----------------- -----------------
By asking the system to synthesize the `choose` construct, By asking the system to synthesize the `choose` construct inside `magicSort`,
we may obtain a function such as the following, which gives we may obtain a function such as the following, which gives
us the natural insertion sort. us the natural insertion sort.
...@@ -572,15 +585,64 @@ us the natural insertion sort. ...@@ -572,15 +585,64 @@ us the natural insertion sort.
Going back and Synthesizing Insertion Going back and Synthesizing Insertion
------------------------------------- -------------------------------------
In fact, if we had a precise enough specification of insert, In fact, if we have a reasonably precise enough
we could have synthesized it from the specification. specification of insert, we can synthesize the implementation.
To try this, remove the body of `sInsert` and replace it
with `???[List]` denoting an unknown value of the given type.
.. code-block:: scala
def sInsert(x : BigInt, l : List) : List = {
require(isSorted(l))
???[List]
} ensuring {(res:List) =>
isSorted(res) && content(res) == content(l) ++ Set(x)}
Leon can then synthesize the missing part, resulting in a similar
body to the one we wrote by hand originally.
Repairing an Incorrect Function
-------------------------------
You may notice that synthesis can take a long time and fail.
Often we do produce some version of the program, but it is
not correct according to a specification. Consider the
following attempt at `sInsert`.
.. code-block:: scala .. code-block:: scala
def insertMagic(x: BigInt, l: List): List = { def sInsert(x : BigInt, l : List) : List = {
require(isSorted(l)) require(isSorted(l))
choose {(res: List) => l match {
isSorted(res) && content(res) == content(l) ++ Set[BigInt](x) case Nil => Cons(x, Nil)
case Cons(e, rest) => Cons(e, sInsert(x,rest))
} }
} } ensuring {(res:List) =>
isSorted(res) && content(res) == content(l) ++ Set(x)}
Leon reports a counterexample to the correctness. Instead of
trying to manually understand the counterexample, we can
instruct the system to **repair** this solution. If Leon can
reuse parts of the existing function, it can be much faster
than doing synthesis from scratch. Leon automatically finds
test inputs that it uses to localize the error and preserve
useful existing pieces of code. In this case, Leon repairs
the above function into the one equivalent to the original
one, by doing a case split and synthesizing two new cases,
resulting in the following equivalent function.
.. code-block:: scala
def sInsert(x : BigInt, l : List): List = {
require(isSorted(l))
l match {
case Nil => Cons(x, Nil)
case Cons(e, rest) =>
if (x < e) Cons(x, l)
else if (x == e) Cons(x, rest)
else Cons(e, sInsert(x, rest))
} ensuring { (res : List) =>
isSorted(res) && content(res) == content(l) ++ Set[BigInt](x) }
This completes the tutorial. To learn more, check the rest of this documentation and browse the examples provided with Leon.
...@@ -174,4 +174,15 @@ def max(x: Int, y: Int): Int = { ...@@ -174,4 +174,15 @@ def max(x: Int, y: Int): Int = {
// def mm = sortMagic(Cons(20, Cons(5, Cons(50, Cons(2, Nil))))) // def mm = sortMagic(Cons(20, Cons(5, Cons(50, Cons(2, Nil)))))
/*
def sInsert(x : BigInt, l : List) : List = {
require(isSorted(l))
l match {
case Nil => Cons(x, Nil)
case Cons(e, rest) => Cons(e, sInsert(x,rest))
}
} ensuring {(res:List) =>
isSorted(res) && content(res) == content(l) ++ Set(x)}
*/
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment