How to simplify the result of (eval (f x y))? - z3

How can I simplify the Boolean expression obtained as a result of evaluating an uninterpreted function?
For instance, in the example: http://rise4fun.com/Z3/G8sL,
(eval (f x y))
yields (not (and (not x) (not y)))
I want to instead get the expression (or x y).
(simplify (eval (f x y))
gives an error.

This is not supported in the SMT-LIB 2.0 front-end.
You should consider one the programmatic front-ends for Z3.
For example, here is how to do it using the Z3 Python front-end (also available here):
B = BoolSort()
f = Function('f', B, B, B)
x, y = Bools('x y')
b1, b2 = Bools('b1 b2')
s = Solver()
s.add(ForAll([b1, b2], Implies(Or(b1, b2), f(b1, b2))))
s.add(Exists([b1, b2], Not(f(b1, b2))))
print(s.check())
m = s.model()
print(m.evaluate(f(x, y)))
print(simplify(m.evaluate(f(x, y)), elim_and=True))

Related

A model of a simple formula 'Exists([y],ForAll([x],Phi))' should be 'y=2' but Z3 it is returning '[]'

Note the following Z3-Py code:
x, y = Ints('x y')
negS0= (x >= 2)
s1 = (y > 1)
s2 = (y <= x)
s = Solver()
phi = Exists([y],ForAll([x], Implies(negS0, And(s1,s2))))
s.add(phi)
print(s.check())
print(s.model())
This prints:
sat
[]
My question is: why is the model empty? I mean, I think y=2 should be a model...
Note that the same result happens with x and y being Real.
z3 will not include any quantified variable (in your case neither y nor x) in its model. Note that you cannot put x in a model anyhow, because the formula is true for all x: That's the meaning of universal quantification. For the outer-most existentials (like your y), z3 can indeed print the model value for that, but it chooses not to do so since it can be confusing: Imagine you had a phi2, which also had an outer-most existential named y: How would you know which y it would be that it prints in the model?
So, z3 keeps things simple and simply prints the top-level declared variables in the model. And since a top-level declaration is equivalent to an outermost existential, you can simply drop it:
from z3 import *
x, y = Ints('x y')
negS0= (x >= 2)
s1 = (y > 1)
s2 = (y <= x)
s = Solver()
phi = ForAll([x], Implies(negS0, And(s1,s2)))
s.add(phi)
print(s.check())
print(s.model())
This prints:
sat
[y = 2]
like you predicted. Note that this y is unambiguous, since it's declared at the top-level. (Of course, you can redefine it to be something else due to the loosely typed nature of Python bindings and still get yourself confused, but that's a different discussion.)

Diamond-like interface constraints issue

I've written an implementation of an Applicative and VerifiedApplicative for a pair with a monoid:
import Interfaces.Verified
VerifiedMonoid a => Applicative (Pair a) where
(l, f) <*> (r, x) = (l <+> r, f x)
pure x = (neutral, x)
VerifiedMonoid a => VerifiedApplicative (Pair a) where
applicativeMap (l, x) g = sym $ cong {f=(\y => (y, g x))} (monoidNeutralIsNeutralR l)
applicativeIdentity = ?h1
applicativeComposition = ?h2
applicativeHomomorphism = ?h3
applicativeInterchange = ?h4
Although it compiles, if I weaken a condition on a in Applicative instance as follows:
Monoid a => Applicative (Pair a) where
(l, f) <*> (r, x) = (l <+> r, f x)
pure x = (neutral, x)
I see an error message complaining on applicativeMap:
When checking right hand side of Interfaces.Verified.Contravariant.Pair a implementation of Interfaces.Verified.VerifiedApplicative, method applicativeMap with expected type
map g (l, x) = pure g <*> (l, x)
When checking an application of function Prelude.Basics.cong:
Type mismatch between
(<+>) {{constructor of Interfaces.Verified.VerifiedSemigroup#Semigroup a {{constructor of Interfaces.Verified.VerifiedMonoid#VerifiedSemigroup a}}}}
neutral
l =
l (Type of monoidNeutralIsNeutralR l)
and
(<+>) {{constructor of Prelude.Algebra.Monoid#Semigroup ty {{constructor of Interfaces.Verified.VerifiedMonoid#Monoid a}}}} neutral l = l (Expected type)
Specifically:
Type mismatch between
(<+>) {{constructor of Interfaces.Verified.VerifiedSemigroup#Semigroup a {{constructor of Interfaces.Verified.VerifiedMonoid#VerifiedSemigroup a}}}}
neutral
l
and
(<+>) {{constructor of Prelude.Algebra.Monoid#Semigroup ty {{constructor of Interfaces.Verified.VerifiedMonoid#Monoid a}}}}
neutral
l
Since it's shown Semigroup ty, apparently Semigroup instance is not inferred. I tried to write it explicitly as (Semigroup a, VerifiedMonoid a) => VerifiedApplicative (Pair a), but it doesn't help.
Also it comes in mind a diamond inheritance issue, since Semigroup => Monoid => VerifiedMonoid and in the same time Semigroup => VerifiedSemigroup => VerifiedMonoid.
Any ideas how to workaround this? Limiting Applicative instance only on VerifiedMonoid is a bit awkward.

Distinct in z3 SMT and python

My question is that Does "Distinct" works in z3 python?. I've compared the following code and it seem not giving the same results:
(declare-const x Int)
(declare-const y Int)
(assert (distinct x y))
(check-sat)
(get-model)
The result was:
sat
(model
(define-fun y () Int
0)
(define-fun x () Int
1)
)
I've added negative assertion just to test and the result was unsat which is correct:
(assert (= x y))
unsat
Z3(6, 10): ERROR: model is not available
But when I use z3 in python it give me always sat as follows:
x = Int('x')
y = Int('y')
Distinct(x, y)
s = Solver
s = Solver()
s.check()
when I add the following assertion It should give me unsat but It returns sat:
s.add(x == y)
[y = 0, x = 0]
Is this means that I used wrong syntax ?
The `Distinct' function only creates a term, it doesn't add itself to the solver. Here's an example that works for me:
x = Int('x')
y = Int('y')
d = Distinct(x, y)
s = Solver()
s.add(d) # SAT without this one, UNSAT with
s.add(x == y)
print s
print s.check()

Validation query formulation, SMT solver, Z3, STP

I have a boolean formula f(a, b, x, y). Where a and b are boolean expressions and x and y are bit vector expressions. a and b are boolean expressions possibly using expressions a, b, x and y.
I want to define the following query for validity:
f(a, b, x, y)* such that *a = false && b = false
f(a, b, x, y)* such that *a = true && b = false
In a way, I need to substitute the values of a and b in the formula on both the sides of implication.
Please advise how to create such a query.
Why not create a fresh variable for a?
you can then assert the following:
(assert (not a))
(assert (not b))
(assert (f a b x y))
(assert a2)
(assert (not (f a2 b x y)))
(check-sat)
your query is valid if and only if the query above is unsatisfiable because the bindings are represented as assertions and the implication was negated ( and flattened into two assertions)

unsat core function in z3

Suppose I read the SMTLIB formula using the API:
context ctx;
...
expr F = to_expr(ctx, Z3_parse_smtlib2_file(ctx,argv[1],0,0,0,0,0,0));
The expression F is a conjunction of assertions of the form:
(and (< (+ x y) 3)
(> (- x1 x2) 0)
(< (- x1 x2) 4)
(not (= (- x1 x2) 1))
(not (= (- x1 x2) 2))
(not (= (- x1 x2) 3)))
I'd like to extract each individual assertion from this conjunction using the following code fragment from post: How to use z3 split clauses of unsat cores & try to find out unsat core again
F = F.simplify();
for (unsigned i = 0; i < F.num_args(); i++) {
expr Ai = F.arg(i);
// ... Do something with Ai, just printing in this example.
std::cout << Ai << "\n";
}
After utilizing the F.arg(i), the original clause (< (+ x y) 3) has been changed into (not (<= 3 (+ x y))). Here is my
a) question : How can I place the clause (not (<= 3 (+ x y))) to (< (+ x y) 3) ?
b) question : I consider the symbol <= mean to imply in this case, not mean to less than. Am I right?
c) question : Because the clause (not (<= 3 (+ x y))) model is true or false, how can I get arithmetic values such as x = 1, y = -1?
It's very grateful for any suggestion.
Thank you very much.
The expression (< (+ x y) 3) is transformed into (not (<= 3 (+ x y))) when F = F.simplify().
In the code fragment you used, the method simplify() is used to "flat" nested "and"s. That is, a formula (and (and A B) (and C (and D E))) is flattened into (and A B C D E). Then, all conjuncts can be easily traversed using the for-loop. However, the simplify() will also perform other transformations in the input formula. Keep in mind, that all transformation preserve equivalence. That is, the input and output formula are logically equivalent. If the transformations applied by simplify() are not desirable, I suggest you avoid this method. If you still want to traverse nested "and"s, you can use an auxiliary todo vector. Here is an example:
expr_vector todo(c);
todo.push_back(F);
while (!todo.empty()) {
expr current = todo.back();
todo.pop_back();
if (current.decl().decl_kind() == Z3_OP_AND) {
// it is an AND, then put children into the todo list
for (unsigned i = 0; i < current.num_args(); i++) {
todo.push_back(current.arg(i));
}
}
else {
// do something with current
std::cout << current << "\n";
}
}

Resources