Understanding Z3's low performance on a quantified LIA formula - z3

I have come across the following formula that takes Z3 several minutes to solve:
(set-logic LIA)
(assert
(forall ((f Int))
(exists ((a Int) (b Int))
(= (+ (* 17 a) (* 19 b)) f)
)
)
)
(check-sat)
(exit)
I have read the paper documenting the approach Z3 uses to decide LIA, however, I fail to see why does Z3 struggles with the given formula. Could you please explain?

Quantifiers are hard, nested quantifiers are harder. It's really hard to guess what's going on internally in any given SMT-solver; unfortunately. They are more-or-less black-boxes, unless you invest in studying their internals. A good book on the techniques used is Kroening and Strichman's Decision Procedures, which has an entire chapter on linear arithmetic. You might want to read through that book, which also contains further references.
Note that SMT solvers do better when they "try to find a model" instead of trying to prove things, so one usually asks the negation of a theorem to be satisfied instead. An unsat result then indicates theoremhood. Doing this translation to your problem, one gets:
(set-logic LIA)
(declare-fun f () Int)
(assert
(forall ((a Int) (b Int))
(distinct (+ (* 17 a) (* 19 b)) f)
)
)
(check-sat)
(exit)
Unfortunately, z3 takes even longer on this form; which surprised me. (I'd have expected at least the same performance.) But yices solves it instantly! (For comparison, CVC5 seems to run forever on it as well, who knows why.) So, you might want to try yices instead if you have problems of this sort.
The fact that yices does well on this while z3 doesn't suggests perhaps z3 is missing some heuristic, or a rewrite that yices has. You might want to report this discrepancy at https://github.com/Z3Prover/z3/issues. Not as a bug, but rather as a curiosity why yices performs much better. I'm sure the developers would appreciate hearing about it, even if they choose not to do anything to make z3 go faster on this problem.

Related

Z3 stuck solving a seemingly simple existential

I'm using the Z3 theorem prover as a backend of a compiler to verify that function calls respect their contracts. However, Z3 appears to be stuck when confronted with solving seemingly simple existential queries.
I'm using Z3 version 4.8.5 - 64 bit (in Linux 5.0). I understand that the SMT solver is not complete for first order logic (as soon as quantifiers are involved), but still I would have expected the following to work.
This is a minimal example showing the problem, which does not terminate:
(declare-datatypes ()
((Term (structure (constructor Int) (arguments TermList)))
(TermList empty (cons (head Term) (tail TermList)))))
(assert
(forall ((A TermList) (B Term))
(implies
(= A (cons B empty))
(exists ((C Term))
(= A (cons C empty))))))
(check-sat)
Is this a well known bug or limitation of Z3?
Are there any reasonable alternatives to represent this query in such a way that Z3 can handle it?
These sorts of problems are just not suitable for SMT solvers. There have been many queries along these lines, here're some of the most relevant ones:
Creating a transitive and not reflexive function in Z3
parthood definition in Z3
max element in Z3 Seq Int
Long story short, use a more powerful system to conduct such proofs, which uses SMT solvers as proof-tools under the hood. You'll have to do some manual "guiding" but the tactic language of theorem provers these days are quite well developed that they can discharge most goals of this form automatically for you. (See this paper for some Isabelle specific details: https://people.mpi-inf.mpg.de/~jblanche/frocos2011-dis-proof.pdf )

Limit scope of defined sort

I am trying to limit the scope of a newly defined sort via SMT-LIB. Doing so would simplify my script by reducing the amount of necessary assertions.
For this I played with define-sort and forall.
(define-sort R () Real)
(assert (forall ((x R)) (< 0 x)))
(declare-const r R)
(check-sat)
But z3 always returns unsat no matter what. So I guess this is not possible. Is it?
Unfortunately, this is not possible in SMTLib. See this answer for details: Is there an UnsignedIntSort in Z3?
As Christoph pointed out your query is unsat because the quantified assertion is false; which puts the solver in the unsat state, regardless of what other constraints you might have.
In my experience, the best way to deal with these scenarios is to not use SMTLib directly; but instead a higher-level API and use the programmatic features afforded by those environments to spit out all the > 0 constraints as you need them. It's hairy and ugly, but it is the only way to deal with these in SMTLib as it stands today. The only solver that supported this sort of "predicate subtyping" was Yices v1.0 with its own input language, which is no longer supported.

Undocumented trigonometric functions in Z3 reparable?

Officially, there is no trig support in Z3. For example, see this question, or this one. However, there are undocumented trigonometric operators in Z3 -- they are used for example in the regression tests. There is even a built-in symbol called pi. Z3 can even do some trivial proofs with these operators, e.g.:
(declare-fun x () Real)
(assert (= (cos pi) x))
(check-sat)
(get-value (x))
Comes back with:
sat
((x (- 1.0)))
These operators do not work well. For example, this little input file will cause a seg fault with Z3 4.4.1, or cause a rapid explosion in memory usage with the master branch as this commit (now):
(declare-fun x () Real)
(assert (< (sin x) -1.0))
(check-sat)
I'm not surprised that an undocumented feature that the team says doesn't exist doesn't work. My question is: are they possible to fix? What level of performance would be a justified addition to Z3? I know that I can do a number of trigonometric proofs with Z3 using uninterpreted functions plus trigonometric identities. Is there any interest in this among the Z3 team?
Thanks, Z3 should not crash in such cases. It should be more graceful about handling these operations. I checked in a fix to this now, 9b91e6f..cb29c07.
OTOH, there is essentially no theory reasoning for such operators.
For example, Z3 does not know the bounds for sin. You would have to axiomatize such properties yourself. Z3 returns "unknown" (or "unsat", but not "sat") when you use the built-in functions that have no (partial) decision procedure support.

Using functions, reals, and quantifiers in Z3

I'm trying to pose queries to Z3 that involve uninterpreted functions (always with domain int), reals, and quantifiers. I know that adding quantifiers will often lead to unknown results, but I was surprised at how quickly this happened:
(declare-fun $in1 (Int) Real)
(declare-fun $in2 (Int) Real)
(assert (< ($in1 0) ($in2 0)))
(assert (forall (($$out Real))
(not (and (< ($in1 0) $$out) (< $$out ($in2 0))))))
(check-sat)
This query should result in unsat but instead times out with unknown. Is there a flag or option I could set which might lead Z3 to solve this query? I'd hate to have to go through and flatten all my uninterpreted functions into scalars, but that is something I could do.
Yes, it looks like this is a hard instance for Z3. E-matching fails to prove unsatisfiability and after that MBQI essentially starts to enumerate real numbers, which will not lead to the goal here.
If you just want a quick result but don't care about unknowns, simply set smt.mbqi.max_iterations to a small enough value. You can also try to help the e-matching engine by providing instantiations patterns (see e.g., the quantifier section in the Z3 guide).
There's also a related question that might help understanding: Z3 patterns and injectivity
Arie Gurfinkel pointed out that (check-sat-using qe-sat) solves this problem.

How to efficiently solve combinations of theories in Z3

I am trying to solve a problem that involves propositional satisfiability (with quantifiers), and linear arithmetic.
I have formulated the problem, and Z3 is able to solve it, but it is taking an unreasonably long time.
I have been trying to help Z3 along by specifying tactics, but I haven't made much progress (I have no knowledge of logic theories).
Following is a highly simplified problem that captures the essence of what I am trying to solve. Could anyone give suggestions?
I tried to read up on things like Nelson Oppen method, but there were a lot of unfamiliar notations, and it'll take a long time to learn it.
Also, does Z3 allow users to tweak these configurations? Lastly, how can I use these tactics with z3py?
(declare-datatypes () ((newtype (item1) (item2) (item3))))
(declare-fun f (newtype newtype) Bool)
(declare-fun cost (newtype newtype) Real)
(assert (exists ((x newtype)(y newtype)) (f x y)))
(assert (forall ((x newtype)(y newtype)) (=> (f x y) (> (cost x y) 0))))
(assert (forall ((x newtype) (y newtype)) (<= (cost x y) 5)))
(check-sat)
(get-model)
The sample problem you encoded uses quantification. Z3 uses a particular procedure for deciding satisfiability of a class of quantified formulas, referred to as model-based quantifier instantiation (the mbqi option). It works by extending a candidate model for the quantifier-free portion of your formulas into a model for also the quantifiers. This process may involve a lot
of search. You can extract statistics from the search process by running Z3 with the option /st and it will show selected statistics of the search process and give a rough idea of what is happening during search. There is no particular tactic combination that specializes to classes of formulas with arithmetic and quantifiers (there is a class of formulas that use bit-vectors and quantifiers that are handled by a default tactic for such formulas).
I tried to read up on things like Nelson Oppen method, but there were a lot of unfamiliar notations, and it'll take a long time to learn it.
This is going to be a bit tangential to understanding the search issue with quantifiers.
Also, does Z3 allow users to tweak these configurations?
Yes, you can configure Z3 from the command-line.
For example, you can disable MBQI using the command line:
z3 tt.smt2 -st smt.auto_config=false smt.mbqi=false
Z3 now returns "unknown" because the weaker quantifier engine that performs selected instantiations
is not going to be able to determine that the formula is satisfiable.
You can learn the command-line options by following the instructions from "z3 -?"
Lastly, how can I use these tactics with z3py?
You can use tactics from z3py. The file z3.py contains
brief information of how to combine tactics.
Though, I would expect that the difficulty of your problem class really has to do
with the search hardness involved with quantifiers. It is very easy to pose
formulas with quantifiers where theorem provers diverge as these classes of formulas
are generally highly undecidable.

Resources