In exercise_1a what could be done to make the assertion cnt > 0 valid?
method exercise_1a(n: int)
requires n > 0
{
var idx := 0;
var cnt := 0;
while idx < n
decreases n - idx
{
idx := idx + 1;
cnt := cnt + 1;
}
assert idx > 0; // valid
assert cnt > 0; // *** invalid ***
}
Surprisingly, this version invalidates the assertion idx > 0, as well:
method exercise_1b(n: int)
requires n > 0
{
var idx := 0;
var cnt := 0;
while idx < n && cnt < n
decreases n - idx
decreases n - cnt
{
idx := idx + 1;
cnt := cnt + 1;
}
assert idx > 0; // *** invalid ***
assert cnt > 0; // *** invalid ***
}
In your first snippet of code, when the while loop exits, all Dafny knows is that !(idx < n). Therefore, it can infer that idx > 0. However, Dafny doesn't know anything about the cnt variable at this point, so your second assertion fails.
In the second snippet of code, when the while loop exits, all Dafny is knows is that !(idx < n && cnt < n) (again, the negation of the condition on the while loop). This is equivalent to idx > n || cnt > n. From there, Dafny could infer idx > 0 || cnt > 0, but it wouldn't be able to prove either idx > 0 or cnt > 0 on its own.
To fix this, you need to add some relation between the variables cnt and idx that Dafny could check and then use.
while idx < n && cnt < n
decreases n - idx
decreases n - cnt
invariant idx == cnt
{
idx := idx + 1;
cnt := cnt + 1;
}
The extra invariant line tells Dafny check that idx == cnt will hold across every iteration of the loop, and then it can use that fact at the end. However, Dafny would not know to bring the fact idx == cnt under consideration unless you tell it to do so.
(As a side note, you'll see that Dafny is able to determine, on its own, that n > 0 holds at the end of the while loop, even while you don't specify it explicitly as in invariant. This is because n is not modified in the body of the while loop, so Dafny will automatically carry across the fact that n > 0 from the beginning.)
Related
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.
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;
}
}
This is a simple segregating 0s and 1s in the array problem. I can't fathom why the loop invariant does not hold.
method rearrange(arr: array<int>, N: int) returns (front: int)
requires N == arr.Length
requires forall i :: 0 <= i < arr.Length ==> arr[i] == 0 || arr[i] == 1
modifies arr
ensures 0 <= front <= arr.Length
ensures forall i :: 0 <= i <= front - 1 ==> arr[i] == 0
ensures forall j :: front <= j <= N - 1 ==> arr[j] == 1
{
front := 0;
var back := N;
while(front < back)
invariant 0 <= front <= back <= N
invariant forall i :: 0 <= i <= front - 1 ==> arr[i] == 0
// The first one does not hold, the second one holds though
invariant forall j :: back <= j < N ==> arr[j] == 1
{
if(arr[front] == 1){
arr[front], arr[back - 1] := arr[back - 1], arr[front];
back := back - 1;
}else{
front := front + 1;
}
}
return front;
}
On entry to your method, the precondition tells you
forall i :: 0 <= i < arr.Length ==> arr[i] == 0 || arr[i] == 1
So, at that time, it is known that all array elements are either 0 or 1. However, since the array is modified by the loop, you must mention in invariants all the things you still want to remember about the contents of the array.
In different words, to verify that the loop body maintains the invariant, think of the loop body as starting in an arbitrary state satisfying the invariant. You may have in your mind that array elements remain 0 or 1, but your invariant does not say that. This is why you're unable to prove that the loop invariant is maintained.
To fix the problem, add
forall i :: 0 <= i < arr.Length ==> arr[i] == 0 || arr[i] == 1
as a loop invariant.
Rustan
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.
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.