annotate yetilab/matrix/matrix.yeti @ 214:709fba377099 matrix_opaque_immutable

Update matrix
author Chris Cannam
date Sat, 11 May 2013 11:39:09 +0100
parents 9997752223b8
children 77c6a81c577f
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@214 14 vec = load yetilab.block.vector;
Chris@99 15 bf = load yetilab.block.blockfuncs;
Chris@9 16
Chris@214 17 load yetilab.block.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@100 125 // Matrices with different storage order but the same contents are
Chris@100 126 // equal (but comparing them is slow)
Chris@97 127 equal m1 m2 =
Chris@208 128 if size m1 != size m2 then false
Chris@208 129 elif isRowMajor? m1 != isRowMajor? m2 then equal (flipped m1) m2;
Chris@100 130 else
Chris@214 131 compare d1 d2 = all id (map2 vec.equal 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@214 138 /*!!! not needed now it's immutable?
Chris@95 139 copyOf m =
Chris@214 140 (copyOfData d = (array (map vec.copyOf d));
Chris@208 141 case m of
Chris@208 142 RowM d: RowM (copyOfData d);
Chris@208 143 ColM d: ColM (copyOfData d);
Chris@208 144 esac);
Chris@214 145 */
Chris@6 146
Chris@163 147 newMatrix type data = //!!! NB does not copy data
Chris@96 148 (tagger = case type of RowMajor (): RowM; ColumnMajor (): ColM esac;
Chris@214 149 if empty? data or vec.empty? (head data)
Chris@201 150 then zeroSizeMatrix ()
Chris@208 151 else tagger (array data)
Chris@96 152 fi);
Chris@96 153
Chris@163 154 newRowVector data = //!!! NB does not copy data
Chris@208 155 RowM (array [data]);
Chris@96 156
Chris@163 157 newColumnVector data = //!!! NB does not copy data
Chris@208 158 ColM (array [data]);
Chris@8 159
Chris@195 160 scaled factor m = //!!! v inefficient
Chris@208 161 generate do row col: factor * (getAt row col m) done (size m);
Chris@98 162
Chris@98 163 sum' m1 m2 =
Chris@208 164 if (size m1) != (size m2)
Chris@208 165 then failWith "Matrices are not the same size: \(size m1), \(size m2)";
Chris@98 166 else
Chris@208 167 generate do row col: getAt row col m1 + getAt row col m2 done (size m1);
Chris@98 168 fi;
Chris@98 169
Chris@98 170 product m1 m2 =
Chris@208 171 if (size m1).columns != (size m2).rows
Chris@208 172 then failWith "Matrix dimensions incompatible: \(size m1), \(size m2) (\((size m1).columns != (size m2).rows)";
Chris@98 173 else
Chris@98 174 generate do row col:
Chris@208 175 bf.sum (bf.multiply (getRow row m1) (getColumn col m2))
Chris@208 176 done { rows = (size m1).rows, columns = (size m2).columns }
Chris@98 177 fi;
Chris@98 178
Chris@161 179 asRows m =
Chris@208 180 map do i: getRow i m done [0 .. (height m) - 1];
Chris@161 181
Chris@161 182 asColumns m =
Chris@208 183 map do i: getColumn i m done [0 .. (width m) - 1];
Chris@161 184
Chris@178 185 concatAgainstGrain tagger getter counter mm =
Chris@208 186 (n = counter (size (head mm));
Chris@208 187 tagger (array
Chris@177 188 (map do i:
Chris@214 189 vec.concat (map (getter i) mm)
Chris@208 190 done [0..n-1])));
Chris@177 191
Chris@178 192 concatWithGrain tagger getter counter mm =
Chris@208 193 tagger (array
Chris@177 194 (concat
Chris@177 195 (map do m:
Chris@208 196 n = counter (size m);
Chris@208 197 map do i: getter i m done [0..n-1]
Chris@208 198 done mm)));
Chris@177 199
Chris@178 200 checkDimensionsFor direction first mm =
Chris@178 201 (counter = if direction == Horizontal () then (.rows) else (.columns) fi;
Chris@208 202 n = counter (size first);
Chris@208 203 if not (all id (map do m: counter (size m) == n done mm)) then
Chris@208 204 failWith "Matrix dimensions incompatible for concat (found \(map do m: counter (size m) done mm) not all of which are \(n))";
Chris@178 205 fi);
Chris@178 206
Chris@187 207 concat direction mm = //!!! doc: storage order is taken from first matrix in sequence
Chris@187 208 //!!! would this be better as separate concatHorizontal/concatVertical functions?
Chris@190 209 case mm of
Chris@190 210 first::rest:
Chris@178 211 checkDimensionsFor direction first mm;
Chris@208 212 row = isRowMajor? first;
Chris@178 213 // horizontal, row-major: against grain with rows
Chris@178 214 // horizontal, col-major: with grain with cols
Chris@178 215 // vertical, row-major: with grain with rows
Chris@178 216 // vertical, col-major: against grain with cols
Chris@178 217 case direction of
Chris@178 218 Horizontal ():
Chris@208 219 if row then concatAgainstGrain RowM getRow (.rows) mm;
Chris@208 220 else concatWithGrain ColM getColumn (.columns) mm;
Chris@178 221 fi;
Chris@178 222 Vertical ():
Chris@208 223 if row then concatWithGrain RowM getRow (.rows) mm;
Chris@208 224 else concatAgainstGrain ColM getColumn (.columns) mm;
Chris@178 225 fi;
Chris@178 226 esac;
Chris@190 227 [single]: single;
Chris@190 228 _: zeroSizeMatrix ();
Chris@190 229 esac;
Chris@177 230
Chris@187 231 rowSlice start count m = //!!! doc: storage order same as input
Chris@208 232 if isRowMajor? m then
Chris@208 233 RowM (array (map ((flip getRow) m) [start .. start + count - 1]))
Chris@187 234 else
Chris@214 235 ColM (array (map (vec.rangeOf start count) (asColumns m)))
Chris@187 236 fi;
Chris@187 237
Chris@187 238 columnSlice start count m = //!!! doc: storage order same as input
Chris@208 239 if not isRowMajor? m then
Chris@208 240 ColM (array (map ((flip getColumn) m) [start .. start + count - 1]))
Chris@187 241 else
Chris@214 242 RowM (array (map (vec.rangeOf start count) (asRows m)))
Chris@187 243 fi;
Chris@187 244
Chris@201 245 resizedTo newsize m =
Chris@208 246 (if newsize == (size m) then
Chris@201 247 m
Chris@208 248 elif (height m) == 0 or (width m) == 0 then
Chris@202 249 zeroMatrixWithTypeOf m newsize;
Chris@201 250 else
Chris@208 251 growrows = newsize.rows - (height m);
Chris@208 252 growcols = newsize.columns - (width m);
Chris@208 253 rowm = isRowMajor? m;
Chris@201 254 resizedTo newsize
Chris@201 255 if rowm and growrows < 0 then
Chris@201 256 rowSlice 0 newsize.rows m
Chris@201 257 elif (not rowm) and growcols < 0 then
Chris@201 258 columnSlice 0 newsize.columns m
Chris@201 259 elif growrows < 0 then
Chris@201 260 rowSlice 0 newsize.rows m
Chris@201 261 elif growcols < 0 then
Chris@201 262 columnSlice 0 newsize.columns m
Chris@201 263 else
Chris@201 264 if growrows > 0 then
Chris@201 265 concat (Vertical ())
Chris@208 266 [m, zeroMatrixWithTypeOf m ((size m) with { rows = growrows })]
Chris@201 267 else
Chris@201 268 concat (Horizontal ())
Chris@208 269 [m, zeroMatrixWithTypeOf m ((size m) with { columns = growcols })]
Chris@201 270 fi
Chris@201 271 fi
Chris@202 272 fi);
Chris@201 273
Chris@5 274 {
Chris@208 275 size,
Chris@208 276 width,
Chris@208 277 height,
Chris@208 278 getAt,
Chris@208 279 getColumn,
Chris@208 280 getRow,
Chris@214 281 // setAt,
Chris@208 282 isRowMajor?,
Chris@208 283 generate,
Chris@208 284 constMatrix,
Chris@208 285 randomMatrix,
Chris@208 286 zeroMatrix,
Chris@208 287 identityMatrix,
Chris@208 288 zeroSizeMatrix,
Chris@208 289 equal,
Chris@214 290 // copyOf,
Chris@208 291 transposed,
Chris@208 292 flipped,
Chris@208 293 toRowMajor,
Chris@208 294 toColumnMajor,
Chris@208 295 scaled,
Chris@208 296 resizedTo,
Chris@208 297 asRows,
Chris@208 298 asColumns,
Chris@208 299 sum = sum',
Chris@208 300 product,
Chris@208 301 concat,
Chris@208 302 rowSlice,
Chris@208 303 columnSlice,
Chris@208 304 newMatrix,
Chris@208 305 newRowVector,
Chris@208 306 newColumnVector,
Chris@208 307 }
Chris@208 308 as
Chris@208 309 {
Chris@208 310 //!!! check whether these are right to be .selector rather than just selector
Chris@208 311
Chris@208 312 size is matrix -> { .rows is number, .columns is number },
Chris@208 313 width is matrix -> number,
Chris@208 314 height is matrix -> number,
Chris@208 315 getAt is number -> number -> matrix -> number,
Chris@214 316 getColumn is number -> matrix -> vector,
Chris@214 317 getRow is number -> matrix -> vector,
Chris@214 318 // setAt is number -> number -> number -> matrix -> (), //!!! lose?
Chris@208 319 isRowMajor? is matrix -> boolean,
Chris@195 320 generate is (number -> number -> number) -> { .rows is number, .columns is number } -> matrix,
Chris@195 321 constMatrix is number -> { .rows is number, .columns is number } -> matrix,
Chris@195 322 randomMatrix is { .rows is number, .columns is number } -> matrix,
Chris@195 323 zeroMatrix is { .rows is number, .columns is number } -> matrix,
Chris@195 324 identityMatrix is { .rows is number, .columns is number } -> matrix,
Chris@195 325 zeroSizeMatrix is () -> matrix,
Chris@195 326 equal is matrix -> matrix -> boolean,
Chris@214 327 // copyOf is matrix -> matrix,
Chris@195 328 transposed is matrix -> matrix,
Chris@195 329 flipped is matrix -> matrix,
Chris@195 330 toRowMajor is matrix -> matrix,
Chris@195 331 toColumnMajor is matrix -> matrix,
Chris@195 332 scaled is number -> matrix -> matrix,
Chris@195 333 resizedTo is { .rows is number, .columns is number } -> matrix -> matrix,
Chris@214 334 asRows is matrix -> list<vector>,
Chris@214 335 asColumns is matrix -> list<vector>,
Chris@208 336 sum is matrix -> matrix -> matrix,
Chris@195 337 product is matrix -> matrix -> matrix,
Chris@195 338 concat is (Horizontal () | Vertical ()) -> list<matrix> -> matrix,
Chris@195 339 rowSlice is number -> number -> matrix -> matrix,
Chris@195 340 columnSlice is number -> number -> matrix -> matrix,
Chris@214 341 newMatrix is (ColumnMajor () | RowMajor ()) -> list<vector> -> matrix,
Chris@214 342 newRowVector is vector -> matrix,
Chris@214 343 newColumnVector is vector -> matrix,
Chris@5 344 }
Chris@5 345