使用递归函数证明迭代算法(Dafny)

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

Proving an iterative algorithm with recursive functions (Dafny)

问题

我有这个算法,用于计算给定整数数组中唯一数字的乘积:

method UniqueProduct(a: array<int>) returns (product: int)
    requires a.Length > 0
    ensures product == ProductOfUniques(a[..])
{
    var p := 1;
    var seen: seq<int> := [];

    for i := 0 to a.Length
        invariant 0 <= i <= a.Length
        invariant forall x :: x in seen ==> x in a[..i]
        invariant forall k :: 0 <= k < i && a[k] !in seen ==> seen == a[..i]
        invariant forall k :: 0 <= k < i && a[k] !in seen ==> p == ProductOfUniques(a[..i])
    {
        if !(a[i] in seen) {
            seen := seen + [a[i]];
            p := p * a[i];
        }
        assert a[..i+1][..i] == a[..i];
        assert forall k :: 0 <= k < |seen| ==> seen[k] in a[..i+1];
        assert a[i] !in seen ==> p == ProductOfUniques(a[..i+1]) == ProductOfUniques(a[..i]+[a[i]]);
        assert a[i] in seen <== p == ProductOfUniques(a[..i]);
    }
    assert a[..a.Length] == a[..];
    assert a[a.Length-1] !in seen ==> p == ProductOfUniques(a[..a.Length]) == ProductOfUniques(a[..a.Length-1]+[a[a.Length-1]]);
    assert a[a.Length-1] in seen <== p == ProductOfUniques(a[..a.Length-1]+[a[a.Length-1]]);
    product := p;
}

还有这些函数:

ghost function ProductOfUniques(a: seq<int>) : int
{
    if |a| == 0 then 1 
    else Multiply(a, [])
}

ghost function Multiply(numbers: seq<int>, seen: seq<int>) : int
    requires |numbers| > 0
{
    var tail := numbers[|numbers|-1];
    if |numbers| == 1 then 
        if tail in seen then 1 else tail 
    else 
        Multiply(numbers[..|numbers|-1], seen + [tail]) * (if tail in seen then 1 else tail) 
}

所有单独的断言和不变量都成立,但该方法的后置条件未能验证。我无法找到方法来让 Dafny 断言这是正确的 p == ProductOfUniques(a[..i])

我尝试编写一个引理来断言:

lemma X(a: seq<int>, i: int, n: int)
    requires |a| > 0
    requires 0 < i < |a|
    ensures n in a[..i] ==> ProductOfUniques(a[..i]) == ProductOfUniques(a[..i] + [n])

但这也导致了死胡同。任何帮助都将不胜感激。谢谢。

英文:

I have this algorithm that computes the product of the unique numbers in a given integer array:

method UniqueProduct(a: array&lt;int&gt;) returns (product: int)
requires a.Length &gt; 0
ensures product == ProductOfUniques(a[..])
{
var p := 1;
var seen: seq&lt;int&gt; := [];
for i := 0 to a.Length
invariant 0 &lt;= i &lt;= a.Length
invariant forall x :: x in seen ==&gt; x in a[..i]
invariant forall k :: 0 &lt;= k &lt; i &amp;&amp; a[k] !in seen ==&gt; seen == a[..i]
invariant forall k :: 0 &lt;= k &lt; i &amp;&amp; a[k] !in seen ==&gt; p == ProductOfUniques(a[..i])
//        invariant p == ProductOfUniques(a[..i])
{
if !(a[i] in seen) {
seen := seen + [a[i]];
p := p * a[i];
}
assert a[..i+1][..i] == a[..i];
assert forall k :: 0 &lt;= k &lt; |seen| ==&gt; seen[k] in a[..i+1];
assert a[i] !in seen ==&gt; p == ProductOfUniques(a[..i+1]) == ProductOfUniques(a[..i]+[a[i]]);
assert a[i] in seen &lt;== p == ProductOfUniques(a[..i]);
}
assert a[..a.Length] == a[..];
assert a[a.Length-1] !in seen ==&gt; p == ProductOfUniques(a[..a.Length]) == ProductOfUniques(a[..a.Length-1]+[a[a.Length-1]]);
assert a[a.Length-1] in seen &lt;== p == ProductOfUniques(a[..a.Length-1]+[a[a.Length-1]]);
//    assert p == ProductOfUniques(a[..]);
product := p;
}

And these functions:

ghost function ProductOfUniques(a: seq&lt;int&gt;) : int
{
if |a| == 0 then 1 
else Multiply(a, [])
}
ghost function Multiply(numbers: seq&lt;int&gt;, seen: seq&lt;int&gt;) : int
requires |numbers| &gt; 0
{
var tail := numbers[|numbers|-1];
if |numbers| == 1 then 
if tail in seen then 1 else tail 
else 
Multiply(numbers[..|numbers|-1], seen + [tail]) * (if tail in seen then 1 else tail) 
}

All the individual assertions and invariants hold, but the method's postcondition does not verify. I can't, for the life of me, figure out how to convince Dafny to assert that this is true p == ProductOfUniques(a[..i]).

I tried writing a lemma that would assert

lemma X(a: seq&lt;int&gt;, i: int, n: int)
requires |a| &gt; 0
requires 0 &lt; i &lt; |a|
ensures n in a[..i] ==&gt; ProductOfUniques(a[..i]) == ProductOfUniques(a[..i] + [n])

But this also led me to a dead end.
Any help appreciated. Thanks.

答案1

得分: 1

以下是已翻译的代码部分:

method UniqueProduct (arr: array<int>) returns (product: int)
ensures product == SetProduct((set i | 0 <= i < arr.Length :: arr[i]))
{
var p := 1;
var seen: set<int> := {};
for i := 0 to arr.Length
invariant 0 <= i <= arr.Length
invariant seen == (set k | 0 <= k < i :: arr[k])
invariant p == SetProduct((set k | 0 <= k < i :: arr[k]))
{
if ! (arr[i] in seen) {
seen := seen + { arr[i] };
p := p * arr[i];
SetProductLemma(seen, arr[i]);
}
}
product := p;
}
ghost function SetProduct(s : set<int>) : int
{
if s == {} then 1
else var x :| x in s; 
x * SetProduct(s - {x})
}
lemma SetProductLemma(s: set<int>, x: int) 
requires x in s
ensures SetProduct(s - {x}) * x == SetProduct(s)
{
if s != {}
{
var y :| y in s && y * SetProduct(s - {y}) == SetProduct(s);
if y == x {}
else {
calc {
SetProduct(s);
y * SetProduct(s - {y});
{ SetProductLemma(s - {y}, x); }
y * x * SetProduct(s - {y} - {x});
{ assert s - {x} - {y} == s - {y} - {x};}
y * x * SetProduct(s - {x} - {y});
x * y * SetProduct(s - {x} - {y});
{ SetProductLemma(s - {x}, y); }
x * SetProduct(s - {x});
}
}
}
}

希望这对你有所帮助。如果你有任何其他问题,可以继续提出。

英文:

Following two lemmas of for loop are valid by being vacuous.

invariant forall k :: 0 &lt;= k &lt; i &amp;&amp; a[k] !in seen ==&gt; seen == a[..i]
invariant forall k :: 0 &lt;= k &lt; i &amp;&amp; a[k] !in seen ==&gt; p == ProductOfUniques(a[..i])

If you replace conclusion with false it still goes through like shown below

invariant forall k :: 0 &lt;= k &lt; i &amp;&amp; a[k] !in seen ==&gt; false

This means these two invariants are not that useful.

There is data structure in dafny which holds unique element namely set. In solution below I will be using set instead of sequence.

With program below I am getting verification error that third invariant is not being maintained.

method UniqueProduct (arr: array&lt;int&gt;) returns (product: int)
ensures product == SetProduct((set i | 0 &lt;= i &lt; arr.Length :: arr[i]))
{
var p := 1;
var seen: set&lt;int&gt; := {};
for i := 0 to arr.Length
invariant 0 &lt;= i &lt;= arr.Length
invariant seen == (set k | 0 &lt;= k &lt; i :: arr[k])
invariant p == SetProduct((set k | 0 &lt;= k &lt; i :: arr[k]))
{
if ! (arr[i] in seen) {
seen := seen + { arr[i] };
p := p * arr[i];
}
}
product := p;
}
ghost function SetProduct(s : set&lt;int&gt;) : int
{
if s == {} then 1
else var x :| x in s; 
x * SetProduct(s - {x})
}

This means Dafny need some help to prove invariant being maintained. To assure myself that I am following right path I added below lemma without proof and invoke SetProductLemma(seen, arr[i]) as last statement inside if, it goes through.

lemma SetProductLemma(s: set &lt;int&gt;, x: int) 
requires x in s
ensures SetProduct(s - {x}) * x == SetProduct(s)

Now proving SetProductLemma is little tricky due to 使用递归函数证明迭代算法(Dafny) operator. I would point to read
https://leino.science/papers/krml274.html.

Full program is listed below

method UniqueProduct (arr: array&lt;int&gt;) returns (product: int)
ensures product == SetProduct((set i | 0 &lt;= i &lt; arr.Length :: arr[i]))
{
var p := 1;
var seen: set&lt;int&gt; := {};
for i := 0 to arr.Length
invariant 0 &lt;= i &lt;= arr.Length
invariant seen == (set k | 0 &lt;= k &lt; i :: arr[k])
invariant p == SetProduct((set k | 0 &lt;= k &lt; i :: arr[k]))
{
if ! (arr[i] in seen) {
seen := seen + { arr[i] };
p := p * arr[i];
SetProductLemma(seen, arr[i]);
}
}
product := p;
}
ghost function SetProduct(s : set&lt;int&gt;) : int
{
if s == {} then 1
else var x :| x in s; 
x * SetProduct(s - {x})
}
lemma SetProductLemma(s: set &lt;int&gt;, x: int) 
requires x in s
ensures SetProduct(s - {x}) * x == SetProduct(s)
{
if s != {}
{
var y :| y in s &amp;&amp; y * SetProduct(s - {y}) == SetProduct(s);
if y == x {}
else {
calc {
SetProduct(s);
y * SetProduct(s - {y});
{ SetProductLemma(s - {y}, x); }
y * x * SetProduct(s - {y} - {x});
{ assert s - {x} - {y} == s - {y} - {x};}
y * x * SetProduct(s - {x} - {y});
x * y * SetProduct(s - {x} - {y});
{ SetProductLemma(s - {x}, y); }
x * SetProduct(s - {x});
}
}
}
}

huangapple
  • 本文由 发表于 2023年6月16日 08:59:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/76486356.html
匿名

发表评论

匿名网友

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

确定