Z3Py is not able to make certain proof? - z3

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.

Related

What does a model mean in a universally quantified formula? Is it a function?

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.

Power and logarithm in Z3

I'm trying to learn Z3 and the following example baffles me:
from z3 import *
a = Int("a")
b = Int("b")
print(solve(2**a <= b))
print(solve(a > 0, b > 0, 2**a <= b))
I would expect it returns "[a = 1, b = 2]" but it instead returns "failed to solve".
Why cannot it be solved?
Is it possible to compute with powers and logarithms in Z3 at all? How do I find, say, the length of binary string representation of a number (log base 2)?
Long story short, z3 (or SMT solvers in general) cannot deal with non-linear constraints like this. Exponentiation/Logs etc are difficult to deal with, and there are no decision procedures for them over the integers. Even over reals they are difficult to handle. That is, the solver will apply some heuristics, which may or may not work. But for these sorts of constraints, SMT solvers are just not the right tool.
For an earlier answer on non-linear arithmetic in z3, see this answer: https://stackoverflow.com/a/13898524/936310
Here're some more details if you are interested. First, there is no power-operator for integers in SMTLib or z3. If you look at the generated program, you'll see that it's actually over real values:
from z3 import *
a = Int("a")
b = Int("b")
s = Solver()
s.add(2**a <= b)
print(s.sexpr())
print(s.check())
This prints:
(declare-fun b () Int)
(declare-fun a () Int)
(assert (<= (^ 2 a) (to_real b)))
unknown
Note the conversion to to_real. The ^ operator automatically creates a real. The way this would be solved is if the solver can come up with a solution over reals, and then checks to see if the result is an integer. Let's see what happens if we try with Reals:
from z3 import *
a = Real("a")
b = Real("b")
s = Solver()
s.add(2**a <= b)
print(s.check())
print(s.model())
This prints:
sat
[b = 1, a = 0]
Great! But you also wanted a > 0, b > 0; so let's add that:
from z3 import *
a = Real("a")
b = Real("b")
s = Solver()
s.add(2**a <= b)
s.add(a > 0)
s.add(b > 0)
print(s.check())
This prints:
unknown
So, the solver can't handle this case either. You can play around with tactics (qfnra-nlsat), but it's unlikely to handle problems of this sort in general. Again, refer to https://stackoverflow.com/a/13898524/936310 for details.

K-out-of-N constraint in Z3Py

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

UNKNOWN when using def's

This simple example generates UNKNOWN for me, I suppose there is something that I don't understand about def's.
from z3 import *
s = Solver()
def Min(b, r):
return If(b, r, 1)
a = Real('a')
b = Bool('b')
s.add(a==0.9)
s.add(a<=Min(b,0.9))
s.check()
print "Presenting result "
m = s.model()
print "traversing model..."
for d in m.decls():
print "%s = %s" % (d.name(), m[d])
You did not make any mistake. This is a problem in one of Z3 solvers. Your problem is in a fragment of arithmetic called "difference-logic". A problem in in this fragment, if the arithmetic atoms can be written as x - y <= k, where k is a numeral. When a problem is in this fragment, Z3 will use a specialized solver for it. However, this solver may fail (returns unknown) when the input problem also contains if-then-else terms (the If in your Min).
The bug has been fixed, and will be available in the next release. In the meantime, you can try one of the following workarounds:
Force Z3 to eliminate if-then-else terms before invoking the solver. You just have to replace s = Solver() with s = Then('elim-term-ite', 'smt').solver(). Here is the modified script at rise4fun.
We can add a redundant assertion that is not in the different logic fragment. Example: z + w > 2. Here is the modified script at rise4fun.

How does Z3 deal with forall proposition with uninterpreted functions in it?

Suppose we have two uninterpreted functions func1 and func2:
stuct_sort func1(struct_sort);
stuct_sort func2(struct_sort ,int).
And they have the relationship:
func2(p,n)=func1(p) if n==1
func2(p,n)=func1(func2(p,n-1)) if n>1
What I want to know is that if the following proposition :
((forall i:[1,m].func2(p,i)==Z)&&(q==func1(p))) implies (forall i:[1,m-1].func2(q,i)==Z)
can be proved to be true in Z3?
In my program, the prove result is Z3_L_UNDEF.
When I assign m with a value such as 3, the proposition now is
((forall i:[1,3].func2(p,i)==Z)&&(q==func1(p))) implies (forall i:[1,3-1].func2(q,i)==Z);
the result is Z3_L_UNDEF.
But when I rewrite the case separately(not using forall) as follows, the result is true.
(func2(p,1)==Z)&&(func2(p,2)==Z)&&(func2(p,3)==Z)&&(q==func1(p)) implies (func2(q,1))&&(func2(q,2)).
I can't find out the reason and looking forward to your answer
I encoded your problem using the Z3 Python interface, and Z3 solved it. It found a counterexample for the conjecture.
Of course, I may have made a mistake when I encoded the problem. The Python code is in the end of the post. We can try it online at rise4fun. BTW, which version of Z3 are you using? I'm assuming you are using the C API. If that is the case, could you provide the C code you used to create the Z3 formulas? Another possibility is to create a log that records the interaction of your application and Z3. To create a log file, we have to execute Z3_open_log("z3.log"); before you execute any other Z3 API. We can use the log file to replay all the interaction between your application and Z3.
from z3 import *
# Declare stuct_sort
S = DeclareSort('stuct_sort')
I = IntSort()
# Declare functions func1 and func2
func1 = Function('func1', S, S)
func2 = Function('func2', S, I, S)
# More declarations
p = Const('p', S)
n = Int('n')
m = Int('m')
i = Int('i')
q = Const('q', S)
Z = Const('Z', S)
# Encoding of the relations
# func2(p,n)=func1(p) if n==1
# func2(p,n)=func1(func2(p,n-1)) if n>1
Relations = And(func2(p, 1) == func1(p),
ForAll([n], Implies(n > 1, func2(p, n) == func1(func2(p, n - 1)))))
# Increase the maximum line width for the Z3 Python formula pretty printer
set_option(max_width=120)
print Relations
# Encoding of the conjecture
# ((forall i:[1,m].func2(p,i)==Z)&&(q==func1(p))) implies (forall i:[1,m-1].func2(q,i)==Z)
Conjecture = Implies(And(q == func1(p), ForAll([i], Implies(And(1 <= i, i <= m), func2(p, i) == Z))),
ForAll([i], Implies(And(1 <= i, i <= m - 1), func2(q, i) == Z)))
print Conjecture
prove(Implies(Relations, Conjecture))

Resources