I am using the Python bindings for the Z3 theorem prover (Z3Py). I have N boolean variables, x1,..,xN. I want to express the constraint that exactly K out of N of them should be true. How can I do that, in Z3Py? Is there any built-in support for that? I checked the online documentation, but the Z3Py docs don't have any mention of any API for that.
For one-out-of-N constraints, I know I can separately express that at least one is true (assert Or(x1,..,xN)) and that at most one is true (assert Not(And(xi,xj)) for all i,j). I also know of other ways to manually express the 1-out-of-N and K-out-of-N constraints. However I have the impression that when the solver has built-in support for this constraint, it can sometimes be more efficient than expressing it manually.
Yes, Z3Py has built-in support for this. There is an undocumented API for this, that isn't mentioned in the Z3Py docs: use PbEq. In particular, the expression
PbEq(((x1,1),(x2,1),..,(xN,1)),K)
will be true if exactly K out of the N boolean variables are set to true. There are some reports that this encoding will be faster than naive ways of manually expressing the constraint.
To express a 1-out-of-N constraint, just set K=1 and use PbEq. To express an at-most-K-out-of-N constraint, use PbLe. To express an at-least-K-out-of-N constraint, use PbGe.
You can express this in Python like this:
import z3
s = z3.Solver()
bvars = [z3.Bool("Var {0}".format(x)) for x in range(10)]
#Exactly 3 of the variables should be true
s.add( z3.PbEq([(x,1) for x in bvars], 3) )
s.check()
m = s.model()
s = z3.Solver()
bvars = [z3.Bool("Var {0}".format(x)) for x in range(10)]
#<=3 of the variables should be true
s.add( z3.PbLe([(x,1) for x in bvars], 3) )
s.check()
m = s.model()
s = z3.Solver()
bvars = [z3.Bool("Var {0}".format(x)) for x in range(10)]
#>=3 of the variables should be true
s.add( z3.PbGe([(x,1) for x in bvars], 3) )
s.check()
m = s.model()
Related
Consider these two formulae:
Exists y. Forall x. (y>x), which is unsat.
Forall x. Exists y. (y>x), which is sat.
Note that we cannot find “models” for the sat formula, e.g., using Z3 it outputs Z3Exception: model is not available for the following code:
phi = ForAll([x],Exists([y], lit))
s_def = Solver()
s_def.add(phi)
print(s_def.model())
Also, quantifier elimination does not output an elimination, but a satisfiability result
[[]] (i.e., True):
x, y = Reals('x, y')
t = Tactic("qe")
lit = (y>x)
ae = Goal()
ae.add(ForAll([x],Exists([y], lit)))
ae_qe = t(ae)
print(ae_qe)
I think this happens because the value of y fully depends on x (e.g., if x is 5 then y can be 6). Thus, I have some questions:
Am I right with this interpretation?
What would be the meaning of “a model of a universally quantified formula”?
Do we say a formula accepts quantifier elimination even if it never eliminates the quantifier but "only: evaluate to True or False?
Is there a way to synthetise or construct a model/function that represents a y that holds the constraint (y>x); e.g. f(x)=x+1. In other words, does it make sense that the quantifier elimination of a Forall x. Exists y. Phi(x,y) like the example would be Forall x. Phi(x,f(x))?
You get a model-not-available, because you didn't call check. A model is only available after a call to check. Try this:
from z3 import *
x, y = Ints('x y')
phi = ForAll([x], Exists([y], y > x))
s = Solver()
s.add(phi)
print(s.check())
print(s.model())
This prints:
sat
[]
Now, you're correct that you won't get a meaningful/helpful model when you have a universal-quantifier; because the model will depend on the choice of x in each case. (You only get values for top-level existentials.) This is why the model is printed as the empty-list.
Side Note In certain cases, you can use the skolemization trick (https://en.wikipedia.org/wiki/Skolem_normal_form) to get rid of the nested existential and get a mapping function, but this doesn't always work with SMT solvers as they can only build "finite" skolem functions. (For your example, there isn't such a finite function; i.e., a function that can be written as a case-analysis on the inputs, or an if-then-else chain.)
For your specific questions:
Yes; value of y depends on x and thus cannot be displayed as is. Sometimes skolemization will let you work around this, but SMT solvers can only build finite skolem-functions.
Model of a universally quantified formula is more or less meaningless. It's true for all values of the universally quantified variables. Only top-level existentials are meaningful in a model.
Quantifier elimination did work here; it got rid of the whole formula. Note that QE preserves satisfiability; so it has done its job.
This is the skolem function. As you noted, this skolem function is not finite (can't be written as a chain of finite if-then-elses on concrete values), and thus existing SMT solvers cannot find/print them for you. If you try, you'll see that the solver simply loops:
from z3 import *
x = Int('x')
skolem = Function('skolem', IntSort(), IntSort())
phi = ForAll([x], skolem(x) > x)
s = Solver()
s.add(phi)
print(s)
print(s.check())
print(s.model())
The above never terminates, unfortunately. This sort of problem is simply too complicated for the push-button approach of SMT solving.
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.)
I'm trying to represent sum of integers from an integer a to an integer b of the form
\sum_{i=a}^b i
Of courese there is a closed form solution for this version, but in general I'd like to sum over expressions parameterized by i. I've currently tried to define a function symSum and described its behavior using universal quantifiers:
from z3 import *
s = Solver()
symSum = Function('symSum', IntSort(), IntSort(), IntSort())
a = Int('a')
b = Int('b')
s.add(ForAll([a,b],If(a > b,symSum(a,b) == 0,symSum(a,b) == a + symSum(a+1,b))))
x = Int('x')
s.add(x == symSum(1,5))
print(s.check())
print(s.model())
I have not gotten this code to terminate (I have only allowed it to run for a couple minutes at max though). Is this outside the capabilities of Z3?
EDIT:
Looking into this a bit more I was able to use recursive functions to define this!
from z3 import *
ctx = Context()
symSum = RecFunction('symSum', IntSort(ctx), IntSort(ctx), IntSort(ctx))
a = Int('a',ctx)
b = Int('b',ctx)
RecAddDefinition(symSum, [a,b], If(a > b, 0, a + symSum(a+1,b)))
x = Int('x',ctx)
s = Solver(ctx=ctx)
s.add(symSum(1,5) == x)
print(s.check())
print(s.model())
Yes; this is beyond the current capabilities of SMT solvers.
Imagine how you'd prove something like this by hand. You'd have to do induction on the natural numbers. SMT solvers do not perform induction, at least not out-of-the box. You can coax them to do so with a lot of helper lemmas and careful guiding via what's known as patterns; but it's not something they're designed for, or even good at. At least not for the time being.
See this answer for more details regarding a relevant question: Is it possible to prove this defined function is an involution in z3?
Having said that, recent versions of SMTLib does include capabilities for definitions recursive functions. (See https://smtlib.cs.uiowa.edu/papers/smt-lib-reference-v2.6-r2017-07-18.pdf, Section 4.2.3.) And z3 and other solvers have some support for the new syntax, though they can't really prove any interesting properties of these functions. I suspect the direction of the community is that eventually some level of (user-guided) induction will become part of the toolbox offered by these solvers, but that is currently not the case for the time being.
I am trying to prove that
4*n^3*m+4*n*m^3 <= n^4+6*n^2*m^2+m^4
for all n, m reals; using Z3Py online.
I am using the code:
n, m = Reals('n m')
s = Solver()
s.add(ForAll([n, m], n**4+6*n**2*m**2+m**4 >= 4*n**3*m+4*n*m**3))
print s.check()
and the output is : unknown.
Please can you tell why Z3 does not obtain "sat".
Note that Z3 checks for "satisfiability" and not "validity".
A formula is valid if and only if the negation is not satisfiable (unsat).
So to prove validity of your inequality, you can add the negation of it to Z3 and see if it is able to reason about it.
n, m = Reals('n m')
s = Solver()
s.add(Not(n**4+6*n**2*m**2+m**4 >= 4*n**3*m+4*n*m**3))
print s.check()
It turns out that Z3 does establish that the inequality is unsat using the default Solver.
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.