Z3 同一公理的编码差异

Z3 同一公理的编码差异,z3,smt,cvc4,Z3,Smt,Cvc4,我想知道同一列表公理的这两种编码之间有什么区别: (define-sort T1 () Int) (declare-fun list_length ( (List T1) ) Int) (assert (forall ( (i T1) (l (List T1)) ) (ite (= l (as nil (List T1))) (= (list_length l) 0) (= (

我想知道同一列表公理的这两种编码之间有什么区别:

(define-sort T1 () Int)

(declare-fun list_length ( (List T1) ) Int)

(assert (forall ( (i T1) (l (List T1)) )
                (ite (= l (as nil (List T1)))
                     (= (list_length l) 0)
                     (= (list_length (insert i l)) (+ 1 (list_length l))))))

对于该基准:

(declare-const a T1)
(declare-const b T1)

(assert (not
         (= (list_length (insert b (insert a (as nil (List T1))))) 2)))

(check-sat)
不知何故,z3能够对第二个版本进行推理,但不能对第一个版本进行推理(它似乎只是永远循环)


编辑:与cvc4相同,第一个版本返回未知。

带量词的一阶逻辑基本上是半可判定的。在SMT上下文中,这意味着没有正确回答每个查询的决策过程,如
sat
/
unsat

(从理论上讲,这并不重要:如果你完全忽略效率方面的考虑,那么有一些算法可以正确地回答所有可满足的查询,但没有任何算法可以正确地推断
unsat
。在后一种情况下,它们将永远循环。但这是一个离题。)

因此,为了处理量词,SMT解决方案通常采用一种称为E匹配的技术。本质上,当它们形成一个提及未解释函数的基本术语时,它们试图实例化量化公理以匹配它们并相应地重写。这种技术在实践中非常有效,并且可以很好地扩展到典型的软件验证问题,但显然它不是万能的。有关详细信息,请参阅本文:

关于你的问题:本质上,当你拥有公理的
ite
形式时,e-matching算法根本无法找到合适的替代来实例化你的公理。出于效率考虑,e-matcher实际上会查看几乎“精确”的匹配。(对这一点持怀疑态度;它比那更聪明,但也不算太聪明。)在实践中,太聪明是不会有回报的,因为你可能会产生太多的匹配,最终会扩大你的搜索空间。和往常一样,它在实用性、效率和覆盖尽可能多的案例之间取得了平衡

Z3允许指定模式在一定程度上指导搜索,但模式使用起来相当棘手且脆弱。(我会在模式文档中为您指出正确的位置,唉,正如您自己所注意到的,z3文档站点暂时关闭了!)您可能想与它们玩一玩,看看它们是否能给您带来更好的结果。但经验法则是让你的量化公理尽可能简单明了。而你的第二个变种正好做到了这一点,与第一个变种相比。对于这个特定的问题,明确地将公理分为两部分,并分别断言这两部分以覆盖
nil
/
insert
案例。将它们组合成一条规则简直超出了当前e-matcher的能力

(declare-const a T1)
(declare-const b T1)

(assert (not
         (= (list_length (insert b (insert a (as nil (List T1))))) 2)))

(check-sat)