nmatrix 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.autotest +23 -0
- data/.gemtest +0 -0
- data/Gemfile +7 -0
- data/History.txt +6 -0
- data/LICENSE.txt +21 -0
- data/Manifest.txt +51 -0
- data/README.rdoc +63 -0
- data/Rakefile +154 -0
- data/ext/nmatrix/cblas.c +150 -0
- data/ext/nmatrix/dense.c +307 -0
- data/ext/nmatrix/dense/blas_header.template.c +52 -0
- data/ext/nmatrix/dense/elementwise.template.c +107 -0
- data/ext/nmatrix/dense/gemm.template.c +159 -0
- data/ext/nmatrix/dense/gemv.template.c +130 -0
- data/ext/nmatrix/dense/rationalmath.template.c +68 -0
- data/ext/nmatrix/depend +18 -0
- data/ext/nmatrix/extconf.rb +143 -0
- data/ext/nmatrix/generator.rb +594 -0
- data/ext/nmatrix/generator/syntax_tree.rb +481 -0
- data/ext/nmatrix/list.c +774 -0
- data/ext/nmatrix/nmatrix.c +1977 -0
- data/ext/nmatrix/nmatrix.h +912 -0
- data/ext/nmatrix/rational.c +98 -0
- data/ext/nmatrix/yale.c +726 -0
- data/ext/nmatrix/yale/complexmath.template.c +71 -0
- data/ext/nmatrix/yale/elementwise.template.c +46 -0
- data/ext/nmatrix/yale/elementwise_op.template.c +73 -0
- data/ext/nmatrix/yale/numbmm.template.c +94 -0
- data/ext/nmatrix/yale/smmp1.template.c +21 -0
- data/ext/nmatrix/yale/smmp1_header.template.c +38 -0
- data/ext/nmatrix/yale/smmp2.template.c +43 -0
- data/ext/nmatrix/yale/smmp2_header.template.c +46 -0
- data/ext/nmatrix/yale/sort_columns.template.c +56 -0
- data/ext/nmatrix/yale/symbmm.template.c +54 -0
- data/ext/nmatrix/yale/transp.template.c +68 -0
- data/lib/array.rb +67 -0
- data/lib/nmatrix.rb +263 -0
- data/lib/string.rb +65 -0
- data/spec/nmatrix_spec.rb +395 -0
- data/spec/nmatrix_yale_spec.rb +239 -0
- data/spec/nvector_spec.rb +43 -0
- data/spec/syntax_tree_spec.rb +46 -0
- metadata +150 -0
data/ext/nmatrix/list.c
ADDED
@@ -0,0 +1,774 @@
|
|
1
|
+
/////////////////////////////////////////////////////////////////////
|
2
|
+
// = NMatrix
|
3
|
+
//
|
4
|
+
// A linear algebra library for scientific computation in Ruby.
|
5
|
+
// NMatrix is part of SciRuby.
|
6
|
+
//
|
7
|
+
// NMatrix was originally inspired by and derived from NArray, by
|
8
|
+
// Masahiro Tanaka: http://narray.rubyforge.org
|
9
|
+
//
|
10
|
+
// == Copyright Information
|
11
|
+
//
|
12
|
+
// SciRuby is Copyright (c) 2010 - 2012, Ruby Science Foundation
|
13
|
+
// NMatrix is Copyright (c) 2012, Ruby Science Foundation
|
14
|
+
//
|
15
|
+
// Please see LICENSE.txt for additional copyright notices.
|
16
|
+
//
|
17
|
+
// == Contributing
|
18
|
+
//
|
19
|
+
// By contributing source code to SciRuby, you agree to be bound by
|
20
|
+
// our Contributor Agreement:
|
21
|
+
//
|
22
|
+
// * https://github.com/SciRuby/sciruby/wiki/Contributor-Agreement
|
23
|
+
//
|
24
|
+
// == list.c
|
25
|
+
//
|
26
|
+
// List-of-lists n-dimensional matrix storage. Uses singly-linked
|
27
|
+
// lists.
|
28
|
+
|
29
|
+
#ifndef LIST_C
|
30
|
+
# define LIST_C
|
31
|
+
|
32
|
+
#include <ruby.h>
|
33
|
+
|
34
|
+
#include "nmatrix.h"
|
35
|
+
|
36
|
+
extern VALUE nm_eStorageTypeError;
|
37
|
+
|
38
|
+
|
39
|
+
/* Calculate the max number of elements in the list storage structure, based on shape and rank */
|
40
|
+
inline size_t count_storage_max_elements(const STORAGE* s) {
|
41
|
+
return count_dense_storage_elements((DENSE_STORAGE*)s);
|
42
|
+
}
|
43
|
+
|
44
|
+
static size_t count_list_storage_elements_r(const LIST* l, size_t recursions) {
|
45
|
+
size_t count = 0;
|
46
|
+
NODE* curr = l->first;
|
47
|
+
if (recursions) {
|
48
|
+
while (curr) {
|
49
|
+
count += count_list_storage_elements_r(curr->val, recursions-1);
|
50
|
+
curr = curr->next;
|
51
|
+
}
|
52
|
+
} else {
|
53
|
+
while (curr) {
|
54
|
+
++count;
|
55
|
+
curr = curr->next;
|
56
|
+
}
|
57
|
+
}
|
58
|
+
return count;
|
59
|
+
}
|
60
|
+
|
61
|
+
|
62
|
+
// Count non-zero elements. See also count_list_storage_nd_elements.
|
63
|
+
size_t count_list_storage_elements(const LIST_STORAGE* s) {
|
64
|
+
return count_list_storage_elements_r(s->rows, s->rank-1);
|
65
|
+
}
|
66
|
+
|
67
|
+
|
68
|
+
// Count non-diagonal non-zero elements
|
69
|
+
size_t count_list_storage_nd_elements(const LIST_STORAGE* s) {
|
70
|
+
NODE *i_curr, *j_curr;
|
71
|
+
size_t count = 0;
|
72
|
+
if (s->rank != 2) rb_raise(rb_eNotImpError, "non-diagonal element counting only defined for rank = 2");
|
73
|
+
|
74
|
+
for (i_curr = s->rows->first; i_curr; i_curr = i_curr->next) {
|
75
|
+
for (j_curr = ((LIST*)(i_curr->val))->first; j_curr; j_curr = j_curr->next) {
|
76
|
+
if (i_curr->key != j_curr->key) ++count;
|
77
|
+
}
|
78
|
+
}
|
79
|
+
return count;
|
80
|
+
}
|
81
|
+
|
82
|
+
|
83
|
+
/* Finds the node that should go before whatever key we request, whether or not that key is present */
|
84
|
+
static NODE* list_find_preceding_from(NODE* prev, size_t key) {
|
85
|
+
NODE* curr = prev->next;
|
86
|
+
|
87
|
+
if (!curr || key <= curr->key) return prev;
|
88
|
+
return list_find_preceding_from(curr, key);
|
89
|
+
}
|
90
|
+
|
91
|
+
|
92
|
+
/* Finds a node or the one immediately preceding it if it doesn't exist */
|
93
|
+
static NODE* list_find_nearest_from(NODE* prev, size_t key) {
|
94
|
+
NODE* f;
|
95
|
+
|
96
|
+
if (prev && prev->key == key) return prev;
|
97
|
+
|
98
|
+
f = list_find_preceding_from(prev, key);
|
99
|
+
|
100
|
+
if (!f->next) return f;
|
101
|
+
else if (key == f->next->key) return f->next;
|
102
|
+
else return prev;
|
103
|
+
}
|
104
|
+
|
105
|
+
|
106
|
+
/* Finds the node or, if not present, the node that it should follow.
|
107
|
+
* NULL indicates no preceding node. */
|
108
|
+
//static NODE* list_find_nearest(LIST* list, size_t key) {
|
109
|
+
// return list_find_nearest_from(list->first, key);
|
110
|
+
//}
|
111
|
+
|
112
|
+
|
113
|
+
/* Find some element in the list and return the node ptr for that key. */
|
114
|
+
static NODE* list_find(LIST* list, size_t key) {
|
115
|
+
NODE* f;
|
116
|
+
if (!list->first) return NULL; // empty list -- does not exist
|
117
|
+
|
118
|
+
// see if we can find it.
|
119
|
+
f = list_find_nearest_from(list->first, key);
|
120
|
+
if (!f || f->key == key) return f;
|
121
|
+
return NULL;
|
122
|
+
}
|
123
|
+
|
124
|
+
|
125
|
+
|
126
|
+
/* Get the contents of some set of coordinates. Note: Does not make a copy! Don't free! */
|
127
|
+
void* list_storage_get(LIST_STORAGE* s, size_t* coords) {
|
128
|
+
//LIST_STORAGE* s = (LIST_STORAGE*)(t);
|
129
|
+
size_t r;
|
130
|
+
NODE* n;
|
131
|
+
LIST* l = s->rows;
|
132
|
+
|
133
|
+
for (r = s->rank; r > 1; --r) {
|
134
|
+
n = list_find(l, coords[s->rank - r]);
|
135
|
+
if (n) l = n->val;
|
136
|
+
else return s->default_val;
|
137
|
+
}
|
138
|
+
|
139
|
+
n = list_find(l, coords[s->rank - r]);
|
140
|
+
if (n) return n->val;
|
141
|
+
else return s->default_val;
|
142
|
+
}
|
143
|
+
|
144
|
+
|
145
|
+
/* Returns the value pointer (not the node) for some key. Note that it doesn't free the memory
|
146
|
+
* for the value stored in the node -- that pointer gets returned! Only the node is destroyed.
|
147
|
+
*/
|
148
|
+
static void* list_remove(LIST* list, size_t key) {
|
149
|
+
NODE *f, *rm;
|
150
|
+
void* val;
|
151
|
+
|
152
|
+
if (!list->first || list->first->key > key) return NULL; // empty list or def. not present
|
153
|
+
|
154
|
+
if (list->first->key == key) {
|
155
|
+
val = list->first->val;
|
156
|
+
rm = list->first;
|
157
|
+
list->first = rm->next;
|
158
|
+
free(rm);
|
159
|
+
return val;
|
160
|
+
}
|
161
|
+
|
162
|
+
f = list_find_preceding_from(list->first, key);
|
163
|
+
if (!f || !f->next) return NULL; // not found, end of list
|
164
|
+
|
165
|
+
if (f->next->key == key) {
|
166
|
+
// remove the node
|
167
|
+
rm = f->next;
|
168
|
+
f->next = rm->next;
|
169
|
+
|
170
|
+
// get the value and free the memory for the node
|
171
|
+
val = rm->val;
|
172
|
+
free(rm);
|
173
|
+
return val;
|
174
|
+
}
|
175
|
+
|
176
|
+
return NULL; // not found, middle of list
|
177
|
+
}
|
178
|
+
|
179
|
+
|
180
|
+
/// TODO: Speed up removal.
|
181
|
+
void* list_storage_remove(LIST_STORAGE* s, size_t* coords) {
|
182
|
+
int r;
|
183
|
+
NODE *n = NULL;
|
184
|
+
LIST* l = s->rows;
|
185
|
+
void* rm = NULL;
|
186
|
+
|
187
|
+
// keep track of where we are in the traversals
|
188
|
+
NODE** stack = ALLOCA_N( NODE*, s->rank - 1 );
|
189
|
+
|
190
|
+
for (r = (int)(s->rank); r > 1; --r) {
|
191
|
+
n = list_find(l, coords[s->rank - r]); // does this row exist in the matrix?
|
192
|
+
|
193
|
+
if (!n) { // not found
|
194
|
+
free(stack);
|
195
|
+
return NULL;
|
196
|
+
} else { // found
|
197
|
+
stack[s->rank - r] = n;
|
198
|
+
l = n->val;
|
199
|
+
}
|
200
|
+
}
|
201
|
+
|
202
|
+
rm = list_remove(l, coords[s->rank - r]);
|
203
|
+
|
204
|
+
// if we removed something, we may now need to remove parent lists
|
205
|
+
if (rm) {
|
206
|
+
for (r = (int)(s->rank) - 2; r >= 0; --r) { // walk back down the stack
|
207
|
+
if (((LIST*)(stack[r]->val))->first == NULL)
|
208
|
+
free(list_remove(stack[r]->val, coords[r]));
|
209
|
+
else
|
210
|
+
break; // no need to continue unless we just deleted one.
|
211
|
+
}
|
212
|
+
}
|
213
|
+
|
214
|
+
return rm;
|
215
|
+
}
|
216
|
+
|
217
|
+
|
218
|
+
/* Creates an empty linked list */
|
219
|
+
static LIST* create_list() {
|
220
|
+
LIST* list;
|
221
|
+
//if (!(list = malloc(sizeof(LIST)))) return NULL;
|
222
|
+
list = ALLOC( LIST );
|
223
|
+
|
224
|
+
//fprintf(stderr, " create_list LIST: %p\n", list);
|
225
|
+
|
226
|
+
list->first = NULL;
|
227
|
+
return list;
|
228
|
+
}
|
229
|
+
|
230
|
+
|
231
|
+
static NODE* list_insert_after(NODE* node, size_t key, void* val) {
|
232
|
+
NODE* ins;
|
233
|
+
|
234
|
+
//if (!(ins = malloc(sizeof(NODE)))) return NULL;
|
235
|
+
ins = ALLOC(NODE);
|
236
|
+
|
237
|
+
// insert 'ins' between 'node' and 'node->next'
|
238
|
+
ins->next = node->next;
|
239
|
+
node->next = ins;
|
240
|
+
|
241
|
+
// initialize our new node
|
242
|
+
ins->key = key;
|
243
|
+
ins->val = val;
|
244
|
+
|
245
|
+
return ins;
|
246
|
+
}
|
247
|
+
|
248
|
+
|
249
|
+
|
250
|
+
/* Given a list and a key/value-ptr pair, create a node (and return that node).
|
251
|
+
* If NULL is returned, it means insertion failed.
|
252
|
+
* If the key already exists in the list, replace tells it to delete the old value
|
253
|
+
* and put in your new one. !replace means delete the new value.
|
254
|
+
*/
|
255
|
+
static NODE* list_insert(LIST* list, bool replace, size_t key, void* val) {
|
256
|
+
NODE *ins;
|
257
|
+
|
258
|
+
if (list->first == NULL) { // List is empty
|
259
|
+
//if (!(ins = malloc(sizeof(NODE)))) return NULL;
|
260
|
+
ins = ALLOC(NODE);
|
261
|
+
ins->next = NULL;
|
262
|
+
ins->val = val;
|
263
|
+
ins->key = key;
|
264
|
+
list->first = ins;
|
265
|
+
return ins;
|
266
|
+
|
267
|
+
} else if (key < list->first->key) { // Goes at the beginning of the list
|
268
|
+
//if (!(ins = malloc(sizeof(NODE)))) return NULL;
|
269
|
+
ins = ALLOC(NODE);
|
270
|
+
ins->next = list->first;
|
271
|
+
ins->val = val;
|
272
|
+
ins->key = key;
|
273
|
+
list->first = ins;
|
274
|
+
return ins;
|
275
|
+
}
|
276
|
+
|
277
|
+
// Goes somewhere else in the list.
|
278
|
+
ins = list_find_nearest_from(list->first, key);
|
279
|
+
|
280
|
+
if (ins->key == key) {
|
281
|
+
// key already exists
|
282
|
+
if (replace) {
|
283
|
+
free(ins->val);
|
284
|
+
ins->val = val;
|
285
|
+
} else free(val);
|
286
|
+
return ins;
|
287
|
+
|
288
|
+
} else return list_insert_after(ins, key, val);
|
289
|
+
|
290
|
+
}
|
291
|
+
|
292
|
+
|
293
|
+
|
294
|
+
// TODO: Allow this function to accept an entire row and not just one value -- for slicing
|
295
|
+
void* list_storage_insert(LIST_STORAGE* s, size_t* coords, void* val) {
|
296
|
+
// Pretend ranks = 2
|
297
|
+
// Then coords is going to be size 2
|
298
|
+
// So we need to find out if some key already exists
|
299
|
+
size_t r;
|
300
|
+
NODE* n;
|
301
|
+
LIST* l = s->rows;
|
302
|
+
|
303
|
+
// drill down into the structure
|
304
|
+
for (r = s->rank; r > 1; --r) {
|
305
|
+
n = list_insert(l, false, coords[s->rank - r], create_list());
|
306
|
+
l = n->val;
|
307
|
+
}
|
308
|
+
|
309
|
+
n = list_insert(l, true, coords[s->rank - r], val);
|
310
|
+
return n->val;
|
311
|
+
}
|
312
|
+
|
313
|
+
// Creates a list-of-lists(-of-lists-of-lists-etc) storage framework for a matrix.
|
314
|
+
//
|
315
|
+
// Note: The pointers you pass in for shape and init_val become property of our new
|
316
|
+
// storage. You don't need to free them, and you shouldn't re-use them.
|
317
|
+
LIST_STORAGE* create_list_storage(int8_t dtype, size_t* shape, size_t rank, void* init_val) {
|
318
|
+
LIST_STORAGE* s;
|
319
|
+
|
320
|
+
s = ALLOC( LIST_STORAGE );
|
321
|
+
|
322
|
+
s->rank = rank;
|
323
|
+
s->shape = shape;
|
324
|
+
s->dtype = dtype;
|
325
|
+
|
326
|
+
s->rows = create_list();
|
327
|
+
|
328
|
+
s->default_val = init_val;
|
329
|
+
|
330
|
+
return s;
|
331
|
+
}
|
332
|
+
|
333
|
+
|
334
|
+
static void cast_copy_list_contents(LIST* lhs, LIST* rhs, int8_t lhs_dtype, int8_t rhs_dtype, size_t recursions) {
|
335
|
+
NODE *lcurr, *rcurr;
|
336
|
+
|
337
|
+
if (rhs->first) {
|
338
|
+
// copy head node
|
339
|
+
rcurr = rhs->first;
|
340
|
+
lcurr = lhs->first = ALLOC( NODE );
|
341
|
+
|
342
|
+
while (rcurr) {
|
343
|
+
lcurr->key = rcurr->key;
|
344
|
+
|
345
|
+
if (recursions == 0) { // contents is some kind of value
|
346
|
+
lcurr->val = ALLOC_N(char, nm_sizeof[lhs_dtype]);
|
347
|
+
//fprintf(stderr, " create_val: %p\n", lcurr->val);
|
348
|
+
|
349
|
+
if (lhs_dtype == rhs_dtype) memcpy(lcurr->val, rcurr->val, nm_sizeof[lhs_dtype]);
|
350
|
+
else SetFuncs[lhs_dtype][rhs_dtype](1, lcurr->val, 0, rcurr->val, 0);
|
351
|
+
|
352
|
+
} else { // contents is a list
|
353
|
+
lcurr->val = ALLOC( LIST );
|
354
|
+
//fprintf(stderr, " create_list: %p\n", lcurr->val);
|
355
|
+
|
356
|
+
cast_copy_list_contents(lcurr->val, rcurr->val, lhs_dtype, rhs_dtype, recursions-1);
|
357
|
+
}
|
358
|
+
if (rcurr->next) lcurr->next = ALLOC( NODE );
|
359
|
+
else lcurr->next = NULL;
|
360
|
+
|
361
|
+
lcurr = lcurr->next;
|
362
|
+
rcurr = rcurr->next;
|
363
|
+
}
|
364
|
+
} else {
|
365
|
+
lhs->first = NULL;
|
366
|
+
}
|
367
|
+
}
|
368
|
+
|
369
|
+
|
370
|
+
/* Deletes the linked list and all of its contents. If you want to delete a list inside of a list,
|
371
|
+
* set recursions to 1. For lists inside of lists inside of the list, set it to 2; and so on.
|
372
|
+
* Setting it to 0 is for no recursions.
|
373
|
+
*/
|
374
|
+
static void delete_list(LIST* list, size_t recursions) {
|
375
|
+
NODE* next;
|
376
|
+
NODE* curr = list->first;
|
377
|
+
|
378
|
+
while (curr != NULL) {
|
379
|
+
next = curr->next;
|
380
|
+
|
381
|
+
if (recursions == 0) {
|
382
|
+
//fprintf(stderr, " free_val: %p\n", curr->val);
|
383
|
+
free(curr->val);
|
384
|
+
} else {
|
385
|
+
//fprintf(stderr, " free_list: %p\n", list);
|
386
|
+
delete_list(curr->val, recursions-1);
|
387
|
+
}
|
388
|
+
|
389
|
+
free(curr);
|
390
|
+
curr = next;
|
391
|
+
}
|
392
|
+
//fprintf(stderr, " free_list: %p\n", list);
|
393
|
+
free(list);
|
394
|
+
}
|
395
|
+
|
396
|
+
|
397
|
+
// Copy dense into lists recursively
|
398
|
+
//
|
399
|
+
// TODO: This works, but could probably be cleaner (do we really need to pass coords around?)
|
400
|
+
static bool cast_copy_list_contents_dense(LIST* lhs, const char* rhs, void* zero, int8_t l_dtype, int8_t r_dtype, size_t* pos, size_t* coords, const size_t* shape, size_t rank, size_t recursions) {
|
401
|
+
NODE *prev;
|
402
|
+
LIST *sub_list;
|
403
|
+
bool added = false, added_list = false;
|
404
|
+
void* insert_value;
|
405
|
+
|
406
|
+
for (coords[rank-1-recursions] = 0; coords[rank-1-recursions] < shape[rank-1-recursions]; ++coords[rank-1-recursions], ++(*pos)) {
|
407
|
+
//fprintf(stderr, "(%u)\t<%u, %u>: ", recursions, coords[0], coords[1]);
|
408
|
+
|
409
|
+
if (recursions == 0) { // create nodes
|
410
|
+
if (memcmp((char*)rhs + (*pos)*nm_sizeof[r_dtype], zero, nm_sizeof[r_dtype])) { // is not zero
|
411
|
+
//fprintf(stderr, "inserting value\n");
|
412
|
+
|
413
|
+
// Create a copy of our value that we will insert in the list
|
414
|
+
insert_value = ALLOC_N(char, nm_sizeof[l_dtype]);
|
415
|
+
cast_copy_value_single(insert_value, rhs + (*pos)*nm_sizeof[r_dtype], l_dtype, r_dtype);
|
416
|
+
|
417
|
+
if (!lhs->first) prev = list_insert(lhs, false, coords[rank-1-recursions], insert_value);
|
418
|
+
else prev = list_insert_after(prev, coords[rank-1-recursions], insert_value);
|
419
|
+
added = true;
|
420
|
+
} //else fprintf(stderr, "zero\n");
|
421
|
+
// no need to do anything if the element is zero
|
422
|
+
} else { // create lists
|
423
|
+
//fprintf(stderr, "inserting list\n");
|
424
|
+
// create a list as if there's something in the row in question, and then delete it if nothing turns out to be there
|
425
|
+
sub_list = create_list();
|
426
|
+
|
427
|
+
added_list = cast_copy_list_contents_dense(sub_list, rhs, zero, l_dtype, r_dtype, pos, coords, shape, rank, recursions-1);
|
428
|
+
|
429
|
+
if (!added_list) { delete_list(sub_list, recursions-1); fprintf(stderr, "deleting list\n"); }// nothing added
|
430
|
+
else if (!lhs->first) prev = list_insert(lhs, false, coords[rank-1-recursions], sub_list);
|
431
|
+
else prev = list_insert_after(prev, coords[rank-1-recursions], sub_list);
|
432
|
+
|
433
|
+
// added = (added || added_list);
|
434
|
+
|
435
|
+
}
|
436
|
+
}
|
437
|
+
|
438
|
+
coords[rank-1-recursions] = 0;
|
439
|
+
--(*pos);
|
440
|
+
|
441
|
+
return added;
|
442
|
+
}
|
443
|
+
|
444
|
+
|
445
|
+
|
446
|
+
LIST_STORAGE* copy_list_storage(LIST_STORAGE* rhs) {
|
447
|
+
LIST_STORAGE* lhs;
|
448
|
+
size_t* shape;
|
449
|
+
void* default_val = ALLOC_N(char, nm_sizeof[rhs->dtype]);
|
450
|
+
|
451
|
+
//fprintf(stderr, "copy_list_storage\n");
|
452
|
+
|
453
|
+
// allocate and copy shape
|
454
|
+
shape = ALLOC_N(size_t, rhs->rank);
|
455
|
+
memcpy(shape, rhs->shape, rhs->rank * sizeof(size_t));
|
456
|
+
memcpy(default_val, rhs->default_val, nm_sizeof[rhs->dtype]);
|
457
|
+
|
458
|
+
lhs = create_list_storage(rhs->dtype, shape, rhs->rank, default_val);
|
459
|
+
|
460
|
+
if (lhs) {
|
461
|
+
lhs->rows = create_list();
|
462
|
+
cast_copy_list_contents(lhs->rows, rhs->rows, rhs->dtype, rhs->dtype, rhs->rank - 1);
|
463
|
+
} else free(shape);
|
464
|
+
|
465
|
+
return lhs;
|
466
|
+
}
|
467
|
+
|
468
|
+
|
469
|
+
LIST_STORAGE* cast_copy_list_storage(LIST_STORAGE* rhs, int8_t new_dtype) {
|
470
|
+
LIST_STORAGE* lhs;
|
471
|
+
size_t* shape;
|
472
|
+
void* default_val = ALLOC_N(char, nm_sizeof[rhs->dtype]);
|
473
|
+
|
474
|
+
//fprintf(stderr, "copy_list_storage\n");
|
475
|
+
|
476
|
+
// allocate and copy shape
|
477
|
+
shape = ALLOC_N(size_t, rhs->rank);
|
478
|
+
memcpy(shape, rhs->shape, rhs->rank * sizeof(size_t));
|
479
|
+
|
480
|
+
// copy default value
|
481
|
+
if (new_dtype == rhs->dtype) memcpy(default_val, rhs->default_val, nm_sizeof[rhs->dtype]);
|
482
|
+
else SetFuncs[new_dtype][rhs->dtype](1, default_val, 0, rhs->default_val, 0);
|
483
|
+
|
484
|
+
lhs = create_list_storage(new_dtype, shape, rhs->rank, default_val);
|
485
|
+
|
486
|
+
lhs->rows = create_list();
|
487
|
+
cast_copy_list_contents(lhs->rows, rhs->rows, new_dtype, rhs->dtype, rhs->rank - 1);
|
488
|
+
|
489
|
+
return lhs;
|
490
|
+
}
|
491
|
+
|
492
|
+
|
493
|
+
|
494
|
+
LIST_STORAGE* scast_copy_list_dense(const DENSE_STORAGE* rhs, int8_t l_dtype) {
|
495
|
+
LIST_STORAGE* lhs;
|
496
|
+
size_t pos = 0;
|
497
|
+
void* l_default_val = ALLOC_N(char, nm_sizeof[l_dtype]);
|
498
|
+
void* r_default_val = ALLOCA_N(char, nm_sizeof[rhs->dtype]); // clean up when finished with this function
|
499
|
+
|
500
|
+
// allocate and copy shape and coords
|
501
|
+
size_t *shape = ALLOC_N(size_t, rhs->rank), *coords = ALLOC_N(size_t, rhs->rank);
|
502
|
+
memcpy(shape, rhs->shape, rhs->rank * sizeof(size_t));
|
503
|
+
memset(coords, 0, rhs->rank * sizeof(size_t));
|
504
|
+
|
505
|
+
// set list default_val to 0
|
506
|
+
if (l_dtype == NM_ROBJ) *(VALUE*)l_default_val = INT2FIX(0);
|
507
|
+
else memset(l_default_val, 0, nm_sizeof[l_dtype]);
|
508
|
+
|
509
|
+
// need test default value for comparing to elements in dense matrix
|
510
|
+
if (rhs->dtype == l_dtype) r_default_val = l_default_val;
|
511
|
+
else if (rhs->dtype == NM_ROBJ) *(VALUE*)r_default_val = INT2FIX(0);
|
512
|
+
else memset(r_default_val, 0, nm_sizeof[rhs->dtype]);
|
513
|
+
|
514
|
+
lhs = create_list_storage(l_dtype, shape, rhs->rank, l_default_val);
|
515
|
+
|
516
|
+
lhs->rows = create_list();
|
517
|
+
cast_copy_list_contents_dense(lhs->rows, rhs->elements, r_default_val, l_dtype, rhs->dtype, &pos, coords, rhs->shape, rhs->rank, rhs->rank - 1);
|
518
|
+
|
519
|
+
return lhs;
|
520
|
+
}
|
521
|
+
|
522
|
+
|
523
|
+
LIST_STORAGE* scast_copy_list_yale(const YALE_STORAGE* rhs, int8_t l_dtype) {
|
524
|
+
LIST_STORAGE* lhs;
|
525
|
+
NODE *last_added, *last_row_added = NULL;
|
526
|
+
LIST* curr_row;
|
527
|
+
y_size_t ija, ija_next, i, jj;
|
528
|
+
bool add_diag;
|
529
|
+
void* default_val = ALLOC_N(char, nm_sizeof[l_dtype]);
|
530
|
+
void* R_ZERO = (char*)(rhs->a) + rhs->shape[0]*nm_sizeof[rhs->dtype];
|
531
|
+
void* insert_val;
|
532
|
+
|
533
|
+
// allocate and copy shape
|
534
|
+
size_t *shape = ALLOC_N(size_t, rhs->rank);
|
535
|
+
shape[0] = rhs->shape[0]; shape[1] = rhs->shape[1];
|
536
|
+
|
537
|
+
// copy default value from the zero location in the Yale matrix
|
538
|
+
SetFuncs[l_dtype][rhs->dtype](1, default_val, 0, R_ZERO, 0);
|
539
|
+
|
540
|
+
lhs = create_list_storage(l_dtype, shape, rhs->rank, default_val);
|
541
|
+
|
542
|
+
if (rhs->rank != 2)
|
543
|
+
rb_raise(nm_eStorageTypeError, "can only convert matrices of rank 2 from yale");
|
544
|
+
|
545
|
+
// Walk through rows and columns as if RHS were a dense matrix
|
546
|
+
for (i = 0; i < rhs->shape[0]; ++i) {
|
547
|
+
|
548
|
+
// Get boundaries of beginning and end of row
|
549
|
+
YaleGetIJA(ija, rhs, i);
|
550
|
+
YaleGetIJA(ija_next, rhs, i+1);
|
551
|
+
|
552
|
+
// Are we going to need to add a diagonal for this row?
|
553
|
+
if (!memcmp((char*)(rhs->a) + i*nm_sizeof[rhs->dtype], R_ZERO, nm_sizeof[rhs->dtype])) add_diag = false; // zero
|
554
|
+
else add_diag = true; // nonzero diagonal
|
555
|
+
|
556
|
+
|
557
|
+
if (ija < ija_next || add_diag) {
|
558
|
+
|
559
|
+
curr_row = create_list();
|
560
|
+
last_added = NULL;
|
561
|
+
|
562
|
+
while (ija < ija_next) {
|
563
|
+
YaleGetIJA(jj, rhs, ija); // what column number is this?
|
564
|
+
|
565
|
+
// Is there a nonzero diagonal item between the previously added item and the current one?
|
566
|
+
if (jj > i && add_diag) {
|
567
|
+
// Allocate and copy insertion value
|
568
|
+
insert_val = ALLOC_N(char, nm_sizeof[l_dtype]);
|
569
|
+
SetFuncs[l_dtype][rhs->dtype](1, insert_val, 0, (char*)(rhs->a) + i*nm_sizeof[rhs->dtype], 0);
|
570
|
+
|
571
|
+
// insert the item in the list at the appropriate location
|
572
|
+
if (last_added) last_added = list_insert_after(last_added, i, insert_val);
|
573
|
+
else last_added = list_insert(curr_row, false, i, insert_val);
|
574
|
+
|
575
|
+
add_diag = false; // don't add again!
|
576
|
+
}
|
577
|
+
|
578
|
+
// now allocate and add the current item
|
579
|
+
insert_val = ALLOC_N(char, nm_sizeof[l_dtype]);
|
580
|
+
SetFuncs[l_dtype][rhs->dtype](1, insert_val, 0, (char*)(rhs->a) + ija*nm_sizeof[rhs->dtype], 0);
|
581
|
+
|
582
|
+
if (last_added) last_added = list_insert_after(last_added, jj, insert_val);
|
583
|
+
else last_added = list_insert(curr_row, false, jj, insert_val);
|
584
|
+
|
585
|
+
++ija; // move to next entry in Yale matrix
|
586
|
+
}
|
587
|
+
|
588
|
+
if (add_diag) { // still haven't added the diagonal.
|
589
|
+
insert_val = ALLOC_N(char, nm_sizeof[l_dtype]);
|
590
|
+
SetFuncs[l_dtype][rhs->dtype](1, insert_val, 0, (char*)(rhs->a) + i*nm_sizeof[rhs->dtype], 0);
|
591
|
+
|
592
|
+
// insert the item in the list at the appropriate location
|
593
|
+
if (last_added) last_added = list_insert_after(last_added, i, insert_val);
|
594
|
+
else last_added = list_insert(curr_row, false, i, insert_val);
|
595
|
+
}
|
596
|
+
|
597
|
+
// Now add the list at the appropriate location
|
598
|
+
if (last_row_added) last_row_added = list_insert_after(last_row_added, i, curr_row);
|
599
|
+
else last_row_added = list_insert(lhs->rows, false, i, curr_row);
|
600
|
+
}
|
601
|
+
|
602
|
+
} // end of walk through rows
|
603
|
+
|
604
|
+
return lhs;
|
605
|
+
}
|
606
|
+
|
607
|
+
|
608
|
+
|
609
|
+
// Do all values in a list == some value?
|
610
|
+
static bool list_eqeq_value(const LIST* l, const void* v, size_t value_size, size_t recursions, size_t* checked) {
|
611
|
+
NODE *next, *curr = l->first;
|
612
|
+
|
613
|
+
while (curr) {
|
614
|
+
next = curr->next;
|
615
|
+
|
616
|
+
if (recursions == 0) {
|
617
|
+
++(*checked);
|
618
|
+
if (memcmp(curr->val, v, value_size)) return false;
|
619
|
+
} else if (!list_eqeq_value(curr->val, v, value_size, recursions-1, checked))
|
620
|
+
return false;
|
621
|
+
|
622
|
+
curr = next;
|
623
|
+
}
|
624
|
+
return true;
|
625
|
+
}
|
626
|
+
|
627
|
+
|
628
|
+
// Are all values in the two lists equal? If one is missing a value, but the other isn't, does the value in the list match
|
629
|
+
// the default value?
|
630
|
+
static bool list_eqeq_list(const LIST* left, const LIST* right, const void* left_val, const void* right_val, size_t value_size, size_t recursions, size_t* checked) {
|
631
|
+
NODE *lnext, *lcurr = left->first, *rnext, *rcurr = right->first;
|
632
|
+
|
633
|
+
//fprintf(stderr, "list_eqeq_list: recursions=%d\n", recursions);
|
634
|
+
|
635
|
+
if (lcurr) lnext = lcurr->next;
|
636
|
+
if (rcurr) rnext = rcurr->next;
|
637
|
+
|
638
|
+
while (lcurr && rcurr) {
|
639
|
+
|
640
|
+
if (lcurr->key == rcurr->key) { // MATCHING KEYS
|
641
|
+
if (recursions == 0) {
|
642
|
+
++(*checked);
|
643
|
+
if (memcmp(lcurr->val, rcurr->val, value_size)) return false;
|
644
|
+
} else if (!list_eqeq_list(lcurr->val, rcurr->val, left_val, right_val, value_size, recursions-1, checked))
|
645
|
+
return false;
|
646
|
+
|
647
|
+
// increment both iterators
|
648
|
+
rcurr = rnext;
|
649
|
+
if (rcurr) rnext = rcurr->next;
|
650
|
+
lcurr = lnext;
|
651
|
+
if (lcurr) lnext = lcurr->next;
|
652
|
+
|
653
|
+
} else if (lcurr->key < rcurr->key) { // NON-MATCHING KEYS
|
654
|
+
|
655
|
+
if (recursions == 0) {
|
656
|
+
// compare left entry to right default value
|
657
|
+
++(*checked);
|
658
|
+
if (memcmp(lcurr->val, right_val, value_size)) return false;
|
659
|
+
} else if (!list_eqeq_value(lcurr->val, right_val, value_size, recursions-1, checked))
|
660
|
+
return false;
|
661
|
+
|
662
|
+
// increment left iterator
|
663
|
+
lcurr = lnext;
|
664
|
+
if (lcurr) lnext = lcurr->next;
|
665
|
+
|
666
|
+
} else { // if (rcurr->key < lcurr->key)
|
667
|
+
|
668
|
+
if (recursions == 0) {
|
669
|
+
// compare right entry to left default value
|
670
|
+
++(*checked);
|
671
|
+
if (memcmp(rcurr->val, left_val, value_size)) return false;
|
672
|
+
} else if (!list_eqeq_value(rcurr->val, left_val, value_size, recursions-1, checked))
|
673
|
+
return false;
|
674
|
+
|
675
|
+
// increment right iterator
|
676
|
+
rcurr = rnext;
|
677
|
+
if (rcurr) rnext = rcurr->next;
|
678
|
+
}
|
679
|
+
|
680
|
+
}
|
681
|
+
|
682
|
+
// One final check, in case we get to the end of one list but not the other one.
|
683
|
+
if (lcurr) { // nothing left in right-hand list
|
684
|
+
if (memcmp(lcurr->val, right_val, value_size)) return false;
|
685
|
+
} else if (rcurr) { // nothing left in left-hand list
|
686
|
+
if (memcmp(rcurr->val, left_val, value_size)) return false;
|
687
|
+
}
|
688
|
+
|
689
|
+
// Nothing different between the two lists -- but make sure after this return that you compare the default values themselves,
|
690
|
+
// if we haven't visited every value in the two matrices.
|
691
|
+
return true;
|
692
|
+
}
|
693
|
+
|
694
|
+
|
695
|
+
// Do these two dense matrices of the same dtype have exactly the same contents?
|
696
|
+
bool list_storage_eqeq(const LIST_STORAGE* left, const LIST_STORAGE* right) {
|
697
|
+
|
698
|
+
// in certain cases, we need to keep track of the number of elements checked.
|
699
|
+
size_t num_checked = 0,
|
700
|
+
max_elements = count_storage_max_elements((STORAGE*)left),
|
701
|
+
sz = nm_sizeof[left->dtype];
|
702
|
+
|
703
|
+
if (!left->rows->first) {
|
704
|
+
// fprintf(stderr, "!left->rows true\n");
|
705
|
+
// Easy: both lists empty -- just compare default values
|
706
|
+
if (!right->rows->first) return !memcmp(left->default_val, right->default_val, sz);
|
707
|
+
|
708
|
+
// Left empty, right not empty. Do all values in right == left->default_val?
|
709
|
+
if (!list_eqeq_value(right->rows, left->default_val, sz, left->rank-1, &num_checked)) return false;
|
710
|
+
|
711
|
+
// If the matrix isn't full, we also need to compare default values.
|
712
|
+
if (num_checked < max_elements) return !memcmp(left->default_val, right->default_val, sz);
|
713
|
+
|
714
|
+
} else if (!right->rows->first) {
|
715
|
+
// fprintf(stderr, "!right->rows true\n");
|
716
|
+
// Right empty, left not empty. Do all values in left == right->default_val?
|
717
|
+
if (!list_eqeq_value(left->rows, right->default_val, sz, left->rank-1, &num_checked)) return false;
|
718
|
+
|
719
|
+
// If the matrix isn't full, we also need to compare default values.
|
720
|
+
if (num_checked < max_elements) return !memcmp(left->default_val, right->default_val, sz);
|
721
|
+
|
722
|
+
} else {
|
723
|
+
// fprintf(stderr, "both matrices have entries\n");
|
724
|
+
// Hardest case. Compare lists node by node. Let's make it simpler by requiring that both have the same default value
|
725
|
+
if (!list_eqeq_list(left->rows, right->rows, left->default_val, right->default_val, sz, left->rank-1, &num_checked)) return false;
|
726
|
+
if (num_checked < max_elements) return !memcmp(left->default_val, right->default_val, sz);
|
727
|
+
}
|
728
|
+
|
729
|
+
return true;
|
730
|
+
}
|
731
|
+
|
732
|
+
|
733
|
+
void delete_list_storage(LIST_STORAGE* s) {
|
734
|
+
if (s) {
|
735
|
+
//fprintf(stderr, "* Deleting list storage rows at %p\n", s->rows);
|
736
|
+
delete_list( s->rows, s->rank - 1 );
|
737
|
+
|
738
|
+
//fprintf(stderr, " Deleting list storage shape at %p\n", s->shape);
|
739
|
+
free(s->shape);
|
740
|
+
//fprintf(stderr, " Deleting list storage default_val at %p\n", s->default_val);
|
741
|
+
free(s->default_val);
|
742
|
+
//fprintf(stderr, " Deleting list storage at %p\n", s);
|
743
|
+
free(s);
|
744
|
+
}
|
745
|
+
}
|
746
|
+
|
747
|
+
|
748
|
+
static void mark_list(LIST* list, size_t recursions) {
|
749
|
+
NODE* next;
|
750
|
+
NODE* curr = list->first;
|
751
|
+
|
752
|
+
while (curr != NULL) {
|
753
|
+
next = curr->next;
|
754
|
+
if (recursions == 0) rb_gc_mark(*((VALUE*)(curr->val)));
|
755
|
+
else mark_list(curr->val, recursions-1);
|
756
|
+
curr = next;
|
757
|
+
}
|
758
|
+
}
|
759
|
+
|
760
|
+
|
761
|
+
void mark_list_storage(void* m) {
|
762
|
+
LIST_STORAGE* storage;
|
763
|
+
|
764
|
+
if (m) {
|
765
|
+
storage = (LIST_STORAGE*)(((NMATRIX*)m)->storage);
|
766
|
+
if (storage && storage->dtype == NM_ROBJ) {
|
767
|
+
rb_gc_mark(*((VALUE*)(storage->default_val)));
|
768
|
+
mark_list(storage->rows, storage->rank - 1);
|
769
|
+
}
|
770
|
+
}
|
771
|
+
}
|
772
|
+
|
773
|
+
|
774
|
+
#endif
|