我想知道同一列表公理的这两种编码之间有什么区别:
(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))))))
和
(define-sort T1 () Int)
(declare-fun list_length ( (List T1) ) Int)
(assert (= (list_length (as nil (List T1))) 0))
(assert (forall ( (i T1) (l (List T1)) )
(= (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相同,第一个版本返回未知。
答案 0 :(得分:3)
带有量词的一阶逻辑实质上是半确定的。在SMT上下文中,这意味着没有决策程序以sat
/ unsat
的形式正确回答每个查询。
(从理论上讲,并不是那么重要:如果您完全忽略了效率方面的考虑,那么有些算法可以正确回答所有可满足的查询,但是有 no 个算法可以正确推断{{ 1}}。在后一种情况下,它们将永远循环。但这是题外话。)
因此,为了处理量词,SMT求解器通常采用一种称为E匹配的技术。本质上,当它们形成提及未解释功能的基本术语时,它们会尝试实例化量化公理以匹配它们并相应地重写。这种技术在实践中可以非常有效,并且可以很好地解决典型的软件验证问题,但显然不是万能药。有关详细信息,请参见本文:https://pdfs.semanticscholar.org/4eb2/c5e05ab5c53f20c6050f8252a30cc23561be.pdf。
关于您的问题:本质上,当您拥有公理的unsat
形式时,电子匹配算法根本无法找到适当的替代来实例化您的公理。出于效率方面的考虑,电子匹配器实际上会查看几乎“完全”匹配。 (用一粒盐来做这件事;它比那要聪明,但要花很多钱。)在这里太聪明了,在实践中几乎没有回报,因为您最终可能会产生太多的匹配项并最终扩大搜索空间。像往常一样,它在实用性,效率和覆盖尽可能多的案例之间取得了平衡。
Z3允许指定模式在一定程度上指导搜索,但是模式使用起来非常棘手且脆弱。 (我已经把您指向了模式文档中的正确位置,可惜您自己注意到z3文档站点暂时关闭了!)您可能想和他们一起玩耍,看看它们是否能为您带来更好的结果。但是,经验法则是使量化公理尽可能简单明了。与第一个变体相比,您的第二个变体恰好做到了。对于这个特定问题,一定要将公理分成两个部分,并分别断言以涵盖ite
/ nil
的情况。将它们组合成一条规则完全超出了当前电子匹配器的功能。