Z3 使用数据类型和forall在SMT-LIB中建模和分析一种小型编程语言

Z3 使用数据类型和forall在SMT-LIB中建模和分析一种小型编程语言,z3,smt,Z3,Smt,我正在尝试用SMT-LIB2为一种小型编程语言建模。 我的目的是表达一些程序分析问题,并用Z3解决它们。 我想我误解了所有的声明。 下面是我的代码片段 ; barriers.smt2 (declare-datatype Barrier ((barrier (proc Int) (rank Int) (group Int) (complete-time Int)))) ; barriers in the same group complete at the same time (assert

我正在尝试用SMT-LIB2为一种小型编程语言建模。 我的目的是表达一些程序分析问题,并用Z3解决它们。 我想我误解了所有的声明。 下面是我的代码片段

; barriers.smt2

(declare-datatype Barrier ((barrier (proc Int) (rank Int) (group Int) (complete-time Int))))

; barriers in the same group complete at the same time
(assert
  (forall ((b1 Barrier) (b2 Barrier))
          (=> (= (group b1) (group b2))
              (= (complete-time b1) (complete-time b2)))))
(check-sat)
当我运行
z3-smt2 barriers.smt2时,结果是
unsat
。 我认为我的分析问题的一个例子是一系列类似于上述的
断言和一系列带有描述输入程序的断言的const声明

(declare-const b00 Barrier)
(assert (= (proc b00) 0))
(assert (= (rank b00) 0))
...

但显然,我错误地使用了
forall
表达式,因为我希望z3能够为该断言确定一个令人满意的模型。我遗漏了什么?

值得一提的是,我能够通过使用排序和未解释的函数而不是数据类型向前推进

(declare-sort Barrier 0)
(declare-fun proc (Barrier) Int)
(declare-fun rank (Barrier) Int)
(declare-fun group (Barrier) Int)
(declare-fun complete-time (Barrier) Int)

然后,
forall
断言是sat。我仍然希望您能解释一下为什么这一更改会产生不同。

当您像这样声明
数据类型时:

(declare-datatype Barrier 
      ((barrier (proc Int) 
                (rank Int) 
                (group Int) 
                (complete-time Int))))
你正在生成一个“自由”生成的宇宙。对于笛卡尔乘积
Int x Int x Int x Int
中的每个可能元素来说,这只是一个花哨的词,表示存在一个
Barrier

稍后,当你说:

(assert
  (forall ((b1 Barrier) (b2 Barrier))
          (=> (= (group b1) (group b2))
              (= (complete-time b1) (complete-time b2)))))
您是在断言所有可能的
b1
b2
值,并且您是说如果组相同,则完成时间必须相同。但请记住,数据类型是自由生成的,因此z3告诉您
unsat
,这意味着从笛卡尔乘积中选取
b1
b2
的适当值显然违反了您的断言,因为笛卡尔乘积中有大量违反此断言的居民对

当然,你想说的是:“我只是想让你注意那些满足这个属性的元素。我不关心其他元素。”但你不是这么说的。为此,只需将断言转换为函数:

(define-fun groupCompletesTogether ((b1 Barrier) (b2 Barrier)) Bool
   (=> (= (group b1) (group b2))
       (= (complete-time b1) (complete-time b2))))
然后,用它作为你暗示的假设。下面是一个愚蠢的例子:

(declare-const b00 Barrier)
(declare-const b01 Barrier)

(assert (=> (groupCompletesTogether b00 b01)
            (> (rank b00) (rank b01))))
(check-sat)
(get-model)
这张照片是:

sat
(model
  (define-fun b01 () Barrier
    (barrier 3 0 2437 1797))
  (define-fun b00 () Barrier
    (barrier 2 1 1236 1796))
)
这不是一个特别有趣的模型,但它仍然是正确的。我希望这能解释这个问题,并让您走上正确的建模之路。您也可以将该谓词与其他事实结合使用,我怀疑在
sat
场景中,这才是您真正想要的。所以,你可以说:

(assert (distinct b00 b01))
(assert (and (= (group b00) (group b01))
             (groupCompletesTogether b00 b01)
             (> (rank b00) (rank b01))))
您将得到以下模型:

sat
(model
  (define-fun b01 () Barrier
    (barrier 3 2436 0 1236))
  (define-fun b00 () Barrier
    (barrier 2 2437 0 1236))
)
现在变得更有趣了

一般来说,虽然SMTLib确实支持量词,但您应该尽量远离它们,因为它会使逻辑半可判定。一般来说,你只想写量化的公理,就像你对未解释的常数所做的那样。(也就是说,引入一个新的函数/常数,让它不被解释,但要断言一个它应该满足的通用量化公理。)这可以让你建模一组有趣的函数,尽管量词可以使解算器响应未知,所以如果可以的话,最好避免它们


[旁注:根据经验,当您在自由生成的数据类型(如屏障)上编写量化公理时,它要么是微不足道的事实,要么永远不会满足,因为宇宙实际上包含了所有可以用这种方式构建的东西。将其视为Haskell/ML等中的数据类型;它只是一个包含所有可能值的容器。]

是的,这就是我在回答中提到的:使用未解释的函数并对它们进行公理化。但是这种方法也有缺点(由于存在量词,使得逻辑半可判定),如果你能在我的答案中不使用编码,那会更好。