Z3 API in Python 学习笔记
简介
本教程演示了 Z3Py 的主要功能,即 Z3 在 Python 中的 API。使用 Z3Py 有很多好处。首先,对于那些见过编程语言但从未见过函数式语言的人来说,Z3Py 更加熟悉。
安装 Z3Py
可以使用以下命令从 pypi 安装 Z3Py 的 Python 绑定:
pip install z3-solver
入门示例
以下是一个简单的示例:
x = Int('x')
y = Int('y')
solve(x > 2, y < 10, x + 2*y == 7)
此示例创建了两个整数变量 x
和 y
,并定义了三个约束条件。solve
函数用于求解这些约束。
表达式简化
Z3Py 可以简化表达式:
x = Int('x')
y = Int('y')
print(simplify(x + y + 2*x + 3))
print(simplify(x < y + x + 2))
print(simplify(And(x + 1 >= 3, x**2 + x**2 + y**2 + 2 >= 5)))
/*
3 + 3*x + y
Not(y <= -2)
And(x >= 2, 2*x**2 + y**2 >= 3)
*/
配置选项
可以通过 set_option
配置 Z3 环境,例如显示结果的小数位数:
x = Real('x')
y = Real('y')
set_option(precision=30)
solve(x**2 + y**2 == 3, x**3 == 2)
/*
[y = -1.188528059421316533710369365015?,
x = 1.259921049894873164767210607278?]
*/
创建有理数
Z3Py 可以处理大整数、有理数和无理代数数:
print(RealVal(1)/3)
print(Q(1, 3))
//1/3
//1/3
布尔逻辑
Z3 支持布尔运算符,例如 And, Or, Not, Implies, If 等:
p = Bool('p')
q = Bool('q')
r = Bool('r')
solve(Implies(p, q), r == Not(q), Or(Not(p), r))
//[q = True, p = False, r = False]
使用 Solver API
Z3 提供了不同的求解器。以下示例展示了基本的 Solver API:
x = Int('x')
y = Int('y')
s = Solver()
s.add(x > 10, y == x + 2)
print(s.check()) //sat
使用求解器遍历约束
可以遍历求解器中断言的约束并收集性能统计信息:
x = Real('x')
y = Real('y')
s = Solver()
s.add(x > 1, y > 1, Or(x + y > 3, x - y < 2))
for c in s.assertions():
print(c)
print(s.statistics())
/*
x > 1
y > 1
Or(x + y > 3, x - y < 2)
(:max-memory 21.15
:memory 21.15
:num-allocs 449202
:rlimit-count 38)
*/
检查模型
以下示例展示了检查模型的基本方法:
x, y, z = Reals('x y z')
s = Solver()
s.add(x > 1, y > 1, x + y > 3, z - x < 10)
print(s.check())
m = s.model()
for d in m.decls():
print("%s = %s" % (d.name(), m[d]))
/*
sat
y = 2
x = 3/2
z = 0
*/
算术操作
Z3 支持实数和整数变量,可以在一个问题中混合使用:
x = Real('x')
y = Int('y')
a, b, c = Reals('a b c')
s, r = Ints('s r')
print(x + y + 1 + (a + s))
//x + ToReal(y) + 1 + a + ToReal(s)
使用简化器
可以使用 simplify
对 Z3 表达式应用简单的转换:
x, y = Reals('x y')
t = simplify((x + y)**3, som=True)
print(t)
//x*x*x + 3*x*x*y + 3*x*y*y + y*y*y
使用位向量
现代 CPU 和主流编程语言使用定长位向量的算术。以下示例展示了如何创建位向量变量和常量:
x = BitVec('x', 16)
y = BitVec('y', 16)
print(x + 2)
print(simplify(x + y - 1))
/*x + 2
65535 + x + y*/
函数
Z3 中的函数没有副作用,是全定义的,即它们在所有输入值上都有定义:
x = Int('x')
y = Int('y')
f = Function('f', IntSort(), IntSort())
solve(f(f(x)) == x, f(x) == y, x != y)
//[x = 0, y = 1, f = [1 -> 0, else -> 1]]
使用 list comprehensions
可以使用 Python 的 list comprehensions 创建 Z3 表达式和问题:
# Create list [1, ..., 5]
print ([ x + 1 for x in range(5) ])
# Create two lists containg 5 integer variables
X = [ Int('x%s' % i) for i in range(5) ]
Y = [ Int('y%s' % i) for i in range(5) ]
print (X)
# Create a list containing X[i]+Y[i]
X_plus_Y = [ X[i] + Y[i] for i in range(5) ]
print (X_plus_Y)
# Create a list containing X[i] > Y[i]
X_gt_Y = [ X[i] > Y[i] for i in range(5) ]
print (X_gt_Y)
print (And(X_gt_Y))
# Create a 3x3 "matrix" (list of lists) of integer variables
X = [ [ Int("x_%s_%s" % (i+1, j+1)) for j in range(3) ]
for i in range(3) ]
pp(X)
# [1, 2, 3, 4, 5]
#[x0, x1, x2, x3, x4]
#[x0 + y0, x1 + y1, x2 + y2, x3 + y3, x4 + y4]
#[x0 > y0, x1 > y1, x2 > y2, x3 > y3, x4 > y4]
#And(x0 > y0, x1 > y1, x2 > y2, x3 > y3, x4 > y4)
#[[x_1_1, x_1_2, x_1_3],
# [x_2_1, x_2_2, x_2_3],
# [x_3_1, x_3_2, x_3_3]]
示例问题
问题1:
d, a, t, v_i, v_f = Reals('d a t v__i v__f')
equations = [d == v_i * t + (a*t**2)/2, v_f == v_i + a*t]
problem = [v_i == 30, v_f == 0, a == -8]
solve(equations + problem)
问题2:
d, a, t, v_i, v_f = Reals('d a t v__i v__f')
equations = [d == v_i * t + (a*t**2)/2, v_f == v_i + a*t]
problem = [v_i == 0, t == 4.10, a == 6]
solve(equations + problem)
位操作技巧
使用 Z3 证明位操作技巧是否有效:
x = BitVec('x', 32)
powers = [2**i for i in range(32)]
fast = And(x != 0, x & (x - 1) == 0)
slow = Or([x == p for p in powers])
prove(fast == slow)
print ("trying to prove buggy version...")
fast = x & (x - 1) == 0
prove(fast == slow)
#And(x != 0, x & x - 1 == 0)
#proved
#trying to prove buggy version...
#counterexample
#[x = 0]
证明有相反的位
x = BitVec('x', 32)
y = BitVec('y', 32)
# Claim: (x ^ y) < 0 iff x and y have opposite signs
trick = (x ^ y) < 0
# Naive way to check if x and y have opposite signs
opposite = Or(And(x < 0, y >= 0),
And(x >= 0, y < 0))
prove(trick == opposite)
//proved