Z3 Python API seems to be slow on linear formulae - z3

I am trying to use Z3 through its Python interface to infer the satisfiability of simple linear formulae. It seems to be quite slow even on simple formulae such as the following (composed of 10 clauses with 10 terms each) which I write down in the smt2 format:
(declare-fun x0 () Int)
(declare-fun x1 () Int)
(declare-fun x2 () Int)
(declare-fun x3 () Int)
(declare-fun x4 () Int)
(declare-fun x5 () Int)
(declare-fun x6 () Int)
(declare-fun x7 () Int)
(declare-fun x8 () Int)
(declare-fun x9 () Int)
(assert (distinct (+ (+ (+ (+ (+ (+ (+ (+ (+ (+ 0 (* 84 x0)) (* 48 x1)) (* 55 x2)) (* -46 x3)) (* -8 x4)) (* -12 x5)) (* 34 x6)) (* 35 x7)) (* -36 x8)) (* 99 x9)) 77))
(assert (distinct (+ (+ (+ (+ (+ (+ (+ (+ (+ (+ 0 (* -66 x0)) (* -13 x1)) (* -81 x2)) (* -88 x3)) (* -42 x4)) (* 57 x5)) (* 46 x6)) (* -9 x7)) (* -39 x8)) (* 18 x9)) 4))
(assert (distinct (+ (+ (+ (+ (+ (+ (+ (+ (+ (+ 0 (* -18 x0)) (* 93 x1)) (* 23 x2)) (* 25 x3)) (* 63 x4)) (* 47 x5)) (* -68 x6)) (* -25 x7)) (* 49 x8)) (* 14 x9)) 78))
(assert (<= (+ (+ (+ (+ (+ (+ (+ (+ (+ (+ 0 (* -44 x0)) (* -89 x1)) (* -48 x2)) (* -25 x3)) (* 40 x4)) (* 84 x5)) (* 40 x6)) (* 52 x7)) (* -8 x8)) (* 66 x9)) 5))
(assert (distinct (+ (+ (+ (+ (+ (+ (+ (+ (+ (+ 0 (* 30 x0)) (* 29 x1)) (* 64 x2)) (* 18 x3)) (* 63 x4)) (* -94 x5)) (* 20 x6)) (* -30 x7)) (* 60 x8)) (* -35 x9)) 72))
(assert (distinct (+ (+ (+ (+ (+ (+ (+ (+ (+ (+ 0 (* 97 x0)) (* -90 x1)) (* 74 x2)) (* -51 x3)) (* 70 x4)) (* 41 x5)) (* 31 x6)) (* 99 x7)) (* -34 x8)) (* 36 x9)) 60))
(assert (distinct (+ (+ (+ (+ (+ (+ (+ (+ (+ (+ 0 (* 38 x0)) (* -11 x1)) (* -82 x2)) (* -59 x3)) (* 93 x4)) (* 2 x5)) (* 69 x6)) (* 86 x7)) (* -83 x8)) (* 49 x9)) 14))
(assert (<= (+ (+ (+ (+ (+ (+ (+ (+ (+ (+ 0 (* -55 x0)) (* 50 x1)) (* -48 x2)) (* -27 x3)) (* -7 x4)) (* 86 x5)) (* -15 x6)) (* 96 x7)) (* -46 x8)) (* 11 x9)) 56))
(assert (distinct (+ (+ (+ (+ (+ (+ (+ (+ (+ (+ 0 (* 50 x0)) (* -51 x1)) (* -32 x2)) (* -23 x3)) (* -75 x4)) (* 24 x5)) (* 39 x6)) (* -89 x7)) (* 8 x8)) (* 23 x9)) 36))
(assert (distinct (+ (+ (+ (+ (+ (+ (+ (+ (+ (+ 0 (* -50 x0)) (* 98 x1)) (* 62 x2)) (* -39 x3)) (* -90 x4)) (* 19 x5)) (* -10 x6)) (* 32 x7)) (* -51 x8)) (* 52 x9)) 24))
(check-sat)
I am generating these formulae dynamically using the following block of code:
(Notice: a formula is a list of clauses; a clause is a list of terms, a comparison sign and a free coefficient; a term is a pair variable-identifier (integer), coefficient of the variable (integer)).
import z3
from pyCloSE.formula import LE, SAT, UNSAT, UNKNOWN
context = z3.Context()
solver = z3.SolverFor("QF_LIA", context)
# Define all variables as integers.
definitions = dict()
for variable in formula.getVariables():
definitions[variable] = z3.Int("x{}".format(variable), context)
# Create assertions.
constraints = []
for clause in formula.getClauses():
terms = [term.getCoefficient() * definitions[term.getIndex()] for term in clause.getTerms()]
sumOfTerms = reduce(lambda a,b: a+b, terms)
if clause.getComparison() == LE:
constraints.append(sumOfTerms <= clause.getFreeCoefficient())
else:
constraints.append(sumOfTerms != clause.getFreeCoefficient())
solver.add(*constraints)
result = solver.check()
return result
The problem I am having is that Z3 is stuck on such a formula. It seems to be stuck too when I save it to a file named formula.smt2 and try to solve it through the bash Z3 command.
The version of the solver I am using is: Z3 version 4.3.2
Is the format I am using inconvenient or is it due to Z3? Is there any trick I could use to speed up the solving process?
As Juan Ospina suggested I tried to figure out which part of the formula was the difficult one for the solver. It seems it is the following:
(declare-fun x0 () Int)
(declare-fun x1 () Int)
(declare-fun x2 () Int)
(declare-fun x3 () Int)
(declare-fun x4 () Int)
(declare-fun x5 () Int)
(declare-fun x6 () Int)
(declare-fun x7 () Int)
(declare-fun x8 () Int)
(declare-fun x9 () Int)
(assert (distinct (+ (+ (+ (+ (+ (+ (+ (+ (+ (+ 0 (* -18 x0)) (* 93 x1)) (* 23 x2)) (* 25 x3)) (* 63 x4)) (* 47 x5)) (* -68 x6)) (* -25 x7)) (* 49 x8)) (* 14 x9)) 78))
(assert (distinct (+ (+ (+ (+ (+ (+ (+ (+ (+ (+ 0 (* 30 x0)) (* 29 x1)) (* 64 x2)) (* 18 x3)) (* 63 x4)) (* -94 x5)) (* 20 x6)) (* -30 x7)) (* 60 x8)) (* -35 x9)) 72))
(assert (distinct (+ (+ (+ (+ (+ (+ (+ (+ (+ (+ 0 (* 97 x0)) (* -90 x1)) (* 74 x2)) (* -51 x3)) (* 70 x4)) (* 41 x5)) (* 31 x6)) (* 99 x7)) (* -34 x8)) (* 36 x9)) 60))
(assert (<= (+ (+ (+ (+ (+ (+ (+ (+ (+ (+ 0 (* -55 x0)) (* 50 x1)) (* -48 x2)) (* -27 x3)) (* -7 x4)) (* 86 x5)) (* -15 x6)) (* 96 x7)) (* -46 x8)) (* 11 x9)) 56))
(assert (distinct (+ (+ (+ (+ (+ (+ (+ (+ (+ (+ 0 (* 50 x0)) (* -51 x1)) (* -32 x2)) (* -23 x3)) (* -75 x4)) (* 24 x5)) (* 39 x6)) (* -89 x7)) (* 8 x8)) (* 23 x9)) 36))
(assert (distinct (+ (+ (+ (+ (+ (+ (+ (+ (+ (+ 0 (* -50 x0)) (* 98 x1)) (* 62 x2)) (* -39 x3)) (* -90 x4)) (* 19 x5)) (* -10 x6)) (* 32 x7)) (* -51 x8)) (* 52 x9)) 24))
In fact if you remove any clause from this formula the solver is able to answer in less than a second while if you feed it with the entire formula it runs out of time.
Why does this happen? Is it normal that Z3 finds this formula so difficult to solve?
Best,
Andrea

Related

Optimising Z3 for rational fractions

I am trying to solve a constraint problem with the following features:
4 variables representing real numbers
a few inequalities by rational fractions which involve polynomials of small-ish degree (4), with an annoying OR.
I copy below the said constraints (note that OR).
I expect "UNSAT" (for N = 4 that's actually a theorem, but I'd like to go beyond 4!)
Unfortunately, z3 cannot solve this first problem. Any ideas or suggestions about tactics or other tools would be very appreciated!
[S_0 >= S_1,
S_1 >= S_2,
S_2 >= S_3,
S_3 >= 0,
Or((S_0/(S_0 + S_1))*(S_0/(S_0 + S_3))*(S_1/(S_1 + S_2)) +
(S_0/(S_0 + S_2))*(S_0/(S_0 + S_3))*(S_2/(S_2 + S_1)) <
(S_0/(S_0 + S_3))*(S_1/(S_1 + S_0))*(S_1/(S_1 + S_2)) +
(S_1/(S_1 + S_2))*(S_1/(S_1 + S_3))*(S_3/(S_3 + S_0)),
(S_0/(S_0 + S_3))*(S_1/(S_1 + S_0))*(S_1/(S_1 + S_2)) +
(S_1/(S_1 + S_2))*(S_1/(S_1 + S_3))*(S_3/(S_3 + S_0)) <
(S_0/(S_0 + S_3))*(S_2/(S_2 + S_0))*(S_2/(S_2 + S_1)) +
(S_2/(S_2 + S_1))*(S_2/(S_2 + S_3))*(S_3/(S_3 + S_0)),
(S_0/(S_0 + S_3))*(S_2/(S_2 + S_0))*(S_2/(S_2 + S_1)) +
(S_2/(S_2 + S_1))*(S_2/(S_2 + S_3))*(S_3/(S_3 + S_0)) <
(S_1/(S_1 + S_2))*(S_3/(S_3 + S_0))*(S_3/(S_3 + S_1)) +
(S_2/(S_2 + S_1))*(S_3/(S_3 + S_0))*(S_3/(S_3 + S_2)))]
Looks like z3 is choking on this problem, but both mathsat and cvc4 are doing just fine. Here's the problem coded in SMTLib:
$ cat a.smt2
(set-logic QF_NRA)
(set-option :produce-models true)
(declare-fun S_1 () Real)
(declare-fun S_0 () Real)
(declare-fun S_2 () Real)
(declare-fun S_3 () Real)
(assert (>= S_0 S_1))
(assert (>= S_1 S_2))
(assert (>= S_2 S_3))
(assert (>= S_3 0.0))
(assert (let ((a!1 (+ (* (/ S_0 (+ S_0 S_1)) (/ S_0 (+ S_0 S_3)) (/ S_1 (+ S_1 S_2)))
(* (/ S_0 (+ S_0 S_2)) (/ S_0 (+ S_0 S_3)) (/ S_2 (+ S_2 S_1)))))
(a!2 (+ (* (/ S_0 (+ S_0 S_3)) (/ S_1 (+ S_1 S_0)) (/ S_1 (+ S_1 S_2)))
(* (/ S_1 (+ S_1 S_2)) (/ S_1 (+ S_1 S_3)) (/ S_3 (+ S_3 S_0)))))
(a!3 (+ (* (/ S_0 (+ S_0 S_3)) (/ S_2 (+ S_2 S_0)) (/ S_2 (+ S_2 S_1)))
(* (/ S_2 (+ S_2 S_1)) (/ S_2 (+ S_2 S_3)) (/ S_3 (+ S_3 S_0)))))
(a!4 (+ (* (/ S_1 (+ S_1 S_2)) (/ S_3 (+ S_3 S_0)) (/ S_3 (+ S_3 S_1)))
(* (/ S_2 (+ S_2 S_1)) (/ S_3 (+ S_3 S_0)) (/ S_3 (+ S_3 S_2))))))
(or (< a!1 a!2) (< a!2 a!3) (< a!3 a!4))))
(check-sat)
(get-model)
Mathsat:
$ mathsat a.smt2
sat
( (S_1 0)
(S_0 (/ 3 2))
(S_2 0)
(S_3 0) )
CVC4:
$ cvc4 a.smt2
sat
(model
(define-fun S_1 () Real 0.0)
(define-fun S_0 () Real 1.0)
(define-fun S_2 () Real 0.0)
(define-fun S_3 () Real 0.0)
)
Both of these results are instantaneous. z3 seems to loop forever indeed.
I didn't check the models to be correct, so would be good to make sure they are OK and I got the SMTLib translation done right as well. You said you were expecting unsat, but obviously mathsat and cvc4 both disagree, assuming the translation I got is ok.
If you find that cvc4 and mathsat are indeed correct, you might want to file this as a z3 issue as this is something they should be able to handle.

Problems with user defined z3 sorts

I'm trying to define a new fraction sort in z3 to gain a better understanding of how z3 works. I'm using this query and define equality between two fractions:
;;;;;;;;;;;;;;;;;;;
;; TEMPLATE CTOR ;;
;;;;;;;;;;;;;;;;;;;
(declare-datatypes (T1 T2) ((Pair (mk-pair (first T1) (second T2)))))
;;;;;;;;;;;;;;;;;;;;;;
;; SORT DEFINITIONS ;;
;;;;;;;;;;;;;;;;;;;;;;
(define-sort Fraction () (Pair Int Int))
;;;;;;;;;;;;;;;;;;;;;;;;;;
;; FUNCTION DEFINITIONS ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-fun Fraction_Eq ((f1 Fraction) (f2 Fraction)) Bool
(if (= (* (first f1) (second f2))
(* (first f2) (second f1)))
true
false
)
)
(declare-const x1 Fraction)
(declare-const x2 Fraction)
(declare-const x3 Fraction)
(assert (Fraction_Eq x1 (mk-pair 3 5)))
(assert (Fraction_Eq x2 (mk-pair 4 7)))
(assert (Fraction_Eq x3 (mk-pair 8 9)))
(check-sat)
(get-value (x1 x2 x3))
I expect that x1 will be equal to 3/5 but it isn't. Here is the answer I get:
sat
((x1 (mk-pair 0 0))
(x2 (mk-pair 1304 2282))
(x3 (mk-pair 0 0)))
Can anyone please help? Thanks!
The equation
n * 5 = d * 3
is perfectly satisfied by n = d = 0.
Since division by zero has no meaning in ordinary arithmetic, you should enrich your statement and require d being different from 0.
Note that this does not guarantee n, d to be equal to 3, 5, since there are an infinite number of solutions to that equation. The normalised value of x1, however, should correspond to 3/5.
Here is the way to fix it:
;;;;;;;;;;;;;;;;;;;
;; TEMPLATE CTOR ;;
;;;;;;;;;;;;;;;;;;;
(declare-datatypes (T1 T2) ((Pair (mk-pair (first T1) (second T2)))))
;;;;;;;;;;;;;;;;;;;;;;
;; SORT DEFINITIONS ;;
;;;;;;;;;;;;;;;;;;;;;;
(define-sort Fraction () (Pair Int Int))
;;;;;;;;;;;;;;;;;;;;;;;;;;
;; FUNCTION DEFINITIONS ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-fun Fraction_Eq ((f1 Fraction) (f2 Fraction)) Bool
(if (= (* (first f1) (second f2))
(* (first f2) (second f1)))
true
false
)
)
(define-fun Fraction_Valid ((f1 Fraction)) Bool
(not (= 0 (second f1)))
)
(declare-const x1 Fraction)
(declare-const x2 Fraction)
(declare-const x3 Fraction)
(assert (Fraction_Valid x1))
(assert (Fraction_Valid x2))
(assert (Fraction_Valid x3))
(assert (Fraction_Eq x1 (mk-pair 3 5)))
(assert (Fraction_Eq x2 (mk-pair 4 7)))
(assert (Fraction_Eq x3 (mk-pair 8 9)))
(check-sat)
(get-value (x1 x2 x3))
With the result:
sat
((x1 (mk-pair 3 5))
(x2 (mk-pair 4 7))
(x3 (mk-pair (- 8) (- 9))))
The newly-introduced Fraction_Valid SmtLibv2 predicate should prevent any fraction, on which it is applied, from having a denominator equal to zero.

Z3 Solver Z3_parse_smtlib2_file - Z3 C API push pop

How to use (push) and (pop) while reading benchmark using Z3_parse_smtlib2_file API. How can I get result for (assert(not(= o2_s o2_i ))) and (assert(not(= o1_s o1_i ))) constraints using Z3_parse_smtlib2_file API. I am getting result for (assert(not(= o1_s o1_i ))) only while reading benchmark using Z3_parse_smtlib2_file API in C.
(declare-fun i_s () Int)
(declare-fun t_s () Int)
(declare-fun o1_s () Int)
(declare-fun o2_s () Int)
(declare-fun i_i () Int)
(declare-fun t_i () Int)
(declare-fun o1_i () Int)
(declare-fun o2_i () Int)
(assert(= i_s 10) )
(assert(>= (+ (- 5) (* 1 i_s)) 0))
(assert(= t_s (+ 1 (* 1 i_s))) )
(assert(< (+ (- 20) (* 1 t_s)) 0))
(assert(= o1_s (+ 1 (* 1 t_s))) )
(assert(= o2_s (+ 0 (* 1 t_s))) )
(assert(= i_i 10) )
(assert(>= (+ (- 5) (* 1 i_i)) 0))
(assert(= t_i (+ 2 (* 1 i_i))) )
(assert(< (+ (- 21) (* 1 t_i)) 0))
(assert(= o1_i (+ 0 (* 1 t_i))) )
(assert(= o2_i (+ 0 (* 1 t_i))) )
(push)
(assert(not(= o2_s o2_i )))
(pop)
(assert(not(= o1_s o1_i )))
This is currently not supported. Z3_parse_smtlib2_file extracts only the assertions from the file and ignores most other SMT2 commands. Z3 does not currently support extraction of a set of SMT2 commands that can be modified later.

Find the largest palindrome made from the product of two 5-digit numbers using Z3-SMTLIB

We look for a number of the form efghiihgfe which is the product of two numbers of the form 999ab and 99qcd.
We use the following code
(declare-const a Int)
(declare-const b Int)
(declare-const c Int)
(declare-const d Int)
(declare-const e Int)
(declare-const f Int)
(declare-const g Int)
(declare-const h Int)
(declare-const i Int)
(declare-const p Int)
(declare-const q Int)
(assert (and (>= a 0) (<= a 9)))
(assert (and (>= b 0) (<= b 9)))
(assert (and (>= c 0) (<= c 9)))
(assert (and (>= d 0) (<= d 9)))
(assert (and (>= e 0) (<= e 9)))
(assert (and (>= f 0) (<= f 9)))
(assert (and (>= g 0) (<= g 9)))
(assert (and (>= h 0) (<= h 9)))
(assert (and (>= i 0) (<= i 9)))
(assert (and (>= p 0) (<= p 9)))
(assert (and (>= q 0) (<= q 9)))
(assert (= (* (+ 99900 (* 10 a) b ) (+ 99000 (* 100 q) (* 10 c) d ))
(+ (* (^ 10 9) e) (* (^ 10 8) f) (* (^ 10 7) g) (* (^ 10 6) h) (* (^ 10 5) i)
(* (^ 10 4) i) (* 1000 h ) (* 100 g) (* 10 f) e) ) )
(check-sat)
(get-model)
(eval (+ (* (^ 10 9) e) (* (^ 10 8) f) (* (^ 10 7) g) (* (^ 10 6) h) (* (^ 10 5) i)
(* (^ 10 4) i) (* 1000 h ) (* 100 g) (* 10 f) e))
and the output is
(model
(define-fun q () Int
6)
(define-fun p () Int
0)
(define-fun i () Int
0)
(define-fun h () Int
6)
(define-fun g () Int
6)
(define-fun f () Int
9)
(define-fun e () Int
9)
(define-fun d () Int
1)
(define-fun c () Int
8)
(define-fun b () Int
9)
(define-fun a () Int
7)
)
9966006699
To verify that 9966006699 is the maxim we run the code
(declare-const a Int)
(declare-const b Int)
(declare-const c Int)
(declare-const d Int)
(declare-const e Int)
(declare-const f Int)
(declare-const g Int)
(declare-const h Int)
(declare-const i Int)
(declare-const p Int)
(declare-const q Int)
(assert (and (>= a 0) (<= a 9)))
(assert (and (>= b 0) (<= b 9)))
(assert (and (>= c 0) (<= c 9)))
(assert (and (>= d 0) (<= d 9)))
(assert (and (>= e 0) (<= e 9)))
(assert (and (>= f 0) (<= f 9)))
(assert (and (>= g 0) (<= g 9)))
(assert (and (>= h 0) (<= h 9)))
(assert (and (>= i 0) (<= i 9)))
(assert (and (>= p 0) (<= p 9)))
(assert (and (>= q 0) (<= q 9)))
(assert (= (* (+ 99900 (* 10 a) b ) (+ 99000 (* 100 q) (* 10 c) d ))
(+ (* (^ 10 9) e) (* (^ 10 8) f) (* (^ 10 7) g) (* (^ 10 6) h) (* (^ 10 5) i)
(* (^ 10 4) i) (* 1000 h ) (* 100 g) (* 10 f) e) ) )
(assert (> (+ (* (^ 10 9) e) (* (^ 10 8) f) (* (^ 10 7) g) (* (^ 10 6) h) (* (^ 10 5) i)
(* (^ 10 4) i) (* 1000 h ) (* 100 g) (* 10 f) e) 9966006699 ))
(check-sat)
and the output is
unsat
Please let me know if there is a more efficient program with Z3 to solve the problem.
Thank you, maybe use bit-vectors to force using finite domains instead of ILP.

z3 times out in case of a formula with quantifiers

I am getting timeout on the following example.
http://rise4fun.com/Z3/zbOcW
Is there any trick to make this work (eg.by reformulating the problem or using triggers)?
For this example, the macro finder will be useful (I think often with forall quantifiers with implications), you can enable it with:
(set-option :macro-finder true)
Here's your updated example that gets sat quickly (rise4fun link: http://rise4fun.com/Z3/Ux7gN ):
(set-option :macro-finder true)
(declare-const a (Array Int Bool))
(declare-const sz Int)
(declare-const n Int)
(declare-const d Int)
(declare-const r Bool)
(declare-const x Int)
(declare-const y Int)
;;ttff
(declare-fun ttff (Int Int Int) Bool)
(assert
(forall ((x1 Int) (y1 Int) (n1 Int))
(= (ttff x1 y1 n1)
(and
(forall ((i Int))
(=> (and (<= x1 i) (< i y1))
(= (select a i) true)))
(forall ((i Int))
(=> (and (<= y1 i) (< i n1))
(= (select a i) false)))))))
;; A1
(assert (and (<= 0 n) (<= n sz)))
;; A2
(assert (< 0 d))
;; A3
(assert (and (and (<= 0 x) (<= x y)) (<= y n)))
;; A4
(assert (ttff x y n))
;; A6
(assert
(=> (< 0 y)
(= (select a (- y 1)) true)))
;; A7
(assert
(=> (< 0 x)
(= (select a (- x 1)) false)))
;;G
(assert
(not
(iff
(and (<= (* 2 d) (+ n 1)) (ttff (- (+ n 1) (* 2 d)) (- (+ n 1) d) (+ n 1)))
(and (= (- (+ n 1) y) d) (<= d (- y x))))))
(check-sat)
(get-model)

Resources