annotate yetilab/matrix/matrix.yeti @ 229:ac1373067054

Add vector max/min, matrix difference/abs, update audiofile ref tests
author Chris Cannam
date Sun, 12 May 2013 18:04:48 +0100
parents 1b02d903aa79
children 51c5ce72832e
rev   line source
Chris@5 1
Chris@94 2 module yetilab.matrix.matrix;
Chris@94 3
Chris@214 4 // A matrix is an array of vectors.
Chris@94 5
Chris@101 6 // A matrix can be stored in either column-major (the default) or
Chris@101 7 // row-major format. Storage order is an efficiency concern only:
Chris@101 8 // every API function operating on matrix objects will return the same
Chris@101 9 // result regardless of storage order. (The transpose function just
Chris@101 10 // switches the row/column order without moving the elements.)
Chris@18 11
Chris@214 12 //!!! check that we are not unnecessarily copying in the transform functions
Chris@214 13
Chris@222 14 vec = load yetilab.vector.vector;
Chris@222 15 bf = load yetilab.vector.blockfuncs;
Chris@9 16
Chris@222 17 load yetilab.vector.vectortype;
Chris@195 18 load yetilab.matrix.matrixtype;
Chris@195 19
Chris@208 20 size m =
Chris@208 21 case m of
Chris@208 22 RowM r:
Chris@208 23 major = length r;
Chris@208 24 {
Chris@208 25 rows = major,
Chris@214 26 columns = if major > 0 then vec.length r[0] else 0 fi,
Chris@208 27 };
Chris@208 28 ColM c:
Chris@208 29 major = length c;
Chris@208 30 {
Chris@214 31 rows = if major > 0 then vec.length c[0] else 0 fi,
Chris@208 32 columns = major,
Chris@208 33 };
Chris@208 34 esac;
Chris@208 35
Chris@208 36 width m = (size m).columns;
Chris@208 37 height m = (size m).rows;
Chris@208 38
Chris@208 39 getAt row col m =
Chris@208 40 case m of
Chris@214 41 RowM rows: r = rows[row]; vec.at col r;
Chris@214 42 ColM cols: c = cols[col]; vec.at row c;
Chris@208 43 esac;
Chris@208 44
Chris@208 45 getColumn j m =
Chris@208 46 case m of
Chris@214 47 RowM rows: vec.fromList (map do i: getAt i j m done [0..length rows-1]);
Chris@208 48 ColM cols: cols[j];
Chris@208 49 esac;
Chris@208 50
Chris@208 51 getRow i m =
Chris@208 52 case m of
Chris@208 53 RowM rows: rows[i];
Chris@214 54 ColM cols: vec.fromList (map do j: getAt i j m done [0..length cols-1]);
Chris@208 55 esac;
Chris@208 56
Chris@214 57 /*
Chris@208 58 setAt row col n m = //!!! dangerous, could modify copies -- should it be allowed?
Chris@208 59 case m of
Chris@214 60 RowM rows: r = rows[row]; (vec.data r)[col] := n;
Chris@214 61 ColM cols: c = cols[col]; (vec.data c)[row] := n;
Chris@208 62 esac;
Chris@214 63 */
Chris@208 64
Chris@208 65 isRowMajor? m =
Chris@208 66 case m of
Chris@208 67 RowM _: true;
Chris@208 68 ColM _: false;
Chris@208 69 esac;
Chris@94 70
Chris@201 71 newColMajorStorage { rows, columns } =
Chris@97 72 if rows < 1 then array []
Chris@214 73 else array (map \(vec.zeros rows) [1..columns])
Chris@97 74 fi;
Chris@94 75
Chris@98 76 zeroMatrix { rows, columns } =
Chris@208 77 ColM (newColMajorStorage { rows, columns });
Chris@201 78
Chris@201 79 zeroMatrixWithTypeOf m { rows, columns } =
Chris@208 80 if isRowMajor? m then
Chris@208 81 RowM (newColMajorStorage { rows = columns, columns = rows });
Chris@201 82 else
Chris@208 83 ColM (newColMajorStorage { rows, columns });
Chris@201 84 fi;
Chris@5 85
Chris@214 86 zeroSizeMatrix () = zeroMatrix { rows = 0, columns = 0 };
Chris@214 87
Chris@98 88 generate f { rows, columns } =
Chris@214 89 if rows < 1 or columns < 1 then zeroSizeMatrix ()
Chris@214 90 else
Chris@214 91 m = array (map \(new double[rows]) [1..columns]);
Chris@214 92 for [0..columns-1] do col:
Chris@214 93 for [0..rows-1] do row:
Chris@214 94 m[col][row] := f row col;
Chris@214 95 done;
Chris@5 96 done;
Chris@214 97 ColM (array (map vec.vector m))
Chris@214 98 fi;
Chris@5 99
Chris@20 100 constMatrix n = generate do row col: n done;
Chris@20 101 randomMatrix = generate do row col: Math#random() done;
Chris@5 102 identityMatrix = constMatrix 1;
Chris@5 103
Chris@100 104 transposed m =
Chris@208 105 case m of
Chris@208 106 RowM d: ColM d;
Chris@208 107 ColM d: RowM d;
Chris@208 108 esac;
Chris@100 109
Chris@100 110 flipped m =
Chris@208 111 if isRowMajor? m then
Chris@208 112 generate do row col: getAt row col m done (size m);
Chris@100 113 else
Chris@100 114 transposed
Chris@208 115 (generate do row col: getAt col row m done
Chris@208 116 { rows = (width m), columns = (height m) });
Chris@100 117 fi;
Chris@100 118
Chris@161 119 toRowMajor m =
Chris@208 120 if isRowMajor? m then m else flipped m fi;
Chris@161 121
Chris@161 122 toColumnMajor m =
Chris@208 123 if not isRowMajor? m then m else flipped m fi;
Chris@161 124
Chris@228 125 equal' vecComparator m1 m2 =
Chris@226 126 if size m1 != size m2 then
Chris@226 127 false
Chris@226 128 elif isRowMajor? m1 != isRowMajor? m2 then
Chris@228 129 equal' vecComparator (flipped m1) m2;
Chris@100 130 else
Chris@228 131 compare d1 d2 = all id (map2 vecComparator d1 d2);
Chris@208 132 case m1 of
Chris@208 133 RowM d1: case m2 of RowM d2: compare d1 d2; _: false; esac;
Chris@208 134 ColM d1: case m2 of ColM d2: compare d1 d2; _: false; esac;
Chris@100 135 esac
Chris@100 136 fi;
Chris@97 137
Chris@228 138 // Compare matrices using the given comparator for individual cells.
Chris@228 139 // Note that matrices with different storage order but the same
Chris@228 140 // contents are equal, although comparing them is slow.
Chris@228 141 equalUnder comparator =
Chris@228 142 equal' (vec.equalUnder comparator);
Chris@228 143
Chris@228 144 equal =
Chris@228 145 equal' vec.equal;
Chris@226 146
Chris@214 147 /*!!! not needed now it's immutable?
Chris@95 148 copyOf m =
Chris@214 149 (copyOfData d = (array (map vec.copyOf d));
Chris@208 150 case m of
Chris@208 151 RowM d: RowM (copyOfData d);
Chris@208 152 ColM d: ColM (copyOfData d);
Chris@208 153 esac);
Chris@214 154 */
Chris@6 155
Chris@163 156 newMatrix type data = //!!! NB does not copy data
Chris@96 157 (tagger = case type of RowMajor (): RowM; ColumnMajor (): ColM esac;
Chris@214 158 if empty? data or vec.empty? (head data)
Chris@201 159 then zeroSizeMatrix ()
Chris@208 160 else tagger (array data)
Chris@96 161 fi);
Chris@96 162
Chris@163 163 newRowVector data = //!!! NB does not copy data
Chris@208 164 RowM (array [data]);
Chris@96 165
Chris@163 166 newColumnVector data = //!!! NB does not copy data
Chris@208 167 ColM (array [data]);
Chris@8 168
Chris@195 169 scaled factor m = //!!! v inefficient
Chris@208 170 generate do row col: factor * (getAt row col m) done (size m);
Chris@98 171
Chris@98 172 sum' m1 m2 =
Chris@208 173 if (size m1) != (size m2)
Chris@208 174 then failWith "Matrices are not the same size: \(size m1), \(size m2)";
Chris@98 175 else
Chris@208 176 generate do row col: getAt row col m1 + getAt row col m2 done (size m1);
Chris@98 177 fi;
Chris@98 178
Chris@229 179 difference m1 m2 = //!!! doc: m1 - m2, not m2 - m1
Chris@229 180 if (size m1) != (size m2)
Chris@229 181 then failWith "Matrices are not the same size: \(size m1), \(size m2)";
Chris@229 182 else
Chris@229 183 generate do row col: getAt row col m1 - getAt row col m2 done (size m1);
Chris@229 184 fi;
Chris@229 185
Chris@229 186 abs' m =
Chris@229 187 generate do row col: abs (getAt row col m) done (size m);
Chris@229 188
Chris@98 189 product m1 m2 =
Chris@208 190 if (size m1).columns != (size m2).rows
Chris@208 191 then failWith "Matrix dimensions incompatible: \(size m1), \(size m2) (\((size m1).columns != (size m2).rows)";
Chris@98 192 else
Chris@98 193 generate do row col:
Chris@208 194 bf.sum (bf.multiply (getRow row m1) (getColumn col m2))
Chris@208 195 done { rows = (size m1).rows, columns = (size m2).columns }
Chris@98 196 fi;
Chris@98 197
Chris@161 198 asRows m =
Chris@208 199 map do i: getRow i m done [0 .. (height m) - 1];
Chris@161 200
Chris@161 201 asColumns m =
Chris@208 202 map do i: getColumn i m done [0 .. (width m) - 1];
Chris@161 203
Chris@178 204 concatAgainstGrain tagger getter counter mm =
Chris@208 205 (n = counter (size (head mm));
Chris@208 206 tagger (array
Chris@177 207 (map do i:
Chris@214 208 vec.concat (map (getter i) mm)
Chris@208 209 done [0..n-1])));
Chris@177 210
Chris@178 211 concatWithGrain tagger getter counter mm =
Chris@208 212 tagger (array
Chris@177 213 (concat
Chris@177 214 (map do m:
Chris@208 215 n = counter (size m);
Chris@208 216 map do i: getter i m done [0..n-1]
Chris@208 217 done mm)));
Chris@177 218
Chris@178 219 checkDimensionsFor direction first mm =
Chris@178 220 (counter = if direction == Horizontal () then (.rows) else (.columns) fi;
Chris@208 221 n = counter (size first);
Chris@208 222 if not (all id (map do m: counter (size m) == n done mm)) then
Chris@208 223 failWith "Matrix dimensions incompatible for concat (found \(map do m: counter (size m) done mm) not all of which are \(n))";
Chris@178 224 fi);
Chris@178 225
Chris@187 226 concat direction mm = //!!! doc: storage order is taken from first matrix in sequence
Chris@187 227 //!!! would this be better as separate concatHorizontal/concatVertical functions?
Chris@190 228 case mm of
Chris@190 229 first::rest:
Chris@178 230 checkDimensionsFor direction first mm;
Chris@208 231 row = isRowMajor? first;
Chris@178 232 // horizontal, row-major: against grain with rows
Chris@178 233 // horizontal, col-major: with grain with cols
Chris@178 234 // vertical, row-major: with grain with rows
Chris@178 235 // vertical, col-major: against grain with cols
Chris@178 236 case direction of
Chris@178 237 Horizontal ():
Chris@208 238 if row then concatAgainstGrain RowM getRow (.rows) mm;
Chris@208 239 else concatWithGrain ColM getColumn (.columns) mm;
Chris@178 240 fi;
Chris@178 241 Vertical ():
Chris@208 242 if row then concatWithGrain RowM getRow (.rows) mm;
Chris@208 243 else concatAgainstGrain ColM getColumn (.columns) mm;
Chris@178 244 fi;
Chris@178 245 esac;
Chris@190 246 [single]: single;
Chris@190 247 _: zeroSizeMatrix ();
Chris@190 248 esac;
Chris@177 249
Chris@187 250 rowSlice start count m = //!!! doc: storage order same as input
Chris@208 251 if isRowMajor? m then
Chris@208 252 RowM (array (map ((flip getRow) m) [start .. start + count - 1]))
Chris@187 253 else
Chris@214 254 ColM (array (map (vec.rangeOf start count) (asColumns m)))
Chris@187 255 fi;
Chris@187 256
Chris@187 257 columnSlice start count m = //!!! doc: storage order same as input
Chris@208 258 if not isRowMajor? m then
Chris@208 259 ColM (array (map ((flip getColumn) m) [start .. start + count - 1]))
Chris@187 260 else
Chris@214 261 RowM (array (map (vec.rangeOf start count) (asRows m)))
Chris@187 262 fi;
Chris@187 263
Chris@201 264 resizedTo newsize m =
Chris@208 265 (if newsize == (size m) then
Chris@201 266 m
Chris@208 267 elif (height m) == 0 or (width m) == 0 then
Chris@202 268 zeroMatrixWithTypeOf m newsize;
Chris@201 269 else
Chris@208 270 growrows = newsize.rows - (height m);
Chris@208 271 growcols = newsize.columns - (width m);
Chris@208 272 rowm = isRowMajor? m;
Chris@201 273 resizedTo newsize
Chris@201 274 if rowm and growrows < 0 then
Chris@201 275 rowSlice 0 newsize.rows m
Chris@201 276 elif (not rowm) and growcols < 0 then
Chris@201 277 columnSlice 0 newsize.columns m
Chris@201 278 elif growrows < 0 then
Chris@201 279 rowSlice 0 newsize.rows m
Chris@201 280 elif growcols < 0 then
Chris@201 281 columnSlice 0 newsize.columns m
Chris@201 282 else
Chris@201 283 if growrows > 0 then
Chris@201 284 concat (Vertical ())
Chris@208 285 [m, zeroMatrixWithTypeOf m ((size m) with { rows = growrows })]
Chris@201 286 else
Chris@201 287 concat (Horizontal ())
Chris@208 288 [m, zeroMatrixWithTypeOf m ((size m) with { columns = growcols })]
Chris@201 289 fi
Chris@201 290 fi
Chris@202 291 fi);
Chris@201 292
Chris@5 293 {
Chris@208 294 size,
Chris@208 295 width,
Chris@208 296 height,
Chris@208 297 getAt,
Chris@208 298 getColumn,
Chris@208 299 getRow,
Chris@214 300 // setAt,
Chris@208 301 isRowMajor?,
Chris@208 302 generate,
Chris@208 303 constMatrix,
Chris@208 304 randomMatrix,
Chris@208 305 zeroMatrix,
Chris@208 306 identityMatrix,
Chris@208 307 zeroSizeMatrix,
Chris@208 308 equal,
Chris@226 309 equalUnder,
Chris@214 310 // copyOf,
Chris@208 311 transposed,
Chris@208 312 flipped,
Chris@208 313 toRowMajor,
Chris@208 314 toColumnMajor,
Chris@208 315 scaled,
Chris@208 316 resizedTo,
Chris@208 317 asRows,
Chris@208 318 asColumns,
Chris@208 319 sum = sum',
Chris@229 320 difference,
Chris@229 321 abs = abs',
Chris@208 322 product,
Chris@208 323 concat,
Chris@208 324 rowSlice,
Chris@208 325 columnSlice,
Chris@208 326 newMatrix,
Chris@208 327 newRowVector,
Chris@208 328 newColumnVector,
Chris@208 329 }
Chris@208 330 as
Chris@208 331 {
Chris@208 332 //!!! check whether these are right to be .selector rather than just selector
Chris@208 333
Chris@208 334 size is matrix -> { .rows is number, .columns is number },
Chris@208 335 width is matrix -> number,
Chris@208 336 height is matrix -> number,
Chris@208 337 getAt is number -> number -> matrix -> number,
Chris@214 338 getColumn is number -> matrix -> vector,
Chris@214 339 getRow is number -> matrix -> vector,
Chris@214 340 // setAt is number -> number -> number -> matrix -> (), //!!! lose?
Chris@208 341 isRowMajor? is matrix -> boolean,
Chris@195 342 generate is (number -> number -> number) -> { .rows is number, .columns is number } -> matrix,
Chris@195 343 constMatrix is number -> { .rows is number, .columns is number } -> matrix,
Chris@195 344 randomMatrix is { .rows is number, .columns is number } -> matrix,
Chris@195 345 zeroMatrix is { .rows is number, .columns is number } -> matrix,
Chris@195 346 identityMatrix is { .rows is number, .columns is number } -> matrix,
Chris@195 347 zeroSizeMatrix is () -> matrix,
Chris@195 348 equal is matrix -> matrix -> boolean,
Chris@226 349 equalUnder is (number -> number -> boolean) -> matrix -> matrix -> boolean,
Chris@214 350 // copyOf is matrix -> matrix,
Chris@195 351 transposed is matrix -> matrix,
Chris@195 352 flipped is matrix -> matrix,
Chris@195 353 toRowMajor is matrix -> matrix,
Chris@195 354 toColumnMajor is matrix -> matrix,
Chris@195 355 scaled is number -> matrix -> matrix,
Chris@195 356 resizedTo is { .rows is number, .columns is number } -> matrix -> matrix,
Chris@214 357 asRows is matrix -> list<vector>,
Chris@214 358 asColumns is matrix -> list<vector>,
Chris@208 359 sum is matrix -> matrix -> matrix,
Chris@229 360 difference is matrix -> matrix -> matrix,
Chris@229 361 abs is matrix -> matrix,
Chris@195 362 product is matrix -> matrix -> matrix,
Chris@195 363 concat is (Horizontal () | Vertical ()) -> list<matrix> -> matrix,
Chris@195 364 rowSlice is number -> number -> matrix -> matrix,
Chris@195 365 columnSlice is number -> number -> matrix -> matrix,
Chris@214 366 newMatrix is (ColumnMajor () | RowMajor ()) -> list<vector> -> matrix,
Chris@214 367 newRowVector is vector -> matrix,
Chris@214 368 newColumnVector is vector -> matrix,
Chris@5 369 }
Chris@5 370