Commit 8a4341d1 authored by Martin Clochard's avatar Martin Clochard

new example: matrix multiplication

parent 32aac69d
This is a complete solution to Verifythis 2016 challenge 1.
A self-contained solution to task 1 can be found in file naive.mlw
Other files solve task 2 and 3:
- sum_extended.mlw prove a few extra lemmas on top of what
the standard library already gives
- matrices.mlw contains the theories of matrices, matrix arithmetic,
and block product. Except for providing the requested program,
it solves task 2.
- matrices_ring_simp.mlw is a support file for proof by reflection
of matrix algebraic equations
- strassen.mlw is the implementation of Strassen's Algorithm for task 3.
It also contains the program associated to task 2 (assoc_proof).
To replay proofs:
- Install Why3 development version from the git source repository
(at the time this file was written, it would not replay with the
release version due to known incompleteness bug in compute)
- Install SMT solvers Alt-Ergo 1.01, CVC4 1.4 and Z3 4.4.1
- Run why3 replay -L . FILE to replay the session associated to FILE.
Challenge text:
Challenge 1: Matrix Multiplication
Consider the following pseudocode algorithm, which is naive implementation of matrix multiplication. For simplicity we assume that the matrices are square.
int[][] matrixMultiply(int[][] A, int[][] B) {
int n = A.length;
// initialise C
int[][] C = new int[n][n];
for (int i = 0; i < n; i++) {
for (int k = 0; k < n; k++) {
for (int j = 0; j < n; j++) {
C[i][j] += A[i][k] * B[k][j];
}
}
}
return C;
}
Tasks.
1. Provide a specification to describe the behaviour of this algorithm, and prove
that it correctly implements its specification.
2. Show that matrix multiplication is associative, i.e., the order in which
matrices are multiplied can be disregarded: A(BC) = (AB)C. To show this,
you should write a program that performs the two different computations,
and then prove that the result of the two computations is always the same.
3. [Optional, if time permits] In the literature, there exist many proposals
for more efficient matrix multiplication algorithms. Strassen’s algorithm
was one of the first. The key idea of the algorithm is to use a recursive
algorithm that reduces the number of multiplications on submatrices
(from 8 to 7), see https://en.wikipedia.org/wiki/Strassen_algorithm for an
explanation. A relatively clean Java implementation (and Python and C++)
can be found here: https://martin-thoma.com/strassen-algorithm-in-python-java-cpp/.
Prove that the naive algorithm above has the same behaviour as Strassen’s
algorithm. Proving it for a restricted case, like a 2x2 matrix should be
straightforward, the challenge is to prove it for arbitrary matrices with size 2^n.
This diff is collapsed.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE why3session PUBLIC "-//Why3//proof session v5//EN"
"http://why3.lri.fr/why3session.dtd">
<why3session shape_version="4">
<prover id="0" name="CVC4" version="1.4" timelimit="5" steplimit="0" memlimit="1000"/>
<prover id="1" name="Alt-Ergo" version="1.01" timelimit="5" steplimit="0" memlimit="1000"/>
<file name="../matrices.mlw" expanded="true">
<theory name="MatrixGen" sum="d41d8cd98f00b204e9800998ecf8427e" expanded="true">
</theory>
<theory name="FloatMatrix" sum="d41d8cd98f00b204e9800998ecf8427e" expanded="true">
</theory>
<theory name="FixedMatrix" sum="81d1f6ecddc981db1f906002ad2c6515">
<goal name="rows_and_cols_nonnegative">
<proof prover="1"><result status="valid" time="0.00" steps="2"/></proof>
</goal>
</theory>
<theory name="SquareFixedMatrix" sum="8866fe956f3cb17788b7229e8763a520">
<goal name="r_and_c_nonnegative">
<proof prover="1"><result status="valid" time="0.00" steps="1"/></proof>
</goal>
</theory>
<theory name="MatrixArithmetic" sum="95c5ea5a8476dec9b6807e6463fa7c50">
<goal name="zero_neutral">
<transf name="split_goal_wp">
<goal name="zero_neutral.1" expl="1.">
<proof prover="1"><result status="valid" time="0.02" steps="30"/></proof>
</goal>
<goal name="zero_neutral.2" expl="2.">
<proof prover="1"><result status="valid" time="0.02" steps="9"/></proof>
</goal>
</transf>
</goal>
<goal name="add_commutative">
<transf name="split_goal_wp">
<goal name="add_commutative.1" expl="1.">
<proof prover="1"><result status="valid" time="0.03" steps="46"/></proof>
</goal>
<goal name="add_commutative.2" expl="2.">
<proof prover="1"><result status="valid" time="0.01" steps="17"/></proof>
</goal>
</transf>
</goal>
<goal name="add_associative">
<transf name="split_goal_wp">
<goal name="add_associative.1" expl="1.">
<proof prover="1"><result status="valid" time="0.14" steps="301"/></proof>
</goal>
<goal name="add_associative.2" expl="2.">
<proof prover="1"><result status="valid" time="0.01" steps="24"/></proof>
</goal>
</transf>
</goal>
<goal name="add_opposite">
<transf name="split_goal_wp">
<goal name="add_opposite.1" expl="1.">
<proof prover="1"><result status="valid" time="0.12" steps="153"/></proof>
</goal>
<goal name="add_opposite.2" expl="2.">
<proof prover="1"><result status="valid" time="0.01" steps="14"/></proof>
</goal>
</transf>
</goal>
<goal name="opp_involutive">
<proof prover="0"><result status="valid" time="0.07"/></proof>
</goal>
<goal name="opposite_add">
<proof prover="0"><result status="valid" time="0.25"/></proof>
</goal>
<goal name="WP_parameter mul_assoc_get" expl="VC for mul_assoc_get">
<transf name="split_goal_wp">
<goal name="WP_parameter mul_assoc_get.1" expl="1. precondition">
<proof prover="1"><result status="valid" time="0.02" steps="21"/></proof>
</goal>
<goal name="WP_parameter mul_assoc_get.2" expl="2. precondition">
<proof prover="1"><result status="valid" time="2.40" steps="94"/></proof>
</goal>
<goal name="WP_parameter mul_assoc_get.3" expl="3. assertion">
<proof prover="1"><result status="valid" time="0.06" steps="37"/></proof>
</goal>
<goal name="WP_parameter mul_assoc_get.4" expl="4. precondition">
<proof prover="1"><result status="valid" time="0.05" steps="35"/></proof>
</goal>
<goal name="WP_parameter mul_assoc_get.5" expl="5. assertion">
<proof prover="1"><result status="valid" time="0.04" steps="34"/></proof>
</goal>
<goal name="WP_parameter mul_assoc_get.6" expl="6. postcondition">
<proof prover="1"><result status="valid" time="0.02" steps="11"/></proof>
</goal>
</transf>
</goal>
<goal name="mul_assoc">
<transf name="split_goal_wp">
<goal name="mul_assoc.1" expl="1.">
<proof prover="1"><result status="valid" time="0.02" steps="60"/></proof>
</goal>
<goal name="mul_assoc.2" expl="2.">
<proof prover="1"><result status="valid" time="0.01" steps="18"/></proof>
</goal>
</transf>
</goal>
<goal name="WP_parameter mul_distr_right_get" expl="VC for mul_distr_right_get">
<transf name="split_goal_wp">
<goal name="WP_parameter mul_distr_right_get.1" expl="1. assertion">
<proof prover="1"><result status="valid" time="0.06" steps="106"/></proof>
</goal>
<goal name="WP_parameter mul_distr_right_get.2" expl="2. precondition">
<proof prover="1"><result status="valid" time="0.10" steps="53"/></proof>
</goal>
<goal name="WP_parameter mul_distr_right_get.3" expl="3. postcondition">
<proof prover="1"><result status="valid" time="0.06" steps="61"/></proof>
</goal>
</transf>
</goal>
<goal name="mul_distr_right">
<transf name="split_goal_wp">
<goal name="mul_distr_right.1" expl="1.">
<proof prover="1"><result status="valid" time="0.05" steps="146"/></proof>
</goal>
<goal name="mul_distr_right.2" expl="2.">
<proof prover="1"><result status="valid" time="0.01" steps="22"/></proof>
</goal>
</transf>
</goal>
<goal name="WP_parameter mul_distr_left_get" expl="VC for mul_distr_left_get">
<transf name="split_goal_wp">
<goal name="WP_parameter mul_distr_left_get.1" expl="1. assertion">
<proof prover="1"><result status="valid" time="0.06" steps="110"/></proof>
</goal>
<goal name="WP_parameter mul_distr_left_get.2" expl="2. precondition">
<proof prover="1"><result status="valid" time="0.05" steps="40"/></proof>
</goal>
<goal name="WP_parameter mul_distr_left_get.3" expl="3. postcondition">
<proof prover="1"><result status="valid" time="0.05" steps="45"/></proof>
</goal>
</transf>
</goal>
<goal name="mul_distr_left">
<transf name="split_goal_wp">
<goal name="mul_distr_left.1" expl="1.">
<proof prover="1"><result status="valid" time="0.04" steps="144"/></proof>
</goal>
<goal name="mul_distr_left.2" expl="2.">
<proof prover="1"><result status="valid" time="0.01" steps="22"/></proof>
</goal>
</transf>
</goal>
<goal name="mul_zero_right">
<proof prover="1"><result status="valid" time="1.91" steps="404"/></proof>
</goal>
<goal name="mul_zero_left">
<proof prover="1"><result status="valid" time="1.65" steps="396"/></proof>
</goal>
<goal name="mul_opp">
<transf name="split_goal_wp">
<goal name="mul_opp.1" expl="1.">
<proof prover="1"><result status="valid" time="0.36" steps="394"/></proof>
</goal>
<goal name="mul_opp.2" expl="2.">
<proof prover="0"><result status="valid" time="0.08"/></proof>
</goal>
<goal name="mul_opp.3" expl="3.">
<proof prover="0"><result status="valid" time="0.04"/></proof>
</goal>
<goal name="mul_opp.4" expl="4.">
<proof prover="1"><result status="valid" time="3.92" steps="2401"/></proof>
</goal>
</transf>
</goal>
</theory>
<theory name="BlockMul" sum="08ae5cbffa4e68e45d31bc65f734c776">
<goal name="WP_parameter block_mul_ij" expl="VC for block_mul_ij">
<proof prover="1"><result status="valid" time="2.31" steps="880"/></proof>
</goal>
<goal name="WP_parameter mul_split" expl="VC for mul_split">
<proof prover="1"><result status="valid" time="0.29" steps="218"/></proof>
</goal>
<goal name="WP_parameter mul_block_cell" expl="VC for mul_block_cell">
<transf name="split_goal_wp">
<goal name="WP_parameter mul_block_cell.1" expl="1. precondition">
<proof prover="1"><result status="valid" time="0.03" steps="27"/></proof>
</goal>
<goal name="WP_parameter mul_block_cell.2" expl="2. postcondition">
<proof prover="1"><result status="valid" time="0.12" steps="120"/></proof>
</goal>
</transf>
</goal>
<goal name="mul_block">
<transf name="split_goal_wp">
<goal name="mul_block.1" expl="1.">
<proof prover="1"><result status="valid" time="0.04" steps="65"/></proof>
</goal>
<goal name="mul_block.2" expl="2.">
<proof prover="1"><result status="valid" time="0.02" steps="24"/></proof>
</goal>
</transf>
</goal>
</theory>
</file>
</why3session>
module MatrixMultiplication
use import int.Int
use import int.Sum
use import map.Map
use import matrix.Matrix
function mul_atom (a b: matrix int) (i j: int) : int -> int =
\k. a.elts[i][k] * b.elts[k][j]
predicate matrix_product (m a b: matrix int) =
forall i j. 0 <= i < m.rows -> 0 <= j < m.columns ->
m.elts[i][j] = sum 0 a.columns (mul_atom a b i j)
let mult_naive (a b: matrix int) : matrix int
requires { a.columns = b.rows }
ensures { result.rows = a.rows /\ result.columns = b.columns }
ensures { matrix_product result a b }
= let rs = make (rows a) (columns b) 0 in
for i = 0 to rows a - 1 do
invariant { forall i0 j0. i <= i0 < rows a /\ 0 <= j0 < columns b ->
rs.elts[i0][j0] = 0 }
invariant { forall i0 j0. 0 <= i0 < i /\ 0 <= j0 < columns b ->
rs.elts[i0][j0] = sum 0 a.columns (mul_atom a b i0 j0) }
'M:
for k = 0 to rows b - 1 do
invariant { forall i0 j0. 0 <= i0 < rows a /\ 0 <= j0 < columns b ->
i0 <> i -> rs.elts[i0][j0] = (at rs 'M).elts[i0][j0] }
invariant { forall j0. 0 <= j0 < columns b ->
rs.elts[i][j0] = sum 0 k (mul_atom a b i j0) }
'I:
for j = 0 to columns b - 1 do
invariant { forall i0 j0. 0 <= i0 < rows a /\ 0 <= j0 < columns b ->
(i0 <> i \/ j0 >= j) -> rs.elts[i0][j0] = (at rs 'I).elts[i0][j0] }
invariant { forall j0. 0 <= j0 < j ->
rs.elts[i][j0] = sum 0 (k+1) (mul_atom a b i j0) }
set rs i j (get rs i j + get a i k * get b k j)
done;
done;
done;
rs
end
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE why3session PUBLIC "-//Why3//proof session v5//EN"
"http://why3.lri.fr/why3session.dtd">
<why3session shape_version="4">
<prover id="0" name="Alt-Ergo" version="1.01" timelimit="5" steplimit="0" memlimit="1000"/>
<file name="../naive.mlw">
<theory name="MatrixMultiplication" sum="888d777f6b0bead427ee1083245abb55">
<goal name="WP_parameter mult_naive" expl="VC for mult_naive">
<transf name="split_goal_wp">
<goal name="WP_parameter mult_naive.1" expl="1. precondition">
<proof prover="0"><result status="valid" time="0.01" steps="5"/></proof>
</goal>
<goal name="WP_parameter mult_naive.2" expl="2. postcondition">
<proof prover="0"><result status="valid" time="0.01" steps="10"/></proof>
</goal>
<goal name="WP_parameter mult_naive.3" expl="3. postcondition">
<proof prover="0"><result status="valid" time="0.01" steps="11"/></proof>
</goal>
<goal name="WP_parameter mult_naive.4" expl="4. loop invariant init">
<proof prover="0"><result status="valid" time="0.01" steps="15"/></proof>
</goal>
<goal name="WP_parameter mult_naive.5" expl="5. loop invariant init">
<proof prover="0"><result status="valid" time="0.01" steps="10"/></proof>
</goal>
<goal name="WP_parameter mult_naive.6" expl="6. loop invariant preservation">
<proof prover="0"><result status="valid" time="0.01" steps="18"/></proof>
</goal>
<goal name="WP_parameter mult_naive.7" expl="7. loop invariant preservation">
<proof prover="0"><result status="valid" time="0.01" steps="21"/></proof>
</goal>
<goal name="WP_parameter mult_naive.8" expl="8. loop invariant init">
<proof prover="0"><result status="valid" time="0.01" steps="18"/></proof>
</goal>
<goal name="WP_parameter mult_naive.9" expl="9. loop invariant preservation">
<proof prover="0"><result status="valid" time="0.01" steps="16"/></proof>
</goal>
<goal name="WP_parameter mult_naive.10" expl="10. loop invariant preservation">
<proof prover="0"><result status="valid" time="0.01" steps="16"/></proof>
</goal>
<goal name="WP_parameter mult_naive.11" expl="11. loop invariant init">
<proof prover="0"><result status="valid" time="0.01" steps="16"/></proof>
</goal>
<goal name="WP_parameter mult_naive.12" expl="12. index in array bounds">
<proof prover="0"><result status="valid" time="0.01" steps="19"/></proof>
</goal>
<goal name="WP_parameter mult_naive.13" expl="13. index in array bounds">
<proof prover="0"><result status="valid" time="0.01" steps="22"/></proof>
</goal>
<goal name="WP_parameter mult_naive.14" expl="14. type invariant">
<proof prover="0"><result status="valid" time="0.01" steps="20"/></proof>
</goal>
<goal name="WP_parameter mult_naive.15" expl="15. index in array bounds">
<proof prover="0"><result status="valid" time="0.01" steps="25"/></proof>
</goal>
<goal name="WP_parameter mult_naive.16" expl="16. index in array bounds">
<proof prover="0"><result status="valid" time="0.01" steps="21"/></proof>
</goal>
<goal name="WP_parameter mult_naive.17" expl="17. loop invariant preservation">
<proof prover="0"><result status="valid" time="0.03" steps="38"/></proof>
</goal>
<goal name="WP_parameter mult_naive.18" expl="18. loop invariant preservation">
<proof prover="0"><result status="valid" time="0.19" steps="73"/></proof>
</goal>
<goal name="WP_parameter mult_naive.19" expl="19. loop invariant preservation">
<proof prover="0"><result status="valid" time="0.02" steps="23"/></proof>
</goal>
<goal name="WP_parameter mult_naive.20" expl="20. loop invariant preservation">
<proof prover="0"><result status="valid" time="0.01" steps="23"/></proof>
</goal>
<goal name="WP_parameter mult_naive.21" expl="21. loop invariant preservation">
<proof prover="0"><result status="valid" time="0.01" steps="20"/></proof>
</goal>
<goal name="WP_parameter mult_naive.22" expl="22. loop invariant preservation">
<proof prover="0"><result status="valid" time="0.03" steps="46"/></proof>
</goal>
<goal name="WP_parameter mult_naive.23" expl="23. type invariant">
<proof prover="0"><result status="valid" time="0.01" steps="10"/></proof>
</goal>
<goal name="WP_parameter mult_naive.24" expl="24. postcondition">
<proof prover="0"><result status="valid" time="0.00" steps="10"/></proof>
</goal>
<goal name="WP_parameter mult_naive.25" expl="25. postcondition">
<proof prover="0"><result status="valid" time="0.01" steps="20"/></proof>
</goal>
</transf>
</goal>
</theory>
</file>
</why3session>
This diff is collapsed.
module Sum_extended
use import int.Int
use import int.Sum
function addf (f g:int -> int) : int -> int = \x. f x + g x
function smulf (l:int) (f:int -> int) : int -> int = \x. l * f x
let rec lemma sum_mult (a b l:int) (f:int -> int) : unit
ensures { sum a b ( smulf l f) = l * sum a b f }
variant { b - a }
= if b > a then sum_mult a (b-1) l f
let rec lemma sum_add (a b:int) (f g:int -> int) : unit
ensures { sum a b (addf f g) = sum a b f + sum a b g }
variant { b - a }
= if b > a then sum_add a (b-1) f g
function sumf (a b:int) (f:int -> int -> int) : int -> int = \x. sum a b (f x)
let rec lemma fubini (f1 f2: int -> int -> int) (a b c d: int) : unit
requires { forall x y. a <= x < b /\ c <= y < d -> f1 x y = f2 y x }
ensures { sum a b (sumf c d f1) = sum c d (sumf a b f2) }
variant { b - a }
= if b <= a
then assert { forall x. sumf a b f2 x = 0 }
else begin
fubini f1 f2 a (b-1) c d;
assert { let ha = addf (sumf a (b-1) f2) (f1 (b-1)) in
sum c d (sumf a b f2) = sum c d ha
by forall y. c <= y < d -> sumf a b f2 y = ha y }
end
let ghost sum_ext (a b: int) (f g: int -> int) : unit
requires {forall i. a <= i < b -> f i = g i }
ensures { sum a b f = sum a b g }
= ()
end
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE why3session PUBLIC "-//Why3//proof session v5//EN"
"http://why3.lri.fr/why3session.dtd">
<why3session shape_version="4">
<prover id="0" name="Alt-Ergo" version="1.01" timelimit="5" steplimit="0" memlimit="1000"/>
<file name="../sum_extended.mlw">
<theory name="Sum_extended" sum="cfab2f022b7b9478b4cc84f0f10fa2ed">
<goal name="WP_parameter sum_mult" expl="VC for sum_mult">
<proof prover="0"><result status="valid" time="0.05" steps="24"/></proof>
</goal>
<goal name="WP_parameter sum_add" expl="VC for sum_add">
<proof prover="0"><result status="valid" time="0.04" steps="37"/></proof>
</goal>
<goal name="WP_parameter fubini" expl="VC for fubini">
<transf name="split_goal_wp">
<goal name="WP_parameter fubini.1" expl="1. assertion">
<proof prover="0"><result status="valid" time="0.01" steps="4"/></proof>
</goal>
<goal name="WP_parameter fubini.2" expl="2. postcondition">
<proof prover="0"><result status="valid" time="0.01" steps="11"/></proof>
</goal>
<goal name="WP_parameter fubini.3" expl="3. variant decrease">
<proof prover="0"><result status="valid" time="0.00" steps="1"/></proof>
</goal>
<goal name="WP_parameter fubini.4" expl="4. precondition">
<proof prover="0"><result status="valid" time="0.01" steps="6"/></proof>
</goal>
<goal name="WP_parameter fubini.5" expl="5. assertion">
<transf name="split_goal_wp">
<goal name="WP_parameter fubini.5.1" expl="1. assertion">
<proof prover="0"><result status="valid" time="0.01" steps="14"/></proof>
</goal>
<goal name="WP_parameter fubini.5.2" expl="2. assertion">
<proof prover="0"><result status="valid" time="0.02" steps="27"/></proof>
</goal>
</transf>
</goal>
<goal name="WP_parameter fubini.6" expl="6. postcondition">
<proof prover="0"><result status="valid" time="0.01" steps="8"/></proof>
</goal>
</transf>
</goal>
<goal name="WP_parameter sum_ext" expl="VC for sum_ext">
<proof prover="0"><result status="valid" time="0.01" steps="17"/></proof>
</goal>
</theory>
</file>
</why3session>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment