As part of a static-time compiler analysis, I am using (the Java API of) Z3 solver to check whether a system of (in)equations is satisfiable. Everything seems to work correctly. However, the solver is too slow. For the system posted below, Z3 takes about 109s to declare the system as unsatisfiable. I do not have any prior experience with Z3. Are there any rule of thumbs to follow in order to help the solver be reasonably efficient?
The set of (in)equations in prefix form are as follows: (Note: ITE represents if-then-else, and takes three operands.)
[ < , i_imopVarPre1, ITE , > , - , + , + , 0, * , tid2, + , / , - , 100, 0, numTh, % , - , 100, 0, numTh, + , / , - , 100, 0, numTh, % , - , 100, 0, numTh, 100, 0, 100, + , + , 0, * , tid2, + , / , - , 100, 0, numTh, % , - , 100, 0, numTh, + , / , - , 100, 0, numTh, % , - , 100, 0, numTh]
[ >= , i_imopVarPre1, + , 0, * , tid2, + , / , - , 100, 0, numTh, % , - , 100, 0, numTh]
[ < , i_imopVarPre0, ITE , > , - , + , + , 0, * , tid1, + , / , - , 100, 0, numTh, % , - , 100, 0, numTh, + , / , - , 100, 0, numTh, % , - , 100, 0, numTh, 100, 0, 100, + , + , 0, * , tid1, + , / , - , 100, 0, numTh, % , - , 100, 0, numTh, + , / , - , 100, 0, numTh, % , - , 100, 0, numTh]
[ >= , i_imopVarPre0, + , 0, * , tid1, + , / , - , 100, 0, numTh, % , - , 100, 0, numTh]
[ = , i_imopVarPre0, i_imopVarPre1]
[ > , numTh, 0]
[ >= , tid2, 0]
[ < , tid2, numTh]
[ >= , tid1, 0]
[ < , tid1, numTh]
[ != , tid2, tid1]
I have declared all variables to be of Int sort. After adding these expressions to Z3, when I print the assertions in the solver, I get the following output:
(let ((a!1 (+ (div (- 100 0) numTh) (mod (- 100 0) numTh))))
(let ((a!2 (> (- (+ 0 (* tid2 a!1) a!1) 100) 0)))
(< i_imopVarPre1 (ite a!2 100 (+ 0 (* tid2 a!1) a!1)))))
(let ((a!1 (* tid2 (+ (div (- 100 0) numTh) (mod (- 100 0) numTh)))))
(>= i_imopVarPre1 (+ 0 a!1)))
(let ((a!1 (+ (div (- 100 0) numTh) (mod (- 100 0) numTh))))
(let ((a!2 (> (- (+ 0 (* tid1 a!1) a!1) 100) 0)))
(< i_imopVarPre0 (ite a!2 100 (+ 0 (* tid1 a!1) a!1)))))
(let ((a!1 (* tid1 (+ (div (- 100 0) numTh) (mod (- 100 0) numTh)))))
(>= i_imopVarPre0 (+ 0 a!1)))
(= i_imopVarPre0 i_imopVarPre1)
(> numTh 0)
(>= tid2 0)
(< tid2 numTh)
(>= tid1 0)
(< tid1 numTh)
(not (= tid2 tid1))
Also, please find the statistics obtained after the call to check()
method here: https://pastebin.com/cKimtm7g
For the above system, is it expected for Z3 to take ~100s? If not, am I missing something?
- Shall I create shorter (in)equations, wherever possible? Would that help?
- Does the order in which we add constraints matter?
Any suggestions in this regard would be really helpful. The number of such queries might be in the order of hundreds.
Furthermore, are there ways to reuse Z3 queries, since many of them might be isomorphic? Since identifier names can be different, I feel that the task of memoization/search itself can be costly.