How use forall in bit-vector - z3

I intend to do is go all the vector, and do different things on each bit.
What I want to know is if I'm doing well forall (in define-fun LT....)?
Because the results do not match the expected: S
Code found in this link: http://rise4fun.com/Z3/xrFK

I'm not sure what the intended semantics of your formula is, but intuitively it seems that your definition of is_in may be the culprit:
(define-fun is_in ((e (_ BitVec 9)) (S (_ BitVec 9))) Bool
;; True if e is an element of the "set" S.
(= (bvand e S) e))
The constraint (= (bvand e S) e)) means that this function can only return true when S is equal to e. Going by the name of the function, I would expect the definition to be (not (= (bvand e S) Empty)).

Related

defining list concat in smtlib

I defined my own version of list concat based on Haskell as below:
(declare-datatypes ((MyList 1))
((par (T) ((cons (head T) (tail (MyList T))) (nil)))))
(declare-fun my-concat ( (MyList T1) (MyList T1) ) (MyList T1))
(assert (forall ((xs (MyList T1)) (ys (MyList T1)) (x T1))
(ite (= (as nil (MyList T1)) xs)
(= (my-concat xs ys) ys)
(= (my-concat (cons x xs) ys) (cons x (my-concat xs ys))))))
I am wondering why z3 is not able to reason about the following?
(assert (not (= (my-concat (cons 4 (as nil (MyList Int))) (as nil (MyList Int)))
(cons 4 (as nil (MyList Int))))))
(check-sat) ; runs forever
Loading your file
If I load your program to z3 as you've given it, it says:
(error "line 3 column 33: Parsing function declaration. Expecting sort list '(': unknown sort 'T1'")
(error "line 4 column 29: invalid sorted variables: unknown sort 'T1'")
(error "line 8 column 79: unknown function/constant my-concat")
That's because you cannot define "polymorphic" functions in SMTLib. At the user-level, only fully monomorphic functions are allowed. (Though internally SMTLib does provide polymorphic constants, there's no way for the user to actually create any polymorphic constants.)
So, I'm not sure how you got to load that file.
Monomorphisation
Looks like you only care about integer-lists anyhow, so let's just modify our program to work on lists of integers. This process is called monomorphisation, and is usually automatically done by some front-end tool before you even get to the SMT-solver, depending on what framework you're working in. That's just a fancy way of saying create instances of all your polymorphic constants at the mono-types they are used. (Since you mentioned Haskell, I'll just throw in that while monomorphisation is usually possible, it's not always feasible: There might be way too many variants to generate making it impractical. Also, if you have polymoprhic-recursion then monomorphisation doesn't work. But that's a digression for the time being.)
If I monomorphise your program to Int's only, I get:
(declare-datatypes ((MyList 1))
((par (T) ((cons (head T) (tail (MyList T))) (nil)))))
(declare-fun my-concat ( (MyList Int) (MyList Int) ) (MyList Int))
(assert (forall ((xs (MyList Int)) (ys (MyList Int)) (x Int))
(ite (= (as nil (MyList Int)) xs)
(= (my-concat xs ys) ys)
(= (my-concat (cons x xs) ys) (cons x (my-concat xs ys))))))
(assert (not (= (my-concat (cons 4 (as nil (MyList Int))) (as nil (MyList Int)))
(cons 4 (as nil (MyList Int))))))
(check-sat)
(get-info :reason-unknown)
When I run z3 on this, I get:
unknown
(:reason-unknown "smt tactic failed to show goal to be sat/unsat (incomplete quantifiers)")
So, it doesn't loop at all like you mentioned; but your file didn't really load in the first place. So maybe you were working with some other contents in the file as well, but that's a digression for the current question.
But it still doesn't prove it!
Of course, you wanted unsat for this trivial formula! But z3 said it's too hard for it to deal with. The reason-unknown is "incomplete quantifiers." What does that mean?
In short, SMTLib is essentially logic of many-sorted first order formulas. Solvers are "complete" for the quantifier-free fragment of this logic. But adding quantifiers makes the logic semi-decidable. What that means is that if you give enough resources, and if smart heuristics are in play, the solver will eventually say sat for a satisifiable formula, but it may loop forever if given an unsat one. (It might get lucky and say unsat as well, but most likely it'll loop.)
There are very good reasons why above is the case, but keep in mind that this has nothing to do with z3: First-order logic with quantifiers is semi-decidable. (Here's a good place to start reading: https://en.wikipedia.org/wiki/Decidability_(logic)#Semidecidability)
What usually happens in practice, however, is that the solver will not even answer sat, and will simply give up and say unknown as z3 did above. Quantifiers are simply beyond the means of SMT-solvers in general. You can try using patterns (search stack-overflow for quantifiers and pattern triggers), but that usually is futile as patterns and triggers are tricky to work with and they can be quite brittle.
What course of action do you have:
Honestly, this is one of those cases where "give up" is good advice. An SMT solver is just not a good fit for this sort of a problem. Use a theorem-prover like Isabelle, ACL2, Coq, HOL, HOL-Light, Lean, ... where you can express quantifiers and recursive functions and reason with them. They are built for this sort of thing. Don't expect your SMT solver to handle these sorts of queries. It's just not the right match.
Still, is there anything I can do?
You can try SMTLib's recursive-function definition facilities. You'd write:
(declare-datatypes ((MyList 1))
((par (T) ((cons (head T) (tail (MyList T))) (nil)))))
(define-fun-rec my-concat ((xs (MyList Int)) (ys (MyList Int))) (MyList Int)
(ite (= (as nil (MyList Int)) xs)
ys
(cons (head xs) (my-concat (tail xs) ys))))
(assert (not (= (my-concat (cons 4 (as nil (MyList Int))) (as nil (MyList Int)))
(cons 4 (as nil (MyList Int))))))
(check-sat)
Note the define-fun-rec construct, which allows for recursive definitions.
And voila, we get:
unsat
But this does not mean z3 will be able to prove arbitrary theorems regarding this concat function. If you try anything that requires induction, it'll either give up (saying unknown) or loop-forever. Of course, as the capabilities improve some induction-like proofs might be possible in z3 or other SMT-solvers, but that's really beyond what they're designed for. So, use with caution.
You can read more about recursive definitions in Section 4.2.3 of http://smtlib.cs.uiowa.edu/papers/smt-lib-reference-v2.6-r2017-07-18.pdf
To sum up
Do not use an SMT-solver for reasoning with quantified or recursive definitions. They just don't have the necessary power to deal with such problems, and it's unlikely they'll ever get there. Use a proper theorem prover for these tasks. Note that most theorem-provers use SMT-solvers as underlying tactics, so you get the best of both worlds: You can do some manual work to guide the inductive proof, and have the prover use an SMT-solver to handle most of the goals automatically for you. Here's a good paper to read to get you started on the details: https://people.mpi-inf.mpg.de/~jblanche/jar-smt.pdf
alias makes valid points, but I don't completely agree with the final conclusion to " not use an SMT-solver for reasoning with quantified or recursive definitions". In the end, it depends on which kind of properties you need to reason about, what kind of answers you need (is unsat/unknown OK, or do you need unsat/sat and a model?), and how much work you're willing to invest :-)
For example, SMT-based program verifiers such as Dafny and Viper quickly verify the following assertions about lists:
assert [4] + [] == [4]; // holds
assert [4,1] + [1,4] == [4,1,1,4]; // holds
assert [4] + [1] == [1,4]; // fails
Both tools can be used online, but the websites are rather slow and not terribly reliable. You can find the Dafny example here and the Viper example here.
Here is the relevant SMT code that Viper generates:
(set-option :auto_config false) ; Usually a good idea
(set-option :smt.mbqi false)
;; The following definitions are an excerpt of Viper's sequence axiomatisation,
;; which is based on Dafny's sequence axiomatisation.
;; See also:
;; https://github.com/dafny-lang/dafny
;; http://viper.ethz.ch
(declare-sort Seq<Int>) ;; Monomorphised sort of integer sequences
(declare-const Seq_empty Seq<Int>)
(declare-fun Seq_length (Seq<Int>) Int)
(declare-fun Seq_singleton (Int) Seq<Int>)
(declare-fun Seq_index (Seq<Int> Int) Int)
(declare-fun Seq_append (Seq<Int> Seq<Int>) Seq<Int>)
(declare-fun Seq_equal (Seq<Int> Seq<Int>) Bool)
(assert (forall ((s Seq<Int>)) (!
(<= 0 (Seq_length s))
:pattern ((Seq_length s))
)))
(assert (= (Seq_length (as Seq_empty Seq<Int>)) 0))
(assert (forall ((s1 Seq<Int>) (s2 Seq<Int>)) (!
(implies
(and
(not (= s1 (as Seq_empty Seq<Int>)))
(not (= s2 (as Seq_empty Seq<Int>))))
(= (Seq_length (Seq_append s1 s2)) (+ (Seq_length s1) (Seq_length s2))))
:pattern ((Seq_length (Seq_append s1 s2)))
)))
(assert (forall ((s Seq<Int>)) (!
(= (Seq_append (as Seq_empty Seq<Int>) s) s)
:pattern ((Seq_append (as Seq_empty Seq<Int>) s))
)))
(assert (forall ((s Seq<Int>)) (!
(= (Seq_append s (as Seq_empty Seq<Int>)) s)
:pattern ((Seq_append s (as Seq_empty Seq<Int>)))
)))
(assert (forall ((s1 Seq<Int>) (s2 Seq<Int>) (i Int)) (!
(implies
(and
(not (= s1 (as Seq_empty Seq<Int>)))
(not (= s2 (as Seq_empty Seq<Int>))))
(ite
(< i (Seq_length s1))
(= (Seq_index (Seq_append s1 s2) i) (Seq_index s1 i))
(= (Seq_index (Seq_append s1 s2) i) (Seq_index s2 (- i (Seq_length s1))))))
:pattern ((Seq_index (Seq_append s1 s2) i))
:pattern ((Seq_index s1 i) (Seq_append s1 s2))
)))
(assert (forall ((s1 Seq<Int>) (s2 Seq<Int>)) (!
(=
(Seq_equal s1 s2)
(and
(= (Seq_length s1) (Seq_length s2))
(forall ((i Int)) (!
(implies
(and (<= 0 i) (< i (Seq_length s1)))
(= (Seq_index s1 i) (Seq_index s2 i)))
:pattern ((Seq_index s1 i))
:pattern ((Seq_index s2 i))
))))
:pattern ((Seq_equal s1 s2))
)))
(assert (forall ((s1 Seq<Int>) (s2 Seq<Int>)) (!
(implies (Seq_equal s1 s2) (= s1 s2))
:pattern ((Seq_equal s1 s2))
)))
; ------------------------------------------------------------
; assert Seq(4) ++ Seq[Int]() == Seq(4)
(push)
(assert (not
(Seq_equal
(Seq_append (Seq_singleton 4) Seq_empty)
(Seq_singleton 4))))
(check-sat) ; unsat -- good!
(pop)
; assert Seq(4, 1) ++ Seq(1, 4) == Seq(4, 1, 1, 4)
(push)
(assert (not
(Seq_equal
(Seq_append
(Seq_append (Seq_singleton 4) (Seq_singleton 1))
(Seq_append (Seq_singleton 1) (Seq_singleton 4)))
(Seq_append
(Seq_append
(Seq_append (Seq_singleton 4) (Seq_singleton 1))
(Seq_singleton 1))
(Seq_singleton 4)))))
(check-sat) ; unsat -- good!
(pop)
; assert Seq(4) ++ Seq(1) == Seq(1, 4)
(push)
(assert (not (Seq_equal
(Seq_append (Seq_singleton 4) (Seq_singleton 1))
(Seq_append (Seq_singleton 1) (Seq_singleton 4)))))
(check-sat) ; unknown -- OK, since property doesn't hold
(pop)

Z3 Find Valid Permutation

I'm attempting to get Z3 to find all possible permutations of a sequence of fixed size that satisfy some constraints. However, I've run into a timeout error with the code I've developed so far:
(set-option :produce-unsat-cores true)
(set-option :produce-models true)
; --------------- Basic Definitions -------------------
(declare-datatypes () ((Obj A B C)))
; --------------- Predicates -------------------------------
(define-sort MyList () (Seq Obj))
(define-fun in_list ((o Obj) (l MyList)) Bool (seq.contains l (seq.unit o)))
(define-fun permutation ((l1 MyList) (l2 MyList)) Bool
(forall ((o Obj)) (= (in_list o l1) (in_list o l2))))
; Two difference permutations of the same list
(declare-const l0 MyList)
(declare-const l1 MyList)
(assert (= 2 (seq.len l0)))
(assert (= 2 (seq.len l1)))
(assert (not (= l1 l0)))
(assert (permutation l0 l1))
; --------------- Verify -------------------
(check-sat)
(get-model)
It seems like this should be a pretty trivial solve (even a brute force should take milliseconds), so I'm pretty baffled what is causing the timeout. Any help?
You're running into limits of what Z3 can do when quantifiers are present.
You might want to look at this question: Defining a Theory of Sets with Z3/SMT-LIB2
In this case, the question is about general set operations, but I think you'll find the answers to apply to your case as well. (In short, disable MBQI, and see if you can use functions instead of sequences.)

List "contains" function in Z3

Given a typical list datatype:
(declare-datatypes (X)(
(Lst
(nil)
(insert
(head X)
(tail Lst)))))
I'm trying to create a function that returns whether a given element is in a given list. It looks like I have to use an uninterpreted function. I've tried different approaches without success. For example:
(declare-fun is-in-lst ((Int)(Lst Int)) Bool)
(assert (forall((elem Int)(lst (Lst Int)))
(=
(is-in-lst elem lst)
(and
(is-insert lst)
(or
(= elem (head lst))
(is-in-lst elem (tail lst)))))))
(declare-const l1 (Lst Int))
(assert (is-in-lst 6 l1))
(assert (is-in-lst 5 l1))
Is this doable in Z3? If so, what would be the best way to address the problem?
Thank you
The latest SMTLib standard allows for recursive definitions. Also List is predefined in Z3, so you don't need to define it yourself. You can code your function as:
(define-fun-rec elem ((e Int) (l (List Int))) Bool
(ite (= l nil) false (or (= e (head l)) (elem e (tail l)))))
(declare-const l1 (List Int))
(assert (elem 6 l1))
(assert (elem 5 l1))
(check-sat)
(get-value (l1))
For this, z3 responds:
sat
((l1 (let ((a!1 (insert 6 (insert 4 (insert 7 (insert 5 nil))))))
(insert 0 (insert 1 (insert 3 (insert 2 a!1)))))))
You might have to get a recent version of Z3 from their nightly builds, as support for recursive-functions is rather new and thus the latest official release (4.6.0) may or may not work with this. (You can get nightly builds from https://github.com/Z3Prover/bin/tree/master/nightly.)

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

a datatype contains a set in Z3

how can I make a datatype that contains a set of another objects. Basically, I am doing the following code:
(define-sort Set(T) (Array Int T))
(declare-datatypes () ((A f1 (cons (value Int) (b (Set B))))
(B f2 (cons (id Int) (a (Set A))))
))
But Z3 tells me unknown sort for A and B. If I remove "Set" it works just as the guide states.
I was trying to use List instead but it does not work. Anyone knows how to make it work?
You are addressing a question that comes up on a regular basis:
how can I mix data-types and arrays (as sets, multi-sets or
data-types in the range)?
As stated above Z3 does not support mixing data-types
and arrays in a single declaration.
A solution is to develop a custom solver for the
mixed datatype + array theory. Z3 contains programmatic
APIs for developing custom solvers.
It is still useful to develop this example
to illustrate the capabilities and limitations
of encoding theories with quantifiers and triggers.
Let me simplify your example by just using A.
As a work-around you can define an auxiliary sort.
The workaround is not ideal, though. It illustrates some
axiom 'hacking'. It relies on the operational semantics
of how quantifiers are instantiated during search.
(set-option :model true) ; We are going to display models.
(set-option :auto-config false)
(set-option :mbqi false) ; Model-based quantifier instantiation is too powerful here
(declare-sort SetA) ; Declare a custom fresh sort SetA
(declare-datatypes () ((A f1 (cons (value Int) (a SetA)))))
(define-sort Set (T) (Array T Bool))
Then define bijections between (Set A), SetA.
(declare-fun injSA ((Set A)) SetA)
(declare-fun projSA (SetA) (Set A))
(assert (forall ((x SetA)) (= (injSA (projSA x)) x)))
(assert (forall ((x (Set A))) (= (projSA (injSA x)) x)))
This is almost what the data-type declaration states.
To enforce well-foundedness you can associate an ordinal with members of A
and enforce that members of SetA are smaller in the well-founded ordering:
(declare-const v Int)
(declare-const s1 SetA)
(declare-const a1 A)
(declare-const sa1 (Set A))
(declare-const s2 SetA)
(declare-const a2 A)
(declare-const sa2 (Set A))
With the axioms so far, a1 can be a member of itself.
(push)
(assert (select sa1 a1))
(assert (= s1 (injSA sa1)))
(assert (= a1 (cons v s1)))
(check-sat)
(get-model)
(pop)
We now associate an ordinal number with the members of A.
(declare-fun ord (A) Int)
(assert (forall ((x SetA) (v Int) (a A))
(=> (select (projSA x) a)
(> (ord (cons v x)) (ord a)))))
(assert (forall ((x A)) (> (ord x) 0)))
By default quantifier instantiation in Z3 is pattern-based.
The first quantified assert above will not be instantiated on all
relevant instances. One can instead assert:
(assert (forall ((x1 SetA) (x2 (Set A)) (v Int) (a A))
(! (=> (and (= (projSA x1) x2) (select x2 a))
(> (ord (cons v x1)) (ord a)))
:pattern ((select x2 a) (cons v x1)))))
Axioms like these, that use two patterns (called a multi-pattern)
are quite expensive. They produce instantiations for every pair
of (select x2 a) and (cons v x1)
The membership constraint from before is now unsatisfiable.
(push)
(assert (select sa1 a1))
(assert (= s1 (injSA sa1)))
(assert (= a1 (cons v s1)))
(check-sat)
(pop)
but models are not necessarily well formed yet.
the default value of the set is 'true', which
would mean that the model implies there is a membership cycle
when there isn't one.
(push)
(assert (not (= (cons v s1) a1)))
(assert (= (projSA s1) sa1))
(assert (select sa1 a1))
(check-sat)
(get-model)
(pop)
We can approximate more faithful models by using
the following approach to enforce that sets that are
used in data-types are finite.
For example, whenever there is a membership check on a set x2,
we enforce that the 'default' value of the set is 'false'.
(assert (forall ((x2 (Set A)) (a A))
(! (not (default x2))
:pattern ((select x2 a)))))
Alternatively, whenever a set occurs in a data-type constructor
it is finite
(assert (forall ((v Int) (x1 SetA))
(! (not (default (projSA x1)))
:pattern ((cons v x1)))))
(push)
(assert (not (= (cons v s1) a1)))
(assert (= (projSA s1) sa1))
(assert (select sa1 a1))
(check-sat)
(get-model)
(pop)
Throughout the inclusion of additional axioms,
Z3 produces the answer 'unknown' and furthermore
the model that is produced indicates that the domain SetA
is finite (a singleton). So while we could patch the defaults
this model still does not satisfy the axioms. It satisfies
the axioms modulo instantiation only.
This is not supported in Z3. You can use arrays in datatype declarations, but they can't contain "references" to the datatypes you are declaring. For example, it is ok to use (Set Int).

Resources