Assertion and Set Cardinality

ε祈祈猫儿з 提交于 2021-02-05 07:46:56

问题


  • Why does the following assertion fail?
  • In addition, why does all the assertions work, if I un-comment ASSERT 0 (line 22)?
function CountFactors(i:nat): nat
  requires i >= 1;
{
  var a := set b | 1 <= b <= i && i % b == 0;
  |a|
}
function CountFactorsSet(i:nat): set<nat>
  requires i >= 1;
{
  var a := set b | 1 <= b <= i && i % b == 0;
  a
}
method CountFactorsMethod(i:nat) returns (a: set<nat>)
  requires i >= 1;
{
  a := set b | 1 <= b <= i && i % b == 0;
}
method Main()
{
  var r:= CountFactorsMethod(2);
  print(r);
  // assert CountFactorsSet(2) == {1, 2}; // ASSERT 0
  assert CountFactors(2) == 2; // ASSERT 1
}

Here is the link to the code. I am using Dafny 2.3.0.10506


回答1:


This is a very common problem when using sets (or maps or sequences) in Dafny. The problem boils down to so-called "extensional equality" of sets.

In math, two sets are equal if they have the same elements. That is, (in pseudo Dafny syntax):

A == B   <==>   (forall x :: x in A <==> x in B)

This gives a very powerful reasoning principle for proving sets equal in two steps. Take an arbitrary element of A and show it is in B, and then take an arbitrary element of B and show it is in A.

Unfortunately, from the perspective of automated reasoning, this is a rather expensive task. If Dafny were to try to prove every set equal to every other set it could think of via this rule, it would just be way too slow.

Instead, Dafny adopts the following rule of thumb:

I, Dafny, will not try to prove two sets equal via extensionality unless you explicitly ask me to by asserting them equal in the program.

This keeps the verification performance in check by limiting the number of sets that Dafny thinks about proving equal to each other.

So, when you assert CountFactorsSet(2) == {1, 2}; you are giving Dafny permission to reason extensionally about the set CountFactorsSet(2), and that turns out to be enough to solve this problem.


As an aside, in larger programs you will have better luck if you never repeat a set comprehension twice. Instead, always wrap a comprehension inside a function, like this.

function CountFactorsSet(i:nat): set<nat>
  requires i >= 1;
{
  set b | 1 <= b <= i && i % b == 0
}
function CountFactors(i:nat): nat
  requires i >= 1;
{
  |CountFactorsSet(i)|
}

By using a function instead of the comprehension directly, you make Dafny's life much easier because it becomes "obvious" that two occurences of the function applied to the same arguments are equal, whereas it is not "obvious" to Dafny that two different syntactic occurrences of the same comprehension are equal.

It turns out not to matter in your example, but I just thought I'd warn you in case you plan to work more with comprehensions.



来源:https://stackoverflow.com/questions/63198848/assertion-and-set-cardinality

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!