Requiring to assert a[0] in a - dafny

I'm currently learning dafny and ran into a really odd assertion requirement:
method M (a : seq<int>, acc : seq<int>)
requires |a| > 0
requires forall va, vacc :: va in a && vacc in acc ==> vacc <= va
{
assert forall vacc :: vacc in acc ==> vacc <= a[0];
}
The above code fails on the assertion, however if I add assert a[0] in a it verifies?
Why is this the case, surely in all circumstances that |a| > 0 a[0] in a holds since seq is immutable?
(also any style guide recommendations would be appreciated :) )

This has to do with "triggers".
The short answer is that until you manually "mention" a[0], Dafny will not be able to take advantage of the quantified requires clause. It does not matter how you mention a[0], just that you mention it. That is why your trivial assertion works even though it doesn't seem to add anything logically: it's just because it mentions a[0].
For more information, see:
the FAQ qustion
this answer "what are triggers?"
this answer about "no terms found to trigger on"

Related

how to prove universal introduction in Dafny

I am trying to find strategies to prove universally quantified assertions in Dafny. I see Dafny proves universal elimination
quite easily:
predicate P<X>(k:X)
lemma unElim<X>(x:X)
ensures (forall a:X :: P(a)) ==> P(x)
{ }
lemma elimHyp<H> ()
ensures forall k:H :: P(k)
lemma elimGoal<X> (x:X)
ensures P(x)
{ elimHyp<X>(); }
but I can not find how to prove the introduction rule:
//lemma unInto<X>(x:X)
// ensures P(x) ==> (forall a:X :: P(a))
// this definition is wrong
lemma introHyp<X> (x:X)
ensures P(x)
lemma introGoal<H> ()
ensures forall k:H :: P(k)
{ }
all ideas appreciated
Universal introduction is done using Dafny's forall statement.
lemma introHyp<X>(x: X)
ensures P(x)
lemma introGoal<H>()
ensures forall k: H :: P(k)
{
forall k: H
ensures P(k)
{
introHyp<H>(k);
}
}
In general, it looks like this:
forall x: X | R(x)
ensures P(x)
{
// for x of type X and satisfying R(x), prove P(x) here
// ...
}
So, inside the curly braces, you prove P(x) for one x. After the forall statement, you get to assume the universal quantifier
forall x: X :: R(x) ==> P(x)
If, like in my introGoal above, the body of the forall statement is exactly one lemma call and the postcondition of that lemma is what you what in the ensures clause of the forall statement, then you can omit the ensures clause of the forall statement and Dafny will infer it for you. Lemma introGoal then looks like this:
lemma introGoal<H>()
ensures forall k: H :: P(k)
{
forall k: H {
introHyp(k);
}
}
There's a Dafny Power User note on Automatic induction that may be helpful, or at least gives some additional examples.
PS. A natural next question would be how to do existential elimination. You do it using Dafny's "assign such that" statement. Here is an example:
type X
predicate P(x: X)
lemma ExistentialElimination() returns (y: X)
requires exists x :: P(x)
ensures P(y)
{
y :| P(y);
}
Some examples are found in this Dafny Power User note. Some advanced technical information about the :| operators are found in this paper.

How do I write a clean function in Dafny to get the minimum of a set?

I am trying to write a function to get the minimum of a non-empty set.
Here is what I came up with:
method minimum(s: set<int>) returns (out: int)
requires |s| >= 1
ensures forall t : int :: t in s ==> out <= t
{
var y :| y in s;
if (|s| > 1) {
var m := minimum(s - {y});
out := (if y < m then y else m);
assert forall t : int :: t in (s - {y}) ==> out <= t;
assert out <= y;
} else {
assert |s| == 1;
assert y in s;
assert |s - {y}| == 0;
assert s - {y} == {};
assert s == {y};
return y;
}
}
This is suboptimal for two reasons:
Dafny gives a "No terms found to trigger on." warning for the line,
assert forall t : int :: t in (s - {y}) ==> out <= t;
However, removing this line causes the code to fail to verify. My understanding is that the trigger warning isn't really bad, it's just a warning that Dafny might have trouble with the line. (Even though it actually seems to help.) So it makes me feel like I'm doing something suboptimal or non-idiomatic.
This is pretty inefficient. (It constructs a new set each time, so it would be O(n^2).) But I don't see any other way to iterate through a set. Is there a faster way to do this? Are sets really intended for programming "real" non-ghost code in Dafny?
So my question (in addition to the above) is: is there a better way to write the minimum function?
In this case, I recommend ignoring the trigger warning, since it seems to work fine despite the warning. (Dafny's trigger inference is a little bit overly conservative when it comes to the set theoretic operators, and Z3 is able to infer a good trigger at the low level.) If you really want to fix it, here is one way. Replace the "then" branch of your code with
var s' := (s - {y});
var m := minimum(s');
out := (if y < m then y else m);
assert forall t :: t in s ==> t == y || t in s';
assert forall t : int :: t in s' ==> out <= t;
assert out <= y;
The second problem (about efficiency) is somewhat fundamental. (See Rustan's paper "Compiling Hilbert's Epsilon Operator" where it is mentioned that compiling let-such-that statements results in quadratic performance.) I prefer to think of Dafny's set as a mathematical construct that should not be compiled. (The fact that it can be compiled is a convenience for toy programs, not for real systems, where one would expect a standard library implementation of sets based on a data structure.)

assertion violation when verifying Max function in Dafny?

The following program results in an assertion violation on assert v==40: why ? The program can be verified when the array a contains only one element.
method Max(a:array<int>) returns(max:int)
requires 1<=a.Length
ensures forall j:int :: 0<=j< a.Length ==> max >= a[j]
ensures exists j:int :: 0<=j< a.Length && max == a[j]
{
max:=a[0];
var i :=1;
while(i < a.Length)
invariant 1<=i<=a.Length
decreases a.Length-i
invariant forall j:int :: 0<=j<i ==> max >= a[j]
invariant exists j:int :: 0<=j<i && max == a[j]
{
if(a[i] >= max){max := a[i];}
i := i + 1;
}
}
method Test(){
var a := new int[2];
a[0],a[1] := 40,10;
var v:int:=Max(a);
assert v==40;
}
This is indeed strange! It boils down to the way Dafny handles quantifiers.
Let's start with a human-level proof that the assertion is actually valid. From the postconditions of Max, we know two things about v: (1) it is at least as big as every element in a, and (2) it is equal to some element of a. By (2), v is either 40 or 10, and by (1), v is at least 40 (because it's at least as big as a[0], which is 40). Since 10 is not at least 40, v can't be 10, so it must be 40.
Now, why does Dafny fail to understand this automatically? It's because of the forall quantifier in (1). Dafny (really Z3) internally uses "triggers" to approximate the behavior of universal quantifiers. (Without any approximation, reasoning with quantifiers is undecidable in general, so some restriction like this is required.) The way triggers work is that for each quantifier in the program, a syntactic pattern called the trigger is inferred. Then, that quantifier is completely ignored unless the trigger matches some expression in the context.
In this example, fact (1) will have a trigger of a[j]. (You can see what triggers are inferred in Visual Studio or VSCode or emacs by hovering over the quantifier. Or on the command line, by passing the option /printTooltips and looking for the line number.) That means that the quantifier will be ignored unless there is some expression of the form a[foo] in the context, for any expression foo. Then (1) will be instantiated with foo for j, and we'll learn max >= a[foo].
Since your Test method's assertion doesn't mention any expression of the form a[foo], Dafny will not be able to use fact (1) at all, which results in the spurious assertion violation.
One way to fix your Test method is add the assertion
assert v >= a[0];
just before the other assertion. This is the key consequence of fact (1) that we needed in our human level proof, and it contains the expression a[0], which matches the trigger, allowing Dafny to instantiate the quantifier. The rest of the proof then goes through automatically.
For more information about triggers in general and how to write them manually, see this answer.

Trivial assertion violation in Dafny

Why does Dafny claim this simple assertion is possibly violated?
var b := new int[2];
b[0],b[1] := 1, -2;
assert exists k | 0 <= k < b.Length :: (b[k] == 1 || b[k] == -1);
This has to do with the way Dafny handles existential quantifiers. It will (almost) never "guess" a value for you. Instead, Dafny uses syntactic heuristics (called "triggers") to try certain values from the context.
In your assertion, the trigger is b[k], which means that Dafny will only try values of k such that the expression b[k] is mentioned explicitly.
Thus, one way to fix the assertion is to explicitly mention the correct value for k, by adding the assertion
assert b[0] == 1;
or even
assert b[0] == 1 || b[0] == -1
This is somewhat related to this question.
For more on triggers see this answer.

Using :| in functional code -- recursion on sets

How might one recurse over a set, S, in Dafny when writing pure functional code? I can use :| in imperative code, having checked for non-emptiness, to select an element, s, then recurse on S - {s}. Not quite sure how to make :| deterministic and use it in functional code.
Good question! (I wish downvoters would have the courage to leave a comment...)
This is addressed in depth in Rustan's paper "Compiling Hilbert's Epsilon Operator".
In particular, see section 3.2, which describes how to write a deterministic function by recursion over sets. For reasons not entirely clear to me, the paper's Dafny code proving lemma ThereIsASmallest doesn't work for me in modern Dafny. Here is a version that works (but is ugly):
lemma ThereIsASmallest(S: set<int>)
requires S != {}
ensures exists x :: x in S && forall y | y in S :: x <= y
{
var y :| y in S;
if S != {y} {
var S' := S - {y};
assert forall z | z in S :: z in S' || z == y;
ThereIsASmallest(S');
var x' :| x' in S' && forall y | y in S' :: x' <= y;
var x := min2(y, x');
assert x in S;
}
}
Finally, as an aside, note that the technique of section 3.2 relies on having a total order on the type. If you are trying to do something fully polymorphic, then as far as I know it isn't possible.

Resources