annotate yetilab/matrix/matrix.yeti @ 188:8a8b07134cad

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