changeset 257:f00ab8baa6d7

Make sum, difference, scaled and abs use sparse operations and return sparse matrices if all inputs are sparse
author Chris Cannam
date Tue, 21 May 2013 22:37:28 +0100
parents 97e97a2e6a4e
children f3b7b5d20f88
files yetilab/matrix/matrix.yeti yetilab/matrix/test/speedtest.yeti yetilab/matrix/test/test_matrix.yeti
diffstat 3 files changed, 162 insertions(+), 15 deletions(-) [+]
line wrap: on
line diff
--- a/yetilab/matrix/matrix.yeti	Tue May 21 22:36:39 2013 +0100
+++ b/yetilab/matrix/matrix.yeti	Tue May 21 22:37:28 2013 +0100
@@ -121,6 +121,12 @@
     _: vec.fromList (map do j: getAt i j m done [0..width m - 1]);
     esac;
 
+asRows m =
+    map do i: getRow i m done [0 .. (height m) - 1];
+
+asColumns m =
+    map do i: getColumn i m done [0 .. (width m) - 1];
+
 isRowMajor? m =
     case m of
     DenseRows _: true;
@@ -169,6 +175,7 @@
 swapij =
     map do { i, j, v }: { i = j, j = i, v } done;
 
+//!!! should use { row = , column = , value = } instead of i, j, v?
 enumerateSparse m =
    (enumerate { values, indices, pointers } =
         concat
@@ -349,30 +356,83 @@
 newColumnVector data = //!!! NB does not copy data
     DenseCols (array [data]);
 
-scaled factor m = //!!! v inefficient
-    generate do row col: factor * (getAt row col m) done (size m);
-
 thresholded threshold m = //!!! v inefficient; and should take a threshold function?
     generate do row col:
         v = getAt row col m; if (abs v) > threshold then v else 0 fi
     done (size m);
 
+denseLinearOp op m1 m2 =
+    if isRowMajor? m1 then
+        newMatrix (RowMajor ()) 
+           (map2 do c1 c2: op c1 c2 done (asRows m1) (asRows m2));
+    else
+        newMatrix (ColumnMajor ()) 
+           (map2 do c1 c2: op c1 c2 done (asColumns m1) (asColumns m2));
+    fi;
+
+sparseSumOrDifference op m1 m2 =
+   (h = [:];
+    for (enumerate m1) do { i, j, v }:
+        if not (i in h) then h[i] := [:] fi;
+        h[i][j] := v;
+    done;
+    for (enumerate m2) do { i, j, v }:
+        if not (i in h) then h[i] := [:] fi;
+        if j in h[i] then h[i][j] := op h[i][j] v;
+        else h[i][j] := op 0 v;
+        fi;
+    done;
+    entries = concat
+       (map do i:
+            kk = keys h[i];
+            map2 do j v: { i, j, v } done kk (map (at h[i]) kk)
+            done (keys h));
+    makeSparse
+        if isRowMajor? m1 then (RowMajor ()) else (ColumnMajor ()) fi
+        (size m1) 
+        entries);
+
 sum' m1 m2 =
     if (size m1) != (size m2)
     then failWith "Matrices are not the same size: \(size m1), \(size m2)";
+    elif isSparse? m1 and isSparse? m2 then
+        sparseSumOrDifference (+) m1 m2;
     else
-        generate do row col: getAt row col m1 + getAt row col m2 done (size m1);
+        denseLinearOp bf.add m1 m2;
     fi;
 
-difference m1 m2 = //!!! doc: m1 - m2, not m2 - m1
+difference m1 m2 =
     if (size m1) != (size m2)
     then failWith "Matrices are not the same size: \(size m1), \(size m2)";
+    elif isSparse? m1 and isSparse? m2 then
+        sparseSumOrDifference (-) m1 m2;
     else
-        generate do row col: getAt row col m1 - getAt row col m2 done (size m1);
+        denseLinearOp bf.subtract m1 m2;
+    fi;
+
+scaled factor m =
+    if isSparse? m then
+        makeSparse
+            if isRowMajor? m then (RowMajor ()) else (ColumnMajor ()) fi
+            (size m)
+            (map do { i, j, v }: { i, j, v = factor * v } done (enumerate m))
+    elif isRowMajor? m then
+        newMatrix (RowMajor ()) (map (bf.scaled factor) (asRows m));
+    else
+        newMatrix (ColumnMajor ()) (map (bf.scaled factor) (asColumns m));
     fi;
 
 abs' m =
-    generate do row col: abs (getAt row col m) done (size m);
+    if isSparse? m then
+        makeSparse
+            if isRowMajor? m then (RowMajor ()) else (ColumnMajor ()) fi
+            (size m)
+            (map do { i, j, v }: { i, j, v = abs v } done (enumerate m))
+    elif isRowMajor? m then
+        newMatrix (RowMajor ()) (map bf.abs (asRows m));
+    else
+        newMatrix (ColumnMajor ()) (map bf.abs (asColumns m));
+    fi;
 
 sparseProductLeft size m1 m2 =
    (e = enumerateSparse m1;
@@ -451,12 +511,6 @@
         fi;
     fi;
 
-asRows m =
-    map do i: getRow i m done [0 .. (height m) - 1];
-
-asColumns m =
-    map do i: getColumn i m done [0 .. (width m) - 1];
-
 concatAgainstGrain tagger getter counter mm =
    (n = counter (size (head mm));
     tagger (array
--- a/yetilab/matrix/test/speedtest.yeti	Tue May 21 22:36:39 2013 +0100
+++ b/yetilab/matrix/test/speedtest.yeti	Tue May 21 22:37:28 2013 +0100
@@ -83,7 +83,7 @@
    (print "                                 ";
     println "isSparse: \(mat.isSparse? m), density \(mat.density m)");
 
-println "\nM * M multiplies:\n";
+println "\nM * M multiplies (and a few sums):\n";
 
 sz = 500;
 
@@ -131,6 +131,34 @@
 
 println "";
 
+print "CMD + CMD... ";
+reportOn (time \(mat.sum cmd cmd));
+
+print "CMD + RMD... ";
+reportOn (time \(mat.sum cmd rmd));
+
+print "RMD + CMD... ";
+reportOn (time \(mat.sum rmd cmd));
+
+print "RMD + RMD... ";
+reportOn (time \(mat.sum rmd rmd));
+
+println "";
+
+print "CMS + CMS... ";
+reportOn (time \(mat.sum cms cms));
+
+print "CMS + RMS... ";
+reportOn (time \(mat.sum cms rms));
+
+print "RMS + CMS... ";
+reportOn (time \(mat.sum rms cms));
+
+print "RMS + RMS... ";
+reportOn (time \(mat.sum rms rms));
+
+println "";
+
 print "CMD * CMD... ";
 reportOn (time \(mat.product cmd cmd));
 
@@ -143,7 +171,7 @@
 print "RMD * RMD... ";
 reportOn (time \(mat.product rmd rmd));
 
-println "\nLarge sparse M * M multiplies:\n";
+println "\nLarge sparse M * M multiplies and adds:\n";
 
 sz = 5000000;
 nnz = 10000;
@@ -178,5 +206,18 @@
 print "RMS * RMS... ";
 reportOn (time \(mat.product rms rms));
 
+println "";
+
+print "CMS + CMS... ";
+reportOn (time \(mat.sum cms cms));
+
+print "CMS + RMS... ";
+reportOn (time \(mat.sum cms rms));
+
+print "RMS + CMS... ";
+reportOn (time \(mat.sum rms cms));
+
+print "RMS + RMS... ";
+reportOn (time \(mat.sum rms rms));
 
 ();
--- a/yetilab/matrix/test/test_matrix.yeti	Tue May 21 22:36:39 2013 +0100
+++ b/yetilab/matrix/test/test_matrix.yeti	Tue May 21 22:37:28 2013 +0100
@@ -215,6 +215,32 @@
     yrt
 ),
 
+"sparseSum-\(name)": \(
+    s = mat.newSparseMatrix (ColumnMajor ()) { rows = 2, columns = 3 } [
+        { i = 0, j = 0, v = 1 },
+        { i = 0, j = 2, v = 2 },
+        { i = 1, j = 1, v = 4 },
+    ];
+    t = mat.newSparseMatrix (ColumnMajor ()) { rows = 2, columns = 3 } [
+        { i = 0, j = 1, v = 7 },
+        { i = 1, j = 0, v = 5 },
+        { i = 1, j = 1, v = 6 },
+    ];
+    tot = mat.sum s t;
+    mat.isSparse? tot and
+        compareMatrices tot (mat.sum (mat.toDense s) t) and
+        compareMatrices tot (mat.sum (mat.toDense s) (mat.toDense t)) and
+        compareMatrices tot (mat.sum s (mat.toDense t)) and
+        compareMatrices tot 
+           (mat.newSparseMatrix (RowMajor ()) { rows = 2, columns = 3 } [
+               { i = 0, j = 0, v = 1 },
+               { i = 0, j = 1, v = 7 },
+               { i = 0, j = 2, v = 2 },
+               { i = 1, j = 0, v = 5 },
+               { i = 1, j = 1, v = 10 },
+            ])
+),
+
 "difference-\(name)": \(
     compareMatrices
        (mat.difference (constMatrix 2 { rows = 3, columns = 4 })
@@ -232,6 +258,32 @@
     yrt
 ),
 
+"sparseDifference-\(name)": \(
+    s = mat.newSparseMatrix (ColumnMajor ()) { rows = 2, columns = 3 } [
+        { i = 0, j = 0, v = 1 },
+        { i = 0, j = 2, v = 2 },
+        { i = 1, j = 1, v = 4 },
+    ];
+    t = mat.newSparseMatrix (ColumnMajor ()) { rows = 2, columns = 3 } [
+        { i = 0, j = 1, v = 7 },
+        { i = 1, j = 0, v = 5 },
+        { i = 1, j = 1, v = 6 },
+    ];
+    diff = mat.difference s t;
+    mat.isSparse? diff and
+        compareMatrices diff (mat.difference (mat.toDense s) t) and
+        compareMatrices diff (mat.difference (mat.toDense s) (mat.toDense t)) and
+        compareMatrices diff (mat.difference s (mat.toDense t)) and
+        compareMatrices diff 
+           (mat.newSparseMatrix (RowMajor ()) { rows = 2, columns = 3 } [
+               { i = 0, j = 0, v = 1 },
+               { i = 0, j = 1, v = -7 },
+               { i = 0, j = 2, v = 2 },
+               { i = 1, j = 0, v = -5 },
+               { i = 1, j = 1, v = -2 },
+            ])
+),
+
 "abs-\(name)": \(
     compareMatrices
        (mat.abs (newMatrix (ColumnMajor ()) [[-1,4],[2,-5],[-3,0]]))