Currently, I'm trying to implement an AVL-Tree in Dafny.
Each subnode and the node itself is part of the nodes Repr:set.
After each change in the tree, an update method gets called to update the Repr set.
The first assert shows that new_node is not part of the Repr set of
the left node.
The second assert shows that this condition is still correct after
I assigned the Repr set of the left subnet to the node.
The third assert fails after I added the node to the Repr set.
I don't understand why the last assert fails, as the set which is checked has not been altered.
if(new_node.left != null && new_node.right == null) {
assert new_node !in new_node.left.Repr;
new_node.Repr := new_node.left.Repr;
assert new_node !in new_node.left.Repr;
new_node.Repr := new_node.Repr + {new_node};
assert new_node !in new_node.left.Repr;
new_node.Contents := {new_node.data} + new_node.left.Contents;
}
The third assertion does not hold if new_node == new_node.left. Have you excluded that possibility?
Related
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".
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
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.