(:var 0) in z3 Quantifier's body - z3

When I get the body of a Quantifier of the Z3 OCaml API, for example a Quantifier whose string is
(exists ((u_1 Int)) (= u_1 x5))
by Quantifier.get_body, I obtain the expression
(= (:var 0) x5)
I guess (:var 0) in this expression is the renaming of u_1 to a fresh variable, but I do not know what is the kind of the expression (:var 0) and how to match it back to u_1 when we want to reconstruct the Quantifier from its body and its bound variables.
Could anyone suggest a solution? Thank you very much.

I got the solution. The expression e
(:var 0)
is an expression of VAR_AST. You can check by
AST.is_var (Expr.ast_of_expr e)
Then, you can get its index, e.g., 0, by
Quantifier.get_index e
With the index, you can easily match the expression e to its bound variable. Note that the list of bound variables of a quantifier can be obtained by
Quantifier.get_bound_variable_names

Related

Strange behaviour of seq.nth in exists-expression

Running z3 on this
(assert (< (seq.nth (seq.unit 0) 0) 0))
(check-sat)
has UNSAT as a result
But running
(assert (exists ((x Int))
(< (seq.nth (seq.unit 0) x) 0)))
(check-sat)
(get-model)
is SAT. Looking at the model
(model
(define-fun seq.nth_u ((x!0 Seq) (x!1 Int)) Int
(- 1))
)
So, doesn't this mean that seq.nth is treated like a variable over a Function?
Shouldn't it be a constant function (returning always the indexed value of seq)?
I would expect the second case also to be UNSAT. In order to achieve that, how can I make seq.nth a non-variable-function?
Help appreciated...
The function seq.nth is underspecified. That is, if you query an element that is out-of-bounds (i.e., either at a negative index or at at an index that is larger than the last), then it is free to return whatever value the solver wants. (And the solver will always pick a value to make your query satisfiable.)
This is typical of SMTLib, where underspecified functions can simply take any value when given arguments that are not in the domain of their arguments. So, z3 is telling you that there is such a model by indexing out-of-bounds using the sequence (seq.unit 0). i.e., we can index it at some negative value, or at some index larger than 0.
Note that the value of seq.nth_u given in the model (note the _u suffix!) is indicative of how the underflow/overflow behavior is modeled. It should not be confused with the value of the function seq.nth.
You can actually get z3 to display the value of the index x if you make it a top-level existential:
(declare-fun x () Int)
(assert (< (seq.nth (seq.unit 0) x) 0))
(check-sat)
(get-value (x))
For this, z3 says:
sat
((x (- 1)))
that is, index at location -1. But do not confuse this with the -1 that you see in the interpretation of seq.nth_u. In fact, if we also add:
(assert (> x 0))
then z3 says:
sat
((x 1))
However, if we add:
(assert (>= x 0))
(assert (< x 1))
then we get unsat as expected. And when working with sequences, you should add these sorts of constraints (if possible!) to avoid out-of-bounds accesses.

Is it possible to detect inconsistent equations in Z3, or before passing to Z3?

I was working with z3 with the following example.
f=Function('f',IntSort(),IntSort())
n=Int('n')
c=Int('c')
s=Solver()
s.add(c>=0)
s.add(f(0)==0)
s.add(ForAll([n],Implies(n>=0, f(n+1)==f(n)+10/(n-c))))
The last equation is inconsistent (since n=c would make it indeterminate). But, Z3 cannot detect this kind of inconsistencies. Is there any way in which Z3 can be made to detect it, or any other tool that can detect it?
As far as I can tell, your assertion that the last equation is inconsistent does not match the documentation of the SMT-LIB standard. The page Theories: Reals says:
Since in SMT-LIB logic all function symbols are interpreted
as total functions, terms of the form (/ t 0) are meaningful in
every instance of Reals. However, the declaration imposes no
constraints on their value. This means in particular that
for every instance theory T and
for every value v (as defined in the :values attribute) and
closed term t of sort Real,
there is a model of T that satisfies (= v (/ t 0)).
Similarly, the page Theories: Ints says:
See note in the Reals theory declaration about terms of the form
(/ t 0).
The same observation applies here to terms of the form (div t 0) and
(mod t 0).
Therefore, it stands to reason to believe that no SMT-LIB compliant tool would ever print unsat for the given formula.
Z3 does not check for division by zero because, as Patrick Trentin mentioned, the semantics of division by zero according to SMT-LIB are that it returns an unknown value.
You can manually ask Z3 to check for division by zero, to ensure that you never depend division by zero. (This is important, for example, if you are modeling a language where division by zero has a different semantics from SMT-LIB.)
For your example, this would look as follows:
(declare-fun f (Int) Int)
(declare-const c Int)
(assert (>= c 0))
(assert (= (f 0) 0))
; check for division by zero
(push)
(declare-const n Int)
(assert (>= n 0))
(assert (= (- n c) 0))
(check-sat) ; reports sat, meaning division by zero is possible
(get-model) ; an example model where division by zero would occur
(pop)
;; Supposing the check had passed (returned unsat) instead, we could
;; continue, safely knowing that division by zero could not happen in
;; the following.
(assert (forall ((n Int))
(=> (>= n 0)
(= (f (+ n 1))
(+ (f n) (/ 10 (- n c)))))))

What additional axioms do we need to add so that Z3 can verify the satisfiability of programs with recurrences?

As we know Z3 has limitations with recurrences. Is there any way get the result for the following program? what will additional equation help z3 get the result?
from z3 import *
ackermann=Function('ackermann',IntSort(),IntSort(),IntSort())
m=Int('m')
n=Int('n')
s=Solver()
s.add(ForAll([n,m],Implies(And(n>=0,m>=0),ackermann(m,n) == If(m!=0,If(n!=0,ackermann(m - 1,ackermann(m,n - 1)),If(n==0,ackermann(m - 1,1),If(m==0,n + 1,0))),If(m==0,n + 1,0)))))
s.add(n>=0)
s.add(m>=0)
s.add(Not(Implies(ackermann(m,n)>=0,ackermann(m+1,0)>=0)))
s.check()
With a nested recursive definition like Ackermann's function, I don't think there's much you can do to convince Z3 (or any other SMT solver) to actually do any interesting proofs. Such properties will require clever inductive arguments, and an SMT solver is just not the right tool for this sort of verification. A theorem prover like Isabelle, HOL, Coq, ... is the better choice here.
Having said that, the basic approach to establishing recursive function properties in SMT is to literally code up the inductive hypothesis as a quantified axiom, and arrange for the property you want proven to precisely line up with that axiom when the e-matching engine kicks in so it can instantiate the quantifiers "correctly." I'm putting the word correctly in quotes here, because the matching engine will go ahead and keep instantiating the axiom in unproductive ways especially for a function like Ackermann's. Theorem provers, on the other hand, precisely give you control over the proof structure so you can explicitly guide the prover through the proof-search space.
Here's an example you can look at: list concat in z3 which is doing an inductive proof of a much simpler inductive property than you are targeting, using the SMT-Lib interface. While it won't be easy to extend it to handle your particular example, it might provide some insight into how to go about it.
In the particular case of Z3, you can also utilize its fixed-point reasoning engine using the PDR algorithm to answer queries about certain recursive functions. See http://rise4fun.com/z3/tutorialcontent/fixedpoints#h22 for an example that shows how to model McCarthy's famous 91 function as an interesting case study.
Z3 will not try to do anything by induction for you, but (as Levent Erkok mentioned) you can give it the induction hypothesis and have it check that the result follows.
This works on your example as follows.
(declare-fun ackermann (Int Int) Int)
(assert (forall ((m Int) (n Int))
(= (ackermann m n)
(ite (= m 0) (+ n 1)
(ite (= n 0) (ackermann (- m 1) 1)
(ackermann (- m 1) (ackermann m (- n 1))))))))
(declare-const m Int)
(declare-const n Int)
(assert (>= m 0))
(assert (>= n 0))
; Here's the induction hypothesis
(assert (forall ((ihm Int) (ihn Int))
(=> (and (<= 0 ihm) (<= 0 ihn)
(or (< ihm m) (and (= ihm m) (< ihn n))))
(>= (ackermann ihm ihn) 0))))
(assert (not (>= (ackermann m n) 0)))
(check-sat) ; reports unsat as desired

Z3 start values [duplicate]

How to specify initial 'soft' values for the model? This initial model is the result of solving a similar query, and it is likely that this model has a correct pieces or even may be true for the current query.
Currently I am simulating this with an incremental solving and hard/soft constraints:
(define-fun trans_assumed ((a Int)) Int
; an initial model, which may be (partially) true
)
(declare-fun trans_sought ((a Int)) Int)
(declare-const p Bool)
(assert (=> p (forall ((a Int)) (= (trans_assumed a) (trans_sought a)))))
(check-sat p) ; in hope that trans_assumed values will be used as initial below
; add here the main constraints for trans_sought function
(check-sat) ; Z3 will use trans_assumed as a starting point for trans_sought
Does this really specify initial values for trans_sought to be trans_assumed?
Incremental mode of solving is slow compared to sequential. Any better ways of introducing initial values?
I think this is a good approach, but you may consider using more Boolean variables. Right now, it is a "all" or "nothing" approach. In your script, when (check-sat p) is executed, Z3 will look for a model where trans_assumed and trans_sought have the same interpretation. If such model does not exist, it will return with the unsat core containing p. When (check) is executed, Z3 is free to assign p to false, and the universal quantifier is essentially a don't care. That is, trans_assumed and trans_sought can be completely different.
If you use multiple Boolean variables to control the interpretation of trans_sought, you will have more flexibility.
If the rest of your problem is quantifier free, you should consider dropping the universal quantifier. This can be done if you only care about the value of trans_sought in a finite number of points.
Suppose we have that trans_assumed(0) = 1 and trans_assumed(1) = 10. Then, we can write:
assert (=> p0 (= (trans_sought 0) 1)))
assert (=> p1 (= (trans_sought 1) 10)))
In this encoding, we can query (check-sat p0 p1), (check-sat p0), (check-sat p1)

Does Z3's simplify() method support absorption law?

Supposed I have a BoolExpr in the form of
a & (a | b) or a | (a & b)
and I want to simplify it to
a
by calling simplify(). It doesn't work.
Also, for a constraint like
(a | b) & (b | a)
simplify() can not turn it into the simplest form
(a|b) or (b|a).
Is there a workaround way?
#Nikolaj Bjorner: Thanks for your help and I have one more question:
Here is my original constraint:
Goal: (goal
(or (> (type o) 2) (= (type o) 1))
(or (= (type o) 1) (> (type o) 2)))
Here is the simplified version(by ctx-solver-simplify):
(or (= (type o) 1) (not (<= (type o) 2)))
The actual constraint I am expecting is:
(or (= (type o) 1) (> (type o) 2))
and I don't want to introduce any negation. What should I do?
The default simplifier seeks only rewrites that are cheap.
There is a different simplifier that you can invoke as a tactic.
It simplifies the goals you describe.
For example:
(declare-const a Bool)
(declare-const b Bool)
(assert (or a (and a b)))
(apply ctx-solver-simplify)
It may return several subgoal that need to be re-assembled to a formula.
The Z3 tutorial on rise4fun.com describes tactics.
The ctx-solver-simplify does limited context-dependent rewriting. It is still incomplete.
It does not produce canonical normal forms (two formulas that are equivalent may simplify to
two different formulas).

Resources