1

I am working with the ocaml for Z3 in order to name each constraints when getting unsat core such as

(set-option :produce-unsat-cores true)
(declare-fun x () Int)
(assert (!(> x 0) :named p1))
(assert (!(not (= x 1)) :named p2))
(assert (!(< x 0) :named p3))
(check-sat)
(get-unsat-core)

The result is unsat. Then I found a API assert_and_track in ocaml for Z3 and a solution in python (Unsatisfiable Cores in Z3 Python), and then I have the following simple example:

from z3 import *

x = Int('x')
s = Solver()
s.set(unsat_core=True)
s.assert_and_track(x > 0, 'p1')
s.assert_and_track(x != 1, 'p2')
s.assert_and_track(x < 0, 'p3')
print(s.check())
print(s.to_smt2())
c = s.unsat_core()
for i in range(0, len(c)):
    print(c[i])

The result of this example is

unsat
; benchmark generated from python API
(set-info :status unknown)
(declare-fun x () Int)
(declare-fun p1 () Bool)
(declare-fun p2 () Bool)
(declare-fun p3 () Bool)
(assert
 (let (($x7 (> x 0)))
 (=> p1 $x7)))
(assert
 (let (($x33 (and (distinct x 1) true)))
 (=> p2 $x33)))
(assert
 (let (($x40 (< x 0)))
 (=> p3 $x40)))
(check-sat)

p1
p3

Then I copy the generated SMT2 format to z3 and found the result is sat. Why the generated z3 smt2 format code from python is inconsistent with python, and How can I use this in ocaml?

Frank Sheng
  • 195
  • 7

1 Answers1

1

The generated benchmark (via the call to s.to_smt2()) does not capture what the python program is actually doing. This is unfortunate and confusing, but follows from how the internal constraints are translated. (It just doesn't know that you're trying to do an unsat-core.)

The good news is it's not hard to fix. You need to get the generated benchmark, and add the following line at the top:

(set-option :produce-unsat-cores true)

Then, you need to replace (check-sat) with:

(check-sat-assuming (p1 p2 p3))
(get-unsat-core)

So, the final program looks like:

; benchmark generated from python API
(set-info :status unknown)
(set-option :produce-unsat-cores true)
(declare-fun x () Int)
(declare-fun p1 () Bool)
(declare-fun p2 () Bool)
(declare-fun p3 () Bool)
(assert
 (let (($x7 (> x 0)))
 (=> p1 $x7)))
(assert
 (let (($x33 (and (distinct x 1) true)))
 (=> p2 $x33)))
(assert
 (let (($x40 (< x 0)))
 (=> p3 $x40)))
(check-sat-assuming (p1 p2 p3))
(get-unsat-core)

If you run this final program, you'll see it'll print:

unsat
(p1 p3)

as expected.

Regarding how to do this from O'Caml: You need to use the get_unsat_core function, see here: https://github.com/Z3Prover/z3/blob/master/src/api/ml/z3.ml#L1843

alias
  • 28,120
  • 2
  • 23
  • 40