I have a program that generates a set of constraints in nonlinear real arithmetic. Consider the following two constraints:
(< (- (- (- (+ (* (- v0_x v3_x)
(- v1_y v3_y)
(+ (* (- v2_x v3_x) (- v2_x v3_x))
(* (- v2_y v3_y) (- v2_y v3_y))))
(* (- v0_y v3_y)
(- v2_x v3_x)
(+ (* (- v1_x v3_x) (- v1_x v3_x))
(* (- v1_y v3_y) (- v1_y v3_y))))
(* (- v1_x v3_x)
(- v2_y v3_y)
(+ (* (- v0_x v3_x) (- v0_x v3_x))
(* (- v0_y v3_y) (- v0_y v3_y)))))
(* (- v1_y v3_y)
(- v2_x v3_x)
(+ (* (- v0_x v3_x) (- v0_x v3_x))
(* (- v0_y v3_y) (- v0_y v3_y)))))
(* (- v0_y v3_y)
(- v1_x v3_x)
(+ (* (- v2_x v3_x) (- v2_x v3_x)) (* (- v2_y v3_y) (- v2_y v3_y)))))
(* (- v0_x v3_x)
(- v2_y v3_y)
(+ (* (- v1_x v3_x) (- v1_x v3_x)) (* (- v1_y v3_y) (- v1_y v3_y)))))
0)
(> (- (- (- (+ (* (- v0_x v2_x)
(- v1_y v2_y)
(+ (* (- v3_x v2_x) (- v3_x v2_x))
(* (- v3_y v2_y) (- v3_y v2_y))))
(* (- v0_y v2_y)
(- v3_x v2_x)
(+ (* (- v1_x v2_x) (- v1_x v2_x))
(* (- v1_y v2_y) (- v1_y v2_y))))
(* (- v1_x v2_x)
(- v3_y v2_y)
(+ (* (- v0_x v2_x) (- v0_x v2_x))
(* (- v0_y v2_y) (- v0_y v2_y)))))
(* (- v1_y v2_y)
(- v3_x v2_x)
(+ (* (- v0_x v2_x) (- v0_x v2_x))
(* (- v0_y v2_y) (- v0_y v2_y)))))
(* (- v0_y v2_y)
(- v1_x v2_x)
(+ (* (- v3_x v2_x) (- v3_x v2_x)) (* (- v3_y v2_y) (- v3_y v2_y)))))
(* (- v0_x v2_x)
(- v3_y v2_y)
(+ (* (- v1_x v2_x) (- v1_x v2_x)) (* (- v1_y v2_y) (- v1_y v2_y)))))
0)
when I assert them to Z3, it says that it is satisfiable, but as soon as I change the second constraint to (< ... 0) instead of (> ... 0) which should be now unsatisfiable, z3 runs forever. I am wondering about the limitations of z3 for handing nonlinear real arithmetic. Is there any chance that Z3 can handle the above constraints (like by changing the way they are formulated or any other way)?
Yes, when we change (< ... 0) to (> ... 0) the problem becomes unsatisfiable, and there is a trivial proof for that since it becomes p < 0 and p > 0. The two polynomials in your post are identical after simplifications. However, Z3 misses this simple proof. This is be fixed in the next releases. In the meantime, we can catch examples that have this kind of simple proof by using a custom strategy.
(check-sat-using (then (! simplify :som true) (! simplify :sort-sums true) smt))
This strategy does the polynomial normalization and calls an engine that detects the inconsistency in p < 0 and p > 0. The whole example is available online at: http://rise4fun.com/Z3/JP4. I also pasted it in the end of the message.
Z3 keeps running forever because it misses the short/easy proof and tries to find the proof using more expensive and complete methods. The algorithm used by Z3 is described here. The algorithm uses a projection operator based on subresultants. This operation is very expensive, and produces very big polynomials for your example. This procedure works well for problems containing small polynomials with a small set of variables on each of them. In the future, we plan to combine complete and incomplete techniques and improve the set of problems that Z3 can solve in a reasonable amount of time.
(declare-const v0_x Real)
(declare-const v1_x Real)
(declare-const v2_x Real)
(declare-const v3_x Real)
(declare-const v4_x Real)
(declare-const v0_y Real)
(declare-const v1_y Real)
(declare-const v2_y Real)
(declare-const v3_y Real)
(declare-const v4_y Real)
(assert
(< (- (- (- (+ (* (- v0_x v3_x) (- v1_y v3_y) (+ (* (- v2_x v3_x) (- v2_x v3_x)) (* (- v2_y v3_y) (- v2_y v3_y)))) (* (- v0_y v3_y) (- v2_x v3_x) (+ (* (- v1_x v3_x) (- v1_x v3_x)) (* (- v1_y v3_y) (- v1_y v3_y)))) (* (- v1_x v3_x) (- v2_y v3_y) (+ (* (- v0_x v3_x) (- v0_x v3_x)) (* (- v0_y v3_y) (- v0_y v3_y))))) (* (- v1_y v3_y) (- v2_x v3_x) (+ (* (- v0_x v3_x) (- v0_x v3_x)) (* (- v0_y v3_y) (- v0_y v3_y))))) (* (- v0_y v3_y) (- v1_x v3_x) (+ (* (- v2_x v3_x) (- v2_x v3_x)) (* (- v2_y v3_y) (- v2_y v3_y))))) (* (- v0_x v3_x) (- v2_y v3_y) (+ (* (- v1_x v3_x) (- v1_x v3_x)) (* (- v1_y v3_y) (- v1_y v3_y))))) 0.0))
(assert
(< (- (- (- (+ (* (- v0_x v2_x) (- v1_y v2_y) (+ (* (- v3_x v2_x) (- v3_x v2_x)) (* (- v3_y v2_y) (- v3_y v2_y)))) (* (- v0_y v2_y) (- v3_x v2_x) (+ (* (- v1_x v2_x) (- v1_x v2_x)) (* (- v1_y v2_y) (- v1_y v2_y)))) (* (- v1_x v2_x) (- v3_y v2_y) (+ (* (- v0_x v2_x) (- v0_x v2_x)) (* (- v0_y v2_y) (- v0_y v2_y))))) (* (- v1_y v2_y) (- v3_x v2_x) (+ (* (- v0_x v2_x) (- v0_x v2_x)) (* (- v0_y v2_y) (- v0_y v2_y))))) (* (- v0_y v2_y) (- v1_x v2_x) (+ (* (- v3_x v2_x) (- v3_x v2_x)) (* (- v3_y v2_y) (- v3_y v2_y))))) (* (- v0_x v2_x) (- v3_y v2_y) (+ (* (- v1_x v2_x) (- v1_x v2_x)) (* (- v1_y v2_y) (- v1_y v2_y))))) 0.0))
(check-sat-using (then (! simplify :som true) (! simplify :sort-sums true) smt))
Related
I want to use z3 to find the result of the following expression after quantifier elimination. What is the correct syntax?
(declare-fun n () Int)
(declare-fun X () Int)
(declare-fun X_1_ () Int)
(declare-fun X_0_ () Int)
(assert (exists ((R__0 Int) (R__1 Int)(R_0_0 Int) (R_1_0 Int)) (and (>= n 3)
(= X n)
(=(+ X_0_ X_1_) X)
(>= X_0_ 0)
(<= R_0_0 X_0_)
(>= R_0_0 0)
(<= R_0_0 R__0)
(>= X_1_ 0)
(<= R_1_0 X_1_)
(>= R_1_0 0)
(<= R_1_0 R__0)
(<= R__0 X)
(<= R__1 X)
(= (+ R_1_0 R_0_0) R__0)
(> (* 3 R__0) (* 2 n))
(>= R_0_0 R_1_0)
(<= (* 3 R_0_0) (* 2 n))
(<= (* 3 R__1) (* 2 n)))))
(apply (using-params qe :qe-nonlinear true))
Use the qe_rec tactic. Changing your last line to:
(apply qe_rec)
produces:
(goals
(goal
(>= X_1_ 0)
(>= X_0_ 0)
(= (+ X_0_ X_1_) X)
(= X n)
(>= n 3)
(<= (+ (* (- 2) n) (* 3 X_1_)) 0)
(>= (* 9 X_1_) 7)
(<= (+ (* 2 n) (* (- 6) X_0_)) (- 1))
:precision precise :depth 1)
)
Following code I have tried in Online and Offline Z3
(set-option :smt.mbqi true)
(declare-var X Int)
(declare-var X_ Int)
(declare-var a_ Int)
(declare-var su_ Int)
(declare-var t_ Int)
(declare-var N1 Int)
(assert (>= X 0))
(assert (forall ((n1 Int)) (=> (< n1 N1) (>= X (* (+ n1 1) (+ n1 1))))))
(assert (= X_ X))
(assert (= a_ N1))
(assert (= su_ (* (+ N1 1) (+ N1 1))))
(assert (= t_ (* (+ N1 1) 2)))
(assert (< X (* (+ N1 1) (+ N1 1))))
(assert (not (< X (* (+ a_ 1) (+ a_ 1)))))
(check-sat)
Result unsat
Following code I have tried in Z3PY
set_option('smt.mbqi', True)
s=Solver()
s.add(X>=0)
s.add(ForAll(n1,Implies(n1 < N1,((n1+1)**2)<=X)))
s.add(((N1+1)**2)>X)
s.add(X_==X)
s.add(a_==N1)
s.add(su_==((N1+1)**2))
s.add(t_==(2*(N1+1)))
s.add(Not(((a_+1)**2)>X))
result- unknown
Is processing power different?
The reason for the difference in results is because the input is not the same. For instance, the expression
(N1+1)**2
is semantically the same as
(* (+ N1 1) (+ N1 1))
but because of the syntactic difference, Z3 will not simplify the formula to something that it can solve easily. The syntactically equivalent problem in Python is
s.add(X>=0)
s.add(ForAll(n1,Implies(n1 < N1,((n1+1)**2)<=X)))
s.add(((N1+1)*(N1+1)) > X)
s.add(X_==X)
s.add(a_==N1)
s.add(su_==((N1+1)*(N1+1)))
s.add(t_==(2*(N1+1)))
s.add(Not(((a_+1)*(a_+1))>X))
which yields the desired result.
Are the constraints the same?
I don't see the python variant of:
(assert (< X (* (+ N1 1) (+ N1 1))))
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)
I have such an expression
z3::expr expr = (exists ((flag Bool))
(exists ((w Int))
(exists ((i Int))
(exists ((counter Int))
(and (= i (+ x y z))
flag
(not (= i 0))
(= i counter)
(not (= w counter))
(>= i 1)
(= w 0)))))))
I use C++ API to do quantifier elimination and simplification.
z3::goal g(ctx);
g.add(expr);
z3::apply_result result = (qe & simplify & propagate_ineqs)(g);
I have defined the tactics.
I want to get such result:
(or (>= (+ x y z) 1 )
(<= (+ x y z) -1 ))
but I get this output which is appropriate for my application:
(let ((a!1 (or (<= (+ (* (- 1) x)
(* (- 1) y)
(* (- 1) z))
(- 1))
(<= (+ x y z) (- 1)))))
(and (or (<= (+ x y z) (- 1))
(>= (+ x y z) 1))
a!1
(>= (+ x y z) 1)))
what tactics should I use to make it work as I want?
I solved the issue by setting these options in the context, before defining the goal:
ctx.set(":pp-min-alias-size", 1000000);
ctx.set(":pp-max-depth", 1000000);
it didn't simplify as I wanted, but removed the "let"s and that's better than nothing!
I'm using z3 (v4.3.2 32bit on Linux) to decide satisfiability of Presburger Arithmetic formulas, but I have a problem with the following formula :
(assert (forall ((x1 Int) (x2 Int) (x3 Int))
(=> (and (= x3 1) (= x1 (- x2)))
(forall ((x4 Int) (x5 Int) (x6 Int))
(=> (= x6 x2)
(exists ((y Int))
(=> (= x5 (+ x6 (- x4)))
(and (= (+ x1 x4) y)
(= x5 (- y))
(= (+ x1 x4) (- x5))
)
)
)
)
)
)
)
)
(check-sat)
I'm quite sure that this formula is satisfiable, but z3 answers "unsat".
In fact, if I try to change a bit the formula, z3 answers "sat", like with the following formula
(assert (forall ((x3 Int) (x1 Int) (x2 Int))
(=> (and (= x3 1) (= x1 (- x2)))
(forall ((x4 Int) (x5 Int) (x6 Int))
(=> (= x6 x2)
(exists ((y Int))
(=> (= x5 (+ x6 (- x4)))
(and (= (+ x1 x4) y)
(= x5 (- y))
(= (+ x1 x4) (- x5))
)
)
)
)
)
)
)
)
(check-sat)
where I've just switched the quantification on x3 in top of the list of the first forall quantification. If I also remove the x3 variable, which is in fact useless, z3 also answers "sat".
Is there something I don't understand or is this a bug ?
Thanks for pointing this out.
This is a bug in the quantifier elimination modules that affects cases of nested quantifiers.
It is now fixed in the unstable branch.