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@222
|
14 vec = load yetilab.vector.vector;
|
Chris@222
|
15 bf = load yetilab.vector.blockfuncs;
|
Chris@9
|
16
|
Chris@222
|
17 load yetilab.vector.vectortype;
|
Chris@195
|
18 load yetilab.matrix.matrixtype;
|
Chris@195
|
19
|
Chris@208
|
20 size m =
|
Chris@208
|
21 case m of
|
Chris@234
|
22 DenseRows 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@234
|
28 DenseCols 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@254
|
34 SparseRows { size, data }: size;
|
Chris@254
|
35 SparseCols { size, data }: size;
|
Chris@208
|
36 esac;
|
Chris@208
|
37
|
Chris@208
|
38 width m = (size m).columns;
|
Chris@208
|
39 height m = (size m).rows;
|
Chris@208
|
40
|
Chris@249
|
41 nonZeroValues m =
|
Chris@254
|
42 (nzDense d =
|
Chris@242
|
43 sum
|
Chris@242
|
44 (map do v:
|
Chris@242
|
45 sum (map do n: if n == 0 then 0 else 1 fi done (vec.list v))
|
Chris@242
|
46 done d);
|
Chris@254
|
47 nzSparse d =
|
Chris@254
|
48 sum (map do k:
|
Chris@254
|
49 length (keys d.data[k])
|
Chris@254
|
50 done (keys d.data));
|
Chris@242
|
51 case m of
|
Chris@254
|
52 DenseRows d: nzDense d;
|
Chris@254
|
53 DenseCols d: nzDense d;
|
Chris@254
|
54 SparseRows d: nzSparse d;
|
Chris@254
|
55 SparseCols d: nzSparse d;
|
Chris@242
|
56 esac);
|
Chris@242
|
57
|
Chris@249
|
58 density m =
|
Chris@249
|
59 ({ rows, columns } = size m;
|
Chris@249
|
60 cells = rows * columns;
|
Chris@249
|
61 (nonZeroValues m) / cells);
|
Chris@249
|
62
|
Chris@254
|
63 fromSlice n m { data } =
|
Chris@254
|
64 if n in data and m in data[n] then data[n][m] else 0 fi;
|
Chris@236
|
65
|
Chris@254
|
66 filledSlice sz n { data } =
|
Chris@254
|
67 (slice = new double[sz];
|
Chris@254
|
68 if n in data then
|
Chris@254
|
69 h = data[n];
|
Chris@254
|
70 for (keys h) do k: slice[k] := h[k] done;
|
Chris@254
|
71 fi;
|
Chris@254
|
72 vec.vector slice);
|
Chris@236
|
73
|
Chris@208
|
74 getAt row col m =
|
Chris@208
|
75 case m of
|
Chris@234
|
76 DenseRows rows: r = rows[row]; vec.at col r;
|
Chris@234
|
77 DenseCols cols: c = cols[col]; vec.at row c;
|
Chris@254
|
78 SparseRows data: fromSlice row col data;
|
Chris@254
|
79 SparseCols data: fromSlice col row data;
|
Chris@208
|
80 esac;
|
Chris@208
|
81
|
Chris@208
|
82 getColumn j m =
|
Chris@208
|
83 case m of
|
Chris@234
|
84 DenseCols cols: cols[j];
|
Chris@254
|
85 SparseCols data: filledSlice data.size.rows j data;
|
Chris@236
|
86 _: vec.fromList (map do i: getAt i j m done [0..height m - 1]);
|
Chris@208
|
87 esac;
|
Chris@208
|
88
|
Chris@208
|
89 getRow i m =
|
Chris@208
|
90 case m of
|
Chris@234
|
91 DenseRows rows: rows[i];
|
Chris@254
|
92 SparseRows data: filledSlice data.size.columns i data;
|
Chris@236
|
93 _: vec.fromList (map do j: getAt i j m done [0..width m - 1]);
|
Chris@208
|
94 esac;
|
Chris@208
|
95
|
Chris@208
|
96 isRowMajor? m =
|
Chris@208
|
97 case m of
|
Chris@234
|
98 DenseRows _: true;
|
Chris@234
|
99 DenseCols _: false;
|
Chris@254
|
100 SparseRows _: true;
|
Chris@254
|
101 SparseCols _: false;
|
Chris@234
|
102 esac;
|
Chris@234
|
103
|
Chris@234
|
104 isSparse? m =
|
Chris@234
|
105 case m of
|
Chris@234
|
106 DenseRows _: false;
|
Chris@234
|
107 DenseCols _: false;
|
Chris@254
|
108 SparseRows _: true;
|
Chris@254
|
109 SparseCols _: true;
|
Chris@208
|
110 esac;
|
Chris@94
|
111
|
Chris@244
|
112 newColumnMajorStorage { rows, columns } =
|
Chris@97
|
113 if rows < 1 then array []
|
Chris@214
|
114 else array (map \(vec.zeros rows) [1..columns])
|
Chris@97
|
115 fi;
|
Chris@94
|
116
|
Chris@98
|
117 zeroMatrix { rows, columns } =
|
Chris@244
|
118 DenseCols (newColumnMajorStorage { rows, columns });
|
Chris@201
|
119
|
Chris@201
|
120 zeroMatrixWithTypeOf m { rows, columns } =
|
Chris@208
|
121 if isRowMajor? m then
|
Chris@244
|
122 DenseRows (newColumnMajorStorage { rows = columns, columns = rows });
|
Chris@201
|
123 else
|
Chris@244
|
124 DenseCols (newColumnMajorStorage { rows, columns });
|
Chris@201
|
125 fi;
|
Chris@5
|
126
|
Chris@214
|
127 zeroSizeMatrix () = zeroMatrix { rows = 0, columns = 0 };
|
Chris@214
|
128
|
Chris@98
|
129 generate f { rows, columns } =
|
Chris@214
|
130 if rows < 1 or columns < 1 then zeroSizeMatrix ()
|
Chris@214
|
131 else
|
Chris@214
|
132 m = array (map \(new double[rows]) [1..columns]);
|
Chris@214
|
133 for [0..columns-1] do col:
|
Chris@214
|
134 for [0..rows-1] do row:
|
Chris@214
|
135 m[col][row] := f row col;
|
Chris@214
|
136 done;
|
Chris@5
|
137 done;
|
Chris@234
|
138 DenseCols (array (map vec.vector m))
|
Chris@214
|
139 fi;
|
Chris@5
|
140
|
Chris@235
|
141 enumerateSparse m =
|
Chris@254
|
142 (enumerate { size, data } =
|
Chris@240
|
143 concat
|
Chris@240
|
144 (map do i:
|
Chris@254
|
145 map do j:
|
Chris@254
|
146 { i, j, v = data[i][j] }
|
Chris@254
|
147 done (keys data[i])
|
Chris@254
|
148 done (keys data));
|
Chris@235
|
149 case m of
|
Chris@254
|
150 SparseCols d:
|
Chris@240
|
151 map do { i, j, v }: { i = j, j = i, v } done (enumerate d);
|
Chris@254
|
152 SparseRows d:
|
Chris@240
|
153 enumerate d;
|
Chris@237
|
154 _: [];
|
Chris@235
|
155 esac);
|
Chris@235
|
156
|
Chris@254
|
157 makeSparse type size entries =
|
Chris@254
|
158 (isRow = case type of RowMajor (): true; ColumnMajor (): false; esac;
|
Chris@254
|
159 data = [:];
|
Chris@254
|
160 preprocess =
|
Chris@254
|
161 if isRow then id
|
Chris@254
|
162 else do { i, j, v }: { i = j, j = i, v } done
|
Chris@238
|
163 fi;
|
Chris@254
|
164 for (map preprocess entries) do { i, j, v }:
|
Chris@254
|
165 if not (i in data) then data[i] := [:] fi;
|
Chris@254
|
166 data[i][j] := v;
|
Chris@254
|
167 done;
|
Chris@254
|
168 if isRow then SparseRows else SparseCols fi { size, data });
|
Chris@238
|
169
|
Chris@243
|
170 toSparse m =
|
Chris@238
|
171 if isSparse? m then m
|
Chris@238
|
172 else
|
Chris@238
|
173 { rows, columns } = size m;
|
Chris@243
|
174 enumerate m ii jj =
|
Chris@238
|
175 case ii of
|
Chris@238
|
176 i::irest:
|
Chris@238
|
177 case jj of
|
Chris@238
|
178 j::rest:
|
Chris@238
|
179 v = getAt i j m;
|
Chris@243
|
180 if v != 0 then { i, j, v } :. \(enumerate m ii rest)
|
Chris@243
|
181 else enumerate m ii rest
|
Chris@238
|
182 fi;
|
Chris@243
|
183 _: enumerate m irest [0..columns-1];
|
Chris@238
|
184 esac;
|
Chris@238
|
185 _: [];
|
Chris@238
|
186 esac;
|
Chris@238
|
187 makeSparse
|
Chris@244
|
188 if isRowMajor? m then RowMajor () else ColumnMajor () fi
|
Chris@238
|
189 (size m)
|
Chris@243
|
190 (enumerate m [0..rows-1] [0..columns-1]);
|
Chris@238
|
191 fi;
|
Chris@238
|
192
|
Chris@238
|
193 toDense m =
|
Chris@238
|
194 if not (isSparse? m) then m
|
Chris@238
|
195 elif isRowMajor? m then
|
Chris@238
|
196 DenseRows (array (map do row: getRow row m done [0..height m - 1]));
|
Chris@238
|
197 else
|
Chris@238
|
198 DenseCols (array (map do col: getColumn col m done [0..width m - 1]));
|
Chris@238
|
199 fi;
|
Chris@235
|
200
|
Chris@20
|
201 constMatrix n = generate do row col: n done;
|
Chris@20
|
202 randomMatrix = generate do row col: Math#random() done;
|
Chris@5
|
203 identityMatrix = constMatrix 1;
|
Chris@5
|
204
|
Chris@100
|
205 transposed m =
|
Chris@208
|
206 case m of
|
Chris@234
|
207 DenseRows d: DenseCols d;
|
Chris@234
|
208 DenseCols d: DenseRows d;
|
Chris@254
|
209 SparseRows { data, size }: SparseCols
|
Chris@254
|
210 { data, size = { rows = size.columns, columns = size.rows } };
|
Chris@254
|
211 SparseCols { data, size }: SparseRows
|
Chris@254
|
212 { data, size = { rows = size.columns, columns = size.rows } };
|
Chris@208
|
213 esac;
|
Chris@100
|
214
|
Chris@254
|
215 sparseFlipped m =
|
Chris@254
|
216 ({ tagger, data } =
|
Chris@254
|
217 case m of
|
Chris@254
|
218 SparseCols { data }: { tagger = SparseRows, data };
|
Chris@254
|
219 SparseRows { data }: { tagger = SparseCols, data };
|
Chris@254
|
220 _: failWith "sparseFlipped called for non-sparse matrix";
|
Chris@254
|
221 esac;
|
Chris@254
|
222 data' = [:];
|
Chris@254
|
223 for (keys data) do i:
|
Chris@254
|
224 for (keys data[i]) do j:
|
Chris@254
|
225 if not (j in data') then data'[j] := [:] fi;
|
Chris@254
|
226 data'[j][i] := data[i][j];
|
Chris@254
|
227 done
|
Chris@254
|
228 done;
|
Chris@254
|
229 tagger { size = size m, data = data' });
|
Chris@254
|
230
|
Chris@100
|
231 flipped m =
|
Chris@235
|
232 if isSparse? m then
|
Chris@254
|
233 sparseFlipped m
|
Chris@100
|
234 else
|
Chris@235
|
235 if isRowMajor? m then
|
Chris@235
|
236 generate do row col: getAt row col m done (size m);
|
Chris@235
|
237 else
|
Chris@235
|
238 transposed
|
Chris@235
|
239 (generate do row col: getAt col row m done
|
Chris@235
|
240 { rows = (width m), columns = (height m) });
|
Chris@235
|
241 fi
|
Chris@100
|
242 fi;
|
Chris@100
|
243
|
Chris@161
|
244 toRowMajor m =
|
Chris@208
|
245 if isRowMajor? m then m else flipped m fi;
|
Chris@161
|
246
|
Chris@161
|
247 toColumnMajor m =
|
Chris@208
|
248 if not isRowMajor? m then m else flipped m fi;
|
Chris@161
|
249
|
Chris@238
|
250 equal'' comparator vecComparator m1 m2 =
|
Chris@254
|
251 // Prerequisite: m1 and m2 have same size, sparse-p, and storage order
|
Chris@241
|
252 (compareVecLists vv1 vv2 = all id (map2 vecComparator vv1 vv2);
|
Chris@238
|
253 compareSparse d1 d2 =
|
Chris@254
|
254 (data1 = d1.data;
|
Chris@254
|
255 data2 = d2.data;
|
Chris@254
|
256 keys data1 == keys data2 and
|
Chris@254
|
257 all id
|
Chris@254
|
258 (map do i:
|
Chris@254
|
259 keys data1[i] == keys data2[i] and
|
Chris@254
|
260 all id
|
Chris@254
|
261 (map do j:
|
Chris@254
|
262 comparator data1[i][j] data2[i][j]
|
Chris@254
|
263 done (keys data1[i]))
|
Chris@254
|
264 done (keys data1)));
|
Chris@238
|
265 case m1 of
|
Chris@238
|
266 DenseRows d1:
|
Chris@238
|
267 case m2 of DenseRows d2: compareVecLists d1 d2; _: false; esac;
|
Chris@238
|
268 DenseCols d1:
|
Chris@238
|
269 case m2 of DenseCols d2: compareVecLists d1 d2; _: false; esac;
|
Chris@254
|
270 SparseRows d1:
|
Chris@254
|
271 case m2 of SparseRows d2: compareSparse d1 d2; _: false; esac;
|
Chris@254
|
272 SparseCols d1:
|
Chris@254
|
273 case m2 of SparseCols d2: compareSparse d1 d2; _: false; esac;
|
Chris@238
|
274 esac);
|
Chris@238
|
275
|
Chris@238
|
276 equal' comparator vecComparator m1 m2 =
|
Chris@226
|
277 if size m1 != size m2 then
|
Chris@226
|
278 false
|
Chris@226
|
279 elif isRowMajor? m1 != isRowMajor? m2 then
|
Chris@238
|
280 equal' comparator vecComparator (flipped m1) m2;
|
Chris@238
|
281 elif isSparse? m1 != isSparse? m2 then
|
Chris@238
|
282 if isSparse? m1 then
|
Chris@243
|
283 equal' comparator vecComparator m1 (toSparse m2)
|
Chris@238
|
284 else
|
Chris@243
|
285 equal' comparator vecComparator (toSparse m1) m2
|
Chris@238
|
286 fi
|
Chris@100
|
287 else
|
Chris@238
|
288 equal'' comparator vecComparator m1 m2
|
Chris@100
|
289 fi;
|
Chris@97
|
290
|
Chris@228
|
291 // Compare matrices using the given comparator for individual cells.
|
Chris@228
|
292 // Note that matrices with different storage order but the same
|
Chris@228
|
293 // contents are equal, although comparing them is slow.
|
Chris@249
|
294 //!!! Document the fact that sparse matrices can only be equal if they
|
Chris@249
|
295 // have the same set of non-zero cells (regardless of comparator used)
|
Chris@228
|
296 equalUnder comparator =
|
Chris@238
|
297 equal' comparator (vec.equalUnder comparator);
|
Chris@228
|
298
|
Chris@228
|
299 equal =
|
Chris@238
|
300 equal' (==) vec.equal;
|
Chris@226
|
301
|
Chris@163
|
302 newMatrix type data = //!!! NB does not copy data
|
Chris@238
|
303 (tagger = case type of RowMajor (): DenseRows; ColumnMajor (): DenseCols esac;
|
Chris@214
|
304 if empty? data or vec.empty? (head data)
|
Chris@201
|
305 then zeroSizeMatrix ()
|
Chris@208
|
306 else tagger (array data)
|
Chris@96
|
307 fi);
|
Chris@96
|
308
|
Chris@163
|
309 newRowVector data = //!!! NB does not copy data
|
Chris@234
|
310 DenseRows (array [data]);
|
Chris@96
|
311
|
Chris@163
|
312 newColumnVector data = //!!! NB does not copy data
|
Chris@234
|
313 DenseCols (array [data]);
|
Chris@8
|
314
|
Chris@195
|
315 scaled factor m = //!!! v inefficient
|
Chris@208
|
316 generate do row col: factor * (getAt row col m) done (size m);
|
Chris@98
|
317
|
Chris@243
|
318 thresholded threshold m = //!!! v inefficient; and should take a threshold function?
|
Chris@243
|
319 generate do row col:
|
Chris@243
|
320 v = getAt row col m; if (abs v) > threshold then v else 0 fi
|
Chris@243
|
321 done (size m);
|
Chris@243
|
322
|
Chris@98
|
323 sum' m1 m2 =
|
Chris@208
|
324 if (size m1) != (size m2)
|
Chris@208
|
325 then failWith "Matrices are not the same size: \(size m1), \(size m2)";
|
Chris@98
|
326 else
|
Chris@208
|
327 generate do row col: getAt row col m1 + getAt row col m2 done (size m1);
|
Chris@98
|
328 fi;
|
Chris@98
|
329
|
Chris@229
|
330 difference m1 m2 = //!!! doc: m1 - m2, not m2 - m1
|
Chris@229
|
331 if (size m1) != (size m2)
|
Chris@229
|
332 then failWith "Matrices are not the same size: \(size m1), \(size m2)";
|
Chris@229
|
333 else
|
Chris@229
|
334 generate do row col: getAt row col m1 - getAt row col m2 done (size m1);
|
Chris@229
|
335 fi;
|
Chris@229
|
336
|
Chris@229
|
337 abs' m =
|
Chris@229
|
338 generate do row col: abs (getAt row col m) done (size m);
|
Chris@229
|
339
|
Chris@249
|
340 sparseProductLeft size m1 m2 =
|
Chris@249
|
341 (e = enumerateSparse m1;
|
Chris@249
|
342 data = array (map \(new double[size.rows]) [1..size.columns]);
|
Chris@249
|
343 for [0..size.columns - 1] do j':
|
Chris@249
|
344 c = getColumn j' m2;
|
Chris@249
|
345 for e do { v, i, j }:
|
Chris@249
|
346 data[j'][i] := data[j'][i] + v * (vec.at j c);
|
Chris@249
|
347 done;
|
Chris@249
|
348 done;
|
Chris@249
|
349 DenseCols (array (map vec.vector (list data))));
|
Chris@249
|
350
|
Chris@249
|
351 sparseProductRight size m1 m2 =
|
Chris@249
|
352 (e = enumerateSparse m2;
|
Chris@249
|
353 data = array (map \(new double[size.columns]) [1..size.rows]);
|
Chris@249
|
354 for [0..size.rows - 1] do i':
|
Chris@249
|
355 r = getRow i' m1;
|
Chris@249
|
356 for e do { v, i, j }:
|
Chris@249
|
357 data[i'][j] := data[i'][j] + v * (vec.at i r);
|
Chris@249
|
358 done;
|
Chris@249
|
359 done;
|
Chris@249
|
360 DenseRows (array (map vec.vector (list data))));
|
Chris@249
|
361
|
Chris@251
|
362 sparseProduct size m1 m2 =
|
Chris@251
|
363 case m2 of
|
Chris@254
|
364 SparseCols d:
|
Chris@251
|
365 (e = enumerateSparse m1;
|
Chris@254
|
366 out = [:];
|
Chris@254
|
367 for (keys d.data) do j':
|
Chris@254
|
368 h = [:];
|
Chris@254
|
369 for e do { v, i, j }:
|
Chris@254
|
370 if j in d.data[j'] then
|
Chris@254
|
371 if not (i in h) then h[i] := 0 fi;
|
Chris@254
|
372 h[i] := h[i] + v * d.data[j'][j];
|
Chris@254
|
373 fi;
|
Chris@254
|
374 done;
|
Chris@254
|
375 out[j'] := h;
|
Chris@254
|
376 done;
|
Chris@254
|
377 SparseCols { size, data = out });
|
Chris@254
|
378 SparseRows _:
|
Chris@251
|
379 sparseProduct size m1 (flipped m2);
|
Chris@251
|
380 _: failWith "sparseProduct called for non-sparse matrices";
|
Chris@251
|
381 esac;
|
Chris@251
|
382
|
Chris@249
|
383 denseProduct size m1 m2 =
|
Chris@249
|
384 (data = array (map \(new double[size.rows]) [1..size.columns]);
|
Chris@249
|
385 for [0..size.rows - 1] do i:
|
Chris@249
|
386 row = getRow i m1;
|
Chris@249
|
387 for [0..size.columns - 1] do j:
|
Chris@249
|
388 data[j][i] := bf.sum (bf.multiply row (getColumn j m2));
|
Chris@249
|
389 done;
|
Chris@249
|
390 done;
|
Chris@249
|
391 DenseCols (array (map vec.vector (list data))));
|
Chris@249
|
392
|
Chris@98
|
393 product m1 m2 =
|
Chris@208
|
394 if (size m1).columns != (size m2).rows
|
Chris@246
|
395 then failWith "Matrix dimensions incompatible: \(size m1), \(size m2) (\((size m1).columns) != \((size m2).rows))";
|
Chris@249
|
396 else
|
Chris@249
|
397 size = { rows = (size m1).rows, columns = (size m2).columns };
|
Chris@249
|
398 if isSparse? m1 then
|
Chris@251
|
399 if isSparse? m2 then
|
Chris@251
|
400 sparseProduct size m1 m2
|
Chris@251
|
401 else
|
Chris@251
|
402 sparseProductLeft size m1 m2
|
Chris@251
|
403 fi
|
Chris@249
|
404 elif isSparse? m2 then
|
Chris@249
|
405 sparseProductRight size m1 m2
|
Chris@249
|
406 else
|
Chris@249
|
407 denseProduct size m1 m2
|
Chris@249
|
408 fi;
|
Chris@98
|
409 fi;
|
Chris@98
|
410
|
Chris@161
|
411 asRows m =
|
Chris@208
|
412 map do i: getRow i m done [0 .. (height m) - 1];
|
Chris@161
|
413
|
Chris@161
|
414 asColumns m =
|
Chris@208
|
415 map do i: getColumn i m done [0 .. (width m) - 1];
|
Chris@161
|
416
|
Chris@178
|
417 concatAgainstGrain tagger getter counter mm =
|
Chris@208
|
418 (n = counter (size (head mm));
|
Chris@208
|
419 tagger (array
|
Chris@177
|
420 (map do i:
|
Chris@214
|
421 vec.concat (map (getter i) mm)
|
Chris@208
|
422 done [0..n-1])));
|
Chris@177
|
423
|
Chris@178
|
424 concatWithGrain tagger getter counter mm =
|
Chris@208
|
425 tagger (array
|
Chris@177
|
426 (concat
|
Chris@177
|
427 (map do m:
|
Chris@208
|
428 n = counter (size m);
|
Chris@208
|
429 map do i: getter i m done [0..n-1]
|
Chris@208
|
430 done mm)));
|
Chris@177
|
431
|
Chris@178
|
432 checkDimensionsFor direction first mm =
|
Chris@178
|
433 (counter = if direction == Horizontal () then (.rows) else (.columns) fi;
|
Chris@208
|
434 n = counter (size first);
|
Chris@208
|
435 if not (all id (map do m: counter (size m) == n done mm)) then
|
Chris@208
|
436 failWith "Matrix dimensions incompatible for concat (found \(map do m: counter (size m) done mm) not all of which are \(n))";
|
Chris@178
|
437 fi);
|
Chris@178
|
438
|
Chris@187
|
439 concat direction mm = //!!! doc: storage order is taken from first matrix in sequence
|
Chris@187
|
440 //!!! would this be better as separate concatHorizontal/concatVertical functions?
|
Chris@190
|
441 case mm of
|
Chris@190
|
442 first::rest:
|
Chris@178
|
443 checkDimensionsFor direction first mm;
|
Chris@208
|
444 row = isRowMajor? first;
|
Chris@178
|
445 // horizontal, row-major: against grain with rows
|
Chris@178
|
446 // horizontal, col-major: with grain with cols
|
Chris@178
|
447 // vertical, row-major: with grain with rows
|
Chris@178
|
448 // vertical, col-major: against grain with cols
|
Chris@178
|
449 case direction of
|
Chris@178
|
450 Horizontal ():
|
Chris@234
|
451 if row then concatAgainstGrain DenseRows getRow (.rows) mm;
|
Chris@234
|
452 else concatWithGrain DenseCols getColumn (.columns) mm;
|
Chris@178
|
453 fi;
|
Chris@178
|
454 Vertical ():
|
Chris@234
|
455 if row then concatWithGrain DenseRows getRow (.rows) mm;
|
Chris@234
|
456 else concatAgainstGrain DenseCols getColumn (.columns) mm;
|
Chris@178
|
457 fi;
|
Chris@178
|
458 esac;
|
Chris@190
|
459 [single]: single;
|
Chris@190
|
460 _: zeroSizeMatrix ();
|
Chris@190
|
461 esac;
|
Chris@177
|
462
|
Chris@240
|
463 //!!! inconsistent with std.slice which has start..end not start+count (see also vec.slice/rangeOf)
|
Chris@187
|
464 rowSlice start count m = //!!! doc: storage order same as input
|
Chris@208
|
465 if isRowMajor? m then
|
Chris@234
|
466 DenseRows (array (map ((flip getRow) m) [start .. start + count - 1]))
|
Chris@187
|
467 else
|
Chris@234
|
468 DenseCols (array (map (vec.rangeOf start count) (asColumns m)))
|
Chris@187
|
469 fi;
|
Chris@187
|
470
|
Chris@187
|
471 columnSlice start count m = //!!! doc: storage order same as input
|
Chris@208
|
472 if not isRowMajor? m then
|
Chris@234
|
473 DenseCols (array (map ((flip getColumn) m) [start .. start + count - 1]))
|
Chris@187
|
474 else
|
Chris@234
|
475 DenseRows (array (map (vec.rangeOf start count) (asRows m)))
|
Chris@187
|
476 fi;
|
Chris@187
|
477
|
Chris@201
|
478 resizedTo newsize m =
|
Chris@208
|
479 (if newsize == (size m) then
|
Chris@201
|
480 m
|
Chris@208
|
481 elif (height m) == 0 or (width m) == 0 then
|
Chris@202
|
482 zeroMatrixWithTypeOf m newsize;
|
Chris@201
|
483 else
|
Chris@208
|
484 growrows = newsize.rows - (height m);
|
Chris@208
|
485 growcols = newsize.columns - (width m);
|
Chris@208
|
486 rowm = isRowMajor? m;
|
Chris@201
|
487 resizedTo newsize
|
Chris@201
|
488 if rowm and growrows < 0 then
|
Chris@201
|
489 rowSlice 0 newsize.rows m
|
Chris@201
|
490 elif (not rowm) and growcols < 0 then
|
Chris@201
|
491 columnSlice 0 newsize.columns m
|
Chris@201
|
492 elif growrows < 0 then
|
Chris@201
|
493 rowSlice 0 newsize.rows m
|
Chris@201
|
494 elif growcols < 0 then
|
Chris@201
|
495 columnSlice 0 newsize.columns m
|
Chris@201
|
496 else
|
Chris@201
|
497 if growrows > 0 then
|
Chris@201
|
498 concat (Vertical ())
|
Chris@208
|
499 [m, zeroMatrixWithTypeOf m ((size m) with { rows = growrows })]
|
Chris@201
|
500 else
|
Chris@201
|
501 concat (Horizontal ())
|
Chris@208
|
502 [m, zeroMatrixWithTypeOf m ((size m) with { columns = growcols })]
|
Chris@201
|
503 fi
|
Chris@201
|
504 fi
|
Chris@202
|
505 fi);
|
Chris@201
|
506
|
Chris@5
|
507 {
|
Chris@208
|
508 size,
|
Chris@208
|
509 width,
|
Chris@208
|
510 height,
|
Chris@246
|
511 density,
|
Chris@249
|
512 nonZeroValues,
|
Chris@208
|
513 getAt,
|
Chris@208
|
514 getColumn,
|
Chris@208
|
515 getRow,
|
Chris@208
|
516 isRowMajor?,
|
Chris@234
|
517 isSparse?,
|
Chris@208
|
518 generate,
|
Chris@208
|
519 constMatrix,
|
Chris@208
|
520 randomMatrix,
|
Chris@208
|
521 zeroMatrix,
|
Chris@208
|
522 identityMatrix,
|
Chris@208
|
523 zeroSizeMatrix,
|
Chris@208
|
524 equal,
|
Chris@226
|
525 equalUnder,
|
Chris@208
|
526 transposed,
|
Chris@208
|
527 flipped,
|
Chris@208
|
528 toRowMajor,
|
Chris@208
|
529 toColumnMajor,
|
Chris@238
|
530 toSparse,
|
Chris@238
|
531 toDense,
|
Chris@208
|
532 scaled,
|
Chris@243
|
533 thresholded,
|
Chris@208
|
534 resizedTo,
|
Chris@208
|
535 asRows,
|
Chris@208
|
536 asColumns,
|
Chris@208
|
537 sum = sum',
|
Chris@229
|
538 difference,
|
Chris@229
|
539 abs = abs',
|
Chris@208
|
540 product,
|
Chris@208
|
541 concat,
|
Chris@208
|
542 rowSlice,
|
Chris@208
|
543 columnSlice,
|
Chris@208
|
544 newMatrix,
|
Chris@208
|
545 newRowVector,
|
Chris@208
|
546 newColumnVector,
|
Chris@245
|
547 newSparseMatrix = makeSparse
|
Chris@208
|
548 }
|
Chris@208
|
549 as
|
Chris@208
|
550 {
|
Chris@208
|
551 //!!! check whether these are right to be .selector rather than just selector
|
Chris@208
|
552
|
Chris@208
|
553 size is matrix -> { .rows is number, .columns is number },
|
Chris@208
|
554 width is matrix -> number,
|
Chris@208
|
555 height is matrix -> number,
|
Chris@246
|
556 density is matrix -> number,
|
Chris@249
|
557 nonZeroValues is matrix -> number,
|
Chris@208
|
558 getAt is number -> number -> matrix -> number,
|
Chris@214
|
559 getColumn is number -> matrix -> vector,
|
Chris@214
|
560 getRow is number -> matrix -> vector,
|
Chris@208
|
561 isRowMajor? is matrix -> boolean,
|
Chris@234
|
562 isSparse? is matrix -> boolean,
|
Chris@195
|
563 generate is (number -> number -> number) -> { .rows is number, .columns is number } -> matrix,
|
Chris@195
|
564 constMatrix is number -> { .rows is number, .columns is number } -> matrix,
|
Chris@195
|
565 randomMatrix is { .rows is number, .columns is number } -> matrix,
|
Chris@195
|
566 zeroMatrix is { .rows is number, .columns is number } -> matrix,
|
Chris@195
|
567 identityMatrix is { .rows is number, .columns is number } -> matrix,
|
Chris@195
|
568 zeroSizeMatrix is () -> matrix,
|
Chris@195
|
569 equal is matrix -> matrix -> boolean,
|
Chris@226
|
570 equalUnder is (number -> number -> boolean) -> matrix -> matrix -> boolean,
|
Chris@195
|
571 transposed is matrix -> matrix,
|
Chris@195
|
572 flipped is matrix -> matrix,
|
Chris@195
|
573 toRowMajor is matrix -> matrix,
|
Chris@195
|
574 toColumnMajor is matrix -> matrix,
|
Chris@243
|
575 toSparse is matrix -> matrix,
|
Chris@238
|
576 toDense is matrix -> matrix,
|
Chris@195
|
577 scaled is number -> matrix -> matrix,
|
Chris@243
|
578 thresholded is number -> matrix -> matrix,
|
Chris@195
|
579 resizedTo is { .rows is number, .columns is number } -> matrix -> matrix,
|
Chris@214
|
580 asRows is matrix -> list<vector>,
|
Chris@214
|
581 asColumns is matrix -> list<vector>,
|
Chris@208
|
582 sum is matrix -> matrix -> matrix,
|
Chris@229
|
583 difference is matrix -> matrix -> matrix,
|
Chris@229
|
584 abs is matrix -> matrix,
|
Chris@195
|
585 product is matrix -> matrix -> matrix,
|
Chris@195
|
586 concat is (Horizontal () | Vertical ()) -> list<matrix> -> matrix,
|
Chris@195
|
587 rowSlice is number -> number -> matrix -> matrix,
|
Chris@195
|
588 columnSlice is number -> number -> matrix -> matrix,
|
Chris@214
|
589 newMatrix is (ColumnMajor () | RowMajor ()) -> list<vector> -> matrix,
|
Chris@214
|
590 newRowVector is vector -> matrix,
|
Chris@214
|
591 newColumnVector is vector -> matrix,
|
Chris@245
|
592 newSparseMatrix is (ColumnMajor () | RowMajor ()) -> { .rows is number, .columns is number } -> list<{ .i is number, .j is number, .v is number }> -> matrix
|
Chris@5
|
593 }
|
Chris@5
|
594
|