在Dafny中验证Max函数时断言是否违反?

时间:2018-04-30 09:02:06

标签: dafny

以下程序导致assert v==40违反断言:为什么?当数组a只包含一个元素时,可以验证程序。

method Max(a:array<int>) returns(max:int)
requires 1<=a.Length
ensures forall j:int :: 0<=j< a.Length ==> max >= a[j]
ensures exists j:int :: 0<=j< a.Length &&  max == a[j]
{
   max:=a[0];
   var i :=1;
   while(i < a.Length)
   invariant 1<=i<=a.Length
   decreases a.Length-i
   invariant forall j:int :: 0<=j<i ==> max >= a[j]
   invariant exists j:int :: 0<=j<i &&  max == a[j]
   {
     if(a[i] >= max){max := a[i];}
     i := i + 1;
   }
}
method Test(){
   var a := new int[2];
   a[0],a[1] := 40,10;
   var v:int:=Max(a);
   assert v==40;
}

1 个答案:

答案 0 :(得分:1)

这确实很奇怪!它归结为Dafny处理量词的方式。

让我们从人类层面证明断言实际上是有效的。从Max的后置条件,我们知道关于v的两件事:(1)它至少与a中的每个元素一样大,(2)它等于某个元素a。通过(2),v是40或10,而按(1),v至少为40(因为它至少与a[0]一样大,是40)。由于10不是至少40,v不能是10,所以它必须是40。

现在,为什么Dafny不能自动理解这一点?这是因为(1)中的forall量词。 Dafny(真的是Z3)在内部使用&#34;触发器&#34;近似通用量词的行为。 (没有任何近似值,量词的推理通常是不可判定的,因此需要这样的限制。)触发工作的方式是对于程序中的每个量词,推断出一种称为触发器的句法模式。然后,除非触发器匹配上下文中的某个表达式,否则完全忽略该量词。

在此示例中,fact(1)将触发a[j]。 (您可以通过将鼠标悬停在量词上来查看在Visual Studio或VSCode或emacs中推断出的触发器。或者在命令行上,通过传递选项/printTooltips并查找行号。)这意味着量词将会除非在上下文中对于任何表达式a[foo]都有foo形式的表达式,否则将被忽略。然后(1)将使用foo j进行实例化,我们将学习max >= a[foo]

由于您的Test方法断言未提及a[foo]形式的任何表达式,因此Dafny将无法使用事实(1),从而导致虚假断言违规。

修复Test方法的一种方法是添加断言

assert v >= a[0];

就在另一个断言之前。这是我们在人类水平证明中需要的事实(1)的关键结果,它包含与触发器匹配的表达式a[0],允许Dafny实例化量词。然后证据的其余部分会自动完成。

有关一般触发器以及如何手动编写触发器的详细信息,请参阅this answer