Please consider the follow statements.
y := 10
z := x + y
After these two statements are executed "z" has the value "x + 10" where "x" represents a symbolic value; such symbolic values are important in program verification -- for example, to compute the path characteristics using Dijkstra'a weakest pre-condition method.
Now, consider the following code for Z3.
(declare-const x Int)
(declare-const y Int)
(declare-const z Int)
(assert (= y 10))
(assert (= z (+ x y)))
(check-sat)
(get-value (z))
It produces the following output since it consider the interpretation where "y" has the 10 and "x" has the value 0.
sat
((z 10))
Is there some way I can make Z3 produce the output "x + 10"? i.e., I am interested in getting the symbolic values in terms of variables that have not been instantiated with any specific values.
I am not an expert but I think that it is not possible using Z3-SMT-LIB. But it is possible using Z3Py.
x = Int('x')
y = 10
z = x + y
print z
and the output is
x + 10
Related
I tried QF_NRA in z3 and it gave me an abstract value about root-obj.
(set-logic QF_NRA)
(declare-const x Real)
(assert (= 2 (* x x)))
(check-sat)
sat
(get-model)
(
(define-fun x () Real
(root-obj (+ (^ x 2) (- 2)) 1))
)
I don’t quite understand its meaning.
In addition, the x seems defined in recursion but not by define-fun-rec.
Thanks.
Algebraic reals
Z3's Real theory supports what's known as algebraic reals. That is, it can express solutions in terms of the roots of polynomials with rational (equivalently, integer) valued coefficients. Note that such a polynomial can have complex roots. Z3 only supports those roots that are real, i.e., those with an imaginary part of 0. An algebraic real is essentially the real-root of a univariate polynomial with integer coefficients.
Dealing with root-obj's
In the example you posted, you're asking z3 to find a satisfying model for x*x == 2. And it's telling you that the solution is "a" zero-of-the polynomial (+ (^ x 2) (- 2)), or written in more familiar notation P(x) = x^2 -2. The index you get is 1 (the second argument to the root-obj), which says it's the "first" real-root of this polynomial. If you ask z3 to give you another solution, it'll give you the next one:
(set-logic QF_NRA)
(declare-const x Real)
(assert (= 2 (* x x)))
(assert (distinct x (root-obj (+ (^ x 2) (- 2)) 1)))
(check-sat)
(get-model)
This prints:
sat
(
(define-fun x () Real
(root-obj (+ (^ x 2) (- 2)) 2))
)
As you see, the "next" solution is the second root. What if we assert we want yet another solution?
(set-logic QF_NRA)
(declare-const x Real)
(assert (= 2 (* x x)))
(assert (distinct x (root-obj (+ (^ x 2) (- 2)) 1)))
(assert (distinct x (root-obj (+ (^ x 2) (- 2)) 2)))
(check-sat)
This prints:
unsat
as expected.
Note that algebraic reals do not include numbers such as pi, e, etc., i.e., they do not include transcendentals. Only those real numbers that can be expressed as the root of polynomials with integer coefficients. Leonardo's paper from 2012 explains this in great detail.
Getting approximations
z3 also allows you to get an approximation for such a root-obj solution, with as arbitrary a precision as you like. To do so, use the incantation:
(set-option :pp.decimal true)
(set-option :pp.decimal_precision 20)
where 20 in the second line is how many digits of precision you'd like, and you can change it as you see fit. If you add these two lines to your original script, z3 will respond:
sat
(
(define-fun x () Real
(- 1.4142135623730950?))
)
Note the ? at the end of the number. This is z3's way of telling you that the number it printed is an "approximation" to the value, i.e., it isn't the precise result.
A note on "recursion"
Your question suggests maybe x is defined recursively. This isn't the case. It just happens that you picked the variable name to be x and z3 always uses the letter x for the polynomial as well. If you picked y as the variable, you'd still get the exact same answer; the parameter to the polynomial has nothing to the with the variables in your program.
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))
We know that, we can prove validity of a theorem by saying :
let Demorgan(x, y) = formula1(x,y) iff formula2(x,y)
assert ( forall (x,y) . Demorgan(x,y) )
Alternatively we can eliminate the forall quantifier by saying that :
let Demorgan(x, y) = formula1(x,y) iff formula2(x,y)
( assert (not Demorgan(x,y) ) )
So if it returns unsat, then we can say the above formula is valid.
Now I want to use this idea to eliminate the forall quantifier from the following assertion:
assert ( exists x1,x2,x3 st .( forall y . formula1(x1,y) iff
formula2(x2,y) iff
formula3(x3,y) ) )
So is there any way in Z3(using C++ API or SMT-LIB2.0) that I can assert something like the following :
assert (exists x1,x2,x3 st. ( and ((not ( formula1(x1,y) iff formula2(x2,y) )) == unsat)
((not ( formula2(x2,y) iff formula3(x3,y) )) == unsat)))
Yes, when we can prove the validity of a formula by showing its negation to be unsatisfiable.
For example, to show that Forall X. F(X) is valid, we just have to show that not (Forall X. F(X)) is unsatisfiable. The formula not (Forall X. F(X)) is equivalent to (Exists X. not F(X)). The formula (Exists X. not F(X)) is equisatisfiable to the formula not F(X) where the bound variable X is replaced by a fresh constant X. By equisatisfiable, I mean that the first one is satisfiable iff the second one is. This step that removes existential quantifiers is usually called skolemization.
Note that these last two formulas are not equivalent.
For example, consider the interpretation { X -> 2 } that assigns X to 2. The formula Exists X. not (X = 2) still evaluates to true in this interpretation because we can choose X to be 3. On the other hand, the formula not (X = 2) evaluates to false in this interpretation.
We usually use the term quantifier elimination procedure for a procedure that given a formula F produces an equivalent quantifier-free formula F'. So, skolemization is not considered a quantifier elimination procedure because the result is not an equivalent formula.
That being said, we don't have to apply the skolemization step by hand. Z3 can do it for us. Here is an example (also available online here).
(declare-sort S)
(declare-fun F (S) Bool)
(declare-fun G (S) Bool)
(define-fun Conjecture () Bool
(forall ((x S)) (= (and (F x) (G x)) (not (or (not (F x)) (not (G x)))))))
(assert (not Conjecture))
(check-sat)
Now, let us consider a formula of the form Exists X. Forall Y. F(X, Y). To prove the validity of this formula, we can show the negation not Exists X. Forall Y. F(X, Y) to be unsatisfiable. The negation is equivalent to Forall X. Exists Y. not F(X, Y). Now, if apply skolemization to this formula, we obtain Forall X. not F(X, Y(X)). In this case, the bound variable Y was replaced with Y(X), where Y is a fresh function symbol in the resultant formula. The intuition is that the function Y is the "choice function". For each X, we can choose a different value to satisfy the formula F. Z3 performs all these steps automatically for us. We don't need to apply skolemization by hand. However, in this case, the resultant formula is usually harder to solve because it contains a universal quantifier after the skolemization step.
I've got several questions about Z3 tactics, most of them concern simplify .
I noticed that linear inequalites after applying simplify are often negated.
For example (> x y) is transformed by simplify into (not (<= x y)). Ideally, I would want integer [in]equalities not to be negated, so that (not (<= x y)) is transformed into (<= y x). I can I ensure such a behavior?
Also, among <, <=, >, >= it would be desirable to have only one type of inequalities to be used in all integer predicates in the simplified formula, for example <=. Can this be done?
What does :som parameter of simplify do? I can see the description that says that it is used to put polynomials in som-of-monomials form, but maybe I'm not getting it right. Could you please give an example of different behavior of simplify with :som set to true and false?
Am I right that after applying simplify arithmetical expressions would always be represented in the form a1*t1+...+an*tn, where ai are constants and ti are distinct terms (variables, uninterpreted constants or function symbols)? In particular is always the case that subtraction operation doesn't appear in the result?
Is there any available description of the ctx-solver-simplify tactic? Superficially, I understand that this is an expensive algorithm because it uses the solver, but it would be interesting to learn more about the underlying algorithm so that I have an idea on how many solver calls I may expect, etc. Maybe you could give a refernce to a paper or give a brief sketch of the algorithm?
Finally, here it was mentioned that a tutorial on how to write tactics inside the Z3 code base might appear. Is there any yet?
Thank you.
Here is an example (with comments) that tries to answer questions 1-4. It is also available online here.
(declare-const x Int)
(declare-const y Int)
;; 1. and 2.
;; The simplifier will map strict inequalities (<, >) into non-strict ones (>=, <=)
;; Example: x < y ===> not x >= y
;; As suggested by you, for integer inequalities, we can also use
;; x < y ==> x <= y - 1
;; This choice was made because it is convenient for solvers implemented in Z3
;; Other normal forms can be used.
;; It is possible to map everything to a single inequality. This is a straightforward modificiation
;; in the Z3 simplifier. The relevant files are src/ast/rewriter/arith_rewriter.* and src/ast/rewriter/poly_rewriter.*
(simplify (<= x y))
(simplify (< x y))
(simplify (>= x y))
(simplify (> x y))
;; 3.
;; :som stands for sum-of-monomials. It is a normal form for polynomials.
;; It is essentially a big sum of products.
;; The simplifier applies distributivity to put a polynomial into this form.
(simplify (<= (* (+ y 2) (+ x 2)) (+ (* y y) 2)))
(simplify (<= (* (+ y 2) (+ x 2)) (+ (* y y) 2)) :som true)
;; Another relevant option is :arith-lhs. It will move all non-constant monomials to the left-hand-side.
(simplify (<= (* (+ y 2) (+ x 2)) (+ (* y y) 2)) :som true :arith-lhs true)
;; 4. Yes, you are correct.
;; The polynomials are encoded using just * and +.
(simplify (- x y))
5) ctx-solver-simplify is implemented in the file src/smt/tactic/ctx-solver-simplify.*
The code is very readable. We can add trace messages to see how it works on particular examples.
6) There is no tutorial yet on how to write tactics. However, the code base has many examples.
The directory src/tactic/core has the basic ones.
I would like to know what is the difference between following 2 statements -
Statement 1
(define-fun max_integ ((x Int) (y Int)) Int
(ite (< x y) y x))
Statement 2
(declare-fun max_integ ((Int)(Int)) Int)
(assert (forall ((x Int) (y Int)) (= (max_integ x y) (if (< x y) y x))))
I observed that when I use Statement1, my z3 constraints give me a result in 0.03 seconds. Whereas when I used Statement2, it does not finish in 2 minutes and I terminate the solver.
I would like also to know how achieve it using C-API.
Thanks !
Statement 1 is a macro. Z3 will replace every occurrence of max_integ with the ite expression. It does that during parsing time. In the second statement, by default, Z3 will not eliminate max_integ, and to be able to return sat it has to build an interpretation for the uninterpreted symbol max_integ that will satisfy the quantifier for all x and y.
Z3 has an option called :macro-finder, it will detect quantifiers that are essentially encoding macros, and will eliminate them. Here is an example (also available online here):
(set-option :macro-finder true)
(declare-fun max_integ ((Int)(Int)) Int)
(assert (forall ((x Int) (y Int)) (= (max_integ x y) (if (< x y) y x))))
(check-sat)
(get-model)
That being said, we can easily simulate macros in a programmatic API by writing a function that given Z3 expressions return a new Z3 expression. Here in an example using the Python API (also available online here):
def max(a, b):
# The function If builds a Z3 if-then-else expression
return If(a >= b, a, b)
x, y = Ints('x y')
solve(x == max(x, y), y == max(x, y), x > 0)
Yet another option is to use the C API: Z3_substitute_vars. The idea is to an expression containing free variables. Free variables are created using the API Z3_mk_bound. Each variable represents an argument. Then, we use Z3_substitute_vars to replace the variables with other expressions.