How can I optimize an smtlib query reasoning about partial orders? - z3

I am working on trying to do type inference in Z3 for a compiler and the compiler generates a Z3 query over a fully defined partial order, and then attempts to find the maximal type for all the variables such that a fixed set of constraints is satisfied.
The query looks something like this:
(declare-datatypes () ((Sort
(|SortInt| )
(|SortExp| )
(|SortKItem| )
(|SortKLabel| )
(|SortK| )
)))
(define-fun <=Sort ((s1 Sort) (s2 Sort)) Bool (or
(and (= s1 |SortKItem|) (= s2 |SortK|))
(and (= s1 |SortExp|) (= s2 |SortKItem|))
(and (= s1 |SortExp|) (= s2 |SortK|))
(and (= s1 |SortInt|) (= s2 |SortKItem|))
(and (= s1 |SortInt|) (= s2 |SortExp|))
(and (= s1 |SortInt|) (= s2 |SortK|))
(and (= s1 |SortKItem|) (= s2 |SortKItem|))
(and (= s1 |SortKLabel|) (= s2 |SortKLabel|))
(and (= s1 |SortExp|) (= s2 |SortExp|))
(and (= s1 |SortK|) (= s2 |SortK|))
(and (= s1 |SortInt|) (= s2 |SortInt|))
))
(define-fun <=SortSyntax ((s1 Sort) (s2 Sort)) Bool (or
(and (= s1 |SortKItem|) (= s2 |SortK|))
(and (= s1 |SortExp|) (= s2 |SortKItem|))
(and (= s1 |SortExp|) (= s2 |SortK|))
(and (= s1 |SortInt|) (= s2 |SortKItem|))
(and (= s1 |SortInt|) (= s2 |SortExp|))
(and (= s1 |SortInt|) (= s2 |SortK|))
(and (= s1 |SortKItem|) (= s2 |SortKItem|))
(and (= s1 |SortKLabel|) (= s2 |SortKLabel|))
(and (= s1 |SortExp|) (= s2 |SortExp|))
(and (= s1 |SortK|) (= s2 |SortK|))
(and (= s1 |SortInt|) (= s2 |SortInt|))
))
(push)
(declare-const |FreshVarSort_6_8_6_36_#KRewrite| Sort)
(declare-const |VarA| Sort)
(declare-const |VarB| Sort)
(declare-const |VarC| Sort)
(declare-datatypes() ((SolutionVariables (SolVars (|Sol_VarA| Sort) (|Sol_VarB| Sort) (|Sol_VarC| Sort) ))))
(declare-datatypes() ((Solution (Sol (vars SolutionVariables) (|Sol_FreshVarSort_6_8_6_36_#KRewrite| Sort) ))))
(define-fun theSolution () Solution (Sol (SolVars |VarA| |VarB| |VarC| ) |FreshVarSort_6_8_6_36_#KRewrite| ))
(define-fun lt ((s1 Solution) (s2 Solution)) Bool (and true (<=SortSyntax (|Sol_VarA| (vars s1)) (|Sol_VarA| (vars s2))) (<=SortSyntax (|Sol_VarB| (vars s1)) (|Sol_VarB| (vars s2))) (<=SortSyntax (|Sol_VarC| (vars s1)) (|Sol_VarC| (vars s2))) (distinct (vars s1) (vars s2))))
(assert (and true (distinct (|Sol_FreshVarSort_6_8_6_36_#KRewrite| theSolution) |SortKLabel|) ))
(define-fun |constraint4_SortExp| ((s Solution)) Bool (and true (<=Sort (|Sol_VarA| (vars s)) |SortExp|) ))
(define-fun |constraint6_SortExp| ((s Solution)) Bool (and true (<=Sort (|Sol_VarB| (vars s)) |SortExp|) ))
(define-fun |constraint3_SortExp| ((s Solution)) Bool (and true (<=Sort |SortExp| |SortExp|) (|constraint4_SortExp| s) (|constraint6_SortExp| s) ))
(define-fun |constraint8_SortExp| ((s Solution)) Bool (and true (<=Sort (|Sol_VarC| (vars s)) |SortExp|) ))
(define-fun |constraint2_(Sol_FreshVarSort_6_8_6_36_#KRewrite s)| ((s Solution)) Bool (and true (<=Sort |SortExp| (|Sol_FreshVarSort_6_8_6_36_#KRewrite| s)) (|constraint3_SortExp| s) (|constraint8_SortExp| s) ))
(define-fun |constraint12_SortInt| ((s Solution)) Bool (and true (<=Sort (|Sol_VarA| (vars s)) |SortInt|) ))
(define-fun |constraint14_SortInt| ((s Solution)) Bool (and true (<=Sort (|Sol_VarB| (vars s)) |SortInt|) ))
(define-fun |constraint11_SortInt| ((s Solution)) Bool (and true (<=Sort |SortInt| |SortInt|) (|constraint12_SortInt| s) (|constraint14_SortInt| s) ))
(define-fun |constraint16_SortInt| ((s Solution)) Bool (and true (<=Sort (|Sol_VarC| (vars s)) |SortInt|) ))
(define-fun |constraint10_(Sol_FreshVarSort_6_8_6_36_#KRewrite s)| ((s Solution)) Bool (and true (<=Sort |SortInt| (|Sol_FreshVarSort_6_8_6_36_#KRewrite| s)) (|constraint11_SortInt| s) (|constraint16_SortInt| s) ))
(define-fun |constraint1_Sort#RuleBody| ((s Solution)) Bool (and true (= (|Sol_FreshVarSort_6_8_6_36_#KRewrite| s) |SortK|) (|constraint2_(Sol_FreshVarSort_6_8_6_36_#KRewrite s)| s) (|constraint10_(Sol_FreshVarSort_6_8_6_36_#KRewrite s)| s) ))
(define-fun |constraint0_Sort#RuleContent| ((s Solution)) Bool (and true (|constraint1_Sort#RuleBody| s) ))
(define-fun |constraint21_SortExp| ((s Solution)) Bool (and true (<=Sort |SortExp| |SortExp|) (|constraint6_SortExp| s) (|constraint8_SortExp| s) ))
(define-fun |constraint20_(Sol_FreshVarSort_6_8_6_36_#KRewrite s)| ((s Solution)) Bool (and true (<=Sort |SortExp| (|Sol_FreshVarSort_6_8_6_36_#KRewrite| s)) (|constraint4_SortExp| s) (|constraint21_SortExp| s) ))
(define-fun |constraint19_Sort#RuleBody| ((s Solution)) Bool (and true (= (|Sol_FreshVarSort_6_8_6_36_#KRewrite| s) |SortK|) (|constraint20_(Sol_FreshVarSort_6_8_6_36_#KRewrite s)| s) (|constraint10_(Sol_FreshVarSort_6_8_6_36_#KRewrite s)| s) ))
(define-fun |constraint18_Sort#RuleContent| ((s Solution)) Bool (and true (|constraint19_Sort#RuleBody| s) ))
(define-fun amb0 ((s Solution)) Bool (or (|constraint0_Sort#RuleContent| s) (|constraint18_Sort#RuleContent| s) ))
(assert (amb0 theSolution))
(assert (not (exists ((s Solution)) (and (lt theSolution s) (amb0 s) (distinct (|Sol_FreshVarSort_6_8_6_36_#KRewrite| s) |SortKLabel|) ))))
(check-sat)
(get-model)
(assert (or false (distinct |VarA| |SortInt|) (distinct |VarB| |SortInt|) (distinct |VarC| |SortInt|) ))
(check-sat)
(pop)
This is a smaller query and runs fine. The problem is, when the number of sorts and variables and disjunctions in the constraints increases, z3 starts to take a significant amount of time to solve the constraints.
What I am wondering is, are there any specific tactics I could use or ways I could refactor the way the query is generated so queries of this type are faster on complex examples?
I can provide a larger query if it would be useful, but it's several hundred kilobytes.

Related

Quantifier patterns in Z3

I am having trouble attempting to prove this fairly simple Z3 query.
(set-option :smt.auto-config false) ; disable automatic self configuration
(set-option :smt.mbqi false) ; disable model-based quantifier instantiation
(declare-fun sum (Int) Int)
(declare-fun list () (Array Int Int))
(declare-fun i0 () Int)
(declare-fun s0 () Int)
(declare-fun i1 () Int)
(declare-fun s1 () Int)
(assert (forall ((n Int))
(! (or (not (<= n 0)) (= (sum n) 0))
:pattern ((sum n)))))
(assert (forall ((n Int))
(! (let ((a1 (= (sum n)
(+ (select list (- n 1))
(sum (- n 1))))))
(or (<= n 0) a1))
:pattern ((sum n)))))
(assert (>= i0 0))
(assert (= s0 (sum i0)))
(assert (= i1 (+ 1 i0)))
(assert (= s1 (+ 1 s0 (select list i0))))
(assert (not (= s1 (sum i1))))
(check-sat)
Seems to me that the final assertion should instantiate the second quantified statement for i1 while the assert involving s0 should instantiate the quantifiers for i0. These two should should easily lead to UNSAT.
However, Z3 returns unknown. What am I missing?
Never mind, there was an silly error in my query.
This code:
(assert (= s1 (+ 1 s0 (select list i0))))
should have been:
(assert (= s1 (+ s0 (select list i0))))

str.suffix in z3 API is unknown

I'm trying to use suffix API function in Z3 in the following script,
but Z3 complains it does not know str.suffix. Since I see it in the API
here I guess it exists but only called different (?)
Thanks!
(declare-const s String)
(declare-const s00 String)
(declare-const s1 String)
(declare-const s2 String)
(declare-const i Int)
(assert (= s "X2a2##aDD\x00444ppa800"))
(assert (= s00 (str.substr s 0 (str.indexof s "\x00" 0))))
(assert (str.suffixof s1 s00))
(assert (str.suffixof s2 s1))
(assert (= (str.len s1) (+ (str.len s2) 1)))
(assert (or (and (str.contains s00 "a")
(str.contains s1 "a"))
(not (str.contains s00 "a"))))
(assert (not (str.contains s2 "a")))
(assert (= i (ite (not (str.contains s00 "a")) -1
(- (str.len s00) (str.len s1)))))
(check-sat)
(get-value (s s00 s1 s2 i))
EDIT:
The script was fixed according to the answer. Here is the output from z3:
sat
((s "X2a2##aDD\x00444ppa800")
(s00 "X2a2##aDD")
(s1 "aDD")
(s2 "DD")
(i 6))
The correct call is: str.suffixof
(PS. Your file still doesn't load even with that fix since it has other issues; but that's besides the point of this question.)

Z3 is the only system that is able to refute REL051+1.p?

The problem in relational algebra REL051+1.p reads
File : REL051+1 : TPTP v6.1.0. Released v4.0.0.
% Domain : Relation Algebra
% Problem : Dense linear ordering
Using TPTP syntax with fof the corresponding code is
fof(f01,axiom,(
! [A] : o(A,A) )).
fof(f02,axiom,(
! [A,B] :
( ( A != B
& o(A,B) )
=> ~ o(B,A) ) )).
fof(f03,axiom,(
! [A,B,C] :
( ( o(A,B)
& o(B,C) )
=> o(A,C) ) )).
fof(f04,axiom,(
! [A,B] :
( ( A != B
& o(A,B) )
=> ( o(A,f(A,B))
& o(f(A,B),B) ) ) )).
fof(f05,axiom,(
! [A,B] :
( f(A,B) != A
& f(A,B) != B ) )).
fof(f06,axiom,(
! [A,B] :
( o(A,B)
| o(B,A) ) )).
As you can see in TPTP all ATPs are unable to prove such problem.
This theorem was refuted with Z3 using the following SMT-LIB
(declare-sort S)
(declare-fun o (S S) Bool)
(declare-fun f (S S) S)
(assert (forall ((A S)) (o A A) ))
(assert (forall ((A S) (B S)) (implies (and (distinct A B) (o A B))
(not (o B A))) ) )
(assert (forall ((A S) (B S) (C S)) (implies (and (o A B) (o B C))
(o A C)) ) )
(assert (forall ((A S) (B S)) (implies (and (distinct A B) (o A B))
(and (o A (f A B)) (o (f A B) B))) ) )
(declare-fun B () S)
(assert (forall ((A S)) (and (distinct (f A B) A)
(distinct (f A B) B)) ) )
(assert (not (forall ((A S)) (or (o A B) (o B A)) ) ))
(check-sat)
(get-model)
and the corresponding output is
sat
(model
;; universe for S:
;; S!val!0 S!val!3 S!val!1 S!val!2
;; -----------
;; definitions for universe elements:
(declare-fun S!val!0 () S)
(declare-fun S!val!3 () S)
(declare-fun S!val!1 () S)
(declare-fun S!val!2 () S)
;; cardinality constraint:
(forall ((x S)) (or (= x S!val!0) (= x S!val!3) (= x S!val!1) (= x S!val!2)))
;; -----------
(define-fun B () S
S!val!1)
(define-fun A!0 () S
S!val!0)
(define-fun f!47 ((x!1 S) (x!2 S)) S
(ite (and (= x!1 S!val!2) (= x!2 S!val!1)) S!val!3
S!val!2))
(define-fun k!46 ((x!1 S)) S
(ite (= x!1 S!val!2) S!val!2
(ite (= x!1 S!val!0) S!val!0
(ite (= x!1 S!val!3) S!val!3
S!val!1))))
(define-fun f ((x!1 S) (x!2 S)) S
(f!47 (k!46 x!1) (k!46 x!2)))
(define-fun o!48 ((x!1 S) (x!2 S)) Bool
(ite (and (= x!1 S!val!1) (= x!2 S!val!1)) true
(ite (and (= x!1 S!val!0) (= x!2 S!val!0)) true
(ite (and (= x!1 S!val!2) (= x!2 S!val!2)) true
(ite (and (= x!1 S!val!3) (= x!2 S!val!3)) true
false)))))
(define-fun o ((x!1 S) (x!2 S)) Bool
(o!48 (k!46 x!1) (k!46 x!2)))
)
Please run this example online here
My question is: This refutation with Z3 is correct?

How to produce the model for partial orders?

I am trying to use Z3 to produce a model for a set of SAT assertions describing a partial order theory. I tried the subtype example in Z3 guide but it seems I cannot get a concrete model. Is there a way that Z3 can produce a model that describes the orders among elements and satisfies all assertions I made?
For example, following are the constraints for "subtype". Is it possible that Z3 may produce a model like "int-type *<* real-type *<* complex-type *<* obj-type *<* root-type" and "string-type *<* obj-type *<* root-type" (if I use "*<*" to denote subtype relation)?
(set-option :produce-models true)
(declare-sort Type)
(declare-fun subtype (Type Type) Bool)
(assert (forall ((x Type)) (subtype x x)))
(assert (forall ((x Type) (y Type))
(=> (and (subtype x y) (subtype y x))
(= x y))))
(assert (forall ((x Type) (y Type) (z Type))
(=> (and (subtype x y) (subtype y z))
(subtype x z))))
(assert (forall ((x Type) (y Type) (z Type))
(=> (and (subtype x y) (subtype x z))
(or (subtype y z) (subtype z y)))))
(declare-const obj-type Type)
(declare-const int-type Type)
(declare-const real-type Type)
(declare-const complex-type Type)
(declare-const string-type Type)
(assert (forall ((x Type)) (subtype x obj-type)))
(assert (subtype int-type real-type))
(assert (subtype real-type complex-type))
(assert (not (subtype string-type real-type)))
(declare-const root-type Type)
(assert (subtype obj-type root-type))
(check-sat)
(get-model)
Currently, I got
sat
(model
;; universe for Type:
;; Type!val!0 Type!val!3 Type!val!2 Type!val!4 Type!val!1
;; -----------
;; definitions for universe elements:
(declare-fun Type!val!0 () Type)
(declare-fun Type!val!3 () Type)
(declare-fun Type!val!2 () Type)
(declare-fun Type!val!4 () Type)
(declare-fun Type!val!1 () Type)
;; cardinality constraint:
(forall ((x Type))
(or (= x Type!val!0)
(= x Type!val!3)
(= x Type!val!2)
(= x Type!val!4)
(= x Type!val!1)))
;; -----------
(define-fun complex-type () Type
Type!val!2)
(define-fun real-type () Type
Type!val!1)
(define-fun obj-type () Type
Type!val!4)
(define-fun root-type () Type
Type!val!4)
(define-fun string-type () Type
Type!val!3)
(define-fun int-type () Type
Type!val!0)
(define-fun subtype!73 ((x!1 Type) (x!2 Type)) Bool
(ite (and (= x!1 Type!val!3) (= x!2 Type!val!1)) false
(ite (and (= x!1 Type!val!2) (= x!2 Type!val!3)) false
(ite (and (= x!1 Type!val!4) (= x!2 Type!val!1)) false
(ite (and (= x!1 Type!val!4) (= x!2 Type!val!3)) false
(ite (and (= x!1 Type!val!2) (= x!2 Type!val!1)) false
(ite (and (= x!1 Type!val!1) (= x!2 Type!val!3)) false
(ite (and (= x!1 Type!val!4) (= x!2 Type!val!0)) false
(ite (and (= x!1 Type!val!4) (= x!2 Type!val!2)) false
(ite (and (= x!1 Type!val!0) (= x!2 Type!val!3)) false
(ite (and (= x!1 Type!val!2) (= x!2 Type!val!0)) false
(ite (and (= x!1 Type!val!1) (= x!2 Type!val!0)) false
(ite (and (= x!1 Type!val!3) (= x!2 Type!val!0)) false
true)))))))))))))
(define-fun k!72 ((x!1 Type)) Type
(ite (= x!1 Type!val!1) Type!val!1
(ite (= x!1 Type!val!4) Type!val!4
(ite (= x!1 Type!val!3) Type!val!3
(ite (= x!1 Type!val!0) Type!val!0
Type!val!2)))))
(define-fun subtype ((x!1 Type) (x!2 Type)) Bool
(subtype!73 (k!72 x!1) (k!72 x!2)))
)
Thank you in advance for any help you could give.
I think that your line
(assert (forall ((x Type)) (subtype x obj-type)))
is wrong.
The correct is
(assert (forall ((x Type)) (subtype x root-type)))
The possible correct model is obtained here

Can options change `sat` into `unsat`?

Another question from a Z3 newbie. Can options change the behavior of Z3? I might expect them to affect termination, or change sat or unsat into unknown but not sat into unsat or vice versa.
This example:
(set-option :smt.macro-finder true)
(declare-datatypes () ((Option (none) (some (Data Int)))))
(define-sort Set () (Array Option Option))
(declare-fun filter1 (Option) Option)
(declare-fun filter2 (Option) Option)
(declare-var s1 Set)
(declare-var s2 Set)
(declare-var x1 Option)
(declare-var x2 Option)
(declare-var x3 Option)
(declare-var x4 Option)
(assert (not (= x1 none)))
(assert (not (= x2 none)))
(assert (not (= x3 none)))
(assert (not (= x4 none)))
(assert (= (select s1 x1) x2))
(assert (= (select s2 x3) x4))
(assert (forall ((x Option)) (= (filter1 x) (ite (or (= none x) (= (Data x) 1)) x none))))
(assert (forall ((x Option)) (= (filter2 x) (ite (or (= none x) (= (Data x) 2)) x none))))
(assert (= ((_ map filter1) s1) s2))
(assert (= ((_ map filter2) s1) s2))
(check-sat)
(get-model)
returns sat with the first line and unsat without it.
Is this a bug or am I missing something fundamental?
This is a bug. The two quantifiers are essentially providing "definitions" for filter1 and filter2.
The option smt.macro-finder is used to eliminate functions symbols by expanding these definitions. It is essentially performing "macro expansion". However, there is a bug in the macro expander. It does not expand the occurrences of filter1 and filter2 in the map constructs: (_ map filter1) and (_ map filter2).
This bug will be fixed.
In the meantime, we should not use the map construct and smt.macro-finder option simultaneously.

Resources