申明:本文翻译自rise4fun,翻译与原文存在一些差异。
z3是由微软公司开发的一个优秀的SMT求解器(也就定理证明器),它能够检查逻辑表达式的可满足性。
使用方式
z3是一个底层的工具,它最好是作为一个组件应用到其它需要求解逻辑公式的工具中。为了方便使用,z3提供了很多的API,这些api支持的语言有C, .NET, and OCaml。当然,z3也可以通过命令行的方式来执行。(最新版本的z3可以到这里下载,微软官网上只更新到4.1)
基本命令
Z3的输入格式是SMT-LIB2.0标准(想进一步了解smt-lib标准,可以参考这个网站)的一个拓展,一个Z3脚本就是一个命令序列。你可以使用help命令去查看一系列可用的命令。echo命令用于显示一个信息。Z3的内部维持着一个栈,这个栈里面保存着用户输入的公式和申明(常量的和函数的)。declare-const命令用于申明一个给定类型的常量,declare-fun命令则用于申明一个函数。
(echo "starting Z3...")
(declare-const a Int)
(declare-fun f (Int Bool) Int)
这个例子中申明了一个函数,有两个参数,分别为Int型和Bool型,返回一个Int型的值
命令assert用于添加一个公式到z3的栈中,如果对于输入的所有公式,z3能够给出一种解释(对于用户定义的常量和函数),使得所有的公式都成立,那么我么说这个公式集是可满足的。看下面这个例子:
(declare-const a Int)
(declare-fun f (Int Bool) Int)
(assert (> a 10))
(assert (< (f a true) 100))
(check-sat)
这个例子中第一个断言表示常量a必须大于10,第二个断言表示参数为a和true的函数f必须返回一个小于100的值。check-sat命令告诉求解器去求解当前z3栈中公式的可满足性。如果栈中的公式是可满足的,z3返回sat;如果不满足,z3返回unsat;当求解器无法判断当前公式是不是可满足的就返回unknown。
如果命令集是可满足的,我们可以使用get-model命令去获取一个使z3栈中所有公式集成立解释。
(declare-const a Int)
(declare-fun f (Int Bool) Int)
(assert (> a 10))
(assert (< (f a true) 100))
(check-sat)
(get-model)
我们用z3去求解这个公式集,得到的结果如下:
sat
(model
(define-fun a () Int
11)
(define-fun f ((x!1 Int) (x!2 Bool)) Int
(ite (and (= x!1 11) (= x!2 true)) 0
0))
)
结果中sat指的是该公式集是可满足的,而model中的内容则是对公式集的一个解释,看起来不太好理解,我们下面对model中的解释方式进行说明。
model中的解释是通过定义的方式给定的,例如
(define-fun a () Int [val])
这个定义申明函数a的返回值为[val]
(define-fun f ((x!1 Int) (x!2 Bool)) Int
...
)
这个定义跟编程语言中的函数申明类似。其中x1和x2是z3给出的函数f的参数解释。在上面的例子中,对f的定义使用了ite(if-than-else)的方式。
(ite (and (= x!1 11) (= x!2 false)) 21 0)
这个表达式是说当x1=11并且x2=false的时候函数返回21,否则返回0。这样就给出了一个使得公式集满足的解。
使用范围
有时候在解决一些相似的问题的时候,我们希望去复用一些定义和断言,z3提供了push和pop命令去实现这个功能。上面也提到了,z3会维持一个全局的栈,定义和断言就存储在栈中。Push命令通过保存当前栈大小的方式创建一个范围。Pop命令则是移除与之相匹配的push之间的所有断言和申明。
(declare-const x Int)
(declare-const y Int)
(declare-const z Int)
(push)
(assert (= (+ x y) 10))
(assert (= (+ x (* 2 y)) 20))
(check-sat)
(pop) ; remove the two assertions
(push)
(assert (= (+ (* 3 x) y) 10))
(assert (= (+ (* 2 x) (* 2 y)) 21))
(check-sat)
(declare-const p Bool)
(pop)
(assert p) ; error, since declaration of p was removed from the stack
未完待续。。。。。。