This cheatsheet provides an overview of Z3, a powerful SMT (Satisfiability Modulo Theories) solver. Z3 is used for solving logical, arithmetic, and optimization problems, among others. This guide focuses on the most common concepts and usage patterns.
- BitVec: A vector of bits, used for bit-level operations.
- BitVecVal: A BitVec with a specific initial value.
- A
BitVec
of8 bits
=1 byte
.
- Bitwise AND, OR, XOR:
x & y
,x | y
,x ^ y
. - Bitwise NOT:
Not(x)
.
Example:
x = BitVec('x', 8)
y = BitVec('y', 8)
z = x + y # Arithmetic addition
-
ZeroExt: Extends a BitVec with trailing zeroes (fills to higher bit-width with
0
s).ZeroExt(24, foo) # Extend foo from 8-bit to 24-bit.
-
SignExt: Extends a BitVec with the sign bit (preserving the sign of the value).
SignExt(24, foo) # Extend foo with sign bit.
- Solver: The core engine for solving logical and arithmetic problems. Constraints are added using
Solver.add(...)
.
from z3 import *
x = BitVec('x', 8)
y = BitVec('y', 8)
solver = Solver()
solver.add(x + y == 10) # Adding constraint
solver.add(x - y == 2) # Adding another constraint
if solver.check() == sat:
model = solver.model()
print(f"x = {model[x]}, y = {model[y]}")
else:
print("No solution")
- Z3 uses expressions instead of actual computation, which means that arithmetic operations on BitVecs will generate symbolic expressions.
foo = BitVecVal(0, 8) bar = [BitVec(f"bar_{i}", 8) for i in range(3)] for i in range(3): foo += bar[i] # Result is the expression: (foo + bar[0] + bar[1] + bar[2])
- Constraints: Logical conditions or equations that variables must satisfy.
- Satisfiability (sat): If there is a solution that satisfies all constraints.
- Unsatisfiability (unsat): If no solution exists.
solver.add(x > 0) # Add another constraint
- You can use relational operators like
==
,!=
,>
,<
,>=
,<=
for comparing BitVecs.solver.add(x == y) # x must equal y solver.add(x > y) # x must be greater than y
- Z3 can be used for optimization problems as well, such as maximizing or minimizing a function under certain constraints.
opt = Optimize()
opt.add(x + y == 10)
opt.maximize(x)
if opt.check() == sat:
model = opt.model()
print(f"Maximized x: {model[x]}")
- Z3 supports Arrays as well. You can create and manipulate arrays of BitVecs.
arr = Array('arr', BitVecSort(8), BitVecSort(8))
solver.add(arr[0] == BitVecVal(5, 8)) # Set first element to 5
Here is a more complex example that uses multiple constraints:
from z3 import *
# Declare BitVecs
x = BitVec('x', 8)
y = BitVec('y', 8)
z = BitVec('z', 8)
# Create solver
solver = Solver()
# Add multiple constraints
solver.add(x + y == 10)
solver.add(y - z == 3)
solver.add(x * z == 5)
# Check for solution
if solver.check() == sat:
model = solver.model()
print(f"x = {model[x]}, y = {model[y]}, z = {model[z]}")
else:
print("No solution found")
Z3 is a powerful tool for solving problems that involve logical constraints, arithmetic, and optimization. This cheatsheet covers basic usage such as bit-vector operations, solver usage, constraints, and optimization. Explore Z3 documentation for more advanced features and capabilities.