2

I'm trying to define a matrix transpose method and functions in Dafny. I'm having difficulty defining the function version.

/** verifies **/
method transpose(matrix: array2<real>) returns (result: array2<real>)
    ensures result.Length0 == matrix.Length1 && result.Length1 == matrix.Length0
    ensures forall i, j :: 0 <= i < matrix.Length1 && 0 <= j < matrix.Length0 ==> result[i,j] == matrix[j,i]
{
    result := new real[matrix.Length1, matrix.Length0]((i,j) reads matrix => if 0 <= i < matrix.Length1 && 0 <= j < matrix.Length0 then matrix[j,i] else 0.0);
    assert result.Length0 == matrix.Length1;
    assert result.Length1 == matrix.Length0;
}
/** says it is an invalid LogicalExpresion**/
function ftranspose(matrix: array2<real>): array2<real> 
    reads matrix
    ensures ftranspose(matrix).Length0 == matrix.Length1 && ftranspose(matrix).Length1 == matrix.Length0
    ensures forall i, j :: 0 <= i < matrix.Length1 && 0 <= j < matrix.Length0 ==> ftranspose(matrix)[i,j] == matrix[j,i]
{
    new real[matrix.Length1, matrix.Length0]((i,j) reads matrix => if 0 <= i < matrix.Length1 && 0 <= j < matrix.Length0 then matrix[j,i] else 0.0)
}

I'm not quite sure why it says it is an invalid logical expression since in the method I am able to assign it to a variable, which makes me assume that it is an expression.

I can see here in the docs that

Array allocation is permitted in ghost contexts. If any expression used to specify a dimension or initialization value is ghost, then the new allocation can only be used in ghost contexts. Because the elements of an array are non-ghost, an array allocated in a ghost context in effect cannot be changed after initialization.

So it seems like I should be able to define a new array in a function. What is the correct syntax here?

Hath995
  • 821
  • 6
  • 12

2 Answers2

1

Functions (even ghost functions) are not allowed to allocate memory or call methods, so calls to new cannot appear in function bodies.

This is because functions must be deterministic (return the same thing when called with the same arguments). As written, your function would return a different (fresh) object every time (reference types like arrays have reference equality, which means that they are the same if they live at the same address, not just if they have the same contents).

The passage you quoted is relevant for ghost methods, but does not apply to functions.

Clément
  • 12,299
  • 15
  • 75
  • 115
  • Thank you for the explanation, but what are the difference between functions and ghost methods? For some reason I thought they were the same thing. I realize I might have been confusing ghost methods and lemmas being the same thing. Are they actually the same or am I mistaken? – Hath995 Aug 01 '22 at 13:05
  • lemmas and ghost methods are very close, and ghost methods are almost never used. functions are very different from ghost methods (functions contain expressions only, no statements, no loops, etc. and can be used in other expressions; ghost methods can have statements and can't be used in expressions) – Clément Aug 02 '22 at 01:16
0

So the answer is 1. Don't use array which is heap based as Clément said. 2. Use datatypes. The following verifies...

datatype Matrix = Matrice(vals: seq<seq<real>>, rows: nat, columns: nat)

predicate isMatrix(mat: Matrix) {
    mat.rows >= 1 && mat.columns >= 1 && |mat.vals| == mat.rows && forall i :: 0 <= i < mat.rows ==> |mat.vals[i]| == mat.columns
}

function method seqTranspose(mat: Matrix): Matrix
    requires isMatrix(mat)
    ensures isMatrix(seqTranspose(mat))
    ensures seqTranspose(mat).columns == mat.rows
    ensures seqTranspose(mat).rows == mat.columns
//     ensures seqTranpose(matrix).Length0 == matrix.Length1 && ftranspose(matrix).Length1 == matrix.Length0
    ensures forall i, j :: 0 <= i < mat.columns && 0 <= j < mat.rows ==> seqTranspose(mat).vals[i][j] == mat.vals[j][i]
{
    Matrice(seq(mat.columns, i requires 0 <= i < mat.columns => seq(mat.rows, j requires 0 <= j < mat.rows => mat.vals[j][i])), mat.columns, mat.rows)
}

lemma matTranspose(mat: Matrix) 
    requires isMatrix(mat)
    ensures seqTranspose(seqTranspose(mat)) == mat
{
    assert forall i :: 0 <= i < |mat.vals| ==> mat.vals[i] == seqTranspose(seqTranspose(mat)).vals[i];
}

Hath995
  • 821
  • 6
  • 12