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.
Related
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.
For an array in Dafny, what's the difference between old(a[0]) and old(a)[0]?
A method modifies an array 'a' by adding 1 to the first element. At the conclusion of the method, what is the value of old(a[0]) and old(a)[0]?
Good question! Yes, they are different.
When an expression is evaluated inside an old, all of its heap dereferences refer to the heap at the beginning of the method. Anything that is not a heap dereference is not affected at all by old.
In your example a[0] is a heap dereference, so old(a[0]) gets the 0-th value of the array "pointed to" by a in the old heap.
However, a by itself is not a heap dereference, it's just a parameter to the local function whose value never changes. So old(a) == a. One way to think about this is that a stores the "address" of the array, and that address doesn't change over the lifetime of the method.
Now that we know old(a) == a, it follows that old(a)[0] == a[0]. In other words, the old has no effect in your second example.
Here is a small example to demonstrate:
method M(a: array<int>)
requires a.Length >= 2
requires a[0] == 1
modifies a
ensures a[1] == old(a[0]) // so far so good
ensures old(a) == a // perhaps surprising: Dafny proves this!
ensures a[1] == old(a)[0] // but this is not necessarily true,
// because old(a)[0] actually means the same thing as a[0]
{
a[1] := 1;
}
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.
Let's say I have the following class:
class Testing {
ghost var myGhostVar: int;
method Init()
modifies this
ensures this.myGhostVar == -1
{
this.myGhostVar := -1;
assert this.myGhostVar == -1;
}
method MyTestingMethod(list: array<int>, t: int)
modifies this
requires list.Length > 1
requires this.myGhostVar == -1
requires t == -1
ensures MyPredicate(list, myGhostVar)
ensures this.myGhostVar < list.Length
{
this.Init();
assert this.myGhostVar < 0;
assert list.Length > 0;
assert this.myGhostVar < list.Length;
}
predicate MyPredicate(list: array<int>, startIndex: int)
requires startIndex < list.Length
{
true
}
}
For some reason, Dafny say that the call to MyPredicate(...) might not hold. But if I instead use t as the argument instead of myGhostVar; Dafny has no complaints.
Both have the same requires predicate which makes it all a bit confusing. Is it something that I am missing when using ghost variables?
It's not an issue with ghost variables - you could check and see that you'd have the same issue if you removed the ghost keyword.
The issue is that to call MyPredicate(list, myGhostVar), you need to satisfy the pre-condition, which here would be myGhostVar < list.Length.
You actually have this condition in your ensures clause, which is great!
ensures MyPredicate(list, myGhostVar)
ensures this.myGhostVar < list.Length
But your call to MyPredicate is before the condition you need. Try flipping around the order:
ensures this.myGhostVar < list.Length
ensures MyPredicate(list, myGhostVar)
And you should see that Dafny stops complaining.
In general, Dafny will try to prove that the pre-conditions within an ensures clauses hold using the previous ensures clauses.
Both have the same requires predicate which makes it all a bit confusing. Is it something that I am missing when using ghost variables?
Here, I think you're asking about the clauses
requires this.myGhostVar == -1
requires t == -1
and wondering why it can't use the this.myGhostVar == -1 pre-condition to prove this.myGhostVar < list.Length.
The answer is that the this.myGhostVar == -1 is a pre-condition, meaning Dafny knows that it holds on entry, but the value of this.myGhostVar might change during the execution of the method. So that doesn't help Dafny when checking the post-condition.
Note that Dafny doesn't look at the body of its methods when checking the well-formedness of its post-conditions. For that purpose, Dafny only knows what you tell it in the method signature.
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.