Using MaxSAT Queries in Z3 - z3

I have a question about the Z3/SMTLib API.
The key SMT subroutine required by the liquid-types
tools is the following query:
INPUT: A "background" formula P
, A list of "goal" formulas Qs = [Q1,...,Qn]
OUTPUT: Largest Qs' \subset Qs s.t. forall Q in Qs', P => Q.
Currently, we compute this in the obvious fashion:
def query1(P, Qs):
Qs' := []
ASSERT(P)
for Q in Qs:
PUSH()
ASSERT (not Q)
if CHECKUNSAT():
Qs'.add(Q)
POP()
return Qs'
My question: is there some faster way to do via MaxSAT?
def query2(P, Qs):
F = (Hard P) /\ ((Soft ~Q1) \/ ... \/ (Soft ~QN))
Rs = MaxSat(F)
Qs' = Qs - Rs
return Qs'
That is Hard means you must satisfy P and Soft
means that maximize the number of ~Qi that can also
be satisfied. Specifically,
Is there an API that would let me do the above?
Would query2 be faster than the loop in query1 above?

Somewhat confusing: this is not a real max-sat problem. It is computing the largest set of implied literals. It is similar to computing a set of implied equalities, e.g, what are the literals that are equal to "true".
We do a little tuning for this in the get-implied-equalities feature and paper with Josh. Generally, there are specialized methods for SAT that use filtering (sat sweeping) to remove candidates before doing full checks.
Your "query2" should really have been something like this:
def query2(P, Qs):
while True:
F = And(P, Or[Not(Q) for Q in Qs])
if sat == CheckSAT(F):
Qs = [Q for Q in Qs if is_false(model.eval(Q)) ]
else:
return Qs

Related

In Z3, I cannot understand result of quantifier elimination of Exists y. Forall x. (x>=2) => ((y>1) /\ (y<=x))

In Z3-Py, I am performing quantifier elimination (QE) over the following formulae:
Exists y. Forall x. (x>=2) => ((y>1) /\ (y<=x))
Forall x. Exists y. (x>=2) => ((y>1) /\ (y<=x)),
where both x and y are Integers. I did QE in the following way:
x, y = Ints('x, y')
t = Tactic("qe")
negS0= (x >= 2)
s1 = (y > 1)
s2 = (y <= x)
#EA
ea = Goal()
ea.add(Exists([y],Implies(negS0, (ForAll([x], And(s1,s2))))))
ea_qe = t(ea)
print(ea_qe)
#AE
ae = Goal()
ae.add(ForAll([x],Implies(negS0, (Exists([y], And(s1,s2))))))
ae_qe = t(ae)
print(ae_qe)
Result QE for ae is as expected: [[]] (i.e., True). However, as for ea, QE outputs: [[Not(x, >= 2)]], which is a results that I do not know how to interpret since (1) it has not really performed QE (note the resulting formula still contains x and indeed does not contain y which is the outermost quantified variable) and (2) I do not understand the meaning of the comma in x, >=. I cannot get the model either:
phi = Exists([y],Implies(negS0, (ForAll([x], And(s1,s2)))))
s_def = Solver()
s_def.add(phi)
print(s_def.model())
This results in the error Z3Exception: model is not available.
I think the point is as follows: since (x>=2) is an implication, there are two ways to satisfy the formula; by making the antecedent False or by satisfying the consequent. In the second case, the model would be y=2. But in the first case, the result of QE would be True, thus we cannot get a single model (as it happens with a universal model):
phi = ForAll([x],Implies(negS0, (Exists([y], And(s1,s2)))))
s_def = Solver()
s_def.add(phi)
print(s_def.model())
In any case, I cannot 'philosophically' understand the meaning of a QE of x where x is part of the (quantifier-eliminated) answer.
Any help?
There are two separate issues here, I'll address them separately.
The mysterious comma This is a common gotcha. You declared:
x, y = Ints('x, y')
That is, you gave x the name "x," and y the name "y". Note the comma after the x in the name. This should be
x, y = Ints('x y')
I guess you can see the difference: The name you gave to the variable x is "x," when you do the first; i.e., comma is part of the name. Simply skip the comma on the right hand side, which isn't what you intended anyhow. And the results will start being more meaningful. To be fair, this is a common mistake, and I wish the z3 developers ignored the commas and other punctuation in the string you give; but that's just not the case. They simply break at whitespace.
Quantification
This is another common gotcha. When you write:
ea.add(Exists([y],Implies(negS0, (ForAll([x], And(s1,s2))))))
the x that exists in negS0 is not quantified over by your ForAll, since it's not in the scope. Perhaps you meant:
ea.add(Exists([y],ForAll([x], Implies(negS0, And(s1,s2)))))
It's hard to guess what you were trying to do, but I hope the above makes it clear that the x wasn't quantified. Also, remember that a top-level exist quantifier in a formula is more or less irrelevant. It's equivalent to a top-level declaration for all practical purposes.
Once you make this fix, I think things will become more clear. If not, please ask further clarifying questions. (As a separate question on Stack-overflow; as edits to existing questions only complicate the matters.)

Using sets and SetHasSize on intersections in z3

I've been trying to enumerate solutions to the following problem. I've started with simple approach first.
Below I have two sets of size k, intersection between them is of size 1 and I want to see how sets A and B look:
Els, elems = EnumSort('Els',['a1', 'a2', 'a3'])
A, B = Consts('A B', SetSort(Els))
k, c = Ints('k c')
s = Solver()
s.add(SetHasSize(A, k))
s.add(SetHasSize(B, k))
s.add(k == 2, c == 1)
s.add(SetHasSize(SetIntersect(A, B), c))
s.check()
s.model()
Here, the solution should be A == ['a1', 'a2'] and B == ['a2', 'a3'] but satisfiability was not reached.
Even a simple task like one below results in never ending execution:
V, _ = EnumSort('Els',['a1', 'a2', 'a3'])
A = Const('A', SetSort(V))
k = Int('k')
s = SimpleSolver()
s.add(SetHasSize(A, k))
s.add(k == IntVal(2))
s.check()
s.model()
Changing k == IntVal(2) to k <= IntVal(2) makes the problem satisfiable and returns [A = ∃k!0 : k!0 = a1 ∨ k!0 = a2, k = 2] as a model of the set. I'm not sure if there is a faster approach.
If I run your program, I get:
WARNING: correct handling of finite domains is TBD
WARNING: correct handling of finite domains is TBD
WARNING: correct handling of finite domains is TBD
before it starts looping. This is a known issue in the implementation: Z3 cannot really deal with sets that have a finite domain.
Alas, replacing it with an infinite domain doesn't help either. Making this change:
A, B = Consts('A B', SetSort(IntSort()))
You get:
unsat
which is clearly bogus. I strongly suspect this is related to the following issue: https://github.com/Z3Prover/z3/issues/3854 (In short, the SMTLib frontend does not support set-has-size. For whatever reason, they left it in the Python and C/C++ interfaces, but clearly it's not really functional.)
So, strictly speaking, this is a bug. But more realistic answer is that set-has-size is no longer supported by z3. You should file an issue at https://github.com/Z3Prover/z3/issues so the authors are aware of this problem and perhaps remove it from the API completely if it won't ever be supported.
UPDATE:
As of this commit, z3 no longer accepts the set-has-size predicate; it is no longer supported.

partial assignments in Z3

I have a Boolean formula (format: CNF) whose satisfiablity I check using the Z3 SAT solver. I am interested in obtaining partial assignments when the formula is satisfiable. I tried model.partial=true on a simple formula for an OR gate and did not get any partial assignment.
Can you suggest how this can be done? I have no constraints on the assignment other than that it be partial.
Z3's partial model mode are just for models of functions. For propositional formulas there is no assurance that models use a minimal number of assignments. On the contrary, the default mode of SAT solvers is that they find complete assignments.
Suppose you are interested in a minimized number of literals, such that the conjunction of the literals imply the formula. You can use unsat cores to get such subsets. The idea is that you first find a model of your formula F as a conjunction of literals l1, l2, ..., ln. Then given that this is a model of F we have that l1 & l2 & ... & ln & not F is unsatisfiable.
So the idea is to assert "not F" and check satisfiability of "not F" modulo assumptions l1, l2, .., ln. Since the result is unsat, you can query Z3 to retrieve an unsat core among l1, l2, .., ln.
From python what you would do is to create two solver objects:
s1 = Solver()
s2 = Solver()
Then you add F, respectively, Not(F):
s1.add(F)
s2.add(Not(F))
then you find a reduced model for F using both solvers:
is_Sat = s1.check()
if is_Sat != sat:
# do something else, return
m = s1.model()
literals = [sign(m, m[idx]()) for idx in range(len(m)) ]
is_sat = s2.check(literals)
if is_Sat != unsat:
# should not happen
core = s2.unsat_core()
print core
where
def sign(m, c):
val = m.eval(c)
if is_true(val):
return c
else if is_false(val):
return Not(c)
else:
# should not happen for propositional variables.
return BoolVal(True)
There are of course other ways to get reduced set of literals. A partial cheap way is to eagerly evaluate each clause and add literals from the model until each clause is satisfied by at least one literal in the model. In other words, you are looking for a minimal hitting set. You would have to implement this outside of Z3.

How does Z3 deal with forall proposition with uninterpreted functions in it?

Suppose we have two uninterpreted functions func1 and func2:
stuct_sort func1(struct_sort);
stuct_sort func2(struct_sort ,int).
And they have the relationship:
func2(p,n)=func1(p) if n==1
func2(p,n)=func1(func2(p,n-1)) if n>1
What I want to know is that if the following proposition :
((forall i:[1,m].func2(p,i)==Z)&&(q==func1(p))) implies (forall i:[1,m-1].func2(q,i)==Z)
can be proved to be true in Z3?
In my program, the prove result is Z3_L_UNDEF.
When I assign m with a value such as 3, the proposition now is
((forall i:[1,3].func2(p,i)==Z)&&(q==func1(p))) implies (forall i:[1,3-1].func2(q,i)==Z);
the result is Z3_L_UNDEF.
But when I rewrite the case separately(not using forall) as follows, the result is true.
(func2(p,1)==Z)&&(func2(p,2)==Z)&&(func2(p,3)==Z)&&(q==func1(p)) implies (func2(q,1))&&(func2(q,2)).
I can't find out the reason and looking forward to your answer
I encoded your problem using the Z3 Python interface, and Z3 solved it. It found a counterexample for the conjecture.
Of course, I may have made a mistake when I encoded the problem. The Python code is in the end of the post. We can try it online at rise4fun. BTW, which version of Z3 are you using? I'm assuming you are using the C API. If that is the case, could you provide the C code you used to create the Z3 formulas? Another possibility is to create a log that records the interaction of your application and Z3. To create a log file, we have to execute Z3_open_log("z3.log"); before you execute any other Z3 API. We can use the log file to replay all the interaction between your application and Z3.
from z3 import *
# Declare stuct_sort
S = DeclareSort('stuct_sort')
I = IntSort()
# Declare functions func1 and func2
func1 = Function('func1', S, S)
func2 = Function('func2', S, I, S)
# More declarations
p = Const('p', S)
n = Int('n')
m = Int('m')
i = Int('i')
q = Const('q', S)
Z = Const('Z', S)
# Encoding of the relations
# func2(p,n)=func1(p) if n==1
# func2(p,n)=func1(func2(p,n-1)) if n>1
Relations = And(func2(p, 1) == func1(p),
ForAll([n], Implies(n > 1, func2(p, n) == func1(func2(p, n - 1)))))
# Increase the maximum line width for the Z3 Python formula pretty printer
set_option(max_width=120)
print Relations
# Encoding of the conjecture
# ((forall i:[1,m].func2(p,i)==Z)&&(q==func1(p))) implies (forall i:[1,m-1].func2(q,i)==Z)
Conjecture = Implies(And(q == func1(p), ForAll([i], Implies(And(1 <= i, i <= m), func2(p, i) == Z))),
ForAll([i], Implies(And(1 <= i, i <= m - 1), func2(q, i) == Z)))
print Conjecture
prove(Implies(Relations, Conjecture))

How do I do convolution in F#?

I would like convolve a discrete signal with a discrete filter. The signal and filter is sequences of float in F#.
The only way I can figure out how to do it is with two nested for loops and a mutable array to store the result, but it does not feel very functional.
Here is how I would do it non-functional:
conv = double[len(signal) + len(filter) - 1]
for i = 1 to len(signal)
for j = 1 to len(filter)
conv[i + j] = conv[i + j] + signal(i) * filter(len(filter) - j)
I don't know F#, but I'll post some Haskell and hopefully it will be close enough to use. (I only have VS 2005 and an ancient version of F#, so I think it would be more confusing to post something that works on my machine)
Let me start by posting a Python implementation of your pseudocode to make sure I'm getting the right answer:
def convolve(signal, filter):
conv = [0 for _ in range(len(signal) + len(filter) - 1)]
for i in range(len(signal)):
for j in range(len(filter)):
conv[i + j] += signal[i] * filter[-j-1]
return conv
Now convolve([1,1,1], [1,2,3]) gives [3, 5, 6, 3, 1]. If this is wrong, please tell me.
The first thing we can do is turn the inner loop into a zipWith; we're essentially adding a series of rows in a special way, in the example above: [[3,2,1], [3,2,1], [3,2,1]]. To generate each row, we'll zip each i in the signal with the reversed filter:
makeRow filter i = zipWith (*) (repeat i) (reverse filter)
(Note: according to a quick google, zipWith is map2 in F#. You might have to use a list comprehension instead of repeat)
Now:
makeRow [1,2,3] 1
=> [3,2,1]
makeRow [1,2,3] 2
=> [6,4,2]
To get this for all i, we need to map over signal:
map (makeRow filter) signal
=> [[3,2,1], [3,2,1], [3,2,1]]
Good. Now we just need a way to combine the rows properly. We can do this by noticing that combining is adding the new row to the existing array, except for the first element, which is stuck on front. For example:
[[3,2,1], [6,4,2]] = 3 : [2 + 6, 1 + 4] ++ [2]
// or in F#
[[3; 2; 1]; [6; 4; 2]] = 3 :: [2 + 6; 1 + 4] # [2]
So we just need to write some code that does this in the general case:
combine (front:combinable) rest =
let (combinable',previous) = splitAt (length combinable) rest in
front : zipWith (+) combinable combinable' ++ previous
Now that we have a way to generate all the rows and a way to combine a new row with an existing array, all we have to do is stick the two together with a fold:
convolve signal filter = foldr1 combine (map (makeRow filter) signal)
convolve [1,1,1] [1,2,3]
=> [3,5,6,3,1]
So that's a functional version. I think it's reasonably clear, as long as you understand foldr and zipWith. But it's at least as long as the imperative version and like other commenters said, probably less efficient in F#. Here's the whole thing in one place.
makeRow filter i = zipWith (*) (repeat i) (reverse filter)
combine (front:combinable) rest =
front : zipWith (+) combinable combinable' ++ previous
where (combinable',previous) = splitAt (length combinable) rest
convolve signal filter = foldr1 combine (map (makeRow filter) signal)
Edit:
As promised, here is an F# version. This was written using a seriously ancient version (1.9.2.9) on VS2005, so be careful. Also I couldn't find splitAt in the standard library, but then I don't know F# that well.
open List
let gen value = map (fun _ -> value)
let splitAt n l =
let rec splitter n l acc =
match n,l with
| 0,_ -> rev acc,l
| _,[] -> rev acc,[]
| n,x::xs -> splitter (n - 1) xs (x :: acc)
splitter n l []
let makeRow filter i = map2 ( * ) (gen i filter) (rev filter)
let combine (front::combinable) rest =
let combinable',previous = splitAt (length combinable) rest
front :: map2 (+) combinable combinable' # previous
let convolve signal filter =
fold1_right combine (map (makeRow filter) signal)
Try this function:
let convolute signal filter =
[|0 .. Array.length signal + Array.length filter - 1|] |> Array.map (fun i ->
[|0 .. i|] |> Array.sum_by (fun j -> signal.[i] * filter.[Array.length filter - (i - j) - 1]))
It's probably not the nicest function solution, but it should do the job. I doubt there exists a purely functional solution that will match the imperative one for speed however.
Hope that helps.
Note: The function is currently untested (though I've confirmed it compiles). Let me know if it doesn't quite do what it should. Also, observe that the i and j variables do not refer to the same things as is your original post.
Indeed, you generally want to avoid loops (plain, nested, whatever) and anything mutable in functional programming.
There happens to be a very simple solution in F# (and probably almost every other functional language):
let convolution = Seq.zip seq1 seq2
The zip function simply combines the two sequences into one of pairs containing the element from seq1 and the element from seq2. As a note, there also exist similar zip functions for the List and Array modules, as well as variants for combining three lists into triples (zip3). If you want tom ore generally zip (or "convolute") n lists into a list of n-tuples, then you'll need to write your own function, but it's pretty straightforward.
(I've been going by this description of convolution by the way - tell me if you mean something else.)
In principle, it should be possible to use the (Fast) Fourier Transform, or the related (Discrete) Cosine Transform, to calculate the convolution of two functions reasonably efficiently. You calculate the FFT for both functions, multiply them, and apply the inverse FFT on the result.
mathematical background
That's the theory. In practice you'd probably best find a math library that implements it for you.

Resources