将列表(on Rise4Fun)上的包含操作公理化为
(declare-fun Seq.in ((List Int) Int) Bool)
(assert (forall ((e Int))
(not (Seq.in nil e))))
(assert (forall ((xs (List Int)) (e Int))
(iff
(not (= xs nil))
(=
(Seq.in xs e)
(or
(= e (head xs))
(Seq.in (tail xs) e))))))
使Z3 4.0能够反驳断言
(declare-const x Int)
(assert (Seq.in nil x))
(check-sat) ; UNSAT, as expected
在我眼中相当于公理化
(assert (forall ((xs (List Int)) (e Int))
(ite (= xs nil)
(= (Seq.in xs e) false)
(=
(Seq.in xs e)
(or
(= e (head xs))
(Seq.in (tail xs) e))))))
结果为unknown
。
这可能是触发器的问题,还是List域特有的东西可以解释行为上的差异?
答案 0 :(得分:2)
您在rise4fun的脚本会禁用:mbqi
引擎。因此,Z3将尝试仅使用E匹配来解决问题。当没有提供模式(aka触发器)时,Z3将为我们推断触发器。 Z3使用许多启发式来推断模式/触发器。其中一个与您的脚本相关,并解释了正在发生的事情。 Z3永远不会选择产生“匹配循环”的模式/触发器。我们说当Q的实例将为P产生新的匹配时,模式/触发器P为量词Q产生匹配循环。
让我们考虑量词
(assert (forall ((xs (List Int)) (e Int))
(ite (= xs nil)
(= (Seq.in xs e) false)
(=
(Seq.in xs e)
(or
(= e (head xs))
(Seq.in (tail xs) e))))))
Z3将不选择(Seq.in xs e)
作为此量词的模式/触发器,因为它将产生匹配的循环。假设我们有一个基础术语(Seq.in a b)
。该术语与模式(Seq.in xs e)
匹配。使用a
b
实例化量词将生成与(Seq.in (tail a) b)
模式匹配的术语(Seq.in xs e)
。
使用(tail a)
和b
实例化量词将生成与(Seq.in (tail (tail a)) b)
模式匹配的术语(Seq.in xs e)
,依此类推。
在搜索过程中,Z3将使用多个阈值阻止匹配循环。但是,性能通常会受到影响。因此,默认情况下,Z3不会选择(Seq.in xs e)
作为模式。相反,它会选择(Seq.in (tail xs) e)
。此模式不会产生匹配循环,但它也会阻止Z3证明第二个和第三个查询不可满足。
E匹配引擎的任何限制通常由:mbqi
引擎处理。但是,您的脚本中已禁用:mbqi
。
如果您在脚本中提供第二个和第三个查询的模式。 Z3将证明所有示例都是unsat
。以下是具有显式模式/触发器的示例:
即使不使用模式,第一个示例也会通过,因为只需要第一个量词来证明示例为unsat
。
(assert (forall ((e Int))
(not (Seq.in nil e))))
请注意(Seq.in nil e)
是此量词的完美模式,它是Z3选择的模式。