I am trying to prove an inductive fact in Z3, an SMT solver by Microsoft. I know that Z3 does not provide this functionality in general, as explained in the Z3 guide (section 8: Datatypes), but it looks like this is possible when we constrain the domain over which we want to prove the fact. Consider the following example:
(declare-fun p (Int) Bool)
(assert (p 0))
(assert (forall ((x Int))
(=>
(and (> x 0) (<= x 20))
(= (p (- x 1)) (p x) ))))
(assert (not (p 20)))
(check-sat)
The solver responds correctly with unsat, which means that (p 20) is valid. The problem is that when we relax this constraint any further (we replace 20 in the previous example by any integer greater than 20), the solver responds with unknown.
I find this strange because it does not take Z3 long to solve the original problem, but when we increase the upper limit by one it becomes suddenly impossible. I have tried to add a pattern to the quantifier as follows:
(declare-fun p (Int) Bool)
(assert (p 0))
(assert (forall ((x Int))
(! (=>
(and (> x 0) (<= x 40))
(= (p (- x 1)) (p x) )) :pattern ((<= x 40)))))
(assert (not (p 40)))
(check-sat)
Which seems to work better, but now the upper limit is 40. Does this mean that I can better not use Z3 to prove such facts, or am I formulating my problem incorrectly?
Z3 uses many heuristics to control quantifier instantiation. One one them is based on the "instantiation depth". Z3 tags every expression with a "depth" attribute. All user supplied assertions are tagged with depth 0. When a quantifier is instantiated, the depth of the new expressions is bumped. Z3 will not instantiate quantifiers using expressions tagged with a depth greater than a pre-defined threshold. In your problem, the threshold is reached: (p 40) is depth 0, (p 39) is depth 1, (p 38) is depth 2, etc.
To increase the threshold, you should use the option:
(set-option :qi-eager-threshold 100)
Here is the example with this option: http://rise4fun.com/Z3/ZdxO.
Of course, using this setting, Z3 will timeout, for example, for (p 110).
In the future, Z3 will have better support for "bounded quantification". In most cases, the best approach for handling this kind of quantifier is to expand it.
With the programmatic API, we can easily "instantiate" expressions before we send them to Z3.
Here is an example in Python (http://rise4fun.com/Z3Py/44lE):
p = Function('p', IntSort(), BoolSort())
s = Solver()
s.add(p(0))
s.add([ p(x+1) == p(x) for x in range(40)])
s.add(Not(p(40)))
print s.check()
Finally, in Z3, patterns containing arithmetic symbols are not very effective. The problem is that Z3 preprocess the formula before solving. Then, most patterns containing arithmetic symbols will never match. For more information on how to use patterns/triggers effectively, see this article. The author also provides a slide deck.
Related
I'm stuck on how to how to create a statement in SMTLIB2 that asserts something like
forall x < 100, f(x) = 100
This property would be check a function that adds 1 to all numbers less than 100 recursively:
(define-fun-rec incUntil100 ((x Int)) Int
(ite
(= x 100)
100
(incUntil100 (+ x 1))
)
)
I read through the Z3 tutorial on quantifiers and patterns, but that didn't seem to get me much anywhere.
In SMTLib, you'd write that property as follows:
(assert (forall ((x Int)) (=> (< x 100) (= (incUntil100 x) 100))))
(check-sat)
But if you try this, you'll see that z3 will loop forever and CVC4 will tell you unknown as the answer. While you can define and assert these sorts of functions in SMTLib, solver support for actual proofs is rather weak, as they do not do induction out-of-the box.
If proving properties of recursive functions is your goal, SMT-solvers are not a good choice; instead look into theorem provers such as Isabelle, HOL, Coq, Lean, ACL2, etc.; which are built for that very purpose.
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)))))))
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
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)
In fact, does the SMT-LIB standard have a rational (not just real) sort? Going by its website, it does not.
If x is a rational and we have a constraint x^2 = 2, then we should get back ``unsatisfiable''. The closest I could get to encoding that constraint is the following:
;;(set-logic QF_NRA) ;; intentionally commented out
(declare-const x Real)
(assert (= (* x x) 2.0))
(check-sat)
(get-model)
for which z3 returns a solution, as there is a solution (irrational) in the reals. I do understand that z3 has its own rational library, which it uses, for instance, when solving QF_LRA constraints using an adaptation of the Simplex algorithm. On a related note, is there an SMT solver that supports rationals at the input level?
I'm sure it's possible to define a Rational sort using two integers as suggested by Nikolaj -- I would be interested to see that. It might be easier to just use the Real sort, and any time you want a rational, assert that it's equal to the ratio of two Ints. For example:
(set-option :pp.decimal true)
(declare-const x Real)
(declare-const p Int)
(declare-const q Int)
(assert (> q 0))
(assert (= x (/ p q)))
(assert (= x 0.5))
(check-sat)
(get-value (x p q))
This quickly comes back with
sat
((x 0.5)
(p 1)
(q 2))