Weighted MaxSMT Z3 - z3

I was going through the Z3 (Objective functions) tutorial and came across the following example:
(declare-const a Bool)
(declare-const b Bool)
(declare-const c Bool)
(assert-soft a :weight 1 :id A)
(assert-soft b :weight 2 :id B)
(assert-soft c :weight 3 :id A)
(assert (= a c))
(assert (not (and a b)))
(check-sat)
(get-model)
I don't understand Z3's output:
A |-> 0
B |-> 2
sat
(model (define-fun c () Bool true)
(define-fun b () Bool false)
(define-fun a () Bool true) )
My understanding is that Weighted MaxSMT tries to maximize weighted sum of boolean expressions. Therefore, are we supposed to get A |->4 and B|->0 since both a and c are true and b is false.

In an assert-soft call, the weights are "penalty" values you are willing to accrue if you were to choose not to satisfy that goal. So, in this assignment, Z3 is picking a and c as true, hence satisfying the first and the third assert-soft constraints, thus taking no penalty for them. This means A |-> 0, since all soft constraints in this group are satisfied.
Of course, to make the problem sat, picking a and c to be true requires b to be false, and hence you accrue a penalty of 2 for that failed constraint group.
Note that z3 has to satisfy all regular assert constraints. In addition to that, it tries to satisfy as "many" of the assert-soft constraints as possible, minimizing the penalty accrued for those that are not satisfied.
This paper has an excellent description of Z3's optimization features.

Related

Reducing Unrestricted SAT (USAT) into 3-SAT

I know the procedure simply by transforming each clause l1 ∨ ⋯ ∨ ln to a conjunction of n − 2 clauses .
I try to show the values satisfying the original and the final formula with Z3 to show that they are equisatisfiable as an SMT file.
For clarify can you give any example about this procedure . Thank you.
Equivalence checking is done by asking the solver if there's an assignment that can distinguish two formulas. If there's no such assignment, then you can conclude the formulas are equivalent, or in the SAT context, equi-satisfiable.
Here's a simple example:
from z3 import *
a, b, c = Bools('a b c')
fml1 = Or(And(a, b), And(a, c))
fml2 = And(a, Or(b, c))
s = Solver()
s.add(Distinct(fml1, fml2))
print(s.check())
Now if fml1 is an arbitrary SAT formula, and fml2 is a 3-SAT converted version (I'm not saying the above are SAT and 3-SAT conversions, but substitute the result of your algorithm here), then we'd expect that the SAT solver cannot distinguish them, i.e., the formula Distinct(fml1, fml2) would be unsatisfiable. Indeed, we get:
unsat
establishing that they are the same.
If you are using SMTLib only, then the template to use is:
(declare-fun a () Bool)
(declare-fun b () Bool)
(declare-fun c () Bool)
(assert (distinct (or (and a b) (and a c))
(and a (or b c))))
(check-sat)

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.

Creating a transitive and not reflexive function in Z3

I'm trying to create a function in Z3 that is transitive but not reflexive. I.e. if (transitive a b) and (transitive b c)hold then (transitive a c) should hold, but (transitive a a) should not.
I've tried to do it the following way, with 5 "tests". The first does what I expect, but the second one fails and results in unknown.
(declare-datatypes () ((T T1 T2 T3)))
(declare-fun f (T T) Bool)
(assert(f T1 T2))
(assert(f T2 T3))
; Make sure that f is not reflexive
(assert
(forall ((x T))
(not (f x x))))
; Now we create the transitivity function ourselves
(define-fun-rec transitive ((x T) (y T)) Bool
(or
(f x y)
(exists ((z T))
(and
(f x z)
(transitive z y)))))
; This works and gives sat
(push)
(assert (not (transitive T1 T1)))
(assert (not (transitive T2 T2)))
(assert (not (transitive T3 T3)))
(check-sat)
(pop)
; This fails with "unknown" and the verbose flag gives: (smt.mbqi "max instantiations 1000 reached")
(push)
(assert
(forall ((x T))
(not (transitive x x))))
(check-sat)
(pop)
My question is: how does the second test differ from the first? Why does the last one give unknown, whereas the one before that works just fine?
The "verbose" message is a hint here. mbqi stands for model-based-quantifier-instantiation. It's a method of dealing with quantifiers in SMT solving. In the first case, MBQI manages to find a model. But your transitive function is just too complicated for MBQI to handle, and thus it gives up. Increasing the limit will not likely address the problem, nor it's a long term solution.
Short story long, recursive definitions are difficult to deal with, and recursive definitions with quantifiers are even harder. The logic becomes semi-decidable, and you're at the mercy of heuristics. Even if you found a way to make z3 compute a model for this, it would be brittle. These sorts of problems are just not suitable for SMT solving; better use a proper theorem prover like Isabelle, Hol, Coq, Lean. Agda, etc. Almost all these tools offer "tactics" to dispatch subgoals to SMT solvers, so you have the best of both worlds. (Of course you lose full automation, but with quantifiers present, you can't expect any better.)

Why does 0 = 0.5?

I noticed some strange behavior with Z3 4.3.1 when working with .smt2 files.
If I do (assert (= 0 0.5)) it will be satisfiable. However, if I switch the order and do (assert (= 0.5 0)) it's not satisfiable.
My guess as to what is happening is that if the first parameter is an integer, it casts both of them to integers (rounding 0.5 down to 0), then does the comparison. If I change "0" to "0.0" it works as expected. This is in contrast to most programming languages I've worked with where if either of the parameters is a floating-point number, they are both cast to floating-point numbers and compared. Is this really the expected behavior in Z3?
I think this is a consequence of lack of type-checking; z3 is being too lenient. It should simply reject such queries as they are simply not well formed.
According to the SMT-Lib standard, v2 (http://smtlib.cs.uiowa.edu/papers/smt-lib-reference-v2.0-r10.12.21.pdf); page 30; the core theory is defined thusly:
(theory Core
:sorts ((Bool 0))
:funs ((true Bool) (false Bool) (not Bool Bool)
(=> Bool Bool Bool :right-assoc) (and Bool Bool Bool :left-assoc)
(or Bool Bool Bool :left-assoc) (xor Bool Bool Bool :left-assoc)
(par (A) (= A A Bool :chainable))
(par (A) (distinct A A Bool :pairwise))
(par (A) (ite Bool A A A))
)
:definition
"For every expanded signature Sigma, the instance of Core with that signature
is the theory consisting of all Sigma-models in which:
- the sort Bool denotes the set {true, false} of Boolean values;
- for all sorts s in Sigma,
- (= s s Bool) denotes the function that
returns true iff its two arguments are identical;
- (distinct s s Bool) denotes the function that
returns true iff its two arguments are not identical;
- (ite Bool s s) denotes the function that
returns its second argument or its third depending on whether
its first argument is true or not;
- the other function symbols of Core denote the standard Boolean operators
as expected.
"
:values "The set of values for the sort Bool is {true, false}."
)
So, by definition equality requires the input sorts to be the same; and hence the aforementioned query should be rejected as invalid.
There might be a switch to z3 or some other setting that forces more strict type-checking than it does by default; but I would've expected this case to be caught even with the most relaxed of the implementations.
Do not rely on the implicit type conversion of any solver. Instead,
use to_real and to_int to do explicit type conversions. Only send
well-typed formulas to the solver. Then Mohamed Iguernelala's examples become the following.
(set-logic AUFLIRA)
(declare-fun x () Int)
(assert (= (to_real x) 1.5))
(check-sat)
(exit)
(set-logic AUFLIRA)
(declare-fun x () Int)
(assert (= 1.5 (to_real x)))
(check-sat)
(exit)
Both of these return UNSAT in Z3 and CVC4. If instead, you really
wanted to find the model where x = 1 you should have instead used one
of the following.
(set-option :produce-models true)
(set-logic AUFLIRA)
(declare-fun x () Int)
(assert (= (to_int 1.5) x))
(check-sat)
(get-model)
(exit)
(set-option :produce-models true)
(set-logic AUFLIRA)
(declare-fun x () Int)
(assert (= x (to_int 1.5)))
(check-sat)
(get-model)
(exit)
Both of these return SAT with x = 1 in Z3 and CVC4.
Once you make all the type conversions explicit and deal only in well-typed formulas, the order of arguments to equality no longer matters (for correctness).
One of our interns, who worked on a conservative extension of SMT2 with polymorphism has noticed the same strange behavior, when he tried the understand how formulas mixing integers and reals are type-checked:
z3 (http://rise4fun.com/z3) says that the following example is SAT, and finds a model x = 1
(set-logic AUFLIRA)
(declare-fun x () Int)
(assert (= x 1.5))
(check-sat)
(get-model)
(exit)
But, it says that the following "equivalent" example in UNSAT
(set-logic AUFLIRA)
(declare-fun x () Int)
(assert (= 1.5 x))
(check-sat)
(exit)
So, this does not comply with the symmetric property of equality predicate. So, I think it's a bug.
Strictly speaking, Z3 is not SMT 2.0 compliant by default, and this is one of those cases. We can add
(set-option :smtlib2-compliant true)
and then this query is indeed rejected correctly.
Z3 is not the unique SMT solver that type-checks these examples:
CVC4 accepts them as well (even with option --smtlib-strict), and answers UNSAT in both cases of my formulas above.
Yices accepts them and answers UNSAT (after changing the logic to QF_LIA, because it does not support AUFLIRA).
With (set-logic QF_LIA), Z3 emits an error: (error "line 3 column 17: logic does not support reals").
Alt-Ergo says "typing error: Int and Real cannot be unified" in both cases. But Alt-Ergo's SMT2 parser is very limited and not heavily tested, as we concentrated on its native polymorphic language. So, it should not be taken as a reference.
I think that developers usually assume an "implicit" sub-typing relation between Int and Real. This is why these examples are successfully type-checked by Z3, CVC4 and Yices (and probably others as well).
Jochen Hoenicke gived the answer (on SMT-LIB mailing list) regarding "mixing reals and integers". Here it is:
I just wanted to point out, that the syntax may be officially correct.
There is an extension in AUFLIRA and AUFNIRA.
From http://smtlib.cs.uiowa.edu/logics/AUFLIRA.smt2
"For every operator op with declaration (op Real Real s) for some
sort s, and every term t1, t2 of sort Int and t of sort Real, the
expression
- (op t1 t) is syntactic sugar for (op (to_real t1) t)
- (op t t1) is syntactic sugar for (op t (to_real t1))
- (/ t1 t2) is syntactic sugar for (/ (to_real t1) (to_real t2)) "
One possible solution is
(declare-fun x () Real)
(declare-fun y () Real)
(assert (= x 0))
(assert (= y 0.5))
(check-sat)
(push)
(assert (= x y) )
(check-sat)
(pop)
and the output is
sat
unsat

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