Accessing a function through z3py that's already been defined - z3

Does anybody please know if there's a way to interact with a function through z3py that I've already defined in Z3 and parsed using parse_smt2_string ? I'm a Z3 and python and Z3Py newbie so apologies if this is obvious, but I've not found any similar questions on here so far...
I've tried things of the form:
original = """
(define-fun MyFun () Real
....
)
"""
f = parse_smt2_string (original)
s = Solver()
s.add(f)
MyFun = Function('MyFun')
s.push()
s.add(MyFun <= 0.4)
s.check()
s.pop()
s.push()
s.add(MyFun >= 0.8)
s.check()
s.pop()
But this returns different results to if I add the assertions into the Z3 string I'm parsing. I think it's because it's not recognising that MyFun in python is the same as in the string. So presumably, I'm defining the function incorrectly, but I can't work out what I'm doing wrong. I've also tried MyFun = Function('MyFun', RealSort()) but that didn't work....
Can anyone help me out please? :)

Related

Lambda Functions for Z3Py

My goal is to find a construct within Z3Py which allows me to:
(1) Write propositions as a function of a variable. For eg theoretically, if I define P(x) = x < 3, then the code should allow me to access P(u) for some other variable u.
(2) And Z3 should be able to solve and find a model for such a construct.
I thought Z3's 'Lambda' function theoretically made sense. However with this construct neither can I do (1) or (2). As a concrete eg, suppose I have the following code:
u, x = Ints('u x')
P = Lambda( [x], x < 5 )
I = Lambda ([x], x < 3)
C1 = Not(Implies(P.body(), I.body() ))
s = Solver()
s.add(C1)
r = s.check()
print(r.__repr__())
s.add( Implies(P(u), u == 2) )
Run this code to get the output:
unknown
Traceback (most recent call last):
File "testfile.py", line 20, in <module>
s.add( Implies(P(u), u == 2) )
TypeError: 'QuantifierRef' object is not callable
There are two issues to fix here:
(1) Why does r._ repr_() have 'unknown' stored and not 'sat' i.e. Why isn't Z3 solving this system?
(2) In the final line, how can I get the predicate u < 5 from P i.e. in lambda calculus terminology, how do I do application of a function to a variable in Z3Py? Clearly P(u) does not work.
For this sort of modeling, you should simply use a regular python function:
from z3 import *
def P(x):
return x < 5
def I(x):
return x < 3
Then, to do the proof Q(x) => P(x), you'd use a quantifier:
dummy = Int('dummy')
C1 = ForAll([dummy], Implies(I(dummy), P(dummy)))
prove(C1)
This prints:
proved
Regarding your specific questions:
(1) Adding Implies(P.body(), Q.body()) means something completely different. If you run:
from z3 import *
x = Int('x')
P = Lambda( [x], x < 5 )
I = Lambda( [x], x < 3 )
s = Solver()
s.add(Implies(P.body(), I.body()))
print(s.sexpr())
You'll see it prints:
(assert (=> (< (:var 0) 5) (< (:var 0) 3)))
where :var is an internal free-variable generating function. This isn't an object you should be passing back and forth to z3; in fact, I think you're becoming a victim of the loosely typed nature of z3; this isn't a construct that really make much sense at all. Long story short, you should never look at P.body() or I.body() in your own code. I'd ignore the unknown result in this context; the input is more or less meaningless, and z3 spits out a nonsensical answer. A better system should've checked and complained about this; but this is not a strong point for z3's Python API.
(2) If you use a regular function, this isn't really a problem at all; because you're just doing regular application at the Python level. You can apply a lambda-bound value by directly calling it as well, though you need the notation P[u]. (Lambda's are similar to arrays in z3.) So, something like:
from z3 import *
u, x = Ints('u x')
P = Lambda([x], x < 5)
I = Lambda([x], x < 3)
s = Solver()
s.add(Implies(P[u], u == 2))
print(s.check())
print(s.model())
will print:
sat
[u = 2]
which is what you were looking for I think.
Multiple arguments
If you want to model a lambda with multiple arguments, the easiest way is to think of it as a nested construct. That is, you store a new lambda at each index. Here's an example:
from z3 import *
dummy1 = FreshInt()
dummy2 = FreshInt()
P = Lambda([dummy1], Lambda([dummy2], dummy1 < dummy2))
s = Solver()
x, y = Ints('x y')
s = Solver()
s.add(P[x][y])
print(s.check())
print(s.model())
This prints:
sat
[y = 1, x = 0]
Note that the above also demonstrates the use of the FreshInt function, which avoids name-clashes by providing a unique name each time it is called.

Transitive Closure in Z3/Z3py

Context: I'm using the Programming Z3 guide:
https://theory.stanford.edu/~nikolaj/programmingz3.html
It looks like Z3 now has built-in support for transitive-closure, but for the moment it's only accessible via Z3py:
https://theory.stanford.edu/~nikolaj/programmingz3.html#sec-transitive-closure
I have two questions:
(1) Is there any means of accessing TransitiveClosure via other APIs or the executable directly via "z3 -in", or is it just for Z3py at the moment?
(2) Is TransitiveClosure supposed to interoperate with push and pop? We built from the master branch earlier this week (commit 2788f72bbb6bfd6bdad2da2b4c37ef1bb502469d) and ran the following example:
from z3 import *
B = BoolSort()
S = DeclareSort('S')
a, b, c = Consts('a b c', S)
R = Function('R', S, S, B)
TCR = TransitiveClosure(R)
s = Solver()
s.add(R(a, b) == True)
s.push() # If this line is uncommented (or both are) the result is sat, which is wrong.
s.add(TCR(a, b) == False)
s.push() # But if THIS line is uncommented (or neither are), the result is unsat, which is correct.
print(s)
print(s.check())
As the comments indicate, a push() call between assertions about R and its transitive closure seem to break the link between the two relations. Not sure if this is a bug, or my own misunderstanding...
From the looks of it, it's also available via the C/C++ api as well:
https://github.com/Z3Prover/z3/blob/62de187d02d8d2e7a3667a31753c508f7c73aaa1/src/api/c%2B%2B/z3%2B%2B.h#L637-L639
I don't think it's available from the SMTLib interface (what you meant by z3 -in I presume), as it's itself returning a relation; and such higher-order constructs are usually not allowed in SMTLib. (But there might be a "magic" switch, of course; z3 is known to experiment with functionality that's not part of SMTLib.)
Regarding whether it should work with push/pop: I don't think the fixed-point engine in z3 allows for incremental solving; so I'm not surprised that it's behaving erratically. You should definitely report this behaviour at their issues site (https://github.com/Z3Prover/z3/issues) so they can at least issue an error message if you try to do incremental stuff, instead of spitting out misleading info. (Or maybe you hit a bug! So, that'd also be good for them to know.)
s.pop(),s.push() is not necessary.
# https://theory.stanford.edu/~nikolaj/programmingz3.html#sec-transitive-closure
from z3 import *
B = BoolSort()
S = DeclareSort('S')
R = Function('R', S, S, B)
TCR = TransitiveClosure(R)
a, b, c = Consts('a b c', S)
s = Solver()
s.add(R(a, b))
s.add(R(b, c))
s.add(Not(TCR(a, c))) # s.add(Not(R(a, c))) will be “sat”,because TCR is transitiveclosure but R is not.
print(s)
print(s.check()) # unsat

z3py: assumptions from (check-sat ...) statement

Is there a way to pass assumptions from (check-sat ...) statement of SMT2 formula into the solver ?
Consider the following example formula stored in ex.smt2:
# cat ex.smt2
(declare-fun p () Bool)
(assert (not p))
(check-sat p)
Running z3 on it gives unsat, as expected. Now, I'd like to solve with assumptions (p) through z3py interface:
In [30]: ctx = z3.Context()
In [31]: s = z3.Solver(ctx=ctx)
In [32]: f = z3.parse_smt2_file("ex.smt2", ctx=ctx)
In [33]: s.add(f)
In [34]: s.check()
Out[34]: sat
Is there an API to get assumptions (i.e. (p) in this example) from the parser ? Or even better, just tell the solver to solve with the assumptions read from the input file ?
No, there is no such API. The parse_smt2_file API is very simple, and only provides access to the assertions in the input file. Extending this API is in the TODO list, but nobody is currently working on that.

Z3Py is not able to make certain proof?

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.

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.

Resources