changeset 518:e39f31bf5d7a sized_matrix

Overhaul sparse construction as well
author Chris Cannam
date Wed, 20 Nov 2013 17:03:20 +0000
parents 8b3c0ed6afd0
children dbeb33a09d0d
files src/may/matrix.yeti src/may/matrix/test/test_matrix.yeti
diffstat 2 files changed, 134 insertions(+), 111 deletions(-) [+]
line wrap: on
line diff
--- a/src/may/matrix.yeti	Wed Nov 20 16:39:36 2013 +0000
+++ b/src/may/matrix.yeti	Wed Nov 20 17:03:20 2013 +0000
@@ -171,14 +171,14 @@
     SparseCSC _: true;
     esac;
 
-typeOf m =
-    if isRowMajor? m then RowMajor ()
-    else ColumnMajor ()
+taggerForTypeOf m =
+    if isRowMajor? m then Rows
+    else Columns
     fi;
 
-flippedTypeOf m =
-    if isRowMajor? m then ColumnMajor ()
-    else RowMajor ()
+taggerForFlippedTypeOf m =
+    if isRowMajor? m then Columns
+    else Rows
     fi;
 
 flippedSize { rows, columns } = { rows = columns, columns = rows };
@@ -205,6 +205,34 @@
 
 zeroSizeMatrix () = zeroMatrix { rows = 0, columns = 0 };
 
+newMatrix size d =
+    case d of
+    Rows rr: 
+        if (length rr) != size.rows then
+            failWith "Wrong number of rows in row-major newMatrix (\(length rr), size calls for \(size.rows)";
+        elif not empty? rr and vec.length (head rr) != size.columns then
+            failWith "Wrong number of columns in row-major newMatrix (\(vec.length (head rr)), size calls for \(size.columns)";
+        else
+            {
+                size,
+                data = DenseRows (array rr)
+            }
+        fi;
+    Columns cc: 
+        if (length cc) != size.columns then
+            failWith "Wrong number of columns in column-major newMatrix (\(length cc), size calls for \(size.columns)";
+        elif not empty? cc and vec.length (head cc) != size.rows then
+            failWith "Wrong number of rows in column-major newMatrix (\(vec.length (head cc)), size calls for \(size.rows)";
+        else
+            {
+                size,
+                data = DenseCols (array cc)
+            }
+        fi;
+    esac;
+
+newMatrixMatching m d = newMatrix (size m) ((taggerForTypeOf m) d);
+
 generate f { rows, columns } =
    (m = array (map \(new double[rows]) [1..columns]);
     for [0..columns-1] do col:
@@ -257,8 +285,9 @@
 
 // Make a sparse matrix from entries whose i, j values are known to be
 // within range
-makeSparse size type data =
-   (isRow = case type of RowMajor (): true; ColumnMajor (): false esac;
+newSparse size d =
+   (isRow = case d of Rows _: true; Columns _: false esac;
+    data = case d of Rows rr: rr; Columns cc: cc esac;
     ordered = 
         sortBy do a b:
             if a.maj == b.maj then a.min < b.min else a.maj < b.maj fi
@@ -296,24 +325,27 @@
         }
     });
 
+newSparseMatching m d = newSparse (size m) ((taggerForTypeOf m) d);
+
 // Make a sparse matrix from entries that may contain out-of-range
 // cells which need to be filtered out. This is the public API for
-// makeSparse and is also used to discard out-of-range cells from
+// newSparse and is also used to discard out-of-range cells from
 // resizedTo.
 //!!! doc: i is row number, j is column number (throughout, for sparse stuff). Would calling them row/column be better?
-//!!! what to do with this, now that it doesn't match the former newMatrix?
-newSparseMatrix size type data =
-    makeSparse size type
-       (filter
-            do { i, j, v }:
-                i == int i and i >= 0 and i < size.rows and 
-                j == int j and j >= 0 and j < size.columns
-            done data);
+//!!! doc: Rows/Columns determines the storage order, the input data are treated the same either way (perhaps this does mean row/column would be better than i/j)
+newSparseMatrix size d =
+   (tagger = case d of Rows _: Rows; Columns _: Columns esac;
+    data = case d of Rows rr: rr; Columns cc: cc esac;
+    data = filter
+        do { i, j, v }:
+            i == int i and i >= 0 and i < size.rows and 
+            j == int j and j >= 0 and j < size.columns
+        done data;
+    newSparse size (tagger data));
 
 toSparse m =
     if isSparse? m then m
-    else
-        makeSparse (size m) (typeOf m) (enumerateDense m);
+    else newSparseMatching m (enumerateDense m);
     fi;
 
 toDense m =
@@ -346,7 +378,7 @@
 
 flipped m =
     if isSparse? m then
-        makeSparse (size m) (flippedTypeOf m) (enumerateSparse m)
+        newSparse (size m) ((taggerForFlippedTypeOf m) (enumerateSparse m))
     else
         if isRowMajor? m then
             generate do row col: at' m row col done (size m);
@@ -407,20 +439,6 @@
 equal =
     equal' (==) vec.equal;
 
-//!!! when external api has settled, revise this internal function to look more like it
-newMatrixOfSize size type rowscols = //!!! NB does not copy data
-    if type == RowMajor () then
-        {
-            size,
-            data = DenseRows (array rowscols)
-        }
-    else
-        {
-            size,
-            data = DenseCols (array rowscols)
-        }
-    fi;
-
 fromRows rows =
     {
         size = { 
@@ -445,6 +463,12 @@
         data = DenseCols (array cols)
     };
 
+fromLists data = 
+    case data of
+    Rows rr: fromRows (map vec.fromList rr);
+    Columns cc: fromColumns (map vec.fromList cc);
+    esac;
+
 newRowVector data = //!!! NB does not copy data
     fromRows (array [data]);
 
@@ -453,10 +477,10 @@
 
 denseLinearOp op m1 m2 =
     if isRowMajor? m1 then
-        newMatrixOfSize m1.size (typeOf m1) 
+        newMatrixMatching m1
            (map2 do c1 c2: op c1 c2 done (asRows m1) (asRows m2));
     else
-        newMatrixOfSize m1.size (typeOf m1) 
+        newMatrixMatching m1
            (map2 do c1 c2: op c1 c2 done (asColumns m1) (asColumns m2));
     fi;
 
@@ -477,7 +501,7 @@
             kk = keys h[i];
             map2 do j v: { i, j, v } done kk (map (at h[i]) kk)
             done (keys h));
-    makeSparse (size m1) (typeOf m1) entries);
+    newSparseMatching m1 entries);
 
 sum' m1 m2 =
     if (size m1) != (size m2)
@@ -500,37 +524,37 @@
 
 scaled factor m =
     if isSparse? m then
-        makeSparse (size m) (typeOf m)
+        newSparseMatching m
            (map do { i, j, v }: { i, j, v = factor * v } done (enumerate m))
     elif isRowMajor? m then
-        newMatrixOfSize (size m) (typeOf m) (map (vec.scaled factor) (asRows m));
+        newMatrixMatching m (map (vec.scaled factor) (asRows m));
     else
-        newMatrixOfSize (size m) (typeOf m) (map (vec.scaled factor) (asColumns m));
+        newMatrixMatching m (map (vec.scaled factor) (asColumns m));
     fi;
 
 abs' m =
     if isSparse? m then
-        makeSparse (size m) (typeOf m)
+        newSparseMatching m
            (map do { i, j, v }: { i, j, v = abs v } done (enumerate m))
     elif isRowMajor? m then
-        newMatrixOfSize (size m) (typeOf m) (map vec.abs (asRows m));
+        newMatrixMatching m (map vec.abs (asRows m));
     else
-        newMatrixOfSize (size m) (typeOf m) (map vec.abs (asColumns m));
+        newMatrixMatching m (map vec.abs (asColumns m));
     fi;
 
 negative m =
     if isSparse? m then
-        makeSparse (size m) (typeOf m)
+        newSparseMatching m
            (map do { i, j, v }: { i, j, v = (-v) } done (enumerate m))
     elif isRowMajor? m then
-        newMatrixOfSize (size m) (typeOf m) (map vec.negative (asRows m));
+        newMatrixMatching m (map vec.negative (asRows m));
     else
-        newMatrixOfSize (size m) (typeOf m) (map vec.negative (asColumns m));
+        newMatrixMatching m (map vec.negative (asColumns m));
     fi;
 
 //!!! doc: filter by predicate, always returns sparse matrix
 filter' f m =
-    makeSparse (size m) (typeOf m) 
+    newSparseMatching m
        (map do { i, j, v }: { i, j, v = if f v then v else 0 fi } done
            (enumerate m));
 
@@ -559,7 +583,7 @@
             data[j'][i] := data[j'][i] + (vec.at values ix) * (vec.at c j);
         done;
     done;
-    newMatrixOfSize size (ColumnMajor ()) (array (map vec.vector (list data))));
+    newMatrix size (Columns (array (map vec.vector (list data)))));
 
 sparseProductRight size m1 m2 =
    ({ values, indices, pointers } = 
@@ -580,7 +604,7 @@
             data[i'][j] := data[i'][j] + (vec.at values ix) * (vec.at r i);
         done;
     done;
-    newMatrixOfSize size (RowMajor ()) (array (map vec.vector (list data))));
+    newMatrix size (Rows (array (map vec.vector (list data)))));
 
 sparseProduct size m1 m2 =
     case m2.data of
@@ -617,7 +641,7 @@
                     { i, j = j', v = hout[i] }
                 done (keys hout);
             done (nonEmptySlices d));
-        makeSparse size (ColumnMajor ()) (concat entries));
+        newSparse size (Columns (concat entries)));
     SparseCSR _:
         sparseProduct size m1 (flipped m2);
      _: failWith "sparseProduct called for non-sparse matrices";
@@ -631,7 +655,7 @@
             data[j][i] := vec.sum (vec.multiply row (getColumn j m2));
         done;
     done;
-    newMatrixOfSize size (ColumnMajor ()) (array (map vec.vector (list data))));
+    newMatrix size (Columns (array (map vec.vector (list data)))));
 
 product m1 m2 =
     if (size m1).columns != (size m2).rows
@@ -688,9 +712,10 @@
                  done (enumerate m)) ++ acc);
          _: acc;
         esac;
-    makeSparse { rows, columns } (typeOf first)
-        if direction == Vertical () then entries 0 0 1 0 mm []
-        else entries 0 0 0 1 mm [] fi);
+    newSparse { rows, columns }
+       ((taggerForTypeOf first)
+           (if direction == Vertical () then entries 0 0 1 0 mm []
+            else entries 0 0 0 1 mm [] fi)));
 
 checkDimensions counter first mm =
    (n = counter (size first);
@@ -754,13 +779,13 @@
         elif end > height m then rowSlice m start (height m)
         else
             if isRowMajor? m then
-                newMatrixOfSize { rows = end - start, columns = width m }
-                   (RowMajor ())
-                   (array (map ((flip getRow) m) [start .. end - 1]))
+                newMatrix { rows = end - start, columns = width m }
+                   (Rows
+                       (array (map ((flip getRow) m) [start .. end - 1])))
             else 
-                newMatrixOfSize { rows = end - start, columns = width m }
-                   (ColumnMajor ())
-                   (array (map do v: vec.slice v start end done (asColumns m)))
+                newMatrix { rows = end - start, columns = width m }
+                   (Columns
+                       (array (map do v: vec.slice v start end done (asColumns m))))
             fi;
         fi;
     fi;
@@ -775,13 +800,13 @@
         elif end > width m then columnSlice m start (width m)
         else
             if not isRowMajor? m then
-                newMatrixOfSize { rows = height m, columns = end - start }
-                   (ColumnMajor ())
-                   (array (map ((flip getColumn) m) [start .. end - 1]))
+                newMatrix { rows = height m, columns = end - start }
+                   (Columns
+                       (array (map ((flip getColumn) m) [start .. end - 1])))
             else 
-                newMatrixOfSize { rows = height m, columns = end - start }
-                   (RowMajor ())
-                   (array (map do v: vec.slice v start end done (asRows m)))
+                newMatrix { rows = height m, columns = end - start }
+                   (Rows
+                       (array (map do v: vec.slice v start end done (asRows m))))
             fi;
         fi;
     fi;
@@ -790,9 +815,9 @@
    (if newsize == (size m) then
         m
     elif isSparse? m then
-        // don't call makeSparse directly: want to discard
+        // don't call newSparse directly: want to discard
         // out-of-range cells
-        newSparseMatrix newsize (typeOf m) (enumerateSparse m)
+        newSparseMatrix newsize ((taggerForTypeOf m) (enumerateSparse m))
     elif (height m) == 0 or (width m) == 0 then
         zeroMatrixWithTypeOf m newsize;
     else
@@ -834,16 +859,10 @@
     fi;
 
 mapRows rf m =
-    newMatrixOfSize (size m) (RowMajor ()) (map rf (asRows m));
+    newMatrix (size m) (Rows (map rf (asRows m)));
 
 mapColumns cf m =
-    newMatrixOfSize (size m) (ColumnMajor ()) (map cf (asColumns m));
-
-newMatrix size d =
-    case d of
-    Rows rr: newMatrixOfSize size (RowMajor ()) (map vec.fromList rr);
-    Columns cc: newMatrixOfSize size (ColumnMajor ()) (map vec.fromList cc);
-    esac;
+    newMatrix (size m) (Columns (map cf (asColumns m)));
 
 format m =
     strJoin "\n"
@@ -917,9 +936,10 @@
     mapColumns,
     fromRows,
     fromColumns,
+    fromLists,
+    newMatrix,
     newRowVector,
     newColumnVector,
-    newMatrix,
     newSparseMatrix,
     enumerate,
     format,
@@ -974,10 +994,11 @@
     mapColumns is (vec.vector_t -> vec.vector_t) -> matrix_t -> matrix_t,
     fromRows is list<vec.vector_t> -> matrix_t, 
     fromColumns is list<vec.vector_t> -> matrix_t, 
+    fromLists is (Rows list<list<number>> | Columns list<list<number>>) -> matrix_t,
+    newMatrix is { rows is number, columns is number } -> (Rows list<vec.vector_t> | Columns list<vec.vector_t>) -> matrix_t,
     newRowVector is vec.vector_t -> matrix_t, 
     newColumnVector is vec.vector_t -> matrix_t,
-    newMatrix is { rows is number, columns is number } -> (Rows list?<list?<number>> | Columns list?<list?<number>>) -> matrix_t,
-    newSparseMatrix is { rows is number, columns is number } -> (ColumnMajor () | RowMajor ()) -> list<{ i is number, j is number, v is number }> -> matrix_t,
+    newSparseMatrix is { rows is number, columns is number } -> (Rows list<{ i is number, j is number, v is number }> | Columns list<{ i is number, j is number, v is number }>) -> matrix_t,
     enumerate is matrix_t -> list<{ i is number, j is number, v is number }>,
     format is matrix_t -> string,
     print is matrix_t -> (),
--- a/src/may/matrix/test/test_matrix.yeti	Wed Nov 20 16:39:36 2013 +0000
+++ b/src/may/matrix/test/test_matrix.yeti	Wed Nov 20 17:03:20 2013 +0000
@@ -245,28 +245,30 @@
 ),
 
 "sparseSum-\(name)": \(
-    s = mat.newSparseMatrix { rows = 2, columns = 3 } (ColumnMajor ()) [
-        { i = 0, j = 0, v = 1 },
-        { i = 0, j = 2, v = 2 },
-        { i = 1, j = 1, v = 4 },
-    ];
-    t = mat.newSparseMatrix { rows = 2, columns = 3 } (ColumnMajor ()) [
-        { i = 0, j = 1, v = 7 },
-        { i = 1, j = 0, v = 5 },
-        { i = 1, j = 1, v = -4 }, // NB this means [1,1] -> 0, sparse zero
-    ];
+    s = mat.newSparseMatrix { rows = 2, columns = 3 }
+       (Columns [
+            { i = 0, j = 0, v = 1 },
+            { i = 0, j = 2, v = 2 },
+            { i = 1, j = 1, v = 4 },
+        ]);
+    t = mat.newSparseMatrix { rows = 2, columns = 3 }
+        (Columns [
+            { i = 0, j = 1, v = 7 },
+            { i = 1, j = 0, v = 5 },
+            { i = 1, j = 1, v = -4 }, // NB this means [1,1] -> 0, sparse zero
+        ]);
     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 { rows = 2, columns = 3 } (RowMajor ()) [
+           (mat.newSparseMatrix { rows = 2, columns = 3 } (Rows [
                { i = 0, j = 0, v = 1 },
                { i = 0, j = 1, v = 7 },
                { i = 0, j = 2, v = 2 },
                { i = 1, j = 0, v = 5 },
-            ]) and
+            ])) and
         compare (mat.density tot) (4/6)
 ),
 
@@ -288,29 +290,29 @@
 ),
 
 "sparseDifference-\(name)": \(
-    s = mat.newSparseMatrix { rows = 2, columns = 3 } (ColumnMajor ()) [
+    s = mat.newSparseMatrix { rows = 2, columns = 3 } (Columns [
         { i = 0, j = 0, v = 1 },
         { i = 0, j = 2, v = 2 },
         { i = 1, j = 1, v = 4 },
-    ];
-    t = mat.newSparseMatrix { rows = 2, columns = 3 } (ColumnMajor ()) [
+    ]);
+    t = mat.newSparseMatrix { rows = 2, columns = 3 } (Columns [
         { 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 { rows = 2, columns = 3 } (RowMajor ()) [
+           (mat.newSparseMatrix { rows = 2, columns = 3 } (Rows [
                { 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)": \(
@@ -331,27 +333,27 @@
 ),
 
 "sparseProduct-\(name)": \(
-    s = mat.newSparseMatrix { rows = 2, columns = 3 } (ColumnMajor ()) [
+    s = mat.newSparseMatrix { rows = 2, columns = 3 } (Columns [
         { i = 0, j = 0, v = 1 },
         { i = 0, j = 2, v = 2 },
         { i = 1, j = 1, v = 4 },
-    ];
-    t = mat.newSparseMatrix { rows = 3, columns = 2 } (ColumnMajor ()) [
+    ]);
+    t = mat.newSparseMatrix { rows = 3, columns = 2 } (Columns [
         { i = 0, j = 1, v = 7 },
         { i = 1, j = 0, v = 5 },
         { i = 2, j = 0, v = 6 },
-    ];
+    ]);
     prod = mat.product s t;
     mat.isSparse? prod and
         compareMatrices prod (mat.product (mat.toDense s) t) and
         compareMatrices prod (mat.product (mat.toDense s) (mat.toDense t)) and
         compareMatrices prod (mat.product s (mat.toDense t)) and
         compareMatrices prod 
-           (mat.newSparseMatrix { rows = 2, columns = 2 } (RowMajor ()) [
+           (mat.newSparseMatrix { rows = 2, columns = 2 } (Rows [
                { i = 0, j = 0, v = 12 },
                { i = 0, j = 1, v = 7 },
                { i = 1, j = 0, v = 20 },
-            ])
+            ]))
 ),
 
 "productFail-\(name)": \(
@@ -397,10 +399,10 @@
                        (fromRows [[]])) and
         compareMatrices
            (mat.zeroMatrix { rows = 0, columns = 0 })
-           (mat.newSparseMatrix { rows = 0, columns = 0 } (ColumnMajor ()) []) and
+           (mat.newSparseMatrix { rows = 0, columns = 0 } (Columns [])) and
         compareMatrices
            (mat.zeroMatrix { rows = 1, columns = 0 })
-           (mat.newSparseMatrix { rows = 1, columns = 0 } (ColumnMajor ()) [])
+           (mat.newSparseMatrix { rows = 1, columns = 0 } (Columns []))
 ),
 
 "asRows-\(name)": \(
@@ -557,24 +559,24 @@
 ),
 
 "newSparseMatrix-\(name)": \(
-    s = mat.newSparseMatrix { rows = 2, columns = 3 } (ColumnMajor ()) [
+    s = mat.newSparseMatrix { rows = 2, columns = 3 } (Columns [
         { i = 0, j = 0, v = 1 },
         { i = 0, j = 2, v = 2 },
         { i = 1, j = 1, v = 4 },
-    ];
+    ]);
     // If there are zeros in the entries list, they should not end up
     // in the sparse data
-    t = mat.newSparseMatrix { rows = 2, columns = 3 } (ColumnMajor ()) [
+    t = mat.newSparseMatrix { rows = 2, columns = 3 } (Columns [
         { i = 0, j = 0, v = 1 },
         { i = 0, j = 2, v = 0 },
         { i = 1, j = 1, v = 4 },
-    ];
+    ]);
     // Any out-of-range or non-integer i, j should be ignored too
-    u = mat.newSparseMatrix { rows = 2, columns = 3 } (ColumnMajor ()) [
+    u = mat.newSparseMatrix { rows = 2, columns = 3 } (Columns [
         { i = -1, j = 0, v = 1 },
         { i = 0, j = 4, v = 3 },
         { i = 1, j = 1.5, v = 4 },
-    ];
+    ]);
     compare (mat.density s) (3/6) and
         compare (mat.density t) (2/6) and
         compareMatrices s (fromRows [[1,0,2],[0,4,0]]) and