Z3求解器基础学习 (一) 从例子入门
Z3基础学习(一) 从例子入门
打了星号的标题表示没有掌握
一.解不等式
例1.
输入:
from z3 import *
x = Int('x')
y = Int('y')
solve(x > 2, y < 10, x + 2*y == 7)
输出:
[y = 0, x = 7]
函数Int('x')创建了一个名为x的变量。函数solve解决了一个约束系统。上面的例子用到了两个变量x和y,以及三个约束条件(x>2)、(y<10)、(x+2y7)。Z3向python使用=进行赋值。运算符<,<=,>,>=,和!=用于比较。上面的例子中,表达式x+2y==7是一个Z3约束。Z3可以解决和紧缩公式。
二.简化器
使用simplify将表达式进行化简
例1.
输入:
from z3 import *
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)
三.表达式分析
Z3提供遍历表达式的函数。
输入
from z3 import *
x = Int('x')
y = Int('y')
n = x + y >= 3
print ("num args: ", n.num_args())
print ("children: ", n.children())
print ("1st child:", n.arg(0))
print ("2nd child:", n.arg(1))
print ("operator: ", n.decl())
print ("op name: ", n.decl().name())
输出
num args: 2
children: [x + y, 3]
1st child: x + y
2nd child: 3
operator: >=
op name: >=
四.数学运算
Z3提供了所有基本的数学运算。 Z3使用Python语言的相同运算符优先级。 像Python一样,**
是幂运算。 Z3可以求解非线性多项式约束。
通常,∧
是逻辑与,∨
是逻辑或
例1.
输入
from z3 import *
x = Real('x')
y = Real('y')
solve(x**2 + y**2 > 3, x**3 + y < 5)
输出
[y = 2, x = 1/8]
其中,Real('x')
创建实际变量x
。 Z3可以表示任意大的整数,有理数(如上例)和无理代数。 一个无理数代数是具有整数系数的多项式的根。 在内部,Z3精确地代表了所有这些数字。 无理数以十进制表示形式显示,以便读取结果。
五.精度设置
set_option
用于配置Z3环境。 它用于设置全局配置选项,如结果如何显示。 选项set_option(precision = 30)
设置显示结果时使用的小数位数。 这个?
标记在1.2599210498?
中表示输出被截断。
Real类型的数据求解默认为分数形式,如果设置rational_to_decimal=True则结果位小数形式,默认显示小数点后10位,10位后用?表示后面还有数值
例1.
输入:
from z3 import *
x = Real('x')
y = Real('y')
solve(x**2 + y**2 == 3, x**3 == 2)
set_option(precision=30)
#precision=30即保留30位小数
print ("Solving, and displaying result with 30 decimal places")
solve(x**2 + y**2 == 3, x**3 == 2)
输出:
[y = -1.1885280594?, x = 1.2599210498?]
Solving, and displaying result with 30 decimal places
[y = -1.188528059421316533710369365015?,
x = 1.259921049894873164767210607278?]
例2.
以下示例演示了一个常见的错误。?
表达式1/3
是一个Python整数,而不是Z3有理数。 该示例还显示了在Z3Py中创建有理数的不同方法。 程序Q(num,den)创建一个Z3有理数,其中num是分子,den是分母。 RealVal(1)
创建一个表示数字1的Z3实数。
注意观察对应的输入输出
输入:
from z3 import *
print (1 / 3)
print (RealVal(1)/3)
print (Q(1,3))
x = Real('x')
print (x + 1/3)
print (x + Q(1,3))
print (x + "1/3")
print (x + 0.25)
输出:
0.3333333333333333
1/3
1/3
x + 3333333333333333/10000000000000000
x + 1/3
x + 1/3
x + 1/4
例3.
有理数也可以用十进制表示法显示。
输入:
from z3 import *
x = Real('x')
solve(3*x == 1)
set_option(rational_to_decimal=True)
solve(3*x == 1)
set_option(precision=30)
solve(3*x == 1)
输出:
[x = 1/3]
[x = 0.3333333333?]
[x = 0.333333333333333333333333333333?]
六.不可满足/无解
约束系统也可能没有解决方案。 在这种情况下,我们说这个系统是不可满足的。
输入:
from z3 import *
x = Real('x')
solve(x > 4, x < 0)
输出:
no solution
七.注释
像在Python中一样,注释以#
开头,并在行尾结束。 Z3Py不支持跨越多行的评论
八.BOOL LOGIC 布尔逻辑*
Z3支持布尔运算符:And
, Or
, Not
, Implies
(implication), If
(if-then-else)。双蕴含符号用==
表示。 以下示例显示如何解决一组简单的布尔约束。
输入:
from z3 import *
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]
九.True、False in Python*
Python布尔常量True
和False
可用于构建Z3布尔表达式。
输入:
from z3 import *
p = Bool('p')
q = Bool('q')
print (And(p, q, True))
print (simplify(And(p, q, True)))
print (simplify(And(p, False)))
输出:
And(p, q, True)
And(p, q)
False
十.多项式与布尔组合
以下示例使用多项式和布尔约束的组合。
输入:
from z3 import *
p = Bool('p')
x = Real('x')
solve(Or(x < 5, x > 10), Or(p, x**2 == 2), Not(p))
输出:
[x = -1.4142135623?, p = False]
因为solve中的三个assert都要满足,所以Not(p)
推出p = False
, 所以x**2 == 2
要成立,所以x = +- sqrt(2)
。又因为x > 10
不可能,所以就是x < 5
,也就是正负根号2都可以,只输出一个解即可,所以输出负根号2.
十一.pop / push 断言堆栈*
输入:
from z3 import *
x = Int('x')
y = Int('y')
s = Solver()
print (s)
s.add(x > 10, y == x + 2)
print (s)
print ("Solving constraints in the solver s ...")
print (s.check())
print ("Create a new scope...")
s.push()
s.add(y < 11)
print (s)
print ("Solving updated set of constraints...")
print ()
print ("Restoring state...")
s.pop()
print (s)
print ("Solving restored set of constraints...")
print (s.check())
输出:
[]
[x > 10, y == x + 2]
Solving constraints in the solver s ...
sat
Create a new scope...
[x > 10, y == x + 2, y < 11]
Solving updated set of constraints...
unsat
Restoring state...
[x > 10, y == x + 2]
Solving restored set of constraints...
sat
可以看到,一开始求解器为空,后来加上两个断言之后,求解器的context就有了那两个断言。check求解器得到结果。sat
意味着满足(satisfied)。接下来创建了一个新的范围,可以看到新增了一个断言,这时候check的结果就是unsat
,意味着不可满足(unsatisfied). 再把新增的assert 弹出(pop)之后,可以看到又sat
了。
Solver()
命令创建一个通用求解器。约束可以使用方法add
添加。方法check()
解决了断言的约束。如果找到解决方案,结果是sat
(满足)。如果不存在解决方案,结果unsat
(不可满足)。我们也可以说,所声明的约束系统是不可行的(infeasible)。最后,求解器可能无法解决约束系统并返回unknown
(未知)。
在一些应用中,我们想要探索几个共享几个约束的类似问题。我们可以使用push
和pop
命令来做到这一点。每个求解器维护一堆断言。命令push
通过保存当前堆栈大小来创建一个新的作用域。命令pop
删除它与匹配推送之间执行的任何断言。检查方法始终对求解器断言堆栈的内容进行操作。
十二.只能求解非线形多项式约束
以下示例显示了Z3无法解决的示例。求解器在这种情况下返回未知数。回想一下,Z3可以求解非线性多项式约束,但2 ** x不是一个多项式。
输入:
from z3 import *
x = Real('x')
s = Solver()
s.add(2**x == 3)
print (s.check())
输出:
unknown
十三.遍历 / 性能统计*
以下示例显示如何遍历断言解析器中的约束,以及如何收集检查方法的性能统计信息。
输入:
from z3 import *
x = Real('x')
y = Real('y')
s = Solver()
s.add(x > 1, y > 1, Or(x + y > 3, x - y < 2))
print ("asserted constraints...")
for c in s.assertions():
print (c)
print (s.check())
print ("statistics for the last check method...")
print (s.statistics())
# Traversing statistics
#遍历统计
for k, v in s.statistics():
print ("%s : %s" % (k, v))
输出:
asserted constraints...
x > 1
y > 1
Or(x + y > 3, x - y < 2)
sat
statistics for the last check method...
(:arith-lower 1
:arith-make-feasible 3
:arith-max-columns 8
:arith-max-rows 2
:arith-upper 3
:decisions 2
:final-checks 1
:max-memory 19.63
:memory 19.23
:mk-bool-var 4
:mk-clause-binary 1
:num-allocs 433302
:num-checks 1
:rlimit-count 355
:time 0.00)
decisions : 2
final checks : 1
mk clause binary : 1
num checks : 1
mk bool var : 4
arith-lower : 1
arith-upper : 3
arith-make-feasible : 3
arith-max-columns : 8
arith-max-rows : 2
num allocs : 433302
rlimit count : 355
max memory : 19.63
memory : 19.23
time : 0.004
当Z3找到一组已确定约束的解决方案时,check
就会返回sat
, 我们就可以说Z3满足了这些约束条件,这个解决方案是这组声明约束的model。model是使每个断言约束都为真的interpretation。(大致翻译:model 模型, interpretion:实例)
十四.检查模型
以下示例显示了检查模型的基本方法。
输入:
from z3 import *
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()
print ("x = %s" % m[x])
print ("traversing model...")
for d in m.decls():
print ("%s = %s" % (d.name(), m[d]))
输出:
sat
x = 3/2
traversing model...
y = 2
x = 3/2
z = 0
在上面的例子中,函数Reals('x y z')
创建变量 x,y和z。 它是以下三句话的缩写:
x = Real('x')
y = Real('y')
z = Real('z')
注意Reals
加了s
表达式m [x]
返回模型m中对x的解释。 表达式“%s=%s”%(d.name(),m [d])
返回一个字符串,其中第一个%s被替换为d的名字(即d.name()),第二个 %s用文本表示d的解释(即m [d])。 Z3Py在需要时自动将Z3对象转换为文本表示。(这是python的写法)
十五.整数/实数变量
输入:
from z3 import *
x = Real('x')
y = Int('y')
a, b, c = Reals('a b c')
s, r = Ints('s r')
print (x + y + 1 + (a + s))
print (ToReal(y) + c)
输出:
x + ToReal(y) + 1 + a + ToReal(s)
ToReal(y) + c
函数ToReal
将整型表达式转换为实型表达式。
Z3Py支持所有基本的算术运算。
十六.算数运算
输入:
from z3 import *
a, b, c = Ints('a b c')
d, e = Reals('d e')
solve(a > b + 2,
a == 2*c + 10,
c + b <= 1000,
d >= e)
输出:
[b = 0, c = 0, e = 0, d = 0, a = 10]
十七.Simplify
simplify
命令对Z3表达式应用简单的转换。
输入
from z3 import *
x, y = Reals('x y')
# Put expression in sum-of-monomials form
t = simplify((x + y)**3, som=True)
print (t)
# Use power operator
t = simplify(t, mul_to_power=True)
print (t)
输出:
x*x*x + 3*x*x*y + 3*x*y*y + y*y*y
x**3 + 3*x**2*y + 3*x*y**2 + y**3
十八.help_simplify*
help_simplify()
命令打印所有可用的选项。 Z3Py允许用户以两种风格书写选项。 Z3内部选项名称以:开头,单词之间用 -
分隔。 这些选项可以在Z3Py中使用。 Z3Py还支持类似Python的名称,其中:被压缩并且被_
替换。
以下示例演示如何使用这两种样式。
输入:
from z3 import *
x, y = Reals('x y')
# Using Z3 native option names
print (simplify(x == y + 2, ':arith-lhs', True))
# Using Z3Py option names
print (simplify(x == y + 2, arith_lhs=True))
print ("\nAll available options:")
help_simplify()
输出:
x + -1*y == 2
x + -1*y == 2
All available options:
algebraic_number_evaluator (bool) simplify/evaluate expressions containing (algebraic) irrational numbers. (default: true)
arith_ineq_lhs (bool) rewrite inequalities so that right-hand-side is a constant. (default: false)
arith_lhs (bool) all monomials are moved to the left-hand-side, and the right-hand-side is just a constant. (default: false)
bit2bool (bool) try to convert bit-vector terms of size 1 into Boolean terms (default: true)
blast_distinct (bool) expand a distinct predicate into a quadratic number of disequalities (default: false)
blast_distinct_threshold (unsigned int) when blast_distinct is true, only distinct expressions with less than this number of arguments are blasted (default: 4294967295)
blast_eq_value (bool) blast (some) Bit-vector equalities into bits (default: false)
blast_select_store (bool) eagerly replace all (select (store ..) ..) term by an if-then-else term (default: false)
bv_extract_prop (bool) attempt to partially propagate extraction inwards (default: false)
bv_ineq_consistency_test_max (unsigned int) max size of conjunctions on which to perform consistency test based on inequalities on bitvectors. (default: 0)
bv_ite2id (bool) rewrite ite that can be simplified to identity (default: false)
bv_le2extract (bool) disassemble bvule to extract (default: true)
bv_le_extra (bool) additional bu_(u/s)le simplifications (default: false)
bv_not_simpl (bool) apply simplifications for bvnot (default: false)
bv_sort_ac (bool) sort the arguments of all AC operators (default: false)
cache_all (bool) cache all intermediate results. (default: false)
elim_and (bool) conjunctions are rewritten using negation and disjunctions (default: false)
elim_ite (bool) eliminate ite in favor of and/or (default: true)
elim_rem (bool) replace (rem x y) with (ite (>= y 0) (mod x y) (- (mod x y))). (default: false)
elim_sign_ext (bool) expand sign-ext operator using concat and extract (default: true)
elim_to_real (bool) eliminate to_real from arithmetic predicates that contain only integers. (default: false)
eq2ineq (bool) expand equalities into two inequalities (default: false)
expand_nested_stores (bool) replace nested stores by a lambda expression (default: false)
expand_power (bool) expand (^ t k) into (* t ... t) if 1 < k <= max_degree. (default: false)
expand_select_ite (bool) expand select over ite expressions (default: false)
expand_select_store (bool) conservatively replace a (select (store ...) ...) term by an if-then-else term (default: false)
expand_store_eq (bool) reduce (store ...) = (store ...) with a common base into selects (default: false)
expand_tan (bool) replace (tan x) with (/ (sin x) (cos x)). (default: false)
flat (bool) create nary applications for and,or,+,*,bvadd,bvmul,bvand,bvor,bvxor (default: true)
gcd_rounding (bool) use gcd rounding on integer arithmetic atoms. (default: false)
hi_div0 (bool) use the 'hardware interpretation' for division by zero (for bit-vector terms) (default: true)
hoist_ite (bool) hoist shared summands under ite expressions (default: false)
hoist_mul (bool) hoist multiplication over summation to minimize number of multiplications (default: false)
ignore_patterns_on_ground_qbody (bool) ignores patterns on quantifiers that don't mention their bound variables. (default: true)
ite_extra_rules (bool) extra ite simplifications, these additional simplifications may reduce size locally but increase globally (default: false)
local_ctx (bool) perform local (i.e., cheap) context simplifications (default: false)
local_ctx_limit (unsigned int) limit for applying local context simplifier (default: 4294967295)
max_degree (unsigned int) max degree of algebraic numbers (and power operators) processed by simplifier. (default: 64)
max_memory (unsigned int) maximum amount of memory in megabytes (default: 4294967295)
max_steps (unsigned int) maximum number of steps (default: 4294967295)
mul2concat (bool) replace multiplication by a power of two into a concatenation (default: false)
mul_to_power (bool) collpase (* t ... t) into (^ t k), it is ignored if expand_power is true. (default: false)
pull_cheap_ite (bool) pull if-then-else terms when cheap. (default: false)
push_ite_arith (bool) push if-then-else over arithmetic terms. (default: false)
push_ite_bv (bool) push if-then-else over bit-vector terms. (default: false)
push_to_real (bool) distribute to_real over * and +. (default: true)
rewrite_patterns (bool) rewrite patterns. (default: false)
som (bool) put polynomials in sum-of-monomials form (default: false)
som_blowup (unsigned int) maximum increase of monomials generated when putting a polynomial in sum-of-monomials normal form (default: 10)
sort_store (bool) sort nested stores when the indices are known to be different (default: false)
sort_sums (bool) sort the arguments of + application. (default: false)
split_concat_eq (bool) split equalities of the form (= (concat t1 t2) t3) (default: false)
rith-lhs
参数被设置成True(default:False),意味着所有参数都放在左手边,右手边只留下常数constant。
十九.无理数、任意大
Z3Py支持任意大的数字。 以下示例演示如何使用较大的整数,有理数和无理数执行基本算术。 Z3Py仅支持代数无理数。 代数无理数对于呈现多项式约束系统的解是足够的。 Z3Py将始终以十进制符号显示无理数,因为它更便于阅读。 内部表示可以使用sexpr()方法提取。 它以s表达式(Lisp-like)的形式显示数学公式和表达式的Z3内部表示。
输入:
from z3 import *
x, y = Reals('x y')
solve(x + 10000000000000000000000 == y, y > 20000000000000000)
print (Sqrt(2) + Sqrt(3))
print (simplify(Sqrt(2) + Sqrt(3)))
print (simplify(Sqrt(2) + Sqrt(3)).sexpr())
# sexpr() 方法可用于任何 Z3 表达式
print ((x + Sqrt(y) * 2).sexpr())
输出:
[y = 20000000000000001, x = -9999979999999999999999]
2**(1/2) + 3**(1/2)
3.1462643699?
(root-obj (+ (^ x 4) (* (- 10) (^ x 2)) 1) 4)
(+ x (* (^ y (/ 1.0 2.0)) 2.0))
二十.位向量
以下示例演示如何创建位向量变量和常量。
函数BitVec('x',16)
在Z3中创建一个位向量变量,名称为x,具有16位。 为了方便起见,可以使用整型常量在Z3Py中创建位向量表达式。 函数BitVecVal(10,32)
创建一个大小为32的位向量,其值为10。
输入:
from z3 import *
x = BitVec('x', 16)
y = BitVec('y', 16)
print (x + 2)
# 内部
print ((x + 2).sexpr())
# -1 等于 65535 对应 16-bit 整数
print (simplify(x + y - 1))
# 创建位向量常量
a = BitVecVal(-1, 16)
b = BitVecVal(65535, 16)
print (simplify(a == b))
a = BitVecVal(-1, 32)
b = BitVecVal(65535, 32)
# -1 对于 32 位整数不等于 65535
print (simplify(a == b))
输出:
x + 2
(bvadd x #x0002)
65535 + x + y
True
False
与诸如C,C ++,C#,Java等编程语言相比,有符号和无符号的位向量之间没有区别。 相反,Z3提供了算术运算的特殊符号版本,无论位向量是有符号还是无符号都是有区别的。 在Z3Py中,运算符<
,<=
,>
,> =
,/
,%
和>>
对应于有符号的版本。 相应的,无符号运算符是ULT
,ULE
,UGT
,UGE
,UDiv
,URem
和LShR
。
输入:
from z3 import *
# 创建大小为 32 的位向量
x, y = BitVecs('x y', 32)
solve(x + y == 2, x > 0, y > 0)
# 位运算符
# & 按位与
# | 按位异或
# ~ 按位取反
solve(x & y == ~y)
solve(x < 0)
# using unsigned version of <
solve(ULT(x, 0))
输出:
[y = 1, x = 1]
[y = 4294967295, x = 0]
[x = 4294967295]
no solution
二十一.位移运算
算子>>
是算术右移,而<<
是左移。 位移符号是左结合的。
输入:
from z3 import *
# 创建大小为 32 的位向量
x, y = BitVecs('x y', 32)
solve(x >> 2 == 3)
solve(x << 2 == 3)
solve(x << 2 == 24)
输出:
[x = 12]
no solution
[x = 6]
二十二.FUNCTIONS 函数
在常见的编程语言中,函数具有副作用,可以抛出异常或永不返回。但在Z3中,函数是没有副作用的,并且是完全的。也就是说,它们是在所有输入值上定义的。比如除法函数。 Z3是基于一阶逻辑的。
给定一个约束,如x + y > 3
,我们说x和y是变量。在许多教科书中,x和y被称为未解释的常量。也就是说,它们允许任何与约束x + y> 3
一致的解释。
更确切地说,纯粹的一阶逻辑中的函数和常量符号是不解释的(uninterprete)、或自由的(free),这意味着没有附加先验解释。这与属于理论特征的函数形成对比,例如函数+
具有固定的标准解释(它将两个数字相加)。不解释的函数和常量能够达到最大程度地灵活;它们允许任何与函数或常数约束相一致的解释。
为了说明未解释的函数和常量,让我们用一个例子来说明。
输入:
from z3 import *
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]]
设不解释的整数常量(又名变量)x,y;设f是一个不解释函数,它接受一个类型(又名 sort)整数的参数并生成一个整数值。这个例子说明了如何强制一个解释,其中f再次应用于x导致x,但是f应用于x不同于x。对于 f 的解(Interpretation)应该被解读为:
f(0)= 1,f(1)= 0,并且对于全部不同于0和1的a, f(a)=1。
在Z3中,我们也可以评估模型中的约束系统表达式。 以下示例显示如何使用评估方法。
输入:
from z3 import *
x = Int('x')
y = Int('y')
f = Function('f', IntSort(), IntSort())
s = Solver()
s.add(f(f(x)) == x, f(x) == y, x != y)
print (s.check())
m = s.model()
print ("f(f(x)) =", m.evaluate(f(f(x))))
print ("f(x) =", m.evaluate(f(x)))
输出:
sat
f(f(x)) = 0
f(x) = 1