nmatrix 0.0.1 → 0.0.2

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 (91) hide show
  1. data/.gitignore +27 -0
  2. data/.rspec +2 -0
  3. data/Gemfile +3 -5
  4. data/Guardfile +6 -0
  5. data/History.txt +33 -0
  6. data/Manifest.txt +41 -38
  7. data/README.rdoc +88 -11
  8. data/Rakefile +35 -53
  9. data/ext/nmatrix/data/complex.h +372 -0
  10. data/ext/nmatrix/data/data.cpp +275 -0
  11. data/ext/nmatrix/data/data.h +707 -0
  12. data/ext/nmatrix/data/rational.h +421 -0
  13. data/ext/nmatrix/data/ruby_object.h +446 -0
  14. data/ext/nmatrix/extconf.rb +101 -51
  15. data/ext/nmatrix/new_extconf.rb +56 -0
  16. data/ext/nmatrix/nmatrix.cpp +1609 -0
  17. data/ext/nmatrix/nmatrix.h +265 -849
  18. data/ext/nmatrix/ruby_constants.cpp +134 -0
  19. data/ext/nmatrix/ruby_constants.h +103 -0
  20. data/ext/nmatrix/storage/common.cpp +70 -0
  21. data/ext/nmatrix/storage/common.h +170 -0
  22. data/ext/nmatrix/storage/dense.cpp +665 -0
  23. data/ext/nmatrix/storage/dense.h +116 -0
  24. data/ext/nmatrix/storage/list.cpp +1088 -0
  25. data/ext/nmatrix/storage/list.h +129 -0
  26. data/ext/nmatrix/storage/storage.cpp +658 -0
  27. data/ext/nmatrix/storage/storage.h +99 -0
  28. data/ext/nmatrix/storage/yale.cpp +1601 -0
  29. data/ext/nmatrix/storage/yale.h +208 -0
  30. data/ext/nmatrix/ttable_helper.rb +126 -0
  31. data/ext/nmatrix/{yale/smmp1_header.template.c → types.h} +36 -9
  32. data/ext/nmatrix/util/io.cpp +295 -0
  33. data/ext/nmatrix/util/io.h +117 -0
  34. data/ext/nmatrix/util/lapack.h +1175 -0
  35. data/ext/nmatrix/util/math.cpp +557 -0
  36. data/ext/nmatrix/util/math.h +1363 -0
  37. data/ext/nmatrix/util/sl_list.cpp +475 -0
  38. data/ext/nmatrix/util/sl_list.h +255 -0
  39. data/ext/nmatrix/util/util.h +78 -0
  40. data/lib/nmatrix/blas.rb +70 -0
  41. data/lib/nmatrix/io/mat5_reader.rb +567 -0
  42. data/lib/nmatrix/io/mat_reader.rb +162 -0
  43. data/lib/{string.rb → nmatrix/monkeys.rb} +49 -2
  44. data/lib/nmatrix/nmatrix.rb +199 -0
  45. data/lib/nmatrix/nvector.rb +103 -0
  46. data/lib/nmatrix/version.rb +27 -0
  47. data/lib/nmatrix.rb +22 -230
  48. data/nmatrix.gemspec +59 -0
  49. data/scripts/mac-brew-gcc.sh +47 -0
  50. data/spec/4x4_sparse.mat +0 -0
  51. data/spec/4x5_dense.mat +0 -0
  52. data/spec/blas_spec.rb +47 -0
  53. data/spec/elementwise_spec.rb +164 -0
  54. data/spec/io_spec.rb +60 -0
  55. data/spec/lapack_spec.rb +52 -0
  56. data/spec/math_spec.rb +96 -0
  57. data/spec/nmatrix_spec.rb +93 -89
  58. data/spec/nmatrix_yale_spec.rb +52 -36
  59. data/spec/nvector_spec.rb +1 -1
  60. data/spec/slice_spec.rb +257 -0
  61. data/spec/spec_helper.rb +51 -0
  62. data/spec/utm5940.mtx +83844 -0
  63. metadata +113 -71
  64. data/.autotest +0 -23
  65. data/.gemtest +0 -0
  66. data/ext/nmatrix/cblas.c +0 -150
  67. data/ext/nmatrix/dense/blas_header.template.c +0 -52
  68. data/ext/nmatrix/dense/elementwise.template.c +0 -107
  69. data/ext/nmatrix/dense/gemm.template.c +0 -159
  70. data/ext/nmatrix/dense/gemv.template.c +0 -130
  71. data/ext/nmatrix/dense/rationalmath.template.c +0 -68
  72. data/ext/nmatrix/dense.c +0 -307
  73. data/ext/nmatrix/depend +0 -18
  74. data/ext/nmatrix/generator/syntax_tree.rb +0 -481
  75. data/ext/nmatrix/generator.rb +0 -594
  76. data/ext/nmatrix/list.c +0 -774
  77. data/ext/nmatrix/nmatrix.c +0 -1977
  78. data/ext/nmatrix/rational.c +0 -98
  79. data/ext/nmatrix/yale/complexmath.template.c +0 -71
  80. data/ext/nmatrix/yale/elementwise.template.c +0 -46
  81. data/ext/nmatrix/yale/elementwise_op.template.c +0 -73
  82. data/ext/nmatrix/yale/numbmm.template.c +0 -94
  83. data/ext/nmatrix/yale/smmp1.template.c +0 -21
  84. data/ext/nmatrix/yale/smmp2.template.c +0 -43
  85. data/ext/nmatrix/yale/smmp2_header.template.c +0 -46
  86. data/ext/nmatrix/yale/sort_columns.template.c +0 -56
  87. data/ext/nmatrix/yale/symbmm.template.c +0 -54
  88. data/ext/nmatrix/yale/transp.template.c +0 -68
  89. data/ext/nmatrix/yale.c +0 -726
  90. data/lib/array.rb +0 -67
  91. data/spec/syntax_tree_spec.rb +0 -46
@@ -0,0 +1,665 @@
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
+ /*
29
+ * Standard Includes
30
+ */
31
+
32
+ #include <ruby.h>
33
+
34
+ /*
35
+ * Project Includes
36
+ */
37
+ // #include "types.h"
38
+ #include "util/math.h"
39
+
40
+ #include "data/data.h"
41
+ #include "common.h"
42
+ #include "dense.h"
43
+
44
+ /*
45
+ * Macros
46
+ */
47
+
48
+ /*
49
+ * Global Variables
50
+ */
51
+
52
+ /*
53
+ * Forward Declarations
54
+ */
55
+
56
+ namespace nm { namespace dense_storage {
57
+
58
+ template <typename LDType, typename RDType>
59
+ DENSE_STORAGE* cast_copy(const DENSE_STORAGE* rhs, dtype_t new_dtype);
60
+
61
+ template <typename LDType, typename RDType>
62
+ bool eqeq(const DENSE_STORAGE* left, const DENSE_STORAGE* right);
63
+
64
+ template <ewop_t op, typename LDType, typename RDType>
65
+ static DENSE_STORAGE* ew_op(const DENSE_STORAGE* left, const DENSE_STORAGE* right);
66
+
67
+ template <typename DType>
68
+ static DENSE_STORAGE* matrix_multiply(const STORAGE_PAIR& casted_storage, size_t* resulting_shape, bool vector);
69
+
70
+ template <typename DType>
71
+ bool is_hermitian(const DENSE_STORAGE* mat, int lda);
72
+
73
+ template <typename DType>
74
+ bool is_symmetric(const DENSE_STORAGE* mat, int lda);
75
+
76
+ }} // end of namespace nm::dense_storage
77
+
78
+
79
+ extern "C" {
80
+
81
+ static size_t* stride(size_t* shape, size_t dim);
82
+ static void slice_copy(DENSE_STORAGE *dest, const DENSE_STORAGE *src, size_t* lengths, size_t pdest, size_t psrc, size_t n);
83
+
84
+ /*
85
+ * Functions
86
+ */
87
+
88
+ ///////////////
89
+ // Lifecycle //
90
+ ///////////////
91
+
92
+ /*
93
+ * Note that elements and elements_length are for initial value(s) passed in.
94
+ * If they are the correct length, they will be used directly. If not, they
95
+ * will be concatenated over and over again into a new elements array. If
96
+ * elements is NULL, the new elements array will not be initialized.
97
+ */
98
+ DENSE_STORAGE* nm_dense_storage_create(dtype_t dtype, size_t* shape, size_t dim, void* elements, size_t elements_length) {
99
+ DENSE_STORAGE* s = ALLOC( DENSE_STORAGE );
100
+
101
+ s->dim = dim;
102
+ s->shape = shape;
103
+ s->dtype = dtype;
104
+
105
+ s->offset = ALLOC_N(size_t, dim);
106
+ memset(s->offset, 0, sizeof(size_t)*dim);
107
+
108
+ s->stride = stride(shape, dim);
109
+ s->count = 1;
110
+ s->src = s;
111
+
112
+ size_t count = nm_storage_count_max_elements(s);
113
+
114
+ if (elements_length == count) {
115
+ s->elements = elements;
116
+
117
+ } else {
118
+ s->elements = ALLOC_N(char, DTYPE_SIZES[dtype]*count);
119
+
120
+ size_t copy_length = elements_length;
121
+
122
+ if (elements_length > 0) {
123
+ // Repeat elements over and over again until the end of the matrix.
124
+ for (size_t i = 0; i < count; i += elements_length) {
125
+
126
+ if (i + elements_length > count) {
127
+ copy_length = count - i;
128
+ }
129
+
130
+ memcpy((char*)(s->elements)+i*DTYPE_SIZES[dtype], (char*)(elements)+(i % elements_length)*DTYPE_SIZES[dtype], copy_length*DTYPE_SIZES[dtype]);
131
+ }
132
+
133
+ // Get rid of the init_val.
134
+ free(elements);
135
+ }
136
+ }
137
+
138
+ return s;
139
+ }
140
+
141
+ /*
142
+ * Destructor for dense storage
143
+ */
144
+ void nm_dense_storage_delete(STORAGE* s) {
145
+ // Sometimes Ruby passes in NULL storage for some reason (probably on copy construction failure).
146
+ if (s) {
147
+ DENSE_STORAGE* storage = (DENSE_STORAGE*)s;
148
+ if(storage->count-- == 1) {
149
+ free(storage->shape);
150
+ free(storage->offset);
151
+ free(storage->stride);
152
+ free(storage->elements);
153
+ free(storage);
154
+ }
155
+ }
156
+ }
157
+
158
+ /*
159
+ * Destructor for dense storage references (slicing).
160
+ */
161
+ void nm_dense_storage_delete_ref(STORAGE* s) {
162
+ // Sometimes Ruby passes in NULL storage for some reason (probably on copy construction failure).
163
+ if (s) {
164
+ DENSE_STORAGE* storage = (DENSE_STORAGE*)s;
165
+ nm_dense_storage_delete( reinterpret_cast<STORAGE*>(storage->src) );
166
+ free(storage->shape);
167
+ free(storage->offset);
168
+ free(storage);
169
+ }
170
+ }
171
+
172
+ /*
173
+ * Mark values in a dense matrix for garbage collection. This may not be necessary -- further testing required.
174
+ */
175
+ void nm_dense_storage_mark(void* storage_base) {
176
+ DENSE_STORAGE* storage = (DENSE_STORAGE*)storage_base;
177
+
178
+ if (storage && storage->dtype == RUBYOBJ) {
179
+ VALUE* els = reinterpret_cast<VALUE*>(storage->elements);
180
+
181
+ for (size_t index = nm_storage_count_max_elements(storage); index-- > 0;) {
182
+ rb_gc_mark(els[index]);
183
+ }
184
+ }
185
+ }
186
+
187
+ ///////////////
188
+ // Accessors //
189
+ ///////////////
190
+
191
+ /*
192
+ * Get a slice or one element, using copying.
193
+ *
194
+ * FIXME: Template the first condition.
195
+ */
196
+ void* nm_dense_storage_get(STORAGE* storage, SLICE* slice) {
197
+ DENSE_STORAGE* s = (DENSE_STORAGE*)storage;
198
+ DENSE_STORAGE* ns;
199
+
200
+ if (slice->single)
201
+ return (char*)(s->elements) + nm_dense_storage_pos(s, slice->coords) * DTYPE_SIZES[s->dtype];
202
+ else { // Make references
203
+ size_t *shape = ALLOC_N(size_t, s->dim);
204
+ for (size_t i = 0; i < s->dim; ++i) {
205
+ shape[i] = slice->lengths[i];
206
+ }
207
+
208
+ ns = nm_dense_storage_create(s->dtype, shape, s->dim, NULL, 0);
209
+
210
+ slice_copy(ns,
211
+ reinterpret_cast<const DENSE_STORAGE*>(s->src),
212
+ slice->lengths,
213
+ 0,
214
+ nm_dense_storage_pos(s, slice->coords),
215
+ 0);
216
+ return ns;
217
+ }
218
+ }
219
+
220
+ /*
221
+ * Get a slice or one element by reference (no copy).
222
+ *
223
+ * FIXME: Template the first condition.
224
+ */
225
+ void* nm_dense_storage_ref(STORAGE* storage, SLICE* slice) {
226
+ DENSE_STORAGE* s = (DENSE_STORAGE*)storage;
227
+
228
+ if (slice->single)
229
+ return (char*)(s->elements) + nm_dense_storage_pos(s, slice->coords) * DTYPE_SIZES[s->dtype];
230
+
231
+ else {
232
+ DENSE_STORAGE* ns = ALLOC( DENSE_STORAGE );
233
+ ns->dim = s->dim;
234
+ ns->dtype = s->dtype;
235
+ ns->offset = ALLOC_N(size_t, ns->dim);
236
+ ns->shape = ALLOC_N(size_t, ns->dim);
237
+
238
+ for (size_t i = 0; i < ns->dim; ++i) {
239
+ ns->offset[i] = slice->coords[i] + s->offset[i];
240
+ ns->shape[i] = slice->lengths[i];
241
+ }
242
+
243
+ ns->stride = s->stride;
244
+ ns->elements = s->elements;
245
+
246
+ s->src->count++;
247
+ ns->src = s->src;
248
+
249
+ return ns;
250
+ }
251
+ }
252
+
253
+
254
+ /*
255
+ * Does not free passed-in value! Different from list_storage_insert.
256
+ */
257
+ void nm_dense_storage_set(STORAGE* storage, SLICE* slice, void* val) {
258
+ DENSE_STORAGE* s = (DENSE_STORAGE*)storage;
259
+ memcpy((char*)(s->elements) + nm_dense_storage_pos(s, slice->coords) * DTYPE_SIZES[s->dtype], val, DTYPE_SIZES[s->dtype]);
260
+ }
261
+
262
+ ///////////
263
+ // Tests //
264
+ ///////////
265
+
266
+ /*
267
+ * Do these two dense matrices have the same contents?
268
+ *
269
+ * TODO: Test the shape of the two matrices.
270
+ * TODO: See if using memcmp is faster when the left- and right-hand matrices
271
+ * have the same dtype.
272
+ */
273
+ bool nm_dense_storage_eqeq(const STORAGE* left, const STORAGE* right) {
274
+ LR_DTYPE_TEMPLATE_TABLE(nm::dense_storage::eqeq, bool, const DENSE_STORAGE*, const DENSE_STORAGE*);
275
+
276
+ return ttable[left->dtype][right->dtype]((const DENSE_STORAGE*)left, (const DENSE_STORAGE*)right);
277
+ }
278
+
279
+ /*
280
+ * Test to see if the matrix is Hermitian. If the matrix does not have a
281
+ * dtype of Complex64 or Complex128 this is the same as testing for symmetry.
282
+ */
283
+ bool nm_dense_storage_is_hermitian(const DENSE_STORAGE* mat, int lda) {
284
+ if (mat->dtype == COMPLEX64) {
285
+ return nm::dense_storage::is_hermitian<nm::Complex64>(mat, lda);
286
+
287
+ } else if (mat->dtype == COMPLEX128) {
288
+ return nm::dense_storage::is_hermitian<nm::Complex128>(mat, lda);
289
+
290
+ } else {
291
+ return nm_dense_storage_is_symmetric(mat, lda);
292
+ }
293
+ }
294
+
295
+ /*
296
+ * Is this dense matrix symmetric about the diagonal?
297
+ */
298
+ bool nm_dense_storage_is_symmetric(const DENSE_STORAGE* mat, int lda) {
299
+ DTYPE_TEMPLATE_TABLE(nm::dense_storage::is_symmetric, bool, const DENSE_STORAGE*, int);
300
+
301
+ return ttable[mat->dtype](mat, lda);
302
+ }
303
+
304
+ //////////
305
+ // Math //
306
+ //////////
307
+
308
+ /*
309
+ * Dense element-wise operations.
310
+ */
311
+ STORAGE* nm_dense_storage_ew_op(nm::ewop_t op, const STORAGE* left, const STORAGE* right) {
312
+ OP_LR_DTYPE_TEMPLATE_TABLE(nm::dense_storage::ew_op, DENSE_STORAGE*, const DENSE_STORAGE* left, const DENSE_STORAGE* right);
313
+
314
+ return ttable[op][left->dtype][right->dtype](reinterpret_cast<const DENSE_STORAGE*>(left), reinterpret_cast<const DENSE_STORAGE*>(right));
315
+ }
316
+
317
+ /*
318
+ * Dense matrix-matrix multiplication.
319
+ */
320
+ STORAGE* nm_dense_storage_matrix_multiply(const STORAGE_PAIR& casted_storage, size_t* resulting_shape, bool vector) {
321
+ DTYPE_TEMPLATE_TABLE(nm::dense_storage::matrix_multiply, DENSE_STORAGE*, const STORAGE_PAIR& casted_storage, size_t* resulting_shape, bool vector);
322
+
323
+ return ttable[casted_storage.left->dtype](casted_storage, resulting_shape, vector);
324
+ }
325
+
326
+ /////////////
327
+ // Utility //
328
+ /////////////
329
+
330
+ /*
331
+ * Determine the linear array position (in elements of s) of some set of coordinates
332
+ * (given by slice).
333
+ */
334
+ size_t nm_dense_storage_pos(const DENSE_STORAGE* s, const size_t* coords) {
335
+ size_t pos = 0;
336
+
337
+ for (size_t i = 0; i < s->dim; ++i)
338
+ pos += (coords[i] + s->offset[i]) * s->stride[i];
339
+
340
+ return pos;
341
+ }
342
+
343
+ /*
344
+ * Calculate the stride length.
345
+ */
346
+ static size_t* stride(size_t* shape, size_t dim) {
347
+ size_t i, j;
348
+ size_t* stride = ALLOC_N(size_t, dim);
349
+
350
+ for (i = 0; i < dim; ++i) {
351
+ stride[i] = 1;
352
+ for (j = i+1; j < dim; ++j) {
353
+ stride[i] *= shape[j];
354
+ }
355
+ }
356
+
357
+ return stride;
358
+ }
359
+
360
+ /*
361
+ * Recursive slicing for N-dimensional matrix.
362
+ */
363
+ static void slice_copy(DENSE_STORAGE *dest, const DENSE_STORAGE *src, size_t* lengths, size_t pdest, size_t psrc, size_t n) {
364
+ if (src->dim - n > 1) {
365
+ for (size_t i = 0; i < lengths[n]; ++i) {
366
+ slice_copy(dest, src, lengths,
367
+ pdest + dest->stride[n]*i,
368
+ psrc + src->stride[n]*i,
369
+ n + 1);
370
+ }
371
+ } else {
372
+ memcpy((char*)dest->elements + pdest*DTYPE_SIZES[dest->dtype],
373
+ (char*)src->elements + psrc*DTYPE_SIZES[src->dtype],
374
+ dest->shape[n]*DTYPE_SIZES[dest->dtype]);
375
+ }
376
+
377
+ }
378
+
379
+ /////////////////////////
380
+ // Copying and Casting //
381
+ /////////////////////////
382
+
383
+ /*
384
+ * Copy dense storage, changing dtype if necessary.
385
+ */
386
+ STORAGE* nm_dense_storage_cast_copy(const STORAGE* rhs, dtype_t new_dtype) {
387
+ NAMED_LR_DTYPE_TEMPLATE_TABLE(ttable, nm::dense_storage::cast_copy, DENSE_STORAGE*, const DENSE_STORAGE* rhs, dtype_t new_dtype);
388
+
389
+ return (STORAGE*)ttable[new_dtype][rhs->dtype]((DENSE_STORAGE*)rhs, new_dtype);
390
+ }
391
+
392
+ /*
393
+ * Copy dense storage without a change in dtype.
394
+ */
395
+ DENSE_STORAGE* nm_dense_storage_copy(const DENSE_STORAGE* rhs) {
396
+ size_t count = 0;
397
+ size_t *shape = ALLOC_N(size_t, rhs->dim);
398
+
399
+ // copy shape and offset
400
+ for (size_t i = 0; i < rhs->dim; ++i) {
401
+ shape[i] = rhs->shape[i];
402
+ }
403
+
404
+ DENSE_STORAGE* lhs = nm_dense_storage_create(rhs->dtype, shape, rhs->dim, NULL, 0);
405
+ count = nm_storage_count_max_elements(lhs);
406
+
407
+
408
+ // Ensure that allocation worked before copying.
409
+ if (lhs && count) {
410
+ if (rhs == rhs->src) // not a reference
411
+ memcpy(lhs->elements, rhs->elements, DTYPE_SIZES[rhs->dtype] * count);
412
+ else { // slice whole matrix
413
+ size_t *offset = ALLOC_N(size_t, rhs->dim);
414
+ memset(offset, 0, sizeof(size_t) * rhs->dim);
415
+
416
+ slice_copy(lhs,
417
+ reinterpret_cast<const DENSE_STORAGE*>(rhs->src),
418
+ rhs->shape,
419
+ 0,
420
+ nm_dense_storage_pos(rhs, offset),
421
+ 0);
422
+ }
423
+ }
424
+
425
+ return lhs;
426
+ }
427
+
428
+
429
+ /*
430
+ * Transpose dense storage into a new dense storage object. Basically a copy constructor.
431
+ *
432
+ * Not much point in templating this as it's pretty straight-forward.
433
+ */
434
+ STORAGE* nm_dense_storage_copy_transposed(const STORAGE* rhs_base) {
435
+ DENSE_STORAGE* rhs = (DENSE_STORAGE*)rhs_base;
436
+
437
+ size_t *shape = ALLOC_N(size_t, rhs->dim);
438
+
439
+ // swap shape and offset
440
+ shape[0] = rhs->shape[1];
441
+ shape[1] = rhs->shape[0];
442
+
443
+ DENSE_STORAGE *lhs = nm_dense_storage_create(rhs->dtype, shape, rhs->dim, NULL, 0);
444
+ lhs->offset[0] = rhs->offset[1];
445
+ lhs->offset[1] = rhs->offset[0];
446
+
447
+ nm_math_transpose_generic(rhs->shape[0], rhs->shape[1], rhs->elements, rhs->shape[1], lhs->elements, lhs->shape[1], DTYPE_SIZES[rhs->dtype]);
448
+
449
+ return (STORAGE*)lhs;
450
+ }
451
+
452
+ } // end of extern "C" block
453
+
454
+ namespace nm { namespace dense_storage {
455
+
456
+ /////////////////////////
457
+ // Templated Functions //
458
+ /////////////////////////
459
+
460
+ template <typename LDType, typename RDType>
461
+ DENSE_STORAGE* cast_copy(const DENSE_STORAGE* rhs, dtype_t new_dtype) {
462
+ size_t count = nm_storage_count_max_elements(rhs);
463
+
464
+ size_t *shape = ALLOC_N(size_t, rhs->dim);
465
+ memcpy(shape, rhs->shape, sizeof(size_t) * rhs->dim);
466
+
467
+ DENSE_STORAGE* lhs = nm_dense_storage_create(new_dtype, shape, rhs->dim, NULL, 0);
468
+
469
+ RDType* rhs_els = reinterpret_cast<RDType*>(rhs->elements);
470
+ LDType* lhs_els = reinterpret_cast<LDType*>(lhs->elements);
471
+
472
+ // Ensure that allocation worked before copying.
473
+ if (lhs && count) {
474
+ if (rhs->src != rhs) {
475
+ /* Make a copy of a ref to a matrix. */
476
+
477
+ DENSE_STORAGE* tmp = nm_dense_storage_copy(rhs);
478
+
479
+ RDType* tmp_els = reinterpret_cast<RDType*>(tmp->elements);
480
+ while (count-- > 0) {
481
+ lhs_els[count] = tmp_els[count];
482
+ }
483
+ nm_dense_storage_delete(tmp);
484
+ } else {
485
+ /* Make a regular copy. */
486
+
487
+ while (count-- > 0) lhs_els[count] = rhs_els[count];
488
+ }
489
+ }
490
+
491
+ return lhs;
492
+ }
493
+
494
+ template <typename LDType, typename RDType>
495
+ bool eqeq(const DENSE_STORAGE* left, const DENSE_STORAGE* right) {
496
+ size_t index;
497
+ DENSE_STORAGE *tmp1, *tmp2;
498
+ tmp1 = NULL; tmp2 = NULL;
499
+ bool result = true;
500
+ /* FIXME: Very strange behavior! The GC calls the method directly with non-initialized data. */
501
+ if (left->dim != right->dim) return false;
502
+
503
+
504
+ LDType* left_elements = (LDType*)left->elements;
505
+ RDType* right_elements = (RDType*)right->elements;
506
+
507
+ // Copy elements in temp matrix if you have refernce to the right.
508
+ if (left->src != left) {
509
+ tmp1 = nm_dense_storage_copy(left);
510
+ left_elements = (LDType*)tmp1->elements;
511
+ }
512
+ if (right->src != right) {
513
+ tmp2 = nm_dense_storage_copy(right);
514
+ right_elements = (RDType*)tmp2->elements;
515
+ }
516
+
517
+
518
+
519
+ for (index = nm_storage_count_max_elements(left); index-- > 0;) {
520
+ if (left_elements[index] != right_elements[index]) {
521
+ result = false;
522
+ break;
523
+ }
524
+ }
525
+
526
+ if (tmp1)
527
+ free(tmp1);
528
+ if (tmp2)
529
+ free(tmp2);
530
+
531
+ return result;
532
+ }
533
+
534
+ template <typename DType>
535
+ bool is_hermitian(const DENSE_STORAGE* mat, int lda) {
536
+ unsigned int i, j;
537
+ register DType complex_conj;
538
+
539
+ const DType* els = (DType*) mat->elements;
540
+
541
+ for (i = mat->shape[0]; i-- > 0;) {
542
+ for (j = i + 1; j < mat->shape[1]; ++j) {
543
+ complex_conj = els[j*lda + 1];
544
+ complex_conj.i = -complex_conj.i;
545
+
546
+ if (els[i*lda+j] != complex_conj) {
547
+ return false;
548
+ }
549
+ }
550
+ }
551
+
552
+ return true;
553
+ }
554
+
555
+ template <typename DType>
556
+ bool is_symmetric(const DENSE_STORAGE* mat, int lda) {
557
+ unsigned int i, j;
558
+ const DType* els = (DType*) mat->elements;
559
+
560
+ for (i = mat->shape[0]; i-- > 0;) {
561
+ for (j = i + 1; j < mat->shape[1]; ++j) {
562
+ if (els[i*lda+j] != els[j*lda+i]) {
563
+ return false;
564
+ }
565
+ }
566
+ }
567
+
568
+ return true;
569
+ }
570
+
571
+ /*
572
+ * Templated dense storage element-wise operations which return the same DType.
573
+ */
574
+ template <ewop_t op, typename LDType, typename RDType>
575
+ static DENSE_STORAGE* ew_op(const DENSE_STORAGE* left, const DENSE_STORAGE* right) {
576
+ unsigned int count;
577
+
578
+ size_t* new_shape = (size_t*)calloc(left->dim, sizeof(size_t));
579
+ memcpy(new_shape, left->shape, sizeof(size_t) * left->dim);
580
+
581
+ // Determine the return dtype. This depends on the type of operation we're doing. Usually, it's going to be
582
+ // set by the left matrix, but for comparisons, we'll use BYTE (in lieu of boolean).
583
+ dtype_t new_dtype = static_cast<uint8_t>(op) < NUM_NONCOMP_EWOPS ? left->dtype : BYTE;
584
+
585
+ DENSE_STORAGE* result = nm_dense_storage_create(new_dtype, new_shape, left->dim, NULL, 0);
586
+
587
+ LDType* l_elems = reinterpret_cast<LDType*>(left->elements);
588
+ RDType* r_elems = reinterpret_cast<RDType*>(right->elements);
589
+
590
+ if (static_cast<uint8_t>(op) < NUM_NONCOMP_EWOPS) { // use left-dtype
591
+
592
+ for (count = nm_storage_count_max_elements(result); count-- > 0;) {
593
+ reinterpret_cast<LDType*>(result->elements)[count] = ew_op_switch<op,LDType,RDType>(l_elems[count], r_elems[count]);
594
+ }
595
+
596
+ } else { // new_dtype is BYTE: comparison operators
597
+ uint8_t* res_elems = reinterpret_cast<uint8_t*>(result->elements);
598
+
599
+ for (count = nm_storage_count_max_elements(result); count-- > 0;) {
600
+ switch (op) {
601
+ case EW_EQEQ:
602
+ res_elems[count] = l_elems[count] == r_elems[count];
603
+ break;
604
+
605
+ case EW_NEQ:
606
+ res_elems[count] = l_elems[count] != r_elems[count];
607
+ break;
608
+
609
+ case EW_LT:
610
+ res_elems[count] = l_elems[count] < r_elems[count];
611
+ break;
612
+
613
+ case EW_GT:
614
+ res_elems[count] = l_elems[count] > r_elems[count];
615
+ break;
616
+
617
+ case EW_LEQ:
618
+ res_elems[count] = l_elems[count] <= r_elems[count];
619
+ break;
620
+
621
+ case EW_GEQ:
622
+ res_elems[count] = l_elems[count] >= r_elems[count];
623
+ break;
624
+
625
+ default:
626
+ rb_raise(rb_eStandardError, "this should not happen");
627
+ }
628
+ }
629
+ }
630
+
631
+ return result;
632
+ }
633
+
634
+
635
+ /*
636
+ * DType-templated matrix-matrix multiplication for dense storage.
637
+ */
638
+ template <typename DType>
639
+ static DENSE_STORAGE* matrix_multiply(const STORAGE_PAIR& casted_storage, size_t* resulting_shape, bool vector) {
640
+ DENSE_STORAGE *left = (DENSE_STORAGE*)(casted_storage.left),
641
+ *right = (DENSE_STORAGE*)(casted_storage.right);
642
+
643
+ // Create result storage.
644
+ DENSE_STORAGE* result = nm_dense_storage_create(left->dtype, resulting_shape, 2, NULL, 0);
645
+
646
+ DType *pAlpha = ALLOCA_N(DType, 1),
647
+ *pBeta = ALLOCA_N(DType, 1);
648
+
649
+ *pAlpha = 1;
650
+ *pBeta = 0;
651
+ // Do the multiplication
652
+
653
+ if (vector) nm::math::gemv<DType>(CblasNoTrans, left->shape[0], left->shape[1], pAlpha,
654
+ reinterpret_cast<DType*>(left->elements), left->shape[1],
655
+ reinterpret_cast<DType*>(right->elements), 1, pBeta,
656
+ reinterpret_cast<DType*>(result->elements), 1);
657
+ else nm::math::gemm<DType>(CblasRowMajor, CblasNoTrans, CblasNoTrans, left->shape[0], right->shape[1], left->shape[1],
658
+ pAlpha, reinterpret_cast<DType*>(left->elements), left->shape[1],
659
+ reinterpret_cast<DType*>(right->elements), right->shape[1], pBeta,
660
+ reinterpret_cast<DType*>(result->elements), result->shape[1]);
661
+
662
+ return result;
663
+ }
664
+
665
+ }} // end of namespace nm::dense_storage