Dafny: Not checking multiple predicates - dafny

Currently, I tried to write a balanced version (AVL-Tree) of the existing Binary-Tree in the Dafny Repo.
It was possible for me to verify that this balanced tree is still a valid Binary-Tree.
To verify that it also suffices the requirements of an AVL-Tree, I created two additional predicates.
These predicates verify that the height of the subtree is valid and that the node and its subtree is balanced.
As the height validity check requires a valid Binary-Tree and as the balance check requires a valid height calculation the predicates depend on each other:
predicate Heigth_Valid()
reads this, Repr
{
Valid() &&
(..)
}
predicate Balanced()
reads this, Repr
{
Valid() &&
Heigth_Valid() &&
(..)
}
predicate Valid()
reads this, Repr
{
(..)
}
Thus it is possible to split the verification for different methods as some might only provide a valid Binary-Tree, while others recalculate the height or balance the tree.
The verification that the tree is balanced was successful,
but when I tried to check the verification with counterexamples and altered the code to produce an unbalanced tree, Dafny showed no errors in the IDE.
Is it possible that Dafny ignores certain predicates?

Related

Difference between multiset(a[..a.Length]) and multiset(a[..]) in Dafny

I'm trying to figure something out in dafny.
Given 2 arrays a and b, my assertions, invariants, post conditions, etc in the form of:
multiset(a[..]) == multiset(b[..]);
fails but
multiset(a[..a.Length]) == multiset(b[..b.Length])
succeeds.
I'm very confused by this because I assumed a[..] and a[..a.Length] would be the exact same thing. However, I found something interesting. If I add at the end of my method:
assert a[..a.Length] == a[..];
assert b[..b.Length] == b[..];
then I can get the invariants, post conditions, assertions involving my first example to work.
This suggests to me that a[..] and a[..a.Length] are actually different.
Could someone please explain why this is the case and what is happening here?
You are correct that a[..] and [..a.Length] (and, for that matter, also a[0..] and a[0..a.Length]) are the same thing. However, it may be that verifier treats these slightly differently. This makes a difference because the lack of (caution: technical word coming up) extensionality in Dafny.
Extensionality means that, if you know two things have the same elements, then they are the same thing. In your example, extensionality would mean that, if you know a[..] and a[..a.Length] to have the same elements, then a[..] and a[..a.Length] are the same thing.
The lack of extensionality in Dafny means that the verifier sometimes knows that two things have the same elements, but it still doesn't draw the conclusion that the two things are the same. This tends to be noticeable when the two things are passed to a function. In your example, that function is multiset(...), which converts a sequence into a multiset.
While Dafny does not support extensionality automatically, it does offer a simple technique to "remind" the verifier about extensionality. The technique is to assert the equality between the two things. In particular, when you write assert A == B; for two sequences A and B, then the verifier will first prove that A and B have the same elements, and then it will draw the conclusion that A is in fact equal to B. In other words, when the verifier is explicitly asked to verify the equality of two sequence-valued expressions like A and B, then it instead just proves that they are element-wise equal, after which it concludes A == B.
So, your remedy above is exactly the right thing to do. When you assert the equality between a[..a.Length] and a[..], the verifier proves that they have the same elements. After that, it "remembers" that this also means that a[..a.Length] and a[..] are equal. Once it has realized the two sequences are the same, it immediately also knows that functions of them, like multiset(a[..a.Length]) and multiset(a[..]), are the same.
More generally, extensionality is relevant for not just sequences, but also for sets, multisets, and maps. So, if you're working with any of those collection types, you may need to write assertions about sequence equality, set equality, etc., to "remind" the verifier about extensionality.
More generally, there are many things that are true and that the verifier doesn't immediately verify. To home in on what may be missing, the common technique is to start breaking down the proof obligations yourself, like you did in the assert statements. By breaking down more complicated proof obligations into simpler ones (which is usually done with assert statements or calc statements), you essentially provide hints to the verifier about how to prove the more complicated things.

Meaning of YACC expression using yysindex and yyrindex

In a yacc-based project I've encountered a complex expression which I don't understand what it does. The expression is repeated multiple times, so it looks like copy-and-paste. In fact the same exact expression occurs inside the YACC skeleton (byacc-1.9), so I'm assuming it has some particular meaning.
Here's the expression:
if (((yyn = yysindex[lastyystate]) && (yyn += tok) >= 0 &&
yyn <= YYTABLESIZE && yycheck[yyn] == tok) ||
((yyn = yyrindex[lastyystate]) && (yyn += tok) >= 0 &&
yyn <= YYTABLESIZE && yycheck[yyn] == tok)) {
If you partition this you get
((yyn = yysindex[lastyystate]) && (yyn += tok) >= 0 && yyn <= YYTABLESIZE && yycheck[yyn] == tok)
OR
((yyn = yyrindex[lastyystate]) && (yyn += tok) >= 0 && yyn <= YYTABLESIZE && yycheck[yyn] == tok))
I'm fairly familiar with parser generators and know that yacc is LALR(1). I'm guessing
yysindex is the "shift table"
yyrindex is the "reduce table"
yyn <= YYTABLESIZE is just range checking
Given the similarity of the two parts, and assuming my guesses are right, it would seem like they both look for something in the packed/coded parsing tables. I haven't digged into the details on how yacc stores the parsing tables, if someone knows more about this that would probably help.
In this context tok is (obiviously) the token number. But why the += tok and what is the yycheck table?
This code is a part of source code completion, say using TAB, if that helps to explain things.
Extra points if you can explain in a single sentence, as in give a function name, what the intention of this complex expression is.
"Next+check" transition table compression, also commonly referred to as the comb algorithm, is described in the Dragon book with respect to lexers in Section 3.9, with a note in chapter 4 about its use with parser tables.
The algorithm stores a sparse matrix by overlaying rows so that valid entries are overlapped only with missing entries. To look up a value in the compressed table you need a vector of the starting index for each row. Then you add the column number you're looking for to that starting index. Finally, you need to know whether the entry corresponds to the row you're looking in; the check vector contains the number of the row for which each table entry is valid.
It's called comb compression from the idea that each row is like a comb with many broken tines.
In the parser framework, that code (or something similar to it) will be used to ascertain the parser action corresponding to a token. Possible answers are:
Shift the token and go to state S. (Push the token onto the stack and read a new lookahead).
Reduce the stack using production number P. (Pop the right-hand side off the stack, execute the reduction action, go to the state found by consulting the GOTO table from the state revealed by the pop and the number of the reduced non-terminal, push the reduced non-terminal onto the stack, and then continue with the same lookahead token.)
Signal an error.
Accept the input. (Only if the lookahead token is the end-of-input marker. This possibility is often special cased.)
I guess that you are right about their being separate shift and reduce action tables. It's possible that the rows overlap better if you compress the actions separately, although the compression would have to be a lot better to compensate for the extra check array.
Given that the code is used for constructing a completion list, I suppose that it is being used in a simulation of the parse for each possible next token, in order to decide what the valid next token candidates are. The statement returns true if the token can be acted upon (if not, the candidate can simply be removed from the completion list) and sets yyn to the next action. It will be necessary to distinguish between Shift and Reduce actions. In some parser frameworks, the sign of the action is used for this purpose but there are other possibilities.
So I'd call the function find_parse_action (or find_parse_action_if_any, if you like wordier names).
Note that in an LALR parser, the simulation will need to iteratively apply reduce actions because the acceptability of a token isn't known until a shift action is actually encountered. Simulating reductions involves popping the stack which would be destructive if applied to the real parser stack. So part of the code probably creates and manages a simulated stack. (Although it's also possible that byacc uses a spaghetti stack. I've never actually looked at its skeleton.)
Bison uses similar code to implement "Lookahead Correction" (LAC), which is used to produce more informative error messages. ("Expecting X or Y or Z", which is another completion list activity.) So you can see another possible implementation technique in the Bison skeleton. IIRC, it makes a copy of the stack in order to simulate pops.

"Some instances of this call cannot safely be inlined."

In Dafny, what does the error message "Some instances of this call cannot safely be inlined" mean?
I see it reported for calls to predicates inside asserts. E.g.
assert LessThanOrEqual( [a[z]], a[z+1..r] ) ;
It's an informational message (not an error), and it's rather obscure! (I had to look it up myself to understand it.)
When there's a proof obligation that involves a predicate (here LessThanOrEqual), then the Dafny verifier internally sets things up so that if it can't prove the predicate, it will be able to tell you which conjunct inside the body of the predicate is failing. You will see this as "associated declaration" messages, which accompany error messages.
You can think of what's going on as, essentially, inlining the predicate's body into the call-site of the predicate. Sometimes, however, this cannot be done. For example, if the predicate is recursive, then there must be some limit to such inlining. If inlining cannot be done, all it means is that any error message you get will just say "can't prove LessThanOrEqual(...)", but it won't tell you which part of the definition of LessThanOrEqual it couldn't manage to prove.
A more subtle reason for why inlining cannot be done involves quantifiers. The verifier works with quantifiers through what are called matching triggers. A trigger informs the verifier when it would be a good idea to instantiate a quantifier. There are certain rules about what can and cannot be a trigger. The one rule that's relevant in your example is that arithmetic + cannot be part of a trigger. I'm guessing the definition of your LessThanOrEqual involves a quantifier, and that the verifier picks as a trigger of that quantifier a term that involves the second parameter to LessThanOrEqual. If the call above to LessThanOrEqual were inlined, then the + would sneak into the trigger, and that's not allowed by the rule.
Dafny thus chooses not to inline this call to LessThanOrEqual. All this means is that you'd get a slightly less precise error location if the verifier failed to prove the assertion. You probably wouldn't have noticed or been bothered about this; indeed, getting a less precise error message is probably less puzzling than the informational message you're getting instead.
There is a way suppress the informational message: if you pass in an equivalent expression that doesn't directly mention +. For example, you can you a local variable:
ghost var s := a[z+1..r];
assert LessThanOrEqual( [a[z]], s );
or a let expression:
assert var s := a[z+1..r]; LessThanOrEqual( [a[z]], s );
Rustan

Dafny rejects a simple postcondition

Below is a first attempt to prove various simple theorems, in this case about parity. Dafny /v. 1.9.9.40414/ verifies that adding 2 to an even number yields an even number but does not accept either of the commented out conditions.
function IsEven(a : int) : bool
requires a >= 0
{
if a == 0 then true
else if a == 1 then false
else IsEven(a - 2)
}
method Check1(a : int)
requires a >= 0
ensures IsEven(a) ==> IsEven(a + 2)
//ensures IsEven(a) ==> IsEven(a + a)
//ensures IsEven(a) ==> IsEven(a * a)
{
}
As I have just started to study this wonderful tool, my approach or the implementation might be incorrect. Any advice would be appreciated.
There are few different things going on here. I will discuss each of the three postconditions in turn.
The first and second postconditions
Since IsEven is a recursively defined predicate, in general, facts about it will require proofs by induction. Your first post condition is simple enough to not require induction, which is why it goes through.
Your second postcondition does require induction to be proved. Dafny has heuristics for automatically performing induction, but these heuristics are only invoked in certain contexts. In particular, Dafny will only attempt induction on "ghost methods" (also called "lemmas").
If you add the keyword ghost in front of method in Check1 (or change method to lemma, which is equivalent), you will see that the second postcondition goes through. This is because Dafny's induction heuristic gets invoked and manages to complete the proof.
The third postcondition
The third postcondition is more complex, because it involves nonlinear arithmetic. (In other words, it involves nontrivial reasoning about multiplying two variables together.) Dafny's underlying solver has trouble reasoning about such things, and so the heuristic proof by induction doesn't go through.
A proof that a * a is even if a is even
One way to prove it is here. I have factored out IsEven(a) ==> IsEven(a * a) into its own lemma, called EvenSquare. I have also changed it to require IsEven(a) as a precondition, rather than put an implication in the postcondition. (A similar proof also goes through with the implication instead, but using preconditions on lemmas like this instead of implications is idiomatic Dafny.)
The proof of EvenSquare is by (manual) induction on a. The base case is handled automatically. In the inductive case (the body of the if statement), I invoke the induction hypothesis (ie, I make a recursive method call to EvenSquare to establish that (a - 2) * (a - 2) is even). I then assert that a * a can be written as the sum of (a - 2) * (a - 2) and some offset. The assertion is dispatched automatically. The proof will be done if I can show that the right hand side of this equality is even.
To do this, I already know that (a - 2) * (a - 2) is even, so I first invoke another lemma to show that the offset is even, because it is twice something else. Finally, I invoke one last lemma to show that the sum of two even numbers is even.
This completes the proof, assuming the two lemmas.
Proofs of the two lemmas
It remains to show that twice anything is even, and that the sum of two even numbers is even. While not completely trivial, neither is as complex as EvenSquare.
The lemma EvenDouble proves that twice anything is even. (This is in fact a stronger version of your second postcondition. Your second postcondition says that doubling any even number is even. In fact, doubling any (non-negative, under your definition of evenness) number at all is even.) The proof of EvenDouble proceeds by (manual) induction on a. The base case is handled automatically. The inductive case only requires explicitly invoking the induction hypothesis.
The lemma EvenPlus is almost proved automatically by Dafny's induction heuristic, except that it trips over a bug or some other problem which causes a loop in the solver. After a little debugging, I determined that the annotation {:induction x} (or {:induction y}, for that matter) makes the proof not loop. These annotations tell Dafny's heuristics which variable(s) to try to induct on. By default in this case, Dafny tries to induct on both x and y, which for some reason causes the solver to loop. But inducting on either variable alone works. I'm investigating this problem further, but the current solution works.

DLV Rule is not safe

I am starting to work with DLV (Disjunctive Datalog) and I have a rule that is reporting a "Rule is not safe" error, when running the code. The rule is the following:
foo(R, 1) :- not foo(R, _)
I have read the manual and seen that "cyclic dependencies are disallowed". I guess this is why I am being reported the error, but I am not sure how this statement is so problematic to DLV. The final goal is to have some kind of initialization in case that the predicate has not been defined.
More precisely, if there is no occurrence of 'foo' with the parameter R (and anything else), then define it with parameters R and 1. Once it is defined, the rule shouldn't be triggered again. So, this is not a real recursion in my opinion.
Any comments on how to solve this issue are welcomed!
I have realised that I probably need another predicate to match the parameter R in the body of the rule. Something like this:
foo(R, 1) :- not foo(R, _), bar(R)
Since, otherwise there would be no way to know whether there are no occurrences of foo(R, _). I don't know whether I made myself clear.
Anyway, this doesn't work either :(
To the particular "Rule is not safe" error: First of all this has nothing to do with cyclic or acyclic dependencies. The same error message shows up for the non-cyclic program:
foo2(R, 1) :- not foo(R,_), bar(R).
The problem is that the program is actually not safe (http://www.dlvsystem.com/html/DLV_User_Manual.html#SAFETY). As mentioned in the section on negative rules (anchor #AEN375, I am only allowed to use 2 links in my answer):
Variables, which occur in a negated literal, must also occur in a
positive literal in the body.
Observe that the _ is an anonymous variable. I.e., the program
foo(R,1) :- not foo(R,_), bar(R).
can be equivalently written as (and is equivalent to)
foo(R,1) :- not foo(R,X), bar(R).
Anonymous variables (DLV manual, anchor #AEN264 - at the end of the section) just allow us to avoid inventing names for variables that will only occur once within the rule (i.e. for variables that only express "there is some value, I absolutely do not care about it), but they are variables nevertheless. And since negation with not is "negation" and not "true negation" (or "strong negation" as it is also often called), none of the three safety conditions is satisfied by the rule.
A very rough and high-level intuition for safety is that it guarantees that every variable in the program can be assigned to some finite domain - as it is now the case with R by adding bar(R). However, the same also must be the case for the anonymous variable _ .
To the actual problem of defining default values:
As pointed out by lambda.xy.x, the problem here is the Answer Set (or stable model) semantics of DLV: Trying to do it in one rule does not give any solution:
In order to get a safe program, we could replace the above problems e.g. by
foo(1,2). bar(1). bar(2).
tmp(R) :- foo(R,_).
foo(R,1) :- not tmp(R), bar(R).
This has no stable model:
Assume the answer is, as intended,
{foo(1,2), bar(1), bar(2), foo(2,1)}
However, this is not a valid model, since tmp(R) :- foo(R,_) would require it to contain tmp(2). But then, "not tmp(2)" is no longer true, and therefore having foo(2,1) in the model violates the required minimality of the model. (This is not exactly what is going on, more a rough intuition. More technical details could be found in any article on answer set programming, a quick Google search gave me this paper as one of the first results: http://www.kr.tuwien.ac.at/staff/tkren/pub/2009/rw2009-asp.pdf)
In order to solve the problem, it is therefore somehow necessary to "break the cycle". One possibility would be:
foo(1,2). bar(1). bar(2). bar(3).
tmp(R) :- foo(R,X), X!=1.
foo(R,1) :- bar(R), not tmp(R).
I.e., by explicitly stating that we want to add R into the intermediate atom only if the value is different from 1, having foo(2,1) in the model does not contradict tmp(2) not being part of the model as well. Of course, this no longer allows to distinguish whether foo(R,1) is there as default value or by input, but if this is not required ...
Another possibility would be to not use foo for the computation, but some foo1 instead. I.e. having
foo1(R,X) :- foo(R,X).
tmp(R) :- foo(R,_).
foo1(R,1) :- bar(R), not tmp(R).
and then just use foo1 instead of foo.

Resources