How do I evaluate a quantification expression within a Z3 model evaluation? - z3

Consider the following, which creates a simple model where a string is equal to a specific value:
from z3 import *
s = Solver()
x = String('x')
s.add(x == StringVal('foo'))
s.check()
I then want to ask whether there exists a string that is equal to x's model value:
y = String('y')
expr = Exists(y, y == x)
print(s.model().evaluate(expr))
I would expect this to return a boolean value, but instead the output is itself an expression:
Exists(y, y == "foo")
How do I get this expression to be actually evaluated within the evaluate method, instead of coughed back up? I'm assuming evaluate doesn't like the fact that I introduced a new unbound variable, but surely putting that variable within an Exists (or for that matter, a ForAll) is sufficient for the expression to be evaluated? Is there a way to write quantification methods without introducing a new unbound variable?

Turns out quantification is slightly beyond what Z3 supports in the evaluate method (which makes sense). The workaround is to re-run s.check() if evaluate returns a QuantifierRef:
result = s.model().evaluate(expr)
print(result)
if isinstance(result, QuantifierRef):
s.push()
s.add(result)
print(s.check())
s.pop()
Of course, you can also ask why you're even bothering with an evaluate call instead of just putting expr as an additional constraint before the call to check.

Related

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

What is the operator "(>=)" called in this context?

Now I understand that the first line of code can be shortened to the second one. This is the first time I'm running into it and I can't find any documentation on what the operator is called. Is it an abstract concept that can be used for other things as well?
let contains optValue value =
Option.exists (fun v -> v >= value) optValue
let contains optValue value =
Option.exists ((>=) value) optValue
You've already been told that the second example should have been (=) for your two functions to be equivalent, so I won't go over that. But I want to warn you that using the >= operator in this way might work differently than what you expect. The underlying reason has to do with how F# does partial application, and https://fsharpforfunandprofit.com/series/thinking-functionally.html is the best reference for that. (The relevant parts are the articles on currying and partial application, but you'll want to read the whole thing in order since later articles build on concepts explained in earlier articles).
Now, if you've read those articles, you know that F# allows partial application of functions: if you have a two-parameter function f a b, but you call it with just one parameter f a, the result is a function that is expecting a parameter b, and when it receives that, it executes f a b. When you wrap an operator in parentheses, F# treats it like a function, so when you do (>=) value, you get a function that's awaiting a second parameter x, and will then evaluate (>=) value x. And when F# evaluates (op) a b, whatever the operator is, it's the same as a op b, so (>=) value x is the same as value >= x.
And that's the bit that trips most people up at first. Because when you read Option.exists ((>=) value) optValue, you naturally want to read that as "Does the option contain something greater than or equal to value"? But in fact, it's actually saying "Does the option contain a value x such that value >= x is true?", i.e., something less than or equal to value.
So the simple rules of partial application, applied consistently, can lead to unexpected results with greater-than or less-than operators, or in fact any operator that isn't commutative. Be aware of this, and if you want to use partial application with non-commutative operators, double-check your logic.

Does z3 distinguish variables by their names?

Suppose we have the following code:
context c;
solver s(c);
expr x=c.bool_const("a");
expr y=c.bool_const("a");
expr z=c.bool_const("b");
s.add(x&&!y);
std::cout<<s.check();
s.reset();
s.add(x&&!z);
std::cout<<s.check();
The executing result of above code is 'unsat sat';
The result shows that z3 regards x and y as the same variable.
Does z3 distinguish variables by their names?
And if I use the same variable in different places, can I write the code as following:
context c;
solver(s);
function test1(){
s.add(c.bool_const("a"));
}
function test2(){
s.add(!c.bool_const("a"));
}
Dose the function 'test1' and 'test2" manipulate the same variable?
You seem to confuse the programming variables x and y that exist in the host language C++ with logical variables a and b that Z3 reasons about. x and y both represent the logical variable a, and by adding x && !y to the constraints you give Z3 the fact a && !a, which makes your constraints unsatisfiable.

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.

Ranges A to B where A > B in F#

I've just found something I'd call a quirk in F# and would like to know whether it's by design or by mistake and if it's by design, why is it so...
If you write any range expression where the first term is greater than the second term the returned sequence is empty. A look at reflector suggests this is by design, but I can't really find a reason why it would have to be so.
An example to reproduce it is:
[1..10] |> List.length
[10..1] |> List.length
The first will print out 10 while the second will print out 0.
Tests were made in F# CTP 1.9.6.2.
EDIT: thanks for suggesting expliciting the range, but there's still one case (which is what inspired me to ask this question) that won't be covered. What if A and B are variables and none is constantly greater than the other although they're always different?
Considering that the range expression does not seem to get optimized at compiled time anyway, is there any good reason for the code which determines the step (not explicitly specified) in case A and B are ints not to allow negative steps?
As suggested by other answers, you can do
[10 .. -1 .. 1] |> List.iter (printfn "%A")
e.g.
[start .. step .. stop]
Adam Wright - But you should be able
to change the binding for types you're
interested in to behave in any way you
like (including counting down if x >
y).
Taking Adam's suggestion into code:
let (..) a b =
if a < b then seq { a .. b }
else seq { a .. -1 .. b }
printfn "%A" (seq { 1 .. 10 })
printfn "%A" (seq { 10 .. 1 })
This works for int ranges. Have a look at the source code for (..): you may be able to use that to work over other types of ranges, but not sure how you would get the right value of -1 for your specific type.
What "should" happen is, of course, subjective. Normal range notation in my mind defines [x..y] as the set of all elements greater than or equal to x AND less than or equal to y; an empty set if y < x. In this case, we need to appeal to the F# spec.
Range expressions expr1 .. expr2 are evaluated as a call to the overloaded operator (..), whose default binding is defined in Microsoft.FSharp.Core.Operators. This generates an IEnumerable<_> for the range of values between the given start (expr1) and finish (expr2) values, using an increment of 1. The operator requires the existence of a static member (..) (long name GetRange) on the static type of expr1 with an appropriate signature.
Range expressions expr1 .. expr2 .. expr3 are evaluated as a call to the overloaded operator (.. ..), whose default binding is defined in Microsoft.FSharp.Core.Operators. This generates an IEnumerable<_> for the range of values between the given start (expr1) and finish (expr3) values, using an increment of expr2. The operator requires the existence of a static member (..) (long name GetRange) on the static type of expr1 with an appropriate signature.
The standard doesn't seem to define the .. operator (a least, that I can find). But you should be able to change the binding for types you're interested in to behave in any way you like (including counting down if x > y).
In haskell, you can write [10, 9 .. 1]. Perhaps it works the same in F# (I haven't tried it)?
edit:
It seems that the F# syntax is different, maybe something like [10..-1..1]
Ranges are generally expressed (in the languages and frameworks that support them) like this:
low_value <to> high_value
Can you give a good argument why a range ought to be able to be expressed differently? Since you were requesting a range from a higher number to a lower number does it not stand to reason that the resulting range would have no members?

Resources