## Microsoft Z320. Jan '14

# Introduction

Z3 is a high-performance theorem proved depeloped at Microsoft. It is distributed under Microsoft Research License Agreement which means it is open-source, free for educational and personal use. For commercial purposes a license has to be purchased which makes this project technically not Free Software.

Z3 is written in C++ but has bindings for .NET, Python and Ocaml. The bindings make it possible to manipulate Z3 data structures directly and efficiently. Microsoft Z3 also supports theories written using SMT-LIB notation which is quite different from traditional programming languages.

# SMT-LIB notation

Z3 can be invoked to parse SMT-LIB files with *-smt2* flag,
it is also possible to read commands from standard input
in which case Z3 behaves pretty much like a shell for
SMT-LIB statements:

`z3 -smt2 -in`

Defining variables is done by defining a function
that has no arguments, in this case we're defining *x*:

```
(declare-fun x () Int)
(declare-fun y () Int)
(declare-fun z () Int)
```

Defining functions is done similarily,
for instance function named *f* which accepts
one integer argument and returns an integer argument:

`(declare-fun f (Int) Int)`

Of function named *g* which accepts two integers and
returns integer:

`(declare-fun g (Int Int) Int)`

Adding constraints to build up the theory for Z3 is done
with *assert* keyword. Mathematical formulas are written
using prefix notation, thus formula like:

Becomes:

`(assert (>= (* 2 x) (+ y z)`

What is unique about Z3 as with most SMT-s is that uninterpreted functions can be defined:

```
(assert (< (f x) (g x x)))
(assert (> (f y) (g x x)))
```

To check the satifiability of the formulas a *check-sat* statement is issued:

`(check-sat)`

Which will return *sat* or *unsat* depending on whether
the conditions are satisfied or not.

# Python bindings

Converting a problem to SMT-LIB format might be time-consuming and error-prone so Z3 tries to integrate well with already existing programming languages and frameworks. In most examples all Z3 classes are loaded to local namepsace:

`from z3 import *`

Defining variables for Z3 means instantiating a Int or Real class from z3 module, the argument for the class constructor is the variable name for z3:

```
x = Int("x")
y = Int("y")
z = Int("z")
```

In case of Python there is no global theory state, thus all variables constraints and formulas have to be bound to a a solver instance:

`s = Solver()`

Since Python supports operator overloading the formula definitions become much more readable for an regular programmer:

`s.add(2*x >= y + z)`

To check whether the theory holds:

`s.check()`

# Solving sudoku with Z3

Using Z3 to solve sudoku is quite straightforward if you understand the basic
concepts of declarative programming and have familiarised yourself with the
operands that Z3 implements. In this case we are using <= (less than or equal),
>= (greater than or equal), *And*, *Distinct* operands to form the theory using Z3.

```
import sys
import itertools
from z3 import *
# Create solver
s = Solver()
# Create Z3 integer variables for matrix cells
cells = [ [ Int("z_%s_%s" % (i+1, j+1)) for j in range(9) ] for i in range(9) ]
# Add sudoku problem instance constraints
for y, line in enumerate(open(sys.argv[1], "rU").read().split("\n")):
for x, value in enumerate(line):
if value != ".":
s.add(cells[x][y] == value)
# Add cell constraints
for y in range(9):
for x in range(9):
s.add(And(1 <= cells[x][y], cells[x][y] <= 9))
# Add column constraints
for x in range(9):
s.add(Distinct(cells[x]))
# Add row constraints
for y in range(9):
s.add(Distinct([cells[x][y] for x in range(9)]))
# Add group constraints
for y in range(0,9,3):
for x in range(0,9,3):
s.add(Distinct([cells[x+i][y+j] for i, j in itertools.product(range(3), range(3))]))
# Check if constraints have been satisfied
if s.check() == sat:
m = s.model()
for y in range(9):
print "".join([str(m.evaluate(cells[x][y])) for x in range(9) ])
else:
print "Failed to solve"
```

As an input file we use *in53.pzl*:

```
.27.3....
1......7.
.83.7.24.
.71..5...
.3.....8.
...9..12.
.15.2.49.
.4......8
....1.56.
```

Running the script presented earlier:

`python solve_using_z3.py in53.pzl`

Yields in following output:

```
927431856
164852379
583679241
871265934
239147685
456983127
615728493
342596718
798314562
```

# Generating SMT-LIB notation

To generate widely recognized SMT-LIB notation of the theory we can use similar approach, only syntax differs:

```
"""
Piece of code to convert Sudoku problem files to SMT2 theories
"""
import sys
print "(set-info :source | Python ftw |)"
print "; Declare variables"
for y in range(9):
for x in range(9):
print "(declare-fun z_%d_%d () Int)" % (x,y)
print "; Sudoku problem instance constraints:"
for y, line in enumerate(open(sys.argv[1], "rU").read().split("\n")):
if not line: break
print ";", line
for x, value in enumerate(line):
if value != ".":
print "(assert (= z_%d_%d %s))" % (x,y,value)
print "; Cell constraints"
for y in range(9):
for x in range(9):
print "(assert (and (< 0 z_%d_%d) (> 10 z_%d_%d)))" % (x,y,x,y)
print "; Column constraints"
for x in range(9):
print "(assert (distinct",
for y in range(9):
print "z_%d_%d" % (x,y),
print "))"
print "; Row constraints"
for y in range(9):
print "(assert (distinct",
for x in range(9):
print "z_%d_%d" % (x,y),
print "))"
print "; Group constraints"
for y in range(0,9,3):
for x in range(0,9,3):
print "(assert (distinct",
for i in range(3):
for j in range(3):
print "z_%d_%d" % (x+i,y+j),
print "))"
print
print "; Check satisfiability"
print "(check-sat)"
print "(get-model)"
```

Theory generated by this code can be viewed and proven here.