Dafny的函数按方法失败无法证明正确的后置条件。

huangapple go评论112阅读模式
英文:

Dafny function-by-method fails to prove correct post-conditions

问题

我的代码只包含function子句,但我需要遍历一些集合。根据官方文档,我使用以下代码遍历我的集合:

var node_set := tree_nodes(t);
var result := 0.0;
while node_set != {}
  decreases |node_set|
  // 这里省略了一些不变量
{
  // 这里省略了一些语句
}

但是我想保持我的代码具有function类型,所以我使用了这里function-by-method

所以代码看起来像这样:

function tree_height(t: Tree) : (h: real)
// 这里省略了一些确保条件
{
  match t
  case Empty =>
    0.0
  case Node(_, _, _, _, _) =>
    // 这里省略了一些代码
} by method {
  match t
  case Empty =>
    {
      h := 0.0;
      return;
    }
  case Node(_, _, _, _, _) =>
    {
      // 这里省略了遍历集合的代码
      h := result;
      return;
    }
}

但是 Dafny 说无法维护后置条件。然而,我将两个部分(函数体和方法体)分开,如下所示:

function tree_height(t: Tree) : (h: real)
// 这里省略了一些确保条件
{
  match t
  case Empty =>
    0.0
  case Node(_, _, _, _, _) =>
    // 这里省略了一些代码
}

method test(t: Tree) returns (h: real)
// 这里省略了一些确保条件
{
  match t
  case Empty =>
    {
      h := 0.0;
      return;
    }
  case Node(_, _, _, _, _) =>
    {
      // 这里省略了一些代码
      h := result;
      return;
    }
}

这样,函数和方法都通过了验证。函数体和方法体中的语句与function-by-method中的语句相同。

有人知道为什么吗?或者有一些在function体中遍历集合的方法吗?

现在我发现了另一个简单的例子,它遇到了相同的问题:

function setToSequence(s: set<int>) : (r: seq<int>)
  ensures multiset(s) == multiset(r)
{
  []
} by method {
  var copy := s;
  r := [];
  while copy != {}
    decreases |copy|
  {
    var k: int :| k in copy;
    r := r + [k];
    copy := copy - {k};
  }
}

method t(s: set<int>) returns (r: seq<int>)
{
  var copy := s;
  r := [];
  while copy != {}
    decreases |copy|
  {
    var k: int :| k in copy;
    r := r + [k];
    copy := copy - {k};
  }
}
英文:

My codes only contains function clauses, but I need travel some sets. According to the official documentation , I use the code to travel my set:

      var node_set := tree_nodes(t);
      var result := 0.0;
      while node_set != {}
        decreases |node_set|
        // some invariants here, omitted
      {
        // some statements here, omitted
      }

But I wanna keep my code with function type, so I use function-by-method from here.
So that the code looks like

function tree_height(t: Tree) : (h: real)
// some ensures here, omitted
{
  match t
  case Empty =&gt; 0.0
  case Node(_, _, _, _, _) =&gt;
    // code omitted here
}by method{
  match t
  case Empty =&gt;
    {
      h := 0.0;
      return;
    }
  case Node(_, _, _, _, _) =&gt;
    {
      // code to travel sets omitted here
      h := result;
      return;
    }
}

But dafny said the post-conditions cannot be maintained. However, I split the two parts(function body and method body) like

function tree_height(t: Tree) : (h: real)
// some ensures here, omitted
{
  match t
  case Empty =&gt; 0.0
  case Node(_, _, _, _, _) =&gt;
    // code omitted here
}

And

method test(t: Tree) returns (h: real)
// some ensures here, omitted
{
  match t
  case Empty =&gt;
    {
      h := 0.0;
      return;
    }
  case Node(_, _, _, _, _) =&gt;
    {
      // code omitted here
      h := result;
      return;
    }
}

In this way, function and method both passed the verification. Statements in function body and method body are the same with those in function-by-method.

Someone knows why?
Or some ways to travel sets in function body?

And now I find another simple example that shares the same problem:

function setToSequence(s: set&lt;int&gt;) : (r: seq&lt;int&gt;)
  ensures multiset(s) == multiset(r)
{
  []
} by method{
  var copy := s;
  r := [];
  while copy != {}
    decreases |copy|
  {
    var k: int :| k in copy;
    r := r + [k];
    copy := copy - {k};
  }
}

method t(s: set&lt;int&gt;) returns (r: seq&lt;int&gt;)
{
  var copy := s;
  r := [];
  while copy != {}
    decreases |copy|
  {
    var k: int :| k in copy;
    r := r + [k];
    copy := copy - {k};
  }
}

答案1

得分: 1

by-method子句旨在为函数添加一个高效的实现(请注意,自Dafny 4以来,默认情况下函数是非幽灵的)。函数部分和方法部分应该给出相同的结果。这就是Dafny抱怨的原因。在setToSequence中,函数部分只产生[],这通常与方法部分产生的结果不同。这回答了你为什么无法验证的问题。

这是否与你遍历集合中的所有元素的方式有关?部分是。你使用的:|运算符的技巧是可以的。然而,它是非确定性的。结果是,setToSequence的函数部分和方法部分(几乎)永远不会给出相同的结果。我认为在这里不能使用函数-方法声明。除非你提出一个确定性的定义,例如通过对元素进行排序,但这将不实际,并且高效方法定义的想法将被打败。相反,你可以使用单独的定义,并添加一个引理来说明它们产生相同的多重集(但不是相同的序列)。

关于树的更大示例,这让我感到困惑。当树被反射时,节点的node_depth可能会改变。例如,看下面的代码,它是可以验证的。

var u := Node(3, Empty, Empty);
var t := Node(1, Node(2, u, Empty), u);
assert node_depth(t, u) == 2.0;

var t' := Node(1, u, Node(2, u, Empty));
assert node_depth(t', u) == 1.0;

由于两棵树的max_depth_node都是u,t的树高度将为2.0,而t'的高度将为1.0。这不是人们对树高度定义的预期结果。因此,我没有进一步研究这个示例。

英文:

The by-method clause is meant to add an efficient implementation of the function (note that functions are by default non-ghost since Dafny 4). The function part and the method part should give the same result. This is what Dafny complains about. In setToSequence, the function part produces only [], which is in general not the same as the method part produces. This answers your question why it does not verify.

Does it have to do with your way of visiting all elements in the set (traveling the set)? Partly. Your technique with the Dafny的函数按方法失败无法证明正确的后置条件。 operator is fine. It is, however, non-deterministic. The consequence is that the function and the method part of setToSequence will (almost) never give the same result. I don't think the function-by-method declaration can be used here. Unless you come up with a deterministic definition by, e.g., sorting the elements, but that will not be practical and the idea of an efficient method definition will be defeated. Instead, you can use the separate definitions and add a lemma stating that they produce the same multisets (but not the same sequences).

Regarding the larger example with the trees, this confuses me. The node_depth of a node may change when the tree is reflected. See for instance the following code, that verifies.

var u := Node(3, Empty, Empty);
var t := Node(1, Node(2, u, Empty), u);
assert node_depth(t, u) == 2.0;

var t&#39; := Node(1, u, Node(2, u, Empty));
assert node_depth(t&#39;, u) == 1.0;

Since the max_depth_node of both trees is u, the tree height of t will be 2.0, whereas the height of t' will be 1.0. Which is not what one would expect of a tree height definition. Therefore, I have not further investigated this example.

huangapple
  • 本文由 发表于 2023年8月9日 13:57:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/76864933.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定