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.
Files changed (43) hide show
  1. data/.autotest +23 -0
  2. data/.gemtest +0 -0
  3. data/Gemfile +7 -0
  4. data/History.txt +6 -0
  5. data/LICENSE.txt +21 -0
  6. data/Manifest.txt +51 -0
  7. data/README.rdoc +63 -0
  8. data/Rakefile +154 -0
  9. data/ext/nmatrix/cblas.c +150 -0
  10. data/ext/nmatrix/dense.c +307 -0
  11. data/ext/nmatrix/dense/blas_header.template.c +52 -0
  12. data/ext/nmatrix/dense/elementwise.template.c +107 -0
  13. data/ext/nmatrix/dense/gemm.template.c +159 -0
  14. data/ext/nmatrix/dense/gemv.template.c +130 -0
  15. data/ext/nmatrix/dense/rationalmath.template.c +68 -0
  16. data/ext/nmatrix/depend +18 -0
  17. data/ext/nmatrix/extconf.rb +143 -0
  18. data/ext/nmatrix/generator.rb +594 -0
  19. data/ext/nmatrix/generator/syntax_tree.rb +481 -0
  20. data/ext/nmatrix/list.c +774 -0
  21. data/ext/nmatrix/nmatrix.c +1977 -0
  22. data/ext/nmatrix/nmatrix.h +912 -0
  23. data/ext/nmatrix/rational.c +98 -0
  24. data/ext/nmatrix/yale.c +726 -0
  25. data/ext/nmatrix/yale/complexmath.template.c +71 -0
  26. data/ext/nmatrix/yale/elementwise.template.c +46 -0
  27. data/ext/nmatrix/yale/elementwise_op.template.c +73 -0
  28. data/ext/nmatrix/yale/numbmm.template.c +94 -0
  29. data/ext/nmatrix/yale/smmp1.template.c +21 -0
  30. data/ext/nmatrix/yale/smmp1_header.template.c +38 -0
  31. data/ext/nmatrix/yale/smmp2.template.c +43 -0
  32. data/ext/nmatrix/yale/smmp2_header.template.c +46 -0
  33. data/ext/nmatrix/yale/sort_columns.template.c +56 -0
  34. data/ext/nmatrix/yale/symbmm.template.c +54 -0
  35. data/ext/nmatrix/yale/transp.template.c +68 -0
  36. data/lib/array.rb +67 -0
  37. data/lib/nmatrix.rb +263 -0
  38. data/lib/string.rb +65 -0
  39. data/spec/nmatrix_spec.rb +395 -0
  40. data/spec/nmatrix_yale_spec.rb +239 -0
  41. data/spec/nvector_spec.rb +43 -0
  42. data/spec/syntax_tree_spec.rb +46 -0
  43. metadata +150 -0
@@ -0,0 +1,307 @@
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
+ // == dense.c
25
+ //
26
+ // Dense n-dimensional matrix storage.
27
+
28
+ #ifndef DENSE_C
29
+ #define DENSE_C
30
+
31
+ #include <ruby.h>
32
+
33
+ #include "nmatrix.h"
34
+
35
+
36
+ /* Calculate the number of elements in the dense storage structure, based on shape and rank */
37
+ size_t count_dense_storage_elements(const DENSE_STORAGE* s) {
38
+ size_t i;
39
+ size_t count = 1;
40
+ for (i = 0; i < s->rank; ++i) count *= s->shape[i];
41
+ return count;
42
+ }
43
+
44
+
45
+ // Do these two dense matrices of the same dtype have exactly the same contents?
46
+ bool dense_storage_eqeq(const DENSE_STORAGE* left, const DENSE_STORAGE* right) {
47
+ return !memcmp(left->elements, right->elements, count_dense_storage_elements(left) / nm_sizeof[left->dtype]);
48
+ }
49
+
50
+
51
+ size_t dense_storage_pos(DENSE_STORAGE* s, size_t* coords) {
52
+ size_t k, l;
53
+ size_t inner, outer = 0;
54
+ for (k = 0; k < s->rank; ++k) {
55
+ inner = coords[k];
56
+ for (l = k+1; l < s->rank; ++l) {
57
+ inner *= s->shape[l];
58
+ }
59
+ outer += inner;
60
+ }
61
+ return outer;
62
+ }
63
+
64
+
65
+
66
+ void* dense_storage_get(DENSE_STORAGE* s, size_t* coords) {
67
+ return (char*)(s->elements) + dense_storage_pos(s, coords) * nm_sizeof[s->dtype];
68
+ }
69
+
70
+
71
+ /* Does not free passed-in value! Different from list_storage_insert. */
72
+ void dense_storage_set(DENSE_STORAGE* s, size_t* coords, void* val) {
73
+ memcpy((char*)(s->elements) + dense_storage_pos(s, coords) * nm_sizeof[s->dtype], val, nm_sizeof[s->dtype]);
74
+ }
75
+
76
+
77
+ DENSE_STORAGE* copy_dense_storage(DENSE_STORAGE* rhs) {
78
+ DENSE_STORAGE* lhs;
79
+ size_t count = count_dense_storage_elements(rhs), p;
80
+ size_t* shape = ALLOC_N(size_t, rhs->rank);
81
+ if (!shape) return NULL;
82
+
83
+ // copy shape array
84
+ for (p = 0; p < rhs->rank; ++p)
85
+ shape[p] = rhs->shape[p];
86
+
87
+ lhs = create_dense_storage(rhs->dtype, shape, rhs->rank, NULL, 0);
88
+
89
+ if (lhs && count) // ensure that allocation worked before copying
90
+ memcpy(lhs->elements, rhs->elements, nm_sizeof[rhs->dtype] * count);
91
+
92
+ return lhs;
93
+ }
94
+
95
+
96
+ DENSE_STORAGE* cast_copy_dense_storage(DENSE_STORAGE* rhs, int8_t new_dtype) {
97
+ DENSE_STORAGE* lhs;
98
+ size_t count = count_dense_storage_elements(rhs), p;
99
+ size_t* shape = ALLOC_N(size_t, rhs->rank);
100
+ if (!shape) return NULL;
101
+
102
+ // copy shape array
103
+ for (p = 0; p < rhs->rank; ++p) shape[p] = rhs->shape[p];
104
+
105
+ lhs = create_dense_storage(new_dtype, shape, rhs->rank, NULL, 0);
106
+
107
+ if (lhs && count) // ensure that allocation worked before copying
108
+ if (lhs->dtype == rhs->dtype)
109
+ memcpy(lhs->elements, rhs->elements, nm_sizeof[rhs->dtype] * count);
110
+ else
111
+ SetFuncs[lhs->dtype][rhs->dtype](count, lhs->elements, nm_sizeof[lhs->dtype], rhs->elements, nm_sizeof[rhs->dtype]);
112
+
113
+
114
+ return lhs;
115
+ }
116
+
117
+
118
+
119
+ // Copy a set of default values into dense
120
+ static inline void cast_copy_dense_list_default(void* lhs, void* default_val, int8_t l_dtype, int8_t r_dtype, size_t* pos, const size_t* shape, size_t rank, size_t max_elements, size_t recursions) {
121
+ size_t i;
122
+
123
+ for (i = 0; i < shape[rank-1-recursions]; ++i, ++(*pos)) {
124
+ //fprintf(stderr, "default: pos = %u, dim = %u\t", *pos, shape[rank-1-recursions]);
125
+
126
+ if (recursions == 0) { cast_copy_value_single((char*)lhs + (*pos)*nm_sizeof[l_dtype], default_val, l_dtype, r_dtype); fprintf(stderr, "zero\n"); }
127
+ else { cast_copy_dense_list_default(lhs, default_val, l_dtype, r_dtype, pos, shape, rank, max_elements, recursions-1); fprintf(stderr, "column of zeros\n"); }
128
+ }
129
+ --(*pos);
130
+ }
131
+
132
+
133
+ // Copy list contents into dense recursively
134
+ static void cast_copy_dense_list_contents(void* lhs, const LIST* rhs, void* default_val, int8_t l_dtype, int8_t r_dtype, size_t* pos, const size_t* shape, size_t rank, size_t max_elements, size_t recursions) {
135
+ NODE *curr = rhs->first;
136
+ int last_key = -1;
137
+ size_t i = 0;
138
+
139
+ for (i = 0; i < shape[rank-1-recursions]; ++i, ++(*pos)) {
140
+
141
+ if (!curr || (curr->key > (size_t)(last_key+1))) {
142
+ //fprintf(stderr, "pos = %u, dim = %u, curr->key XX, last_key+1 = %d\t", *pos, shape[rank-1-recursions], last_key+1);
143
+ if (recursions == 0) cast_copy_value_single((char*)lhs + (*pos)*nm_sizeof[l_dtype], default_val, l_dtype, r_dtype); //fprintf(stderr, "zero\n"); }
144
+ else cast_copy_dense_list_default(lhs, default_val, l_dtype, r_dtype, pos, shape, rank, max_elements, recursions-1); //fprintf(stderr, "column of zeros\n"); }
145
+
146
+ ++last_key;
147
+ } else {
148
+ //fprintf(stderr, "pos = %u, dim = %u, curr->key = %u, last_key+1 = %d\t", *pos, shape[rank-1-recursions], curr->key, last_key+1);
149
+ if (recursions == 0) cast_copy_value_single((char*)lhs + (*pos)*nm_sizeof[l_dtype], curr->val, l_dtype, r_dtype); //fprintf(stderr, "value\n"); }
150
+ else cast_copy_dense_list_contents(lhs, curr->val, default_val, l_dtype, r_dtype, pos, shape, rank, max_elements, recursions-1); //fprintf(stderr, "column of values\n"); }
151
+
152
+ last_key = curr->key;
153
+ curr = curr->next;
154
+ }
155
+ }
156
+ --(*pos);
157
+ }
158
+
159
+
160
+ // Convert (by creating a copy) from list storage to dense storage.
161
+ DENSE_STORAGE* scast_copy_dense_list(const LIST_STORAGE* rhs, int8_t l_dtype) {
162
+ DENSE_STORAGE* lhs;
163
+ size_t pos = 0; // position in lhs->elements
164
+
165
+ // allocate and copy shape
166
+ size_t* shape = ALLOC_N(size_t, rhs->rank);
167
+ memcpy(shape, rhs->shape, rhs->rank * sizeof(size_t));
168
+
169
+ lhs = create_dense_storage(l_dtype, shape, rhs->rank, NULL, 0);
170
+
171
+ // recursively copy the contents
172
+ cast_copy_dense_list_contents(lhs->elements, rhs->rows, rhs->default_val, l_dtype, rhs->dtype, &pos, shape, lhs->rank, count_storage_max_elements((STORAGE*)rhs), rhs->rank-1);
173
+
174
+ return lhs;
175
+ }
176
+
177
+
178
+ DENSE_STORAGE* scast_copy_dense_yale(const YALE_STORAGE* rhs, int8_t l_dtype) {
179
+ DENSE_STORAGE* lhs;
180
+ y_size_t i, j, // position in lhs->elements
181
+ ija, ija_next, jj; // position in rhs->elements
182
+ y_size_t pos = 0; // position in dense to write to
183
+ void* R_ZERO = (char*)(rhs->a) + rhs->shape[0] * nm_sizeof[rhs->dtype]; // determine zero representation
184
+
185
+ // allocate and set shape
186
+ size_t* shape = ALLOC_N(size_t, rhs->rank);
187
+ memcpy(shape, rhs->shape, rhs->rank * sizeof(size_t));
188
+
189
+ lhs = create_dense_storage(l_dtype, shape, rhs->rank, NULL, 0);
190
+
191
+ // Walk through rows. For each entry we set in dense, increment pos.
192
+ for (i = 0; i < rhs->shape[0]; ++i) {
193
+
194
+ // get boundaries of this row, store in ija and ija_next
195
+ YaleGetIJA(ija, rhs, i);
196
+ YaleGetIJA(ija_next, rhs, i+1);
197
+
198
+ if (ija == ija_next) { // row is empty?
199
+
200
+ for (j = 0; j < rhs->shape[1]; ++j) { // write zeros in each column
201
+
202
+ // Fill in zeros (except for diagonal)
203
+ if (i == j) cast_copy_value_single((char*)(lhs->elements) + pos*nm_sizeof[l_dtype], (char*)(rhs->a) + i*nm_sizeof[rhs->dtype], l_dtype, rhs->dtype);
204
+ else cast_copy_value_single((char*)(lhs->elements) + pos*nm_sizeof[l_dtype], R_ZERO, l_dtype, rhs->dtype);
205
+
206
+ ++pos; // move to next dense position
207
+ }
208
+
209
+ } else {
210
+ // row contains entries: write those in each column, interspersed with zeros
211
+ YaleGetIJA(jj, rhs, ija);
212
+
213
+ for (j = 0; j < rhs->shape[1]; ++j) {
214
+ if (i == j) {
215
+
216
+ cast_copy_value_single((char*)(lhs->elements) + pos*nm_sizeof[l_dtype], (char*)(rhs->a) + i*nm_sizeof[rhs->dtype], l_dtype, rhs->dtype);
217
+
218
+ } else if (j == jj) {
219
+
220
+ // copy from rhs
221
+ cast_copy_value_single((char*)(lhs->elements) + pos*nm_sizeof[l_dtype], (char*)(rhs->a) + ija*nm_sizeof[rhs->dtype], l_dtype, rhs->dtype);
222
+
223
+ // get next
224
+ ++ija;
225
+
226
+ // increment to next column ID (or go off the end)
227
+ if (ija < ija_next) YaleGetIJA(jj, rhs, ija);
228
+ else jj = rhs->shape[1];
229
+
230
+ } else { // j < jj
231
+
232
+ // insert zero
233
+ cast_copy_value_single((char*)(lhs->elements) + pos*nm_sizeof[l_dtype], R_ZERO, l_dtype, rhs->dtype);
234
+ }
235
+ ++pos; // move to next dense position
236
+ }
237
+ }
238
+ }
239
+
240
+ return lhs;
241
+ }
242
+
243
+
244
+ // Note that elements and elements_length are for initial value(s) passed in. If they are the correct length, they will
245
+ // be used directly. If not, they will be concatenated over and over again into a new elements array. If elements is NULL,
246
+ // the new elements array will not be initialized.
247
+ DENSE_STORAGE* create_dense_storage(int8_t dtype, size_t* shape, size_t rank, void* elements, size_t elements_length) {
248
+ DENSE_STORAGE* s;
249
+ size_t count, i, copy_length = elements_length;
250
+
251
+ s = ALLOC( DENSE_STORAGE );
252
+ //if (!(s = malloc(sizeof(DENSE_STORAGE)))) return NULL;
253
+
254
+ s->rank = rank;
255
+ s->shape = shape;
256
+ s->dtype = dtype;
257
+
258
+ //fprintf(stderr, "create_dense_storage: %p\n", s);
259
+
260
+ count = count_dense_storage_elements(s);
261
+ //fprintf(stderr, "count_dense_storage_elements: %d\n", count);
262
+
263
+ if (elements_length == count) s->elements = elements;
264
+ else {
265
+ s->elements = ALLOC_N(char, nm_sizeof[dtype]*count);
266
+
267
+ if (elements_length > 0) {
268
+ // repeat elements over and over again until the end of the matrix
269
+ for (i = 0; i < count; i += elements_length) {
270
+ if (i + elements_length > count) copy_length = count - i;
271
+ memcpy((char*)(s->elements)+i*nm_sizeof[dtype], (char*)(elements)+(i % elements_length)*nm_sizeof[dtype], copy_length*nm_sizeof[dtype]);
272
+ }
273
+
274
+ // get rid of the init_val
275
+ free(elements);
276
+ }
277
+ }
278
+
279
+ return s;
280
+ }
281
+
282
+
283
+ void delete_dense_storage(DENSE_STORAGE* s) {
284
+ if (s) { // sometimes Ruby passes in NULL storage for some reason (probably on copy construction failure)
285
+ free(s->shape);
286
+ free(s->elements);
287
+ free(s);
288
+ }
289
+ }
290
+
291
+
292
+ void mark_dense_storage(void* m) {
293
+ size_t i;
294
+ DENSE_STORAGE* storage;
295
+
296
+ if (m) {
297
+ storage = (DENSE_STORAGE*)(((NMATRIX*)m)->storage);
298
+ //fprintf(stderr, "mark_dense_storage\n");
299
+ if (storage && storage->dtype == NM_ROBJ)
300
+ for (i = 0; i < count_dense_storage_elements(storage); ++i)
301
+ rb_gc_mark(*((VALUE*)((char*)(storage->elements) + i*nm_sizeof[NM_ROBJ])));
302
+ }
303
+ }
304
+
305
+
306
+
307
+ #endif
@@ -0,0 +1,52 @@
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
+ // == blas.c
25
+ //
26
+ // blas.c is automatically generated by generator.rb. Do not modify
27
+ // it directly!
28
+ //
29
+ // This file contains C ports of BLAS functions that work on integer
30
+ // types and eventually rationals. You can find the original template
31
+ // in ext/nmatrix/blas/igemm.template.c.
32
+ //
33
+ // The port was accomplished using f2c (on dgemm.f) and a magnifying
34
+ // glass.
35
+ //
36
+ // Note that this is in no way comparable to ATLAS. Why?
37
+ // * We didn't test any options other than CblasNoTrans (for A and B)
38
+ // * It doesn't use any of that cool cross-over crap (e.g.,
39
+ // Strassen's algorithm)
40
+ // * It really hasn't been tested exhaustively in any way, shape, or
41
+ // form.
42
+ //
43
+ // This file also contains rational math helper functions so that the
44
+ // aforementioned port will work with rationals too. These are in
45
+ // ext/nmatrix/blas/rationalmath.template.c. They are derived from
46
+ // rational.c in Ruby 1.9.3.
47
+
48
+ #include "nmatrix.h"
49
+
50
+
51
+
52
+
@@ -0,0 +1,107 @@
1
+
2
+ int nm_d_%%TYPE_ABBREV%%_elementwise(const %%TYPE%%* A, const %%TYPE%%* B, %%TYPE%%* C, size_t n, enum NMatrix_Ops op)
3
+ {
4
+ size_t i;
5
+ //fprintf(stderr, "elementwise: n=%d, op=%c\n", n, op);
6
+
7
+ switch (op) {
8
+ case '+':
9
+ for (i = 0; i < n; ++i) {
10
+ %%TYPE C[i] = A[i] + B[i]%%
11
+ }
12
+ break;
13
+ case '-':
14
+ for (i = 0; i < n; ++i) {
15
+ %%TYPE C[i] = A[i] - B[i]%%
16
+ }
17
+ break;
18
+ case '*':
19
+ for (i = 0; i < n; ++i) {
20
+ %%TYPE C[i] = A[i] * B[i]%%
21
+ }
22
+ break;
23
+ case '/':
24
+ for (i = 0; i < n; ++i) {
25
+ %%TYPE C[i] = A[i] / B[i]%%
26
+ }
27
+ break;
28
+ case '%':
29
+ for (i = 0; i < n; ++i) {
30
+ %%TYPE C[i] = A[i] % B[i]%%
31
+ }
32
+ break;
33
+ case '!':
34
+ for (i = 0; i < n; ++i) {
35
+ %%TYPE C[i] = !A[i]%%
36
+ }
37
+ break;
38
+ case NM_OP_NEG:
39
+ for (i = 0; i < n; ++i) {
40
+ %%TYPE C[i] = -A[i]%%
41
+ }
42
+ break;
43
+ case NM_OP_EQEQ:
44
+ for (i = 0; i < n; ++i) {
45
+ %%TYPE C[i] = A[i] == B[i]%%
46
+ }
47
+ break;
48
+ case NM_OP_NEQ:
49
+ for (i = 0; i < n; ++i) {
50
+ %%TYPE C[i] = A[i] != B[i]%%
51
+ }
52
+ break;
53
+ case '>':
54
+ for (i = 0; i < n; ++i) {
55
+ %%TYPE C[i] = A[i] > B[i]%%
56
+ }
57
+ break;
58
+ case '<':
59
+ for (i = 0; i < n; ++i) {
60
+ %%TYPE C[i] = A[i] < B[i]%%
61
+ }
62
+ break;
63
+ case NM_OP_GTE:
64
+ for (i = 0; i < n; ++i) {
65
+ %%TYPE C[i] = A[i] >= B[i]%%
66
+ }
67
+ break;
68
+ case NM_OP_LTE:
69
+ for (i = 0; i < n; ++i) {
70
+ %%TYPE C[i] = A[i] <= B[i]%%
71
+ }
72
+ break;
73
+ case '~':
74
+ for (i = 0; i < n; ++i) {
75
+ %%TYPE C[i] = ~A[i]%%
76
+ }
77
+ break;
78
+ case '&':
79
+ for (i = 0; i < n; ++i) {
80
+ %%TYPE C[i] = A[i] & B[i]%%
81
+ }
82
+ break;
83
+ case '|':
84
+ for (i = 0; i < n; ++i) {
85
+ %%TYPE C[i] = A[i] | B[i]%%
86
+ }
87
+ break;
88
+ case '^':
89
+ for (i = 0; i < n; ++i) {
90
+ %%TYPE C[i] = A[i] ^ B[i]%%
91
+ }
92
+ break;
93
+ case NM_OP_LSH:
94
+ for (i = 0; i < n; ++i) {
95
+ %%TYPE C[i] = A[i] << B[i]%%
96
+ }
97
+ break;
98
+ case NM_OP_RSH:
99
+ for (i = 0; i < n; ++i) {
100
+ %%TYPE C[i] = A[i] >> B[i]%%
101
+ }
102
+ break;
103
+ default:
104
+ rb_raise(rb_eNotImpError, "Unrecognized element-wise operator");
105
+ }
106
+ return 0;
107
+ }