annotate yetilab/matrix/matrix.yeti @ 163:3fbaa25aad89

Make newMatrix/newRowVector/newColumnVector take block rather than list<number> args. Makes some uses simpler, some more complex, but most faster (sometimes by a lot).
author Chris Cannam
date Wed, 01 May 2013 21:42:03 +0100
parents 38938ca5db0c
children df2383f6b99b
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@161 171 map (block.list . m.getRow) [0 .. m.size.rows - 1];
Chris@161 172
Chris@161 173 asColumns m =
Chris@161 174 map (block.list . m.getColumn) [0 .. m.size.columns - 1];
Chris@161 175
Chris@5 176 {
Chris@158 177 constMatrix, randomMatrix, zeroMatrix, identityMatrix, zeroSizeMatrix,
Chris@96 178 generate,
Chris@96 179 width, height,
Chris@97 180 equal,
Chris@20 181 copyOf,
Chris@15 182 transposed,
Chris@161 183 flipped, toRowMajor, toColumnMajor,
Chris@98 184 scaled,
Chris@158 185 resizedTo,
Chris@161 186 asRows, asColumns,
Chris@98 187 sum = sum', product,
Chris@98 188 newMatrix, newRowVector, newColumnVector,
Chris@5 189 }
Chris@5 190