Disabling introduction of Skolem function in linear arithmetic proof - z3

Given the following SMT2 script:
(set-option :produce-proofs true)
(set-logic AUFLIRA)
(declare-sort Complex$ 0)
(declare-fun r$ () Real)
(declare-fun s$ () Complex$)
(declare-fun re$ (Complex$) Real)
(declare-fun norm$ (Complex$) Real)
(assert (! (not (=> (and (forall ((?v0 Complex$)) (<= (ite (< (re$ ?v0) 0.0) (- (re$ ?v0)) (re$ ?v0)) (norm$ ?v0))) (<= (norm$ s$) r$)) (<= (ite (< (re$ s$) 0.0) (- (re$ s$)) (re$ s$)) (+ r$ 1.0)))) :named a0))
(check-sat)
(get-proof)
Z3 (unstable version) produces a proof that contains a Skolem function "norm$0". This function is introduced in a rewrite step:
(ALL v0. (if 0 <= Re v0 then Re v0 else - 1 * Re v0) <= cmod v0) =
((ALL v0. cmod v0 = (if 0 <= Re v0 then Re v0 else - 1 * Re v0) + norm_0 v0) & (ALL v0. 0 <= norm_0 v0))
Can this behavior be suppressed by a command-line switch? That is, is there an option such that Z3 produces a proof without such a Skolem function? This should, in principle, be possible, as Z3 version 3.2 finds a proof that does not require a Skolem function.

The skolem constant is introduced by the MBQI (model-based quantifier instantiation) module. This is probably the "new part" that makes you see the different behavior.
MBQI is much more powerful than the pattern-based quantifier instantiation.
For your example, however, the pattern-based quantifier instantiation strategy works. So you can try this.
In other words run Z3 as follows to suppress MBQI:
z3 skolem-example.smt2 smt.mbqi=false auto-config=false

Related

Should naming an assertion affect the satisfiability of a check in Z3?

We have encountered constraints for which Z3 returns sat but if we then add a certain named assertion then Z3 returns unknown. One example is:
(declare-fun a () Real)
(declare-fun b () Bool)
(assert (and (<= 0.1 a) (<= a 10.0)))
(assert (= b (= 1.0 (/ 1.0 a))))
(check-sat)
Z3 reports that this is satisfiable as expected. We can additionally assert that b is either true or false, both of which are satisfiable as expected. However, if we use a named assertion for the value of b then the result of the satisfiability check can become unknown depending on the value. With the value true, Z3 still returns sat:
(set-option :produce-unsat-cores true)
(declare-fun a () Real)
(declare-fun b () Bool)
(assert (and (<= 0.1 a) (<= a 10.0)))
(assert (= b (= 1.0 (/ 1.0 a))))
(assert (! (= b true) :named c))
(check-sat)
Using the value false, Z3 returns unknown:
(set-option :produce-unsat-cores true)
(declare-fun a () Real)
(declare-fun b () Bool)
(assert (and (<= 0.1 a) (<= a 10.0)))
(assert (= b (= 1.0 (/ 1.0 a))))
(assert (! (= b false) :named c))
(check-sat)
Checking the reason for the unknown result (with (get-info :reason-unknown)) returns (incomplete (theory arithmetic)).
It appears that z3 is just picking the wrong tactic in the latter case. Replace your check-sat call with:
(check-sat-using qfnra-nlsat)
And z3 says sat in both cases.
Why z3 ends up picking a different tactic is a difficult question to answer; I'd recommend filing this as a ticket at their github site; they might be missing a heuristic.
Side note: I found this by running both cases with -v:10 and skimming through the verbose output. It's not a bad way to see what z3 is doing for short-enough benchmarks.

Z3 support for exponentials

I'm new to Z3 and I'm trying to understand how it works, and what it can and cannot do. I know that Z3 has at least some support for exponentials through the power (^) operator (see Z3py returns unknown for equation using pow() function, How to represent logarithmic formula in z3py, and Use Z3 and SMT-LIB to define sqrt function with a real number). What I'm unclear on is how extensive this support is, and what kind of inferences z3 can make about exponentials.
Here's a simple example involving exponentials which z3 can analyze. We define an exponential function, and then ask it to verify that exp(0) == 1:
(define-fun exp ((x Real)) Real
(^ 2.718281828459045 x))
(declare-fun x1 () Real)
(declare-fun y1 () Real)
(assert (= y1 (exp x1)))
(assert (not (=> (= x1 0.0) (= y1 1.0))))
(check-sat)
(exit)
Z3 returns unsat, as expected. On the other hand, here's a simple example which Z3 can't analyze:
(define-fun exp ((x Real)) Real
(^ 2.718281828459045 x))
(declare-fun x1 () Real)
(declare-fun y1 () Real)
(assert (= y1 (exp x1)))
(assert (not (< y1 0.0)))
(check-sat)
(exit)
This should be satisfiable, since literally any value for x1 would give y1 > 0. However, Z3 returns unknown. Naively I might have expected that Z3 would be able to analyze this, given that it could analyze the first example.
I realize this question is a bit broad, but: can anyone give me any insight into how Z3 handles exponentials, and (more specifically) why it can solve the first example I gave but not the second?
It is hard to say in general, since non-linear solving is challenging, but the case you presented is actually not so mysterious. You wrote:
(assert (= y (exp x)))
(assert (not (=> (= x 0) (= y 1))))
Z3 is going to simplify the second assertion, yielding:
(assert (= y (exp x)))
(assert (= x 0))
(assert (not (= y 1)))
Then it will propagate the first equality, yielding:
(assert (= y (exp 0)))
(assert (not (= y 1)))
Now when exp is expanded, you have a case of constant^constant, which Z3 can handle (for integer exponents, etc).
For the second case, you are asking it a very very basic question about variable exponents, and Z3 immediately barfs. That's not too odd, since so many questions about variable exponents are either known uncomputable or unknown but hard.

Minor change results in "unknown" - related to quantifier preprocessing?

The following "minimal" program, distilled from a much larger program, is expected to yield unsat (and does). However, uncommenting the additional conjunct in the quantifier AX-1 changes the result to unknown (in Z3 4.5.0 x64 on Windows 10).
(set-option :auto_config false)
(set-option :smt.mbqi false)
(declare-fun foo (Int) Bool)
(declare-const k Real)
(assert (forall ((i Int)) (!
(and
(< 0.0 k)
; (implies (<= 0 i) (< 0.0 k)) ;;; ---- uncomment this line ----
)
:pattern ((foo i))
:qid |AX-1|)))
(assert (forall ((i Int)) (!
(foo i)
:pattern ((foo i))
:qid |AX-2|)))
(declare-const j Int)
(assert (< j 0))
; (push) ;;; doesn't make a difference
(assert (not
(ite
(foo j)
(< 0.0 k)
false)))
; (set-option :smt.qi.profile true)
(check-sat)
; (get-info :all-statistics)
; (pop)
The quantifier instantiation statistics show that AX-2 is instantiated in both cases, but AX-1 is only instantiated if the additional conjunct is not included. My assumption is that in the latter case, Z3 eliminates the quantifier since the quantified variable doesn't occur in the body.
However, I find it surprising that the version with the additional conjunct - in which Z3 presumably doesn't eliminate the quantifier - yields unknown, since the trigger for the quantifier ((foo j)) should be available.
Question: Is this behaviour expected - and if so, why?
Confirmed as a bug, see Github issue 935.

y=1/x, x=0 satisfiable in the reals?

In SMT-LIB:
(declare-fun y () Real)
(declare-fun x () Real)
(assert (= 0.0 x))
(assert (= y (/ 1.0 x)))
(check-sat)
Should this model be SAT or UNSAT?
In SMT-LIB 2.0 and 2.5, all functions are total, so this example is SAT in SMT-LIB. Both Z3 and CVC4 do indeed return SAT for the example in the question.
I found this counter-intuitive. I think it would be mathematically more well justified to say that y=1/x, x=0 is unsatisfiable in the reals. In Mathematica, the equivalent code returns an empty list, indicating that no solution exists, i.e., FindInstance[Element[x, Reals] && Element[y, Reals] && x == 0 && y == 1/x, {x, y}] returns {}
Nonetheless, / is defined this way in SMT-LIB. So as far as Z3 or CVC4 are concerned, this problem is SAT.

Using Z3 QFNRA tactic with datatypes: interaction or inlining

In Non-linear arithmetic and uninterpreted functions, Leonardo de Moura states that the qfnra-nlsat tactic hasn't been fully integrated with the rest of Z3 yet. I thought that the situation has changed in two years, but apparently the integration is still not very complete.
In the example below, I use datatypes purely for "software engineering" purposes: to organize my data into records. Even though there are no uninterpreted functions, Z3 still fails to give me a solution:
(declare-datatypes () (
(Point (point (point-x Real) (point-y Real)))
(Line (line (line-a Real) (line-b Real) (line-c Real)))))
(define-fun point-line-subst ((p Point) (l Line)) Real
(+ (* (line-a l) (point-x p)) (* (line-b l) (point-y p)) (line-c l)))
(declare-const p Point)
(declare-const l Line)
(assert (> (point-y p) 20.0))
(assert (= 0.0 (point-line-subst p l)))
(check-sat-using qfnra-nlsat)
(get-model)
> unknown
(model
)
However, if I manually inline all the functions, Z3 finds a model instantly:
(declare-const x Real)
(declare-const y Real)
(declare-const a Real)
(declare-const b Real)
(declare-const c Real)
(assert (> y 20.0))
(assert (= 0.0 (+ (* a x) (* b y) c)))
(check-sat-using qfnra-nlsat)
(get-model)
> sat
(model
(define-fun y () Real
21.0)
(define-fun a () Real
0.0)
(define-fun x () Real
0.0)
(define-fun b () Real
0.0)
(define-fun c () Real
0.0)
)
My question is, is there a way to perform such an inlining automatically? I'm fine with either one of these workflows:
Launch Z3 with a tactic that says "Inline first, then apply qfnra-nlsat. I haven't found a way to do so, but maybe I wasn't looking well enough.
Launch Z3 using some version of simplify to do the inlining. Launch Z3 the second time on the result of the first invocation (the inlined version).
In other words, how to make qfnra-nlsat work with tuples?
Thank you!
That's correct, the NLSAT solver is still not integrated with the other theories. At the moment, we can only use it if we eliminate all datatypes (or elements of other theories) before running it. I believe there is no useful existing tactic inside of Z3 at the moment though, so this would have to be done beforehand. In general it's not hard to compose tactics, e.g., like this:
(check-sat-using (and-then simplify qfnra-nlsat))
but the simplifier is not strong enough to eliminate the datatype constants in this problem. (The respective implementation files are datatype_rewriter.cpp and datatype_simplifier_plugin.cpp.)

Resources