Z3求解器基本指南

原文链接: https://blog.csdn.net/alu_xd/article/details/52169885

这是博主 alu_xd 翻译的官网上的z3手册,但是不全面,想看全面的手册见官网手册:z3手册

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提供了pushpop命令去实现这个功能。上面也提到了,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

配置

set-option命令用来配置z3,该命令有几个选项去控制z3的行为。这些选项中的一部分只能够在断言和申明命令之前设定。使用reset命令可以擦出所有的断言和申明,在使用reset命令后,所有的配置选项都可以设置了。

(set-option :print-success true) 
(set-option :produce-unsat-cores true) ; enable generation of unsat cores
(set-option :produce-models true) ; enable model generation
(set-option :produce-proofs true) ; enable proof generation
(declare-const x Int)
(set-option :produce-proofs false) ; error, cannot change this option after a declaration or assertion
(echo "before reset")
(reset)
(set-option :produce-proofs false) ; ok

其中配置选项print-success true是相当有用的,这个选项可以用来判断你的输入文件中的命令是否有错误,如果命令没有错误,会输出success,有错的话则会报出具体的出错位置。

命题逻辑

z3中预定义的类型Bool是所有的Boolean命题表达式的类型。Z3支持常用的Boolean运算符and,or,xor,not,=>(蕴含),ite(if-than-else)。蕴含式通常用=表示。下面的例子中我们展示了如何证明这个命题:如果p蕴含q并且q蕴含r,那么p蕴含r。我们通过证明该命题的否命题是不满足的方式来证明该命题。注意该例子中我们给定了函数conjecture一个具体的定义。

(declare-const p Bool)
(declare-const q Bool)
(declare-const r Bool)
(define-fun conjecture () Bool
    (=> (and (=> p q) (=> q r))
        (=> p r)))
(assert (not conjecture))
(check-sat)

无解释的函数与常量

SMT公式的基本构建单元就是常量和函数,常量说白了就是没有参数的函数,所以,基本的构建单元事实上都是函数。

(declare-fun f (Int) Int)
(declare-fun a () Int) ; a is a constant
(declare-const b Int) ; syntax sugar for (declare-fun b () Int)
(assert (> a 20))
(assert (> b a))
(assert (= (f 10) 1))
(check-sat)
(get-model)

不像程序语言,函数有边际效应,能够抛异常或者没有返回值,传统的一阶逻辑表达式函数没有边际效应,它是完整的。

纯的一阶逻辑函数和常量是无解释的,或者说是自由的,这意味着没有一个先天的解释是附加在改表达式上的。这和那些有理论签名的函数形成对比,比如数学上函数+有一个固定的标准的解释(对两个数进行加操作)。无解释的函数和常量是最大限度自由的,它们允许任何和约束一致的解释。

为了说明无解释的类型,在下面的例子中,我们定义了一个无解释的类型A,并且定义了两个类型为A的常量x和y。

(declare-sort A)
(declare-const x A)
(declare-const y A)
(declare-fun f (A) A)
(assert (= (f (f x)) x))
(assert (= (f x) y))
(assert (not (= x y)))
(check-sat)
(get-model)

求解该约束集得到的结果如下:

sat
(model 
  ;; universe for A:
  ;;   A!val!0 A!val!1 
  ;; -----------
  ;; definitions for universe elements:
  (declare-fun A!val!0 () A)
  (declare-fun A!val!1 () A)
  ;; cardinality constraint:
  (forall ((x A)) (or (= x A!val!0) (= x A!val!1)))
  ;; -----------
  (define-fun y () A
    A!val!1)
  (define-fun x () A
    A!val!0)
  (define-fun f ((x!1 A)) A
    (ite (= x!1 A!val!0) A!val!1
    (ite (= x!1 A!val!1) A!val!0
      A!val!1)))
)

结果模型中为A给定了一个抽象的值,因为A是无解释的。

猜你喜欢

转载自blog.csdn.net/weixin_39408343/article/details/102680614
Z3
今日推荐