Unexpected unknown with ill-defined function definition axiom - z3

The SMT program further down encodes the (ill-defined) function definition ∀ s · wild(s) = 1 + wild(s) in a slightly roundabout way (applying Dafny's "limited functions" encoding of recursive functions) and then attempts to prove that wild(emp) = 1 + wild(emp). However, Z3 4.6.0 (and also a recent 4.7.0 nightly) unexpectedly yield unknown instead of unsat.
(set-option :auto_config false) ;; true -> no change in behaviour
(set-option :smt.mbqi false) ;; true -> no change in behaviour
(declare-sort Snap) ;; In the full example, this is ...
(declare-const emp Snap) ;; ... declared using declare-datatypes
(declare-fun wild (Snap) Int)
(declare-fun wild%limited (Snap) Int)
(assert (forall ((s Snap)) (! ;; AX-1
(= (wild%limited s) (wild s))
:pattern ((wild s))
)))
(assert (forall ((s Snap)) (! ;; AX-2
(=
(wild s)
(+ 1 (wild%limited emp)))
:pattern ((wild s))
)))
(push) ;; Full examples uses incremental mode
(assert
(not
(=
(wild emp)
(+ 1 (wild emp)))))
(check-sat) ;; UNKNOWN --- but why?
(pop)
Given my understanding of Z3 and triggers, I would expect the following proof steps to happen:
¬(wild(emp) = 1 + wild(emp)) // Source assertion
≡ ¬(1 + wild%limited(emp) = 1 + wild(emp)) // By AX-2
≡ ¬(1 + wild%limited(emp) = 1 + wild%limited(emp)) // By AX-1
≡ ¬(true) // Done: UNSAT
But that doesn't appear to happen. My guess is that the axioms aren't instantiated — and indeed, get-info :all-statistics reports no quantified instantiation.
Can anybody shed some light on this?

the last assert simplifies to "true", so there is no ground occurrence of (wild emp) that would trigger quantifier instantiation.

Related

Quantifier-free way to specify constructor of Z3 sum type

Let's say I have a simple sum type in Z3 with several constructors of different arities:
(declare-datatypes ()
((Foo bar
(baz (unbaz String))
(quux (unquux1 String) (unquux2 Int)))))
How can I assert that I know value of type Foo is a quux? I could introduce an existential over quux1 and quux2 but I'm wary of introducing seemingly unnecessary quantifiers. Is there a better way to assert this?
Existentials are rather harmless when assumed because the existentially quantified variables are directly instantiated and thus only entail additional symbols. That is, the second half of the snippet
(declare-datatypes ()
((Foo bar
(baz (unbaz String))
(quux (unquux1 String) (unquux2 Int)))))
(declare-const f Foo)
(assert (exists ((s String) (i Int)) ;; Let f be a quux
(= f (quux s i))
))
(assert (= f (baz "test"))) ;; Also let f be a baz
(check-sat) ;; UNSAT - as expected
is equivalent to
...
(declare-const _s String)
(declare-const _i Int)
(assert (= f (quux _s _i))) ;; Let f be a quux
(assert (= f (baz "test"))) ;; Also let f be a baz
(check-sat) ;; UNSAT - as expected
If you're wary of existentials but not of foralls, then you could tag Foo values by axiomatising a mapping from Foo constructors to distinct tags:
(set-option :smt.mbqi false)
(declare-datatypes ()
((Foo bar
(baz (unbaz String))
(quux (unquux1 String) (unquux2 Int)))))
;; Declare a finite sort Foo_tag with three distinct elements
(declare-datatypes () ((Foo_tag Foo_tag.bar Foo_tag.baz Foo_tag.quux)))
;; Alternatively, three distinct elements from an infinite sort such
;; as Int can be used. Either by choosing distinct but unspecified
;; values, as done below, or by directly choosing concrete values,
;; e.g. 1, 2, 3.
; (define-sort Foo_tag () Int)
; (declare-const Foo_tag.bar Foo_tag)
; (declare-const Foo_tag.baz Foo_tag)
; (declare-const Foo_tag.quux Foo_tag)
; (assert (distinct Foo_tag.bar Foo_tag.baz Foo_tag.quux))
;; Tagging function
(declare-fun tag_of (Foo) Foo_tag)
;; Tagging axiom for bar ...
(assert (= (tag_of bar) Foo_tag.bar))
;; ... baz ...
(assert (forall ((s String)) (!
(= (tag_of (baz s)) Foo_tag.baz)
:pattern ((baz s))
)))
;; ... and quux
(assert (forall ((s String) (i Int)) (!
(= (tag_of (quux s i)) Foo_tag.quux)
:pattern ((quux s i))
)))
;; Let's do some testing
(declare-const f Foo)
(assert (= (tag_of f) Foo_tag.quux)) ;; Tag f as a quux
(push)
(assert (= f bar))
(check-sat) ;; UNSAT - as expected
(pop)
(push)
(assert (= f (baz "test")))
(check-sat) ;; UNSAT - as expected
(pop)

Guiding z3's proof search

I'm trying to get z3 to work (most of the time) for very simple non-linear integer arithmetic problems. Unfortunately, I've hit a bit of a wall with exponentiation. I want to be able handle problems like x^{a+b+2} = (x * x * x^{a} * x{b}). I only need to handle non-negative exponents.
I tried redefining exponentiation as a recursive function (so that it's just allowed to return 1 for any non-positive exponent) and using a pattern to facilitate z3 inferring that x^{a+b} = x^{a} * x^{b}, but it doesn't seem to work - I'm still timing out.
(define-fun-rec pow ((x!1 Int) (x!2 Int)) Int
(if (<= x!2 0) 1 (* x!1 (pow x!1 (- x!2 1)))))
; split +
(assert (forall ((a Int) (b Int) (c Int))
(! (=>
(and (>= b 0) (>= c 0))
(= (pow a (+ b c)) (* (pow a c) (pow a b))))
:pattern ((pow a (+ b c))))))
; small cases
(assert (forall ((a Int)) (= 1 (pow a 0))))
(assert (forall ((a Int)) (= a (pow a 1))))
(assert (forall ((a Int)) (= (* a a) (pow a 2))))
(assert (forall ((a Int)) (= (* a a a) (pow a 3))))
; Our problem
(declare-const x Int)
(declare-const i Int)
(assert (>= i 0))
; This should be provably unsat, by splitting and the small case for 2
(assert (not (= (* (* x x) (pow x i)) (pow x (+ i 2)))))
(check-sat) ;times out
Am I using patterns incorrectly, is there a way to give stronger hints to the proof search, or an easier way to do achieve what I want?
Pattern (also called triggers) may only contain uninterpreted functions. Since + is an interpreted function, you essentially provide an invalid pattern, in which case virtually anything can happen.
As a first step, I disabled Z3's auto-configuration feature and also MBQI-based quantifier instantiation:
(set-option :auto_config false)
(set-option :smt.mbqi false)
Next, I introduced an uninterpreted plus function and replaced each application of + by plus. That sufficed to make your assertion verify (i.e. yield unsat). You can of course also axiomatise plus in terms of +, i.e.
(declare-fun plus (Int Int) Int)
(assert (forall ((a Int) (b Int))
(! (= (plus a b) (+ a b))
:pattern ((plus a b)))))
but your assertion already verifies without the definitional axioms for plus.

Is this a bug in z3 int.to.str?

I'm posting this z3 issue here too in case any z3 users have somehow encountered it. We are experimenting a little with the semantics of int.to.str, and we found this weird behavior that looks like a bug. Here is the first query that works OK:
(declare-const s String)
(declare-const i Int)
(assert (< i -2))
(assert (= s (int.to.str i)))
(assert (< 0 (str.len s )))
(check-sat)
(get-value (s i))
And the result:
sat
((s "-11")
(i (- 11)))
When I change the '<' sign to '=' I get an unsat response:
(declare-const s String)
(declare-const i Int)
(assert (= i -2))
(assert (= s (int.to.str i)))
(assert (< 0 (str.len s )))
(check-sat)
(get-value (s i))
Here is the result I get:
unsat
(error "line 10 column 16: model is not available")
Am I missing something here? Thanks!
This is indeed a bug, as described here.

Ground terms inside quantifiers seem not to be used for e-matching (until quantifier is instantiated once)

When choosing instantiations for quantified assumptions (without mbqi), Z3 searches its e-graph of ground terms (modulo equalities) for possible matches. I understood that this e-graph consists of the ground terms present in the original problem, plus those which are formed during the proof search. However, it seems that a ground term mentioned under a quantifier is not used as a candidate for matching, until the surrounding quantifier itself is instantiated.
For example, if I write:
(assert
(forall ((n Int) ) (!
(and (f 0) (g n))
:pattern ((g n))
)))
(assert (forall ((n Int) ) (!
(not (= (h n) n))
:pattern ((f n))
)))
(assert (= (h 0) 0))
(check-sat)
Then Z3 reports unknown. Whereas if I pull the conjunction of (f 0) out of the forall (as in the full example below) then Z3 reports unsat (and if I track statistics, I can see that the second quantifier is instantiated.
I find this behaviour a bit confusing, and I wondered whether or not it is intended? If so, what is the rationale behind this choice? I think it could be affecting some of our axiomatisations (but need to debug the details further).
By the way, we tried using a let expression to pull the ground term out of the quantifier, but (probably because let expressions are just substituted back in again?) we didn't see a difference.
The full version of the example is here:
(set-option :print-success true) ; Boogie: false
(set-option :global-decls true) ; Boogie: default
(set-option :auto_config false) ; Usually a good idea
(set-option :smt.mbqi false)
(set-option :model.v2 true)
(set-option :smt.phase_selection 0)
(set-option :smt.restart_strategy 0)
(set-option :smt.restart_factor |1.5|)
(set-option :smt.arith.random_initial_value true)
(set-option :smt.case_split 5)
(set-option :smt.delay_units true)
(set-option :smt.delay_units_threshold 16)
(set-option :nnf.sk_hack true)
(set-option :smt.qi.eager_threshold 100)
(set-option :smt.qi.cost "(+ weight generation)")
(set-option :type_check true)
(set-option :smt.bv.reflect true)
(set-option :smt.qi.profile true)
(declare-fun f (Int) Bool)
(declare-fun g (Int) Bool)
(declare-fun h (Int) Int)
(assert
(forall ((n Int) ) (!
(and (f 0) (g n))
:pattern ((g n))
)))
; this version of the above axiom lets us get "unsat"
;(assert
; (and (f 0) (forall ((n Int) ) (!
; (g n)
; :pattern ((g n)))
; )))
(assert (forall ((n Int) ) (!
(not (= (h n) n))
:pattern ((f n))
)))
(assert (= (h 0) 0))
(check-sat)
(get-info :all-statistics)
FWIW, given what I know about Z3's quantifier instantiation mechanisms these days, it's pretty clear that this behaviour is expected; ground terms that happen to be under a quantifier won't make it into the E-graph until the quantifier is instantiated.

Possible bug with Z3: Z3 is not able to prove a theorem in Topology

I am trying to prove with Z3 the theorem in general topology given at
TPTP-Topology
I am translating the code given there using the following Z3-SMT-LIB code
;; File : TOP001-2 : TPTP v6.0.0. Released v1.0.0.
;; Domain : Topology
;; Problem : Topology generated by a basis forms a topological space, part 1
(declare-sort S)
(declare-sort Q)
(declare-sort P)
(declare-fun elemcoll (S Q) Bool)
(declare-fun elemset (P S) Bool)
(declare-fun unionmemb (Q) S)
(declare-fun f1 (Q P) S)
(declare-fun f11 (Q S) P)
(declare-fun basis (S Q) Bool)
(declare-fun Subset (S S) Bool)
(declare-fun topbasis (Q) Q)
;; union of members axiom 1.
(assert (forall ((U P) (Vf Q)) (or (not (elemset U (unionmemb Vf)))
(elemset U (f1 Vf U) ) ) ))
;; union of members axiom 2.
(assert (forall ((U P) (Vf Q)) (or (not (elemset U (unionmemb Vf)))
(elemcoll (f1 Vf U) Vf ) ) ))
;; basis for topology, axiom 28
(assert (forall ((X S) (Vf Q)) (or (not (basis X Vf)) (= (unionmemb Vf) X ) ) ))
;; Topology generated by a basis, axiom 40.
(assert (forall ((Vf Q) (U S)) (or (elemcoll U (topbasis Vf))
(elemset (f11 Vf U) U)) ))
;; Set theory, axiom 7.
(assert (forall ((X S) (Y Q)) (or (not (elemcoll X Y)) (Subset X (unionmemb Y) ) ) ))
;; Set theory, axiom 8.
(assert (forall ((X S) (Y S) (U P)) (or (not (Subset X Y)) (not (elemset U X))
(elemset U Y) )))
;; Set theory, axiom 9.
(assert (forall ((X S)) (Subset X X ) ))
;; Set theory, axiom 10.
(assert (forall ((X S) (Y S) (Z S)) (or (not (= X Y)) (not (Subset Z X)) (Subset Z Y) ) ))
;; Set theory, axiom 11.
(assert (forall ((X S) (Y S) (Z S)) (or (not (= X Y)) (not (Subset X Z)) (Subset Y Z) ) ))
(check-sat)
(push)
(declare-fun cx () S)
(declare-fun f () Q)
(assert (basis cx f))
(assert (not (elemcoll cx (topbasis f))))
(check-sat)
(pop)
(push)
(assert (basis cx f))
(assert (elemcoll cx (topbasis f)))
(check-sat)
(pop)
The corresponding output is
sat
sat
sat
Please run this example online here
The first sat is correct; but the second sat is wrong, it must be unsat. In other words, Z3 is saying that the theorem and its negation are true simultaneously.
Please let me know what happens in this case. Many thanks. All the best.
It is possible that both a formula and the negation of the formula is consistent with respect to a background theory T. In particular, when T is not complete, then there are sentences that are neither consequences of T nor inconsistent with T. In your case the theory T is the set of topology axioms.
You can use the command (get-model) to obtain a model that satisfies the axioms and the sentence.

Resources