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