How to substitute a function body in z3py? - z3

I have a z3 function, e.g., zero = [else → Epoch!val!2], where Epoch!val!2 is a constant of sort Epoch. I want to get a new function zero(m) = [else -> Epoch!val!2(m)], where I'm basically introducing an argument m as the first parameter in every function in the body.
In SMT-Lib speak, I want to go from the following:
(declare-sort Epoch)
(declare-fun Epoch!val!2 () Epoch)
(define-fun zero () Epoch
(Epoch!val!2)
)
to the following:
(declare-sort M)
(declare-sort Epoch)
(declare-fun Epoch!val!2 (M) Epoch)
(define-fun zero ((m M)) Epoch
(Epoch!val!2 m)
)
I tried to use the substitute function, but that only accepts z3 expressions, whereas I have zero from the model, i.e., as a FuncInterp instance. I'm not sure how to convert that into an expression, and couldn't find docs for that.
What should I do?
Edit: Adding more context.
I'm trying to write a CEGIS tool with SAT-based-synthesis (as opposed to enumerated synthesis), but here counter examples are entire models (including uninterpreted functions) rather than simpler entities. So, for example, the generated counter example might be:
(declare-sort Epoch)
(declare-fun Epoch!Val!0 () Epoch)
(declare-fun Epoch!Val!1 () Epoch)
(declare-fun Epoch!Val!2 () Epoch)
(define-fun le ((e1 Epoch) (e2 Epoch)) Bool
(ite (and (= x!0 Epoch!val!0) (= x!1 Epoch!val!1))
false
(ite (and (= x!0 Epoch!val!0) (= x!1 Epoch!val!2))
false
(ite (and (= x!0 Epoch!val!2) (= x!1 Epoch!val!1))
false
true))))
As you can see, the model declares its universe, and gives the definition of le.
Since this is CEGIS, I will have multiple such models (counter examples). Since this is SAT based synthesis, I am encoding these counter examples into a SAT (well, SMT) query, and asking the solver to synthesize my function. Say, I know the template of the function I'm synthesizing, and it is either le(Epoch, Epoch) or not(le(Epoch, Epoch)). Then I will encode this as an SMT query like below:
<declarations>
(declare-const rev_ord Bool)
(declare-const to_not Bool)
(define-fun inv ((e1 Epoch) (e2 Epoch)) Bool
(ite (and (not rev_ord) (not to_not))
(le e1 e2)
(ite (and (not rev_ord) to_not)
(not (le e1 e2))
(ite (and rev_ord (not to_not))
(le e2 e1)
(ite (and rev_ord to_not)
(not (le e2 e1))
)))))
<constraints??>
(check-sat)
(get-value rev_ord to_not)
Here, encoding the constraints is the tricky part. If I have two counter examples, I want two constraints, each asserting that the synthesized function satisfies the counter example model. To encode the constraints, I've decided that the best approach ahead is to write the SAT query like this:
(declare-sort ModelId)
(declare-sort Epoch)
(declare-fun M0 () ModelId)
(declare-fun M1 () ModelId)
(declare-fun Epoch!val!1 (ModelId) Epoch)
(declare-fun Epoch!val!2 (ModelId) Epoch)
(declare-fun Epoch!val!0 (ModelId) Epoch)
(declare-const rev_ord Bool)
(declare-const to_not Bool)
;;
;; Notice the addition of the model id
;;
(define-fun inv ((m ModelId) (e1 Epoch) (e2 Epoch)) Bool
(ite (and (not rev_ord) (not to_not))
(le m e1 e2)
(ite (and (not rev_ord) to_not)
(not (le m e1 e2))
(ite (and rev_ord (not to_not))
(le m e2 e1)
(ite (and rev_ord to_not)
(not (le m e2 e1))
)))))
; counter example 1.
; Format: (and <model_description> <function_requirements>)
(assert (and
(and
(= (le (Epoch!val!0 M0) (Epoch!val!1 M0)) false)
(= (le (Epoch!val!0 M0) (Epoch!val!2 M0)) false)
(= (le (Epoch!val!2 M0) (Epoch!val!1 M0)) true)
)
(and
(inv M0 (Epoch!val!0 M0) (Epoch!val!1 M0))
(inv M0 (Epoch!val!0 M0) (Epoch!val!2 M0))
(inv M0 (Epoch!val!2 M0) (Epoch!val!1 M0))
)
))
; similarly, counter example 2
(check-sat)
(get-value rev_ord to_not)
To summarize: I get counter example models, which I want to feed into a synthesis query, but I want to tag the model's uninterpreted functions and constants with that model's "ID", so that the synthesis engine doesn't give me meaningless functions.

Related

Z3 returns model not available

If possible I'd like a second opinion on my code.
The constraints of the problem are:
a,b,c,d,e,f are non-zero integers
s1 = [a,b,c] and s2 = [d,e,f] are sets
The sum s1_i + s2_j for i,j = 0..2 has to be a perfect square
I don't understand why but my code returns model not available. Moreover, when commenting out the following lines:
(assert (and (> sqrtx4 1) (= x4 (* sqrtx4 sqrtx4))))
(assert (and (> sqrtx5 1) (= x5 (* sqrtx5 sqrtx5))))
(assert (and (> sqrtx6 1) (= x6 (* sqrtx6 sqrtx6))))
(assert (and (> sqrtx7 1) (= x7 (* sqrtx7 sqrtx7))))
(assert (and (> sqrtx8 1) (= x8 (* sqrtx8 sqrtx8))))
(assert (and (> sqrtx9 1) (= x9 (* sqrtx9 sqrtx9))))
The values for d, e, f are negative. There is no constraint that requires them to do so. I'm wondering if perhaps there are some hidden constraints that sneaked in and mess up the model.
A valid expected solution would be:
a = 3
b = 168
c = 483
d = 1
e = 193
f = 673
Edit: inserting (assert (= a 3)) and (assert (= b 168)) results in the solver finding the correct values. This only puzzles me further.
Full code:
(declare-fun sqrtx1 () Int)
(declare-fun sqrtx2 () Int)
(declare-fun sqrtx3 () Int)
(declare-fun sqrtx4 () Int)
(declare-fun sqrtx5 () Int)
(declare-fun sqrtx6 () Int)
(declare-fun sqrtx7 () Int)
(declare-fun sqrtx8 () Int)
(declare-fun sqrtx9 () Int)
(declare-fun a () Int)
(declare-fun b () Int)
(declare-fun c () Int)
(declare-fun d () Int)
(declare-fun e () Int)
(declare-fun f () Int)
(declare-fun x1 () Int)
(declare-fun x2 () Int)
(declare-fun x3 () Int)
(declare-fun x4 () Int)
(declare-fun x5 () Int)
(declare-fun x6 () Int)
(declare-fun x7 () Int)
(declare-fun x8 () Int)
(declare-fun x9 () Int)
;all numbers are non-zero integers
(assert (not (= a 0)))
(assert (not (= b 0)))
(assert (not (= c 0)))
(assert (not (= d 0)))
(assert (not (= e 0)))
(assert (not (= f 0)))
;both arrays need to be sets
(assert (not (= a b)))
(assert (not (= a c)))
(assert (not (= b c)))
(assert (not (= d e)))
(assert (not (= d f)))
(assert (not (= e f)))
(assert (and (> sqrtx1 1) (= x1 (* sqrtx1 sqrtx1))))
(assert (and (> sqrtx2 1) (= x2 (* sqrtx2 sqrtx2))))
(assert (and (> sqrtx3 1) (= x3 (* sqrtx3 sqrtx3))))
(assert (and (> sqrtx4 1) (= x4 (* sqrtx4 sqrtx4))))
(assert (and (> sqrtx5 1) (= x5 (* sqrtx5 sqrtx5))))
(assert (and (> sqrtx6 1) (= x6 (* sqrtx6 sqrtx6))))
(assert (and (> sqrtx7 1) (= x7 (* sqrtx7 sqrtx7))))
(assert (and (> sqrtx8 1) (= x8 (* sqrtx8 sqrtx8))))
(assert (and (> sqrtx9 1) (= x9 (* sqrtx9 sqrtx9))))
;all combinations of sums need to be squared
(assert (= (+ a d) x1))
(assert (= (+ a e) x2))
(assert (= (+ a f) x3))
(assert (= (+ b d) x4))
(assert (= (+ b e) x5))
(assert (= (+ b f) x6))
(assert (= (+ c d) x7))
(assert (= (+ c e) x8))
(assert (= (+ c f) x9))
(check-sat-using (then simplify solve-eqs smt))
(get-model)
(get-value (a))
(get-value (b))
(get-value (c))
(get-value (d))
(get-value (e))
(get-value (f))
Nonlinear integer arithmetic is undecidable. This means that there is no decision procedure that can decide arbitrary non-linear integer constraints to be satisfiable. This is what z3 is telling you when it says "unknown" as the answer your query.
This, of course, does not mean that individual cases cannot be answered. Z3 has certain tactics it applies to solve such formulas, but it is inherently limited in what it can handle. Your problem falls into that category: One that Z3 is just not capable of solving.
Z3 has a dedicated NRA (non-linear real arithmetic) tactic that you can utilize. It essentially treats all variables as reals, solves the problem (nonlinear real arithmetic is decidable and z3 can find all algebraic real solutions), and then checks if the results are actually integer. If not, it tries another solution over the reals. Sometimes this tactic can handle non-linear integer problems, if you happen to hit the right solution. You can trigger it using:
(check-sat-using qfnra)
Unfortunately it doesn't solve your particular problem in the time I allowed it to run. (More than 10 minutes.) It's unlikely it'll ever hit the right solution.
You really don't have many options here. SMT solvers are just not a good fit for nonlinear integer problems. In fact, as I alluded to above, there is no tool that can handle arbitrary nonlinear integer problems due to undecidability; but some tools fare better than others depending on the algorithms they use.
When you tell z3 what a and b are, you are essentially taking away much of the non-linearity, and the rest becomes easy to handle. It is possible that you can find a sequence of tactics to apply that solves your original, but such tricks are very brittle in practice and not easily discovered; as you are essentially introducing heuristics into the search and you don't have much control over how that behaves.
Side note: Your script can be improved slightly. To express that a bunch of numbers are all different, use the distinct predicate:
(assert (distinct (a b c)))
(assert (distinct (d e f)))

Defining functions on enumeration types in Z3

I have an enumeration type BoolT that contains Bool and Bot
(declare-datatypes () ((BoolT Bool Bot)))
and I want to define an equality function eq that given two BoolT returns Bot if one of the arguments is Bot, otherwise the equality between the two Bool arguments. However, I am not able to define the actual comparison between the boolean values. Until now I get the function
(define-fun eq ((x1 BoolT) (x2 BoolT)) BoolT
(ite (or (= x1 Bot) (= x2 Bot)) Bot Bool))
while I need something like
(define-fun eq ((x1 BoolT) (x2 BoolT)) BoolT
(ite (or (= x1 Bot) (= x2 Bot)) Bot
(or (and (= x1 true)(= x2 true)) (and (= x1 false)(= x2 false)))
)
or at least a correct definition of the following predicate
(define-fun is-True ((x1 BoolT)) Bool
(ite (= x1 true) true false)
)
Is there a way to model the eq function or the previous predicate on BoolT?
You will need tagged unions in Z3, similar to ML data-types. You can't just take the union of an existing type and included it in a datatype declaration.
So you will need to write something like:
(declare-datatypes () ((BoolT True False Bot)))
or
(declare-datatypes () ((BoolT (mkB (tf B)) Bot) (B True False)))
Then you can write:
(define-fun is-True1 ((x BoolT)) Bool
(= (mkB True) x))
or
(define-fun is-True2 ((x BoolT)) Bool
(and (is-mkB x) (= True (tf x))))
and assert
(declare-const a BoolT)
(assert (is-True2 a))
Declaring (B True False)
automatically declares predicates is-True and is-False
(declare-const b B)
(assert (is-True b))

How to compute with Inductive datatypes using Yices and Z3-SMT-LIB

A simple example of computation with inductive datatypes using Yices is:
(define-type T (datatype c1
c2
(c3 val::bool)))
(define x1::T)
(define x2::T)
(assert (/= x1 x2))
(check)
and the corresponding output is:
sat
(= x1 c1)
(= (c3 false) x2)
This example is solved using Z3-SMT-LIB using the following code
(declare-datatypes () ((T c1 ( c3 (T Bool)))))
(declare-fun x1 () T)
(declare-fun x2 () T)
(assert (not (= x2 x1)))
(check-sat)
(get-model)
and the corresponding output is
sat
(model
(define-fun x2 () T (c3 false))
(define-fun x1 () T c1)
)
Run this example online here
As it is observed Yices and Z3 produce the same results.
Other example:
Yices:
(define-type T (datatype c1
c2
(c3 val::bool)))
(define x1::T)
(define x2::T)
(define x3::T)
(define x4::T)
(assert (/= x1 x2))
(assert (/= x1 x3))
(assert (/= x1 x4))
(assert (/= x2 x3))
(assert (/= x2 x4))
(assert (/= x3 x4))
(check)
sat
(= x1 c1)
(= x3 c2)
(= (c3 false) x4)
(= (c3 true) x2)
Z3:
(declare-datatypes () ((T c1 c2 ( c3 (T Bool)))))
(declare-fun x1 () T)
(declare-fun x2 () T)
(declare-fun x3 () T)
(declare-fun x4 () T)
(assert (not (= x4 x3)))
(assert (not (= x4 x2)))
(assert (not (= x4 x1)))
(assert (not (= x3 x2)))
(assert (not (= x3 x1)))
(assert (not (= x2 x1)))
(check-sat)
(get-model)
sat
(model
(define-fun x3 () T c2)
(define-fun x2 () T (c3 false))
(define-fun x1 () T c1)
(define-fun x4 () T (c3 true))
)
Run this example online here
As it is observed in this example Yices and Z3 produce different results.
Other example: Natural numbers as an inductive type:
Yices
(define-type Nat (datatype zero
(succ val::Nat)))
(define x1::Nat)
(define x2::Nat)
(define x3::Nat)
(assert (/= x1 x2))
(assert (/= x1 x3))
(assert (/= x2 x3))
(check)
sat
(= zero x1)
(= (succ x2) x3)
(= (succ x1) x2)
Z3
(declare-datatypes () ((Nat zero (succ (Nat Nat)))))
(declare-fun x1 () Nat)
(declare-fun x2 () Nat)
(declare-fun x3 () Nat)
(assert (not (= x1 x2)))
(assert (not (= x1 x3)))
(assert (not (= x2 x3)))
(check-sat)
(get-model)
sat
(model
(define-fun x3 () Nat (succ (succ (succ zero))))
(define-fun x2 () Nat (succ zero))
(define-fun x1 () Nat zero)
)
Run this example online here
As it is observed in this example Yices and Z3 produce different results.
The questions are;
How to write the Z3 code with the aim to obtain the same results that are obtained with Yices.
How to obtain all possible models using both Z3 and Yices.
Your example has an infinite number of models.
Z3 and Yices produce different models, but the solutions produced by both of them are correct.
Z3 and Yices use slightly different decision procedures for inductive datatypes. This is why they produce different models. There is no way to force them to always produce the same solution for an input set of assertions that has more than one model.
Regarding enumerating all possible models, we can use the Z3 API. See this post:
Z3: finding all satisfying models

Can options change `sat` into `unsat`?

Another question from a Z3 newbie. Can options change the behavior of Z3? I might expect them to affect termination, or change sat or unsat into unknown but not sat into unsat or vice versa.
This example:
(set-option :smt.macro-finder true)
(declare-datatypes () ((Option (none) (some (Data Int)))))
(define-sort Set () (Array Option Option))
(declare-fun filter1 (Option) Option)
(declare-fun filter2 (Option) Option)
(declare-var s1 Set)
(declare-var s2 Set)
(declare-var x1 Option)
(declare-var x2 Option)
(declare-var x3 Option)
(declare-var x4 Option)
(assert (not (= x1 none)))
(assert (not (= x2 none)))
(assert (not (= x3 none)))
(assert (not (= x4 none)))
(assert (= (select s1 x1) x2))
(assert (= (select s2 x3) x4))
(assert (forall ((x Option)) (= (filter1 x) (ite (or (= none x) (= (Data x) 1)) x none))))
(assert (forall ((x Option)) (= (filter2 x) (ite (or (= none x) (= (Data x) 2)) x none))))
(assert (= ((_ map filter1) s1) s2))
(assert (= ((_ map filter2) s1) s2))
(check-sat)
(get-model)
returns sat with the first line and unsat without it.
Is this a bug or am I missing something fundamental?
This is a bug. The two quantifiers are essentially providing "definitions" for filter1 and filter2.
The option smt.macro-finder is used to eliminate functions symbols by expanding these definitions. It is essentially performing "macro expansion". However, there is a bug in the macro expander. It does not expand the occurrences of filter1 and filter2 in the map constructs: (_ map filter1) and (_ map filter2).
This bug will be fixed.
In the meantime, we should not use the map construct and smt.macro-finder option simultaneously.

soundness issue with integer/bv mixed benchmarks?

I've the following SMT-Lib2 script:
(set-option :produce-models true)
(declare-fun s0 () Int)
(declare-fun table0 (Int) (_ BitVec 8))
(assert (= (table0 0) #x00))
(assert
(let ((s3 (ite (or (< s0 0) (<= 1 s0)) #x01 (table0 s0))))
(let ((s5 (ite (bvuge s3 #x02) #b1 #b0)))
(= s5 #b1))))
(check-sat)
(get-model)
With Z3 v3.2 running on the Mac, I get:
sat
(model
;; universe for (_ BitVec 8):
;; bv!val!2 bv!val!3 bv!val!0 bv!val!1
;; -----------
;; definitions for universe elements:
(declare-fun bv!val!2 () (_ BitVec 8))
(declare-fun bv!val!3 () (_ BitVec 8))
(declare-fun bv!val!0 () (_ BitVec 8))
(declare-fun bv!val!1 () (_ BitVec 8))
;; cardinality constraint:
(forall ((x (_ BitVec 8)))
(and (= x bv!val!2) (= x bv!val!3) (= x bv!val!0) (= x bv!val!1)))
;; -----------
(define-fun s0 () Int
(- 1))
(define-fun table0 ((x!1 Int)) (_ BitVec 8)
(ite (= x!1 0) bv!val!0
(ite (= x!1 (- 1)) bv!val!3
bv!val!0)))
)
Which states that s0 = -1 is a model. However, with s0 = -1, we have s3 = 1 and s5 = #b0, which makes the assertion false. In fact, I'm quite sure the benchmark as stated is unsatisfiable.
One thing I noticed in the Z3 output is the quantified formula it gives for the cardinality constraint. It says:
;; cardinality constraint:
(forall ((x (_ BitVec 8)))
(and (= x bv!val!2) (= x bv!val!3) (= x bv!val!0) (= x bv!val!1)))
The assertion is a conjunction, which sounds rather weird; shouldn't that be a disjunction? I'm not sure if this is the root-cause of the problem, but it sure sounds fishy.
There are two problems in Z3.
First, you are correct, there is a typo in the model printer. It should be a "or" instead of an "and". The second problem is that Z3 did not install the bit-vector theory and treated (_ BitVec 8) as a uninterpreted sort. This was a bug in the preprocessor that is used to decide in which logic the problem is in. You can workaround this bug by adding the following command in the beginning of the file:
(set-option :auto-config false)
These bugs have been fixed, and the fix will be available in the next release.

Resources