Missing model when solving Exists-Expression - z3

I was setting up an example using the Java-API (along the lines of the provided Z3 Java examples):
Context ctx = new Context();
int size = 1;
IntExpr[] xs = new IntExpr[size];
Symbol[] names = new Symbol[size];
for (int j = 0; j < size; j++) {
names[j] = ctx.mkSymbol("x_" + Integer.toString(j));
xs[j] = (IntExpr) ctx.mkConst(names[j], ctx.getIntSort());
}
BoolExpr body_const = ctx.mkEq(xs[0], ctx.mkInt(3));
BoolExpr exists = ctx.mkExists(xs, body_const, 1, null, null,
null, null);
Solver s = ctx.mkSolver();
s.add(exists);
if (s.check() != Status.SATISFIABLE)
throw new IllegalStateException();
Model model = s.getModel();
System.out.println("---> " + model);
I wonder why the model is empty! Shouldn't it simply state that xs[0] is 3?

The entire Java program is just clutter in this context. Essentially, you're asking the solver the following SMTLib question:
(assert (exists ((x_0 Int)) (= x_0 3)))
(check-sat)
(get-model)
When run, the above prints:
sat
(
)
That is, it is indeed satisfiable, but there's no model to display. Because SMT solvers do not display values of existentially quantified variables like this. The model only contains values that are declared at the top-level. So, instead, you need to ask something like:
(declare-const x_0 Int)
(assert (= x_0 3))
(check-sat)
(get-model)
to which you'll get the model:
sat
(
(define-fun x_0 () Int
3)
)
Hope that helps out. So, if the Java program you gave is indeed what you intended, then the output is correct; by design you do not get the values of "existentially" quantified variables from an SMT solver. If you want to get them, make them top-level variables instead.

Related

How to model variable swap in SMT (Z3)?

I have a program that sorts variables, and I'm trying to check its validity with Z3, but I have one code segment where the variables are being swapped and I don't know how to model it in SMT syntax. Here is the original code segment:
if (x > y) {
temp = x;
x = y;
y = temp;
}
And regarding the SMT I have written an assertion, but I guess it is not exactly the correct thing:
(declare-fun x () Int)
(declare-fun y () Int)
(declare-fun temp () Int)
(assert (=> (> s1 s2) (and (= tmp s1) (= s1 s2) (= s2 tmp))))
Any ideas how to do variable assignment in SMT?
You should look into Single Static Assignment [1]. In this way you can rewrite your original code as follows.
if (x0 > y0) {
temp0 = x0;
x1 = y0;
y1 = temp0;
}
It thus becomes clear that you have two different instances of x and y. The first (x0, y0) is the one you are comparing in the if condition. The second (x1, y1) is the result of the operation.
This introduces an implicit notion of time that makes it also easier to write properties about your code. E.g.,
((x1 = x0) & (y1 = y0)) | ((x1 = y0) | (y1 = x0))
Of course, this might require adjusting other parts of your code, so that you are using the right variables.
[1] https://en.wikipedia.org/wiki/Static_single_assignment_form
We can rewrite what you want using a single expression as a tuple:
(result1, result2) = x > y ? (x, y) : (y, x)
Z3 supports tuples but I'm less experienced with that. It's probably easier to blast this into parts:
result1 = x > y ? x : y
result2 = x > y ? y : x
And the ?: operator maps to ITE in Z3.
You don't even need "temp variables" for this (but clearly you can).
(assert (=> (> s1 s2) (and (= tmp s1) (= s1 s2) (= s2 tmp))))
I think this is revealing that you don't understand that Z3 "variables" are actually constants and you cannot actually swap them. In each model they take on a single value only. There is no temporal component to constants. = means "is equal?" and not "make it equal!".

Z3 / c++ API for quantifiers

I am using z3 with the C++ API for the example:
context c;
sort I = c.int_sort();
sort B = c.bool_sort();
expr x = c.int_const("x");
expr x1 = c.int_const("x1");
func_decl p1 = function("p1", I, B);
func_decl p2 = function("p2", I, B);
solver s(c);
s.add(forall(x, (implies(p1(x), ((p2(x)))))));
s.add(p1(x1));
The generated model is:
sat
(define-fun x1 () Int
0)
(define-fun p1 ((x!1 Int)) Bool
true)
(define-fun p2 ((x!1 Int)) Bool
true)
The imagined to have the model p1(x1) and p2(x1). I tried also with the options:
p.set("mbqi", true);
p.set("smt.mbqi.max_iterations", "10000000");
p.set("auto-config", true);
But, I have the same result. Am I missing something?
Thank you.
Z3 produces a correct model, so I'm not exactly sure what the problem is. I can only imagine that there is some confusion about the argument names of the functions.
Per declaration, p1 is a function that takes an int and returns a Boolean. When Z3 builds an interpretation for this function, it names the first argument x!1 which has nothing to do with the constant function x1 (no bang). The model returned states
(define-fun x1 () Int
0)
(define-fun p1 ((x!1 Int)) Bool
true)
which means x1 is a constant function that always returns zero, i.e., x1() := 0. Additionally, p1 is a function with one argument (called x!1) and which returns true regardless of the input, i.e., for all x!1, p1(x!1) := true.

Z3 randomness of generated model values

I'm trying to influence the randomness of results for model values generated by Z3. As far as I understand, the options for this are very limited: in case of linear arithmetic, the simplex solver does not allow for random results that still satisfy the given constraints. However, there is an option smt.arith.random_initial_value ("use random initial values in the simplex-based procedure for linear arithmetic (default: false)") which I don't seem to get working:
from z3 import *
set_option('smt.arith.random_initial_value',True)
x = Int('x')
y = Int('y')
s = Solver()
s.add( x+y > 0)
s.check()
s.model()
This seems to always produce [y = 0, x = 1] as a result. Even model completion for variables unused in the given constraints seems to produce deterministic results all the time.
Any ideas or hints about how this option works?
Thanks for catching that! There was indeed a bug that caused the random seed not to be passed through to the arithmetic theory. This is now fixed in the unstable branch (fix here).
This example:
(set-option :smt.arith.random_initial_value true)
(declare-const x Int)
(declare-const y Int)
(assert (> (+ x y) 0))
(check-sat-using (using-params qflra :random_seed 1))
(get-model)
(check-sat-using (using-params qflra :random_seed 2))
(get-model)
(check-sat-using (using-params qflra :random_seed 3))
(get-model)
Now produces three different models:
sat
model
(define-fun y () Int
4294966763)
(define-fun x () Int
4294966337)
)
sat
(model
(define-fun y () Int
216)
(define-fun x () Int
4294966341)
)
sat
(model
(define-fun y () Int
196)
(define-fun x () Int
4294966344)
)
It looks like there may be another place where this option isn't passed through correctly (e.g., when using set-logic instead of calling the qflra tactic directly), we're still looking into that.

Strange results with quantified formula

I am getting some strange results when I use the ForAll quantifier. My goal is to restrict the interpretation of a function foo to the following:
\Ax,y. foo(x,y)= if x=A && y=B then C1 else C2
So if i assert the above into a context I should get back an interpretation for foo which essentially equivalent to the above. However I do not. What i get back is something like
foo(x,y)= if x=A && y=B then C1 else C1
And I have no idea why. The code I am using is below (accessing Z3 via the .net API)
let ctx = new Context()
let Sort1 = ctx.MkEnumSort("S1", [|"A";"AA"|])
let Sort2 = ctx.MkEnumSort("S2", [|"B"|])
let Sort3 = ctx.MkEnumSort("S3", [|"C1";"C2"|])
let s1 = ctx.MkSymbol "s1"
let s2 = ctx.MkSymbol "s2"
let types = [|Sort1:>Sort; Sort2:>Sort |]
let names = [|s1:>Symbol ; s2:>Symbol|]
let vars = [| ctx.MkConst(names.[0],types.[0]);ctx.MkConst(names.[1],types.[1])|]
let FDecl = ctx.MkFuncDecl("foo", [|Sort1:>Sort;Sort2:>Sort|], Sort3)
let f_body = ctx.MkITE(ctx.MkAnd(ctx.MkEq(vars.[0],getZ3Id("A",Sort1)),
ctx.MkEq(vars.[1], getZ3Id("B",Sort2))),
getZ3Id("C1",Sort3),
getZ3Id("C2",Sort3))
let f_app = FDecl.Apply vars //ctx.MkApp(FDecl, vars)
let body = ctx.MkEq(f_app, f_body)
let std_weight = uint32 1
let form = ctx.MkForall(types, names, body, std_weight, null, null, null, null)
:> BoolExpr
let s = ctx.MkSolver()
satisfy s [|form|]
s.Model
where getZ3Id converts the given string to the corresponding constant in the Enum
let getZ3Id (id,sort:EnumSort) =
let matchingConst zconst = zconst.ToString().Equals(id)
Array.find matchingConst sort.Consts
And satisfy is:
let satisfy (s:Solver) formula =
s.Assert (formula)
let res = s.Check()
assert (res = Status.SATISFIABLE)
s.Model
The model returns an interpretation for foo that returns C1 no matter what
(define-fun foo ((x!1 S1) (x!2 S2)) S3
(ite (and (= x!1 A) (= x!2 B)) C1
C1))
Could someone point out where I'm going wrong?
thanks
PS Also what is the difference between the two API calls to MkForAll - one takes sorts and names and other takes "bound constants"?
Here is my further problem:
If i define
let set1 = Set.map (fun (p:string)-> ctx.MkConst(p,Sort3))
(new Set<string>(["C1"]))
and change the body of f
let f_body = ctx.MkITE(ctx.MkAnd(ctx.MkEq(vars.[0],getZ3Id("A",Sort1))),
ctx.MkEq(vars.[1], getZ3Id("B",Sort2))),
mkZ3Set ctx set1,
ctx.MkEmptySet Sort3)
where
let mkZ3Set (ctx:Context) exprs sort =
Set.fold (fun xs x-> ctx.MkSetAdd(xs,x)) (ctx.MkEmptySet(sort)) exprs
The Z3 formula looks reasonable
form= (forall ((s1 S1))
(= (foo s1)
(ite (and (= s1 A))
(store ((as const (Array S3 Bool)) false) C1 true)
((as const (Array S3 Bool)) false))))
yet Z3 returns Unsatisfiable. Can you tell me why?
The problem is the quantifier abstraction. It does not abstract the variables you intend.
let form = ctx.MkForall(types, names, body, std_weight, null, null, null, null)
:> BoolExpr
should instead be:
let form = ctx.MkForall(vars, body, std_weight, null, null, null, null)
:> BoolExpr
The background is that Z3 exposes two different ways for you to quantify variables.
Option 1: you can abstract constants that appear in formulas. You should pass an array of those constants to the quantifier abstraction. This is the version my correction uses.
Option 2: you can abstract de-Brujin indices that appear free in a formula. You can then use the overload of ctx.MkForall that you used in your example. But it requires that whenever you refer to a bound variable, you use a bound index (something created using ctx.MkBound).

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