1

I'm trying to prove the correctness of a method that determines whether a sequence of size n is a permutation of 0,1,...,n-1. I managed to prove that whenever the method returns true then the sequence is indeed a valid permutation. Still, proving the converse is much harder (for me). I think I have the right loop invariants and triggers, but Dafny fires an assert error I can not understand. Here is the permalink, and here is the code for completeness:

method perm_imperative(s: seq<int>) returns (status : bool)
    requires |s| > 0
    ensures (status == true ) ==> (forall i    :: 0 <= i      < |s| ==> 0 <= s[i] < |s|)
    ensures (status == true ) ==> (forall i, j :: 0 <= i <  j < |s| ==> (s[i] != s[j]))
    ensures (status == false) ==> (exists i, j :: 0 <= i <= j < |s| &&  (
        (s[i] <   0  ) ||
        (s[i] >= |s| ) ||
        (s[i] == s[j]  && (i != j))))
{
    var i := 0;
    var used := new int[|s|];

    while (i < |s|)
        invariant 0 <= i <= |s|
        invariant forall k :: 0 <= k < i ==> used[k] == -1; 
    {
        used[i] := -1;
        i := i + 1;
    }

    assert (forall k :: 0 <= k < |s| ==> used[k] == -1);

    i := 0;
    while (i < |s|)
        invariant 0 <= i <= |s|
        invariant forall k    :: 0 <= k < i     ==> 0 <= s[k]       < |s|
        invariant forall k    :: 0 <= k < i     ==> 0 <= used[s[k]] <  i
        invariant forall k, m :: 0 <= k < m < i ==> s[k] != s[m]
        invariant forall k    :: 0 <= k     < i ==> used[s[k]] == k
        invariant forall k    :: 0 <= k     < i ==> s[used[s[k]]] == s[k]
        invariant forall k, m :: 0 <= k < m < i ==> used[s[k]] != used[s[m]]
        invariant forall k    :: 0 <= k < |s|   ==> (0 <= used[k] < i) || (used[k] == -1)
    {
        if (s[i] < 0)
        {
            // assert (s[i] < 0);
            return false;
        }
        else if (s[i] >= |s|)
        {
            // assert (s[i] >= |s|);
            return false;
        }
        else if (used[s[i]] != -1)
        {
            assert (0 <= s[i]       < |s|);
            assert (0 <= used[s[i]] <  i );
            //////////////////
            //              //
            // ASSERT ERROR //
            //       |      //
            //       V      //
            //////////////////
            assert (s[used[s[i]]] == s[i]);
            return false;
        }

        assert (used[s[i]] == -1);
        used[s[i]] := i;
        i := i + 1;
    }
    return true;
}
OrenIshShalom
  • 5,974
  • 9
  • 37
  • 87
  • I can't look into this in-depth right now, but you could try changing the failing `assert` into an `assume` and see if it works then or what else fails. Maybe that unblocks you. – Matthias Schlaipfer Jul 28 '19 at 02:32

1 Answers1

0

The proof goes through if you add the following loop invariant:

invariant forall n | 0 <= n < |s| :: used[n] != -1 ==> exists k | 0 <= k < i :: s[k] == n

This basically states that used correctly records all of the numbers seen in s in previous iterations.

If you add this loop invariant, you can get rid of the previous four loop invariants, as well as all the assertions in the body of the while loop:

method perm_imperative(s: seq<int>) returns (status : bool)
    requires |s| > 0
    ensures (status == true ) ==> (forall i    :: 0 <= i      < |s| ==> 0 <= s[i] < |s|)
    ensures (status == true ) ==> (forall i, j :: 0 <= i <  j < |s| ==> (s[i] != s[j]))
    ensures (status == false) ==> (exists i, j :: 0 <= i <= j < |s| &&  (
        (s[i] <   0  ) ||
        (s[i] >= |s| ) ||
        (s[i] == s[j]  && (i != j))))
{
    var i := 0;
    var used := new int[|s|];

    while (i < |s|)
        invariant 0 <= i <= |s|
        invariant forall k :: 0 <= k < i ==> used[k] == -1; 
    {
        used[i] := -1;
        i := i + 1;
    }

    assert (forall k :: 0 <= k < |s| ==> used[k] == -1);

    i := 0;
    while (i < |s|)
        invariant 0 <= i <= |s|
        invariant forall k    :: 0 <= k < i     ==> 0 <= s[k]       < |s|
        invariant forall k    :: 0 <= k < i     ==> 0 <= used[s[k]] <  i
        invariant forall k, m :: 0 <= k < m < i ==> s[k] != s[m]
        // invariant forall k    :: 0 <= k     < i ==> used[s[k]] == k
        // invariant forall k    :: 0 <= k     < i ==> s[used[s[k]]] == s[k]
        // invariant forall k, m :: 0 <= k < m < i ==> used[s[k]] != used[s[m]]
        // invariant forall k    :: 0 <= k < |s|   ==> (0 <= used[k] < i) || (used[k] == -1)
        invariant forall n | 0 <= n < |s| :: used[n] != -1 ==> exists k | 0 <= k < i :: s[k] == n
    {
        if (s[i] < 0)
        {
            // assert (s[i] < 0);
            return false;
        }
        else if (s[i] >= |s|)
        {
            // assert (s[i] >= |s|);
            return false;
        }
        else if (used[s[i]] != -1)
        {
            // assert (0 <= s[i]       < |s|);
            // assert (0 <= used[s[i]] <  i );
            // assert (s[used[s[i]]] == s[i]);
            return false;
        }

        // assert (used[s[i]] == -1);
        used[s[i]] := i;
        i := i + 1;
    }
    return true;
}
Daniel Ricketts
  • 447
  • 2
  • 8