Mercurial > hg > may
changeset 250:6a141098c059
Merge from sparse branch
author | Chris Cannam |
---|---|
date | Mon, 20 May 2013 22:17:35 +0100 |
parents | 545a32830886 (current diff) 1ea5bf6e76b6 (diff) |
children | 9fe3192cce38 |
files | |
diffstat | 5 files changed, 521 insertions(+), 123 deletions(-) [+] |
line wrap: on
line diff
--- a/yetilab/matrix/matrix.yeti Sun May 19 18:42:10 2013 +0100 +++ b/yetilab/matrix/matrix.yeti Mon May 20 22:17:35 2013 +0100 @@ -19,68 +19,128 @@ size m = case m of - RowM r: + DenseRows r: major = length r; { rows = major, columns = if major > 0 then vec.length r[0] else 0 fi, }; - ColM c: + DenseCols c: major = length c; { rows = if major > 0 then vec.length c[0] else 0 fi, columns = major, }; + SparseCSR { values, indices, pointers, extent }: + { + rows = (length pointers) - 1, + columns = extent + }; + SparseCSC { values, indices, pointers, extent }: + { + rows = extent, + columns = (length pointers) - 1 + }; esac; width m = (size m).columns; height m = (size m).rows; +nonZeroValues m = + (nz d = + sum + (map do v: + sum (map do n: if n == 0 then 0 else 1 fi done (vec.list v)) + done d); + case m of + DenseRows d: nz d; + DenseCols d: nz d; + SparseCSR d: vec.length d.values; + SparseCSC d: vec.length d.values; + esac); + +density m = + ({ rows, columns } = size m; + cells = rows * columns; + (nonZeroValues m) / cells); + +sparseSlice n d = + (start = d.pointers[n]; + end = d.pointers[n+1]; + { + values = vec.slice d.values start end, + indices = slice d.indices start end, + }); + +fromSlice n m d = + (slice = sparseSlice n d; + var v = 0; + for [0..length slice.indices - 1] do i: + if slice.indices[i] == m then + v := vec.at i slice.values; + fi + done; + v); + +filledSlice n d = + (slice = sparseSlice n d; + dslice = new double[d.extent]; + for [0..length slice.indices - 1] do i: + dslice[slice.indices[i]] := vec.at i slice.values; + done; + vec.vector dslice); + getAt row col m = case m of - RowM rows: r = rows[row]; vec.at col r; - ColM cols: c = cols[col]; vec.at row c; + DenseRows rows: r = rows[row]; vec.at col r; + DenseCols cols: c = cols[col]; vec.at row c; + SparseCSR data: fromSlice row col data; + SparseCSC data: fromSlice col row data; esac; getColumn j m = case m of - RowM rows: vec.fromList (map do i: getAt i j m done [0..length rows-1]); - ColM cols: cols[j]; + DenseCols cols: cols[j]; + SparseCSC data: filledSlice j data; + _: vec.fromList (map do i: getAt i j m done [0..height m - 1]); esac; getRow i m = case m of - RowM rows: rows[i]; - ColM cols: vec.fromList (map do j: getAt i j m done [0..length cols-1]); + DenseRows rows: rows[i]; + SparseCSR data: filledSlice i data; + _: vec.fromList (map do j: getAt i j m done [0..width m - 1]); esac; -/* -setAt row col n m = //!!! dangerous, could modify copies -- should it be allowed? - case m of - RowM rows: r = rows[row]; (vec.data r)[col] := n; - ColM cols: c = cols[col]; (vec.data c)[row] := n; - esac; -*/ - isRowMajor? m = case m of - RowM _: true; - ColM _: false; + DenseRows _: true; + DenseCols _: false; + SparseCSR _: true; + SparseCSC _: false; esac; -newColMajorStorage { rows, columns } = +isSparse? m = + case m of + DenseRows _: false; + DenseCols _: false; + SparseCSR _: true; + SparseCSC _: true; + esac; + +newColumnMajorStorage { rows, columns } = if rows < 1 then array [] else array (map \(vec.zeros rows) [1..columns]) fi; zeroMatrix { rows, columns } = - ColM (newColMajorStorage { rows, columns }); + DenseCols (newColumnMajorStorage { rows, columns }); zeroMatrixWithTypeOf m { rows, columns } = if isRowMajor? m then - RowM (newColMajorStorage { rows = columns, columns = rows }); + DenseRows (newColumnMajorStorage { rows = columns, columns = rows }); else - ColM (newColMajorStorage { rows, columns }); + DenseCols (newColumnMajorStorage { rows, columns }); fi; zeroSizeMatrix () = zeroMatrix { rows = 0, columns = 0 }; @@ -94,7 +154,90 @@ m[col][row] := f row col; done; done; - ColM (array (map vec.vector m)) + DenseCols (array (map vec.vector m)) + fi; + +enumerateSparse m = + (enumerate { values, indices, pointers } = + concat + (map do i: + start = pointers[i]; + end = pointers[i+1]; + map2 do j v: { i, j, v } done + (slice indices start end) + (vec.list (vec.slice values start end)) + done [0..length pointers - 2]); + case m of + SparseCSC d: + map do { i, j, v }: { i = j, j = i, v } done (enumerate d); + SparseCSR d: + enumerate d; + _: []; + esac); + +makeSparse type size data = + (isRow = case type of RowMajor (): true; ColumnMajor (): false esac; + ordered = + sortBy do a b: + if a.maj == b.maj then a.min < b.min else a.maj < b.maj fi + done + (map + if isRow then + do { i, j, v }: { maj = i, min = j, v } done; + else + do { i, j, v }: { maj = j, min = i, v } done; + fi + data); + tagger = if isRow then SparseCSR else SparseCSC fi; + majorSize = if isRow then size.rows else size.columns fi; + minorSize = if isRow then size.columns else size.rows fi; + majorPointers acc nn n i data = + if n < nn then + case data of + d::rest: + majorPointers (acc ++ (map \(i) [n..d-1])) nn d (i+1) rest; + _: + majorPointers (acc ++ [i]) nn (n+1) i []; + esac; + else + acc + fi; + tagger { + values = vec.fromList (map (.v) ordered), + indices = array (map (.min) ordered), + pointers = array (majorPointers [0] majorSize 0 0 (map (.maj) ordered)), + extent = minorSize, + }); + +toSparse m = + if isSparse? m then m + else + { rows, columns } = size m; + enumerate m ii jj = + case ii of + i::irest: + case jj of + j::rest: + v = getAt i j m; + if v != 0 then { i, j, v } :. \(enumerate m ii rest) + else enumerate m ii rest + fi; + _: enumerate m irest [0..columns-1]; + esac; + _: []; + esac; + makeSparse + if isRowMajor? m then RowMajor () else ColumnMajor () fi + (size m) + (enumerate m [0..rows-1] [0..columns-1]); + fi; + +toDense m = + if not (isSparse? m) then m + elif isRowMajor? m then + DenseRows (array (map do row: getRow row m done [0..height m - 1])); + else + DenseCols (array (map do col: getColumn col m done [0..width m - 1])); fi; constMatrix n = generate do row col: n done; @@ -103,17 +246,27 @@ transposed m = case m of - RowM d: ColM d; - ColM d: RowM d; + DenseRows d: DenseCols d; + DenseCols d: DenseRows d; + SparseCSR d: SparseCSC d; + SparseCSC d: SparseCSR d; esac; flipped m = - if isRowMajor? m then - generate do row col: getAt row col m done (size m); + if isSparse? m then + if isRowMajor? m then + makeSparse (ColumnMajor ()) (size m) (enumerateSparse m) + else + makeSparse (RowMajor ()) (size m) (enumerateSparse m) + fi else - transposed - (generate do row col: getAt col row m done - { rows = (width m), columns = (height m) }); + if isRowMajor? m then + generate do row col: getAt row col m done (size m); + else + transposed + (generate do row col: getAt col row m done + { rows = (width m), columns = (height m) }); + fi fi; toRowMajor m = @@ -122,53 +275,72 @@ toColumnMajor m = if not isRowMajor? m then m else flipped m fi; -equal' vecComparator m1 m2 = +equal'' comparator vecComparator m1 m2 = + // Prerequisite: m1 and m2 have same sparse-p and storage order + (compareVecLists vv1 vv2 = all id (map2 vecComparator vv1 vv2); + compareSparse d1 d2 = + d1.extent == d2.extent and + vecComparator d1.values d2.values and + d1.indices == d2.indices and + d1.pointers == d2.pointers; + case m1 of + DenseRows d1: + case m2 of DenseRows d2: compareVecLists d1 d2; _: false; esac; + DenseCols d1: + case m2 of DenseCols d2: compareVecLists d1 d2; _: false; esac; + SparseCSR d1: + case m2 of SparseCSR d2: compareSparse d1 d2; _: false; esac; + SparseCSC d1: + case m2 of SparseCSC d2: compareSparse d1 d2; _: false; esac; + esac); + +equal' comparator vecComparator m1 m2 = if size m1 != size m2 then false elif isRowMajor? m1 != isRowMajor? m2 then - equal' vecComparator (flipped m1) m2; + equal' comparator vecComparator (flipped m1) m2; + elif isSparse? m1 != isSparse? m2 then + if isSparse? m1 then + equal' comparator vecComparator m1 (toSparse m2) + else + equal' comparator vecComparator (toSparse m1) m2 + fi else - compare d1 d2 = all id (map2 vecComparator d1 d2); - case m1 of - RowM d1: case m2 of RowM d2: compare d1 d2; _: false; esac; - ColM d1: case m2 of ColM d2: compare d1 d2; _: false; esac; - esac + equal'' comparator vecComparator m1 m2 fi; // Compare matrices using the given comparator for individual cells. // Note that matrices with different storage order but the same // contents are equal, although comparing them is slow. +//!!! Document the fact that sparse matrices can only be equal if they +// have the same set of non-zero cells (regardless of comparator used) equalUnder comparator = - equal' (vec.equalUnder comparator); + equal' comparator (vec.equalUnder comparator); equal = - equal' vec.equal; - -/*!!! not needed now it's immutable? -copyOf m = - (copyOfData d = (array (map vec.copyOf d)); - case m of - RowM d: RowM (copyOfData d); - ColM d: ColM (copyOfData d); - esac); -*/ + equal' (==) vec.equal; newMatrix type data = //!!! NB does not copy data - (tagger = case type of RowMajor (): RowM; ColumnMajor (): ColM esac; + (tagger = case type of RowMajor (): DenseRows; ColumnMajor (): DenseCols esac; if empty? data or vec.empty? (head data) then zeroSizeMatrix () else tagger (array data) fi); newRowVector data = //!!! NB does not copy data - RowM (array [data]); + DenseRows (array [data]); newColumnVector data = //!!! NB does not copy data - ColM (array [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); + sum' m1 m2 = if (size m1) != (size m2) then failWith "Matrices are not the same size: \(size m1), \(size m2)"; @@ -186,13 +358,50 @@ abs' m = generate do row col: abs (getAt row col m) done (size m); +sparseProductLeft size m1 m2 = + (e = enumerateSparse m1; + data = array (map \(new double[size.rows]) [1..size.columns]); + for [0..size.columns - 1] do j': + c = getColumn j' m2; + for e do { v, i, j }: + data[j'][i] := data[j'][i] + v * (vec.at j c); + done; + done; + DenseCols (array (map vec.vector (list data)))); + +sparseProductRight size m1 m2 = + (e = enumerateSparse m2; + data = array (map \(new double[size.columns]) [1..size.rows]); + for [0..size.rows - 1] do i': + r = getRow i' m1; + for e do { v, i, j }: + data[i'][j] := data[i'][j] + v * (vec.at i r); + done; + done; + DenseRows (array (map vec.vector (list data)))); + +denseProduct size m1 m2 = + (data = array (map \(new double[size.rows]) [1..size.columns]); + for [0..size.rows - 1] do i: + row = getRow i m1; + for [0..size.columns - 1] do j: + data[j][i] := bf.sum (bf.multiply row (getColumn j m2)); + done; + done; + DenseCols (array (map vec.vector (list data)))); + product m1 m2 = if (size m1).columns != (size m2).rows - then failWith "Matrix dimensions incompatible: \(size m1), \(size m2) (\((size m1).columns != (size m2).rows)"; - else - generate do row col: - bf.sum (bf.multiply (getRow row m1) (getColumn col m2)) - done { rows = (size m1).rows, columns = (size m2).columns } + then failWith "Matrix dimensions incompatible: \(size m1), \(size m2) (\((size m1).columns) != \((size m2).rows))"; + else + size = { rows = (size m1).rows, columns = (size m2).columns }; + if isSparse? m1 then + sparseProductLeft size m1 m2 + elif isSparse? m2 then + sparseProductRight size m1 m2 + else + denseProduct size m1 m2 + fi; fi; asRows m = @@ -235,30 +444,31 @@ // vertical, col-major: against grain with cols case direction of Horizontal (): - if row then concatAgainstGrain RowM getRow (.rows) mm; - else concatWithGrain ColM getColumn (.columns) mm; + if row then concatAgainstGrain DenseRows getRow (.rows) mm; + else concatWithGrain DenseCols getColumn (.columns) mm; fi; Vertical (): - if row then concatWithGrain RowM getRow (.rows) mm; - else concatAgainstGrain ColM getColumn (.columns) mm; + if row then concatWithGrain DenseRows getRow (.rows) mm; + else concatAgainstGrain DenseCols getColumn (.columns) mm; fi; esac; [single]: single; _: zeroSizeMatrix (); esac; +//!!! inconsistent with std.slice which has start..end not start+count (see also vec.slice/rangeOf) rowSlice start count m = //!!! doc: storage order same as input if isRowMajor? m then - RowM (array (map ((flip getRow) m) [start .. start + count - 1])) + DenseRows (array (map ((flip getRow) m) [start .. start + count - 1])) else - ColM (array (map (vec.rangeOf start count) (asColumns m))) + DenseCols (array (map (vec.rangeOf start count) (asColumns m))) fi; columnSlice start count m = //!!! doc: storage order same as input if not isRowMajor? m then - ColM (array (map ((flip getColumn) m) [start .. start + count - 1])) + DenseCols (array (map ((flip getColumn) m) [start .. start + count - 1])) else - RowM (array (map (vec.rangeOf start count) (asRows m))) + DenseRows (array (map (vec.rangeOf start count) (asRows m))) fi; resizedTo newsize m = @@ -294,11 +504,13 @@ size, width, height, + density, + nonZeroValues, getAt, getColumn, getRow, -// setAt, isRowMajor?, + isSparse?, generate, constMatrix, randomMatrix, @@ -307,12 +519,14 @@ zeroSizeMatrix, equal, equalUnder, -// copyOf, transposed, flipped, toRowMajor, toColumnMajor, + toSparse, + toDense, scaled, + thresholded, resizedTo, asRows, asColumns, @@ -326,6 +540,7 @@ newMatrix, newRowVector, newColumnVector, + newSparseMatrix = makeSparse } as { @@ -334,11 +549,13 @@ size is matrix -> { .rows is number, .columns is number }, width is matrix -> number, height is matrix -> number, + density is matrix -> number, + nonZeroValues is matrix -> number, getAt is number -> number -> matrix -> number, getColumn is number -> matrix -> vector, getRow is number -> matrix -> vector, -// setAt is number -> number -> number -> matrix -> (), //!!! lose? isRowMajor? is matrix -> boolean, + isSparse? is matrix -> boolean, generate is (number -> number -> number) -> { .rows is number, .columns is number } -> matrix, constMatrix is number -> { .rows is number, .columns is number } -> matrix, randomMatrix is { .rows is number, .columns is number } -> matrix, @@ -347,12 +564,14 @@ zeroSizeMatrix is () -> matrix, equal is matrix -> matrix -> boolean, equalUnder is (number -> number -> boolean) -> matrix -> matrix -> boolean, -// copyOf is matrix -> matrix, transposed is matrix -> matrix, flipped is matrix -> matrix, toRowMajor is matrix -> matrix, toColumnMajor is matrix -> matrix, + toSparse is matrix -> matrix, + toDense is matrix -> matrix, scaled is number -> matrix -> matrix, + thresholded is number -> matrix -> matrix, resizedTo is { .rows is number, .columns is number } -> matrix -> matrix, asRows is matrix -> list<vector>, asColumns is matrix -> list<vector>, @@ -366,5 +585,6 @@ newMatrix is (ColumnMajor () | RowMajor ()) -> list<vector> -> matrix, newRowVector is vector -> matrix, newColumnVector is vector -> matrix, + newSparseMatrix is (ColumnMajor () | RowMajor ()) -> { .rows is number, .columns is number } -> list<{ .i is number, .j is number, .v is number }> -> matrix }
--- a/yetilab/matrix/matrixtype.yeti Sun May 19 18:42:10 2013 +0100 +++ b/yetilab/matrix/matrixtype.yeti Mon May 20 22:17:35 2013 +0100 @@ -3,7 +3,21 @@ load yetilab.vector.vectortype; -typedef opaque matrix = RowM. array<vector> | ColM. array<vector>; +typedef opaque matrix = + DenseRows. array<vector> | // array of rows + DenseCols. array<vector> | // array of columns + SparseCSR. { + .values is vector, + .indices is array<number>, // column index of each value + .pointers is array<number>, // offset of first value in each row + .extent is number // max possible index + 1, i.e. number of columns + } | + SparseCSC. { + .values is vector, + .indices is array<number>, // row index of each value + .pointers is array<number>, // offset of first value in each column + .extent is number // max pointers index + 1, i.e. number of rows + }; ();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/yetilab/matrix/test/speedtest.yeti Mon May 20 22:17:35 2013 +0100 @@ -0,0 +1,142 @@ + +program yetilab.matrix.test.speedtest; + +mat = load yetilab.matrix.matrix; +vec = load yetilab.vector.vector; + +{ compare, compareUsing } = load yetilab.test.test; + +compareMatrices = compareUsing mat.equal; + +time f = + (start = System#currentTimeMillis(); + result = f (); + end = System#currentTimeMillis(); + println " \(end-start)ms"; + result); + +makeMatrices sz sparsity = + (print "Making \(sz) * \(sz) random matrix..."; + m = time \(mat.randomMatrix { rows = sz, columns = sz }); + makeSparse () = + (print "Making \(sparsity * 100)% sparse version (as dense matrix)..."; + t = time \(mat.thresholded sparsity m); + println "Reported density: \(mat.density t) (non-zero values: \(mat.nonZeroValues t))"; + print "Converting to sparse matrix..."; + s = time \(mat.toSparse t); + println "Reported density: \(mat.density s) (non-zero values: \(mat.nonZeroValues s))"; + s); + s = makeSparse (); + println "Making types:"; + print "Col-major dense..."; + cmd = time \(mat.toColumnMajor m); + print "Row-major dense..."; + rmd = time \(mat.toRowMajor m); + print "Col-major sparse..."; + cms = time \(mat.toColumnMajor s); + print "Row-major sparse..."; + rms = time \(mat.toRowMajor s); + println ""; + { cmd, rmd, cms, rms }); + +println "\nR * M multiplies:\n"; + +sz = 2000; +sparsity = 0.98; + +{ cmd, rmd, cms, rms } = makeMatrices sz sparsity; + +row = mat.newRowVector (vec.fromList (map \(Math#random()) [1..sz])); +col = mat.newColumnVector (vec.fromList (map \(Math#random()) [1..sz])); + +print "R * CMD... "; +a = (time \(mat.product row cmd)); + +print "R * RMD... "; +b = (time \(mat.product row rmd)); + +print "R * CMS... "; +c = (time \(mat.product row cms)); + +print "R * RMS... "; +d = (time \(mat.product row rms)); + +println "\nChecking results: \(compareMatrices a b) \(compareMatrices c d)"; + +println "\nM * C multiplies:\n"; + +print "CMD * C... "; +a = (time \(mat.product cmd col)); + +print "RMD * C... "; +b = (time \(mat.product rmd col)); + +print "CMS * C... "; +c = (time \(mat.product cms col)); + +print "RMS * C... "; +d = (time \(mat.product rms col)); + +println "\nChecking results: \(compareMatrices a b) \(compareMatrices c d)"; + +println "\nM * M multiplies:\n"; + +sz = 500; + +{ cmd, rmd, cms, rms } = makeMatrices sz sparsity; + +print "CMS * CMD... "; +\() (time \(mat.product cms cmd)); + +print "CMS * RMD... "; +\() (time \(mat.product cms rmd)); + +print "RMS * CMD... "; +\() (time \(mat.product rms cmd)); + +print "RMS * RMD... "; +\() (time \(mat.product rms rmd)); + +println ""; + +print "CMD * CMS... "; +\() (time \(mat.product cmd cms)); + +print "CMD * RMS... "; +\() (time \(mat.product cmd rms)); + +print "RMD * CMS... "; +\() (time \(mat.product rmd cms)); + +print "RMD * RMS... "; +\() (time \(mat.product rmd rms)); + +println ""; + +print "CMS * CMS... "; +\() (time \(mat.product cms cms)); + +print "CMS * RMS... "; +\() (time \(mat.product cms rms)); + +print "RMS * CMS... "; +\() (time \(mat.product rms cms)); + +print "RMS * RMS... "; +\() (time \(mat.product rms rms)); + +println ""; + +print "CMD * CMD... "; +\() (time \(mat.product cmd cmd)); + +print "CMD * RMD... "; +\() (time \(mat.product cmd rmd)); + +print "RMD * CMD... "; +\() (time \(mat.product rmd cmd)); + +print "RMD * RMD... "; +\() (time \(mat.product rmd rmd)); + +();
--- a/yetilab/matrix/test/test_matrix.yeti Sun May 19 18:42:10 2013 +0100 +++ b/yetilab/matrix/test/test_matrix.yeti Mon May 20 22:17:35 2013 +0100 @@ -90,11 +90,11 @@ ), "equal-\(name)": \( - m = newMatrix (ColumnMajor ()) [[1,4],[2,5],[3,6]]; + m = newMatrix (ColumnMajor ()) [[1,4],[0,5],[3,6]]; n = m; - p = newMatrix (RowMajor ()) [[1,2,3],[4,5,6]]; - q = newMatrix (ColumnMajor ()) [[1,2,3],[4,5,6]]; - r = newMatrix (ColumnMajor ()) [[1,4],[2,5]]; + p = newMatrix (RowMajor ()) [[1,0,3],[4,5,6]]; + q = newMatrix (ColumnMajor ()) [[1,0,3],[4,5,6]]; + r = newMatrix (ColumnMajor ()) [[1,4],[0,5]]; compareMatrices m n and compareMatrices m p and compareMatrices n p and @@ -106,8 +106,8 @@ p = newMatrix (ColumnMajor ()) [[1,2,3],[4,5,6]]; q = newMatrix (ColumnMajor ()) [[1,2,3],[4,5,6]]; r = newMatrix (ColumnMajor ()) [[4,3,1],[3,1,2]]; - s = newMatrix (ColumnMajor ()) [[1,2,5],[6,7,8]]; - t = newMatrix (ColumnMajor ()) [[1,2,5],[6,7,9]]; + s = newMatrix (ColumnMajor ()) [[1,4,5],[6,7,8]]; + t = newMatrix (ColumnMajor ()) [[1,4,5],[6,7,9]]; mat.equalUnder (==) p p and mat.equalUnder (==) p q and mat.equalUnder (!=) p r and @@ -123,30 +123,7 @@ (map do col: mat.getAt row col m == generator row col done [0..2]) done [0..1]) ), -/*!!! -"setAt-\(name)": \( - generator row col = row * 10 + col; - m = generate generator { rows = 2, columns = 3 }; - mat.setAt 1 2 16 m; - compare (mat.getAt 1 2 m) 16 and - compare (mat.getAt 1 1 m) 11 and - compare (mat.getAt 0 2 m) 2 -), -"copyOfEqual-\(name)": \( - m = constMatrix 2 { rows = 3, columns = 4 }; - m'' = mat.copyOf m; - compareMatrices m'' m -), - -"copyOfAlias-\(name)": \( - m = constMatrix 2 { rows = 3, columns = 4 }; - m' = m; - m'' = mat.copyOf m; - mat.setAt 0 0 6 m; - compareMatrices m' m and not mat.equal m m''; -), -*/ "transposedEmpty-\(name)": \( compare (mat.size (mat.transposed (constMatrix 2 { rows = 0, columns = 0 }))) { columns = 0, rows = 0 } and compare (mat.size (mat.transposed (constMatrix 2 { rows = 0, columns = 4 }))) { columns = 0, rows = 0 } and @@ -175,14 +152,14 @@ ), "flipped-\(name)": \( - m = newMatrix (ColumnMajor ()) [[1,4],[2,5],[3,6]]; + m = newMatrix (ColumnMajor ()) [[1,4],[0,5],[3,6]]; m' = mat.flipped m; - m'' = newMatrix (RowMajor ()) [[1,2,3],[4,5,6]]; + m'' = newMatrix (RowMajor ()) [[1,0,3],[4,5,6]]; compareMatrices m m' and compareMatrices m m'' and compareMatrices m' m''; ), "flipped-back-\(name)": \( - m = newMatrix (ColumnMajor ()) [[1,4],[2,5],[3,6]]; + m = newMatrix (ColumnMajor ()) [[1,4],[0,5],[3,6]]; compareMatrices m (mat.flipped (mat.flipped m)); ), @@ -192,18 +169,18 @@ ), "toRowMajor-\(name)": \( - m = newMatrix (ColumnMajor ()) [[1,4],[2,5],[3,6]]; + m = newMatrix (ColumnMajor ()) [[1,4],[0,5],[3,6]]; m' = mat.toRowMajor m; - m'' = newMatrix (RowMajor ()) [[1,2,3],[4,5,6]]; + m'' = newMatrix (RowMajor ()) [[1,0,3],[4,5,6]]; m''' = mat.toRowMajor m''; compareMatrices m m' and compareMatrices m m'' and compareMatrices m' m'' and compareMatrices m m'''; ), "toColumnMajor-\(name)": \( - m = newMatrix (RowMajor ()) [[1,4],[2,5],[3,6]]; + m = newMatrix (RowMajor ()) [[1,4],[0,5],[3,6]]; m' = mat.toColumnMajor m; - m'' = newMatrix (ColumnMajor ()) [[1,2,3],[4,5,6]]; + m'' = newMatrix (ColumnMajor ()) [[1,0,3],[4,5,6]]; m''' = mat.toColumnMajor m''; compareMatrices m m' and compareMatrices m m'' and compareMatrices m' m'' and compareMatrices m m'''; @@ -310,29 +287,29 @@ "asRows-\(name)": \( compare (map vec.list - (mat.asRows (newMatrix (ColumnMajor ()) [[1,4],[2,5],[3,6]]))) - [[1,2,3],[4,5,6]]; + (mat.asRows (newMatrix (ColumnMajor ()) [[1,4],[0,5],[3,6]]))) + [[1,0,3],[4,5,6]]; ), "asColumns-\(name)": \( compare (map vec.list - (mat.asColumns (newMatrix (ColumnMajor ()) [[1,4],[2,5],[3,6]]))) - [[1,4],[2,5],[3,6]]; + (mat.asColumns (newMatrix (ColumnMajor ()) [[1,4],[0,5],[3,6]]))) + [[1,4],[0,5],[3,6]]; ), "concat-horiz-\(name)": \( compareMatrices (mat.concat (Horizontal ()) - [(newMatrix (ColumnMajor ()) [[1,4],[2,5]]), + [(newMatrix (ColumnMajor ()) [[1,4],[0,5]]), (newMatrix (RowMajor ()) [[3],[6]])]) - (newMatrix (ColumnMajor ()) [[1,4],[2,5],[3,6]]) + (newMatrix (ColumnMajor ()) [[1,4],[0,5],[3,6]]) ), "concatFail-horiz-\(name)": \( try \() (mat.concat (Horizontal ()) - [(newMatrix (ColumnMajor ()) [[1,4],[2,5]]), + [(newMatrix (ColumnMajor ()) [[1,4],[0,5]]), (newMatrix (ColumnMajor ()) [[3],[6]])]); false catch FailureException e: @@ -343,15 +320,15 @@ "concat-vert-\(name)": \( compareMatrices (mat.concat (Vertical ()) - [(newMatrix (ColumnMajor ()) [[1,4],[2,5]]), + [(newMatrix (ColumnMajor ()) [[1,4],[0,5]]), (newMatrix (RowMajor ()) [[3,6]])]) - (newMatrix (ColumnMajor ()) [[1,4,3],[2,5,6]]) + (newMatrix (ColumnMajor ()) [[1,4,3],[0,5,6]]) ), "concatFail-vert-\(name)": \( try \() (mat.concat (Vertical ()) - [(newMatrix (ColumnMajor ()) [[1,4],[2,5]]), + [(newMatrix (ColumnMajor ()) [[1,4],[0,5]]), (newMatrix (RowMajor ()) [[3],[6]])]); false catch FailureException e: @@ -361,24 +338,64 @@ "rowSlice-\(name)": \( compareMatrices - (mat.rowSlice 1 2 (newMatrix (RowMajor ()) [[1,2],[3,4],[5,6],[7,8]])) - (newMatrix (RowMajor ()) [[3,4],[5,6]]) + (mat.rowSlice 1 2 (newMatrix (RowMajor ()) [[1,0],[3,4],[0,6],[7,8]])) + (newMatrix (RowMajor ()) [[3,4],[0,6]]) ), "columnSlice-\(name)": \( compareMatrices - (mat.columnSlice 1 2 (newMatrix (RowMajor ()) [[1,2,3,4],[5,6,7,8]])) - (newMatrix (RowMajor ()) [[2,3],[6,7]]) + (mat.columnSlice 1 2 (newMatrix (RowMajor ()) [[1,0,3,4],[0,6,7,8]])) + (newMatrix (RowMajor ()) [[0,3],[6,7]]) +), + +"density-\(name)": \( + compare (mat.density (newMatrix (ColumnMajor ()) [[1,2,0],[0,5,0]])) (3/6) and + compare (mat.density (newMatrix (ColumnMajor ()) [[1,2,3],[4,5,6]])) (6/6) and + compare (mat.density (newMatrix (ColumnMajor ()) [[0,0,0],[0,0,0]])) 0 +), + +"nonZeroValues-\(name)": \( + compare (mat.nonZeroValues (newMatrix (ColumnMajor ()) [[1,2,0],[0,5,0]])) 3 and + compare (mat.nonZeroValues (newMatrix (ColumnMajor ()) [[1,2,3],[4,5,6]])) 6 and + compare (mat.nonZeroValues (newMatrix (ColumnMajor ()) [[0,0,0],[0,0,0]])) 0 +), + +"toSparse-\(name)": \( + m = newMatrix (ColumnMajor ()) [[1,2,0],[-1,-4,6],[0,0,3]]; + compareMatrices (mat.toSparse m) m and + compareMatrices (mat.toDense (mat.toSparse m)) m and + compare (mat.density (mat.toSparse m)) (6/9) +), + +"toDense-\(name)": \( + m = newMatrix (ColumnMajor ()) [[1,2,0],[-1,-4,6],[0,0,3]]; + compareMatrices (mat.toDense m) m and + compareMatrices (mat.toSparse (mat.toDense m)) m +), + +"thresholded-\(name)": \( + m = newMatrix (ColumnMajor ()) [[1,2,0],[-1,-4,6],[0,0,3]]; + compareMatrices + (mat.thresholded 2 m) + (newMatrix (ColumnMajor ()) [[0,0,0],[0,-4,6],[0,0,3]]) and + compare (mat.density (mat.thresholded 2 m)) (3/9) ), ]); -colhash = makeTests "column-major" id; -rowhash = makeTests "row-major" mat.flipped; +colhash = makeTests "column-dense" id; +rowhash = makeTests "row-dense" mat.flipped; +sparsecolhash = makeTests "column-sparse" mat.toSparse; + +// there are two possible orders for constructing a sparse row-major +// matrix from a dense col-major one, so test them both: +sparserowhash1 = makeTests "row-sparse-a" (mat.toSparse . mat.flipped); +sparserowhash2 = makeTests "row-sparse-b" (mat.flipped . mat.toSparse); all = [:]; -for (keys colhash) do k: all[k] := colhash[k] done; -for (keys rowhash) do k: all[k] := rowhash[k] done; +for [ colhash, rowhash, sparsecolhash, sparserowhash1, sparserowhash2 ] do h: + for (keys h) do k: all[k] := h[k] done; +done; all is hash<string, () -> boolean>;
--- a/yetilab/vector/vector.yeti Sun May 19 18:42:10 2013 +0100 +++ b/yetilab/vector/vector.yeti Mon May 20 22:17:35 2013 +0100 @@ -72,6 +72,9 @@ rangeOf start len v is number -> number -> ~double[] -> ~double[] = Arrays#copyOfRange(v, start, start + len); +slice v start end is ~double[] -> number -> number -> ~double[] = + rangeOf start (end - start) v; + resizedTo n v is number -> ~double[] -> ~double[] = Arrays#copyOf(v, n); @@ -103,6 +106,7 @@ equal, equalUnder, rangeOf, + slice, resizedTo, concat, } as { @@ -122,6 +126,7 @@ equal is vector -> vector -> boolean, equalUnder is (number -> number -> boolean) -> vector -> vector -> boolean, rangeOf is number -> number -> vector -> vector, //!!! not well-named now vector arg is at the end + slice is vector -> number -> number -> vector, //!!! duplication with rangeOf (std module function on arrays is called slice though) resizedTo is number -> vector -> vector, concat is list?<vector> -> vector, }