# Programming Z3

## TU Wien, April 5 2022

 Nikolaj Bjørner Microsoft Research nbjorner@microsoft.com

## Sections

1. Satisfiability Modulo Theories and Z3

2. Encoding and Programming Interfaces

3. SAT and SMT Algorithms using Z3

## Bio

• 90's: DTU, DIKU, Stanford
• This is me a week before fixing my thesis topic
• Late 90's: Kestrel Institute
• Early 2000s: XDegrees (file sharing startup)
• 2002-06: Distributed File Replication @ Microsoft
• 2006: MSR: , Network Verification

## Satisfiability Modulo Theories and Z3

### Z3

• A state-of-art Satisfiability Modulo Theories (SMT) solver

• On GitHub: https://github.com/Z3prover/z3.git
• Developments: Scalable simplex solver, Strings, Horn clauses, floating points, SAT extensions, word-level bit-vector reasoning, propagators

## SAT/SMT examples

### Basic SAT

  Tie, Shirt = Bools('Tie Shirt')
s = Solver()
s.add(Or(Tie, Shirt), Or(Not(Tie), Shirt), Or(Not(Tie), Not(Shirt)))
print(s.check())
print(s.model())

### Basic SMT

I = IntSort()
f = Function('f', I, I)
x, y, z = Ints('x y z')
A = Array('A',I,I)

fml = Implies(x + 2 == y, f(Select(Store(A, x, 3), y - 2)) == f(y - x + 1))

s = Solver()
print(s.check())

### SMT in a nutshell

Is formula satisfiable under theory ?

SMT solvers use specialized algorithms for .

## Logics and Theories

 Terms Logical Connectives Quantifiers, Variables Propositional logic Theory of Equality Uninterpreted Functions Arrays Arithmetic Arrays Algebraic Data-types Bit-vectors, Floating points Sequences, Strings

### EUF

  S = DeclareSort('S')
f = Function('f', S, S)
x = Const('x', S)
solve(f(f(x)) == x, f(f(f(x))) == x)
solve(f(f(x)) == x, f(f(f(x))) == x, f(x) != x)

### Linear Arithmetic

  x, y = Reals('x y')
solve([x >= 0, Or(x + y <= 2, x + 2*y >= 6),
Or(x + y >= 2, x + 2*y > 4)])

Z3 introduces auxiliary variables and represents the formula as

Only bounds (e.g., ) are asserted during search.

### Arrays

The declaration

  A = Array('A', IntSort(), IntSort())

solve(A[x] == x, Store(A, x, y) == A)

which produces a solution where x necessarily equals y.

### Arrays as Combinators [8]

  A = Array(Index, Elem) # array sort

a[i]             # index array 'a' at index 'i'
# Select(a, i)

Store(a, i, v)   # update array 'a' with value 'v' at index 'i'
# = lambda j: If(i == j, v, a[j])

Const(v, A)      # constant array
# = lambda j: v

Map(f, a)        # map function 'f' on values of 'a'
# = lambda j: f(a[j])

Ext(a, b)        # Extensionality
# Implies(a[Ext(a, b)] == b[Ext(a, b)], a == b)

### Axiomatizing Arrays

   def array_axioms(s, index, range):
A = ArraySort(index, range)
a, b = Const('a b', A)
i, j = Const('i j', index)
v = Const('v', range)
s.add(ForAll([a, i, j, v], Store(a, i, v)[j] == If(i == j, v, a[j])))
s.add(ForAll([a, b], Implies(a[Ext(a,b)] == b[Ext(a,b)], a == b)))

### Bit-Vectors

def is_power_of_two(x):
return And(x != 0, 0 == (x & (x - 1)))
x = BitVec('x', 4)
prove(is_power_of_two(x) == Or([x == 2**i for i in range(4)]))

The absolute value of a variable can be obtained using addition and xor with a sign bit.

v = BitVec('v',32)
prove(If(v > 0, v, -v) == (v + mask) ^ mask)

### Floating points

x = FP('x', FPSort(3, 4))
print(10 + x)

### Sequences

s, t, u = Strings('s t u')
prove(Implies(And(PrefixOf(s, t), SuffixOf(u, t),
Length(t) == Length(s) + Length(u)),
t == Concat(s, u)))

One can concatenate single elements to a sequence as units:

s, t = Consts('s t', SeqSort(IntSort()))
solve(Concat(s, Unit(IntVal(2))) == Concat(Unit(IntVal(1)), t))
prove(Concat(s, Unit(IntVal(2))) != Concat(Unit(IntVal(1)), s))

### Algebraic Data-types

Tree = Datatype('Tree')
Tree.declare('Empty')
Tree.declare('Node', ('left', Tree), ('data', Z), ('right', Tree))
Tree = Tree.create()
t = Const('t', Tree)
solve(t != Tree.Empty)

It may produce the solution

[t = Node(Empty, 0, Empty)]

Similarly, one can prove that a tree cannot be a part of itself.

prove(t != Tree.Node(t, 0, t))