z3 with LIA logic can return real? - z3

I am experimenting with z3. I am surprised that, when setting the logic to LIA, z3 can return real numbers. For example, the following:
from z3 import *
sol = SolverFor("LIA")
vB = Real('vB')
sol.add(vB < 3)
sol.add(vB > 2)
sol.check()
print(sol.model())
returns:
[vB = 5/2]
Can someone please explain how it can be the case?

Strictly speaking, this is a "bug" in z3. Here's the equivalent SMT script:
(set-logic LIA)
(declare-fun vB () Real)
(assert (< vB 3.0))
(assert (> vB 2.0))
(check-sat)
(get-model)
If you run this with cvc4/cvc5, you get:
(error "Parse Error: a.smt2:2.23: Symbol 'Real' not declared as a type
(declare-fun vB () Real)
^
")
And Yices says:
(error "at line 2, column 20: undefined sort: Real")
CVC and Yices are correct here, since you restricted the logic to "LIA", the name "Real" is no longer defined. Z3, however, is playing loose here, and allowing reals by default. (It internally uses logics to pick which algorithms to run, but it's not always consistent in making sure the logic restricts the names available.)
You can report this at the z3 issue tracker here: https://github.com/Z3Prover/z3/issues. I suspect they're already aware of this, but would be good to have it recorded.

Related

Can Z3 fpRealToFP be used for practical floating point accuracy proofs?

I would like to use Z3 to prove the accuracy of certain floating point expressions. My plan was to:
Implement the expression as Reals.
Implement the same expression as Float32().
Use fpRealToFP to create correspondences between the FP and Real consts.
Prove that the results are within some margin of error.
However, despite fpRealToFP existing, it seems to fail on even the most trivial tests.
>>> import z3
>>> r = z3.Real("r")
>>> f = z3.Const("f", z3.Float32())
>>> z3.solve(f > z3.fpRealToFP(z3.RNE(), r, z3.Float32()))
failed to solve
>>> z3.solve(z3.fpToReal(f) > r)
failed to solve
Is there a reasonable way to approach this type of problem in Z3, or is this simply not something that it can do?
One approach that I've considered is to replace the Real part with a higher precision float, i.e. prove that the F32 result is within some MoE of the F128 result. This may be good enough to convince myself of probable correctness, but it wouldn't technically be a proper proof on its own.
Floating-point from/to real conversions are extremely expensive. But I'm surprised z3 can't even handle the example you posted, as there are literally no constraints at all. Here's the SMTLib equivalent:
(set-logic ALL)
(set-option :produce-models true)
(declare-fun r () Real)
(declare-fun f () (_ FloatingPoint 8 24))
(assert (fp.gt f ((_ to_fp 8 24) roundNearestTiesToEven r)))
(check-sat)
(get-model)
z3 responds unknown, but cvc5 does much better on it:
$ cvc5 a.smt2
sat
(
(define-fun r () Real (- 1.0))
(define-fun f () (_ FloatingPoint 8 24) (fp #b0 #b11111110 #b11111111111111111111111))
)
So, you might get better mileage by trying CVC5 on your examples. But keep in mind that once you have complicated constraints, all solvers will have hard time coping with these sorts of problems. Real/FP conversions create highly non-linear terms, and so far as I know no solver can deal with such problems all that well, at least not for the time being.

Why does smtlib/z3/cvc4 allow to declare the same constant more than once?

I have a question about declare-const in smtlib.
For example,
In z3/cvc4, the following program doesn't report an error:
C:\Users\Chansey>z3 -in
(declare-const x Int)
(declare-const x Bool)
In the smt-lib-reference, it says that
(declare-fun f (s1 ... sn) s) ... The command reports an error if a function symbol with name f is already present in the current signature.
So the sort s is included in the entire signature of the x, is that right?
But why is it so? What is the motivation behind it?
In my understanding, the x is variable identifier and in general (e.g. in some general programming languages) we are not allowed to declare the same variable with different types. So I think the above code is best to report an error.
I once thought that perhaps z3/smtlib can support redefinition?, but not...
C:\Users\Chansey>z3 -in
(declare-const x Int)
(declare-const x Bool)
(assert (= x true))
(error "line 3 column 11: ambiguous constant reference, more than one constant with the same sort, use a qualified expre
ssion (as <symbol> <sort>) to disambiguate x")
So the above code is definitely wrong, why not report the error earlier?
PS. If I use the same sort, then it will report an error (that great, I hope the Bool case can also report the error):
C:\Users\Chansey>z3 -in
(declare-fun x () Int)
(declare-fun x () Int)
(error "line 2 column 21: invalid declaration, constant 'x' (with the given signature) already declared")
Thanks.
In SMTLib, a symbol is identified not just by its name, but also by its sort. And it's perfectly fine to use the same name, so long as you have a different sort, as you observed. Here's an example:
(set-logic ALL)
(set-option :produce-models true)
(declare-fun x () Int)
(declare-fun x () Bool)
(assert (= (as x Int) 4))
(assert (= (as x Bool) true))
(check-sat)
(get-model)
(get-value ((as x Int)))
(get-value ((as x Bool)))
This prints:
sat
(
(define-fun x () Bool
true)
(define-fun x () Int
4)
)
(((as x Int) 4))
(((as x Bool) true))
Note how we use the as construct to disambiguate between the two x's. This is explained in Section 3.6.4 of http://smtlib.cs.uiowa.edu/papers/smt-lib-reference-v2.6-r2021-05-12.pdf
Having said that, I do agree the part of the document you quoted isn't very clear about this, and perhaps can use a bit of a clarifying text.
Regarding what the motivation is for allowing this sort of usage: There are two main reasons. The first is o simplify generating SMTLib. Note that SMTLib is usually not intended to be hand-written. It's often generated from a higher-level system that uses an SMT solver underneath. So being flexible in allowing symbols to share name so long as they can be distinguished by explicit sort annotations can be beneficial when you use SMTLib as an intermediate language between a higher-level system and the solver itself. But when you're writing SMTLib by hand, you should probably avoid this sort of duplication if you can, for clarity if nothing else.
The second reason is to allow for a limited form of "overloading" to be used. For instance, think about the SMTLib function distinct. This function can operate on any type of object (Int, Bool, Real etc.), yet it's always called distinct. (We don't have distinct-int, distinct-bool etc.) The solver "distinguishes" which one you meant by doing a bit of an analysis, but in cases it cannot, you can help it along with an as declaration as well. So, theory symbols can be overloaded in this way, which is also the case for =, +, *, etc. Of course, SMTLib does not allow for users to define such overloaded names, but as the document states in footnote 29 on page 91, this restriction might be removed in the future.

Is that normal that Z3 solver cannot solve 2^x=4?

I have tried to solve 2^x=4 with Z3, by putting the following on the Z3 website: https://rise4fun.com/z3/tutorial.
(declare-const x Real)
(declare-const y Real)
(declare-const z Real)
(assert (=(^ 2 x) 4))
(check-sat)
(get-model)
Z3 produced
unknown
(model
)
Am I misusing Z3?
Problems involving exponentials are typically beyond the reach of z3, or general-purpose SMT solvers. This doesn't mean that they cannot solve them: Theory of reals is decidable. But they may not have the right "heuristics" to kick in to answer every query involving exponentials as sat/unsat. You can search stack-overflow for keywords like nnf, non-linear, etc., to see a plethora of questions regarding queries that involve such difficult terms.
Having said that, there's a separate line of research called delta-satisfiability that can help with these sorts of problems to a great extent. Note that delta-satisfiability is different than regular satisfiability. It means either the formula is satisfiable, or a delta-perturbation of it is. The most prominent such solver is dReal, and you can read all about it here: http://dreal.github.io/
For your query, dReal says:
[urfa]~/qq>dreal a.smt2
delta-sat with delta = 0.001
(model
(define-fun x () Real [2, 2])
(define-fun y () Real [-0.0005, 0.0005])
(define-fun z () Real [-0.0005, 0.0005])
)
(You didn't actually use y and z in your query, so you can ignore those outputs.)
As you can see dReal determines x must be in the range [2, 2], i.e., it must be 2. But it also says delta-sat with delta = 0.001: This means it has ensured the correctness within that factor. (You can tweak the factor yourself, making it arbitrarily small, but not zero.) When you have problems that arise from physical systems, delta-sat is the right choice in modeling them in the SMT-world.

z3 converting from bit vectors to integers

There are several posts regarding the conversion of bit vectors
to integers (and vice versa) in z3. See for example here,
here and here.
The documentation says that Z3_mk_bv2int is uninterpreted:
"...This function is essentially treated as uninterpreted.
So you cannot expect Z3 to precisely reflect the semantics of this
function when solving constraints with this function..."
However, I could not find a simple example where it does fail
to reflect the expected semantics.
For instance, whenever I use queries like this:
(declare-const s String)
(declare-const someBitVec10 (_ BitVec 10))
(assert (= s "74g\x00!!#2#$$"))
(assert (str.in.re (str.at s (bv2int someBitVec10)) (re.range "1" "3")))
(check-sat)
(get-value (s someBitVec10))
I get a correct answer (index should 7, and it is)
sat
((s "74g\x00!!#2#$$")
(someBitVec10 #b0000000111))
Could anyone please provide a simple example where z3's
bv2int and/or int2bv fail?? thanks!
This issue is now resolved, as it turns out that both int2bv and bv2int are indeed interpreted. The documentation hasn't been updated, and this might have caused the confusion (at least in my case it did). All the details are in this GitHub/z3/issues post.

Using define-fun-rec in SMT

I'm currently trying to write an SMT script using define-fun-rec. I've tested with both Z3, version 4.4.2, and CVC4, version 1.4. As far as I can tell, these are the most recent versions of both, and both support the feature*. However, both do not seem to recognize the command.
(I made some changes to this based on Nikolaj's reply. It still gives the error messages.) Specifically, given:
(define-fun-rec
fac ((x Int)) Int
(
ite (<= x 1)
1
(* x (fac (- x 1)))
)
)
(assert (= (fac 4) 24))
(check-sat)
Z3 outputs:
unsupported
; define-fun-rec
(error "line 10 column 17: unknown function/constant fac")
sat
And CVC4 outputs:
(error "Parse Error: fac.smt2:1.15: expected SMT-LIBv2 command, got `define-fun-rec'.
(define-fun-rec
^
")
My best guess is that there is some sort of flag I need to set or I need to be using some specific logic, but I've had a lot of trouble finding any sort of detailed instructions or examples with define-fun-rec. Any advice would be appreciated. Thanks!
*Z3 has support: How to deal with recursive function in Z3?
CVC4 has support: http://lara.epfl.ch/~reynolds/pres-smt15.pdf
The latest version of CVC4 can be downloaded under "Development versions" (on the right hand side) of:
http://cvc4.cs.nyu.edu/downloads/
The latest development version has support for recursive function definitions. You can use the cvc4 command line option "--fmf-fun" to enable a technique that finds small models for problems involving recursive function applications, assuming definitions are admissible.
(Although, unfortunately your example with factorial also requires non-linear arithmetic, which CVC4 does not yet support.)
Don't set the logic to LIA. This is not in the LIA fragment and Z3 will use the wrong tactic to solve the problem.Just remove the set-logic line.
It helps to not use an undefined function "f" inside the definition of "fib".
I would suggest that you call the function "fac" and not "fib" since you are defining a factorial function.
Thus,
(define-fun-rec
fac ((x Int)) Int
(
ite (<= x 1)
1
(* x (fac (- x 1)))
)
)
(assert (= (fac 4) 24))
(check-sat)
z3 -version
Z3 version 4.4.2
z3 fac.smt2
sat
If you change 24 to 25 you get unsat.

Resources