How to declare/use Arrays and Quantifiers in Z3Py? - z3

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.

Related

Foundations of software analysis

i have question about Mathematical Foundations
Let (D, ⊑) be a partial order, where D is a finite, non-empty set. An anti-chain is a set of elements, where any distinct two elements are incomparable. Prove that the minimum number ofanti-chains Si , for 1 ≤ i ≤ n, that are required to cover D, i.e., D = ∪ Si ( i= 1 to n ) , is equal to the length of the longest chain in D.
i have any idea about it.

In Z3, I cannot understand result of quantifier elimination of Exists y. Forall x. (x>=2) => ((y>1) /\ (y<=x))

In Z3-Py, I am performing quantifier elimination (QE) over the following formulae:
Exists y. Forall x. (x>=2) => ((y>1) /\ (y<=x))
Forall x. Exists y. (x>=2) => ((y>1) /\ (y<=x)),
where both x and y are Integers. I did QE in the following way:
x, y = Ints('x, y')
t = Tactic("qe")
negS0= (x >= 2)
s1 = (y > 1)
s2 = (y <= x)
#EA
ea = Goal()
ea.add(Exists([y],Implies(negS0, (ForAll([x], And(s1,s2))))))
ea_qe = t(ea)
print(ea_qe)
#AE
ae = Goal()
ae.add(ForAll([x],Implies(negS0, (Exists([y], And(s1,s2))))))
ae_qe = t(ae)
print(ae_qe)
Result QE for ae is as expected: [[]] (i.e., True). However, as for ea, QE outputs: [[Not(x, >= 2)]], which is a results that I do not know how to interpret since (1) it has not really performed QE (note the resulting formula still contains x and indeed does not contain y which is the outermost quantified variable) and (2) I do not understand the meaning of the comma in x, >=. I cannot get the model either:
phi = Exists([y],Implies(negS0, (ForAll([x], And(s1,s2)))))
s_def = Solver()
s_def.add(phi)
print(s_def.model())
This results in the error Z3Exception: model is not available.
I think the point is as follows: since (x>=2) is an implication, there are two ways to satisfy the formula; by making the antecedent False or by satisfying the consequent. In the second case, the model would be y=2. But in the first case, the result of QE would be True, thus we cannot get a single model (as it happens with a universal model):
phi = ForAll([x],Implies(negS0, (Exists([y], And(s1,s2)))))
s_def = Solver()
s_def.add(phi)
print(s_def.model())
In any case, I cannot 'philosophically' understand the meaning of a QE of x where x is part of the (quantifier-eliminated) answer.
Any help?
There are two separate issues here, I'll address them separately.
The mysterious comma This is a common gotcha. You declared:
x, y = Ints('x, y')
That is, you gave x the name "x," and y the name "y". Note the comma after the x in the name. This should be
x, y = Ints('x y')
I guess you can see the difference: The name you gave to the variable x is "x," when you do the first; i.e., comma is part of the name. Simply skip the comma on the right hand side, which isn't what you intended anyhow. And the results will start being more meaningful. To be fair, this is a common mistake, and I wish the z3 developers ignored the commas and other punctuation in the string you give; but that's just not the case. They simply break at whitespace.
Quantification
This is another common gotcha. When you write:
ea.add(Exists([y],Implies(negS0, (ForAll([x], And(s1,s2))))))
the x that exists in negS0 is not quantified over by your ForAll, since it's not in the scope. Perhaps you meant:
ea.add(Exists([y],ForAll([x], Implies(negS0, And(s1,s2)))))
It's hard to guess what you were trying to do, but I hope the above makes it clear that the x wasn't quantified. Also, remember that a top-level exist quantifier in a formula is more or less irrelevant. It's equivalent to a top-level declaration for all practical purposes.
Once you make this fix, I think things will become more clear. If not, please ask further clarifying questions. (As a separate question on Stack-overflow; as edits to existing questions only complicate the matters.)

Using sets and SetHasSize on intersections in z3

I've been trying to enumerate solutions to the following problem. I've started with simple approach first.
Below I have two sets of size k, intersection between them is of size 1 and I want to see how sets A and B look:
Els, elems = EnumSort('Els',['a1', 'a2', 'a3'])
A, B = Consts('A B', SetSort(Els))
k, c = Ints('k c')
s = Solver()
s.add(SetHasSize(A, k))
s.add(SetHasSize(B, k))
s.add(k == 2, c == 1)
s.add(SetHasSize(SetIntersect(A, B), c))
s.check()
s.model()
Here, the solution should be A == ['a1', 'a2'] and B == ['a2', 'a3'] but satisfiability was not reached.
Even a simple task like one below results in never ending execution:
V, _ = EnumSort('Els',['a1', 'a2', 'a3'])
A = Const('A', SetSort(V))
k = Int('k')
s = SimpleSolver()
s.add(SetHasSize(A, k))
s.add(k == IntVal(2))
s.check()
s.model()
Changing k == IntVal(2) to k <= IntVal(2) makes the problem satisfiable and returns [A = ∃k!0 : k!0 = a1 ∨ k!0 = a2, k = 2] as a model of the set. I'm not sure if there is a faster approach.
If I run your program, I get:
WARNING: correct handling of finite domains is TBD
WARNING: correct handling of finite domains is TBD
WARNING: correct handling of finite domains is TBD
before it starts looping. This is a known issue in the implementation: Z3 cannot really deal with sets that have a finite domain.
Alas, replacing it with an infinite domain doesn't help either. Making this change:
A, B = Consts('A B', SetSort(IntSort()))
You get:
unsat
which is clearly bogus. I strongly suspect this is related to the following issue: https://github.com/Z3Prover/z3/issues/3854 (In short, the SMTLib frontend does not support set-has-size. For whatever reason, they left it in the Python and C/C++ interfaces, but clearly it's not really functional.)
So, strictly speaking, this is a bug. But more realistic answer is that set-has-size is no longer supported by z3. You should file an issue at https://github.com/Z3Prover/z3/issues so the authors are aware of this problem and perhaps remove it from the API completely if it won't ever be supported.
UPDATE:
As of this commit, z3 no longer accepts the set-has-size predicate; it is no longer supported.

How to prove that the halving function over positive rationals always has an existential?

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.)

Prove() method, but ignore some 'free' variables?

I have 2 formulas F1 and F2. These two formulas share most variables, except some 'temporary' (or I call them 'free') variables having different names, that are there for some reasons.
Now I want to prove F1 == F2, but prove() method of Z3 always takes into account all the variables. How can I tell prove() to ignore those 'free' variables, and focuses only on a list of variables I really care about?
I mean with all the same input to the list of my variables, if at the output time, F1 and F2 have the same value of all these variables (regardless the values of 'free' variables), then I consider them 'equivalence'
I believe this problem has been solved in other researches before, but I dont know where to look for the information.
Thanks so much.
We can use existential quantifiers to capture 'temporary'/'free' variables.
For example, in the following example, the formulas F and G are not equivalent.
x, y, z, w = Ints('x y z w')
F = And(x >= y, y >= z)
G = And(x > z - 1, w < z)
prove(F == G)
The script will produce the counterexample [z = 0, y = -1, x = 0, w = -1].
If we consider y and w as 'temporary' variables, we may try to prove:
prove(Exists([y], F) == Exists([w], G))
Now, Z3 will return proved. Z3 is essentially showing that for all x and z, there is a y that makes F true if and only if there is a w that makes G true.
Here is the full example.
Remark: when we add quantifiers, we are making the problem much harder for Z3. It may return unknown for problems containing quantifiers.
Apparently, I cannot comment, so I have to add another answer. The process of "disregarding" certain variables is typically called "projection" or "forgetting". I am not familiar with it in contexts going beyond propositional logic, but if direct existential quantification is possible (which Leo described), it is conceptually the simplest way to do it.

Resources