Should naming an assertion affect the satisfiability of a check in Z3? - z3

We have encountered constraints for which Z3 returns sat but if we then add a certain named assertion then Z3 returns unknown. One example is:
(declare-fun a () Real)
(declare-fun b () Bool)
(assert (and (<= 0.1 a) (<= a 10.0)))
(assert (= b (= 1.0 (/ 1.0 a))))
(check-sat)
Z3 reports that this is satisfiable as expected. We can additionally assert that b is either true or false, both of which are satisfiable as expected. However, if we use a named assertion for the value of b then the result of the satisfiability check can become unknown depending on the value. With the value true, Z3 still returns sat:
(set-option :produce-unsat-cores true)
(declare-fun a () Real)
(declare-fun b () Bool)
(assert (and (<= 0.1 a) (<= a 10.0)))
(assert (= b (= 1.0 (/ 1.0 a))))
(assert (! (= b true) :named c))
(check-sat)
Using the value false, Z3 returns unknown:
(set-option :produce-unsat-cores true)
(declare-fun a () Real)
(declare-fun b () Bool)
(assert (and (<= 0.1 a) (<= a 10.0)))
(assert (= b (= 1.0 (/ 1.0 a))))
(assert (! (= b false) :named c))
(check-sat)
Checking the reason for the unknown result (with (get-info :reason-unknown)) returns (incomplete (theory arithmetic)).

It appears that z3 is just picking the wrong tactic in the latter case. Replace your check-sat call with:
(check-sat-using qfnra-nlsat)
And z3 says sat in both cases.
Why z3 ends up picking a different tactic is a difficult question to answer; I'd recommend filing this as a ticket at their github site; they might be missing a heuristic.
Side note: I found this by running both cases with -v:10 and skimming through the verbose output. It's not a bad way to see what z3 is doing for short-enough benchmarks.

Related

Z3 support for exponentials

I'm new to Z3 and I'm trying to understand how it works, and what it can and cannot do. I know that Z3 has at least some support for exponentials through the power (^) operator (see Z3py returns unknown for equation using pow() function, How to represent logarithmic formula in z3py, and Use Z3 and SMT-LIB to define sqrt function with a real number). What I'm unclear on is how extensive this support is, and what kind of inferences z3 can make about exponentials.
Here's a simple example involving exponentials which z3 can analyze. We define an exponential function, and then ask it to verify that exp(0) == 1:
(define-fun exp ((x Real)) Real
(^ 2.718281828459045 x))
(declare-fun x1 () Real)
(declare-fun y1 () Real)
(assert (= y1 (exp x1)))
(assert (not (=> (= x1 0.0) (= y1 1.0))))
(check-sat)
(exit)
Z3 returns unsat, as expected. On the other hand, here's a simple example which Z3 can't analyze:
(define-fun exp ((x Real)) Real
(^ 2.718281828459045 x))
(declare-fun x1 () Real)
(declare-fun y1 () Real)
(assert (= y1 (exp x1)))
(assert (not (< y1 0.0)))
(check-sat)
(exit)
This should be satisfiable, since literally any value for x1 would give y1 > 0. However, Z3 returns unknown. Naively I might have expected that Z3 would be able to analyze this, given that it could analyze the first example.
I realize this question is a bit broad, but: can anyone give me any insight into how Z3 handles exponentials, and (more specifically) why it can solve the first example I gave but not the second?
It is hard to say in general, since non-linear solving is challenging, but the case you presented is actually not so mysterious. You wrote:
(assert (= y (exp x)))
(assert (not (=> (= x 0) (= y 1))))
Z3 is going to simplify the second assertion, yielding:
(assert (= y (exp x)))
(assert (= x 0))
(assert (not (= y 1)))
Then it will propagate the first equality, yielding:
(assert (= y (exp 0)))
(assert (not (= y 1)))
Now when exp is expanded, you have a case of constant^constant, which Z3 can handle (for integer exponents, etc).
For the second case, you are asking it a very very basic question about variable exponents, and Z3 immediately barfs. That's not too odd, since so many questions about variable exponents are either known uncomputable or unknown but hard.

Minor change results in "unknown" - related to quantifier preprocessing?

The following "minimal" program, distilled from a much larger program, is expected to yield unsat (and does). However, uncommenting the additional conjunct in the quantifier AX-1 changes the result to unknown (in Z3 4.5.0 x64 on Windows 10).
(set-option :auto_config false)
(set-option :smt.mbqi false)
(declare-fun foo (Int) Bool)
(declare-const k Real)
(assert (forall ((i Int)) (!
(and
(< 0.0 k)
; (implies (<= 0 i) (< 0.0 k)) ;;; ---- uncomment this line ----
)
:pattern ((foo i))
:qid |AX-1|)))
(assert (forall ((i Int)) (!
(foo i)
:pattern ((foo i))
:qid |AX-2|)))
(declare-const j Int)
(assert (< j 0))
; (push) ;;; doesn't make a difference
(assert (not
(ite
(foo j)
(< 0.0 k)
false)))
; (set-option :smt.qi.profile true)
(check-sat)
; (get-info :all-statistics)
; (pop)
The quantifier instantiation statistics show that AX-2 is instantiated in both cases, but AX-1 is only instantiated if the additional conjunct is not included. My assumption is that in the latter case, Z3 eliminates the quantifier since the quantified variable doesn't occur in the body.
However, I find it surprising that the version with the additional conjunct - in which Z3 presumably doesn't eliminate the quantifier - yields unknown, since the trigger for the quantifier ((foo j)) should be available.
Question: Is this behaviour expected - and if so, why?
Confirmed as a bug, see Github issue 935.

Executing get-model or unsat-core depending on solver's decision

I wonder, if there is a possibility in a SMT-LIB 2.0 script to access the last satisfiability decision of a solver (sat, unsat, ...). For example, the following code:
(set-option :produce-unsat-cores true)
(set-option :produce-models true)
(set-logic QF_UF)
(declare-fun p () Bool)
(declare-fun q () Bool)
(declare-fun r () Bool)
(assert (! (=> p q) :named PQ))
(assert (! (=> q r) :named QR))
(assert (! (not (=> p r)) :named nPR))
(check-sat)
(get-model)
(get-unsat-core)
ran in Z3 returns:
unsat
(error "line 15 column 10: model is not available")
(PQ QR nPR)
and ran in MathSAT returns:
unsat
(error "model generation not enabled")
In MathSAT 5 it just breaks on (get-model) and doesn't even reach (get-unsat-core).
Is there any way in SMT-LIB 2.0 language to get-model iff the decision was SAT and unsat-core iff the decision was UNSAT? Solution could for example look like this:
(check-sat)
(ite (= (was-sat) true) (get-model) (get-unsat-core))
I searched SMT-LIB 2.0 language documentation, but I did not found any hint.
EDIT:
I also tried the code below, and unfortunately it did not work.
(ite (= (check-sat) "sat") (get-model) (get-unsat-core))
The SMT language does not let you write commands like this.
The way that tools, such as Boogie, deal with this is to use
a two-way text pipe: It reads back the result from (check-sat).
If the resulting string is "unsat" models are not available, but
cores would be if the check uses asssumptions. If the resulting
string is "sat" the tool can expect that a (get-model) command
succeeds.
As Nikolaj said in his answer, the right way to do this is to parse the solver output and conditionally generate either a (get-model) or a (get-unsat-core) statement.
However, with mathsat you can use the code without the (get-model) statement, and call mathsat with the -model option. For example:
$ cat demo_sat.smt2
(set-option :produce-unsat-cores true)
(set-option :produce-models true)
(set-logic QF_UF)
(declare-fun p () Bool)
(declare-fun q () Bool)
(declare-fun r () Bool)
(assert (! (=> p q) :named PQ))
(assert (! (=> q r) :named QR))
; (assert (! (not (=> p r)) :named nPR))
(check-sat)
(get-unsat-core)
$ mathsat -model demo_sat.smt2
sat
( (p false)
(q false)
(r false) )
(error "no unsatisfiability proof, impossible to compute unsat core")
And in the unsat case:
$ cat demo_unsat.smt2
(set-option :produce-unsat-cores true)
(set-option :produce-models true)
(set-logic QF_UF)
(declare-fun p () Bool)
(declare-fun q () Bool)
(declare-fun r () Bool)
(assert (! (=> p q) :named PQ))
(assert (! (=> q r) :named QR))
(assert (! (not (=> p r)) :named nPR))
(check-sat)
(get-unsat-core)
$ mathsat -model demo_unsat.smt2
unsat
( PQ
QR
nPR )
Unfortunately there does not seem to exist an option like -model for producing unsat cores. So this hack won't work if you want to use it with an incremental problem, unless you are OK with the solver terminating after the first sat result. (Because at the first sat result the solver will exit on the error for (get-unsat-core).)

Why is Z3 not able to solve this instance without a seemingly trivial modification?

The original problem is:
(declare-const a Real)
(declare-const b Bool)
(declare-const c Int)
(assert (distinct a 0.))
(assert (= b (distinct (* a a) 0.)))
(assert (= c (ite b 1 0)))
(assert (not (distinct c 0)))
(check-sat)
The result is unknown.
But the last two constraints, taken together, are equivalent to (assert (= b false)), and after performing this rewrite by hand
(declare-const a Real)
(declare-const b Bool)
(declare-const c Int)
(assert (distinct a 0.))
(assert (= b (distinct (* a a) 0.)))
(assert (= b false))
;(assert (= c (ite b 1 0)))
;(assert (not (distinct c 0)))
(check-sat)
Z3 is now able to solve this instance (it is unsat).
Why can Z3 solve the second instance but not the first one, even though the first instance can be simplified to the second?
edit:
While locating the problem I found something very strange.
Z3 solves the following instance and returns "unsat":
(declare-fun a() Real)
(declare-fun b() Bool)
(declare-fun c() Int)
(assert (distinct a 0.0))
(assert (= b (distinct (* a a) 0.0)))
(assert (= b false))
;(assert (= c 0))
(check-sat)
But if I uncomment (assert (= c 0)), the solver returns "unknown", even though c=0 has nothing to do with the above assertions.
The problem here is that expressions like (* a a) are non-linear and Z3's default solver for non-linear problems gives up because it thinks it's too hard. Z3 does have another solver, but that one has very limited theory combination, i.e., you won't be able to use it for mixed Boolean, bit-vector, array, etc, problems, but only for arithmetic problems. It's easy to test by replacing the (check-sat) command with (check-sat-using qfnra-nlsat).

Tracking nonlinear real arithmetic assertions using implications versus the :named mechanism gives different answers

While trying to solve large nonlinear real arithmetic problems, I track every assertion using answer literals and explicit implications, as recommended in other posts. It should be equivalent to using the (! (...) :named p1) syntax of the SMT2 format. It seems, though, that both methods are handled differently internally.
The following SMT2 code gives an UNKNOWN result, with explanation "(incomplete (theory arithmetic))":
(set-option :print-success false)
(set-option :produce-unsat-cores true) ; enable generation of unsat cores
(set-option :produce-models true) ; enable model generation
(declare-const p1 Bool)
(declare-const p2 Bool)
(declare-const p3 Bool)
(declare-const p4 Bool)
(declare-const p5 Bool)
(declare-const x1 Real)
(declare-const x2 Real)
(declare-const x3 Real)
(assert (=> p1 (= x1 (/ 1.0 (* x2 x2)))))
(assert (=> p2 (not (= x2 0.0))))
(assert (=> p3 (= x3 (* 2.0 x1))))
(assert (=> p4 (= x3 5.0)))
(assert (=> p5 (< x3 0.0)))
(check-sat p1 p2 p3)
(get-info:reason-unknown)
On the other hand, the following SMT2 code gives the correct answer, UNSAT, and produces an informative unsat core (p4, p5):
(set-option :print-success false)
(set-option :produce-unsat-cores true) ; enable generation of unsat cores
(set-option :produce-models true) ; enable model generation
(declare-const x1 Real)
(declare-const x2 Real)
(declare-const x3 Real)
(assert (! (= x1 (/ 1.0 (* x2 x2))) :named p1))
(assert (! (not (= x2 0.0)) :named p2))
(assert (! (= x3 (* 2.0 x1)) :named p3))
(assert (! (= x3 5.0) :named p4))
(assert (! (< x3 0) :named p5))
(check-sat)
(get-unsat-core)
;(get-model)
My specific questions are:
How can this differing behavior be explained? What is recommended practice for tracking nonlinear real equations and inequalities?
What would be the equivalent OCaml API call for the (! (...) :named p1) syntax of SMT2? Is it assert_and_track?
I am using Z3 version 4.3.2 from the ml-ng branch under Linux.
Many thanks!
The new ML API has been integrated into the unstable branch a couple months ago, and the ml-ng branch has been removed. A few bugfixes/extensions were added to it's worth updating.
assert_and_track does exactly what you suspect and it is internally translated to the first example given.
The difference in behavior is explained by (check-sat p1 p2 p3) which is missing p4 and p5. Once those are added, the two versions behave exactly the same and they produce the same unsat core.

Resources