I am trying to get my head around Z3. Tthough I understand the basic principles and the examples for solving basic problems.
I am creating a symbolic dynamic execution tool and use Z3 as solver. In the sample program under test there's is a condition table.Rows.Count == 1, which I have successfully manually translated into a Z3 model with a solution:
(declare-datatypes () ((Type (Char) (Decimal) (String) (Bool) (Int))))
(declare-datatypes (T S) ((Column (mkcol (first T) (second S)))))
(declare-datatypes () ((Row (Array (String (Column Type String))))))
(declare-datatypes () ((Table (Array (Int (Row))))))
(declare-const a Int)
(declare-const row Row)
(declare-const column (Column String String))
(declare-const c Row)
(declare-const x Int)
(declare-const table Table)
(declare-const table.Rows (List Row))
(declare-const list2 (List Row))
(assert (not (= table.Rows nil))) ; an actual instance (not null)
(assert (= (head table.Rows) row)) ; firt row
(check-sat)
(get-model)
And the solution
sat
(model
(define-fun table.Rows () (List Row)
(insert (Array (mkcol String "")) nil))
(define-fun row () Row
(Array (mkcol String "")))
)
I don't think my input model is perfect and then I don't know how to model the constraint (int)table.Rows[0]["name"], i.e. the named cell contains a int value.
So my question is how to model this and how to approach such translations from these more complicated code constraints written in code to Z3 constraints (i.e. type mapping). And answering basic questions like
Should the Rows property be modeled as (declare-const table.Rows (List Row)) on the table variable?
Or the Rows property should be modeled using custom sort?
Should Count be also declared or it can be "by-passed" by multiple head and tail assertions?
If you can recommend any paper or post or project, that would be awesome :)
Thanks,
Karel
With the help of the project I was able to come up with a solution
from model import *
from z3 import *
import yaml
import pprint
import inspect
import linecache
import timeit
import itertools
classes_yaml = """
-
name: DataColumn
attribute: [{name: Value, type: Integer}]
-
name: DataTable
reference: [
{name: Rows, type: DataRowCollection}
]
-
name: DataRow
reference: [
{name: Columns, type: DataColumn, multiple: true}
]
-
name: DataRowCollection
reference: [
{name: Row, type: DataRow, multiple: true}
]
"""
classes = yaml.load(classes_yaml)
DataColumn, DataTable, DataRow, DataRowCollection = load_all_classes(classes)
dc = DefineObject('col1', DataColumn)
drc = DefineObject('drc', DataRowCollection)
dt = DefineObject('dt', DataTable).force_value('Rows', drc)
dr1 = DefineObject('dr1', DataRow)
dr2 = DefineObject('dr2', DataRow)
dr3 = DefineObject('dr3', DataRow)
generate_meta_constraints()
generate_config_constraints()
solver = Optimize()
solver = Solver()
solver.add(*get_all_meta_facts())
solver.add(*get_all_config_facts())
solver.add(dt.isinstance(DataTable))
solver.add(dt['Rows'] == drc)
solver.add(drc['Row'].count() == 1)
solver.add(dc['Value'] > 5);
solver.add(dr1['Columns'].count() == 1)
print(solver)
print(solver.check())
print(cast_all_objects(solver.model()))
The problem the solver is solving is to find a DataTable with a single DataRow that has a value greater than 5 in column with the name Value.
The resulting model is
{
"dt":{
"name": "dt", "type": "DataTable", "alive": True,
"Rows": "drc"
},
"dr1":{
"name": "dr1", "type": "DataRow", "alive": True,
"Columns":[
"col1"
]
},
"col1": {
"name": "col1", "type":"DataColumn", "alive": True,
"Value":6
},
"dr3": {
"name": "dr3", "type": "DataRow", "alive": True,
"Columns": []
},
"dr2": {
"name": "dr2", "type": "DataRow", "alive": True,
"Columns": []
},
"drc": {
"name": "drc", "type": "DataRowCollection", "alive": True,
"Row": [
"dr2"
]
}
}
This is not a generic solution and the assumptions class have to be recreated every time the code constraint is different, i.e. the assumptions and initilizations will be different for "2 rows with two distinct values table" problem. Also, the class definitions for DataRow will be different as well (but there might be a trick how to generalize it a little bit).
Related
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.
{
"identity": 7,
"labels": [
"Parent"
],
"properties": {
"name": "foo1"
}
},
{
"identity": 8,
"labels": [
"Child"
],
"properties": {
"name": "bar2"
}
},
{
"identity": 9,
"labels": [
"Child"
],
"properties": {
"name": "bar1"
}
},
{
"identity": 10,
"labels": [
"Parent"
],
"properties": {
"name": "foo2"
}
}
I want to select the Parents which do not have any child with name='abc'
Expected Behaviour : I should get both Parents (foo1 and foo2) as result
Query1 :
Match (x1:Parent) with x1
optional Match (x1)-[:CHILD]-(x2:Child) with x1 , collect(x2) as x3
UNWIND x3 as x2
WITH x1 , x2 , x3
where none (x IN x3 where x.name IN ['abc'])
return DISTINCT x1
This query is returning me only 1 Parent(foo1) but it should return both parents and 2nd parent(foo2) is not connected to any child.
PS : Reason for using UNWIND is to use the variables for further WHERE clauses on variable x2.
This will get all Parents without a child name: 'abc' or without any child
MATCH (p:Parent)
WHERE NOT EXISTS((p)-[:CHILD]->(:Child {name: 'abc'}))
RETURN p
Check if the given parent, p has a child not named: 'abc'
==========
EDITED:
Personally I dislike this query because it is collecting x2 then unwinding it later. But for sake of answering the SO question,
Match (x1:Parent) with x1
optional Match (x1)-[:CHILD]->(x2:Child) with x1 , collect({x1:x1, x2:x2}) as x3
UNWIND x3 as x2
WITH x2.x1 as x1, x2.x2 as x2, x3
where NOT x2.name IN ['abc'] OR x2 is NULL
RETURN distinct x1
I collected BOTH x1 and x2 then UNWIND the collection by getting x1
and x2 during unwind. This will include other nodes that has no x2
(without a CHILD). I also simplified the where condition because x2 is also accessible rather than looping again on x3
Is it possible to treat int constants as uninterpreted in z3? For example, treat tuple(project(t, 0), project(t, 1)) = t as tuple(project(t, left), project(t, right)) = t. Context: my equations are essentially in QF_UF, but because they contain int constants I'm forced to use a logic with integer arithmetic which leads to nontermination sometimes.
You can declare Int to be an uninterpreted sort, so long as you set the logic to be something that doesn't define it already:
(set-logic QF_UF)
(declare-sort Int 0)
(declare-fun f ((Int)) Int)
(declare-fun i () Int)
(assert (distinct (f i) (f i)))
(check-sat)
z3 says:
unsat
If you add:
(assert (= (f 2) 2))
then you get:
(error "line 8 column 15: Sort mismatch at argument #1 for function (declare-fun f (Int) Int) supplied sort is Int")
which avoids confusions. (Though the error message is rather confusing to read!)
If you set your logic to be:
(set-logic QF_LIA)
then z3 says:
(error "line 3 column 18: sort already defined Int")
so, that works out as well. See Section 4.2.3 of the SMTLib specification for details.
Hope that helps!
In z3, one can declare a fully-uninterpreted const like so:
(declare-const x Int)
Similarly, one can define a fully-interpreted one like this:
(define-fun y () Int 3)
; y == 3
Given an algebraic datatype, one can have a fully interpreted tuple like the following:
(declare-datatypes () ((Item (mk-item (size Int) (weight Int)))))
(define-fun z () Item (mk-item 3 4))
; z == Item(size=3, weight=4)
... Or a non-interpreted one like below:
(declare-const i1 (Item Int Int))
Now is it possible to have a partially-interpreted data type, so that, based on the previous example, weight would be fixed for each item and size could vary?
; (bad syntax, but I hope you get the idea)
; in this case the size is varying, but weight is fixed to 5
(declare-const i2 (Item Int 5))
You should simply declare it with declare-fun and assert an equality for the portions that you know:
(declare-datatypes () ((Item (mk-item (size Int) (weight Int)))))
(declare-fun x () Item)
(assert (= (weight x) 5))
(check-sat)
(get-model)
This produces:
sat
(model
(define-fun x () Item
(mk-item 0 5))
)
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).