Assertion fails after modifying another array in Dafny - 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".

Related

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

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.

Dafny as a SAT-QBF solver is not giving right results

I am trying to get the habit to use Dafny as a friendly SAT-QBF solver for some simple formulae, since doing it in, for instance, Z3 is too uncomfortable.
The context for this is that I have implemented Cooper's algorithm for quantifier elimination and, when all the variables are bounded, it can be used as a decision procedure: therefore, I want to know which is the result that I should get before executing it.
However, I encountered a problem in Dafny.
Let us raise, for instance, this formula (written in Dafny):
assert forall x_1: int :: exists y_1: int :: forall x_2: int :: exists y_2 : int
:: (y_2<y_1) && (x_2<y_2) && (x_1<x_2);
In my Cooper, it returns False, while Dafny returns assertion violation (along with the typical triggerswarnings), which I interpret as False too. Okay, so no problem with this.
But if I raise:
assert exists x_1: int :: exists y_1: int :: exists x_2: int :: exists y_2 : int
:: (y_2<y_1) && (x_2<y_2) && (x_1<x_2);
In my Cooper, it returns True, while Dafny also returns assertion violation. I have done a manual Cooper execution (pencil and paper) and I think the True is right one.
Any idea of what is going on?
PS: I have not tried it in Z3, yet, because I am doing first other attempts with other theories.
EDIT
Trigger warnings can be avoided using a simple trick to instantiate the quantified variables: creating an uninterpreted function.
method Main() {
assert exists x_1 : int {:trigger P(x_1)} :: exists y_1: int {:trigger P(y_1)}
:: exists x_2: int {:trigger P(x_2)} :: exists y_2 : int {:trigger P(y_2)}
:: (y_2<y_1) && (x_2<y_2) && (x_1<x_2);
}
predicate P(a: int)
{
true
}
You cannot do this with Dafny. While Dafny supports quantifiers, booleans, arithmetic, and many other things (recursive functions, sets, sequences, objects and references, multi-dimensional arrays, induction, inductive and coinductive datatypes, bitvectors, greatest and least fixpoints of monotonic functions, etc.), it is not suitable for SAT-QBF (or QBF + artihmetic) benchmarks.
Dafny's errors, including the assertion violation, tell you that the verifier was not able to do the proof. It may be that the property still holds, but you'll need to supply more of the proof yourself. In other words, you should interpret the assertion violation as a "don't know" answer. Stated differently, you cannot decide (only semi-decide) formulas with Dafny.
Dafny uses quantifiers in the SMT solvers via matching patterns, aka triggers. When a quantifier has no good triggers, which is what Dafny's "no trigger" warning is telling you, you may see bad performance, unstable verifications, and so-called butterfly effects (where a small and seemingly unrelated part of the program causes a change in the automatic construction of other proofs). Triggers are driven by uninterpreted function symbols, which your example doesn't have at all.
If you want a readable syntax, you may be able to do what you're trying through Boogie. I have not tried that, but you could try putting Boogie in its monomorphic mode and then supplying prover options to ask for the SAT-QBF or something similar (see Boogie's /help). Otherwise, if you're interested in deciding these problems, then going directly to SMT solver is the way to go.

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.

Why this Dafny example verification fails?

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

Dafny, triggers in forall assignment

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.

Resources