eliminating forall using unsat - z3

We know that, we can prove validity of a theorem by saying :
let Demorgan(x, y) = formula1(x,y) iff formula2(x,y)
assert ( forall (x,y) . Demorgan(x,y) )
Alternatively we can eliminate the forall quantifier by saying that :
let Demorgan(x, y) = formula1(x,y) iff formula2(x,y)
( assert (not Demorgan(x,y) ) )
So if it returns unsat, then we can say the above formula is valid.
Now I want to use this idea to eliminate the forall quantifier from the following assertion:
assert ( exists x1,x2,x3 st .( forall y . formula1(x1,y) iff
formula2(x2,y) iff
formula3(x3,y) ) )
So is there any way in Z3(using C++ API or SMT-LIB2.0) that I can assert something like the following :
assert (exists x1,x2,x3 st. ( and ((not ( formula1(x1,y) iff formula2(x2,y) )) == unsat)
((not ( formula2(x2,y) iff formula3(x3,y) )) == unsat)))

Yes, when we can prove the validity of a formula by showing its negation to be unsatisfiable.
For example, to show that Forall X. F(X) is valid, we just have to show that not (Forall X. F(X)) is unsatisfiable. The formula not (Forall X. F(X)) is equivalent to (Exists X. not F(X)). The formula (Exists X. not F(X)) is equisatisfiable to the formula not F(X) where the bound variable X is replaced by a fresh constant X. By equisatisfiable, I mean that the first one is satisfiable iff the second one is. This step that removes existential quantifiers is usually called skolemization.
Note that these last two formulas are not equivalent.
For example, consider the interpretation { X -> 2 } that assigns X to 2. The formula Exists X. not (X = 2) still evaluates to true in this interpretation because we can choose X to be 3. On the other hand, the formula not (X = 2) evaluates to false in this interpretation.
We usually use the term quantifier elimination procedure for a procedure that given a formula F produces an equivalent quantifier-free formula F'. So, skolemization is not considered a quantifier elimination procedure because the result is not an equivalent formula.
That being said, we don't have to apply the skolemization step by hand. Z3 can do it for us. Here is an example (also available online here).
(declare-sort S)
(declare-fun F (S) Bool)
(declare-fun G (S) Bool)
(define-fun Conjecture () Bool
(forall ((x S)) (= (and (F x) (G x)) (not (or (not (F x)) (not (G x)))))))
(assert (not Conjecture))
(check-sat)
Now, let us consider a formula of the form Exists X. Forall Y. F(X, Y). To prove the validity of this formula, we can show the negation not Exists X. Forall Y. F(X, Y) to be unsatisfiable. The negation is equivalent to Forall X. Exists Y. not F(X, Y). Now, if apply skolemization to this formula, we obtain Forall X. not F(X, Y(X)). In this case, the bound variable Y was replaced with Y(X), where Y is a fresh function symbol in the resultant formula. The intuition is that the function Y is the "choice function". For each X, we can choose a different value to satisfy the formula F. Z3 performs all these steps automatically for us. We don't need to apply skolemization by hand. However, in this case, the resultant formula is usually harder to solve because it contains a universal quantifier after the skolemization step.

Related

Reducing Unrestricted SAT (USAT) into 3-SAT

I know the procedure simply by transforming each clause l1 ∨ ⋯ ∨ ln to a conjunction of n − 2 clauses .
I try to show the values satisfying the original and the final formula with Z3 to show that they are equisatisfiable as an SMT file.
For clarify can you give any example about this procedure . Thank you.
Equivalence checking is done by asking the solver if there's an assignment that can distinguish two formulas. If there's no such assignment, then you can conclude the formulas are equivalent, or in the SAT context, equi-satisfiable.
Here's a simple example:
from z3 import *
a, b, c = Bools('a b c')
fml1 = Or(And(a, b), And(a, c))
fml2 = And(a, Or(b, c))
s = Solver()
s.add(Distinct(fml1, fml2))
print(s.check())
Now if fml1 is an arbitrary SAT formula, and fml2 is a 3-SAT converted version (I'm not saying the above are SAT and 3-SAT conversions, but substitute the result of your algorithm here), then we'd expect that the SAT solver cannot distinguish them, i.e., the formula Distinct(fml1, fml2) would be unsatisfiable. Indeed, we get:
unsat
establishing that they are the same.
If you are using SMTLib only, then the template to use is:
(declare-fun a () Bool)
(declare-fun b () Bool)
(declare-fun c () Bool)
(assert (distinct (or (and a b) (and a c))
(and a (or b c))))
(check-sat)

Solving predicate calculus problems with Z3 SMT

I'd like to use Z3 to solve problems that are most naturally expressed in terms of atoms (symbols), sets, predicates, and first order logic. For example (in pseudocode):
A = {a1, a2, a3, ...} # A is a set
B = {b1, b2, b3...}
C = {c1, c2, c3...}
def p = (a:A, b:B, c:C) -> Bool # p is unspecified predicate
def q = (a:A, b:B, c:C) -> Bool
# Predicates can be defined in terms of other predicates:
def teaches = (a:A, b:B) -> there_exists c:C
such_that [ p(a, b, c) OR q(a, b, c) ]
constraint1 = forall b:B there_exists a:A
such_that teaches(a, b)
solve(constraint1)
What are good ways to express atoms, sets, predicates, relations, and first order quantifiers in Z3 (or other SMTs)?
Is there a standard idiom for this? Must it be done manually? Is there perhaps a translation library (not necessarily specific to Z3) that can convert them?
I believe Alloy uses SMT to implement predicate logic and relations, but Alloy seems designed more for interactive use to explore consistency of models, rather than to find specific solutions for problems.
"Alloy seems designed more for interactive use to explore consistency of models, rather than to find specific solutions for problems."
IMHO, Alloy shines when it comes to validate your own way of thinking. You model something and through the visualization of several instances you can sometime come to realize that what you modeled is not exactly what you'd have hoped for.
In that sense, I agree with you.
Yet, Alloy can also be used to find specific solutions to problems. You can overload a model with constraints so that only one instance can be found (i.e. your solution).
It works also quite well when your domain space remains relatively small.
Here's your model translated in Alloy :
sig A,B,C{}
pred teaches(a:A,b:B) {
some c:C | a->b->c in REL.q or a->b->c in REL.p}
// I'm a bit rusted, so .. that's my unelegant take on defining an "undefined predicate"
one sig REL {
q: A->B ->C,
p: A->B->C
}
fact constraint1 {
all b:B | some a:A | teaches[a,b]
}
run{}
If you want to define the atoms in sets A,B,C yourself and refer to them in predicates you could always over-constraint this model as follows:
abstract sig A,B,C{}
one sig A1,A2 extends A{}
one sig B1 extends B{}
one sig C1,C2,C3 extends C{}
pred teaches(a:A,b:B) {
some c:C | a->b->c in REL.q or a->b->c in REL.p}
one sig REL {
q: A->B ->C,
p: A->B->C
}{
// here you could for example define the content of p and q yourself
q= A1->B1->C2 + A2 ->B1->C3
p= A1->B1->C3 + A1 ->B1->C2
}
fact constraint1 {
all b:B | some a:A | teaches[a,b]
}
run{}
Modeling predicate logic in SMTLib is indeed possible; though it might be a bit cumbersome compared to a regular theorem prover like Isabelle/HOL etc. And interpreting the results can require a fair amount of squinting.
Having said that, here's a direct encoding of your sample problem using SMTLib:
(declare-sort A)
(declare-sort B)
(declare-sort C)
(declare-fun q (A B C) Bool)
(declare-fun p (A B C) Bool)
(assert (forall ((b B))
(exists ((a A))
(exists ((c C)) (or (p a b c) (q a b c))))))
(check-sat)
(get-model)
A few notes:
declare-sort creates an uninterpreted sort. It's essentially a non-empty set of values. (Can be infinite as well, there are no cardinality assumptions made, aside from the fact that it's not empty.) For your specific problem, it doesn't seem to matter what this sort actually is since you didn't use any of its elements directly. If you do so, you might also want to try a "declared" sort, i.e., a data-type declaration. This can be an enumeration, or something even more complicated; depending on the problem. For the current question as posed, an uninterpreted sort works just fine.
declare-fun tells the solver that there's an uninterpreted function with that name and the signature. But otherwise it neither defines it, nor constrains it in any way. You can add "axioms" about them to be more specific on how they behave.
Quantifiers are supported, as you see with forall and exists in how your constraint1 is encoded. Note that SMTLib isn't that suitable for code-reuse, and one usually programs in a higher-level binding. (Bindings from C/C++/Java/Python/Scala/O'Caml/Haskell etc. are provided, with similar but varying degrees of support and features.) Otherwise, it should be easy to read.
We finally issue check-sat and get-model, to ask the solver to create a universe where all the asserted constraints are satisfied. If so, it'll print sat and will have a model. Otherwise, it'll print unsat if there's no such universe; or it can also print unknown (or loop forever!) if it cannot decide. Use of quantifiers are difficult for SMT solvers to deal with, and heavy use of quantifiers will no doubt lead to unknown as the answer. This is an inherent limitation of the semi-decidability of first-order predicate calculus.
When I run this specification through z3, I get:
sat
(
;; universe for A:
;; A!val!1 A!val!0
;; -----------
;; definitions for universe elements:
(declare-fun A!val!1 () A)
(declare-fun A!val!0 () A)
;; cardinality constraint:
(forall ((x A)) (or (= x A!val!1) (= x A!val!0)))
;; -----------
;; universe for B:
;; B!val!0
;; -----------
;; definitions for universe elements:
(declare-fun B!val!0 () B)
;; cardinality constraint:
(forall ((x B)) (= x B!val!0))
;; -----------
;; universe for C:
;; C!val!0 C!val!1
;; -----------
;; definitions for universe elements:
(declare-fun C!val!0 () C)
(declare-fun C!val!1 () C)
;; cardinality constraint:
(forall ((x C)) (or (= x C!val!0) (= x C!val!1)))
;; -----------
(define-fun q ((x!0 A) (x!1 B) (x!2 C)) Bool
(and (= x!0 A!val!0) (= x!2 C!val!0)))
(define-fun p ((x!0 A) (x!1 B) (x!2 C)) Bool
false)
)
This takes a bit of squinting to understand fully. The first set of values tell you how the solver constructed a model for the uninterpreted sorts A, B, and C; with witness elements and cardinality constraints. You can ignore this part for the most part, though it does contain useful information. For instance, it tells us that A is a set with two elements (named A!val!0 and A!val!1), so is C, and B only has one element. Depending on your constraints, you'll get different sets of elements.
For p, we see:
(define-fun p ((x!0 A) (x!1 B) (x!2 C)) Bool
false)
This means p always is False; i.e., it's the empty set, regardless of what the arguments passed to it are.
For q we get:
(define-fun q ((x!0 A) (x!1 B) (x!2 C)) Bool
(and (= x!0 A!val!0) (= x!2 C!val!0)))
Let's rewrite this a little more simply:
q (a, b, c) = a == A0 && c == C0
where A0 and C0 are the members of the sorts A and C respectively; see the sort declarations above. So, it says q is True whenever a is A0, c is C0, and it doesn't matter what b is.
You can convince yourself that this model does indeed satisfy the constraint you wanted.
To sum up; modeling these problems in z3 is indeed possible, though a bit clumsy and heavy use of quantifiers can make the solver loop-forever or return unknown. Interpreting the output can be a bit cumbersome, though you'll realize that the models will follow a similar schema: First the uninterpreted sorts, and then the the definitions for the predicates.
Side note
As I mentioned, programming z3 in SMTLib is cumbersome and error-prone. Here's the same program done using the Python interface:
from z3 import *
A = DeclareSort('A')
B = DeclareSort('B')
C = DeclareSort('C')
p = Function('p', A, B, C, BoolSort())
q = Function('q', A, B, C, BoolSort())
dummyA = Const('dummyA', A)
dummyB = Const('dummyB', B)
dummyC = Const('dummyC', C)
def teaches(a, b):
return Exists([dummyC], Or(p(a, b, dummyC), q(a, b, dummyC)))
constraint1 = ForAll([dummyB], Exists([dummyA], teaches(dummyA, dummyB)))
s = Solver()
s.add(constraint1)
print(s.check())
print(s.model())
This has some of its idiosyncrasies as well, though hopefully it'll provide a starting point for your explorations should you choose to program z3 in Python. Here's the output:
sat
[p = [else -> And(Var(0) == A!val!0, Var(2) == C!val!0)],
q = [else -> False]]
Which has the exact same info as the SMTLib output, though written slightly differently.
Function definition style
Note that we defined teaches as a regular Python function. This is the usual style in z3py programming, as the expression it produces gets substituted as calls are made. You can also create a z3-function as well, like this:
teaches = Function('teaches', A, B, BoolSort())
s.add(ForAll([dummyA, dummyB],
teaches(dummyA, dummyB) == Exists([dummyC], Or(p(dummyA, dummyB, dummyC), q(dummyA, dummyB, dummyC)))))
Note that this style of definition will rely on quantifier instantiation internally, instead of the general function-definition facilities of SMTLib. So, you should prefer the python function style in general as it translates to "simpler" internal constructs. It is also much easier to define and use in general.
One case where you need the z3 function definition style is if the function you're defining is recursive and its termination relies on a symbolic argument. For a discussion of this, see: https://stackoverflow.com/a/68457868/936310

Creating a transitive and not reflexive function in Z3

I'm trying to create a function in Z3 that is transitive but not reflexive. I.e. if (transitive a b) and (transitive b c)hold then (transitive a c) should hold, but (transitive a a) should not.
I've tried to do it the following way, with 5 "tests". The first does what I expect, but the second one fails and results in unknown.
(declare-datatypes () ((T T1 T2 T3)))
(declare-fun f (T T) Bool)
(assert(f T1 T2))
(assert(f T2 T3))
; Make sure that f is not reflexive
(assert
(forall ((x T))
(not (f x x))))
; Now we create the transitivity function ourselves
(define-fun-rec transitive ((x T) (y T)) Bool
(or
(f x y)
(exists ((z T))
(and
(f x z)
(transitive z y)))))
; This works and gives sat
(push)
(assert (not (transitive T1 T1)))
(assert (not (transitive T2 T2)))
(assert (not (transitive T3 T3)))
(check-sat)
(pop)
; This fails with "unknown" and the verbose flag gives: (smt.mbqi "max instantiations 1000 reached")
(push)
(assert
(forall ((x T))
(not (transitive x x))))
(check-sat)
(pop)
My question is: how does the second test differ from the first? Why does the last one give unknown, whereas the one before that works just fine?
The "verbose" message is a hint here. mbqi stands for model-based-quantifier-instantiation. It's a method of dealing with quantifiers in SMT solving. In the first case, MBQI manages to find a model. But your transitive function is just too complicated for MBQI to handle, and thus it gives up. Increasing the limit will not likely address the problem, nor it's a long term solution.
Short story long, recursive definitions are difficult to deal with, and recursive definitions with quantifiers are even harder. The logic becomes semi-decidable, and you're at the mercy of heuristics. Even if you found a way to make z3 compute a model for this, it would be brittle. These sorts of problems are just not suitable for SMT solving; better use a proper theorem prover like Isabelle, Hol, Coq, Lean. Agda, etc. Almost all these tools offer "tactics" to dispatch subgoals to SMT solvers, so you have the best of both worlds. (Of course you lose full automation, but with quantifiers present, you can't expect any better.)

Creating List in z3 using function

I'm trying to convert this piece of pseudocode to SMT-LIB language, but I got stuck.
List function my_fun(int x)
{
list = nil
for(i in 1 to x):
if(some_condition_on_i)
list.concat(i)
return list
}
what I've done so far is this:
(declare-const l1 (List Int))
(define-fun my_fun ((x Int)) (List Int)
(forall ((t Int))
(ite (and (some_condition_on_t) (< t x)) (insert t l1) l1 )
)
)
)
which I know it is wrong, and does not work. can you help me to understand how can I do this?
SMT-LIB models logic, where variables are always immutable; your code, on the other hand, appears to be imperative, i.e. variables such as list and i are mutable. This crucial difference will be the biggest challenge in encoding your program and the challenge of reasoning about imperative programs has sparked research tools such as Dafny, Boogie, or Viper
Here are a few pointers:
(insert t l1) represents a new list, obtained by inserting t into l1. It will not modify l1 (and there is no way to modify l1 since it is a logical variable)
A logical forall is a boolean formula (it evaluates to true or false), it is not a statement that you can execute (e.g. for its side effects)
If the value of x were statically known (i.e. if it were 5), then you could unroll the loop (here in pseudo-code):
l0 := Nil
l1 := ite(condition(1), insert(1, l0), l0)
l2 := ite(condition(2), insert(2, l1), l1)
...
l4 := ite(condition(4), insert(4, l3), l3)
If the value of x isn't statically known then you'll most likely either need a loop invariant or work with fix points in order to account for an unknown number of loop iterations

Datatypes and quantifier patterns/triggers

I observed a difference in Z3's quantifier triggering behaviour (I tried 4.4.0 and 4.4.2.3f02beb8203b) that I cannot explain. Consider the following program:
(set-option :auto_config false)
(set-option :smt.mbqi false)
(declare-datatypes () ((Snap
(Snap.unit)
(Snap.combine (Snap.first Snap) (Snap.second Snap))
)))
(declare-fun fun (Snap Int) Bool)
(declare-fun bar (Int) Int)
(declare-const s1 Snap)
(declare-const s2 Snap)
(assert (forall ((i Int)) (!
(> (bar i) 0)
:pattern ((fun s1 i))
)))
(assert (fun s2 5))
(assert (not (> (bar 5) 0)))
(check-sat) ; unsat
As far as my understanding goes, the unsat is unexpected: Z3 should not be able to trigger the forall since it is guarded by the pattern (fun s1 i), and Z3 should not be able (and actually isn't) to prove that s1 = s2.
In contrast, if I declare Snap to be an uninterpreted sort, then the final check-sat yields unknown - which is what I would expect:
(set-option :auto_config false)
(set-option :smt.mbqi false)
(declare-sort Snap 0)
...
(check-sat) ; unknown
If I assume s1 and s2 to be different, i.e.
(assert (not (= s1 s2)))
then the final check-sat yields unknown in both cases.
For convenience, here is the example on rise4fun.
Q: Is the difference in behaviour a bug, or is it intended?
The assertion (not (= s1 s2)) is essential. With pattern based quantifier instantiation, the pattern matches if the current state of the search satisfies s1 = s2. In the case of algebraic data-types, Z3 tries to satisfy formulas with algebraic data-types by building a least model in terms of constructor applications. In the case of Snap as an algebraic data-type the least model for s1, s2 have them both as Snap.unit. At that point, the trigger is enabled because the terms E-match. In other words, modulo the congruences, the variable I can be instantiated such that (fun s1 I) matches (fun s2 5), but setting I <- 5. After the trigger is enabled, the quantifier is instantiated and the axiom
(=> (forall I F(I)) (F(5)))
is added (where F is the formula under the quantifier).
This then enables to infer the contradiction and infer unsat.
When Snap is uninterpreted, Z3 attempts to construct a model where terms s1 and s2 are different. Since there is nothing to force these terms to be equal they remain distinct
It's not a bug since z3 doesn't say unsat for a sat formula (or sat for an unsat one). In presence of quantified formulas, SMT solvers are (in general) not complete. So they sometimes answer unknown when they are not sure that the input formula in sat.
For your example:
a - It's normal that, with matching techniques, z3 does not prove the formula when you assume that s1 and s2 are different. In fact, there is no ground term of the form (fun s1 5) that matches the pattern (fun s1 i), and that would allow the generation of the useful instance (> (bar 5) 0) from your quantified formula;
b - When you don't assume that s1 and s2 are different, you would not be able to get the proof too. Except that z3 probably assumes internally that s1 = s2 when Snap is a datatype. This is correct as long as there is nothing that contradicts s1 = s2. Thanks to this and to matching modulo equality, the ground term (fun s2 5) matches the pattern (fun s1 i), and the needed instance to prove unsatisfiability is generated.

Resources