strange output using ctx-simplify tactic - z3

Small example is prepared below:
(declare-datatypes () ((Type1 a b c d e g h i f k l m n o p q r s t u v w z)))
(declare-const x Type1)
(declare-const y Type1)
(assert (and (= y x) (or (and (not (= x g)) (not (= x a))) (and (or (not (= x g)) (not (= x q))) (not (= x a))))))
(apply ctx-simplify)
The output is:
(goals
(goal
(= y x)
(or (not and) (not (= x a)))
:precision precise :depth 1)
)
What (or (not and) (not (= x a))) means? Bug?
Thank you.

Thanks for pointing this out.
I agree it looks strange with the "and" taking no arguments in the printout.
The context simplifier creates a conjunction with 0 arguments.
It gets printed as simply "and".
So the expression returned by ctx-simplify is equivalent to (not (= x a)).
I will update the ctx-simplify tactic to return expressions without the empty conjunction.

Related

Is it possible to use both bit-blast and soft-assert with the z3 solver?

I'm trying to use the z3 smt solver to allocate values to variables subject to constraints. As well as hard constraints I have some soft constraints (e.g. a != c). I expected to be able to specify the hard constraints with assert and the soft constraints as soft-assert and this works if I solve with (check-sat).
Some of the files are large and complex and only solve in a reasonable time if I turn on bit-blasting using (check-sat-using (then simplify solve-eqs bit-blast sat)). When I do this the soft asserts seem to be ignored (example below or at rise4fun). Is this expected? Is it possible to use both bit-blast solving and soft-assert at the same time?
The following SMT code defines 4 bitvectors, a, b, c & d which should all be able to take unique values but are only forced to do so by soft asserts. Using the check-sat (line 39) works as expected but the check-sat-using (line 38) assigns b and d to the same value.
(set-option :produce-models true)
(set-logic QF_BV)
;; Declaring all the variables
(declare-const a (_ BitVec 2))
(declare-const b (_ BitVec 2))
(declare-const c (_ BitVec 2))
(declare-const d (_ BitVec 2))
(assert (or (= a #b00)
(= a #b01)
(= a #b10)
(= a #b11)))
(assert (or (= b #b00)
(= b #b01)
(= b #b10)
(= b #b11)))
(assert (or (= c #b00)
(= c #b01)
(= c #b10)
(= c #b11)))
(assert (or (= d #b00)
(= d #b01)
(= d #b10)
(= d #b11)))
;; Soft constraints to limit reuse
(assert-soft (not (= a b)))
(assert-soft (not (= a c)))
(assert-soft (not (= a d)))
(assert-soft (not (= b c)))
(assert-soft (not (= b d)))
(assert-soft (not (= c d)))
(check-sat-using (then simplify solve-eqs bit-blast sat))
;;(check-sat)
(get-value (a
b
c
d))
Great question! When you use assert-soft the optimization engine kicks in by default. You can see this by using your program with the (check-sat) clause, and running with higher verbosity. I've put your program in a file called a.smt2:
$ z3 -v:3 a.smt2
(optimize:check-sat)
(sat.solver)
(optimize:sat)
(maxsmt)
(opt.maxres [0:6])
(sat.solver)
(opt.maxres [0:0])
found optimum
sat
((a #b01)
(b #b00)
(c #b11)
(d #b10))
So, we can see z3 is treating this as an optimization problem, which takes soft-constraints into account and gives you the "disjointness" you're seeking.
Let's do the same, but this time we'll use the check-sat call that specifies the tactics to use. We get:
$ z3 -v:3 a.smt2
(smt.searching)
sat
((a #b11)
(b #b11)
(c #b11)
(d #b10))
And this confirms your suspicion: When you tell z3 exactly what to do, it doesn't do the optimization pass. In hindsight, this is to be expected, but I do agree that it's rather surprising.
The question is then whether we can tell z3 to do the optimization explicitly. However I'm not sure if this is even possible within the tactic language. I think this question is well worthy of asking at their issues site (https://github.com/Z3Prover/z3/issues) and see if there's a magic incantation you can use to kick off the maxres engine from the tactic language. (This may not be possible due to a number of reasons, but there's no reason to speculate here.) Please report back here what you find out!

Decidable sqrt function in Z3

Since disabling unsound simplification of root objects, Z3 will now fail on this simple model involving a square root:
(define-fun sqrt ((x Real)) Real (^ x 0.5))
(declare-fun y () Real)
(declare-fun x () Real)
(assert (= y (sqrt x)))
(check-sat)
This returns sat with Z3 4.4.1, but unknown with master.
If I change the problem definition to use is_sqrt as defined by Nikolaj in this question, then Z3 master will return sat. The approach using is_sqrt shows that all real roots can be pushed into QF_NRA by introducing auxiliary variables, so I think Z3 should be able to solve all problems involving roots over the reals.
How can I define a square-root function in the reals that will result in a decidable theory, assuming the rest of the model is in QF_NRA?
There is a subtle difference between (assert (= y (^ x 0.5))) and (assert (and (= x (* y y)) (> y 0.0))). The difference comes from the requirement that all functions in Z3 (and SMT-LIB) are total. This means, for example, that y=1/x, x=0 is considered satisfiable. Given that ^ is total in Z3, (assert (and (= y (^ x 0.5)) (< x 0.0))) is considered satisfiable. We can't convert (= y (^ x 0.5)) to (and (= x (* y y)) (> y 0.0)), because if x < 0 then the former is considered satisfiable but the latter is unsatisfiable. Similarly, any sqrt function defined within SMT-LIB would also be total, so we cannot define a sqrt function by any other means such that (assert (= y (sqrt x))) is equivalent to (assert (and (= x (* y y)) (> y 0.0))). In addition to the above difference as to whether or not y = sqrt(x), x < 0 (pseudocode) is considered satisfiable, it is also the case that (assert (and (= x (* y y)) (> y 0.0))) is decidable (it is in QF_NRA), while (assert (= y (^ x 0.5))) is not.
The solution for my purpose is to not use a Z3 or SMT-LIB function definition for the square-root. Instead, I will use statements of the form (assert (and (= x (* y y)) (> y 0.0))) to indicate that y is the square-root of x. Such assertions are within QF_NRA, so models built in this way will be decidable. Furthermore, this has the advantage that y = sqrt(x), x < 0 (pseudocode) will return unsat if it is represented in SMT-LIB via the statements (assert (and (= x (* y y)) (> y 0.0))) and (assert (< x 0.0)). To return unsat for this example is more in-line with my use case.

mysterious 'unknown' from Z3

I'm writing a proofchecker for a novel program logic, dealing with weak memory. Z3 does the heavy lifting: I translate all my checks into ASTs and throw them at Z3 using the ML binding. (But, see below, I've checked that Z3 online, via rise4fun, gives the same answer). Here's the implication I would like to check, pretty-printed so I can understand the operator nesting, with the names slightly simplified so it's easy to see what they are:
r1=1
=> y=1
/\ x=1
/\ xnew=x
/\ ynew=2
=> xnew=x
/\ ynew=y
\/ Exists(r1)
r1=1
=> y=1
/\ x=1
/\ xnew=x
/\ ynew=2
This translates into ASTs nicely (type declarations not shown, but see below for actual Z3 input):
(let ((a1 (and (=> (= r1 1) (and (= y 1) (= x 1)))
(= xnew x)
(= ynew 2)))
(a2 (exists ((r1 Int))
(and (=> (= r1 1) (and (= y 1) (= x 1)))
(= xnew x)
(= ynew 2)))))
(=> a1 (or
(and (= xnew x) (= ynew y)) a2)))
So that's all fine. But Z3 says 'unknown'. Oddly, this is the only one of many thousands of queries in all my tests which gives this result. So I investigated with the online version of Z3, via the rise4fun tutorial, which accepted this input
(declare-const r1 Int)
(declare-const y Int)
(declare-const x Int)
(declare-const xnew Int)
(declare-const ynew Int)
(define-fun a1 () Bool
(and (=> (= r1 1) (and (= y 1) (= x 1)))
(= xnew x)
(= ynew 2))
)
(define-fun a2 () Bool
(exists ((r1 Int))
(and (=> (= r1 1) (and (= y 1) (= x 1)))
(= xnew x)
(= ynew 2)))
)
(define-fun conjecture () Bool
(=> a1 (or (and (= xnew x) (= ynew y)) a2))
)
(assert (not conjecture))
(check-sat)
and said 'unknown'.
Am I making a simple error, or is this a bug, or what?
This seems to be a bug in the master branch and in the executable used online.
The behavior does not reproduce in the latest unstable branch.

Simplification of AC symbols in Z3: bvadd vs bvxor/bvor/etc

I wonder why Z3 is able to demonstrate some trivial equalities by applying associativity and commutativity (AC) axioms in some cases but not in others. For instance,
(simplify (= (bvadd x (bvadd y z)) (bvadd z (bvadd y x))))
reduces to true, but
(simplify (= (bvxor x (bvxor y z)) (bvxor z (bvxor y x))))
does not (Z3 just flattens bvxor applications).
I took a look at the source code (src/ast/bv_decl_plugin.cpp) and both bvadd and bvxor are declared as AC symbols. Is it related with the rewriting rules that are applied to each of those symbols? In particular, mk_bv_add (src/ast/rewriter/bv_rewriter.cpp) calls mk_add_core (src/ast/simplifier/poly_simplifier_plugin.cpp) which process a bvadd term as a sum of monomials.
Yes, it is related to the rewriting rules that are applied to the symbols.
Z3 has two expression simplifiers: src/ast/rewriter and src/ast/simplifier. The src/ast/simplifier is legacy and it is not used in new code. The simplify command in the SMT-LIB front-end is based on src/ast/rewriter. The mk_bv_add is actually using mk_add_core in src/ast/rewriter/poly_rewriter.h.
It is not hard to change the code to force Z3 to simplify the bvxor expression in your question to true. We just have to add the following line of code at src/ast/rewriter/bv_rewriter.h.
The new line is simply sorting the bvxor arguments. This is a correct rewrite for any AC operator.
br_status bv_rewriter::mk_bv_xor(unsigned num, expr * const * args, expr_ref & result) {
SASSERT(num > 0);
...
default:
// NEW LINE
std::sort(new_args.begin(), new_args.end(), ast_to_lt());
//
result = m_util.mk_bv_xor(new_args.size(), new_args.c_ptr());
return BR_DONE;
}
}
That being said, the Z3 rewriters are not supposed to apply every possible simplification and/or produce canonical normal forms. Their main goal is to produce formulas that are possibly simpler to solver. The rules grow based on demand (e.g., Z3 is too slow in example X, and the performance problem can be "fixed" by applying a new preprocessing rule), or based on user request. So, if you think this is an useful feature, we can add an option that will sort the arguments of every AC operator.
EDIT
Correction: we also have to modify the following statement
if (!merged && !flattened && (num_coeffs == 0 || (num_coeffs == 1 && !v1.is_zero() && v1 != (rational::power_of_two(sz) - numeral(1))))
return BR_FAILED;
This statement interrupts the execution of mk_bv_xor, when none of the existing rewriting rules are applicable. We also have to modify it. I implemented these modifications here. We can activate them by using the new option :bv-sort-ac. This option is not enabled by default. The new option is available in the unstable (work-in-progress) branch. When set to true, it will sort bit-vector AC operators.
Note that, the unstable branch uses the new parameter setting framework that will be available in the next official release. Here are instructions on how to build the unstable branch.
These modifications will also be available this week on the nightly builds.
Here are some examples that use the new option:
(declare-const a (_ BitVec 8))
(declare-const b (_ BitVec 8))
(declare-const c (_ BitVec 8))
(declare-const d (_ BitVec 8))
(simplify (= (bvxor a b c) (bvxor b a c)))
(simplify (= (bvxor a b c) (bvxor b a c)) :bv-sort-ac true)
(simplify (= (bvxor a (bvxor b c)) (bvxor b a c)) :bv-sort-ac true)
(simplify (= (bvor a b c) (bvor b a c)))
(simplify (= (bvor a b c) (bvor b a c)) :bv-sort-ac true)
(simplify (= (bvor a (bvor b c)) (bvor b a c)) :bv-sort-ac true)
(simplify (= (bvand a b c) (bvand b a c)))
(simplify (= (bvand a b c) (bvand b a c)) :bv-sort-ac true)
(simplify (= (bvand a (bvand b c)) (bvand b a c)) :bv-sort-ac true)
; In the new parameter setting framework, each module has its own parameters.
; The bv-sort-ac is a parameter of the "rewriter" framework.
(set-option :rewriter.bv-sort-ac true)
; Now, Z3 will rewrite the following formula to true even when we do not provide
; the option :bv-sort-ac true
(simplify (= (bvand a b c) (bvand b a c)))
; It will also simplify the following assertion.
(assert (= (bvand a b c) (bvand b a c)))
(check-sat)
END EDIT

Distinct values in Z3

I wanted to ask a question about assigning distinct values in Z3.
suppose that I have 6 variables A,B,C,D,E,F.
Now, I would like to assign distinct values to some of them and some of them will be zero. How many variables will be distinct and how many variables are zero is unknown before-hand. It depends on certain other conditions.
generally for all the variables I shall write
(assert (distinct A B C D E F))
However is it possible to write constraint such as A, B, D are zero and others are distinct? Again remember that A,B, D are just example variables. They can change dynamically depending on the constraints.
Thanks !
As far as I understand, you have a set V of variables v1, ..., vn, where each variable vi is either zero or different from all other variables vj, j ≠ i.
As an example, let V = {a, b, c, d}.
(declare-const a Int)
(declare-const b Int)
(declare-const c Int)
(declare-const d Int)
You could encode your constraints as
(assert (or (= a 0) (not (or (= a b) (= a c) (= a d)))))
(assert (or (= b 0) (not (or (= b a) (= b c) (= b d)))))
(assert (or (= c 0) (not (or (= c a) (= c b) (= c d)))))
(assert (or (= d 0) (not (or (= d a) (= d b) (= d c)))))
Adding two constraints and querying Z3 for a model
(assert (= a 0))
(assert (not (= b 0)))
(check-sat)
(get-model)
then yields
sat
(model
(define-fun b () Int
(- 2))
(define-fun c () Int
(- 1))
(define-fun d () Int
0)
(define-fun a () Int
0)
)
Since generating the "zero or distinct" constraints is a tedious task you'd probably want to work with a Z3 front-end such as PyZ3 or Scala^Z3.

Resources