ini-option CASE_SPLIT produces strange model - z3

While working on my masters thesis with z3 I found something strange I can't understand.
I hope you can help me. :)
The smt-file I wrote looks like this:
(set-logic QF_UF)
(set-info :smt-lib-version 2.0)
;Declare sort Node and its objects.
(declare-sort Node 0)
(declare-fun n0 () Node)
(declare-fun n1 () Node)
;Predicate
(declare-fun dead_0 (Node) Bool)
;Abbreviation
(declare-fun I () Bool)
;initial configuration
(assert(= I (and
(not(= n0 n1))
(not(dead_0 n0))
(dead_0 n1))))
;Predicate
(declare-fun dead_1 (Node) Bool)
;variable
(declare-fun m0_1 () Node)
;Abbreviation for Transformation
(declare-fun TL1_1 () Bool)
;Transformation1neuerKnoten1
(assert(or (= m0_1 n0)(= m0_1 n1)))
;Is the whole formula satisfiable?
(assert(= (and I TL1_1) true))
(check-sat)
(get-model)
Everything works quite well while using z3 as a command line tool with default-options.
The generated model contains:
;; universe for Node:
;; Node!val!0 Node!val!1
;; -----------
and
(define-fun n0 () Node
Node!val!0)
(define-fun n1 () Node
Node!val!1)
(define-fun m0_1 () Node
Node!val!0)
So my variable m0_1 is bound to the node n0.
Then I used z3 with an ini-file only containing CASE_SPLIT=5.
The result was a strange model. In my opinion the difference is basically
that my variable m0_1 is NOT bound to any of my nodes n0 or n1.
The produced model contains:
;; universe for Node:
;; Node!val!2 Node!val!0 Node!val!1
;; -----------
and
(define-fun n0 () Node
Node!val!0)
(define-fun n1 () Node
Node!val!1)
(define-fun m0_1 () Node
Node!val!2)
So my question is this: why did z3 create another node (Node!val!2) and why is my variable m0_1 bound to this new node? I thought that one of my assertions ((assert(or (= m0_1 n0)(= m0_1 n1)))) would forbid this.
Thanks in advance! :)

Z3 has a feature called "relevancy propagation". This is feature is very effective for problems containing quantifiers, but it is usually overhead for quantifier free problems. The command line option RELEVANCY=0 disables relevancy propagation, and RELEVANCY=2 or RELEVANCY=1 enables it.
The option CASE_SPLIT=5 assumes that relevancy propagation is enabled.
If we provide CASE_SPLIT=5 RELEVANCY=0, then Z3 will generate a warning message
WARNING: relevacy must be enabled to use option CASE_SPLIT=3, 4 or 5
and, ignores the option.
Moreover, by default, Z3 uses a "automatic configuration" feature. This feature scans the input formula and adjusts the Z3 configuration for the given instance.
So, in your example, the following happens:
You provide the option CASE_SPLIT=5
When Z3 validates the command line options, relevancy propagation is disabled, and no warning message is generated.
Z3 runs the auto configuration procedure, and since your example is quantifier free, it disables relevancy propagation RELEVANCY=0. Now, an inconsistent configuration is uses, and Z3 produces the wrong result.
To avoid this problem, if you use CASE_SPLIT=5, then you should also use AUTO_CONFIG=false (disables auto configuration) and RELEVANCY=2 (enables relevancy propagation). So, the command line should be:
z3 CASE_SPLIT=5 AUTO_CONFIG=false RELEVANCY=2 file.smt2
In the next release (Z3 4.2), I will make Z3 to display the warning message if the user tries to set CASE_SPLIT=5 when auto configuration is enabled.

Related

Confused about a simple SAT matching problem using Z3

I am trying to solve a simple matching problem using Z3 which it claims is unsat. I have set this up the following way:
p_{x}_{y} to match up offer x with request y.
sum_{x} {p_x_y} <= 1 meaning y can only be matched once (using PbLe)
sum_{y} {p_x_y} <= 1 meaning x can only be matched once (using PbLe)
whether p_x_y is a valid match comes from an external computation that returns a Bool, so for this I have p_x_y => computation_result (i.e. if paired, then computation_result).
finally, I want to maximize the number of matchings. So I have:
maximize sum_{x} ( sum_{y} p_x_y ) (I do this with p_x_y.ite(Int(1), Int(0))).
I was able to whip this up quite quickly using z3-rs in Rust (not sure if that makes a difference). And this is the solver state before I run check on it:
Solver: (declare-fun p_0_0 () Bool)
(declare-fun p_1_0 () Bool)
(declare-fun k!0 () Int)
(declare-fun k!1 () Int)
(assert (=> p_0_0 true))
(assert (=> p_1_0 true))
(assert ((_ at-most 1) p_0_0))
(assert ((_ at-most 1) p_1_0))
(assert ((_ at-most 1) p_0_0 p_1_0))
(maximize (+ (ite p_1_0 k!1 k!0) (ite p_0_0 k!1 k!0)))
(check-sat)
Z3 claims this is Unsat and I am quite stumped. I don't see why p_0_0 = T, p_1_0 = F doesn't satisfy this formula.
Thank you very much for the help.
I can't replicate this. When I run your program, z3 prints: (after adding (get-model) at the end)
sat
(
(define-fun p_0_0 () Bool
true)
(define-fun p_1_0 () Bool
false)
(define-fun k!1 () Int
0)
(define-fun k!0 () Int
(- 1))
)
which matches your expectations.
Couple of things to make sure:
Is your z3 version "new" enough? 4.11.3 is the latest master I think
You mentioned you use it from Rust. Perhaps you didn't use the rust-API correctly? Or, maybe Rust interface has a bug.
I'd start by running it manually on your machine using the SMTLib script you've given. If you get SAT (which you should!), perhaps ask at the Rust forum as the bug is likely either in your Rust program or the Rust bindings itself. If you get UNSAT, try upgrading your z3 installation and possibly recompile the Rust bindings if that's relevant. (I'm not familiar with the Rust bindings to say if it needs a recompile or not if you upgrade your z3. It could be either way.)
A guess
Without seeing the details, it's hard to opine further. However, notice that you've posed this as an optimization problem; and asked z3 to maximize the addition of two uninterpreted integers. So, it's possible the Rust bindings are adding a call of the form:
(get-objectives)
at the end, to which z3 will respond:
sat
(objectives
((+ (ite p_1_0 k!1 k!0) (ite p_0_0 k!1 k!0)) oo)
)
That is, the objective you're maximizing is unbounded. This means there's no value for k!0 and k!1 the solver can present to you: The goal gets arbitrarily large as these get larger. It's possible the Rust interface is internally treating this as "unsat" since it cannot find the values for these constants. But that's just my guess without knowing the specifics of how the Rust bindings work.

Why does smtlib/z3/cvc4 allow to declare the same constant more than once?

I have a question about declare-const in smtlib.
For example,
In z3/cvc4, the following program doesn't report an error:
C:\Users\Chansey>z3 -in
(declare-const x Int)
(declare-const x Bool)
In the smt-lib-reference, it says that
(declare-fun f (s1 ... sn) s) ... The command reports an error if a function symbol with name f is already present in the current signature.
So the sort s is included in the entire signature of the x, is that right?
But why is it so? What is the motivation behind it?
In my understanding, the x is variable identifier and in general (e.g. in some general programming languages) we are not allowed to declare the same variable with different types. So I think the above code is best to report an error.
I once thought that perhaps z3/smtlib can support redefinition?, but not...
C:\Users\Chansey>z3 -in
(declare-const x Int)
(declare-const x Bool)
(assert (= x true))
(error "line 3 column 11: ambiguous constant reference, more than one constant with the same sort, use a qualified expre
ssion (as <symbol> <sort>) to disambiguate x")
So the above code is definitely wrong, why not report the error earlier?
PS. If I use the same sort, then it will report an error (that great, I hope the Bool case can also report the error):
C:\Users\Chansey>z3 -in
(declare-fun x () Int)
(declare-fun x () Int)
(error "line 2 column 21: invalid declaration, constant 'x' (with the given signature) already declared")
Thanks.
In SMTLib, a symbol is identified not just by its name, but also by its sort. And it's perfectly fine to use the same name, so long as you have a different sort, as you observed. Here's an example:
(set-logic ALL)
(set-option :produce-models true)
(declare-fun x () Int)
(declare-fun x () Bool)
(assert (= (as x Int) 4))
(assert (= (as x Bool) true))
(check-sat)
(get-model)
(get-value ((as x Int)))
(get-value ((as x Bool)))
This prints:
sat
(
(define-fun x () Bool
true)
(define-fun x () Int
4)
)
(((as x Int) 4))
(((as x Bool) true))
Note how we use the as construct to disambiguate between the two x's. This is explained in Section 3.6.4 of http://smtlib.cs.uiowa.edu/papers/smt-lib-reference-v2.6-r2021-05-12.pdf
Having said that, I do agree the part of the document you quoted isn't very clear about this, and perhaps can use a bit of a clarifying text.
Regarding what the motivation is for allowing this sort of usage: There are two main reasons. The first is o simplify generating SMTLib. Note that SMTLib is usually not intended to be hand-written. It's often generated from a higher-level system that uses an SMT solver underneath. So being flexible in allowing symbols to share name so long as they can be distinguished by explicit sort annotations can be beneficial when you use SMTLib as an intermediate language between a higher-level system and the solver itself. But when you're writing SMTLib by hand, you should probably avoid this sort of duplication if you can, for clarity if nothing else.
The second reason is to allow for a limited form of "overloading" to be used. For instance, think about the SMTLib function distinct. This function can operate on any type of object (Int, Bool, Real etc.), yet it's always called distinct. (We don't have distinct-int, distinct-bool etc.) The solver "distinguishes" which one you meant by doing a bit of an analysis, but in cases it cannot, you can help it along with an as declaration as well. So, theory symbols can be overloaded in this way, which is also the case for =, +, *, etc. Of course, SMTLib does not allow for users to define such overloaded names, but as the document states in footnote 29 on page 91, this restriction might be removed in the future.

Defining Rules for Bit Vectors in SMT2

I have switched from using Int to Bit Vectors in SMT. However, the logic QF_BV does not allow the use of any quantifiers in your script, and I need to define FOL rules.
I know how to eliminate existential quantifiers, but universal quantifiers? How to do that?
Imagine a code like that:
(set-logic QF_AUFBV)
(define-sort Index () (_ BitVec 3))
(declare-fun P (Index) Bool)
(assert (forall ((i Index)) (= (P (bvadd i #b001)) (not (P i)) ) ) )
Strictly speaking, you're out-of-luck. According to http://smtlib.cs.uiowa.edu/logics.shtml, there's no logic that contains quantifiers and bit-vectors at the same time.
Having said that, most solvers will allow non-standard combinations. Simply leave out the set-logic command, and you might get lucky. For instance, Z3 takes your query just fine without the set-logic part; I just tried..

How to define enumerated types in SMT-LIB for Z3

I previously used Z3's API to define an enumerated type like so
let T = ctx.MkEnumSort("T", [| "a"; "b"; "c"|])
which enumerates the elements of a type T as being "a" "b" and "c" (and nothing else). However I am now trying to do something similar but via SMT-LIB rather than the API and I am running into a problem of Z3 complaining about quantifiers. The program I am using (Boogie) generates the following smt
...
(declare-sort T#T 0)
(declare-fun a() T#T)
(declare-fun b() T#T)
(declare-fun c() T#T)
(assert (forall ((x T#T) )
(! (or
(= x a)
(= x b)
(= x c)
)
:qid |gen.28:15|
:skolemid |1|
)))
...
The assertion is the type closure axiom that asserts that the type has no other members. But when I send this (along with other stuff) to Z3, after thinking about it a bit, returns
WARNING: relevacy must be enabled to use option CASE_SPLIT=3, 4 or 5
unknown
(:reason-unknown (incomplete quantifiers))
Notes: 1. I have MBQI turned on. 2. Boogie has an option called "z3types" but it doesn't seem to make any difference
What is the SMT-LIB equivalent of the MkEnumSort API call?
thanks
P.S. I have tried with RELEVANCY set to both 1 and 2 and I still get the warning about relevancy (CASE_SPLIT is set to 3)
Use
(declare-datatypes () ((T#T (a) (b) (c)))
There is a tutorial with more details: http://rise4fun.com/z3/tutorialcontent/guide#h27
Use this for SMTLib v2:
(declare-datatypes ((T#T 0)) (((a) (b) (c))))
where T#T is the name of your type, and a, b, c its possible values.

Tactic to use soft constraints/assumptions only after a time-out?

Assume that I have a Z3 preamble that includes several function declarations and definitional axioms (with explicit patterns), e.g., my own sequence axioms:
(declare-sort $Seq)
(declare-fun $Seq.nil () $Seq)
(declare-fun $Seq.len ($Seq) Int)
(declare-fun $Seq.con (Int $Seq) $Seq)
(declare-fun $Seq.at ($Seq Int) Int)
(declare-fun $Seq.in ($Seq Int) Bool)
...
(assert (forall ((xs $Seq)) (! ... )
(assert (forall ((xs $Seq) (x Int)) (! ... )
...
After this preamble has been emitted a lot of assertions are pushed to Z3, interspersed with calls to check-set to see whether certain negated formulas can be shown unsat (FYI: my context is software verification using symbolic execution).
Most of these assertions are simple and don't refuting them doesn't require the sequence axioms. However, from a few simple tests I get the impression that their presence nevertheless slows Z3 down.
I thus guarded the definitional axioms by an implication with a dummy boolean constant as the left-hand side of the implication (as suggested by this answer), e.g.,
(declare-const $useSeq Bool)
(assert (=> ($useSeq (forall ((xs $Seq)) (! ... )
and changed every check-sat that needs to reason about sequences into one that assumes $useSeq, i.e.,
(check-sat $useSeq)
Question: Is there a tactic/way to make Z3 use certain assertions only after a time-out? E.g.,
(check-sat-using (or-else (try-for smt 500) (smt $useSeq)))
I could of course manually emit a time-bounded check-sat first, followed by a check-sat useSeq $useSeq if needed, but it would be nice if it could be done with some kind of tactics.
Unfortunately, this cannot be done with the current set of tactics available in Z3.

Resources