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;
}
}
Attempting to verify formally the following problem. https://leetcode.com/problems/count-equal-and-divisible-pairs-in-an-array/
Given a 0-indexed integer array nums of length n and an integer k, return the number of pairs (i, j) where 0 <= i < j < n, such that nums[i] == nums[j] and (i * j) is divisible by k.
function countPairs(nums: number[], k: number): number {
let count = 0;
for(let i = 0; i < nums.length-1; i++) {
for(let j = i+1; j < nums.length; j++) {
if(nums[i] == nums[j] && (i*j) % k == 0) {
count++;
}
}
}
return count;
};
I cannot figure out how to verify this silly thing. Maybe I'm missing a super simple description of the problem and I'm too focused on irrelevant details.
My strategy started with trying to compare the result to the cardinality of a matching set. However, getting dafny to believe the cardinality of the set matches during the two loops seems impossible.
function method satPairs(nums: seq<nat>, k: nat, a: nat, b: nat): bool
requires k > 0
requires a <= b < |nums|
{
nums[a] == nums[b] && (a*b) % k == 0
}
function matchPairs(nums: seq<nat>, k: nat): nat
requires k > 0
{
|set x,y | 0 <= x < y < |nums| && nums[x] == nums[y] && (x*y) % k == 0 :: (x,y)|
}
function method pairsI(nums: seq<nat>, k: nat, i: nat): set<(nat, nat)>
requires k > 0
requires 0 <= i < |nums|
ensures forall x,y :: 0 <= x < i && x <= y < |nums| && satPairs(nums, k, x, y) ==> (x,y) in pairsI(nums, k, i)
{
set x: nat,y:nat | 0 <= x < i && x <= y < |nums| && satPairs(nums, k, x, y) :: (x,y)
}
I also tried to setup invariants based on counts using methods which could count the matching pairs using a column at a time. However, I ran into no end of incompatible conditions maintaining the invariants before and after the while loop. I was hoping these helper functions could be defined recursively allowing some induction on the result to be used inside the two loops for each invariant, but it didn't work.
function countSeqPairs(nums: seq<nat>, k: nat, start: nat, stop: nat): nat
requires k > 0
requires start <= stop <= |nums|
decreases |nums|-start, |nums|-stop
{
if start > stop || stop >= |nums| || start >= |nums| then 0 else
if stop < |nums| then (if satPairs(nums, k, start, stop) then 1 + countSeqPairs(nums, k, start, stop+1) else countSeqPairs(nums, k, start, stop+1)) else countSeqPairs(nums, k, start+1, stop+2)
}
function countSeqSlice(nums: seq<nat>, k: nat, start: nat, stop: nat): nat
requires k > 0
requires start <= stop <= |nums|
decreases |nums| - stop
{
if start > stop || stop >= |nums| then 0
else if satPairs(nums, k, start, stop) then 1 + countSeqSlice(nums, k, start, stop+1) else countSeqSlice(nums, k, start, stop+1)
}
Here is the main method, with various non-working attempts at invariants.
method countPairs(nums: seq<nat>, k: nat) returns (count: nat)
requires k > 0
requires |nums| >= 2;
{
count := 0;
//ghost var cpairs: set<(nat, nat)> := {};
for i : nat := 0 to |nums|-2
invariant count >= 0
//invariant cpairs == pairsI(nums, k, i)
{
// ghost var occount := count;
// ghost var increment := 0;
for j : nat := i+1 to |nums|-1
invariant count >= 0
// invariant count == occount + increment
// invariant satPairs(nums, k, i, j) ==> increment == increment + 1
// invariant count == 0 || satPairs(nums, k, i, j) ==> count == count + 1
//invariant cpairs == pairsI(nums, k, i) + set z: nat | i+1 <= z <= j && satPairs(nums, k, i, z) :: (i, z)
{
// ghost var currcount := count;
// if nums[i] == nums[j] && (i*j)% k == 0 {
if i+1 <= j <= j && satPairs(nums, k, i, j) {
// increment := increment + 1;
//cpairs := {(i,j)}+cpairs;
count := count + 1;
}
}
}
}
It seems like there is no shorter description than the method itself for what is to be ensured. In addition to help with the above, I have three questions, generally how do you describe an invariant for something which is sort of manifestly arbitrary like this?
Secondly, what strategy can be used to handle loop invariants which are not true before the loop is run but are afterwards? It tends to be the initial condition is set to 0 or some other empty value, but then after the 0-th iteration of the loop it will be set to some value, and then the invariant fails. I keep running into this situation and it feels like there should be some sort of standard guard for it.
Finally, can or should ghost variables be used in method ensure statements?
Here is one way to do it.
function method satPairs(nums: seq<nat>, k: nat, a: nat, b: nat): bool
requires k > 0
requires a <= b < |nums|
{
nums[a] == nums[b] && (a*b) % k == 0
}
function matchPairsHelper(nums: seq<nat>, k: nat, bound: int): set<(int, int)>
requires k > 0
requires bound <= |nums|
{
set x,y | 0 <= x < bound && x < y < |nums| && satPairs(nums, k, x, y) :: (x,y)
}
function matchPairs(nums: seq<nat>, k: nat): set<(int, int)>
requires k > 0
{
matchPairsHelper(nums, k, |nums|)
}
function innerMatchPairsHelper(nums: seq<nat>, k: nat, outer: int, inner_bound: int): set<(int, int)>
requires k > 0
requires inner_bound <= |nums|
{
set y | 0 <= outer < y < inner_bound && satPairs(nums, k, outer, y) :: (outer,y)
}
method countPairs(nums: seq<nat>, k: nat) returns (count: nat)
requires k > 0
requires |nums| >= 2
ensures count == |matchPairs(nums, k)|
{
count := 0;
for i : nat := 0 to |nums|
invariant count == |matchPairsHelper(nums, k, i)|
{
for j : nat := i+1 to |nums|
invariant count == |matchPairsHelper(nums, k, i)| + |innerMatchPairsHelper(nums, k, i, j)|
{
assert innerMatchPairsHelper(nums, k, i, j+1) ==
(if satPairs(nums, k, i, j) then {(i, j)} else {}) + innerMatchPairsHelper(nums, k, i, j);
if satPairs(nums, k, i, j) {
count := count + 1;
}
}
assert matchPairsHelper(nums, k, i+1) == matchPairsHelper(nums, k, i) + innerMatchPairsHelper(nums, k, i, |nums|);
}
}
A few notes:
James's rule of set comprehensions: never use a set comprehension as part of any larger expression, but only as the body of a function that returns that set. This facilitates referring to the set multiple times in assertions without confusing Dafny.
James's rule of cardinalities: Dafny will never prove two cardinalities equal. You must manually ask it to prove two sets equal, and from there, it will conclude the cardinalities are equal.
I didn't really look at your invariants. I just tried to write down what the loops are doing. The outer loop is a loop over the x coordinate. So the invariant is that all the "right" pairs have been counted for all x coordinates smaller than i (for all values of y). Then for the inner loop, the invariant is that all the pairs for the exact x coordinate i have been counted up to j. I define two functions for these notions.
Other than that, just need to assert some set equalities in a few places.
To your other questions:
how do you describe an invariant for something which is sort of
manifestly arbitrary like this?
I'm not sure I understand the question. Are you asking "how do I discover the right loop invariants for this postcondition?" or "how do I state the postcondition when it seems like it is just as long as the code itself?"
what strategy can be used to handle loop invariants which are
not true before the loop is run but are afterwards?
I didn't need this in my solution, but if you do need it then the easiest way is something like i == 0 || <invariant that is true only after loop starts>. Generally I consider such invariants to have a "bad smell" and try to refactor to avoid them. Sometimes they are unavoidable though.
can or should ghost variables be used in method ensure statements?
Not sure I understand this one either. You cannot refer to any local variables of a method in an ensures clause except those that are returned by the method. If needed you can return a ghost variable as in
method Foo() returns (bar: int, ghost baz: int)
ensures ... mentions bar and baz just fine ...
Does that answer this one?
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.
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 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;
}