annotate yetilab/matrix/matrix.yeti @ 101:2bc6534248fe

Tidier comments
author Chris Cannam
date Thu, 21 Mar 2013 21:53:13 +0000
parents 4e52d04887a5
children b6db07468ed1
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@96 48 setAt row col n =
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@5 80
Chris@96 81 width m = m.size.columns;
Chris@96 82 height m = m.size.rows;
Chris@6 83
Chris@100 84 transposed m =
Chris@100 85 make
Chris@100 86 (case m.data of
Chris@100 87 RowM d: ColM d;
Chris@100 88 ColM d: RowM d;
Chris@100 89 esac);
Chris@100 90
Chris@100 91 flipped m =
Chris@100 92 if m.isRowMajor? then
Chris@100 93 generate do row col: m.getAt row col done m.size;
Chris@100 94 else
Chris@100 95 transposed
Chris@100 96 (generate do row col: m.getAt col row done
Chris@100 97 { rows = m.size.columns, columns = m.size.rows });
Chris@100 98 fi;
Chris@100 99
Chris@100 100 // Matrices with different storage order but the same contents are
Chris@100 101 // equal (but comparing them is slow)
Chris@97 102 equal m1 m2 =
Chris@100 103 if m1.isRowMajor? != m2.isRowMajor?
Chris@100 104 then equal (flipped m1) m2;
Chris@100 105 else
Chris@100 106 compare d1 d2 = all id (map2 vec.equal d1 d2);
Chris@100 107 case m1.data of
Chris@100 108 RowM d1: case m2.data of RowM d2: compare d1 d2; _: false; esac;
Chris@100 109 ColM d1: case m2.data of ColM d2: compare d1 d2; _: false; esac;
Chris@100 110 esac
Chris@100 111 fi;
Chris@97 112
Chris@95 113 copyOf m =
Chris@95 114 (copyOfData d = (array (map vec.copyOf d));
Chris@96 115 make
Chris@95 116 (case m.data of
Chris@96 117 RowM d: RowM (copyOfData d);
Chris@96 118 ColM d: ColM (copyOfData d);
Chris@95 119 esac));
Chris@6 120
Chris@96 121 newMatrix type data is RowMajor () | ColumnMajor () -> list?<list?<number>> -> 'a =
Chris@96 122 (tagger = case type of RowMajor (): RowM; ColumnMajor (): ColM esac;
Chris@97 123 if empty? data or empty? (head data)
Chris@98 124 then zeroMatrix { rows = 0, columns = 0 }
Chris@96 125 else make (tagger (array (map vec.vector data)))
Chris@96 126 fi);
Chris@96 127
Chris@96 128 newRowVector data =
Chris@96 129 newMatrix (RowMajor ()) [data];
Chris@96 130
Chris@96 131 newColumnVector data =
Chris@96 132 newMatrix (ColumnMajor ()) [data];
Chris@8 133
Chris@98 134 scaled factor m =
Chris@98 135 generate do row col: factor * m.getAt row col done m.size;
Chris@98 136
Chris@98 137 sum' m1 m2 =
Chris@98 138 if m1.size != m2.size
Chris@98 139 then failWith "Matrices are not the same size: \(m1.size), \(m2.size)";
Chris@98 140 else
Chris@98 141 generate do row col: m1.getAt row col + m2.getAt row col done m1.size;
Chris@98 142 fi;
Chris@98 143
Chris@98 144 product m1 m2 =
Chris@98 145 if m1.size.columns != m2.size.rows
Chris@98 146 then failWith "Matrix dimensions incompatible: \(m1.size), \(m2.size) (\(m1.size.columns != m2.size.rows)";
Chris@98 147 else
Chris@98 148 generate do row col:
Chris@99 149 bf.sum (bf.multiply (m1.getRow row) (m2.getColumn col))
Chris@98 150 done { rows = m1.size.rows, columns = m2.size.columns }
Chris@98 151 fi;
Chris@98 152
Chris@5 153 {
Chris@97 154 constMatrix, randomMatrix, zeroMatrix, identityMatrix,
Chris@96 155 generate,
Chris@96 156 width, height,
Chris@97 157 equal,
Chris@20 158 copyOf,
Chris@15 159 transposed,
Chris@96 160 flipped,
Chris@98 161 scaled,
Chris@98 162 sum = sum', product,
Chris@98 163 newMatrix, newRowVector, newColumnVector,
Chris@5 164 }
Chris@5 165