Postcondition for method to find min value of an array - dafny

I have a function to find the minimun value of an array
method arrayMin(a: array<int>) returns (m: int)
requires a.Length > 0;
ensures forall k :: 0 <= k < a.Length ==> a[k] >= m;
{
var i: nat := 1 ;
m := a[0] ;
while (i < a.Length)
invariant 1 <= i <= a.Length && forall k :: 0 <= k < i ==> a[k] >= m;
decreases a.Length - i;
{
if (a[i] < m) { m := a[i] ; }
i := i + 1 ;
}
}
When I try to call
var a := new int[5];
a[0], a[1], a[2], a[3], a[4] := 9, 4, 6, 3, 8;
var min := arrayMin(a);
assert min == 3;
Dafny is to able to verfiy the assertion. Are the post conditions not sufficient?

You are correct that the postcondition is not sufficient. In English, the postcondition says the following:
No element in the array a is below the value m returned.
But, this does not mean m has to be a value from the original array! For example, we can safely modify this line:
if (a[i] < m) { m := a[i] ; }
To be this:
if (a[i] < m) { m := a[i] - 1; }
And this program will still meet the postcondition.
Therefore to get your assertion to pass, you will a stronger postcondition. In particular, it needs to ensure that m is one of the elements contained in a.

Related

How to prove Loop Invariants in insertion?

I'm having trouble with writing the proper loop invariants for my insertion sort algorithm listed below. I am trying to prove that all items in the array before the current index is already sorted as insertion sort is supposed to do but Dafny is not recognizing my invariants properly.
method Sort(a : array<int>)
modifies a
ensures forall i,j :: 0 <= i < j < a.Length ==> a[i] <= a[j]
{
var i := 0;
while (i < a.Length)
invariant 0 <= i <= a.Length
invariant forall x,y :: 0 <= x < y < i ==> a[x] <= a[y]
{
var j := i - 1;
while (j >= 0 && a[j] > a[j + 1])
invariant forall k,l :: 0 <= k < l <i ==> a[k] <= a[l]
{
a[j], a[j + 1] := a[j + 1], a[j];
j := j - 1;
}
i := i + 1;
}
}
I've tried asserting that a[j] <= a[j+1] outside the loop but Dafny doesn't seem to think it's true despite it working fine inside of the loop after the swap. When I try using numbers outside the loop such as a[0] <= a[1], it doesn't verify either and I'm not sure why.
Your inner loop invariant doesn't seem right to me.
During iteration, it does not hold. For example
take following snapshot during insertion sort.
i = 3
j = 1
init arr = [5, 4, 3, 2, 1]
arr at start of inner loop = [3, 4, 5, 2, 1]
curr arr = [3, 4, 2, 5, 1]
You need certain book keeping facts so that it can verify.
Let's say that you are inserting element at i, into [0..(i-1)].
Consider extended slice [0..i], this slice is sorted unless
we are comparing with element we are currently inserting. First
invariant captures this. Second invariant which need to
be maintained is, assuming number currently being inserted is
at index j, slice [j..i] is sorted.
method sort(a: array<int>)
modifies a
ensures forall i, j :: 0 <= i < j < a.Length ==> a[i] <= a[j]
{
var i := 0;
while i < a.Length
invariant 0 <= i <= a.Length
invariant forall m, n :: 0 <= m < n < i ==> a[m] <= a[n]
{
var j := i - 1;
while j >= 0 && a[j] > a[j+1]
invariant -1 <= j <= i - 1
invariant forall m, n :: 0 <= m < n <= i && ((m != j + 1) && (n != j + 1)) ==> a[m] <= a[n]
invariant forall m :: j < m <= i ==> a[j+1] <= a[m]
{
a[j], a[j+1] := a[j+1], a[j];
j := j - 1;
}
i := i + 1;
}
}

dafny assertion violation when using the result of a method

I have written the program below to verify that an array is 'clean' of any particular element or not. I am having trouble asserting the result of the method. I keep getting an assertion violation when trying to assert the result of the method.
method Main (){
var a:= new int[3];
a[0], a[1], a[2] := 1,2,3;
var v := isClean (a, 1);
assert v == false;
}
method isClean (a : array <int>, key : int) returns (clean : bool)
requires a.Length > 0
{
var i := 0;
while (i < a.Length)
invariant 0 <= i <= a.Length
invariant forall k :: 0 <= k < i ==> a[k] != key
{
if (a[i] == key) {
clean := false;
return;
}
i := i + 1;
}
clean := true;
}
Dafny 2.3.0.10506
stdin.dfy(8,11): Error: assertion violation
Dafny program verifier finished with 2 verified, 1 error
You need to give an ensures clause on isClean. When Dafny verifies a program, it only looks at one method at a time. So when Dafny verifies Main, it does not look at the definition of isClean at all. Instead, it only looks at the requires and ensures clauses.
You already have the hard part of the proof done in the loop invariant. Basically you just need to modify a copy of that invariant so that it makes sense in the context of the caller, as an ensures clause, like this:
ensures clean <==> (forall k :: 0 <= k < a.Length ==> a[k] != key)
(Add that right below the requires clause on isClean.) In this ensures clause, clean refers to the named return value of the isClean method. If you add this clause, Dafny will still complain, because you are asking it to prove that a forall quantifier is false. That is equivalent to trying to prove an exists quantifier true, and requires an explicit "witness", which is a value of k that makes the body of the formula turn out true/false.
In this case, the intuitive reason why isClean returns false is because a[0] has the value 1, so a is not "clean" of 1. We can demonstrate this "witness" to Dafny by adding the assertion
assert a[0] == 1;
to the body of Main, right after the call to isClean.
For clarity, here is a complete version of the program that verifies:
method Main() {
var a := new int[3];
a[0], a[1], a[2] := 1,2,3;
var v := isClean (a, 1);
assert a[0] == 1;
assert v == false;
}
method isClean(a: array <int>, key: int) returns (clean: bool)
requires a.Length > 0
ensures clean <==> (forall k :: 0 <= k < a.Length ==> a[k] != key)
{
var i := 0;
while (i < a.Length)
invariant 0 <= i <= a.Length
invariant forall k :: 0 <= k < i ==> a[k] != key
{
if (a[i] == key) {
clean := false;
return;
}
i := i + 1;
}
clean := true;
}

Modifying an array in Dafny with postconditions

Trying to implement a fairly simple method, where you pass an empty array and place values into it (natural numbers).
The code runs fine, but a simple postcondition that ought to pass in my mind is throwing me errors.
method Main() {
var a := new int[5];
initialise(a);
}
method initialise(a: array<int>)
modifies a
requires a.Length > 0
ensures forall i :: 0 <= i < a.Length ==> a[i] == i
{
var i := 0;
while i < a.Length
invariant 0 <= i <= a.Length
decreases a.Length - i
{
a[i] := i;
i := i + 1;
}
}
Error:
A postcondition might not hold on this return path. Related location 1: Line: 10, Col: 8
You need to tell Dafny about the invariant maintained by the loop.
Once you add
invariant forall j :: 0 <= j < i ==> a[j] == j
the proof goes through.

What invariant do I miss in the BubbleSort proof?

I think I may need additional invariants to convince Dafny the code works.
I tried a few but failed to pass.
method BubbleSort(arr: array<int>)
ensures forall k, k' :: 0 <= k < k' < arr.Length ==> arr[k] <= arr[k']
modifies arr
{
var i := arr.Length;
while i > 0
invariant 0 <= i <= arr.Length
// Violating the invariant next line
invariant forall k, k' :: i <= k < k' < arr.Length ==> arr[k] <= arr[k']
decreases i
{
var j := 0;
while j < i - 1
invariant 0 <= j < i
invariant forall k :: 0 <= k < j ==> arr[j] >= arr[k]
decreases i - j
{
if arr[j] > arr[j+1] {
arr[j], arr[j+1] := arr[j+1], arr[j];
}
j := j + 1;
}
i := i - 1;
}
}
The invariant sorted(a , i , a . Length -1) has to be maintained when i is decreased. The invariant partioned(a, i) guarantees this, since it implies a[i-1] <= a[i]. The reason that it is stronger than this is that otherwise its invariance could not be proven.
Note: partitioned(a, i) says that a[k] <= a[l] for all k <= i and i < l .
I found this example of a dafny verified bubble sort among these notes.
https://www.cs.cmu.edu/~mfredrik/15414/lectures/17-notes.pdf
predicate sorted ( a : array <int > , l : int , u : int)
reads a
{
forall i , j :: 0 <= l <= i <= j <= u < a . Length ==> a [ i ] <= a [ j ]
}
predicate partitioned ( a : array <int > , i : int)
reads a
{
forall k , k' :: 0 <= k <= i < k' < a . Length ==> a [ k ] <= a [k']
}
method BubbleSort(a: array<int>) returns (b: array<int>)
requires a.Length!=0
modifies a
ensures sorted(a,0,a.Length-1)
{
var i:=0;
var j:=0;
var temp:=0;
var n:=a.Length;
i:=n-1;
b:=a;
while i>0
invariant i<0 ==> a.Length==0
invariant -1<=i<n
invariant sorted (a , i , a . Length -1)
invariant partitioned(a,i)
{
j:=0;
while j<i
invariant 0<=j<=i
invariant 0<=i<n
invariant sorted(a , i , a . Length -1)
invariant forall k :: 0 <= k <j ==> a[j] >= a[k]
invariant partitioned(a,i)
{ if a[j]>a[j+1]
{
temp:=a[j];
a[j]:=a[j+1];
a[j+1]:=temp;
}
j:=j+1;
}
i:=i-1;
}
b:=a;
}

What is the wrong with my code in dafny?

I try to use dafny to verify the correctness with my qsort function, but idk why there are verified failures about my code.
Here is my code:
method Testing (a: array<int>)
requires a.Length > 0
modifies a
{
qsort(a,0,a.Length-1);
var i :int := 0;
while(i<a.Length-1)
decreases a.Length - 1 - i
{
assert a[i] <= a[i+1];
i := i+1;
}
}
method qsort(a: array<int>,left: int,right: int)
requires left>=0 && right < a.Length
modifies a
decreases right - left
{
if (right > left)
{
var pivotValue: int := a[left];
var t: int := a[left];
a[left] := a[right-1];
a[right-1] := t;
var storeIndex: int := left;
var i :int := left;
while i < right - 1
invariant left <= storeIndex < right
decreases right - i
{
if a[i] < pivotValue
{
t := a[storeIndex];
a[storeIndex] := a[i];
a[i] := t;
storeIndex := storeIndex+1;
}
i := i+1;
}
t := a[right-1];
a[right-1] := a[storeIndex];
a[storeIndex] := t;
qsort(a,left,storeIndex);
qsort(a,storeIndex+1,right);
}
}
The errors are:
assertion violation
assert a[i] <= a[i+1];
This loop invariant might not be maintained by the loop.
invariant left <= storeIndex < right + 1
failure to decrease termination measure
qsort(a,left,storeIndex);
Thanks to the answer from #James Wilcox, I rewrote my code as:
method qsort(a: array<int>,left: int,right: int)
requires left>=0 && right <= a.Length
ensures (exists p | left<=p<right :: (forall k: int :: left < k < p ==> a[k] <= a[p]) && (forall j: int :: p < j < right ==> a[j] >= a[p]))
modifies a
decreases right - left
{
if (right > left)
{
var pivotValue: int := a[left];
var t: int := a[left];
a[left] := a[right-1];
a[right-1] := t;
var storeIndex: int := left;
var i :int := left;
while i < right - 1
invariant left <= storeIndex < right
invariant storeIndex <= i
decreases right - i
{
if a[i] < pivotValue
{
t := a[storeIndex];
a[storeIndex] := a[i];
a[i] := t;
storeIndex := storeIndex+1;
}
i := i+1;
}
t := a[right-1];
a[right-1] := a[storeIndex];
a[storeIndex] := t;
qsort(a,left,storeIndex);
qsort(a,storeIndex+1,right);
}
}
method Testing (a: array<int>)
requires a.Length > 0
modifies a
{
qsort(a,0,a.Length);
var i :int := 0;
while(i<a.Length-1)
decreases a.Length - 1 - i
{
assert a[i] <= a[i+1];
i := i+1;
}
}
But the postcondition of qsort may not hold, what can i do to correct it?
My final verified code:
method qsort(a: array<int>,left: int,right: int)
requires 0<= left <= right <= a.Length
requires 0 <= left <= right < a.Length ==> forall j:: left <= j < right ==> a[j] < a[right]
requires 0 < left <= right <= a.Length ==> forall j:: left <= j < right ==> a[left-1] <= a[j]
ensures forall j,k:: left <= j < k < right ==> a[j] <= a[k]
ensures forall j:: (0<= j < left) || (right <= j < a.Length) ==> old(a[j])==a[j]
ensures 0<= left <= right < a.Length ==> forall j:: left <= j < right ==> a[j] < a[right]
ensures 0< left <= right <= a.Length ==> forall j:: left <= j < right ==> a[left-1] <= a[j]
modifies a
decreases right - left
{
if (right > left)
{
var pivot := left;
var i :int := left + 1;
while i < right
invariant left <= pivot < i <= right
invariant forall j::left <= j < pivot ==> a[j] < a[pivot]
invariant forall j::pivot < j < i ==> a[pivot] <= a[j]
invariant forall j::0 <= j < left || right <= j < a.Length ==> old(a[j])==a[j]
invariant 0 <= left <= right < a.Length ==> forall j:: left <= j < right ==> a[j] < a[right]
invariant 0 < left <= right <= a.Length ==> forall j:: left <= j < right ==> a[left-1] <= a[j]
decreases right - i
{
if a[i] < a[pivot]
{
var count :=i -1;
var tmp:=a[i];
a[i] := a[count];
while (count>pivot)
invariant a[pivot] > tmp
invariant forall j::left <= j < pivot ==> a[j]<a[pivot]
invariant forall j::pivot< j < i+1 ==> a[pivot]<=a[j]
invariant forall j::0<=j<left || right <= j <a.Length ==> old(a[j])==a[j]
invariant 0 <= left <= right < a.Length ==> forall j:: left <= j < right ==> a[j] < a[right]
invariant 0 < left <= right <= a.Length ==> forall j:: left <= j < right ==> a[left-1] <= a[j]
{
a[count+1]:=a[count];
count:=count-1;
}
a[pivot+1]:=a[pivot];
pivot:=pivot+1;
a[pivot-1]:=tmp;
}
i := i+1;
}
qsort(a,left,pivot);
qsort(a,pivot+1,right);
}
}
method Testing (a: array<int>)
requires a.Length > 0
modifies a
{
qsort(a,0,a.Length);
var i :int := 0;
while(i<a.Length-1)
decreases a.Length - 1 - i
{
assert a[i] <= a[i+1];
i := i+1;
}
}
Your code may be correct, but Dafny typically needs some help in order to prove it.
Dafny will only reason about method calls by their postconditions (ensures clauses). Since your qsort method has no postcondition, Dafny will assume it can do anything. This explains why the Testing method fails to prove the assertion. If you add a postcondition to qsort, then you will have to prove it! That will be a good exercise!
Dafny reasons about loops in terms of their invariants. If a variable that is modified by the loop body is not mentioned in the loop invariant, Dafny assumes its value is arbitrary. Your invariant about storeIndex is not true if, somehow, i was smaller than storeIndex. You can fix this by adding the additional invariant storeIndex <= i to the loop. Then both invariants will pass.
The previous fix also fixes the termination problem.
In order to better understand how Dafny uses pre/postconditions and loop invariants to break down the verification problem, I suggest you read the guide. Then you can add a suitable postcondition to qsort and try to prove it using additional loop invariants. Feel free to ask more questions if you get stuck!
In your second version of your code, the postcondition seems too weak, since qsort is a sorting method, it seems the postcondition should be that the array is sorted. (Since it is recursive, actually the postcondition should only talk about the region of the array between left and right.) If you do this, then the assertion in Testing should pass. You will then still have some work to do to prove the postcondition for qsort, by adding invariants to the while loop inside it.

Resources