Synthesizing a boolean expression based on several constraints - z3

What I need is pretty straight-forward, I'm looking for a tool/library that gets several
constraints in a format like this:
{"vars": {a=2, b=1, c=3}, "eval": "False"}
{"vars": {a=1, b=1, c=2}, "eval": "True"}
{"vars": {a=1, b=2, c=1}, "eval": "True"}
And then synthesize a boolean expression for me based on predefined symbols:
Symbols: ["between variables": ['<=', '>=', '<', '>', '==', '!='], "between expressions": ['and', 'or']]
I need only one solution for this system; for instance, one can be
a == b or b == c or a == c
I've been looking into Z3 library features but couldn't find anything useful. Please do inform me if you know about an appropriate tool.

Program synthesis is a very active area of research; and the quality of the programs you'll get really depends on what sort of program templates you want to use. An SMT solver will most likely not give you the kind of programs that you're looking for out-of-the-box, although they will work.
Here's how I'd code your current problem: I'd use an uninterpreted function and add the "test-cases" as constraints. Like this:
(declare-fun f (Int Int Int) Bool)
(assert (not (f 2 1 3)))
(assert (f 1 1 2))
(assert (f 1 2 1))
(check-sat)
(get-model)
When I run this, z3 says:
sat
(
(define-fun f ((x!0 Int) (x!1 Int) (x!2 Int)) Bool
(ite (and (= x!0 2) (= x!1 1) (= x!2 3)) false
true))
)
If you read the output, you'll see that it essentially synthesized the following program:
if (a == 2) && (b == 1) && (c == 3)
then False
else True
Is this correct? Absolutely. Is this really what you wanted it to do? Most likely not. But out-of-the box you'll not get anything better from an SMT solver.
For synthesizing cool programs using SMT based technologies, see, for instance, Emina Torlak's research. Here's a good paper to start with: http://synapse.uwplse.org

Related

How to declare forall quantifiers in SMTLIB / Z3 / CVC4?

I'm stuck on how to how to create a statement in SMTLIB2 that asserts something like
forall x < 100, f(x) = 100
This property would be check a function that adds 1 to all numbers less than 100 recursively:
(define-fun-rec incUntil100 ((x Int)) Int
(ite
(= x 100)
100
(incUntil100 (+ x 1))
)
)
I read through the Z3 tutorial on quantifiers and patterns, but that didn't seem to get me much anywhere.
In SMTLib, you'd write that property as follows:
(assert (forall ((x Int)) (=> (< x 100) (= (incUntil100 x) 100))))
(check-sat)
But if you try this, you'll see that z3 will loop forever and CVC4 will tell you unknown as the answer. While you can define and assert these sorts of functions in SMTLib, solver support for actual proofs is rather weak, as they do not do induction out-of-the box.
If proving properties of recursive functions is your goal, SMT-solvers are not a good choice; instead look into theorem provers such as Isabelle, HOL, Coq, Lean, ACL2, etc.; which are built for that very purpose.

Modeling a small programming language and analysis in SMT-LIB using datatypes and forall

I am trying to model a small programming language in SMT-LIB 2.
My intent is to express some program analysis problems and solve them with Z3.
I think I am misunderstanding the forall statement though.
Here is a snippet of my code.
; barriers.smt2
(declare-datatype Barrier ((barrier (proc Int) (rank Int) (group Int) (complete-time Int))))
; barriers in the same group complete at the same time
(assert
(forall ((b1 Barrier) (b2 Barrier))
(=> (= (group b1) (group b2))
(= (complete-time b1) (complete-time b2)))))
(check-sat)
When I run z3 -smt2 barriers.smt2 I get unsat as the result.
I am thinking that an instance of my analysis problem would be a series of forall assertions like the above and a series of const declarations with assertions that describe the input program.
(declare-const b00 Barrier)
(assert (= (proc b00) 0))
(assert (= (rank b00) 0))
...
But apparently I am using the forall expression incorrectly because I expected z3 to decide that there was a satisfying model for that assertion. What am I missing?
When you declare a datatype like this:
(declare-datatype Barrier
((barrier (proc Int)
(rank Int)
(group Int)
(complete-time Int))))
you are generating a universe that is "freely" generated. That's just a fancy word for saying there is a value for Barrier for each possible element in the cartesian product Int x Int x Int x Int.
Later on, when you say:
(assert
(forall ((b1 Barrier) (b2 Barrier))
(=> (= (group b1) (group b2))
(= (complete-time b1) (complete-time b2)))))
you are making an assertion about all possible values of b1 and b2, and you are saying that if groups are the same then completion times must be the same. But remember that datatypes are freely generated so z3 tells you unsat, meaning that your assertion is clearly violated by picking up proper values of b1 and b2 from that cartesian product, which have plenty of inhabitant pairs that violate this assertion.
What you were trying to say, of course, was: "I just want you to pay attention to those elements that satisfy this property. I don't care about the others." But that's not what you said. To do so, simply turn your assertion to a function:
(define-fun groupCompletesTogether ((b1 Barrier) (b2 Barrier)) Bool
(=> (= (group b1) (group b2))
(= (complete-time b1) (complete-time b2))))
then, use it as the hypothesis of your implications. Here's a silly example:
(declare-const b00 Barrier)
(declare-const b01 Barrier)
(assert (=> (groupCompletesTogether b00 b01)
(> (rank b00) (rank b01))))
(check-sat)
(get-model)
This prints:
sat
(model
(define-fun b01 () Barrier
(barrier 3 0 2437 1797))
(define-fun b00 () Barrier
(barrier 2 1 1236 1796))
)
This isn't a particularly interesting model, but it is correct nonetheless. I hope this explains the issue and sets you on the right path to model. You can use that predicate in conjunction with other facts as well, and I suspect in a sat scenario, that's really what you want. So, you can say:
(assert (distinct b00 b01))
(assert (and (= (group b00) (group b01))
(groupCompletesTogether b00 b01)
(> (rank b00) (rank b01))))
and you'd get the following model:
sat
(model
(define-fun b01 () Barrier
(barrier 3 2436 0 1236))
(define-fun b00 () Barrier
(barrier 2 2437 0 1236))
)
which is now getting more interesting!
In general, while SMTLib does support quantifiers, you should try to stay away from them as much as possible as it renders the logic semi-decidable. And in general, you only want to write quantified axioms like you did for uninterpreted constants. (That is, introduce a new function/constant, let it go uninterpreted, but do assert a universally quantified axiom that it should satisfy.) This can let you model a bunch of interesting functions, though quantifiers can make the solver respond unknown, so they are best avoided if you can.
[Side note: As a rule of thumb, When you write a quantified axiom over a freely-generated datatype (like your Barrier), it'll either be trivially true or will never be satisfied because the universe literally will contain everything that can be constructed in that way. Think of it like a datatype in Haskell/ML etc.; where it's nothing but a container of all possible values.]
For what it is worth I was able to move forward by using sorts and uninterpreted functions instead of data types.
(declare-sort Barrier 0)
(declare-fun proc (Barrier) Int)
(declare-fun rank (Barrier) Int)
(declare-fun group (Barrier) Int)
(declare-fun complete-time (Barrier) Int)
Then the forall assertion is sat. I would still appreciate an explanation of why this change made a difference.

What additional axioms do we need to add so that Z3 can verify the satisfiability of programs with recurrences?

As we know Z3 has limitations with recurrences. Is there any way get the result for the following program? what will additional equation help z3 get the result?
from z3 import *
ackermann=Function('ackermann',IntSort(),IntSort(),IntSort())
m=Int('m')
n=Int('n')
s=Solver()
s.add(ForAll([n,m],Implies(And(n>=0,m>=0),ackermann(m,n) == If(m!=0,If(n!=0,ackermann(m - 1,ackermann(m,n - 1)),If(n==0,ackermann(m - 1,1),If(m==0,n + 1,0))),If(m==0,n + 1,0)))))
s.add(n>=0)
s.add(m>=0)
s.add(Not(Implies(ackermann(m,n)>=0,ackermann(m+1,0)>=0)))
s.check()
With a nested recursive definition like Ackermann's function, I don't think there's much you can do to convince Z3 (or any other SMT solver) to actually do any interesting proofs. Such properties will require clever inductive arguments, and an SMT solver is just not the right tool for this sort of verification. A theorem prover like Isabelle, HOL, Coq, ... is the better choice here.
Having said that, the basic approach to establishing recursive function properties in SMT is to literally code up the inductive hypothesis as a quantified axiom, and arrange for the property you want proven to precisely line up with that axiom when the e-matching engine kicks in so it can instantiate the quantifiers "correctly." I'm putting the word correctly in quotes here, because the matching engine will go ahead and keep instantiating the axiom in unproductive ways especially for a function like Ackermann's. Theorem provers, on the other hand, precisely give you control over the proof structure so you can explicitly guide the prover through the proof-search space.
Here's an example you can look at: list concat in z3 which is doing an inductive proof of a much simpler inductive property than you are targeting, using the SMT-Lib interface. While it won't be easy to extend it to handle your particular example, it might provide some insight into how to go about it.
In the particular case of Z3, you can also utilize its fixed-point reasoning engine using the PDR algorithm to answer queries about certain recursive functions. See http://rise4fun.com/z3/tutorialcontent/fixedpoints#h22 for an example that shows how to model McCarthy's famous 91 function as an interesting case study.
Z3 will not try to do anything by induction for you, but (as Levent Erkok mentioned) you can give it the induction hypothesis and have it check that the result follows.
This works on your example as follows.
(declare-fun ackermann (Int Int) Int)
(assert (forall ((m Int) (n Int))
(= (ackermann m n)
(ite (= m 0) (+ n 1)
(ite (= n 0) (ackermann (- m 1) 1)
(ackermann (- m 1) (ackermann m (- n 1))))))))
(declare-const m Int)
(declare-const n Int)
(assert (>= m 0))
(assert (>= n 0))
; Here's the induction hypothesis
(assert (forall ((ihm Int) (ihn Int))
(=> (and (<= 0 ihm) (<= 0 ihn)
(or (< ihm m) (and (= ihm m) (< ihn n))))
(>= (ackermann ihm ihn) 0))))
(assert (not (>= (ackermann m n) 0)))
(check-sat) ; reports unsat as desired

Z3: eliminate don't care variables

I have a test.smt2 file:
(set-logic QF_IDL)
(declare-const a Int)
(declare-const b Int)
(declare-const c Int)
(assert (or (< a 2) (< b 2 )) )
(check-sat)
(get-model)
(exit)
Is there anyway to tell Z3 to only output a=1 (or b=1)? Because when a is 1, b's value does not matter any more.
I executed z3 smt.relevancy=2 -smt2 test.smt2
(following How do I get Z3 to return minimal model?, although smt.relevancy seems has default value 2), but it still outputs:
sat
(model
(define-fun b () Int
2)
(define-fun a () Int
1)
)
Thank you!
The example given in the answer to the question referred to is slightly out of date. A Solver() will pick a suitable tactic to solve the problem, and it appears that it picks a different one now. We can still get that behavior by using a SimpleSolver() (at a possibly significant performance loss). Here's an updated example:
from z3 import *
x, y = Bools('x y')
s = SimpleSolver()
s.set(auto_config=False,relevancy=2)
s.add(Or(x, y))
print s.check()
print s.model()
Note that the (check-sat) command will not execute the same tactic as the SimpleSolver(); to get the same behavior when solving SMT2 files, we need to use the smt tactic, i.e., use (check-sat-using smt). In many cases it will be beneficial to additionally run the simplifier on the problem first which we can achieve by constructing a custom tactic, e.g., (check-sat-using (then simplify smt))

Does not 'check-sat' support Boolean function as assumption?

In the following example, I tried to use uninterpreted Boolean function like "(declare-const p (Int) Bool)" rather than single Boolean constant for each assumption. But it does not work (it gives compilation error).
(set-option :produce-unsat-cores true)
(set-option :produce-models true)
(declare-fun p (Int) Bool)
;(declare-const p1 Bool)
;(declare-const p2 Bool)
; (declare-const p3 Bool)
;; We assert (=> p C) to track C using p
(declare-const x Int)
(declare-const y Int)
(assert (=> (p 1) (> x 10)))
;; An Boolean constant may track more than one formula
(assert (=> (p 1) (> y x)))
(assert (=> (p 2) (< y 5)))
(assert (=> (p 3) (> y 0)))
(check-sat (p 1) (p 2) (p 3))
(get-unsat-core)
Output
Z3(18, 16): ERROR: invalid check-sat command, 'not' expected, assumptions must be Boolean literals
Z3(19, 19): ERROR: unsat core is not available
I understand that it is not possible (unsupported) to use Boolean function. Is there any reason behind that? Is there different way to do that?
We have this restriction because Z3 applies many simplifications before it solves a problem. Some of them will rewrite formulas and terms. The problem that is actually solved by Z3 is very often quite different from the input problem. We would have trace back the simplified assumptions to the original assumptions, or introduce auxiliary variables. Restricting to Boolean literals avoids this issue, and makes the interface very clean. Note that this restriction does not limit the expressiveness. If you think it is too annoying to declare many Boolean variables to track different assertions. I suggest you take a look at the new Python front-end for Z3 called Z3Py. It is much more convenient to use than SMT 2.0. Here is your example in Z3Py: http://rise4fun.com/Z3Py/cL
In this example, instead of creating an uninterpreted predicate p, a "vector" (actually, it is a Python list) o Boolean constants is created.
The Z3Py online tutorial contains many examples.
It is also possible to implement in Z3Py the approach that creates auxiliary variables.
Here is the script that does the trick. I defined a function check_ext that does all the plumbing. http://rise4fun.com/Z3Py/B4

Resources