Programming Z3

TU Wien, April 5 2022

Nikolaj Bjørner
Microsoft Research

Contents

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 Baby
  • Late 90's: Kestrel Institute
  • Early 2000s: XDegrees (file sharing startup)
  • 2002-06: Distributed File Replication @ Microsoft
  • 2006: MSR: Z3, Network Verification SecGuru

Satisfiability Modulo Theories and Z3

Z3

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



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

Z3 for software ++

MicrosoftToolsBasedOnZ3

SAT/SMT examples

Basic SAT

\[\begin{mdmathpre}%mdk (\mathid{Tie}~\vee \mathid{Shirt})~\wedge (\neg \mathid{Tie}~\vee \mathid{Shirt})~\wedge (\neg \mathid{Tie}~\vee \neg \mathid{Shirt}) \end{mdmathpre}%mdk \]



  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()
s.add(Not(fml))
print(s.check())

SMT in a nutshell

Is formula $\varphi$ satisfiable under theory ${\color{red}{T}}$?


SMT solvers use specialized algorithms for ${\color{red}{T}}$.

Complexity

Complexity

SAT/SMT

SATSMT

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 $s_1, s_2$ and represents the formula as

\[\begin{mdmathpre}%mdk \mdmathindent{2}\mathid{s}_1~\equiv \mathid{x}~+~\mathid{y},~\mathid{s}_2~\equiv \mathid{x}~+~2\mathid{y},\\ \mdmathindent{2}\mathid{x}~\geq 0,~(\mathid{s}_1~\leq 2~\vee \mathid{s}_2~\geq 6),~(\mathid{s}_1~\geq 2~\vee \mathid{s}_2~>~4) \end{mdmathpre}%mdk \]

Only bounds (e.g., $s_1 \leq 2$) are asserted during search.

An Overview of Arithmetic Theories

ArithmeticTheories

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)
mask = v >> 31
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))

Extended SAT queries