Z3 — Motorul de deducție matematică și verificare formală

O introducere practică în Z3, motorul SMT de la Microsoft Research: cum funcționează, cum se programează și cum verifică automat sute de mii de constrângeri logice pe secundă.

Ce este Z3?

Z3 este un SMT Solver (Satisfiability Modulo Theories) dezvoltat de Microsoft Research, lansat inițial în 2007. În termeni simpli, Z3 este un motor care decide automat dacă o mulțime de constrângeri logice poate fi satisfăcută și, în caz afirmativ, găsește o soluție.

Ce îl face special:

  • Automat — nu trebuie să scrii demonstrația, doar specifici problema
  • Rapid — poate verifica milioane de constrângeri pe secundă
  • General — acoperă aritmetică, vectori, șiruri, biti, funcții, cuantificatori
  • Folosit în producție — AWS, Azure, securitate, criptografie, optimizare

1. Ce înseamnă SMT?

SAT (Boolean Satisfiability)

Problema SAT: există o asignare de valori true/false pentru variabilele booleene astfel încât o formulă să fie adevărată?

$$(x \lor y) \land (\lnot x \lor z) \land (\lnot y \lor \lnot z)$$

Un SAT solver găsește automat: $x = true, y = false, z = false$ (sau alte combinații).

SMT (Satisfiability Modulo Theories)

SMT extinde SAT cu teorii de fond — nu doar true/false, ci și:

  • Aritmetică — $x + y > 10$
  • Șiruri — $s_1 \cdot s_2 = "hello"$
  • Vectori / array-uri — $a[i] = 5$
  • Bitvectori — operații pe biți
  • Tipuri de date — liste, arbori, opționale

Exemplu: În SAT nu poți exprima $x + y > 10$. În SMT (cu Z3), da.


2. Cum funcționează Z3 pe dinăuntru

Arhitectura pe stratificare

Problema (Python/SMT-LIB)
        ↓
   Parser & Type Checker
        ↓
   Simplifier (algebraic rewrite)
        ↓
   SAT Solver (CDCL) ←→ Theory Solvers (EUF, LRA, LIA, BV, Arrays...)
        ↓
   Model / unsat / unknown

CDCL — Conflict-Driven Clause Learning

Inima lui Z3 e un algoritm CDCL (Conflict-Driven Clause Learning), o extensie modernă a algoritmului DPLL:

  1. Alege o variabilă și îi atribuie o valoare
  2. Propagă — deduce consecințe (unit propagation)
  3. Dacă apare un conflict — învață o nouă clauză care previne același conflict
  4. Backtrack și reia

Theory Solvers

Când SAT solver-ul întâlnește o constrângere teoretică (ex: $x + y = 5$), o trimite la theory solver-ul specializat:

  • Arithmetic solver — eliminare Gaussiană, simplex
  • Bitvector solver — saturare pe biți, BDD-uri
  • Array solver — teoria array-urilor (read-over-write)
  • String solver — automat finit, length constraints

Cheia eficienței: Theory solver-ul comunică înapoi noi clauze (theory lemmas) care ajută SAT solver-ul să nu exploreze ramuri imposibile.


3. Programare cu Z3 (Python)

3.1 Instalare

pip install z3-solver

3.2 Exemplu de bază

from z3 import *

x = Int('x')
y = Int('y')

solve(x > 5, y < 10, x + y == 20)
# [y = 6, x = 14]

3.3 Tipuri de variabile

# Booleene
p = Bool('p')
q = Bool('q')

# Întregi
a = Int('a')
b = Int('b')

# Reale
r = Real('r')

# Bitvectori (ex: pe 8 biți)
bv = BitVec('bv', 8)

# Șiruri
s = String('s')
t = String('t')

3.4 Constrângeri combinate

x = Int('x')
y = Int('y')

solver = Solver()
solver.add(x > 0)
solver.add(y > 0)
solver.add(x * y == 12)
solver.add(x + y == 8)

if solver.check() == sat:
    print(solver.model())  # [x = 6, y = 2] sau [x = 2, y = 6]
else:
    print("unsat")

3.5 Cuantificatori

x = Int('x')
y = Int('y')

# Pentru toți x, există y astfel încât y > x
solver.add(ForAll([x], Exists([y], y > x)))

print(solver.check())  # sat (evident, întregii sunt infiniti)

3.6 Optimizare (MaxSMT)

opt = Optimize()
x = Int('x')
y = Int('y')

opt.add(x > 0)
opt.add(y > 0)
opt.add(x + y <= 10)

opt.maximize(x * y)  # găsește maximul produsului

if opt.check() == sat:
    print(opt.model())  # [x = 5, y = 5], produs = 25

4. Aplicații practice reale

4.1 AWS Zelkova (Cloud Security)

Amazon folosește Z3 pentru a verifica politicile de securitate IAM. Exemplu:

Permiți accesul la resursa S3:users/boss?
Politica: Allow dacă user = "admin" SAU IP-range = 10.0.0.0/8
User: "guest", IP: 10.0.1.5

Z3 verifică automat: există o cale prin care un utilizator neautorizat poate accesa o resursă? Dacă Z3 zice unsat, configurația e sigură.

4.2 Azure Resource Manager

Azure folosește Z3 pentru a verifica constrângeri de rețea, dependențe între resurse și politici de securitate înainte de deploy.

4.3 Criptografie și securitate

Z3 e folosit pentru:

  • Cryptanalysis — găsirea de atacuri asupra cifrurilor
  • Reverse engineering — deducerea constrângerilor dintr-un binar
  • Fuzzing ghidat — generarea de intrări care explorează căi noi (ex: AFL + Z3)

4.4 Sisteme embedded

Arm Holdings folosește Z3 pentru verificarea modelelor de memorie și a protocoalelor de coerență (cache coherence).

4.5 Planificare și optimizare

De la orarul școlar la rutarea în rețele — Z3 poate găsi soluții la probleme combinatoriale cu mii de variabile.


5. Z3 în contextul verificării formale

Comparație cu alte instrumente

InstrumentTipAutomat?ExpresivitateUtilizare
Z3SMT solverDaMedieAWS, Azure, securitate
Isabelle/HOLTheorem proverSemi-automatFoarte înaltăseL4, CakeML
CoqTheorem proverManualFoarte înaltăCompCert
CBMCBounded model checkerDaScăzutăVerificare C
DafnyVerifier înglobatSemi-automatMedieProgramare generală

Z3 e cel mai automat — nu trebuie să scrii demonstrația, doar problema. Dar e și mai puțin expresiv — nu poate demonstra orice proprietate.

Cum se integrează Z3 cu alte limbaje

LimbajBibliotecă
Pythonz3-solver
C++#include <z3++.h>
Javacom.microsoft.z3
Rustz3 (wrapper)
Gogo-z3
JavaScriptz3-solver (WebAssembly)

6. Limitări

  1. Indecidabilitate — Pentru teorii cu cuantificatori peste întregi, problema e indecidabilă (Teorema lui Gödel). Z3 poate răspunde unknown.

  2. Explozia exponențială — Probleme cu mii de variabile booleene pot fi rezolvate rapid; milioane — deja greu.

  3. Non-linear arithmetic — $x \cdot y \cdot z = 1000$ cu variabile nelineare e dificil.

  4. Sisteme hibride — Z3 nu poate verifica direct sisteme cu timp continuu (ecuații diferențiale).

  5. Necesită formalizare — Trebuie să traduci problema în constrângeri logice, ceea ce poate fi dificil.


7. Exercițiu practic: Verifică un program

Să zicem că ai funcția:

def binary_search(arr, target):
    low, high = 0, len(arr) - 1
    while low <= high:
        mid = (low + high) // 2
        if arr[mid] == target:
            return mid
        elif arr[mid] < target:
            low = mid + 1
        else:
            high = mid - 1
    return -1

Vrei să verifici că nu accesezi niciodată indexul în afara limitelor. Cu Z3:

from z3 import *

arr_len = Int('arr_len')
low = Int('low')
high = Int('high')
mid = Int('mid')

s = Solver()
s.add(arr_len > 0)
s.add(low >= 0)
s.add(high < arr_len)
s.add(low <= high)
s.add(mid == (low + high) // 2)

# Verific: mid e întotdeauna între 0 și arr_len-1?
s.add(Not(And(mid >= 0, mid < arr_len)))

result = s.check()
if result == unsat:
    print("✅ Accesul la mid e întotdeauna safe!")
else:
    print(f"❌ Contraspemplu: {s.model()}")

Concluzie

Z3 e unul dintre cele mai practice instrumente de verificare formală — rapid, automat, bine documentat și folosit în producție la scară largă. Nu trebuie să fii logician ca să-l folosești: instalezi pip install z3-solver și începi să rezolvi probleme.

Pentru aprofundare, vezi articolul despre Verificarea Matematică a Software-ului, care plasează Z3 în contextul mai larg al verificării formale.


Referințe

  • De Moura, L. & Bjørner, N. Z3: An Efficient SMT Solver (TACAS 2008)
  • Microsoft Research. Z3 Documentation. https://github.com/Z3Prover/z3
  • Barrett, C. et al. The SMT-LIB Standard (2024)
  • AWS. Zelkova: Using Z3 for Cloud Security. https://aws.amazon.com/blogs/security/
  • Bjørner, N. SMT Solving: A Tutorial