annotate yetilab/matrix/matrix.yeti @ 198:0b187d845491

Simplify
author Chris Cannam
date Mon, 06 May 2013 16:53:49 +0100
parents 3f4f3af724b0
children 71a09107ee3e
rev   line source
Chris@5 1
Chris@94 2 module yetilab.matrix.matrix;
Chris@94 3
Chris@94 4 // A matrix is an array of fvectors (i.e. primitive double[]s).
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@93 12 vec = load yetilab.block.fvector;
Chris@96 13 block = load yetilab.block.block;
Chris@99 14 bf = load yetilab.block.blockfuncs;
Chris@9 15
Chris@195 16 load yetilab.block.blocktype;
Chris@195 17 load yetilab.matrix.matrixtype;
Chris@195 18
Chris@96 19 make d = {
Chris@96 20 get data () = d,
Chris@96 21 get size () =
Chris@96 22 case d of
Chris@96 23 RowM r:
Chris@96 24 major = length r;
Chris@96 25 {
Chris@96 26 rows = major,
Chris@96 27 columns = if major > 0 then vec.length r[0] else 0 fi,
Chris@96 28 };
Chris@96 29 ColM c:
Chris@96 30 major = length c;
Chris@96 31 {
Chris@96 32 rows = if major > 0 then vec.length c[0] else 0 fi,
Chris@96 33 columns = major,
Chris@96 34 };
Chris@94 35 esac,
Chris@96 36 getColumn j =
Chris@96 37 case d of
Chris@96 38 RowM rows: block.fromList (map do i: getAt i j done [0..length rows-1]);
Chris@96 39 ColM cols: block.block cols[j];
Chris@94 40 esac,
Chris@96 41 getRow i =
Chris@96 42 case d of
Chris@96 43 RowM rows: block.block rows[i];
Chris@96 44 ColM cols: block.fromList (map do j: getAt i j done [0..length cols-1]);
Chris@94 45 esac,
Chris@96 46 getAt row col =
Chris@96 47 case d of
Chris@96 48 RowM rows: r = rows[row]; (r is ~double[])[col];
Chris@96 49 ColM cols: c = cols[col]; (c is ~double[])[row];
Chris@94 50 esac,
Chris@158 51 setAt row col n = //!!! dangerous, could modify copies -- should it be allowed?
Chris@96 52 case d of
Chris@96 53 RowM rows: r = rows[row]; (r is ~double[])[col] := n;
Chris@96 54 ColM cols: c = cols[col]; (c is ~double[])[row] := n;
Chris@95 55 esac,
Chris@95 56 get isRowMajor? () =
Chris@96 57 case d of
Chris@96 58 RowM _: true;
Chris@96 59 ColM _: false;
Chris@95 60 esac,
Chris@94 61 };
Chris@94 62
Chris@98 63 newStorage { rows, columns } =
Chris@97 64 if rows < 1 then array []
Chris@98 65 else array (map \(vec.zeros rows) [1..columns])
Chris@97 66 fi;
Chris@94 67
Chris@98 68 zeroMatrix { rows, columns } =
Chris@98 69 make (ColM (newStorage { rows, columns }));
Chris@5 70
Chris@98 71 generate f { rows, columns } =
Chris@98 72 (m = newStorage { rows, columns };
Chris@98 73 for [0..columns-1] do col:
Chris@94 74 for [0..rows-1] do row:
Chris@94 75 m[col][row] := f row col;
Chris@5 76 done;
Chris@5 77 done;
Chris@96 78 make (ColM m));
Chris@5 79
Chris@20 80 constMatrix n = generate do row col: n done;
Chris@20 81 randomMatrix = generate do row col: Math#random() done;
Chris@5 82 identityMatrix = constMatrix 1;
Chris@158 83 zeroSizeMatrix () = zeroMatrix { rows = 0, columns = 0 };
Chris@5 84
Chris@96 85 width m = m.size.columns;
Chris@96 86 height m = m.size.rows;
Chris@6 87
Chris@100 88 transposed m =
Chris@100 89 make
Chris@100 90 (case m.data of
Chris@100 91 RowM d: ColM d;
Chris@100 92 ColM d: RowM d;
Chris@100 93 esac);
Chris@100 94
Chris@100 95 flipped m =
Chris@100 96 if m.isRowMajor? then
Chris@100 97 generate do row col: m.getAt row col done m.size;
Chris@100 98 else
Chris@100 99 transposed
Chris@100 100 (generate do row col: m.getAt col row done
Chris@100 101 { rows = m.size.columns, columns = m.size.rows });
Chris@100 102 fi;
Chris@100 103
Chris@161 104 toRowMajor m =
Chris@161 105 if m.isRowMajor? then m else flipped m fi;
Chris@161 106
Chris@161 107 toColumnMajor m =
Chris@161 108 if not m.isRowMajor? then m else flipped m fi;
Chris@161 109
Chris@100 110 // Matrices with different storage order but the same contents are
Chris@100 111 // equal (but comparing them is slow)
Chris@97 112 equal m1 m2 =
Chris@159 113 if m1.size != m2.size then false
Chris@159 114 elif m1.isRowMajor? != m2.isRowMajor? then equal (flipped m1) m2;
Chris@100 115 else
Chris@100 116 compare d1 d2 = all id (map2 vec.equal d1 d2);
Chris@100 117 case m1.data of
Chris@100 118 RowM d1: case m2.data of RowM d2: compare d1 d2; _: false; esac;
Chris@100 119 ColM d1: case m2.data of ColM d2: compare d1 d2; _: false; esac;
Chris@100 120 esac
Chris@100 121 fi;
Chris@97 122
Chris@95 123 copyOf m =
Chris@95 124 (copyOfData d = (array (map vec.copyOf d));
Chris@96 125 make
Chris@95 126 (case m.data of
Chris@96 127 RowM d: RowM (copyOfData d);
Chris@96 128 ColM d: ColM (copyOfData d);
Chris@95 129 esac));
Chris@6 130
Chris@163 131 newMatrix type data = //!!! NB does not copy data
Chris@96 132 (tagger = case type of RowMajor (): RowM; ColumnMajor (): ColM esac;
Chris@163 133 if empty? data or block.empty? (head data)
Chris@98 134 then zeroMatrix { rows = 0, columns = 0 }
Chris@163 135 else make (tagger (array (map block.data data)))
Chris@96 136 fi);
Chris@96 137
Chris@163 138 newRowVector data = //!!! NB does not copy data
Chris@163 139 make (RowM (array [block.data data]));
Chris@96 140
Chris@163 141 newColumnVector data = //!!! NB does not copy data
Chris@163 142 make (ColM (array [block.data data]));
Chris@8 143
Chris@195 144 scaled factor m = //!!! v inefficient
Chris@98 145 generate do row col: factor * m.getAt row col done m.size;
Chris@98 146
Chris@195 147 resizedTo newsize m = //!!! also v inefficient
Chris@158 148 (oldsize = m.size;
Chris@158 149 if newsize == oldsize then m
Chris@158 150 else
Chris@158 151 generate do row col:
Chris@158 152 if row < oldsize.rows and col < oldsize.columns
Chris@158 153 then m.getAt row col else 0 fi
Chris@158 154 done newsize;
Chris@158 155 fi);
Chris@158 156
Chris@98 157 sum' m1 m2 =
Chris@98 158 if m1.size != m2.size
Chris@98 159 then failWith "Matrices are not the same size: \(m1.size), \(m2.size)";
Chris@98 160 else
Chris@98 161 generate do row col: m1.getAt row col + m2.getAt row col done m1.size;
Chris@98 162 fi;
Chris@98 163
Chris@98 164 product m1 m2 =
Chris@98 165 if m1.size.columns != m2.size.rows
Chris@98 166 then failWith "Matrix dimensions incompatible: \(m1.size), \(m2.size) (\(m1.size.columns != m2.size.rows)";
Chris@98 167 else
Chris@98 168 generate do row col:
Chris@99 169 bf.sum (bf.multiply (m1.getRow row) (m2.getColumn col))
Chris@98 170 done { rows = m1.size.rows, columns = m2.size.columns }
Chris@98 171 fi;
Chris@98 172
Chris@161 173 asRows m =
Chris@171 174 map m.getRow [0 .. m.size.rows - 1];
Chris@161 175
Chris@161 176 asColumns m =
Chris@171 177 map m.getColumn [0 .. m.size.columns - 1];
Chris@161 178
Chris@178 179 concatAgainstGrain tagger getter counter mm =
Chris@178 180 (n = counter (head mm).size;
Chris@177 181 make (tagger (array
Chris@177 182 (map do i:
Chris@177 183 block.data (block.concat (map do m: getter m i done mm))
Chris@178 184 done [0..n-1]))));
Chris@177 185
Chris@178 186 concatWithGrain tagger getter counter mm =
Chris@177 187 make (tagger (array
Chris@177 188 (concat
Chris@177 189 (map do m:
Chris@178 190 n = counter m.size;
Chris@178 191 map do i: block.data (getter m i) done [0..n-1]
Chris@177 192 done mm))));
Chris@177 193
Chris@178 194 checkDimensionsFor direction first mm =
Chris@178 195 (counter = if direction == Horizontal () then (.rows) else (.columns) fi;
Chris@178 196 n = counter first.size;
Chris@178 197 if not (all id (map do m: counter m.size == n done mm)) then
Chris@178 198 failWith "Matrix dimensions incompatible for concat (found \(map do m: counter m.size done mm) not all of which are \(n))";
Chris@178 199 fi);
Chris@178 200
Chris@187 201 concat direction mm = //!!! doc: storage order is taken from first matrix in sequence
Chris@187 202 //!!! would this be better as separate concatHorizontal/concatVertical functions?
Chris@190 203 case mm of
Chris@190 204 first::rest:
Chris@178 205 checkDimensionsFor direction first mm;
Chris@178 206 row = first.isRowMajor?;
Chris@178 207 // horizontal, row-major: against grain with rows
Chris@178 208 // horizontal, col-major: with grain with cols
Chris@178 209 // vertical, row-major: with grain with rows
Chris@178 210 // vertical, col-major: against grain with cols
Chris@178 211 case direction of
Chris@178 212 Horizontal ():
Chris@178 213 if row then concatAgainstGrain RowM (.getRow) (.rows) mm;
Chris@178 214 else concatWithGrain ColM (.getColumn) (.columns) mm;
Chris@178 215 fi;
Chris@178 216 Vertical ():
Chris@178 217 if row then concatWithGrain RowM (.getRow) (.rows) mm;
Chris@178 218 else concatAgainstGrain ColM (.getColumn) (.columns) mm;
Chris@178 219 fi;
Chris@178 220 esac;
Chris@190 221 [single]: single;
Chris@190 222 _: zeroSizeMatrix ();
Chris@190 223 esac;
Chris@177 224
Chris@187 225 rowSlice start count m = //!!! doc: storage order same as input
Chris@187 226 if m.isRowMajor? then
Chris@187 227 make (RowM (array (map (block.data . m.getRow) [start .. start + count - 1])))
Chris@187 228 else
Chris@187 229 make (ColM (array (map (block.data . (block.rangeOf start count)) (asColumns m))))
Chris@187 230 fi;
Chris@187 231
Chris@187 232 columnSlice start count m = //!!! doc: storage order same as input
Chris@187 233 if not m.isRowMajor? then
Chris@187 234 make (ColM (array (map (block.data . m.getColumn) [start .. start + count - 1])))
Chris@187 235 else
Chris@187 236 make (RowM (array (map (block.data . (block.rangeOf start count)) (asRows m))))
Chris@187 237 fi;
Chris@187 238
Chris@5 239 {
Chris@195 240 generate is (number -> number -> number) -> { .rows is number, .columns is number } -> matrix,
Chris@195 241 constMatrix is number -> { .rows is number, .columns is number } -> matrix,
Chris@195 242 randomMatrix is { .rows is number, .columns is number } -> matrix,
Chris@195 243 zeroMatrix is { .rows is number, .columns is number } -> matrix,
Chris@195 244 identityMatrix is { .rows is number, .columns is number } -> matrix,
Chris@195 245 zeroSizeMatrix is () -> matrix,
Chris@195 246 width is matrix -> number,
Chris@195 247 height is matrix -> number,
Chris@195 248 equal is matrix -> matrix -> boolean,
Chris@195 249 copyOf is matrix -> matrix,
Chris@195 250 transposed is matrix -> matrix,
Chris@195 251 flipped is matrix -> matrix,
Chris@195 252 toRowMajor is matrix -> matrix,
Chris@195 253 toColumnMajor is matrix -> matrix,
Chris@195 254 scaled is number -> matrix -> matrix,
Chris@195 255 resizedTo is { .rows is number, .columns is number } -> matrix -> matrix,
Chris@195 256 asRows is matrix -> list<block>,
Chris@195 257 asColumns is matrix -> list<block>,
Chris@198 258 sum is matrix -> matrix -> matrix = sum',
Chris@195 259 product is matrix -> matrix -> matrix,
Chris@195 260 concat is (Horizontal () | Vertical ()) -> list<matrix> -> matrix,
Chris@195 261 rowSlice is number -> number -> matrix -> matrix,
Chris@195 262 columnSlice is number -> number -> matrix -> matrix,
Chris@195 263 newMatrix is (ColumnMajor () | RowMajor ()) -> list<block> -> matrix,
Chris@195 264 newRowVector is block -> matrix,
Chris@195 265 newColumnVector is block -> matrix,
Chris@5 266 }
Chris@5 267