I am trying to create a Dafny program that returns true if and only if, A contains no duplicates.
This is what I have so far, however the invariant invariant r <==> (forall j :: 0 <= j < i && j != i ==> A[j] != A[i]); says that it will not hold on entry.
Any advice on what I am doing wrong?
`method CheckArr1(A: array<int>) returns (r: bool)
requires A.Length > 0
ensures r <==> (forall i, j :: 0 <= i < A.Length && 0 <= j < A.Length && i != j ==> A[i] != A[j]);
{
var i := 0;
r := true;
while i < A.Length
decreases A.Length - i;
invariant i <= A.Length;
invariant r <==> (forall x, y :: 0 <= x < i && 0 <= y < i && x != y ==> A[x] != A[y]);
{
var j := 0;
while j < i
decreases i - j;
invariant j <= i;
invariant r <==> (forall j :: 0 <= j < i && j != i ==> A[j] != A[i]);
{
r := (r && (A[j] != A[i]));
j := j + 1;
}
i := i + 1;
}
}`
The "invariant doesn't hold on entry" error is for the declared invariant
r <==> (forall j :: 0 <= j < i && j != i ==> A[j] != A[i])
of the inner loop. On entry to the that loop, j is 0, so the condition that needs to hold on entry to the inner loop is
r <==> (0 <= 0 < i && 0 != i ==> A[0] != A[i])
which we can simplify to
r <==> (0 < i ==> A[0] != A[i]) // (*)
There is no reason to believe that r will hold this value on entry to the inner loop. All you know inside the body of the outer loop is that
r <==> (forall x, y :: 0 <= x < i && 0 <= y < i && x != y ==> A[x] != A[y]) // (**)
which says that r tells you whether or not there are any duplicates within the first i elements. Condition (*) says something about a[i], whereas (**) does not say anything about a[i].
From your current program, it would be easier if you changed the inner loop to use a different variable, say s, to achieve the invariant you have given. That is, use the invariant
s <==> (forall j :: 0 <= j < i ==> A[j] != A[i])
Then, after the inner loop, update r using the value you computed for s.
Related
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;
}
}
I'm following the tutorials here and the code seems correct but when I test using assertions I get an error!
Running the program prints the correct answer, but the assertions seem to be paradoxical. When a show the counter examples it seems that -1 is considered even though it shouldn't be.
method binarySearch(a:array<int>, key:int) returns (r:int)
requires forall i,j :: 0 <= i <= j < a.Length ==> a[i] <= a[j]
ensures 0 <= r ==> r < a.Length && a[r] == key
ensures r < 0 ==> forall i :: 0 <= i < a.Length ==> a[i] != key
{
var lo, hi := 0, a.Length;
while (lo < hi)
invariant 0 <= lo <= hi <= a.Length
invariant forall i :: 0 <= i < lo ==> a[i] < key
invariant forall i :: hi <= i < a.Length ==> a[i] > key
decreases hi - lo;
{
var mid := (lo + hi) / 2;
if key < a[mid]
{
hi := mid;
}
else if key > a[mid]
{
lo := mid + 1;
}
else
{
return mid;
}
}
return -1;
}
// tests
method Main()
{
var a := new int[6][1,2,3,4,5,6];
var r := binarySearch(a, 5);
assert r == 4; // fails
assert r != 4; // also fails
}
Is this a bug or am I missing something?
Dafny uses post-condition of method to reason about result of method call.
Here post-condition is
If r is between 0 to array length, element at r is equal to key
If r is less than 0, it is not in array.
Dafny doesn't know which of these is vacuous, but you can hint it.
Guarding assert r == 4 with if r >= 0 && r < a.Length will make it verify.
Alternatively after adding assert a[4] == 5 before assert r == 4, verification will go through.
Reason for strange error is after call to binary search, these are facts known to dafny
assert (r < 0) || (0 <= r < 6)
assert (r < 0) ==> forall i :: 0 <= i < a.Length ==> a[i] != 5
assert (0 <= r < 6) ==> a[r] == 5
Using these it can neither prove r == 4 nor r != 4. Dafny doesn't seem to propagate information like assert a[0] == 1 etc by default to prover, you have to explicitly ask for it.
ghost method lemma1(A:array<int>)
requires A.Length>2
requires forall i:: 0 <= i-1 < i < A.Length ==> A[i-1] <= A[i]
ensures A[0] <= A[1] <= A[2]
{
}
the code above works, but the following doesn't work
ghost method lemma2(A:array<int>)
requires A.Length>2
requires forall i:: 0 <= i-1 < i < A.Length ==> A[i-1] <= A[i]
ensures A[0] <= A[2]
{
}
ghost method lemma3(A:array<int>)
requires A.Length>2
requires forall i:: 0 <= i-1 < i < A.Length ==> A[i-1] <= A[i]
ensures forall i,j :: 0 <= i < j < A.Length ==> A[i] <= A[j]
{
}
Problem: A postcondition might not hold on this return path.
What extra preconditions does it require?
No extra preconditions are required here. Dafny (more correctly underlying Z3) uses heuristics to prove. It looks like dafny need some help here to figure out proofs as heuristics are not able to figure out by itself. You can ask dafny to assert things which makes it first prove it and then add it as part of knowledge base for applying further heuristics to prove latter goals.
ghost method lemma2(A:array<int>)
requires A.Length>2
requires forall i:: 0 <= i-1 < i < A.Length ==> A[i-1] <= A[i]
ensures A[0] <= A[2]
{
assert A[0] <= A[1];
assert A[1] <= A[2];
}
For second code snippet, a while loop with single assert inside it is enough to guide dafny towards proof. Earlier loop iteration has proved that forall m, n :: 0 <= m < n < i ==> A[m] <= A[n]. Current loop iteration need to prove forall m, n :: 0 <= m < n < i + 1 ==> A[m] <= A[n] for which assert A[i-1] <= A[i] hint is enough for dafny.
ghost method lemma3(A:array<int>)
requires A.Length>2
requires forall i:: 0 <= i-1 < i < A.Length ==> A[i-1] <= A[i]
ensures forall i,j :: 0 <= i < j < A.Length ==> A[i] <= A[j]
{
var i := 0;
while i < A.Length
invariant i <= A.Length
invariant forall m, n :: 0 <= m < n < i ==> A[m] <= A[n]
{
if i >= 1 {
assert A[i-1] <= A[i];
}
i := i + 1;
}
}
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;
}
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.