布吉奇怪的断言(假)行为

Boogie strange assert(false) behavior

我在和 Boogie 一起工作,我遇到了一些我不理解的行为。

我一直在使用 assert(false) 来检查之前的 assume 陈述是否荒谬。

比如下面的例子,程序验证无误...

type T;
const t1, t2: T;
procedure test()
{
  assume (t1 == t2);
  assume (t1 != t2);
  assert(false);
}

...因为 t1 == t2 && t1 != t2 是一个荒谬的说法。

另一方面,如果我有类似的东西

type T;
var map: [T]bool;
const t1, t2: T;

procedure test()
{
  assume(forall a1: T, a2: T :: !map[a1] && map[a2]);
  //assert(!map[t1]);
  assert(false);
}

assert(false) 仅在注释行未被注释时才会失败。为什么评论的断言会改变 assert(false) 的结果?

要点:如果您在程序中未提及 map[...] 的基础实例,则 Boogie 底层的 SMT 求解器将不会实例化量词。

原因如下:SMT 求解器(使用电子匹配)通常使用句法启发式来决定何时实例化量词。考虑以下量词:

forall i: Int :: f(i)

这个量词允许无限多个实例化(因为 i 范围在一个无界域上),因此尝试所有将导致非终止。相反,SMT 求解器期望语法提示指示它 i 应该实例化量词。这些提示称为 patternstriggers。在 Boogie 中,它们可以这样写:

forall i: Int :: {f(i)} f(i)

此触发器指示 SMT 求解器为程序中提到的每个 i 实例化量词 f(i)(或者更确切地说,当前证明搜索)。例如,如果您假设 f(5),那么量词将被实例化为 5 代替 i

在您的示例中,您没有明确提供模式,因此 SMT 求解器可能会通过检查量词主体为您选择一个模式。它很可能会选择 {map[a1], map[a2]}(允许多函数应用,模式必须覆盖所有量化变量)。如果您取消注释该假设,基本项 map[t1] 将可用,并且 SMT 求解器可以实例化量词 a1, a2 映射到 t1, t1。于是,矛盾成立。

有关模式的更多详细信息,请参阅 Z3 guide。可以找到更多关于模式的相关文本,例如在 this paper, 在 this paper 或在 this paper.