z3 behaviour changing on request for unsat core - z3

I have several SMTLIB2 examples which z3 normally finds unsat in 10s of milliseconds, yet, when I add in a request for it to generate unsat cores, the check-sat keeps going for minutes without returning. Is this behaviour to be expected? Does requesting unsat cores do more than just switch on instrumentation recording dependencies, and change which procedures and options z3 runs with? Is it possible to set further options so I see the same behaviour when I'm using unsat core generation as I see when I'm not using it?
I'm using Z3 4.3.1 (stable branch) on Scientific Linux 6.3.
The examples are in AUFNIRA, though several involve no reals and probably are not non-linear.
Thanks,
Paul.

The unsat cores are tracked using "answer literals" (aka assumptions).
When we enable unsat core extraction and use assertions such as
(assert (! (= x 10) :named a1))
Z3 will internally create a fresh Boolean variable for the name a1, and assert
(assert (=> a1 (= x 10)))
When, check-sat is invoked, it assumes all these auxiliary variables are true. That is, Z3 tries to show the problem is unsat/sat modulo these assumptions. For satisfiable instances, it will terminate as usual with a model. For unsatisfiable instances, it will terminate whenever it generates a lemma that contains only these assumed Boolean variables. The lemma is of the form (or (not a_i1) ... (not a_in)) where the a_i's are a subset of the assumed Boolean variables.
As far as I know, this technique has been introduced by the MiniSAT solver. It is described here (Section 3). I really like it because it is simple to implement and we essentially get unsat core generation for free.
However, this approach has some disadvantages. First, some preprocessing steps are not applicable anymore. If we just assert
(assert (= x 10))
Z3 will replace x with 10 everywhere. We say Z3 is performing "value propagation". This preprocessing step is not applied if the assertion is of the form
(assert (=> a1 (= x 10)))
This is just an example, many other preprocessing steps are affected.
During solving time, some of the simplification steps are also disabled.
If we inspect the Z3 source file smt_context.cpp we will find code such as:
void context::simplify_clauses() {
// Remark: when assumptions are used m_scope_lvl >= m_search_lvl > m_base_lvl. Therefore, no simplification is performed.
if (m_scope_lvl > m_base_lvl)
return;
...
}
The condition m_scope_lvl > m_base_lvl) is always true when "answer literals"/assumptions are used.
So, when we enable unsat core generation, we may really impact the performance. It seems that nothing is really for free :)

Related

Naming assertions in Z3 proofs

Is it possible to get assertion names inside Z3 (version 4.8.9) proofs?
As a minimal example:
(set-option :produce-proofs true)
(assert (! false :named name))
(check-sat)
(get-proof)
I would like to have the following output:
unsat
((proof (asserted name)))
However, this is the actual output:
unsat
((proof (asserted false)))
Is it possible to have the proof refering to the assertion names instead of the actual formula?
Via experimenting, I found out that it is possible to add (set-option :unsat-core true).
However, this makes the proof more complicated. With the option set, the output is:
unsat
((proof
(let (($x27 (not name)))
(let ((#x30 (mp (asserted (=> name false)) (rewrite (= (=> name false) $x27)) $x27)))
(unit-resolution #x30 (asserted name) false)))))
Also I am not sure if enabling proof and unsat-core generation simultaneously is allowed, in https://github.com/Z3Prover/z3/issues/189#issuecomment-129786093 NikolajBjorner states:
Z3 doesn't really support simultaneous proof and core generation, ...
This is extremely unlikely. Names are pretty much only used in unsat-core generation. Proof objects in z3 remain more or less a black-box. However, if you restrict yourself to only bit-vectors, it might work better. See: https://link.springer.com/chapter/10.1007/978-3-642-25379-9_15
This paper is also quite relevant in this context: http://homepage.divms.uiowa.edu/~ajreynol/lpar15.pdf. In short, BV-solvers usually reduce the problem to propositional reasoning, and thus resolution-style proofs that can be easily checked by external tools are much easier to construct. But for other logics, and especially when quantifiers are involved, the proof steps can be rather opaque and replay will be much more difficult.

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.

Can I replay a proof in Z3?

Is it possible to have Z3 serialise a proof for some assertion, and replay the proof on later invocations instead of running a proof-search again? I know Z3 can output counter-examples for unsat, but can it provide proofs for models that are sat?
Terminology note: Z3 (and SAT/SMT solvers in general) output models for sat, and proofs for unsat.
Proof generation is actually an SMT-Lib feature. See page 56 of http://smtlib.cs.uiowa.edu/papers/smt-lib-reference-v2.6-r2017-07-18.pdf
And Z3 indeed supports it, here's the simplest example:
(set-option :produce-proofs true)
(declare-fun a () Bool)
(assert (= a (not a)))
(check-sat)
(get-proof)
Z3 says:
unsat
((proof
(mp (asserted (= a (not a))) (rewrite (= (= a (not a)) false)) false)))
The format is solver-specific. The SMTLib document says:
(get-proof) asks the solver for a proof of unsatisfiability for the
set of all formulas in the current context. The command can be issued
only if the most recent check command had an empty set of assumptions.
The solver responds by printing a refutation proof on its regular
output channel. The format of the proof is solver-specific. The
only requirement is that, like all responses, it be a member of
s_expr.
So far as I know there's no "public" switch to tell Z3 to read this proof back and do anything with it. It wouldn't surprise me, however, that they might have internal tools to consume this output.
Replaying in a theorem prover
Isabelle theorem prover can read Z3's proofs back and replay them internally to construct the corresponding proof. This is probably closer to what you are looking for. Here's a paper that describes this work: http://www21.in.tum.de/~boehmes/proofrec.pdf Of course, precisely which logics are supported and whether the connection is actively maintained is a different question! You might find the "related work" section of that paper quite helpful.

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