This is an example to learn Dafny.
method test5(x:array<int>,y:array<int>,n:int)
requires 0<=n
requires 0< x.Length
requires 0< y.Length
requires x[0]==y[0];
requires (x[0]>=0 ==> y[0]>=0)
requires (y[0]>= 0 ==> x[0]>= 0)
requires (x[0]+y[0]==0 || x[0]+y[0]>0);
modifies y;
modifies x;
ensures (x[0]==y[0]);
ensures (x[0]>0 ==> y[0]>0)
ensures (y[0] > 0 ==> x[0] > 0)
ensures (x[0]+y[0]==0 || x[0]+y[0]>0)
{ if (x[0]>0)
{x[0]:=x[0]- 1;
y[0]:=y[0]- 1;
}
}
Why does the verification fails?
Can Dafny show a counterexample?
Yes, you can get a counterexample. If you're using Visual Studio, simply click on the red circle where the error is. This brings up the Verification Debugger. If you're using VS Code, then press F7 (see https://marketplace.visualstudio.com/items?itemName=correctnessLab.dafny-vscode). That will reverify your program and show you some information from the counterexample.
In your case, these counterexamples show x and y as being equal and with x[0]==y[0]==1 initially. Indeed, starting from that initial state, test5 will not establish its declared postcondition.
Rustan
Related
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.
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.
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".
in my method that converts a sequence to an array, I get a recommendation by debugger of dafny for VSCode that I can not understand what it is.
method toArrayConvert(s:seq<int>) returns(res:array<int>)
requires |s|>0;
ensures |s| == res.Length;
ensures forall i::0<=i<res.Length ==> s[i] == res[i];
{
res :=new int[|s|];
forall i|0<=i && i<|s| {res[i]:=s[i];} /*on this line I get the following*/
// rewrite: forall i#inv: int {:trigger res[i#inv]} | 0 <= i#inv && i#inv < |s| { res[i#inv] := s[i#inv]; }
//Not generating triggers for "res[i#inv] == s[i#inv]".
return res;
}
This is not a warning or error, but just a diagnostic message from Dafny telling you how it plans to encode the forall assignment. You can safely ignore it.
I agree that the message is a little bit confusing, since it contains the string "Not generating triggers", when, in fact, it has already generated a trigger. This message is due to some internal technical details of how Dafny handles forall statements. I will file an issue to look at it.
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.