Loop Invariant for linear array search - loop-invariant

int i = 0
boolean answer = false
while (i < a.length) {
if a[i] == 0
answer = true
i = i + 1
where 'a' is an array of integers.
I'm doing a question paper where it has asked me what the loop invariant of this is, I have already established that the code works out whether the array contains a 0. But so far I can only think of the invariant as being
i <= a.length
and the question states to include variables i, a and answer in the invariant so I know this can't be right. I have not come across loop invariants involving booleans before and am confused, could anyone help explaining?

Here is an implementation of your loop in Microsoft Dafny with appropriate loop invariants specified:
method Main(a:array<int>) returns (answer:bool)
requires a != null
ensures answer <==> (exists i :: 0 <= i < a.Length && a[i] == 0)
{
var i:int := 0;
answer := false;
while (i < a.Length)
invariant 0 <= i <= a.Length;
invariant !answer ==> !(exists j :: 0 <= j < i && a[j] == 0)
invariant answer ==> (exists j :: 0 <= j < i && a[j] == 0)
{
if a[i] == 0 {
answer := true;
}
i := i + 1;
}
}
You can verify its correctness automatically in the online version of Dafny

Related

The same return but one function can not assert values properly, but other don't in Dafny

I have 2 functions, all is for checking how many values that are the same values and same index in 2 sequences, example:
requires there's no duplicates in the sequence
s:= [1,3,2,5,6]
u:= [2,3,4,5,1]
==> bullspec(s,u) = 2
So both my 2 functions return the right value but the assertion of one is true, other is false
Here are my 2 functions:
function bullspec(s:seq<nat>, u:seq<nat>): nat
requires 0 < |s| <= 10
requires 0 < |u| <= 10
requires |s| <= |u|
// Remove duplicates
requires forall i, j | 0 <= i < |s| && 0 <= j < |s| && i != j :: s[i] != s[j] && s[i] <= 10
requires forall i, j | 0 <= i < |u| && 0 <= j < |u| && i != j :: u[i] != u[j] && u[i] <= 10
{
if |s| == 1 then (
if s[0] in u && s[0] == u[0]
then 1 else 0
) else (
if s[|s|-1] in u && s[|s|-1]==u[|s|-1]
then (1 + bullspec(s[..|s|-1], u))
else bullspec(s[..|s|-1],u)
)
}
and
function bullspec2(s:seq<nat>, u:seq<nat>): nat
requires 0 < |s| <= 10
requires 0 < |u| <= 10
requires |s| <= |u|
// Remove duplicates
requires forall i, j | 0 <= i < |s| && 0 <= j < |s| && i != j :: s[i] != s[j] && s[i] <= 10
requires forall i, j | 0 <= i < |u| && 0 <= j < |u| && i != j :: u[i] != u[j] && u[i] <= 10
{
if |s| == 1 then (
if s[0] in u && s[0] == u[0]
then 1 else 0
) else (
if s[0] in u && s[0] == u[0]
then (1 + bullspec2(s[1..], u))
else bullspec2(s[1..], u)
)
}
I have a method Main below:
method Main()
{
var sys:seq<nat> := [4,2,9,3,1];
var usr:seq<nat> := [1,2,3,4,5];
assert bullspec(sys, usr) == 1; //Assertion might not hold
assert bullspec2(sys, usr) == 1; //This is good
}
The difference between 2 functions is the loop recusive, one start from the beginning, other start from the end, and somehow the backward make assertion works well
I try to write some ensures statements but there's no working.
First, please be aware that you are using a static analyzer, not a run-time tester. So the assertion might hold at run-time, but the static analyzer can't prove it.
The fact that the static analyzer can prove the second one is pretty astounding already. But I would always advise you switch the order of assertions, because an assertion after a first unproven one is always proved with the condition that the unproven one is true. In your case, it's still bullspec that can't be resolved, no matter what.
The reason why it currently fail to verify is that the axiom that indirectly serves at evaluating a sequence's length in the presence of a sequence literal without consuming fuel is not implemented yet. See this similar issue. That means that, for your second example, "evaluating" the function using axioms does not consume "fuel" (I'll go back to that later), but for the first failing example, it cannot unroll the function enough to do the computation.
What you can do for now is:
Create a lemma and prove it (hard)
lemma bullspec2equalsBullspec(s:seq<nat>, u:seq<nat>): nat
ensures bullspec2(sys, usr) == bullspec(sys, usr)
{
// TODO
}
and then write:
assert bullspec2(sys, usr) == 1;
assert bullspec(sys, usr) == 1 by {
bullspec2equalsBullspec(sys, usr);
}
Pass the sequence's length as a ghost parameter in bullspec
function bullspec(s:seq<nat>, u:seq<nat>, length: nat): nat
requires |s| == length
requires 0 < |s| <= 10
requires 0 < |u| <= 10
requires |s| <= |u|
// Remove duplicates
requires forall i, j | 0 <= i < |s| && 0 <= j < |s| && i != j :: s[i] != s[j] && s[i] <= 10
requires forall i, j | 0 <= i < |u| && 0 <= j < |u| && i != j :: u[i] != u[j] && u[i] <= 10
{
if length == 1 then (
if s[0] in u && s[0] == u[0]
then 1 else 0
) else (
if s[length-1] in u && s[length-1]==u[length-1]
then (1 + bullspec(s[..length-1], u,length-1))
else bullspec(s[..length-1],u,length-1)
)
}
/// ...
method Main()
{
var sys:seq<nat> := [4,2,9,3,1];
var usr:seq<nat> := [1,2,3,4,5];
assert bullspec2(sys, usr) == 1; //This is good
assert bullspec(sys, usr, 5) == 1; //This is good
}
verifies because now it can unroll the function applied to literals without fuel.
Unroll bullspec by verification debugging to see where it blocks.
If this assertion should hold, what should hold before? You can unroll the function's definition and assert intermediate results. If you assert something non-trivial that Dafny can finally prove, all the rest will be proven. I unrolled the function for 3 steps only.
method Main()
{
var sys:seq<nat> := [4,2,9,3,1];
var usr:seq<nat> := [1,2,3,4,5];
assert bullspec2(sys, usr) == 1; //This is good
var sys1 := sys[..|sys|-1];
var sys2 := sys1[..|sys1|-1];
var sys3 := sys2[..|sys2|-1];
var sys4 := sys3[..|sys3|-1];
var sys5 := sys4[..|sys4|-1];
assert bullspec(sys3, usr) == 1;
assert bullspec(sys2, usr) == 1;
assert bullspec(sys1, usr) == 1;
assert bullspec(sys, usr) == 1; //Assertion might not hold
}
Give more fuel to your function (best in your case)
By just changing the definition of your function, you can give it more fuel for the verifier to instantiate it. For your case, a fuel of 3 is sufficient.
function {:fuel 10} bullspec(s:seq<nat>, u:seq<nat>): nat

How to prove time complexity of bubble sort using dafny?

Is there a way in dafny to create an invariant specific to a single loop iteration? Below I'm trying to create a dafny program to upper bound the number of swaps of bubble sort. That value is stored in variable n. So I'd like to upper bound n by (a.Length * (a.Length - 1))/2. The reason being is that the maximum number of swaps that can occur is if the array is in the opposite order so then there must be n swaps on the first iteration of the inside loop and then n-1 swaps of the second iteration of the inside loop, etc. until the array is sorted. This is equivalent to 1+2+3+...+n-1+n = n(n-1)/2, which is precisely what I'm trying to show. I'm wondering if there's a method in dafny to upper bound the value of i-j or n in the code below based on the iteration of the inner loop. Is there such a way?
If not is there another method to upper bound this n value given the explanation provided above? Perhaps additional invariants I'm not thinking of?
method BubbleSort(a: array<int>) returns (n: nat)
modifies a
requires a != null
ensures n <= (a.Length * (a.Length - 1))/2
{
var i := a.Length - 1;
n := 0;
while (i > 0)
invariant 0 < a.Length ==> 0 <= i < a.Length;
decreases i;
{
var j := 0;
while (j < i)
invariant i - j <= old(i) - old(j);
invariant i < 0 && j < 0 ==> 0 <= n <= a.Length;
decreases i - j;
{
if(a[j] > a[j+1])
{
a[j], a[j+1] := a[j+1], a[j];
n := n + 1;
}
j := j + 1;
}
i := i -1;
}
}
I don't think you need to "create an invariant specific to a single loop iteration", but instead to rephrase your loop invariants in a way that they apply to all iterations.
Your English argument for why the complexity is bounded is a good one.
the maximum number of swaps that can occur is if [there are] n swaps on the first iteration of the inside loop and then n-1 swaps of the second iteration of the inside loop, etc. until the array is sorted. This is equivalent to 1+2+3+...+n-1+n = n(n-1)/2
All you need to do is to formalize this argument into Dafny. I suggest introducing a function for "the sum of all integers between a and b" and then using that function to phrase your loop invariants.

Am i calculating the big-o correctly?

Following loops:
for(var i = 0; i < A; i++) {
for(var j = 0; j < B; j++) {
for(var k = 0; k < C; k++) {
//not concerned with instructions here
}
}
}
As I understand each loop complexity is 2n+2 so based on that I calculate the complexity of above nested loops to be (2A+2)*((2B+2)*(2C+2)). Is this correct? if so, how do I get the big-o out of it?
Edit 1
I've learned so much about big-o since this question was asked and have found an interesting visualization that I'd like to place here in case others come across this thread. For detailed reference (way better than student textbooks) and original drawing check out Wikipedia. There are a variety of time complexities explained there.
Since the original question involves three nested loops each with a different n, then the big-o is O(A * B * C) as mentioned in the answers. The difficulty arises when we try to determine the big-o for something like this where A is an array of objects (aka hash in some languages). The algorithm itself non-sense and is for demonstration only (although I've been asked non-sense in interviews before):
var cache = {}
for(var i = 0; i < A.length; i++) {
var obj = A[i]
if(!obj.someProperty) {
continue;
}
else if(cache[obj.someProperty]) {
return obj;
}
else if(obj.someProperty === 'some value') {
for(var j = 1; j < A.length; j++) {
if(A[j].someProperty === obj.someProperty) {
cache[obj.someProperty] = obj.someProperty
break
}
}
}
else {
for(var j = i; j < A.length; j++) {
//do something linear here
}
}
}
The outer loop is O(A.length). For inner loops:
obj.someProperty does not exist, we have no complexity per theory.
obj.someProperty is in the cache, we have no complexity per theory.
obj.someProperty is equal to some value either of:
we have O(A.length - 1) where there are no duplicates
We have O(A.length - x) where A.length - x refers to a duplicate's index within A.
Everything else, we have O(log A.length)
At best performance this algorithm gives us O(3) when A[0] and A[1] are considered duplicates and A[0].someProperty === 'some value' as we'll have one iteration for outer loop and one iteration of inner loop (3.2 A.length - x = index 1, finally 3rd iteration returns cached value breaking out of the outer loop entirely. At worse we'll have O(A.length log A.length) as the outer loop and inner loop at 4 are exhausted when no object has someProperty === 'some value'.
To "optimize" this algorithm we can simply write as follows:
for(var i = 0; i < A.length; i++) {
if(A[i].someProperty === 'some value') {
return obj
}
else {
for(var j = i; j < A.length; j++) {
//do something linear here
}
}
}
The outermost for-loop runs a total of A times. For each iteration, the second-level for-loop runs B times, each time triggering C iterations of the omitted instructions.
Thus, the time complexity is O(A * B * C).
Constants are ignored while calculating the time complexity
O((2A+2)*((2B+2)*(2C+2)))
=> O((2A)(2B)(2C))
=> O(8*ABC)
=> O(ABC)

Verifying sum of sequence under a condition in dafny

I am having a problem with getting an invariant to be be maintained in dafny. The invariant is trying to maintain that a total is equal to the a recursive sum of elements in a sequence that match a given condition. What do i need to add/change to get this to verify. Here is my attempt:
datatype MovieTitle = A | B | C
class Movie {
var title: MovieTitle;
var run_time: int;
predicate Valid()
reads this
{
run_time >= 0
}
constructor(mt: MovieTitle, rt: int)
requires rt >= 0;
ensures Valid();
modifies this;
{
title := mt;
run_time := rt;
}
}
function movieSum(s: seq<Movie>, mt: MovieTitle, i: int): int
requires 0 <= i <= |s|;
decreases s, i;
reads s;
reads set x | x in s[..];
{
if |s| == 0 || i == 0 then 0
else if s[0].title == mt then s[0].run_time + movieSum(s[1..], mt, i - 1)
else movieSum(s[1..], mt, i - 1)
}
lemma {:induction s, mt, i} movieSumLemma(s: seq<Movie>, mt: MovieTitle, i: int)
requires 0 <= i < |s|;
ensures s[i].title == mt ==> (movieSum(s, mt, i) + s[i].run_time == movieSum(s, mt, i + 1)) &&
s[i].title != mt ==> (movieSum(s, mt, i) == movieSum(s, mt, i + 1));
decreases s;
{
}
method SumRuntimes(s: seq<Movie>)
{
var total := 0;
var i := 0;
while i < |s|
invariant 0 <= i <= |s|;
invariant total == movieSum(s, A, i);
decreases |s| - i;
{
if s[i].title == A {
total := total + s[i].run_time;
movieSumLemma(s, A, i);
}
i := i + 1;
}
}
Here invariant total == movieSum(s, A, i); won't hold. Any help in getting this to verify is appreciated. Thank you!
The problem is in function movieSum. You're both chopping off the initial element of s in the recursive call and decrementing i. That will not produce the sum of all the mt-movie elements.
You don't need the lemma. But its postcondition doesn't say what you may think it says. It currently has the form
ensures A ==> B && !A ==> C
The 2-character-wide && has higher binding power than the 3-character-wide ==>. So, your postcondition is saying
ensures A ==> ((B && !A) ==> C)
which simplifies to true. Instead, you want
ensures (A ==> B) && (!A ==> C)
which you can also write on two lines (for better readability):
ensures A ==> B
ensures !A ==> C
Your program also has a number of redundant decreases clauses and :induction attributes. I'm guessing you have added these from the blue information squiggles in the IDE. The blue squiggles are just FYI--Dafny is just trying to tell you what its default settings are.
It seems you may be using a very version of Dafny, or I expected you should have got a warning about the deprecated modifies this on the constructor.

Dafny, post condition does not hold after loop

In the following method, Dafny reports that the postcondition might not hold, even though I am quite sure that it does.
method toArrayConvert(s:seq<int>) returns (a:array<int>)
requires |s| > 0
ensures |s| == a.Length
ensures forall i :: 0 <= i < a.Length ==> s[i] == a[i] // This is the postcondition that might not hold.
{
a := new int[|s|];
var i:int := 0;
while i < |s|
decreases |s| - i
invariant 0 <= i <= |s|
{
a[i] := s[i];
i := i + 1;
}
return a; // A postcondition might not hold on this return path.
}
Indeed, the postcondition does always hold, but Dafny cannot tell!
That's because you're missing a loop invariant annotation such as
invariant forall j :: 0 <= j < i ==> s[j] == a[j]
After adding that line to the loop, the method verifies.
For more explanation about why Dafny sometimes reports errors on correct programs, see the (brand new) FAQ. For more about loop invariants, see the corresponding section in the rise4fun guide.