nmatrix 0.0.8 → 0.0.9

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