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.
Related
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.
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.
Using smtlib I would like to make something like modulo using QF_UFNRA. This disables me from using mod, to_int, to_real an such things.
In the end I want to get the fractional part of z in the following code:
(set-logic QF_UFNRA)
(declare-fun z () Real)
(declare-fun z1 () Real)
(define-fun zval_1 ((x Real)) Real
x
)
(declare-fun zval (Real) Real)
(assert (= z 1.5));
(assert (=> (and (<= 0.0 z) (< z 1.0)) (= (zval z) (zval_1 z))))
(assert (=> (>= z 1.0) (= (zval z) (zval (- z 1.0)))))
(assert (= z1 (zval z)))
Of course, as I am asking this question here, implies, that it didn't work out.
Has anybody got an idea how to get the fractional part of z into z1 using logic QF_UFNRA?
This is a great question. Unfortunately, what you want to do is not possible in general if you restrict yourself to QF_UFNRA.
If you could encode such functionality, then you can decide arbitrary Diophantine equations. You would simply cast a given Diophantine equation over reals, compute the "fraction" of the real solution with this alleged method, and assert that the fraction is 0. Since reals are decidable, this would give you a decision procedure for Diophantine equations, accomplishing the impossible. (This is known as Hilbert's 10th problem.)
So, as innocent as the task looks, it is actually not doable. But that doesn't mean you cannot encode this with some extensions, and possibly have the solver successfully decide instances of it.
If you allow quantifiers and recursive functions
If you allow yourself quantifiers and recursive-functions, then you can write:
(set-logic UFNRA)
(define-fun-rec frac ((x Real)) Real (ite (< x 1) x (frac (- x 1))))
(declare-fun res () Real)
(assert (= (frac 1.5) res))
(check-sat)
(get-value (res))
To which z3 responds:
sat
((res (/ 1.0 2.0)))
Note that we used the UFNRA logic allowing quantification, which is required here implicitly due to the use of the define-fun-rec construct. (See the SMTLib manual for details.) This is essentially what you tried to encode in your question, but instead using the recursive-function-definition facilities instead of implicit encoding. There are several caveats in using recursive functions in SMTLib however: In particular, you can write functions that render your system inconsistent rather easily. See Section 4.2.3 of http://smtlib.cs.uiowa.edu/papers/smt-lib-reference-v2.5-draft.pdf for details.
If you can use QF_UFNIRA
If you move to QF_UFNIRA, i.e., allow mixing reals and integers, the encoding is easy:
(set-logic QF_UFNIRA)
(declare-fun z () Real)
(declare-fun zF () Real)
(declare-fun zI () Int)
(assert (= z (+ zF zI)))
(assert (<= 0 zF))
(assert (< zF 1))
(assert (= z 1.5))
(check-sat)
(get-value (zF zI))
z3 responds:
sat
((zF (/ 1.0 2.0))
(zI 1))
(You might have to be careful about the computation of zI when z < 0, but the idea is the same.)
Note that just because the encoding is easy doesn't mean z3 will always be able to answer the query successfully. Due to mixing of Real's and Integer's, the problem remains undecidable as discussed before. If you have other constraints on z, z3 might very well respond unknown to this encoding. In this particular case, it happens to be simple enough so z3 is able to find a model.
If you have sin and pi:
This is more of a thought experiment than a real alternative. If SMTLib allowed for sin and pi, then you can check whether sin (zI * pi) is 0, for a suitably constrained zI. Any satisfying model to this query would ensure that zI is integer. You can then use this value to extract the fractional part by subtracting zI from z.
But this is futile as SMTLib neither allows for sin nor pi. And for good reason: Decidability would be lost. Having said that, maybe some brave soul can design a logic that supported sin, pi, etc., and successfully answered your query correctly, while returning unknown when the problem becomes too hard for the solver. This is already the case for nonlinear arithmetic and the QF_UFNIRA fragment: The solver may give up in general, but the heuristics it employs might solve problems of practical interest.
Restriction to Rationals
As a theoretical aside, it turns out that if you restrict yourself to rationals only (instead of actual reals) then you can indeed write a first-order formula to recognize integers. The encoding is not for the faint of heart, however: http://math.mit.edu/~poonen/papers/ae.pdf. Furthermore, since the encoding involves quantifiers, it's probably quite unlikely that SMT solvers will do well with a formulation based on this idea.
[Incidentally, I should extend thanks to my work colleagues; this question made for a great lunch-time conversation!]
I would like to construct an SMT formula having a number of assertions over integer linear arithmetic and Boolean variables as well as some assertions over real non-linear arithmetic and again Boolean variables. The assertions over integers and reals share only the Boolean variables. As an example, consider the following formula:
(declare-fun b () Bool)
(assert (= b true))
(declare-fun x () Int)
(declare-fun y () Int)
(declare-fun z () Int)
(assert (or (not b) (>= (+ x y) (- x (+ (* 2 z) 1)))))
(declare-fun r () Real)
(assert (or (not b) (= (+ (* r r) (* 3 r) (- 4)) 0)))
If I feed z3 with this formula, it immediately reports "unknown". But if I remove the integer part of it, I get the solution right away, which satisfies the constraint with variable "r". I presume this means that the non-linear constraint on its own is not hard for the solver. The issue should be in mixing (linear) constraints over integers and (non-linear) constraints over reals.
So my question is the following. What is the correct way to handle this kind of mixed formulas using z3 (if there is any)? My understanding of DPLL(T) is that it should be able to deal with such formulas using different theory solvers for different constraints. Please, correct me if I am wrong.
As George said in his comment, the non-linear solver in Z3 is rather fragile and the out-of-the-box performance isn't great. That said, there are a number of questions and answers about this problem here on stackoverflow, e.g., see these:
Z3 Performance with Non-Linear Arithmetic
How does Z3 handle non-linear integer arithmetic?
Z3 : strange behavior with non linear arithmetic
Non-linear arithmetic and uninterpreted functions
Z3 Theorem Prover: Pythagorean Theorem (Non-Linear Artithmetic)
Which techniques are used to handle Non-linear Integer Real problems in z3?
Satisfiablity checking in non-linear integer arithmetic by approximation
I have a test.smt2 file:
(set-logic QF_IDL)
(declare-const a Int)
(declare-const b Int)
(declare-const c Int)
(assert (or (< a 2) (< b 2 )) )
(check-sat)
(get-model)
(exit)
Is there anyway to tell Z3 to only output a=1 (or b=1)? Because when a is 1, b's value does not matter any more.
I executed z3 smt.relevancy=2 -smt2 test.smt2
(following How do I get Z3 to return minimal model?, although smt.relevancy seems has default value 2), but it still outputs:
sat
(model
(define-fun b () Int
2)
(define-fun a () Int
1)
)
Thank you!
The example given in the answer to the question referred to is slightly out of date. A Solver() will pick a suitable tactic to solve the problem, and it appears that it picks a different one now. We can still get that behavior by using a SimpleSolver() (at a possibly significant performance loss). Here's an updated example:
from z3 import *
x, y = Bools('x y')
s = SimpleSolver()
s.set(auto_config=False,relevancy=2)
s.add(Or(x, y))
print s.check()
print s.model()
Note that the (check-sat) command will not execute the same tactic as the SimpleSolver(); to get the same behavior when solving SMT2 files, we need to use the smt tactic, i.e., use (check-sat-using smt). In many cases it will be beneficial to additionally run the simplifier on the problem first which we can achieve by constructing a custom tactic, e.g., (check-sat-using (then simplify smt))