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.
Related
I'm new to Z3py and I found an exercise where it would ask to check if some verification conditions were true. Up to this moment, the exercises I've done were basically to transform simple propositional formulas into z3py clauses, something like:
Propositional Formula would be:
(n>=4) -> (x = y +2)
Which would become in Z3Py:
n, x, y = Ints('n x y')
s.add(Implies(n>=5, x == y+3))
The conditions I'm presented now, introduce Arrays and Quantifiers and after spending hours trying to figure it out on the documentation around, I'm still not able to get it properly done.
For example, how would I do the same process above but with the following conditions:
n ≥ 1 ∧ i = 1 ∧ m = A[0]
i <= n ∧ ∀j. 0 ≤ j < i → m ≥ A[j]
A little snippet of what I think that is correctly done:
i, n = Ints('i n')
s.add(And(n>=1, i == 1, ???)
s.add(And(i<=n,Implies(???))
How should I replace the ??? so that the conditions would be correctly transformed into Z3Py?
Solution:
- The constraint
n ≥ 1 ∧ i = 1 ∧ m = A[0]
would become in Z3Py:
A = Array('A', IntSort(), IntSort()) //Array declaration
i, n, m, j = Ints('i n m j') //Ints declaration for both constraints
And(n>=1, i==1, m==A[0])
- The constraint
i <= n ∧ ∀j. 0 ≤ j < i → m ≥ A[j]
would become:
And(i<=n,ForAll(j,Implies(And(j>=0,j<i),m>=A[j])))
Your questions is quite ambiguous. Note that you've the constraints:
n ≥ 1 ∧ i = 1
and then
i <= n
but that consequent is already implied by the first, and hence is redundant. Meaning, if you add them both to the solver like you did with your s.add lines, then it won't really mean much of anything at all.
I'm guessing these two lines actually arise in different "subparts" of the problem, i.e., they aren't used together for the same verification goal. And, making an educated guess, you're trying to say something about the maximum element of an array; where i is some sort of a loop-counter. The first line is what happens before the loop starts, and the second is the invariant the loop-body ensures. If this is the case, you should be explicit about that.
Assuming this is the case, then these sorts of problems are usually modeled on the "body" of that loop, i.e., you need to show us exactly what sort of a "program" you're dealing with. That is, these constraints will only make sense in the presence of some sort of a transformation of your program variables.
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.
I wrote a function on the natural numbers that uses the operator _<?_ with the with-abstraction.
open import Data.Maybe
open import Data.Nat
open import Data.Nat.Properties
open import Relation.Binary.PropositionalEquality
open import Relation.Nullary
fun : ℕ → ℕ → Maybe ℕ
fun x y with x <? y
... | yes _ = nothing
... | no _ = just y
I would like to prove that if the result of computing with fun is nothing then the original two values (x and y) fulfill x < y.
So far all my attempts fall short to prove the property:
prop : ∀ (x y)
→ fun x y ≡ nothing
→ x < y
prop x y with fun x y
... | just _ = λ()
... | nothing = λ{refl → ?} -- from-yes (x <? y)}
-- This fails because the pattern matching is incomplete,
-- but it shouldn't. There are no other cases
prop' : ∀ (x y)
→ fun x y ≡ nothing
→ x < y
prop' x y with fun x y | x <? y
... | nothing | yes x<y = λ{refl → x<y}
... | just _ | no _ = λ()
--... | _ | _ = ?
In general, I've found that working with the with-abstraction is painful. It is probably due to the fact that with and | hide some magic in the background. I would like to understand what with and | really do, but the "Technical details" currently escape my understanding. Do you know where to look for to understand how to interpret them?
Concrete solution
You need to case-split on the same element on which you case-split in your function:
prop : ∀ x y → fun x y ≡ nothing → x < y
prop x y _ with x <? y
... | yes p = p
In the older versions of Agda, you would have had to write the following:
prop-old : ∀ x y → fun x y ≡ nothing → x < y
prop-old x y _ with x <? y
prop-old _ _ refl | yes p = p
prop-old _ _ () | no _
But now you are able to completely omit a case when it leads to a direct contradiction, which is, in this case, that nothing and just smth can never be equal.
Detailed explanation
To understand how with works you first need to understand how definitional equality is used in Agda to reduce goals. Definitional equality binds a function call with its associated expression depending on the structure of its input. In Agda, this is easily seen by the use of the equal sign in the definition of the different cases of a function (although since Agda builds a tree of cases some definitional equalities might not hold in some cases, but let's forget this for now).
Let us consider the following definition of the addition over naturals:
_+_ : ℕ → ℕ → ℕ
zero + b = b
(suc a) + b = suc (a + b)
This definition provides two definitional equalities that bind zero + b with b and (suc a) + b with suc (a + b). The good thing with definitional equalities (as opposed to propositional equalities) is that Agda automatically uses them to reduce goals whenever possible. This means that, for instance, if in a further goal you have the element zero + p for any p then Agda will automatically reduce it to p.
To allow Agda to do such reduction, which is fundamental in most cases, Agda needs to know which of these two equalities can be exploited, which means a case-split on the first argument of this addition has to be made in any further proof about addition for a reduction to be possible. (Except for composite proofs based on other proofs which use such case-splits).
When using with you basically add additional definitional equalities depending on the structure of the additional element. This only makes sense, understanding that, that you need to case-split on said element when doing proofs about such a function, in order for Agda once again to be able to make use of these definitional equalities.
Let us take your example and apply this reasoning to it, first without the recent ability to omit impossible cases. You need to prove the following statement:
prop-old : ∀ x y → fun x y ≡ nothing → x < y
Introducing parameters in the context, you write the following line:
prop-old x y p = ?
Having written that line, you need to provide a proof of x < y with the elements in the context. x and y are just natural so you expect p to hold enough information for this result to be provable. But, in this case, p is just of type fun x y ≡ nothing which does not give you enough information. However, this type contains a call to function fun so there is hope ! Looking at the definition of fun, we can see that it yields two definitional equalities, which depend on the structure of x <? y. This means that adding this parameter to the proof by using with once more will allow Agda to make use of these equalities. This leads to the following code:
prop-old : ∀ x y → fun x y ≡ nothing → x < y
prop-old x y p with x <? y
prop-old _ _ p | yes q = ?
prop-old _ _ p | no q = ?
At that point, not only did Agda case-split on x <? y, but it also reduced the goal because it is able, in both cases, to use a specific definitional equality of fun. Let us take a closer look at both cases:
In the yes q case, p is now of type nothing ≡ nothing and q is of type x < y which is exactly what you want to prove, which means the goal is simply solved by:
prop-old _ _ p | yes q = q
I the no q case, something more interesting happens, which is somewhat harder to understand. After reduction, p is now of type just y ≡ nothing because Agda could use the second definitional equality of fun. Since _≡_ is a data type, it is possible to case-split on p which basically asks Agda: "Look at this data type and give me all the possible constructors for an element of type just y ≡ nothing". At first, Agda only finds one possible constructor, refl, but this constructor only builds an element of a type where both sides of the equality are the same, which is not the case here by definition because just and nothing are two distinct constructors from the same data type, Maybe. Agda then concludes that there are no possible constructors that could ever build an element of such type, hence this case is actually not possible, which leads to Agda replacing p with the empty pattern () and dismissing this case. This line is thus simply:
prop-old _ _ () | no _
In the more recent versions of Agda, as I explained earlier, some of these steps are done directly by Agda which allows us to directly omit impossible cases when the emptiness of a pattern can be deduced behind the curtain, which leads to the prettier:
prop : ∀ x y → fun x y ≡ nothing → x < y
prop x y _ with x <? y
... | yes p = p
But it is the same process, just done a bit more automatically. Hopefully, these elements will be of some use in your journey towards understanding Agda.
open import Data.Nat using (ℕ;suc;zero)
open import Data.Rational
open import Data.Product
open import Relation.Nullary
open import Data.Bool using (Bool;false;true)
halve : ℕ → ℚ
halve zero = 1ℚ
halve (suc p) = ½ * halve p
∃-halve : ∀ {a b} → 0ℚ < a → a < b → ∃[ p ] (halve p * b < a)
∃-halve {a} {b} 0<a a<b = h 1 where
h : ℕ → ∃[ p ] (halve p * b < a)
h p with halve p * b <? a
h p | .true because ofʸ b'<a = p , b'<a
h p | .false because ofⁿ ¬b'<a = h (suc p)
The termination checker fails in that last case and it is no wonder as the recursion obviously is neither well funded nor structural. Nevertheless, I am quite sure this should be valid, but have no idea how to prove the termination of ∃-halve. Any advice for how this might be done?
When you’re stuck on a lemma like this, a good general principle is to forget Agda and its technicalities for a minute. How would you prove it in ordinary human-readable mathematical prose, in as elementary way as possible?
Your “iterated halving” function is computing b/(2^p). So you’re trying to show: for any positive rationals a, b, there is some natural p such that b/(2^p) < a. This inequality is equivalent to 2^p > b/a. You can break this down into two steps: find some natural n ≥ b/a, and then find some p such that 2^p > n.
As mentioned in comments, a natural way to do find such numbers would be to implement the ceiling function and the log_2 function. But as you say, those would be rather a lot of work, and you don’t need them here; you just need existence of such numbers. So you can do the above proof in three steps, each of which is elementary enough for a self-contained Agda proof, requiring only very basic algebraic facts as background:
Lemma 1: for any rational q, there’s some natural n > q. (Proof: use the definition of the ordering on rationals, and a little bit of algebra.)
Lemma 2: for any natural n, there’s some natural p such that 2^p > n. (Proof: take e.g. p := (n+1); prove by induction on n that 2^(n+1) > n.)
Lemma 3: these together imply the theorem about halving that you wanted. (Proof: a bit of algebra with rationals, showing that the b/(2^p) < a is equivalent to 2^p > b/a, and showing that your iterated-halving function gives b/2^p.)
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.)