214 lines
5.8 KiB
OCaml
214 lines
5.8 KiB
OCaml
(* Matrix multiplication using Strassen's algorithm in OCaml *)
|
|
|
|
type matrix = float array array
|
|
|
|
type shape = {
|
|
rows : int;
|
|
cols : int;
|
|
}
|
|
|
|
(* Get the shape of a matrix *)
|
|
let shape m =
|
|
let rows = Array.length m in
|
|
let cols = if rows = 0 then 0 else Array.length m.(0) in
|
|
{ rows; cols }
|
|
|
|
(* Create a matrix from a list of lists *)
|
|
let matrix_of_lists lists =
|
|
Array.of_list (List.map Array.of_list lists)
|
|
|
|
(* Convert matrix to list of lists for printing *)
|
|
let lists_of_matrix m =
|
|
Array.to_list (Array.map Array.to_list m)
|
|
|
|
(* Matrix addition *)
|
|
let add_matrix a b =
|
|
let { rows; cols } = shape a in
|
|
let { rows = b_rows; cols = b_cols } = shape b in
|
|
assert (rows = b_rows && cols = b_cols);
|
|
Array.init rows (fun i ->
|
|
Array.init cols (fun j -> a.(i).(j) +. b.(i).(j)))
|
|
|
|
(* Matrix subtraction *)
|
|
let sub_matrix a b =
|
|
let { rows; cols } = shape a in
|
|
let { rows = b_rows; cols = b_cols } = shape b in
|
|
assert (rows = b_rows && cols = b_cols);
|
|
Array.init rows (fun i ->
|
|
Array.init cols (fun j -> a.(i).(j) -. b.(i).(j)))
|
|
|
|
(* Naive matrix multiplication *)
|
|
let dot_product a b =
|
|
let a_shape = shape a in
|
|
let b_shape = shape b in
|
|
assert (a_shape.cols = b_shape.rows);
|
|
|
|
Array.init a_shape.rows (fun i ->
|
|
Array.init b_shape.cols (fun j ->
|
|
let sum = ref 0.0 in
|
|
for k = 0 to a_shape.cols - 1 do
|
|
sum := !sum +. (a.(i).(k) *. b.(k).(j))
|
|
done;
|
|
!sum))
|
|
|
|
(* Extract a submatrix *)
|
|
let submatrix m start_row end_row start_col end_col =
|
|
Array.init (end_row - start_row) (fun i ->
|
|
Array.init (end_col - start_col) (fun j ->
|
|
m.(start_row + i).(start_col + j)))
|
|
|
|
(* Combine four submatrices into a single matrix *)
|
|
let block_matrix c11 c12 c21 c22 =
|
|
let p = Array.length c11 in
|
|
let result = Array.make_matrix (2 * p) (2 * p) 0.0 in
|
|
|
|
(* Copy c11 to top-left *)
|
|
for i = 0 to p - 1 do
|
|
for j = 0 to p - 1 do
|
|
result.(i).(j) <- c11.(i).(j)
|
|
done
|
|
done;
|
|
|
|
(* Copy c12 to top-right *)
|
|
for i = 0 to p - 1 do
|
|
for j = 0 to p - 1 do
|
|
result.(i).(p + j) <- c12.(i).(j)
|
|
done
|
|
done;
|
|
|
|
(* Copy c21 to bottom-left *)
|
|
for i = 0 to p - 1 do
|
|
for j = 0 to p - 1 do
|
|
result.(p + i).(j) <- c21.(i).(j)
|
|
done
|
|
done;
|
|
|
|
(* Copy c22 to bottom-right *)
|
|
for i = 0 to p - 1 do
|
|
for j = 0 to p - 1 do
|
|
result.(p + i).(p + j) <- c22.(i).(j)
|
|
done
|
|
done;
|
|
|
|
result
|
|
|
|
(* Check if a number is a power of 2 *)
|
|
let is_power_of_2 n =
|
|
n > 0 && (n land (n - 1)) = 0
|
|
|
|
(* Strassen's matrix multiplication *)
|
|
let rec strassen a b =
|
|
let a_shape = shape a in
|
|
let b_shape = shape b in
|
|
let rows = a_shape.rows in
|
|
let cols = a_shape.cols in
|
|
|
|
assert (rows = cols); (* matrices must be square *)
|
|
assert (a_shape.rows = b_shape.rows && a_shape.cols = b_shape.cols); (* same shape *)
|
|
assert (is_power_of_2 rows); (* size must be power of 2 *)
|
|
|
|
if rows = 1 then
|
|
dot_product a b
|
|
else
|
|
let p = rows / 2 in
|
|
|
|
(* Partition matrix a *)
|
|
let a11 = submatrix a 0 p 0 p in
|
|
let a12 = submatrix a 0 p p rows in
|
|
let a21 = submatrix a p rows 0 p in
|
|
let a22 = submatrix a p rows p rows in
|
|
|
|
(* Partition matrix b *)
|
|
let b11 = submatrix b 0 p 0 p in
|
|
let b12 = submatrix b 0 p p rows in
|
|
let b21 = submatrix b p rows 0 p in
|
|
let b22 = submatrix b p rows p rows in
|
|
|
|
(* Compute the 7 products *)
|
|
let m1 = strassen (add_matrix a11 a22) (add_matrix b11 b22) in
|
|
let m2 = strassen (add_matrix a21 a22) b11 in
|
|
let m3 = strassen a11 (sub_matrix b12 b22) in
|
|
let m4 = strassen a22 (sub_matrix b21 b11) in
|
|
let m5 = strassen (add_matrix a11 a12) b22 in
|
|
let m6 = strassen (sub_matrix a21 a11) (add_matrix b11 b12) in
|
|
let m7 = strassen (sub_matrix a12 a22) (add_matrix b21 b22) in
|
|
|
|
(* Compute the result submatrices *)
|
|
let c11 = add_matrix (sub_matrix (add_matrix m1 m4) m5) m7 in
|
|
let c12 = add_matrix m3 m5 in
|
|
let c21 = add_matrix m2 m4 in
|
|
let c22 = add_matrix (sub_matrix (add_matrix m1 m3) m2) m6 in
|
|
|
|
block_matrix c11 c12 c21 c22
|
|
|
|
(* Round matrix elements to specified decimal places *)
|
|
let round_matrix ?(ndigits=0) m =
|
|
let factor = 10.0 ** (float_of_int ndigits) in
|
|
Array.map (Array.map (fun x ->
|
|
Float.round (x *. factor) /. factor)) m
|
|
|
|
(* Pretty print a matrix *)
|
|
let print_matrix m =
|
|
let lists = lists_of_matrix m in
|
|
print_string "[";
|
|
List.iteri (fun i row ->
|
|
if i > 0 then print_string "; ";
|
|
print_string "[";
|
|
List.iteri (fun j x ->
|
|
if j > 0 then print_string "; ";
|
|
Printf.printf "%.6g" x) row;
|
|
print_string "]") lists;
|
|
print_endline "]"
|
|
|
|
(* Example usage *)
|
|
let examples () =
|
|
let a = matrix_of_lists [
|
|
[1.0; 2.0];
|
|
[3.0; 4.0]
|
|
] in
|
|
|
|
let b = matrix_of_lists [
|
|
[5.0; 6.0];
|
|
[7.0; 8.0]
|
|
] in
|
|
|
|
let c = matrix_of_lists [
|
|
[1.0; 1.0; 1.0; 1.0];
|
|
[2.0; 4.0; 8.0; 16.0];
|
|
[3.0; 9.0; 27.0; 81.0];
|
|
[4.0; 16.0; 64.0; 256.0]
|
|
] in
|
|
|
|
let d = matrix_of_lists [
|
|
[4.0; -3.0; 4.0/.3.0; -1.0/.4.0];
|
|
[-13.0/.3.0; 19.0/.4.0; -7.0/.3.0; 11.0/.24.0];
|
|
[3.0/.2.0; -2.0; 7.0/.6.0; -1.0/.4.0];
|
|
[-1.0/.6.0; 1.0/.4.0; -1.0/.6.0; 1.0/.24.0]
|
|
] in
|
|
|
|
let e = matrix_of_lists [
|
|
[1.0; 2.0; 3.0; 4.0];
|
|
[5.0; 6.0; 7.0; 8.0];
|
|
[9.0; 10.0; 11.0; 12.0];
|
|
[13.0; 14.0; 15.0; 16.0]
|
|
] in
|
|
|
|
let f = matrix_of_lists [
|
|
[1.0; 0.0; 0.0; 0.0];
|
|
[0.0; 1.0; 0.0; 0.0];
|
|
[0.0; 0.0; 1.0; 0.0];
|
|
[0.0; 0.0; 0.0; 1.0]
|
|
] in
|
|
|
|
print_endline "Naive matrix multiplication:";
|
|
print_string " a * b = "; print_matrix (dot_product a b);
|
|
print_string " c * d = "; print_matrix (round_matrix (dot_product c d));
|
|
print_string " e * f = "; print_matrix (dot_product e f);
|
|
|
|
print_endline "\nStrassen's matrix multiplication:";
|
|
print_string " a * b = "; print_matrix (strassen a b);
|
|
print_string " c * d = "; print_matrix (round_matrix (strassen c d));
|
|
print_string " e * f = "; print_matrix (strassen e f)
|
|
|
|
(* Run examples when executed *)
|
|
let () = examples ()
|