I'm confused about Dafny's forall statement if the statement inside the forall is to update a variable such as c.arr[i].d, which is mixed with field and array element access. For instance, the example as follows can't be compiled.
I think, explicitly indicating modifies clause is very tedious, I need indicate: modifies top, modifies top.Cache[i]
datatype CACHE_STATE = I| S| E
datatype MSG_CMD = Empty| ReqS| ReqE| Inv| InvAck| GntS| GntE
type NODE=nat
type DATA=nat
type boolean=bool
class class_0 {
var
Data : DATA,
Cmd : MSG_CMD
}
class class_1 {
var
Data : DATA,
State : CACHE_STATE
}
class TopC{
var
AuxData : DATA,
MemData : DATA,
CurPtr : NODE,
CurCmd : MSG_CMD,
ExGntd : boolean,
ShrSet : array<boolean>,
InvSet : array<boolean>,
Chan3 : array<class_0 > ,
Chan2 : array<class_0 > ,
Chan1 : array<class_0 > ,
Cache : array<class_1 > }
method n_RecvReqS(top:TopC,i:nat, N0:nat )
requires 0<= i<N0
requires top.Chan1.Length ==N0
requires N0>0
requires top.InvSet.Length ==N0
requires N0>0
requires top.ShrSet.Length ==N0
requires N0>0
requires top.Cache.Length ==N0
requires ((top.Chan1[i].Cmd == ReqS) && (top.CurCmd == Empty))
modifies top.Chan1[i]
modifies top.InvSet
modifies top.ShrSet
modifies top
modifies top.Cache
{
top.CurCmd := ReqS;
top.CurPtr := i;
top.Chan1[i].Cmd := Empty;
forall j | 0<= j< N0 {
top.InvSet[j] := top.ShrSet[j];
}
forall j | 0<= j< N0 {
top.Cache[j].State:= I;
}
}
Knowing about a method's effect on the heap is crucial to verification, though writing modifies clauses can be tedious. In your case, you need to account for all of the modifications that method n_RecvReqS performs. As you've noted, this includes every top.Cache[j] objects, for all j's.
One way to include these in your modifies clause is to use a set comprehension:
modifies set j | 0 <= j < top.Cache.Length :: top.Cache[j]
This denotes the set of the objects top.Cache[j], where j ranges among the values satisfying 0 <= j < top.Cache.Length.
Read on for further options.
If you do this often and you consider all of these caches to be part of the representation of a TopC object, then you'll probably want to abstract over the many pieces of your state and put these into a set. If you search the Dafny test suite for declarations like
ghost var Repr: set<object>
you'll find some examples. Then, your entire modifies clause would just be
modifies Repr
If you're not ready to use such a Repr set yet, then there's another simple modifies clause you can write for the cache modifications in your program. If you list a sequence of objects in a modifies clause, Dafny will automatically convert it to the corresponding set of objects. You can convert all of the array elements of top.Cache into a sequence by the expression top.Cache[..]. Thus, to account for the updates in your forall statement, you can write
modifies top.Cache[..]
Unfortunately, Dafny doesn't automatically realize that each top.Cache[j] in the body of your forall statement is indeed an element of the set formed from the sequence top.Cache[..]. It is possible to assist the verifier to help it prove this, but doing so adds some clutter to the forall statement. Here's how I did it:
forall j | 0 <= j < N0 {
(assert top.Cache[j] in top.Cache[..]; top.Cache[j]).State:= I;
}
Rustan
Related
https://dart.dev/guides/language/language-tour#generators
When you need to lazily produce a sequence of values, consider using a
generator function. Dart has built-in support for two kinds of
generator functions:
As explained above, what exactly does "lazily" mean for a synchronous generator function?
Is there a difference in behavior from a normal function?
void main(){
Iterable<int> list1 = naturalsTo(3);
print(list1.toList());//[0, 1, 2]
print(naturalsTo(3).toList());//[0, 1, 2]
}
Iterable<int> naturalsTo(int n) sync* {
int k = 0;
while (k < n) yield k++;
}
Iterable<int> naturalsTo2(int n){
int k=0;
List<int> resultList = [];
for(k;k<n;k++){
resultList.add(k);
}
return resultList;
}
The results of the above code both return the same list, but I'm not sure how they behave differently.
What exactly does "lazily" mean?
Lazy evaluation is a general term to describe behavior where a result isn't computed until it's needed. It defers computation to avoid computing results that won't be used.
Your example (even after correcting it to not call the same function for both cases) does not demonstrate the difference. Instead consider a situation where:
Each loop iteration has an observable side-effect. In the example below, we'll use print, but in practice that observable side-effect usually is that each loop iteration takes some measurable amount of time.
We don't need to iterate over all elements.
void main() {
print(naturalsTo(3).take(2).toList());
print(naturalsTo2(3).take(2).toList());
}
Iterable<int> naturalsTo(int n) sync* {
int k = 0;
while (k < n) {
print(k);
yield k++;
}
}
Iterable<int> naturalsTo2(int n) {
int k = 0;
List<int> resultList = [];
for (k; k < n; k++) {
print(k);
resultList.add(k);
}
return resultList;
}
The first case (which uses the synchronous generator) prints:
0
1
[0, 1]
The second case (which uses a normal function) prints:
0
1
2
[0, 1]
lazily means it calculates the values as you need them.
The thing is though, when you say toList() you actually say, "I need them all now", so you lose all benefits of the lazy calculation.
To demonstrate how it works let's say this is the function, notice the print:
Iterable<int> naturalsTo(int n) sync* {
int k = 0;
while (k < n) {
print(k);
yield k++;
}
}
Then execute this code:
var a = naturalsTo(10).toList();
print("=====");
var b = naturalsTo(10);
print("-----");
var c = b.elementAt(5);
you will see it generates this output:
0
1
2
3
4
5
6
7
8
9
=====
-----
0
1
2
3
4
5
So, just creating the iterable doesn't execute any of the code in it. converting it to a list generates all the values. And just requesting a single value from an iterable only generates the values up to that value.
This can be beneficial if the calculations take a long time to process or even when the iterable generates an infinite sequence. It's often used in Listviews so only the rows that are shown on screen are calculated.
Hi Relatively new to Dafny and have defined methods set2Seq and seq2Set for conversion between sets and seqs. But can only find how to write a function fseq2Set from sets to sequences.
I can not find how to define fseq2Set. As Lemmas can not reference methods this makes proving the identity beyond me. Any help much appreciated?
Code:
function method fseq2Set(se: seq<int>) :set<int>
{ set x:int | x in se :: x }
method seq2Set(se: seq<int>) returns (s:set<int>)
{ s := set x:int | x in se :: x; }
method set2Seq(s: set<int>) returns (se:seq<int>)
requires s != {}
ensures s == fseq2Set(se)
decreases |s|
{
var y :| y in s;
var tmp ;
if (s=={y}) {tmp := [];} else {tmp := set2Seq(s-{y});}
se := [y] + tmp;
assert (s-{y}) + {y} == fseq2Set([y] + tmp);
}
/* below fails */
function fset2Seq(s:set<int>):seq<int>
decreases s { var y :| y in s ; [y] + fset2Seq(s-{y}) }
lemma cycle(s:set<int>) ensures forall s:set<int> :: fseq2Set(fset2Seq(s)) == s { }
Ok, there is kind of a lot going on here. First of all, I'm not sure if you intended to do anything with the methods seq2Set and set2Seq, but they don't seem to be relevant to your failure, so I'm just going to ignore them and focus on the functions.
Speaking of functions, Danfy reports an error on your definition of fset2Seq, because s might be empty. In that case, we should return the empty sequence, so I adjusted your definition to:
function fset2Seq(s:set<int>):seq<int>
decreases s
{
if s == {} then []
else
var y := Pick(s);
[y] + fset2Seq(s - {y})
}
function Pick(s: set<int>): int
requires s != {}
{
var x :| x in s; x
}
which fixes that error. Notice that I also wrapped the let-such-that operator :| in a function called Pick. This is essential, but hard to explain. Just trust me for now.
Now on to the lemma. Your lemma is stated a bit weirdly, because it takes a parameter s, but then the ensures clause doesn't mention s. (Instead it mentions a completely different variable, also called s, that is bound by the forall quantifier!) So I adjusted it to get rid of the quantifier, as follows:
lemma cycle(s:set<int>)
ensures fseq2Set(fset2Seq(s)) == s
Next, I follow My Favorite Heuristicâ„¢ in program verification, which is that the structure of the proof follows the structure of the program. In this case, the "program" in question is fseq2Set(fset2Seq(s)). Starting from our input s, it first gets processed recursively by fset2Seq and then through the set comprehension in fseq2Set. So, I expect a proof by induction on s that follows the structure of fset2Seq. That structure is to branch on whether s is empty, so let's do that in the lemma too:
lemma cycle(s:set<int>)
ensures fseq2Set(fset2Seq(s)) == s
{
if s == {} {
} else {
}
...
Dafny reports an error on the else branch but not on the if branch. In other words, Dafny has proved the base case, but it needs help with the inductive case. The next thing that fset2Seq(s) does is call Pick(s). Let's do that too.
lemma cycle(s:set<int>)
ensures fseq2Set(fset2Seq(s)) == s
{
if s == {} {
} else {
var y := Pick(s);
...
Now we know from its definition that fset2Seq(s) is going to return [y] + fset2Seq(s - {y}), so we can copy-paste our ensures clause and manually substitute this expression.
lemma cycle(s:set<int>)
ensures fseq2Set(fset2Seq(s)) == s
{
if s == {} {
} else {
var y := Pick(s);
assert fseq2Set([y] + fset2Seq(s - {y})) == s;
...
Dafny reports an error on this assertion, which is not surprising, since it's just a lightly edited version of the ensures clause we're trying to prove. But importantly, Dafny no longer reports an error on the ensures clause itself. In other words, if we can prove this assertion, we are done.
Looking at this assert, we can see that fseq2Set is applied to two lists appended together. And we would expect that to be equivalent to separately converting the two lists to sets, and then taking their union. We could prove a lemma to that effect, or we could just ask Dafny if it already knows this fact, like this:
lemma cycle(s:set<int>)
ensures fseq2Set(fset2Seq(s)) == s
{
if s == {} {
} else {
var y := Pick(s);
assert fseq2Set([y] + fset2Seq(s - {y})) == fseq2Set([y]) + fseq2Set(fset2Seq(s - {y}));
assert fseq2Set([y] + fset2Seq(s - {y})) == s;
...
(Note that the newly added assertion is before the last one.)
Dafny now accepts our lemma. We can clean up a little by deleting the the base case and the final assertion that was just a copy-pasted version of our ensures clause. Here is the polished proof.
lemma cycle(s:set<int>)
ensures fseq2Set(fset2Seq(s)) == s
{
if s != {} {
var y := Pick(s);
assert fseq2Set([y] + fset2Seq(s - {y})) == fseq2Set([y]) + fseq2Set(fset2Seq(s - {y}));
}
}
I hope this explains how to prove the lemma and also gives you a little bit of an idea about how to make progress when you are stuck.
I did not explain Pick. Basically, as a rule of thumb, you should just always wrap :| in a function whenever you use it. To understand why, see the Dafny power user posts on iterating over collecion and functions over set elements. Also, see Rustan's paper Compiling Hilbert's epsilon operator.
The following constructor does not work and fails at
parent !in Repr
Why can't Dafny proof the postcondition, that parent is not part of the Repr set?
constructor Init(x: HashObj, parent:Node?)
ensures Valid() && fresh(Repr - {this, data})
ensures Contents == {x.get_hash()}
ensures Repr == {this, data};
ensures left == null;
ensures right == null;
ensures data == x;
ensures parent != null ==> parent !in Repr;
ensures this in Repr;
{
data := x;
left := null;
right := null;
Contents := {x.get_hash()};
Repr := {this} + {data};
}
I'm guessing that HashObj is a trait? (If it's a class, then your example verifies for me.) The verification fails because the verifier thinks x might equal parent.
The verifier ought to know that Node is not a HashObj (unless, of course, your class Node really does extend HashObj), but it doesn't. You may file this as an Issue on https://github.com/dafny-lang/dafny to get that corrected.
In the meantime, you can write a precondition that says x and parent are different. Here, there's a wrinkle, too. You'd like to write
requires x != parent
but (unless Node really does extend HashObj) this does not type check. So, you would want to cast parent to object?. There's no direct syntax for such an up-cast, but you can do it with a let expression:
requires x != var o: object? := parent; o
I have a class that allocates an iterator on the heap and calls MoveNext() on that iterator. I want to prove that if MoveNext() returns true, Valid() holds for the iterator.
iterator Iter()
{}
class MyClass
{
var iter : Iter;
constructor ()
{
iter := new Iter();
}
method next() returns (more : bool)
requires iter.Valid();
modifies iter, iter._new, iter._modifies;
{
more := iter.MoveNext();
// This assertion fails:
assert more ==> iter.Valid();
}
}
I took a look at the output of /rprint, and the method MoveNext() contains ensures more ==> this.Valid(), which seems to imply my desired assertion. If I change iter to be locally allocated within the method next(), then Dafny verifies the assertion.
The problem is that nothing is known about what's in iter._new and iter._modifies. If this is in one of those sets, then this.iter may have changed during the call to MoveNext().
This body for next() confirms what I just said:
var i := iter;
more := iter.MoveNext();
assert i == iter; // this assertion fails
assert more ==> iter.Valid();
So, under the assumption more, Valid() does still hold of the iterator, but the iterator may no longer referenced by iter:
more := iter.MoveNext();
assert more ==> old(iter).Valid(); // this holds
Probably the way you want to solve this problem is to add a precondition to next():
method next() returns (more : bool)
requires iter.Valid()
requires this !in iter._new + iter._modifies // add this precondition
modifies iter, iter._new, iter._modifies
{
more := iter.MoveNext();
assert more ==> iter.Valid(); // this now verifies
}
Rustan
I have the following program , which transforms a string into a Boolean formula (string_to_formula), where I am defining expr_vector b(c). This code works, but I am not being able to reason about the context. What is the function of a context? Is there any way we can define the variable b just once? Why do we need to send the context to the function? And can this code be written in a more succinct way?
int main() { try {
context c;
expr form(c);
form = string_to_formula("x1x00xx011",c);
expr form1(c);
form1 = string_to_formula("1100x1x0",c);
solver s(c);
s.add(form && form1);
s.check();
model m = s.get_model();
cout << m << "\n";
}
expr string_to_formula(string str, context& c )
{
expr_vector b(c) ;
for ( unsigned i = 0; i < str.length(); i++)
{ stringstream b_name;
b_name << "b_" << i;
b.push_back(c.bool_const(b_name.str().c_str()));
}
expr formula(c);
formula = c.bool_val(true);
for( unsigned i = 0 ; i < str.length() ; ++i )
{ char element = str.at(i) ;
if ( element == '1' )
formula = formula && ( b[i] == c.bool_val(true) ) ;
else if ( element == '0' )
formula = formula && ( b[i] == c.bool_val(false) ) ;
else if ( element == 'x' )
continue;
}
return formula;
}
The context object is relevant for multi-threaded programs.
Each execution thread can have its own context, and they can be accessed without using any form of synchronization (e.g., mutexes).
Each expression belongs to a single context. We cannot use the same expression in two different contexts, but we can copy them from one context to another.
In Z3, expressions are maximally shared. For example, if we have an expressions such as (f T T) where T is a big term, then internally Z3 has only one copy of T. For implementing this feature, we use a hashtable. The hashtable is stored in the context.
If we use the same context C in two different execution threads, Z3 will probably crash due to race conditions updating C.
If your program has only one execution thread, you can avoid "moving" the context around by having a global variable.
The idea of context/manager is present in many libraries. For example, in CUDD (BDD library), they have a DdManager. In the script language Lua, they have a lua_State. These are all instances of the same idea.