pnmatrix 1.2.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. checksums.yaml +7 -0
  2. data/ext/nmatrix/binary_format.txt +53 -0
  3. data/ext/nmatrix/data/complex.h +388 -0
  4. data/ext/nmatrix/data/data.cpp +274 -0
  5. data/ext/nmatrix/data/data.h +651 -0
  6. data/ext/nmatrix/data/meta.h +64 -0
  7. data/ext/nmatrix/data/ruby_object.h +386 -0
  8. data/ext/nmatrix/extconf.rb +70 -0
  9. data/ext/nmatrix/math/asum.h +99 -0
  10. data/ext/nmatrix/math/cblas_enums.h +36 -0
  11. data/ext/nmatrix/math/cblas_templates_core.h +507 -0
  12. data/ext/nmatrix/math/gemm.h +241 -0
  13. data/ext/nmatrix/math/gemv.h +178 -0
  14. data/ext/nmatrix/math/getrf.h +255 -0
  15. data/ext/nmatrix/math/getrs.h +121 -0
  16. data/ext/nmatrix/math/imax.h +82 -0
  17. data/ext/nmatrix/math/laswp.h +165 -0
  18. data/ext/nmatrix/math/long_dtype.h +62 -0
  19. data/ext/nmatrix/math/magnitude.h +54 -0
  20. data/ext/nmatrix/math/math.h +751 -0
  21. data/ext/nmatrix/math/nrm2.h +165 -0
  22. data/ext/nmatrix/math/rot.h +117 -0
  23. data/ext/nmatrix/math/rotg.h +106 -0
  24. data/ext/nmatrix/math/scal.h +71 -0
  25. data/ext/nmatrix/math/trsm.h +336 -0
  26. data/ext/nmatrix/math/util.h +162 -0
  27. data/ext/nmatrix/math.cpp +1368 -0
  28. data/ext/nmatrix/nm_memory.h +60 -0
  29. data/ext/nmatrix/nmatrix.cpp +285 -0
  30. data/ext/nmatrix/nmatrix.h +476 -0
  31. data/ext/nmatrix/ruby_constants.cpp +151 -0
  32. data/ext/nmatrix/ruby_constants.h +106 -0
  33. data/ext/nmatrix/ruby_nmatrix.c +3130 -0
  34. data/ext/nmatrix/storage/common.cpp +77 -0
  35. data/ext/nmatrix/storage/common.h +183 -0
  36. data/ext/nmatrix/storage/dense/dense.cpp +1096 -0
  37. data/ext/nmatrix/storage/dense/dense.h +129 -0
  38. data/ext/nmatrix/storage/list/list.cpp +1628 -0
  39. data/ext/nmatrix/storage/list/list.h +138 -0
  40. data/ext/nmatrix/storage/storage.cpp +730 -0
  41. data/ext/nmatrix/storage/storage.h +99 -0
  42. data/ext/nmatrix/storage/yale/class.h +1139 -0
  43. data/ext/nmatrix/storage/yale/iterators/base.h +143 -0
  44. data/ext/nmatrix/storage/yale/iterators/iterator.h +131 -0
  45. data/ext/nmatrix/storage/yale/iterators/row.h +450 -0
  46. data/ext/nmatrix/storage/yale/iterators/row_stored.h +140 -0
  47. data/ext/nmatrix/storage/yale/iterators/row_stored_nd.h +169 -0
  48. data/ext/nmatrix/storage/yale/iterators/stored_diagonal.h +124 -0
  49. data/ext/nmatrix/storage/yale/math/transpose.h +110 -0
  50. data/ext/nmatrix/storage/yale/yale.cpp +2074 -0
  51. data/ext/nmatrix/storage/yale/yale.h +203 -0
  52. data/ext/nmatrix/types.h +55 -0
  53. data/ext/nmatrix/util/io.cpp +279 -0
  54. data/ext/nmatrix/util/io.h +115 -0
  55. data/ext/nmatrix/util/sl_list.cpp +627 -0
  56. data/ext/nmatrix/util/sl_list.h +144 -0
  57. data/ext/nmatrix/util/util.h +78 -0
  58. data/lib/nmatrix/blas.rb +378 -0
  59. data/lib/nmatrix/cruby/math.rb +744 -0
  60. data/lib/nmatrix/enumerate.rb +253 -0
  61. data/lib/nmatrix/homogeneous.rb +241 -0
  62. data/lib/nmatrix/io/fortran_format.rb +138 -0
  63. data/lib/nmatrix/io/harwell_boeing.rb +221 -0
  64. data/lib/nmatrix/io/market.rb +263 -0
  65. data/lib/nmatrix/io/point_cloud.rb +189 -0
  66. data/lib/nmatrix/jruby/decomposition.rb +24 -0
  67. data/lib/nmatrix/jruby/enumerable.rb +13 -0
  68. data/lib/nmatrix/jruby/error.rb +4 -0
  69. data/lib/nmatrix/jruby/math.rb +501 -0
  70. data/lib/nmatrix/jruby/nmatrix_java.rb +840 -0
  71. data/lib/nmatrix/jruby/operators.rb +283 -0
  72. data/lib/nmatrix/jruby/slice.rb +264 -0
  73. data/lib/nmatrix/lapack_core.rb +181 -0
  74. data/lib/nmatrix/lapack_plugin.rb +44 -0
  75. data/lib/nmatrix/math.rb +953 -0
  76. data/lib/nmatrix/mkmf.rb +100 -0
  77. data/lib/nmatrix/monkeys.rb +137 -0
  78. data/lib/nmatrix/nmatrix.rb +1172 -0
  79. data/lib/nmatrix/rspec.rb +75 -0
  80. data/lib/nmatrix/shortcuts.rb +1163 -0
  81. data/lib/nmatrix/version.rb +39 -0
  82. data/lib/nmatrix/yale_functions.rb +118 -0
  83. data/lib/nmatrix.rb +28 -0
  84. data/spec/00_nmatrix_spec.rb +892 -0
  85. data/spec/01_enum_spec.rb +196 -0
  86. data/spec/02_slice_spec.rb +407 -0
  87. data/spec/03_nmatrix_monkeys_spec.rb +80 -0
  88. data/spec/2x2_dense_double.mat +0 -0
  89. data/spec/4x4_sparse.mat +0 -0
  90. data/spec/4x5_dense.mat +0 -0
  91. data/spec/blas_spec.rb +215 -0
  92. data/spec/elementwise_spec.rb +311 -0
  93. data/spec/homogeneous_spec.rb +100 -0
  94. data/spec/io/fortran_format_spec.rb +88 -0
  95. data/spec/io/harwell_boeing_spec.rb +98 -0
  96. data/spec/io/test.rua +9 -0
  97. data/spec/io_spec.rb +159 -0
  98. data/spec/lapack_core_spec.rb +482 -0
  99. data/spec/leakcheck.rb +16 -0
  100. data/spec/math_spec.rb +1363 -0
  101. data/spec/nmatrix_yale_resize_test_associations.yaml +2802 -0
  102. data/spec/nmatrix_yale_spec.rb +286 -0
  103. data/spec/rspec_monkeys.rb +56 -0
  104. data/spec/rspec_spec.rb +35 -0
  105. data/spec/shortcuts_spec.rb +474 -0
  106. data/spec/slice_set_spec.rb +162 -0
  107. data/spec/spec_helper.rb +172 -0
  108. data/spec/stat_spec.rb +214 -0
  109. data/spec/test.pcd +20 -0
  110. data/spec/utm5940.mtx +83844 -0
  111. metadata +295 -0
@@ -0,0 +1,2074 @@
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 - 2014, Ruby Science Foundation
13
+ // NMatrix is Copyright (c) 2012 - 2014, John Woods and the 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
+ // == yale.c
25
+ //
26
+ // "new yale" storage format for 2D matrices (like yale, but with
27
+ // the diagonal pulled out for O(1) access).
28
+ //
29
+ // Specifications:
30
+ // * dtype and index dtype must necessarily differ
31
+ // * index dtype is defined by whatever unsigned type can store
32
+ // max(rows,cols)
33
+ // * that means vector ija stores only index dtype, but a stores
34
+ // dtype
35
+ // * vectors must be able to grow as necessary
36
+ // * maximum size is rows*cols+1
37
+
38
+ /*
39
+ * Standard Includes
40
+ */
41
+
42
+ #include <ruby.h>
43
+ #include <algorithm> // std::min
44
+ #include <cstdio> // std::fprintf
45
+ #include <iostream>
46
+ #include <array>
47
+ #include <typeinfo>
48
+ #include <tuple>
49
+ #include <queue>
50
+
51
+ /*
52
+ * Project Includes
53
+ */
54
+
55
+ // #include "types.h"
56
+ #include "../../data/data.h"
57
+ #include "../../math/math.h"
58
+
59
+ #include "../common.h"
60
+
61
+ #include "../../nmatrix.h"
62
+ #include "../../data/meta.h"
63
+
64
+ #include "iterators/base.h"
65
+ #include "iterators/stored_diagonal.h"
66
+ #include "iterators/row_stored_nd.h"
67
+ #include "iterators/row_stored.h"
68
+ #include "iterators/row.h"
69
+ #include "iterators/iterator.h"
70
+ #include "class.h"
71
+ #include "yale.h"
72
+ #include "../../ruby_constants.h"
73
+
74
+ /*
75
+ * Macros
76
+ */
77
+
78
+ #ifndef NM_MAX
79
+ #define NM_MAX(a,b) (((a)>(b))?(a):(b))
80
+ #define NM_MIN(a,b) (((a)<(b))?(a):(b))
81
+ #endif
82
+
83
+ /*
84
+ * Forward Declarations
85
+ */
86
+
87
+ extern "C" {
88
+ static YALE_STORAGE* alloc(nm::dtype_t dtype, size_t* shape, size_t dim);
89
+
90
+ static size_t yale_count_slice_copy_ndnz(const YALE_STORAGE* s, size_t*, size_t*);
91
+
92
+ static void* default_value_ptr(const YALE_STORAGE* s);
93
+ static VALUE default_value(const YALE_STORAGE* s);
94
+ static VALUE obj_at(YALE_STORAGE* s, size_t k);
95
+
96
+ /* Ruby-accessible functions */
97
+ static VALUE nm_size(VALUE self);
98
+ static VALUE nm_a(int argc, VALUE* argv, VALUE self);
99
+ static VALUE nm_d(int argc, VALUE* argv, VALUE self);
100
+ static VALUE nm_lu(VALUE self);
101
+ static VALUE nm_ia(VALUE self);
102
+ static VALUE nm_ja(VALUE self);
103
+ static VALUE nm_ija(int argc, VALUE* argv, VALUE self);
104
+ static VALUE nm_row_keys_intersection(VALUE m1, VALUE ii1, VALUE m2, VALUE ii2);
105
+
106
+ static VALUE nm_nd_row(int argc, VALUE* argv, VALUE self);
107
+
108
+ static inline size_t src_ndnz(const YALE_STORAGE* s) {
109
+ return reinterpret_cast<YALE_STORAGE*>(s->src)->ndnz;
110
+ }
111
+
112
+ } // end extern "C" block
113
+
114
+ namespace nm { namespace yale_storage {
115
+
116
+ template <typename LD, typename RD>
117
+ static VALUE map_merged_stored(VALUE left, VALUE right, VALUE init);
118
+
119
+ template <typename DType>
120
+ static bool ndrow_is_empty(const YALE_STORAGE* s, IType ija, const IType ija_next);
121
+
122
+ template <typename LDType, typename RDType>
123
+ static bool ndrow_eqeq_ndrow(const YALE_STORAGE* l, const YALE_STORAGE* r, IType l_ija, const IType l_ija_next, IType r_ija, const IType r_ija_next);
124
+
125
+ template <typename LDType, typename RDType>
126
+ static bool eqeq(const YALE_STORAGE* left, const YALE_STORAGE* right);
127
+
128
+ template <typename LDType, typename RDType>
129
+ static bool eqeq_different_defaults(const YALE_STORAGE* s, const LDType& s_init, const YALE_STORAGE* t, const RDType& t_init);
130
+
131
+ static void increment_ia_after(YALE_STORAGE* s, IType ija_size, IType i, long n);
132
+
133
+ static IType insert_search(YALE_STORAGE* s, IType left, IType right, IType key, bool& found);
134
+
135
+ template <typename DType>
136
+ static char vector_insert(YALE_STORAGE* s, size_t pos, size_t* j, void* val_, size_t n, bool struct_only);
137
+
138
+ template <typename DType>
139
+ static char vector_insert_resize(YALE_STORAGE* s, size_t current_size, size_t pos, size_t* j, size_t n, bool struct_only);
140
+
141
+ template <typename DType>
142
+ static std::tuple<long,bool,std::queue<std::tuple<IType,IType,int> > > count_slice_set_ndnz_change(YALE_STORAGE* s, size_t* coords, size_t* lengths, DType* v, size_t v_size);
143
+
144
+ static inline IType* IJA(const YALE_STORAGE* s) {
145
+ return reinterpret_cast<YALE_STORAGE*>(s->src)->ija;
146
+ }
147
+
148
+ static inline IType IJA_SET(const YALE_STORAGE* s, size_t loc, IType val) {
149
+ return IJA(s)[loc] = val;
150
+ }
151
+
152
+ template <typename DType>
153
+ static inline DType* A(const YALE_STORAGE* s) {
154
+ return reinterpret_cast<DType*>(reinterpret_cast<YALE_STORAGE*>(s->src)->a);
155
+ }
156
+
157
+ template <typename DType>
158
+ static inline DType A_SET(const YALE_STORAGE* s, size_t loc, DType val) {
159
+ return A<DType>(s)[loc] = val;
160
+ }
161
+
162
+
163
+ /*
164
+ * Functions
165
+ */
166
+
167
+ /*
168
+ * Copy a vector from one DType to another.
169
+ */
170
+ template <typename LType, typename RType>
171
+ static inline void copy_recast_vector(const void* in_, void* out_, size_t length) {
172
+ const RType* in = reinterpret_cast<const RType*>(in_);
173
+ LType* out = reinterpret_cast<LType*>(out_);
174
+ for (size_t i = 0; i < length; ++i) {
175
+ out[i] = in[i];
176
+ }
177
+ out;
178
+ }
179
+
180
+
181
+
182
+ /*
183
+ * Create Yale storage from IA, JA, and A vectors given in Old Yale format (probably from a file, since NMatrix only uses
184
+ * new Yale for its storage).
185
+ *
186
+ * This function is needed for Matlab .MAT v5 IO.
187
+ */
188
+ template <typename LDType, typename RDType>
189
+ YALE_STORAGE* create_from_old_yale(dtype_t dtype, size_t* shape, char* r_ia, char* r_ja, char* r_a) {
190
+ IType* ir = reinterpret_cast<IType*>(r_ia);
191
+ IType* jr = reinterpret_cast<IType*>(r_ja);
192
+ RDType* ar = reinterpret_cast<RDType*>(r_a);
193
+
194
+ // Read through ia and ja and figure out the ndnz (non-diagonal non-zeros) count.
195
+ size_t ndnz = 0, i, p, p_next;
196
+
197
+ for (i = 0; i < shape[0]; ++i) { // Walk down rows
198
+ for (p = ir[i], p_next = ir[i+1]; p < p_next; ++p) { // Now walk through columns
199
+
200
+ if (i != jr[p]) ++ndnz; // entry is non-diagonal and probably nonzero
201
+
202
+ }
203
+ }
204
+
205
+ // Having walked through the matrix, we now go about allocating the space for it.
206
+ YALE_STORAGE* s = alloc(dtype, shape, 2);
207
+
208
+ s->capacity = shape[0] + ndnz + 1;
209
+ s->ndnz = ndnz;
210
+
211
+ // Setup IJA and A arrays
212
+ s->ija = NM_ALLOC_N( IType, s->capacity );
213
+ s->a = NM_ALLOC_N( LDType, s->capacity );
214
+ IType* ijl = reinterpret_cast<IType*>(s->ija);
215
+ LDType* al = reinterpret_cast<LDType*>(s->a);
216
+
217
+ // set the diagonal to zero -- this prevents uninitialized values from popping up.
218
+ for (size_t index = 0; index < shape[0]; ++index) {
219
+ al[index] = 0;
220
+ }
221
+
222
+ // Figure out where to start writing JA in IJA:
223
+ size_t pp = s->shape[0]+1;
224
+
225
+ // Find beginning of first row
226
+ p = ir[0];
227
+
228
+ // Now fill the arrays
229
+ for (i = 0; i < s->shape[0]; ++i) {
230
+
231
+ // Set the beginning of the row (of output)
232
+ ijl[i] = pp;
233
+
234
+ // Now walk through columns, starting at end of row (of input)
235
+ for (size_t p_next = ir[i+1]; p < p_next; ++p, ++pp) {
236
+
237
+ if (i == jr[p]) { // diagonal
238
+
239
+ al[i] = ar[p];
240
+ --pp;
241
+
242
+ } else { // nondiagonal
243
+
244
+ ijl[pp] = jr[p];
245
+ al[pp] = ar[p];
246
+
247
+ }
248
+ }
249
+ }
250
+
251
+ ijl[i] = pp; // Set the end of the last row
252
+
253
+ // Set the zero position for our output matrix
254
+ al[i] = 0;
255
+
256
+ return s;
257
+ }
258
+
259
+
260
+ /*
261
+ * Empty the matrix by initializing the IJA vector and setting the diagonal to 0.
262
+ *
263
+ * Called when most YALE_STORAGE objects are created.
264
+ *
265
+ * Can't go inside of class YaleStorage because YaleStorage creation requires that
266
+ * IJA already be initialized.
267
+ */
268
+ template <typename DType>
269
+ void init(YALE_STORAGE* s, void* init_val) {
270
+ IType IA_INIT = s->shape[0] + 1;
271
+
272
+ IType* ija = reinterpret_cast<IType*>(s->ija);
273
+ // clear out IJA vector
274
+ for (IType i = 0; i < IA_INIT; ++i) {
275
+ ija[i] = IA_INIT; // set initial values for IJA
276
+ }
277
+
278
+ clear_diagonal_and_zero<DType>(s, init_val);
279
+ }
280
+
281
+
282
+ template <typename LDType, typename RDType>
283
+ static YALE_STORAGE* slice_copy(YALE_STORAGE* s) {
284
+ YaleStorage<RDType> y(s);
285
+ return y.template alloc_copy<LDType, false>();
286
+ }
287
+
288
+
289
+ /*
290
+ * Template version of copy transposed. This could also, in theory, allow a map -- but transpose.h
291
+ * would need to be updated.
292
+ *
293
+ * TODO: Update for slicing? Update for different dtype in and out? We can cast rather easily without
294
+ * too much modification.
295
+ */
296
+ template <typename D>
297
+ YALE_STORAGE* copy_transposed(YALE_STORAGE* rhs) {
298
+ YaleStorage<D> y(rhs);
299
+ return y.template alloc_copy_transposed<D, false>();
300
+ }
301
+
302
+
303
+ ///////////////
304
+ // Accessors //
305
+ ///////////////
306
+
307
+
308
+ /*
309
+ * Determine the number of non-diagonal non-zeros in a not-yet-created copy of a slice or matrix.
310
+ */
311
+ template <typename DType>
312
+ static size_t count_slice_copy_ndnz(const YALE_STORAGE* s, size_t* offset, size_t* shape) {
313
+ IType* ija = s->ija;
314
+ DType* a = reinterpret_cast<DType*>(s->a);
315
+
316
+ DType ZERO(*reinterpret_cast<DType*>(default_value_ptr(s)));
317
+
318
+ // Calc ndnz for the destination
319
+ size_t ndnz = 0;
320
+ size_t i, j; // indexes of destination matrix
321
+ size_t k, l; // indexes of source matrix
322
+ for (i = 0; i < shape[0]; i++) {
323
+ k = i + offset[0];
324
+ for (j = 0; j < shape[1]; j++) {
325
+ l = j + offset[1];
326
+
327
+ if (j == i) continue;
328
+
329
+ if (k == l) { // for diagonal element of source
330
+ if (a[k] != ZERO) ++ndnz;
331
+ } else { // for non-diagonal element
332
+ for (size_t c = ija[k]; c < ija[k+1]; c++) {
333
+ if (ija[c] == l) {
334
+ ++ndnz;
335
+ break;
336
+ }
337
+ }
338
+ }
339
+ }
340
+ }
341
+
342
+ return ndnz;
343
+ }
344
+
345
+
346
+
347
+ /*
348
+ * Get a single element of a yale storage object
349
+ */
350
+ template <typename DType>
351
+ static void* get_single(YALE_STORAGE* storage, SLICE* slice) {
352
+ YaleStorage<DType> y(storage);
353
+ return reinterpret_cast<void*>(y.get_single_p(slice));
354
+ }
355
+
356
+
357
+ /*
358
+ * Returns a reference-slice of a matrix.
359
+ */
360
+ template <typename DType>
361
+ YALE_STORAGE* ref(YALE_STORAGE* s, SLICE* slice) {
362
+ return YaleStorage<DType>(s).alloc_ref(slice);
363
+ }
364
+
365
+
366
+ /*
367
+ * Attempt to set a cell or cells in a Yale matrix.
368
+ */
369
+ template <typename DType>
370
+ void set(VALUE left, SLICE* slice, VALUE right) {
371
+ YALE_STORAGE* storage = NM_STORAGE_YALE(left);
372
+ YaleStorage<DType> y(storage);
373
+ y.insert(slice, right);
374
+ }
375
+
376
+ ///////////
377
+ // Tests //
378
+ ///////////
379
+
380
+ /*
381
+ * Yale eql? -- for whole-matrix comparison returning a single value.
382
+ */
383
+ template <typename LDType, typename RDType>
384
+ static bool eqeq(const YALE_STORAGE* left, const YALE_STORAGE* right) {
385
+ return YaleStorage<LDType>(left) == YaleStorage<RDType>(right);
386
+ }
387
+
388
+
389
+ //////////
390
+ // Math //
391
+ //////////
392
+
393
+ #define YALE_IA(s) (reinterpret_cast<IType*>(s->ija))
394
+ #define YALE_IJ(s) (reinterpret_cast<IType*>(s->ija) + s->shape[0] + 1)
395
+ #define YALE_COUNT(yale) (yale->ndnz + yale->shape[0])
396
+
397
+ /////////////
398
+ // Utility //
399
+ /////////////
400
+
401
+
402
+ /*
403
+ * Binary search for finding the beginning of a slice. Returns the position of the first element which is larger than
404
+ * bound.
405
+ */
406
+ IType binary_search_left_boundary(const YALE_STORAGE* s, IType left, IType right, IType bound) {
407
+ if (left > right) return -1;
408
+
409
+ IType* ija = IJA(s);
410
+
411
+ if (ija[left] >= bound) return left; // shortcut
412
+
413
+ IType mid = (left + right) / 2;
414
+ IType mid_j = ija[mid];
415
+
416
+ if (mid_j == bound)
417
+ return mid;
418
+ else if (mid_j > bound) { // eligible! don't exclude it.
419
+ return binary_search_left_boundary(s, left, mid, bound);
420
+ } else // (mid_j < bound)
421
+ return binary_search_left_boundary(s, mid + 1, right, bound);
422
+ }
423
+
424
+
425
+ /*
426
+ * Binary search for returning stored values. Returns a non-negative position, or -1 for not found.
427
+ */
428
+ int binary_search(YALE_STORAGE* s, IType left, IType right, IType key) {
429
+ if (s->src != s) throw; // need to fix this quickly
430
+
431
+ if (left > right) return -1;
432
+
433
+ IType* ija = s->ija;
434
+
435
+ IType mid = (left + right)/2;
436
+ IType mid_j = ija[mid];
437
+
438
+ if (mid_j == key)
439
+ return mid;
440
+
441
+ else if (mid_j > key)
442
+ return binary_search(s, left, mid - 1, key);
443
+
444
+ else
445
+ return binary_search(s, mid + 1, right, key);
446
+ }
447
+
448
+
449
+ /*
450
+ * Resize yale storage vectors A and IJA, copying values.
451
+ */
452
+ static void vector_grow(YALE_STORAGE* s) {
453
+ if (s != s->src) {
454
+ throw; // need to correct this quickly.
455
+ }
456
+ nm_yale_storage_register(s);
457
+ size_t new_capacity = s->capacity * GROWTH_CONSTANT;
458
+ size_t max_capacity = YaleStorage<uint8_t>::max_size(s->shape);
459
+
460
+ if (new_capacity > max_capacity) new_capacity = max_capacity;
461
+
462
+ IType* new_ija = NM_ALLOC_N(IType, new_capacity);
463
+ void* new_a = NM_ALLOC_N(char, DTYPE_SIZES[s->dtype] * new_capacity);
464
+
465
+ IType* old_ija = s->ija;
466
+ void* old_a = s->a;
467
+
468
+ memcpy(new_ija, old_ija, s->capacity * sizeof(IType));
469
+ memcpy(new_a, old_a, s->capacity * DTYPE_SIZES[s->dtype]);
470
+
471
+ s->capacity = new_capacity;
472
+
473
+ if (s->dtype == nm::RUBYOBJ)
474
+ nm_yale_storage_register_a(new_a, s->capacity * DTYPE_SIZES[s->dtype]);
475
+
476
+ NM_FREE(old_ija);
477
+ nm_yale_storage_unregister(s);
478
+ NM_FREE(old_a);
479
+ if (s->dtype == nm::RUBYOBJ)
480
+ nm_yale_storage_unregister_a(new_a, s->capacity * DTYPE_SIZES[s->dtype]);
481
+
482
+ s->ija = new_ija;
483
+ s->a = new_a;
484
+
485
+ }
486
+
487
+
488
+ /*
489
+ * Resize yale storage vectors A and IJA in preparation for an insertion.
490
+ */
491
+ template <typename DType>
492
+ static char vector_insert_resize(YALE_STORAGE* s, size_t current_size, size_t pos, size_t* j, size_t n, bool struct_only) {
493
+ if (s != s->src) throw;
494
+
495
+ // Determine the new capacity for the IJA and A vectors.
496
+ size_t new_capacity = s->capacity * GROWTH_CONSTANT;
497
+ size_t max_capacity = YaleStorage<DType>::max_size(s->shape);
498
+
499
+ if (new_capacity > max_capacity) {
500
+ new_capacity = max_capacity;
501
+
502
+ if (current_size + n > max_capacity) rb_raise(rb_eNoMemError, "insertion size exceeded maximum yale matrix size");
503
+ }
504
+
505
+ if (new_capacity < current_size + n)
506
+ new_capacity = current_size + n;
507
+
508
+ nm_yale_storage_register(s);
509
+
510
+ // Allocate the new vectors.
511
+ IType* new_ija = NM_ALLOC_N( IType, new_capacity );
512
+ NM_CHECK_ALLOC(new_ija);
513
+
514
+ DType* new_a = NM_ALLOC_N( DType, new_capacity );
515
+ NM_CHECK_ALLOC(new_a);
516
+
517
+ IType* old_ija = reinterpret_cast<IType*>(s->ija);
518
+ DType* old_a = reinterpret_cast<DType*>(s->a);
519
+
520
+ // Copy all values prior to the insertion site to the new IJA and new A
521
+ if (struct_only) {
522
+ for (size_t i = 0; i < pos; ++i) {
523
+ new_ija[i] = old_ija[i];
524
+ }
525
+ } else {
526
+ for (size_t i = 0; i < pos; ++i) {
527
+ new_ija[i] = old_ija[i];
528
+ new_a[i] = old_a[i];
529
+ }
530
+ }
531
+
532
+
533
+ // Copy all values subsequent to the insertion site to the new IJA and new A, leaving room (size n) for insertion.
534
+ if (struct_only) {
535
+ for (size_t i = pos; i < current_size; ++i) {
536
+ new_ija[i+n] = old_ija[i];
537
+ }
538
+ } else {
539
+ for (size_t i = pos; i < current_size; ++i) {
540
+ new_ija[i+n] = old_ija[i];
541
+ new_a[i+n] = old_a[i];
542
+ }
543
+ }
544
+
545
+ s->capacity = new_capacity;
546
+ if (s->dtype == nm::RUBYOBJ)
547
+ nm_yale_storage_register_a(new_a, new_capacity);
548
+
549
+ NM_FREE(s->ija);
550
+ nm_yale_storage_unregister(s);
551
+ NM_FREE(s->a);
552
+
553
+ if (s->dtype == nm::RUBYOBJ)
554
+ nm_yale_storage_unregister_a(new_a, new_capacity);
555
+
556
+ s->ija = new_ija;
557
+ s->a = reinterpret_cast<void*>(new_a);
558
+
559
+ return 'i';
560
+ }
561
+
562
+ /*
563
+ * Insert a value or contiguous values in the ija and a vectors (after ja and
564
+ * diag). Does not free anything; you are responsible!
565
+ *
566
+ * TODO: Improve this so it can handle non-contiguous element insertions
567
+ * efficiently. For now, we can just sort the elements in the row in
568
+ * question.)
569
+ */
570
+ template <typename DType>
571
+ static char vector_insert(YALE_STORAGE* s, size_t pos, size_t* j, void* val_, size_t n, bool struct_only) {
572
+
573
+ if (pos < s->shape[0]) {
574
+ rb_raise(rb_eArgError, "vector insert pos (%lu) is before beginning of ja (%lu); this should not happen", pos, s->shape[0]);
575
+ }
576
+
577
+ DType* val = reinterpret_cast<DType*>(val_);
578
+
579
+ size_t size = s->ija[s->shape[0]];
580
+
581
+ IType* ija = s->ija;
582
+ DType* a = reinterpret_cast<DType*>(s->a);
583
+
584
+ if (size + n > s->capacity) {
585
+ vector_insert_resize<DType>(s, size, pos, j, n, struct_only);
586
+
587
+ // Need to get the new locations for ija and a.
588
+ ija = s->ija;
589
+ a = reinterpret_cast<DType*>(s->a);
590
+ } else {
591
+ /*
592
+ * No resize required:
593
+ * easy (but somewhat slow), just copy elements to the tail, starting at
594
+ * the end, one element at a time.
595
+ *
596
+ * TODO: This can be made slightly more efficient, but only after the tests
597
+ * are written.
598
+ */
599
+
600
+ if (struct_only) {
601
+ for (size_t i = 0; i < size - pos; ++i) {
602
+ ija[size+n-1-i] = ija[size-1-i];
603
+ }
604
+ } else {
605
+ for (size_t i = 0; i < size - pos; ++i) {
606
+ ija[size+n-1-i] = ija[size-1-i];
607
+ a[size+n-1-i] = a[size-1-i];
608
+ }
609
+ }
610
+ }
611
+
612
+ // Now insert the new values.
613
+ if (struct_only) {
614
+ for (size_t i = 0; i < n; ++i) {
615
+ ija[pos+i] = j[i];
616
+ }
617
+ } else {
618
+ for (size_t i = 0; i < n; ++i) {
619
+ ija[pos+i] = j[i];
620
+ a[pos+i] = val[i];
621
+ }
622
+ }
623
+
624
+ return 'i';
625
+ }
626
+
627
+ /*
628
+ * If we add n items to row i, we need to increment ija[i+1] and onward.
629
+ */
630
+ static void increment_ia_after(YALE_STORAGE* s, IType ija_size, IType i, long n) {
631
+ IType* ija = s->ija;
632
+
633
+ ++i;
634
+ for (; i <= ija_size; ++i) {
635
+ ija[i] += n;
636
+ }
637
+ }
638
+
639
+ /*
640
+ * Binary search for returning insertion points.
641
+ */
642
+ static IType insert_search(YALE_STORAGE* s, IType left, IType right, IType key, bool& found) {
643
+
644
+ if (left > right) {
645
+ found = false;
646
+ return left;
647
+ }
648
+
649
+ IType* ija = s->ija;
650
+ IType mid = (left + right)/2;
651
+ IType mid_j = ija[mid];
652
+
653
+ if (mid_j == key) {
654
+ found = true;
655
+ return mid;
656
+
657
+ } else if (mid_j > key) {
658
+ return insert_search(s, left, mid-1, key, found);
659
+
660
+ } else {
661
+ return insert_search(s, mid+1, right, key, found);
662
+ }
663
+ }
664
+
665
+ /////////////////////////
666
+ // Copying and Casting //
667
+ /////////////////////////
668
+
669
+ /*
670
+ * Templated copy constructor for changing dtypes.
671
+ */
672
+ template <typename L, typename R>
673
+ YALE_STORAGE* cast_copy(const YALE_STORAGE* rhs) {
674
+ YaleStorage<R> y(rhs);
675
+ return y.template alloc_copy<L>();
676
+ }
677
+
678
+ /*
679
+ * Template access for getting the size of Yale storage.
680
+ */
681
+ size_t get_size(const YALE_STORAGE* storage) {
682
+ return storage->ija[ storage->shape[0] ];
683
+ }
684
+
685
+
686
+ template <typename DType>
687
+ static STORAGE* matrix_multiply(const STORAGE_PAIR& casted_storage, size_t* resulting_shape, bool vector) {
688
+ YALE_STORAGE *left = (YALE_STORAGE*)(casted_storage.left),
689
+ *right = (YALE_STORAGE*)(casted_storage.right);
690
+
691
+ nm_yale_storage_register(left);
692
+ nm_yale_storage_register(right);
693
+ // We can safely get dtype from the casted matrices; post-condition of binary_storage_cast_alloc is that dtype is the
694
+ // same for left and right.
695
+ // int8_t dtype = left->dtype;
696
+
697
+ IType* ijl = left->ija;
698
+ IType* ijr = right->ija;
699
+
700
+ // First, count the ndnz of the result.
701
+ // TODO: This basically requires running symbmm twice to get the exact ndnz size. That's frustrating. Are there simple
702
+ // cases where we can avoid running it?
703
+ size_t result_ndnz = nm::math::symbmm(resulting_shape[0], left->shape[1], resulting_shape[1], ijl, ijl, true, ijr, ijr, true, NULL, true);
704
+
705
+ // Create result storage.
706
+ YALE_STORAGE* result = nm_yale_storage_create(left->dtype, resulting_shape, 2, result_ndnz);
707
+ init<DType>(result, NULL);
708
+ IType* ija = result->ija;
709
+
710
+ // Symbolic multiplication step (build the structure)
711
+ nm::math::symbmm(resulting_shape[0], left->shape[1], resulting_shape[1], ijl, ijl, true, ijr, ijr, true, ija, true);
712
+
713
+ // Numeric multiplication step (fill in the elements)
714
+
715
+ nm::math::numbmm<DType>(result->shape[0], left->shape[1], result->shape[1],
716
+ ijl, ijl, reinterpret_cast<DType*>(left->a), true,
717
+ ijr, ijr, reinterpret_cast<DType*>(right->a), true,
718
+ ija, ija, reinterpret_cast<DType*>(result->a), true);
719
+
720
+
721
+ // Sort the columns
722
+ nm::math::smmp_sort_columns<DType>(result->shape[0], ija, ija, reinterpret_cast<DType*>(result->a));
723
+
724
+ nm_yale_storage_unregister(right);
725
+ nm_yale_storage_unregister(left);
726
+ return reinterpret_cast<STORAGE*>(result);
727
+ }
728
+
729
+
730
+ /*
731
+ * Get the sum of offsets from the original matrix (for sliced iteration).
732
+ */
733
+ static std::array<size_t,2> get_offsets(YALE_STORAGE* x) {
734
+ std::array<size_t, 2> offsets{ {0,0} };
735
+ while (x != x->src) {
736
+ offsets[0] += x->offset[0];
737
+ offsets[1] += x->offset[1];
738
+ x = reinterpret_cast<YALE_STORAGE*>(x->src);
739
+ }
740
+ return offsets;
741
+ }
742
+
743
+
744
+ class RowIterator {
745
+ protected:
746
+ YALE_STORAGE* s;
747
+ IType* ija;
748
+ void* a;
749
+ IType i, k, k_end;
750
+ size_t j_offset, j_shape;
751
+ bool diag, End;
752
+ VALUE init;
753
+ public:
754
+ RowIterator(YALE_STORAGE* s_, IType* ija_, IType i_, size_t j_shape_, size_t j_offset_ = 0)
755
+ : s(s_),
756
+ ija(ija_),
757
+ a(reinterpret_cast<YALE_STORAGE*>(s->src)->a),
758
+ i(i_),
759
+ k(ija[i]),
760
+ k_end(ija[i+1]),
761
+ j_offset(j_offset_),
762
+ j_shape(j_shape_),
763
+ diag(row_has_no_nd() || diag_is_first()),
764
+ End(false),
765
+ init(default_value(s))
766
+ { }
767
+
768
+ RowIterator(YALE_STORAGE* s_, IType i_, size_t j_shape_, size_t j_offset_ = 0)
769
+ : s(s_),
770
+ ija(IJA(s)),
771
+ a(reinterpret_cast<YALE_STORAGE*>(s->src)->a),
772
+ i(i_),
773
+ k(ija[i]),
774
+ k_end(ija[i+1]),
775
+ j_offset(j_offset_),
776
+ j_shape(j_shape_),
777
+ diag(row_has_no_nd() || diag_is_first()),
778
+ End(false),
779
+ init(default_value(s))
780
+ { }
781
+
782
+ RowIterator(const RowIterator& rhs) : s(rhs.s), ija(rhs.ija), a(reinterpret_cast<YALE_STORAGE*>(s->src)->a), i(rhs.i), k(rhs.k), k_end(rhs.k_end), j_offset(rhs.j_offset), j_shape(rhs.j_shape), diag(rhs.diag), End(rhs.End), init(rhs.init) { }
783
+
784
+ VALUE obj() const {
785
+ return diag ? obj_at(s, i) : obj_at(s, k);
786
+ }
787
+
788
+ template <typename T>
789
+ T cobj() const {
790
+ if (typeid(T) == typeid(RubyObject)) return obj();
791
+ return A<T>(s)[diag ? i : k];
792
+ }
793
+
794
+ inline IType proper_j() const {
795
+ return diag ? i : ija[k];
796
+ }
797
+
798
+ inline IType offset_j() const {
799
+ return proper_j() - j_offset;
800
+ }
801
+
802
+ inline size_t capacity() const {
803
+ return reinterpret_cast<YALE_STORAGE*>(s->src)->capacity;
804
+ }
805
+
806
+ inline void vector_grow() {
807
+ YALE_STORAGE* src = reinterpret_cast<YALE_STORAGE*>(s->src);
808
+ nm::yale_storage::vector_grow(src);
809
+ ija = reinterpret_cast<IType*>(src->ija);
810
+ a = src->a;
811
+ }
812
+
813
+ /* Returns true if an additional value is inserted, false if it goes on the diagonal */
814
+ bool insert(IType j, VALUE v) {
815
+ if (j == i) { // insert regardless on diagonal
816
+ reinterpret_cast<VALUE*>(a)[j] = v;
817
+ return false;
818
+
819
+ } else {
820
+ if (rb_funcall(v, rb_intern("!="), 1, init) == Qtrue) {
821
+ if (k >= capacity()) {
822
+ vector_grow();
823
+ }
824
+ reinterpret_cast<VALUE*>(a)[k] = v;
825
+ ija[k] = j;
826
+ k++;
827
+ return true;
828
+ }
829
+ return false;
830
+ }
831
+ }
832
+
833
+ void update_row_end() {
834
+ ija[i+1] = k;
835
+ k_end = k;
836
+ }
837
+
838
+ /* Past the j_shape? */
839
+ inline bool end() const {
840
+ if (End) return true;
841
+ //if (diag) return i - j_offset >= j_shape;
842
+ //else return k >= s->capacity || ija[k] - j_offset >= j_shape;
843
+ return (int)(diag ? i : ija[k]) - (int)(j_offset) >= (int)(j_shape);
844
+ }
845
+
846
+ inline bool row_has_no_nd() const { return ija[i] == k_end; /* k_start == k_end */ }
847
+ inline bool diag_is_first() const { return i < ija[ija[i]]; }
848
+ inline bool diag_is_last() const { return i > ija[k_end-1]; } // only works if !row_has_no_nd()
849
+ inline bool k_is_last_nd() const { return k == k_end-1; }
850
+ inline bool k_is_last() const { return k_is_last_nd() && !diag_is_last(); }
851
+ inline bool diag_is_ahead() const { return i > ija[k]; }
852
+ inline bool row_has_diag() const { return i < s->shape[1]; }
853
+ inline bool diag_is_next() const { // assumes we've already tested for diag, row_has_no_nd(), diag_is_first()
854
+ if (i == ija[k]+1) return true; // definite next
855
+ else if (k+1 < k_end && i >= ija[k+1]+1) return false; // at least one item before it
856
+ else return true;
857
+ }
858
+
859
+ RowIterator& operator++() {
860
+ if (diag) { // we're at the diagonal
861
+ if (row_has_no_nd() || diag_is_last()) End = true; // and there are no non-diagonals (or none still to visit)
862
+ diag = false;
863
+ } else if (!row_has_diag()) { // row has no diagonal entries
864
+ if (row_has_no_nd() || k_is_last_nd()) End = true; // row is totally empty, or we're at last entry
865
+ else k++; // still entries to visit
866
+ } else { // not at diag but it exists somewhere in the row, and row has at least one nd entry
867
+ if (diag_is_ahead()) { // diag is ahead
868
+ if (k_is_last_nd()) diag = true; // diag is next and last
869
+ else if (diag_is_next()) { // diag is next and not last
870
+ diag = true;
871
+ k++;
872
+ } else k++; // diag is not next
873
+ } else { // diag is past
874
+ if (k_is_last_nd()) End = true; // and we're at the end
875
+ else k++; // and we're not at the end
876
+ }
877
+ }
878
+
879
+ return *this;
880
+ }
881
+
882
+
883
+ RowIterator operator++(int unused) {
884
+ RowIterator x(*this);
885
+ ++(*this);
886
+ return x;
887
+ }
888
+ };
889
+
890
+
891
+ // Helper function used only for the RETURN_SIZED_ENUMERATOR macro. Returns the length of
892
+ // the matrix's storage.
893
+ static VALUE nm_yale_stored_enumerator_length(VALUE nmatrix) {
894
+ NM_CONSERVATIVE(nm_register_value(&nmatrix));
895
+ YALE_STORAGE* s = NM_STORAGE_YALE(nmatrix);
896
+ YALE_STORAGE* src = s->src == s ? s : reinterpret_cast<YALE_STORAGE*>(s->src);
897
+ size_t ia_size = src->shape[0];
898
+ // FIXME: This needs to be corrected for slicing.
899
+ size_t len = std::min( s->shape[0] + s->offset[0], s->shape[1] + s->offset[1] ) + nm_yale_storage_get_size(src) - ia_size;
900
+ NM_CONSERVATIVE(nm_unregister_value(&nmatrix));
901
+ return INT2FIX(len);
902
+ }
903
+
904
+
905
+ // Helper function used only for the RETURN_SIZED_ENUMERATOR macro. Returns the length of
906
+ // the matrix's storage.
907
+ static VALUE nm_yale_stored_nondiagonal_enumerator_length(VALUE nmatrix) {
908
+ NM_CONSERVATIVE(nm_register_value(&nmatrix));
909
+ YALE_STORAGE* s = NM_STORAGE_YALE(nmatrix);
910
+ if (s->src != s) s = reinterpret_cast<YALE_STORAGE*>(s->src); // need to get the original storage shape
911
+
912
+ size_t ia_size = s->shape[0];
913
+ size_t len = nm_yale_storage_get_size(NM_STORAGE_YALE(nmatrix)) - ia_size;
914
+ NM_CONSERVATIVE(nm_unregister_value(&nmatrix));
915
+ return INT2FIX(len);
916
+ }
917
+
918
+ // Helper function for diagonal length.
919
+ static VALUE nm_yale_stored_diagonal_enumerator_length(VALUE nmatrix) {
920
+ NM_CONSERVATIVE(nm_register_value(&nmatrix));
921
+ YALE_STORAGE* s = NM_STORAGE_YALE(nmatrix);
922
+ size_t len = std::min( s->shape[0] + s->offset[0], s->shape[1] + s->offset[1] );
923
+ NM_CONSERVATIVE(nm_unregister_value(&nmatrix));
924
+ return INT2FIX(len);
925
+ }
926
+
927
+
928
+ // Helper function for full enumerator length.
929
+ static VALUE nm_yale_enumerator_length(VALUE nmatrix) {
930
+ NM_CONSERVATIVE(nm_register_value(&nmatrix));
931
+ YALE_STORAGE* s = NM_STORAGE_YALE(nmatrix);
932
+ size_t len = s->shape[0] * s->shape[1];
933
+ NM_CONSERVATIVE(nm_unregister_value(&nmatrix));
934
+ return INT2FIX(len);
935
+ }
936
+
937
+
938
+ /*
939
+ * Map the stored values of a matrix in storage order.
940
+ */
941
+ template <typename D>
942
+ static VALUE map_stored(VALUE self) {
943
+ NM_CONSERVATIVE(nm_register_value(&self));
944
+ YALE_STORAGE* s = NM_STORAGE_YALE(self);
945
+ YaleStorage<D> y(s);
946
+
947
+ RETURN_SIZED_ENUMERATOR_PRE
948
+ NM_CONSERVATIVE(nm_unregister_value(&self));
949
+ RETURN_SIZED_ENUMERATOR(self, 0, 0, nm_yale_stored_enumerator_length);
950
+
951
+ YALE_STORAGE* r = y.template alloc_copy<nm::RubyObject, true>();
952
+ nm_yale_storage_register(r);
953
+ NMATRIX* m = nm_create(nm::YALE_STORE, reinterpret_cast<STORAGE*>(r));
954
+ VALUE to_return = Data_Wrap_Struct(CLASS_OF(self), nm_mark, nm_delete, m);
955
+ nm_yale_storage_unregister(r);
956
+ NM_CONSERVATIVE(nm_unregister_value(&self));
957
+ return to_return;
958
+ }
959
+
960
+
961
+ /*
962
+ * map_stored which visits the stored entries of two matrices in order.
963
+ */
964
+ template <typename LD, typename RD>
965
+ static VALUE map_merged_stored(VALUE left, VALUE right, VALUE init) {
966
+ nm::YaleStorage<LD> l(NM_STORAGE_YALE(left));
967
+ nm::YaleStorage<RD> r(NM_STORAGE_YALE(right));
968
+ VALUE to_return = l.map_merged_stored(CLASS_OF(left), r, init);
969
+ return to_return;
970
+ }
971
+
972
+
973
+ /*
974
+ * Iterate over the stored entries in Yale (diagonal and non-diagonal non-zeros)
975
+ */
976
+ template <typename DType>
977
+ static VALUE each_stored_with_indices(VALUE nm) {
978
+ NM_CONSERVATIVE(nm_register_value(&nm));
979
+ YALE_STORAGE* s = NM_STORAGE_YALE(nm);
980
+ YaleStorage<DType> y(s);
981
+
982
+ // If we don't have a block, return an enumerator.
983
+ RETURN_SIZED_ENUMERATOR_PRE
984
+ NM_CONSERVATIVE(nm_unregister_value(&nm));
985
+ RETURN_SIZED_ENUMERATOR(nm, 0, 0, nm_yale_stored_enumerator_length);
986
+
987
+ for (typename YaleStorage<DType>::const_stored_diagonal_iterator d = y.csdbegin(); d != y.csdend(); ++d) {
988
+ rb_yield_values(3, ~d, d.rb_i(), d.rb_j());
989
+ }
990
+
991
+ for (typename YaleStorage<DType>::const_row_iterator it = y.cribegin(); it != y.criend(); ++it) {
992
+ for (auto jt = it.ndbegin(); jt != it.ndend(); ++jt) {
993
+ rb_yield_values(3, ~jt, it.rb_i(), jt.rb_j());
994
+ }
995
+ }
996
+
997
+ NM_CONSERVATIVE(nm_unregister_value(&nm));
998
+
999
+ return nm;
1000
+ }
1001
+
1002
+
1003
+ /*
1004
+ * Iterate over the stored diagonal entries in Yale.
1005
+ */
1006
+ template <typename DType>
1007
+ static VALUE stored_diagonal_each_with_indices(VALUE nm) {
1008
+ NM_CONSERVATIVE(nm_register_value(&nm));
1009
+
1010
+ YALE_STORAGE* s = NM_STORAGE_YALE(nm);
1011
+ YaleStorage<DType> y(s);
1012
+
1013
+ // If we don't have a block, return an enumerator.
1014
+ RETURN_SIZED_ENUMERATOR_PRE
1015
+ NM_CONSERVATIVE(nm_unregister_value(&nm));
1016
+ RETURN_SIZED_ENUMERATOR(nm, 0, 0, nm_yale_stored_diagonal_length); // FIXME: need diagonal length
1017
+
1018
+ for (typename YaleStorage<DType>::const_stored_diagonal_iterator d = y.csdbegin(); d != y.csdend(); ++d) {
1019
+ rb_yield_values(3, ~d, d.rb_i(), d.rb_j());
1020
+ }
1021
+
1022
+ NM_CONSERVATIVE(nm_unregister_value(&nm));
1023
+
1024
+ return nm;
1025
+ }
1026
+
1027
+
1028
+ /*
1029
+ * Iterate over the stored diagonal entries in Yale.
1030
+ */
1031
+ template <typename DType>
1032
+ static VALUE stored_nondiagonal_each_with_indices(VALUE nm) {
1033
+ NM_CONSERVATIVE(nm_register_value(&nm));
1034
+
1035
+ YALE_STORAGE* s = NM_STORAGE_YALE(nm);
1036
+ YaleStorage<DType> y(s);
1037
+
1038
+ // If we don't have a block, return an enumerator.
1039
+ RETURN_SIZED_ENUMERATOR_PRE
1040
+ NM_CONSERVATIVE(nm_unregister_value(&nm));
1041
+ RETURN_SIZED_ENUMERATOR(nm, 0, 0, 0); // FIXME: need diagonal length
1042
+
1043
+ for (typename YaleStorage<DType>::const_row_iterator it = y.cribegin(); it != y.criend(); ++it) {
1044
+ for (auto jt = it.ndbegin(); jt != it.ndend(); ++jt) {
1045
+ rb_yield_values(3, ~jt, it.rb_i(), jt.rb_j());
1046
+ }
1047
+ }
1048
+
1049
+ NM_CONSERVATIVE(nm_unregister_value(&nm));
1050
+
1051
+ return nm;
1052
+ }
1053
+
1054
+
1055
+ /*
1056
+ * Iterate over the stored entries in Yale in order of i,j. Visits every diagonal entry, even if it's the default.
1057
+ */
1058
+ template <typename DType>
1059
+ static VALUE each_ordered_stored_with_indices(VALUE nm) {
1060
+ NM_CONSERVATIVE(nm_register_value(&nm));
1061
+
1062
+ YALE_STORAGE* s = NM_STORAGE_YALE(nm);
1063
+ YaleStorage<DType> y(s);
1064
+
1065
+ // If we don't have a block, return an enumerator.
1066
+ RETURN_SIZED_ENUMERATOR_PRE
1067
+ NM_CONSERVATIVE(nm_unregister_value(&nm));
1068
+ RETURN_SIZED_ENUMERATOR(nm, 0, 0, nm_yale_stored_enumerator_length);
1069
+
1070
+ for (typename YaleStorage<DType>::const_row_iterator it = y.cribegin(); it != y.criend(); ++it) {
1071
+ for (auto jt = it.begin(); jt != it.end(); ++jt) {
1072
+ rb_yield_values(3, ~jt, it.rb_i(), jt.rb_j());
1073
+ }
1074
+ }
1075
+
1076
+ NM_CONSERVATIVE(nm_unregister_value(&nm));
1077
+
1078
+ return nm;
1079
+ }
1080
+
1081
+
1082
+ template <typename DType>
1083
+ static VALUE each_with_indices(VALUE nm) {
1084
+ NM_CONSERVATIVE(nm_register_value(&nm));
1085
+
1086
+ YALE_STORAGE* s = NM_STORAGE_YALE(nm);
1087
+ YaleStorage<DType> y(s);
1088
+
1089
+ // If we don't have a block, return an enumerator.
1090
+ RETURN_SIZED_ENUMERATOR_PRE
1091
+ NM_CONSERVATIVE(nm_unregister_value(&nm));
1092
+ RETURN_SIZED_ENUMERATOR(nm, 0, 0, nm_yale_enumerator_length);
1093
+
1094
+ for (typename YaleStorage<DType>::const_iterator iter = y.cbegin(); iter != y.cend(); ++iter) {
1095
+ rb_yield_values(3, ~iter, iter.rb_i(), iter.rb_j());
1096
+ }
1097
+
1098
+ NM_CONSERVATIVE(nm_unregister_value(&nm));
1099
+
1100
+ return nm;
1101
+ }
1102
+
1103
+ template <typename D>
1104
+ static bool is_pos_default_value(YALE_STORAGE* s, size_t apos) {
1105
+ YaleStorage<D> y(s);
1106
+ return y.is_pos_default_value(apos);
1107
+ }
1108
+
1109
+ } // end of namespace nm::yale_storage
1110
+
1111
+ } // end of namespace nm.
1112
+
1113
+ ///////////////////
1114
+ // Ruby Bindings //
1115
+ ///////////////////
1116
+
1117
+ /* These bindings are mostly only for debugging Yale. They are called from Init_nmatrix. */
1118
+
1119
+ extern "C" {
1120
+
1121
+ void nm_init_yale_functions() {
1122
+ /*
1123
+ * This module stores methods that are useful for debugging Yale matrices,
1124
+ * i.e. the ones with +:yale+ stype.
1125
+ */
1126
+ cNMatrix_YaleFunctions = rb_define_module_under(cNMatrix, "YaleFunctions");
1127
+
1128
+ // Expert recommendation. Eventually this should go in a separate gem, or at least a separate module.
1129
+ rb_define_method(cNMatrix_YaleFunctions, "yale_row_keys_intersection", (METHOD)nm_row_keys_intersection, 3);
1130
+
1131
+ // Debugging functions.
1132
+ rb_define_method(cNMatrix_YaleFunctions, "yale_ija", (METHOD)nm_ija, -1);
1133
+ rb_define_method(cNMatrix_YaleFunctions, "yale_a", (METHOD)nm_a, -1);
1134
+ rb_define_method(cNMatrix_YaleFunctions, "yale_size", (METHOD)nm_size, 0);
1135
+ rb_define_method(cNMatrix_YaleFunctions, "yale_ia", (METHOD)nm_ia, 0);
1136
+ rb_define_method(cNMatrix_YaleFunctions, "yale_ja", (METHOD)nm_ja, 0);
1137
+ rb_define_method(cNMatrix_YaleFunctions, "yale_d", (METHOD)nm_d, -1);
1138
+ rb_define_method(cNMatrix_YaleFunctions, "yale_lu", (METHOD)nm_lu, 0);
1139
+
1140
+ rb_define_method(cNMatrix_YaleFunctions, "yale_nd_row", (METHOD)nm_nd_row, -1);
1141
+
1142
+ /* Document-const:
1143
+ * Defines the growth rate of the sparse NMatrix's size. Default is 1.5.
1144
+ */
1145
+ rb_define_const(cNMatrix_YaleFunctions, "YALE_GROWTH_CONSTANT", rb_float_new(nm::yale_storage::GROWTH_CONSTANT));
1146
+
1147
+ // This is so the user can easily check the IType size, mostly for debugging.
1148
+ size_t itype_size = sizeof(IType);
1149
+ VALUE itype_dtype;
1150
+ if (itype_size == sizeof(uint64_t)) {
1151
+ itype_dtype = ID2SYM(rb_intern("int64"));
1152
+ } else if (itype_size == sizeof(uint32_t)) {
1153
+ itype_dtype = ID2SYM(rb_intern("int32"));
1154
+ } else if (itype_size == sizeof(uint16_t)) {
1155
+ itype_dtype = ID2SYM(rb_intern("int16"));
1156
+ } else {
1157
+ rb_raise(rb_eStandardError, "unhandled length for sizeof(IType): %lu; note that IType is probably defined as size_t", sizeof(IType));
1158
+ }
1159
+ rb_define_const(cNMatrix, "INDEX_DTYPE", itype_dtype);
1160
+ }
1161
+
1162
+ /////////////////
1163
+ // C ACCESSORS //
1164
+ /////////////////
1165
+
1166
+ /* C interface for NMatrix#each_with_indices (Yale) */
1167
+ VALUE nm_yale_each_with_indices(VALUE nmatrix) {
1168
+ NAMED_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::each_with_indices, VALUE, VALUE)
1169
+
1170
+ return ttable[ NM_DTYPE(nmatrix) ](nmatrix);
1171
+ }
1172
+
1173
+
1174
+ /* C interface for NMatrix#each_stored_with_indices (Yale) */
1175
+ VALUE nm_yale_each_stored_with_indices(VALUE nmatrix) {
1176
+ NAMED_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::each_stored_with_indices, VALUE, VALUE)
1177
+
1178
+ return ttable[ NM_DTYPE(nmatrix) ](nmatrix);
1179
+ }
1180
+
1181
+
1182
+ /* Iterate along stored diagonal (not actual diagonal!) */
1183
+ VALUE nm_yale_stored_diagonal_each_with_indices(VALUE nmatrix) {
1184
+ NAMED_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::stored_diagonal_each_with_indices, VALUE, VALUE)
1185
+
1186
+ return ttable[ NM_DTYPE(nmatrix) ](nmatrix);
1187
+ }
1188
+
1189
+ /* Iterate through stored nondiagonal (not actual diagonal!) */
1190
+ VALUE nm_yale_stored_nondiagonal_each_with_indices(VALUE nmatrix) {
1191
+ NAMED_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::stored_nondiagonal_each_with_indices, VALUE, VALUE)
1192
+
1193
+ return ttable[ NM_DTYPE(nmatrix) ](nmatrix);
1194
+ }
1195
+
1196
+
1197
+ /* C interface for NMatrix#each_ordered_stored_with_indices (Yale) */
1198
+ VALUE nm_yale_each_ordered_stored_with_indices(VALUE nmatrix) {
1199
+ NAMED_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::each_ordered_stored_with_indices, VALUE, VALUE)
1200
+
1201
+ return ttable[ NM_DTYPE(nmatrix) ](nmatrix);
1202
+ }
1203
+
1204
+
1205
+
1206
+ /*
1207
+ * C accessor for inserting some value in a matrix (or replacing an existing cell).
1208
+ */
1209
+ void nm_yale_storage_set(VALUE left, SLICE* slice, VALUE right) {
1210
+ NAMED_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::set, void, VALUE left, SLICE* slice, VALUE right);
1211
+
1212
+ ttable[NM_DTYPE(left)](left, slice, right);
1213
+ }
1214
+
1215
+
1216
+ /*
1217
+ * Determine the number of non-diagonal non-zeros in a not-yet-created copy of a slice or matrix.
1218
+ */
1219
+ static size_t yale_count_slice_copy_ndnz(const YALE_STORAGE* s, size_t* offset, size_t* shape) {
1220
+ NAMED_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::count_slice_copy_ndnz, size_t, const YALE_STORAGE*, size_t*, size_t*)
1221
+
1222
+ return ttable[s->dtype](s, offset, shape);
1223
+ }
1224
+
1225
+
1226
+ /*
1227
+ * C accessor for yale_storage::get, which returns a slice of YALE_STORAGE object by copy
1228
+ *
1229
+ * Slicing-related.
1230
+ */
1231
+ void* nm_yale_storage_get(const STORAGE* storage, SLICE* slice) {
1232
+ YALE_STORAGE* casted_storage = (YALE_STORAGE*)storage;
1233
+
1234
+ if (slice->single) {
1235
+ NAMED_DTYPE_TEMPLATE_TABLE(elem_copy_table, nm::yale_storage::get_single, void*, YALE_STORAGE*, SLICE*)
1236
+
1237
+ return elem_copy_table[casted_storage->dtype](casted_storage, slice);
1238
+ } else {
1239
+ nm_yale_storage_register(casted_storage);
1240
+ //return reinterpret_cast<void*>(nm::YaleStorage<nm::dtype_enum_T<storage->dtype>::type>(casted_storage).alloc_ref(slice));
1241
+ NAMED_DTYPE_TEMPLATE_TABLE(ref_table, nm::yale_storage::ref, YALE_STORAGE*, YALE_STORAGE* storage, SLICE* slice)
1242
+
1243
+ YALE_STORAGE* ref = ref_table[casted_storage->dtype](casted_storage, slice);
1244
+
1245
+ NAMED_LR_DTYPE_TEMPLATE_TABLE(slice_copy_table, nm::yale_storage::slice_copy, YALE_STORAGE*, YALE_STORAGE*)
1246
+
1247
+ YALE_STORAGE* ns = slice_copy_table[casted_storage->dtype][casted_storage->dtype](ref);
1248
+
1249
+ NM_FREE(ref);
1250
+
1251
+ nm_yale_storage_unregister(casted_storage);
1252
+
1253
+ return ns;
1254
+ }
1255
+ }
1256
+
1257
+ /*
1258
+ * C accessor for yale_storage::vector_insert
1259
+ */
1260
+ static char nm_yale_storage_vector_insert(YALE_STORAGE* s, size_t pos, size_t* js, void* vals, size_t n, bool struct_only, nm::dtype_t dtype) {
1261
+ NAMED_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::vector_insert, char, YALE_STORAGE*, size_t, size_t*, void*, size_t, bool);
1262
+
1263
+ return ttable[dtype](s, pos, js, vals, n, struct_only);
1264
+ }
1265
+
1266
+ /*
1267
+ * C accessor for yale_storage::increment_ia_after, typically called after ::vector_insert
1268
+ */
1269
+ static void nm_yale_storage_increment_ia_after(YALE_STORAGE* s, size_t ija_size, size_t i, long n) {
1270
+ nm::yale_storage::increment_ia_after(s, ija_size, i, n);
1271
+ }
1272
+
1273
+
1274
+ /*
1275
+ * C accessor for yale_storage::ref, which returns either a pointer to the correct location in a YALE_STORAGE object
1276
+ * for some set of coordinates, or a pointer to a single element.
1277
+ */
1278
+ void* nm_yale_storage_ref(const STORAGE* storage, SLICE* slice) {
1279
+ YALE_STORAGE* casted_storage = (YALE_STORAGE*)storage;
1280
+
1281
+ if (slice->single) {
1282
+ //return reinterpret_cast<void*>(nm::YaleStorage<nm::dtype_enum_T<storage->dtype>::type>(casted_storage).get_single_p(slice));
1283
+ NAMED_DTYPE_TEMPLATE_TABLE(elem_copy_table, nm::yale_storage::get_single, void*, YALE_STORAGE*, SLICE*)
1284
+ return elem_copy_table[casted_storage->dtype](casted_storage, slice);
1285
+ } else {
1286
+ //return reinterpret_cast<void*>(nm::YaleStorage<nm::dtype_enum_T<storage->dtype>::type>(casted_storage).alloc_ref(slice));
1287
+ NAMED_DTYPE_TEMPLATE_TABLE(ref_table, nm::yale_storage::ref, YALE_STORAGE*, YALE_STORAGE* storage, SLICE* slice)
1288
+ return reinterpret_cast<void*>(ref_table[casted_storage->dtype](casted_storage, slice));
1289
+
1290
+ }
1291
+ }
1292
+
1293
+
1294
+ /*
1295
+ * C accessor for determining whether two YALE_STORAGE objects have the same contents.
1296
+ */
1297
+ bool nm_yale_storage_eqeq(const STORAGE* left, const STORAGE* right) {
1298
+ NAMED_LR_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::eqeq, bool, const YALE_STORAGE* left, const YALE_STORAGE* right);
1299
+
1300
+ const YALE_STORAGE* casted_left = reinterpret_cast<const YALE_STORAGE*>(left);
1301
+
1302
+ return ttable[casted_left->dtype][right->dtype](casted_left, (const YALE_STORAGE*)right);
1303
+ }
1304
+
1305
+
1306
+ /*
1307
+ * Copy constructor for changing dtypes. (C accessor)
1308
+ */
1309
+ STORAGE* nm_yale_storage_cast_copy(const STORAGE* rhs, nm::dtype_t new_dtype, void* dummy) {
1310
+ NAMED_LR_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::cast_copy, YALE_STORAGE*, const YALE_STORAGE* rhs);
1311
+
1312
+ const YALE_STORAGE* casted_rhs = reinterpret_cast<const YALE_STORAGE*>(rhs);
1313
+ //return reinterpret_cast<STORAGE*>(nm::YaleStorage<nm::dtype_enum_T< rhs->dtype >::type>(rhs).alloc_copy<nm::dtype_enum_T< new_dtype >::type>());
1314
+ return (STORAGE*)ttable[new_dtype][casted_rhs->dtype](casted_rhs);
1315
+ }
1316
+
1317
+
1318
+ /*
1319
+ * Returns size of Yale storage as a size_t (no matter what the itype is). (C accessor)
1320
+ */
1321
+ size_t nm_yale_storage_get_size(const YALE_STORAGE* storage) {
1322
+ return nm::yale_storage::get_size(storage);
1323
+ }
1324
+
1325
+
1326
+
1327
+ /*
1328
+ * Return a pointer to the matrix's default value entry.
1329
+ */
1330
+ static void* default_value_ptr(const YALE_STORAGE* s) {
1331
+ return reinterpret_cast<void*>(reinterpret_cast<char*>(((YALE_STORAGE*)(s->src))->a) + (((YALE_STORAGE*)(s->src))->shape[0] * DTYPE_SIZES[s->dtype]));
1332
+ }
1333
+
1334
+ /*
1335
+ * Return the Ruby object at a given location in storage.
1336
+ */
1337
+ static VALUE obj_at(YALE_STORAGE* s, size_t k) {
1338
+ if (s->dtype == nm::RUBYOBJ) return reinterpret_cast<VALUE*>(((YALE_STORAGE*)(s->src))->a)[k];
1339
+ else return nm::rubyobj_from_cval(reinterpret_cast<void*>(reinterpret_cast<char*>(((YALE_STORAGE*)(s->src))->a) + k * DTYPE_SIZES[s->dtype]), s->dtype).rval;
1340
+ }
1341
+
1342
+
1343
+ /*
1344
+ * Return the matrix's default value as a Ruby VALUE.
1345
+ */
1346
+ static VALUE default_value(const YALE_STORAGE* s) {
1347
+ if (s->dtype == nm::RUBYOBJ) return *reinterpret_cast<VALUE*>(default_value_ptr(s));
1348
+ else return nm::rubyobj_from_cval(default_value_ptr(s), s->dtype).rval;
1349
+ }
1350
+
1351
+
1352
+ /*
1353
+ * Check to see if a default value is some form of zero. Easy for non-Ruby object matrices, which should always be 0.
1354
+ */
1355
+ static bool default_value_is_numeric_zero(const YALE_STORAGE* s) {
1356
+ return rb_funcall(default_value(s), rb_intern("=="), 1, INT2FIX(0)) == Qtrue;
1357
+ }
1358
+
1359
+
1360
+
1361
+ /*
1362
+ * Transposing copy constructor.
1363
+ */
1364
+ STORAGE* nm_yale_storage_copy_transposed(const STORAGE* rhs_base) {
1365
+ YALE_STORAGE* rhs = (YALE_STORAGE*)rhs_base;
1366
+ NAMED_DTYPE_TEMPLATE_TABLE(transp, nm::yale_storage::copy_transposed, YALE_STORAGE*, YALE_STORAGE*)
1367
+ return (STORAGE*)(transp[rhs->dtype](rhs));
1368
+ }
1369
+
1370
+ /*
1371
+ * C accessor for multiplying two YALE_STORAGE matrices, which have already been casted to the same dtype.
1372
+ *
1373
+ * FIXME: There should be some mathematical way to determine the worst-case IType based on the input ITypes. Right now
1374
+ * it just uses the default.
1375
+ */
1376
+ STORAGE* nm_yale_storage_matrix_multiply(const STORAGE_PAIR& casted_storage, size_t* resulting_shape, bool vector) {
1377
+ DTYPE_TEMPLATE_TABLE(nm::yale_storage::matrix_multiply, STORAGE*, const STORAGE_PAIR& casted_storage, size_t* resulting_shape, bool vector);
1378
+
1379
+ YALE_STORAGE* left = reinterpret_cast<YALE_STORAGE*>(casted_storage.left);
1380
+ YALE_STORAGE* right = reinterpret_cast<YALE_STORAGE*>(casted_storage.right);
1381
+
1382
+ if (!default_value_is_numeric_zero(left) || !default_value_is_numeric_zero(right)) {
1383
+ rb_raise(rb_eNotImpError, "matrix default value must be some form of zero (not false or nil) for multiplication");
1384
+ return NULL;
1385
+ }
1386
+
1387
+ return ttable[left->dtype](casted_storage, resulting_shape, vector);
1388
+ }
1389
+
1390
+
1391
+ ///////////////
1392
+ // Lifecycle //
1393
+ ///////////////
1394
+
1395
+ /*
1396
+ * C accessor function for creating a YALE_STORAGE object. Prior to calling this function, you MUST
1397
+ * allocate shape (should be size_t * 2) -- don't use use a regular size_t array!
1398
+ *
1399
+ * For this type, dim must always be 2. The final argument is the initial capacity with which to
1400
+ * create the storage.
1401
+ */
1402
+
1403
+ YALE_STORAGE* nm_yale_storage_create(nm::dtype_t dtype, size_t* shape, size_t dim, size_t init_capacity) {
1404
+ if (dim != 2) {
1405
+ rb_raise(nm_eStorageTypeError, "yale supports only 2-dimensional matrices");
1406
+ }
1407
+ DTYPE_OBJECT_STATIC_TABLE(nm::YaleStorage, create, YALE_STORAGE*, size_t* shape, size_t init_capacity)
1408
+ return ttable[dtype](shape, init_capacity);
1409
+ }
1410
+
1411
+ /*
1412
+ * Destructor for yale storage (C-accessible).
1413
+ */
1414
+ void nm_yale_storage_delete(STORAGE* s) {
1415
+ if (s) {
1416
+ YALE_STORAGE* storage = (YALE_STORAGE*)s;
1417
+ if (storage->count-- == 1) {
1418
+ NM_FREE(storage->shape);
1419
+ NM_FREE(storage->offset);
1420
+ NM_FREE(storage->ija);
1421
+ NM_FREE(storage->a);
1422
+ NM_FREE(storage);
1423
+ }
1424
+ }
1425
+ }
1426
+
1427
+ /*
1428
+ * Destructor for the yale storage ref
1429
+ */
1430
+ void nm_yale_storage_delete_ref(STORAGE* s) {
1431
+ if (s) {
1432
+ YALE_STORAGE* storage = (YALE_STORAGE*)s;
1433
+ nm_yale_storage_delete( reinterpret_cast<STORAGE*>(storage->src) );
1434
+ NM_FREE(storage->shape);
1435
+ NM_FREE(storage->offset);
1436
+ NM_FREE(s);
1437
+ }
1438
+ }
1439
+
1440
+ /*
1441
+ * C accessor for yale_storage::init, a templated function.
1442
+ *
1443
+ * Initializes the IJA vector of the YALE_STORAGE matrix.
1444
+ */
1445
+ void nm_yale_storage_init(YALE_STORAGE* s, void* init_val) {
1446
+ DTYPE_TEMPLATE_TABLE(nm::yale_storage::init, void, YALE_STORAGE*, void*);
1447
+
1448
+ ttable[s->dtype](s, init_val);
1449
+ }
1450
+
1451
+
1452
+ /*
1453
+ * Ruby GC mark function for YALE_STORAGE. C accessible.
1454
+ */
1455
+ void nm_yale_storage_mark(STORAGE* storage_base) {
1456
+ YALE_STORAGE* storage = (YALE_STORAGE*)storage_base;
1457
+
1458
+ if (storage && storage->dtype == nm::RUBYOBJ) {
1459
+
1460
+ VALUE* a = (VALUE*)(storage->a);
1461
+ rb_gc_mark_locations(a, &(a[storage->capacity-1]));
1462
+ }
1463
+ }
1464
+
1465
+ void nm_yale_storage_register_a(void* a, size_t size) {
1466
+ nm_register_values(reinterpret_cast<VALUE*>(a), size);
1467
+ }
1468
+
1469
+ void nm_yale_storage_unregister_a(void* a, size_t size) {
1470
+ nm_unregister_values(reinterpret_cast<VALUE*>(a), size);
1471
+ }
1472
+
1473
+ void nm_yale_storage_register(const STORAGE* s) {
1474
+ const YALE_STORAGE* y = reinterpret_cast<const YALE_STORAGE*>(s);
1475
+ if (y->dtype == nm::RUBYOBJ) {
1476
+ nm_register_values(reinterpret_cast<VALUE*>(y->a), nm::yale_storage::get_size(y));
1477
+ }
1478
+ }
1479
+
1480
+ void nm_yale_storage_unregister(const STORAGE* s) {
1481
+ const YALE_STORAGE* y = reinterpret_cast<const YALE_STORAGE*>(s);
1482
+ if (y->dtype == nm::RUBYOBJ) {
1483
+ nm_unregister_values(reinterpret_cast<VALUE*>(y->a), nm::yale_storage::get_size(y));
1484
+ }
1485
+ }
1486
+
1487
+ /*
1488
+ * Allocates and initializes the basic struct (but not the IJA or A vectors).
1489
+ *
1490
+ * This function is ONLY used when creating from old yale.
1491
+ */
1492
+ static YALE_STORAGE* alloc(nm::dtype_t dtype, size_t* shape, size_t dim) {
1493
+ YALE_STORAGE* s;
1494
+
1495
+ s = NM_ALLOC( YALE_STORAGE );
1496
+
1497
+ s->ndnz = 0;
1498
+ s->dtype = dtype;
1499
+ s->shape = shape;
1500
+ s->offset = NM_ALLOC_N(size_t, dim);
1501
+ for (size_t i = 0; i < dim; ++i)
1502
+ s->offset[i] = 0;
1503
+ s->dim = dim;
1504
+ s->src = reinterpret_cast<STORAGE*>(s);
1505
+ s->count = 1;
1506
+
1507
+ return s;
1508
+ }
1509
+
1510
+ YALE_STORAGE* nm_yale_storage_create_from_old_yale(nm::dtype_t dtype, size_t* shape, char* ia, char* ja, char* a, nm::dtype_t from_dtype) {
1511
+ NAMED_LR_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::create_from_old_yale, YALE_STORAGE*, nm::dtype_t dtype, size_t* shape, char* r_ia, char* r_ja, char* r_a);
1512
+
1513
+ return ttable[dtype][from_dtype](dtype, shape, ia, ja, a);
1514
+
1515
+ }
1516
+
1517
+ //////////////////////////////////////////////
1518
+ // YALE-SPECIFIC FUNCTIONS (RUBY ACCESSORS) //
1519
+ //////////////////////////////////////////////
1520
+
1521
+ /*
1522
+ * call-seq:
1523
+ * yale_size -> Integer
1524
+ *
1525
+ * Get the size of a Yale matrix (the number of elements actually stored).
1526
+ *
1527
+ * For capacity (the maximum number of elements that can be stored without a resize), use capacity instead.
1528
+ */
1529
+ static VALUE nm_size(VALUE self) {
1530
+ YALE_STORAGE* s = (YALE_STORAGE*)(NM_SRC(self));
1531
+ VALUE to_return = INT2FIX(nm::yale_storage::IJA(s)[s->shape[0]]);
1532
+ return to_return;
1533
+ }
1534
+
1535
+
1536
+ /*
1537
+ * Determine if some pos in the diagonal is the default. No bounds checking!
1538
+ */
1539
+ static bool is_pos_default_value(YALE_STORAGE* s, size_t apos) {
1540
+ DTYPE_TEMPLATE_TABLE(nm::yale_storage::is_pos_default_value, bool, YALE_STORAGE*, size_t)
1541
+ return ttable[s->dtype](s, apos);
1542
+ }
1543
+
1544
+
1545
+ /*
1546
+ * call-seq:
1547
+ * yale_row_keys_intersection(i, m2, i2) -> Array
1548
+ *
1549
+ * This function is experimental.
1550
+ *
1551
+ * It finds the intersection of row i of the current matrix with row i2 of matrix m2.
1552
+ * Both matrices must be Yale. They may not be slices.
1553
+ *
1554
+ * Only checks the stored indices; does not care about matrix default value.
1555
+ */
1556
+ static VALUE nm_row_keys_intersection(VALUE m1, VALUE ii1, VALUE m2, VALUE ii2) {
1557
+
1558
+ NM_CONSERVATIVE(nm_register_value(&m1));
1559
+ NM_CONSERVATIVE(nm_register_value(&m2));
1560
+
1561
+ if (NM_SRC(m1) != NM_STORAGE(m1) || NM_SRC(m2) != NM_STORAGE(m2)) {
1562
+ NM_CONSERVATIVE(nm_unregister_value(&m2));
1563
+ NM_CONSERVATIVE(nm_unregister_value(&m1));
1564
+ rb_raise(rb_eNotImpError, "must be called on a real matrix and not a slice");
1565
+ }
1566
+
1567
+ size_t i1 = FIX2INT(ii1),
1568
+ i2 = FIX2INT(ii2);
1569
+
1570
+ YALE_STORAGE *s = NM_STORAGE_YALE(m1),
1571
+ *t = NM_STORAGE_YALE(m2);
1572
+
1573
+ size_t pos1 = s->ija[i1],
1574
+ pos2 = t->ija[i2];
1575
+
1576
+ size_t nextpos1 = s->ija[i1+1],
1577
+ nextpos2 = t->ija[i2+1];
1578
+
1579
+ size_t diff1 = nextpos1 - pos1,
1580
+ diff2 = nextpos2 - pos2;
1581
+
1582
+ // Does the diagonal have a nonzero in it?
1583
+ bool diag1 = i1 < s->shape[0] && !is_pos_default_value(s, i1),
1584
+ diag2 = i2 < t->shape[0] && !is_pos_default_value(t, i2);
1585
+
1586
+ // Reserve max(diff1,diff2) space -- that's the max intersection possible.
1587
+ VALUE ret = rb_ary_new2(std::max(diff1,diff2)+1);
1588
+ nm_register_value(&ret);
1589
+
1590
+ // Handle once the special case where both have the diagonal in exactly
1591
+ // the same place.
1592
+ if (diag1 && diag2 && i1 == i2) {
1593
+ rb_ary_push(ret, INT2FIX(i1));
1594
+ diag1 = false; diag2 = false; // no need to deal with diagonals anymore.
1595
+ }
1596
+
1597
+ // Now find the intersection.
1598
+ size_t idx1 = pos1, idx2 = pos2;
1599
+ while (idx1 < nextpos1 && idx2 < nextpos2) {
1600
+ if (s->ija[idx1] == t->ija[idx2]) {
1601
+ rb_ary_push(ret, INT2FIX(s->ija[idx1]));
1602
+ ++idx1; ++idx2;
1603
+ } else if (diag1 && i1 == t->ija[idx2]) {
1604
+ rb_ary_push(ret, INT2FIX(i1));
1605
+ diag1 = false;
1606
+ ++idx2;
1607
+ } else if (diag2 && i2 == s->ija[idx1]) {
1608
+ rb_ary_push(ret, INT2FIX(i2));
1609
+ diag2 = false;
1610
+ ++idx1;
1611
+ } else if (s->ija[idx1] < t->ija[idx2]) {
1612
+ ++idx1;
1613
+ } else { // s->ija[idx1] > t->ija[idx2]
1614
+ ++idx2;
1615
+ }
1616
+ }
1617
+
1618
+ // Past the end of row i2's stored entries; need to try to find diagonal
1619
+ if (diag2 && idx1 < nextpos1) {
1620
+ idx1 = nm::yale_storage::binary_search_left_boundary(s, idx1, nextpos1, i2);
1621
+ if (s->ija[idx1] == i2) rb_ary_push(ret, INT2FIX(i2));
1622
+ }
1623
+
1624
+ // Find the diagonal, if possible, in the other one.
1625
+ if (diag1 && idx2 < nextpos2) {
1626
+ idx2 = nm::yale_storage::binary_search_left_boundary(t, idx2, nextpos2, i1);
1627
+ if (t->ija[idx2] == i1) rb_ary_push(ret, INT2FIX(i1));
1628
+ }
1629
+
1630
+ nm_unregister_value(&ret);
1631
+ NM_CONSERVATIVE(nm_unregister_value(&m1));
1632
+ NM_CONSERVATIVE(nm_unregister_value(&m2));
1633
+
1634
+ return ret;
1635
+ }
1636
+
1637
+
1638
+ /*
1639
+ * call-seq:
1640
+ * yale_a -> Array
1641
+ * yale_d(index) -> ...
1642
+ *
1643
+ * Get the A array of a Yale matrix (which stores the diagonal and the LU portions of the matrix).
1644
+ */
1645
+ static VALUE nm_a(int argc, VALUE* argv, VALUE self) {
1646
+ NM_CONSERVATIVE(nm_register_value(&self));
1647
+
1648
+ VALUE idx;
1649
+ rb_scan_args(argc, argv, "01", &idx);
1650
+ NM_CONSERVATIVE(nm_register_value(&idx));
1651
+
1652
+ YALE_STORAGE* s = reinterpret_cast<YALE_STORAGE*>(NM_SRC(self));
1653
+ size_t size = nm_yale_storage_get_size(s);
1654
+
1655
+ if (idx == Qnil) {
1656
+
1657
+ VALUE* vals = NM_ALLOCA_N(VALUE, size);
1658
+
1659
+ nm_register_values(vals, size);
1660
+
1661
+ if (NM_DTYPE(self) == nm::RUBYOBJ) {
1662
+ for (size_t i = 0; i < size; ++i) {
1663
+ vals[i] = reinterpret_cast<VALUE*>(s->a)[i];
1664
+ }
1665
+ } else {
1666
+ for (size_t i = 0; i < size; ++i) {
1667
+ vals[i] = nm::rubyobj_from_cval((char*)(s->a) + DTYPE_SIZES[s->dtype]*i, s->dtype).rval;
1668
+ }
1669
+ }
1670
+ VALUE ary = rb_ary_new4(size, vals);
1671
+
1672
+ for (size_t i = size; i < s->capacity; ++i)
1673
+ rb_ary_push(ary, Qnil);
1674
+
1675
+ nm_unregister_values(vals, size);
1676
+ NM_CONSERVATIVE(nm_unregister_value(&idx));
1677
+ NM_CONSERVATIVE(nm_unregister_value(&self));
1678
+ return ary;
1679
+ } else {
1680
+ size_t index = FIX2INT(idx);
1681
+ NM_CONSERVATIVE(nm_unregister_value(&idx));
1682
+ NM_CONSERVATIVE(nm_unregister_value(&self));
1683
+ if (index >= size) rb_raise(rb_eRangeError, "out of range");
1684
+ return nm::rubyobj_from_cval((char*)(s->a) + DTYPE_SIZES[s->dtype] * index, s->dtype).rval;
1685
+ }
1686
+ }
1687
+
1688
+
1689
+ /*
1690
+ * call-seq:
1691
+ * yale_d -> Array
1692
+ * yale_d(index) -> ...
1693
+ *
1694
+ * Get the diagonal ("D") portion of the A array of a Yale matrix.
1695
+ */
1696
+ static VALUE nm_d(int argc, VALUE* argv, VALUE self) {
1697
+ NM_CONSERVATIVE(nm_register_value(&self));
1698
+ VALUE idx;
1699
+ rb_scan_args(argc, argv, "01", &idx);
1700
+ NM_CONSERVATIVE(nm_register_value(&idx));
1701
+
1702
+ YALE_STORAGE* s = reinterpret_cast<YALE_STORAGE*>(NM_SRC(self));
1703
+
1704
+ if (idx == Qnil) {
1705
+ VALUE* vals = NM_ALLOCA_N(VALUE, s->shape[0]);
1706
+
1707
+ nm_register_values(vals, s->shape[0]);
1708
+
1709
+ if (NM_DTYPE(self) == nm::RUBYOBJ) {
1710
+ for (size_t i = 0; i < s->shape[0]; ++i) {
1711
+ vals[i] = reinterpret_cast<VALUE*>(s->a)[i];
1712
+ }
1713
+ } else {
1714
+ for (size_t i = 0; i < s->shape[0]; ++i) {
1715
+ vals[i] = nm::rubyobj_from_cval((char*)(s->a) + DTYPE_SIZES[s->dtype]*i, s->dtype).rval;
1716
+ }
1717
+ }
1718
+ nm_unregister_values(vals, s->shape[0]);
1719
+ NM_CONSERVATIVE(nm_unregister_value(&idx));
1720
+ NM_CONSERVATIVE(nm_unregister_value(&self));
1721
+
1722
+ return rb_ary_new4(s->shape[0], vals);
1723
+ } else {
1724
+ size_t index = FIX2INT(idx);
1725
+ NM_CONSERVATIVE(nm_unregister_value(&idx));
1726
+ NM_CONSERVATIVE(nm_unregister_value(&self));
1727
+ if (index >= s->shape[0]) rb_raise(rb_eRangeError, "out of range");
1728
+ return nm::rubyobj_from_cval((char*)(s->a) + DTYPE_SIZES[s->dtype] * index, s->dtype).rval;
1729
+ }
1730
+ }
1731
+
1732
+ /*
1733
+ * call-seq:
1734
+ * yale_lu -> Array
1735
+ *
1736
+ * Get the non-diagonal ("LU") portion of the A array of a Yale matrix.
1737
+ */
1738
+ static VALUE nm_lu(VALUE self) {
1739
+ NM_CONSERVATIVE(nm_register_value(&self));
1740
+
1741
+ YALE_STORAGE* s = reinterpret_cast<YALE_STORAGE*>(NM_SRC(self));
1742
+
1743
+ size_t size = nm_yale_storage_get_size(s);
1744
+
1745
+ VALUE* vals = NM_ALLOCA_N(VALUE, size - s->shape[0] - 1);
1746
+
1747
+ nm_register_values(vals, size - s->shape[0] - 1);
1748
+
1749
+ if (NM_DTYPE(self) == nm::RUBYOBJ) {
1750
+ for (size_t i = 0; i < size - s->shape[0] - 1; ++i) {
1751
+ vals[i] = reinterpret_cast<VALUE*>(s->a)[s->shape[0] + 1 + i];
1752
+ }
1753
+ } else {
1754
+ for (size_t i = 0; i < size - s->shape[0] - 1; ++i) {
1755
+ vals[i] = nm::rubyobj_from_cval((char*)(s->a) + DTYPE_SIZES[s->dtype]*(s->shape[0] + 1 + i), s->dtype).rval;
1756
+ }
1757
+ }
1758
+
1759
+ VALUE ary = rb_ary_new4(size - s->shape[0] - 1, vals);
1760
+
1761
+ for (size_t i = size; i < s->capacity; ++i)
1762
+ rb_ary_push(ary, Qnil);
1763
+
1764
+ nm_unregister_values(vals, size - s->shape[0] - 1);
1765
+ NM_CONSERVATIVE(nm_unregister_value(&self));
1766
+
1767
+ return ary;
1768
+ }
1769
+
1770
+ /*
1771
+ * call-seq:
1772
+ * yale_ia -> Array
1773
+ *
1774
+ * Get the IA portion of the IJA array of a Yale matrix. This gives the start and end positions of rows in the
1775
+ * JA and LU portions of the IJA and A arrays, respectively.
1776
+ */
1777
+ static VALUE nm_ia(VALUE self) {
1778
+ NM_CONSERVATIVE(nm_register_value(&self));
1779
+
1780
+ YALE_STORAGE* s = reinterpret_cast<YALE_STORAGE*>(NM_SRC(self));
1781
+
1782
+ VALUE* vals = NM_ALLOCA_N(VALUE, s->shape[0] + 1);
1783
+
1784
+ for (size_t i = 0; i < s->shape[0] + 1; ++i) {
1785
+ vals[i] = INT2FIX(s->ija[i]);
1786
+ }
1787
+
1788
+ NM_CONSERVATIVE(nm_unregister_value(&self));
1789
+
1790
+ return rb_ary_new4(s->shape[0]+1, vals);
1791
+ }
1792
+
1793
+ /*
1794
+ * call-seq:
1795
+ * yale_ja -> Array
1796
+ *
1797
+ * Get the JA portion of the IJA array of a Yale matrix. This gives the column indices for entries in corresponding
1798
+ * positions in the LU portion of the A array.
1799
+ */
1800
+ static VALUE nm_ja(VALUE self) {
1801
+
1802
+ NM_CONSERVATIVE(nm_register_value(&self));
1803
+
1804
+ YALE_STORAGE* s = reinterpret_cast<YALE_STORAGE*>(NM_SRC(self));
1805
+
1806
+ size_t size = nm_yale_storage_get_size(s);
1807
+
1808
+ VALUE* vals = NM_ALLOCA_N(VALUE, size - s->shape[0] - 1);
1809
+
1810
+ nm_register_values(vals, size - s->shape[0] - 1);
1811
+
1812
+ for (size_t i = 0; i < size - s->shape[0] - 1; ++i) {
1813
+ vals[i] = INT2FIX(s->ija[s->shape[0] + 1 + i]);
1814
+ }
1815
+
1816
+ VALUE ary = rb_ary_new4(size - s->shape[0] - 1, vals);
1817
+
1818
+ for (size_t i = size; i < s->capacity; ++i)
1819
+ rb_ary_push(ary, Qnil);
1820
+
1821
+ nm_unregister_values(vals, size - s->shape[0] - 1);
1822
+ NM_CONSERVATIVE(nm_unregister_value(&self));
1823
+
1824
+ return ary;
1825
+ }
1826
+
1827
+ /*
1828
+ * call-seq:
1829
+ * yale_ija -> Array
1830
+ * yale_ija(index) -> ...
1831
+ *
1832
+ * Get the IJA array of a Yale matrix (or a component of the IJA array).
1833
+ */
1834
+ static VALUE nm_ija(int argc, VALUE* argv, VALUE self) {
1835
+ NM_CONSERVATIVE(nm_register_value(&self));
1836
+
1837
+ VALUE idx;
1838
+ rb_scan_args(argc, argv, "01", &idx);
1839
+ NM_CONSERVATIVE(nm_register_value(&idx));
1840
+
1841
+ YALE_STORAGE* s = reinterpret_cast<YALE_STORAGE*>(NM_SRC(self));
1842
+ size_t size = nm_yale_storage_get_size(s);
1843
+
1844
+ if (idx == Qnil) {
1845
+
1846
+ VALUE* vals = NM_ALLOCA_N(VALUE, size);
1847
+
1848
+ nm_register_values(vals, size);
1849
+
1850
+ for (size_t i = 0; i < size; ++i) {
1851
+ vals[i] = INT2FIX(s->ija[i]);
1852
+ }
1853
+
1854
+ VALUE ary = rb_ary_new4(size, vals);
1855
+
1856
+ for (size_t i = size; i < s->capacity; ++i)
1857
+ rb_ary_push(ary, Qnil);
1858
+
1859
+ nm_unregister_values(vals, size);
1860
+ NM_CONSERVATIVE(nm_unregister_value(&idx));
1861
+ NM_CONSERVATIVE(nm_unregister_value(&self));
1862
+
1863
+ return ary;
1864
+
1865
+ } else {
1866
+ size_t index = FIX2INT(idx);
1867
+ if (index >= size) rb_raise(rb_eRangeError, "out of range");
1868
+ NM_CONSERVATIVE(nm_unregister_value(&self));
1869
+ NM_CONSERVATIVE(nm_unregister_value(&idx));
1870
+ return INT2FIX(s->ija[index]);
1871
+ }
1872
+ }
1873
+
1874
+
1875
+ /*
1876
+ * call-seq:
1877
+ * yale_nd_row -> ...
1878
+ *
1879
+ * This function gets the non-diagonal contents of a Yale matrix row.
1880
+ * The first argument should be the row index. The optional second argument may be :hash or :keys, but defaults
1881
+ * to :hash. If :keys is given, it will only return the Hash keys (the column indices).
1882
+ *
1883
+ * This function is meant to accomplish its purpose as efficiently as possible. It does not check for appropriate
1884
+ * range.
1885
+ */
1886
+ static VALUE nm_nd_row(int argc, VALUE* argv, VALUE self) {
1887
+
1888
+ NM_CONSERVATIVE(nm_register_value(&self));
1889
+ if (NM_SRC(self) != NM_STORAGE(self)) {
1890
+ NM_CONSERVATIVE(nm_unregister_value(&self));
1891
+ rb_raise(rb_eNotImpError, "must be called on a real matrix and not a slice");
1892
+ }
1893
+
1894
+ VALUE i_, as;
1895
+ rb_scan_args(argc, argv, "11", &i_, &as);
1896
+ NM_CONSERVATIVE(nm_register_value(&as));
1897
+ NM_CONSERVATIVE(nm_register_value(&i_));
1898
+
1899
+ bool keys = false;
1900
+ if (as != Qnil && rb_to_id(as) != nm_rb_hash) keys = true;
1901
+
1902
+ size_t i = FIX2INT(i_);
1903
+
1904
+ YALE_STORAGE* s = NM_STORAGE_YALE(self);
1905
+ //nm::dtype_t dtype = NM_DTYPE(self);
1906
+
1907
+ if (i >= s->shape[0]) {
1908
+ NM_CONSERVATIVE(nm_unregister_value(&self));
1909
+ NM_CONSERVATIVE(nm_unregister_value(&as));
1910
+ NM_CONSERVATIVE(nm_unregister_value(&i_));
1911
+ rb_raise(rb_eRangeError, "out of range (%lu >= %lu)", i, s->shape[0]);
1912
+ }
1913
+
1914
+ size_t pos = s->ija[i];
1915
+ size_t nextpos = s->ija[i+1];
1916
+ size_t diff = nextpos - pos;
1917
+
1918
+ VALUE ret;
1919
+ if (keys) {
1920
+ ret = rb_ary_new3(diff);
1921
+
1922
+ for (size_t idx = pos; idx < nextpos; ++idx) {
1923
+ rb_ary_store(ret, idx - pos, INT2FIX(s->ija[idx]));
1924
+ }
1925
+
1926
+ } else {
1927
+ ret = rb_hash_new();
1928
+
1929
+ for (size_t idx = pos; idx < nextpos; ++idx) {
1930
+ rb_hash_aset(ret, INT2FIX(s->ija[idx]), nm::rubyobj_from_cval((char*)(s->a) + DTYPE_SIZES[s->dtype]*idx, s->dtype).rval);
1931
+ }
1932
+ }
1933
+ NM_CONSERVATIVE(nm_unregister_value(&as));
1934
+ NM_CONSERVATIVE(nm_unregister_value(&i_));
1935
+ NM_CONSERVATIVE(nm_unregister_value(&self));
1936
+ return ret;
1937
+ }
1938
+
1939
+ /*
1940
+ * call-seq:
1941
+ * yale_vector_set(i, column_index_array, cell_contents_array, pos) -> Fixnum
1942
+ *
1943
+ * Insert at position pos an array of non-diagonal elements with column indices given. Note that the column indices and values
1944
+ * must be storage-contiguous -- that is, you can't insert them around existing elements in some row, only amid some
1945
+ * elements in some row. You *can* insert them around a diagonal element, since this is stored separately. This function
1946
+ * may not be used for the insertion of diagonal elements in most cases, as these are already present in the data
1947
+ * structure and are typically modified by replacement rather than insertion.
1948
+ *
1949
+ * The last argument, pos, may be nil if you want to insert at the beginning of a row. Otherwise it needs to be provided.
1950
+ * Don't expect this function to know the difference. It really does very little checking, because its goal is to make
1951
+ * multiple contiguous insertion as quick as possible.
1952
+ *
1953
+ * You should also not attempt to insert values which are the default (0). These are not supposed to be stored, and may
1954
+ * lead to undefined behavior.
1955
+ *
1956
+ * Example:
1957
+ * m.yale_vector_set(3, [0,3,4], [1,1,1], 15)
1958
+ *
1959
+ * The example above inserts the values 1, 1, and 1 in columns 0, 3, and 4, assumed to be located at position 15 (which
1960
+ * corresponds to row 3).
1961
+ *
1962
+ * Example:
1963
+ * next = m.yale_vector_set(3, [0,3,4], [1,1,1])
1964
+ *
1965
+ * This example determines that i=3 is at position 15 automatically. The value returned, next, is the position where the
1966
+ * next value(s) should be inserted.
1967
+ */
1968
+ VALUE nm_vector_set(int argc, VALUE* argv, VALUE self) { //, VALUE i_, VALUE jv, VALUE vv, VALUE pos_) {
1969
+
1970
+ NM_CONSERVATIVE(nm_register_value(&self));
1971
+
1972
+ if (NM_SRC(self) != NM_STORAGE(self)) {
1973
+ NM_CONSERVATIVE(nm_unregister_value(&self));
1974
+ rb_raise(rb_eNotImpError, "must be called on a real matrix and not a slice");
1975
+ }
1976
+
1977
+ // i, jv, vv are mandatory; pos is optional; thus "31"
1978
+ VALUE i_, jv, vv, pos_;
1979
+ rb_scan_args(argc, argv, "31", &i_, &jv, &vv, &pos_);
1980
+ NM_CONSERVATIVE(nm_register_value(&i_));
1981
+ NM_CONSERVATIVE(nm_register_value(&jv));
1982
+ NM_CONSERVATIVE(nm_register_value(&vv));
1983
+ NM_CONSERVATIVE(nm_register_value(&pos_));
1984
+
1985
+ size_t len = RARRAY_LEN(jv); // need length in order to read the arrays in
1986
+ size_t vvlen = RARRAY_LEN(vv);
1987
+
1988
+ if (len != vvlen) {
1989
+ NM_CONSERVATIVE(nm_unregister_value(&pos_));
1990
+ NM_CONSERVATIVE(nm_unregister_value(&vv));
1991
+ NM_CONSERVATIVE(nm_unregister_value(&jv));
1992
+ NM_CONSERVATIVE(nm_unregister_value(&i_));
1993
+ NM_CONSERVATIVE(nm_unregister_value(&self));
1994
+ rb_raise(rb_eArgError, "lengths must match between j array (%lu) and value array (%lu)", len, vvlen);
1995
+ }
1996
+
1997
+ YALE_STORAGE* s = NM_STORAGE_YALE(self);
1998
+ nm::dtype_t dtype = NM_DTYPE(self);
1999
+
2000
+ size_t i = FIX2INT(i_); // get the row
2001
+ size_t pos = s->ija[i];
2002
+
2003
+ // Allocate the j array and the values array
2004
+ size_t* j = NM_ALLOCA_N(size_t, len);
2005
+ void* vals = NM_ALLOCA_N(char, DTYPE_SIZES[dtype] * len);
2006
+ if (dtype == nm::RUBYOBJ){
2007
+ nm_register_values(reinterpret_cast<VALUE*>(vals), len);
2008
+ }
2009
+
2010
+ // Copy array contents
2011
+ for (size_t idx = 0; idx < len; ++idx) {
2012
+ j[idx] = FIX2INT(rb_ary_entry(jv, idx));
2013
+ rubyval_to_cval(rb_ary_entry(vv, idx), dtype, (char*)vals + idx * DTYPE_SIZES[dtype]);
2014
+ }
2015
+
2016
+ nm_yale_storage_vector_insert(s, pos, j, vals, len, false, dtype);
2017
+ nm_yale_storage_increment_ia_after(s, s->shape[0], i, len);
2018
+ s->ndnz += len;
2019
+
2020
+ if (dtype == nm::RUBYOBJ){
2021
+ nm_unregister_values(reinterpret_cast<VALUE*>(vals), len);
2022
+ }
2023
+
2024
+ NM_CONSERVATIVE(nm_unregister_value(&pos_));
2025
+ NM_CONSERVATIVE(nm_unregister_value(&vv));
2026
+ NM_CONSERVATIVE(nm_unregister_value(&jv));
2027
+ NM_CONSERVATIVE(nm_unregister_value(&i_));
2028
+ NM_CONSERVATIVE(nm_unregister_value(&self));
2029
+
2030
+ // Return the updated position
2031
+ pos += len;
2032
+ return INT2FIX(pos);
2033
+ }
2034
+
2035
+
2036
+
2037
+
2038
+ /*
2039
+ * call-seq:
2040
+ * __yale_default_value__ -> ...
2041
+ *
2042
+ * Get the default_value property from a yale matrix.
2043
+ */
2044
+ VALUE nm_yale_default_value(VALUE self) {
2045
+ VALUE to_return = default_value(NM_STORAGE_YALE(self));
2046
+ return to_return;
2047
+ }
2048
+
2049
+
2050
+ /*
2051
+ * call-seq:
2052
+ * __yale_map_merged_stored__(right) -> Enumerator
2053
+ *
2054
+ * A map operation on two Yale matrices which only iterates across the stored indices.
2055
+ */
2056
+ VALUE nm_yale_map_merged_stored(VALUE left, VALUE right, VALUE init) {
2057
+ NAMED_LR_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::map_merged_stored, VALUE, VALUE, VALUE, VALUE)
2058
+ return ttable[NM_DTYPE(left)][NM_DTYPE(right)](left, right, init);
2059
+ //return nm::yale_storage::map_merged_stored(left, right, init);
2060
+ }
2061
+
2062
+
2063
+ /*
2064
+ * call-seq:
2065
+ * __yale_map_stored__ -> Enumerator
2066
+ *
2067
+ * A map operation on two Yale matrices which only iterates across the stored indices.
2068
+ */
2069
+ VALUE nm_yale_map_stored(VALUE self) {
2070
+ NAMED_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::map_stored, VALUE, VALUE)
2071
+ return ttable[NM_DTYPE(self)](self);
2072
+ }
2073
+
2074
+ } // end of extern "C" block