Dafny loop invariant fails even though invariant assertions work. Is this a small bug? - dafny

Hi for teaching I am setting up a mass of simple dafny questions. Mostly going fine, but... Either I have missed some detail about loop invariants in Dafny or this is a weakness/bug?
method whS(a:int) returns ()
{ var i:int := 0;
while (i<a )
decreases a-i
invariant i<= a
{ assert i<= a;
i:=i+1;
assert i<= a;
}
}
The method fails to verify with error "invariant might not hold on entry" Yet with the invariant commented out the method verifies even with the assertions.

A good way to remember the answer to the question "when must the loop invariant be true?" in Dafny is "whenever the loop condition would be evaluated".
So the loop invariant needs to be true before the first iteration of the loop, even if the body would never execute, because the loop condition will be evaluated to determine whether to enter the loop at all. The invariant also needs to be true after the last iteration, because again, the loop condition will be evaluated to determine whether to execute another iteration.
In your example, if the argument a to the method is negative, then the loop invariant is false before the first time the loop condition is evaluated. An example of an invariant that would work for this loop would be something like a >= 0 ==> i <= a.

many thanks James
I can see that I misunderstood how dafny implemented loop invariants. Wrapping the while in an if statement now verifies and gives me the semantics that I thought while on its own had.
method whS(a:int) returns ()
{ var i:int := 0;
if (i<a) {
while (i<a )
decreases a-i
invariant i<= a
{ i:=i+1;
}
}
}
Thanks again james . I will avoid this mistake when I give my lecture on Tuesday.

Related

How do I prove the while loop terminates in Dafny?

I'm new to Dafny and learning it. For the following Dafny code
method Dummy(s: seq<nat>) returns (ret: nat)
requires forall k :: 0 <= k < |s| ==> s[k] > 0
{
ret := 0;
var se := s;
while |se| > 0
{
// Pick a random index of the seq.
var i :| 0 <= i < |se|;
if se[i] == 1 {
// Remove the seq element if it is 1.
se := se[..i] + se[i + 1..];
} else {
// Replace the seq element with 3 occurences of 1.
se := [1, 1, 1] + se[..i] + se[i + 1..];
}
}
return;
}
I'm seeing complaints which are
decreases |se| - 0Dafny VSCode
cannot prove termination; try supplying a decreases clause for the loopDafny VSCode
I know that to prove termination, generally I should provide a decreases clause. I just cannot find what to put in the decreases clause in this case. Simply putting
decreases |se|
there will not work, as in the else part of the if statement the seq size may actaully increase.
If this is a pen and paper proof, I would like to argue that for any element that is 1, it will be removed; and for any element that is greater than 1, it will be replaced by 3 occurences of 1, which will still be removed finally. Hence se will be empty in the end and loop terminates.
How do I turn it to codes that Dafny would agree?
The key is to introduce a function that I call "total potential" that computes how many iterations the loop will take to process the list. Its definition is "for every 1 in the list, add 1 to the total potential, for every non-1 in the list, add 4 to the total potential". Then, we will make the decreases clause the total potential. This way, when a 1 gets removed, the potential goes down by 1, and when a non-1 gets removed and replaced by three ones, the potential also goes down by 1.
There is some additional work to convince Dafny that the total potential actually decreases on each loop iteration. You will need a few lemmas about how the total potential relates to slicing and appending sequences.

Dafny iterator: precondition and modifes clause violated

Dafny shows multiple errors when calling MoveNext() on an iterator that does nothing:
iterator Iter()
{}
method main()
decreases *
{
var iter := new Iter();
while (true)
decreases *
{
var more := iter.MoveNext();
if (!more) { break; }
}
}
The errors are on the call to iter.MoveNext():
call may violate context's modifies clause
A precondition for this call might not hold.
There is no modifies clause for main or Iter, and there is no precondition for Iter. Why is this program incorrect?
You need the following invariant on the loop
invariant iter.Valid() && fresh(iter._new)
Then your program verifies. As usual, there's nothing wrong (dynamically) with your program, but you can have false positives at verification time due to missing annotations.
As far as I know, this invariant is always required when using iterators.
(A little) More information about iterators can be found in the Dafny Reference, in Chapter 16. (At least, enough information for me to remember the answer to this question.)

What wrong with my dafny method. simple method postcondition might not hold

I am new to Dafny, and I am trying to write a code to computer 5*m-3*n without using *.
Can anybody tell me what wrong with my code? I think it is the invariant and decrease.
method CalcTerm(m: int, n: nat) returns (res: int)
ensures res == 5*m-3*n;
{
var m1: nat := abs(m);
var n1: nat := n;
res := 0;
while (m1!=0)
invariant m1>=0
decreases m1
{
res := res+5;
m1 := m1-1;
}
if (m<0) { res := -res; }
while (n1!=0)
invariant n1 >= 0
decreases n1
{
res := res-3;
n1 := n1-1;
}
}
But it keep saying that:
A postcondition might not hold on this return path. 29 2
You're right that the issue has to do with loop invariants. I recommend reading the two sections of the Guide on Assertions and Loop Invariants.
Dafny "summarizes" the effect of a loop using only its invariants. So after the second loop in your method, Dafny will only know that n1 >= 0 and that the loop has terminated, so actually n1 == 0. This is not enough information to prove your postcondition: you need a stronger invariant. Here's one that might help you make progress
invariant res == 5 * m - 3 * (n - n1)
This invariant computes the value of res in terms of how many iterations of the loop have executed so far (n - n1). If you add this invariant to the second loop, you'll get a new error (progress!) that says it might not hold on entry. This means that Dafny is able to prove your postcondition, but not able to establish that the new invariant is true after the first loop is done. This is again because the invariant on the first loop is too weak.
Maybe this gives you enough information to try coming up with another invariant for the first loop on your own. Feel free to ask more questions here if you get stuck.

Assertion fails after modifying another array in Dafny

I ran into a weird problem in Dafny. I tried to extract it as much as possible here: https://rise4fun.com/Dafny/F7sK
The thing is, after the modification of truthAssignment, stack.valid fails even if stack.valid doesn't know about truthAssignment.
assert stack.valid();
truthAssignment[variable] := 1;
assert stack.valid(); // assertion violation
The reason Dafny fails to verify the assertion assert stack.valid(); is the last conjunct in the body of valid():
(forall c :: c in contents ==>
exists i,j :: 0 <= i < stack.Length
&& 0 <= j < |stack[i]|
&& stack[i][j] == c)
It has the form forall ... exists ..., and proving such a condition to be invariant is difficult for the verifier. Once you have figured out that this is the part of valid() that the verifier cannot prove (which you can do by, for example, manually inlining the definition of valid() in place of your assertion), then the solution is to give the verifier some assistance.
When the verifier--or a human, for that matter--tries to prove something for the form forall c :: P(c), then it makes up an arbitrary c and then tries to prove the "P(c)" for it. (Logicians call this rule "universal introduction".) Easy. Then, to prove something of the form exists i,j :: Q(i,j), the verifier looks for a witness to the "Q(i,j)". (This is known as "existential introduction".) Here, the verifier is not particularly creative and often needs help. Sometimes, you have to figure out some i and j yourself and then assert Q(i,j). Other times, it suffices to just mention some component of the needed Q(i,j) and the verifier will then figure out the rest. Exactly what to do can be a trial-and-error process.
What makes James's remedy above work is the fact that it mentions stack.stack[..] after the update truthAssignment[variable] := 1;. This tickles the verifier in a way that lets it see the light. Just writing the trivially proved assertion:
assert 0 <= |stack.stack[..]|; // mentioning stack.stack[..] helps the verifier in the next line
after the update also works in this case.
Rustan
The following verifies for me:
assert stack.valid();
ghost var old_stack := stack.stack[..];
truthAssignment[variable] := 1;
assert stack.stack[..] == old_stack;
assert stack.valid();
I don't really understand why this works, but it falls under the general category of "extentional equality for sequences is hard for Dafny".

Sorted post-condition doesn't hold

What I want to do in the method is simply to overwrite the previous array and fill it with number that are sorted, however dafny says that the post-condition does not hold and I can't for the life of me figure out why.
I'm guessing I need to add some invariant to the loop but since they are checked before entering the loop I don't know how to put a valid invariant.
method sort2(a : array<int>)
modifies a;
requires a != null && a.Length >= 2;
ensures sorted2(a[..]);
{
var i := a.Length - 1;
while (i >= 0)
decreases i
{
a[i] := i;
i := i - 1;
}
}
predicate sorted2(s: seq<int>)
{
forall i :: 1 <= i < |s| ==> s[i] >= s[i-1]
}
My previous attempt was just to reinitialize a but dafny apparently doesn't allow that inside methods.
You do need a loop invariant in order to prove this program correct.
See Section 6 of the Dafny Tutorial for a general introduction to loop invariants and how to come up with them.
In this case, a good loop invariant will be something like sorted(a[i+1..]), which says that the part of the array after index i is sorted. That way, when the loop terminates and i is zero, you'll know that the whole array is sorted.
You will also need one or two more loop invariants describing bounds on i and the elements of the array itself.

Resources