Reputation: 15
I want to prove the correctness of 2D matrix addition in Dafny (using the VSCode extension) with the aim of identifying SMT-proveable loop invariants. I believe this should be fairly straight forward as the computations only involve addition.
My implementation involves 2 loops. An outer loop iterating over the rows. And an inner loop iterating over the columns. Dafny is throwing errors regarding my outer loop invariant.
I believe I have not 'helped' Dafny enough with reasoning about the invariant. However I am unsure with how to proceed.
Below is my implementation. Without the assertion at the end of the outer loop, Dafny reports this invariant could not be proved to be maintained by the loop
. When I include the assertion of the invariant at the end of the outer loop body, Dafny reports assertion might not hold
.
method Order2Addition(m1: array2<int>, m2: array2<int>, m3: array2<int>)
requires m1 != m3 && m2 != m3
requires m1.Length0 == m2.Length0 == m3.Length0 && m1.Length1 == m2.Length1 == m3.Length1
ensures CorrectMatrixAddition(m1, m2, m3)
modifies m3
{
var rows: int := m1.Length0;
assert(rows == m2.Length0 && rows == m3.Length0);
var cols: int := m1.Length1;
assert(cols == m2.Length1 && cols == m3.Length1);
var i: int := 0;
while (i < rows)
invariant 0 <= i <= rows
// All rows up to row i-1 are correct
invariant forall i':int, j':int :: 0 <= i' < i && 0 <= j' < cols ==> m3[i', j'] == m1[i', j'] + m2[i', j']
decreases rows - i
modifies m3
{
var j: int := 0;
while (j < cols)
invariant 0 <= j <= cols
// All column values on row i, up to column j-1 are correct
invariant forall j': int :: 0 <= j' < j ==> m3[i, j'] == m1[i, j'] + m2[i, j']
decreases cols - j
modifies m3
{
m3[i, j] := m1[i, j] + m2[i, j];
j := j + 1;
}
i := i + 1;
assert(forall i':int, j':int :: 0 <= i' < i && 0 <= j' < cols ==> m3[i', j'] == m1[i', j'] + m2[i', j']);
}
}
predicate CorrectMatrixAddition(m1: array2<int>, m2: array2<int>, m3: array2<int>)
requires m1.Length0 == m2.Length0 == m3.Length0 && m1.Length1 == m2.Length1 == m3.Length1
requires m1 != m3 && m2 != m3
reads m1, m2, m3
{
forall i: int, j: int {:trigger m3[i,j]} :: 0 <= i < m1.Length0 && 0 <= j < m1.Length1 ==> m3[i, j] == m1[i, j] + m2[i, j]
}
Upvotes: 0
Views: 54
Reputation: 2097
The reason your program does not verify is that the inner loop also needs the invariant that "All rows up to row i-1 are correct". (My book "Program Proofs" discusses this point in section 14.1.1.)
You can also shorten many things. The following is a version that verifies:
method Order2Addition(m1: array2<int>, m2: array2<int>, m3: array2<int>)
requires m1 != m3 && m2 != m3
requires m1.Length0 == m2.Length0 == m3.Length0 && m1.Length1 == m2.Length1 == m3.Length1
ensures CorrectMatrixAddition(m1, m2, m3)
modifies m3
{
var rows, cols := m1.Length0, m1.Length1;
var i := 0;
while i < rows
invariant 0 <= i <= rows
// All rows up to row i-1 are correct
invariant forall i', j' :: 0 <= i' < i && 0 <= j' < cols ==> m3[i', j'] == m1[i', j'] + m2[i', j']
{
var j := 0;
while j < cols
invariant 0 <= j <= cols
// All column values on row i, up to column j-1 are correct
invariant forall i', j' :: 0 <= i' < i && 0 <= j' < cols ==> m3[i', j'] == m1[i', j'] + m2[i', j']
invariant forall j' :: 0 <= j' < j ==> m3[i, j'] == m1[i, j'] + m2[i, j']
{
m3[i, j] := m1[i, j] + m2[i, j];
j := j + 1;
}
i := i + 1;
}
}
predicate CorrectMatrixAddition(m1: array2<int>, m2: array2<int>, m3: array2<int>)
requires m1.Length0 == m2.Length0 == m3.Length0 && m1.Length1 == m2.Length1 == m3.Length1
reads m1, m2, m3
{
forall i, j :: 0 <= i < m1.Length0 && 0 <= j < m1.Length1 ==> m3[i, j] == m1[i, j] + m2[i, j]
}
You can also use for
loops instead of the while
loops, if you prefer.
In fact, you can also do this example in Dafny without any loops at all, which saves you having to write any loop invariants (but I suppose that won't help your goal of trying to identify SMT-solvable loop invariants). You can replace the body of the method with this aggregate statement:
forall i, j | 0 <= i < m1.Length0 && 0 <= j < m1.Length1 {
m3[i, j] := m1[i, j] + m2[i, j];
}
Upvotes: 0
Reputation: 1300
Not quite sure what is going wrong with your implementation. My guess would be you might have the wrong index specified somewhere or the wrong range.
I have a similar implementation here
I applied your predicate to that method and it didn't immediately verify because of your precondition that the arrays be different, by relaxing/removing that require it then verified.
Arrays are mutable in Dafny and using them requires understanding the frame system which is painfully under-documented. Unless you really need the mutability using datatypes rather than arrays will make your life easier.
There are a few useful examples of using the frame system on the Dafny blog but the general gist of it is that everything that changed must be specified and everything that is unchanged must be specified, also it only applies to heap objects like arrays or classes, it does not apply to variables using primitive or immutable types. Here is an implementation of the sieve of Eratosthenes as an example.
Upvotes: 0