nmatrix 0.0.8 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
@@ -1,2284 +0,0 @@
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
-
49
- #define RB_P(OBJ) \
50
- rb_funcall(rb_stderr, rb_intern("print"), 1, rb_funcall(OBJ, rb_intern("object_id"), 0)); \
51
- rb_funcall(rb_stderr, rb_intern("puts"), 1, rb_funcall(OBJ, rb_intern("inspect"), 0));
52
-
53
- /*
54
- * Project Includes
55
- */
56
-
57
- // #include "types.h"
58
- #include "data/data.h"
59
- #include "math/math.h"
60
-
61
- #include "common.h"
62
- #include "yale.h"
63
-
64
- #include "nmatrix.h"
65
- #include "ruby_constants.h"
66
-
67
- /*
68
- * Macros
69
- */
70
-
71
- #ifndef NM_MAX
72
- #define NM_MAX(a,b) (((a)>(b))?(a):(b))
73
- #define NM_MIN(a,b) (((a)<(b))?(a):(b))
74
- #endif
75
-
76
- #ifndef NM_MAX_ITYPE
77
- #define NM_MAX_ITYPE(a,b) ((static_cast<int8_t>(a) > static_cast<int8_t>(b)) ? static_cast<nm::itype_t>(a) : static_cast<nm::itype_t>(b))
78
- #define NM_MIN_ITYPE(a,b) ((static_cast<int8_t>(a) < static_cast<int8_t>(b)) ? static_cast<nm::itype_t>(a) : static_cast<nm::itype_t>(b))
79
- #endif
80
-
81
- /*
82
- * Forward Declarations
83
- */
84
-
85
- extern "C" {
86
- static YALE_STORAGE* nm_copy_alloc_struct(const YALE_STORAGE* rhs, const nm::dtype_t new_dtype, const size_t new_capacity, const size_t new_size);
87
- static YALE_STORAGE* alloc(nm::dtype_t dtype, size_t* shape, size_t dim, nm::itype_t min_itype);
88
-
89
- static size_t yale_count_slice_copy_ndnz(const YALE_STORAGE* s, size_t*, size_t*);
90
-
91
- static void* default_value_ptr(const YALE_STORAGE* s);
92
- static VALUE default_value(const YALE_STORAGE* s);
93
- static VALUE obj_at(YALE_STORAGE* s, size_t k);
94
-
95
- /* Ruby-accessible functions */
96
- static VALUE nm_size(VALUE self);
97
- static VALUE nm_a(int argc, VALUE* argv, VALUE self);
98
- static VALUE nm_d(int argc, VALUE* argv, VALUE self);
99
- static VALUE nm_lu(VALUE self);
100
- static VALUE nm_ia(VALUE self);
101
- static VALUE nm_ja(VALUE self);
102
- static VALUE nm_ija(int argc, VALUE* argv, VALUE self);
103
-
104
- static VALUE nm_nd_row(int argc, VALUE* argv, VALUE self);
105
-
106
- static inline size_t src_ndnz(const YALE_STORAGE* s) {
107
- return reinterpret_cast<YALE_STORAGE*>(s->src)->ndnz;
108
- }
109
-
110
- } // end extern "C" block
111
-
112
- namespace nm { namespace yale_storage {
113
-
114
- template <typename DType, typename IType>
115
- static bool ndrow_is_empty(const YALE_STORAGE* s, IType ija, const IType ija_next);
116
-
117
- template <typename LDType, typename RDType, typename IType>
118
- 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);
119
-
120
- template <typename LDType, typename RDType, typename IType>
121
- static bool eqeq(const YALE_STORAGE* left, const YALE_STORAGE* right);
122
-
123
- template <typename LDType, typename RDType, typename IType>
124
- static bool eqeq_different_defaults(const YALE_STORAGE* s, const LDType& s_init, const YALE_STORAGE* t, const RDType& t_init);
125
-
126
- template <typename IType>
127
- static YALE_STORAGE* copy_alloc_struct(const YALE_STORAGE* rhs, const dtype_t new_dtype, const size_t new_capacity, const size_t new_size);
128
-
129
- template <typename IType>
130
- static void increment_ia_after(YALE_STORAGE* s, IType ija_size, IType i, IType n);
131
-
132
- template <typename IType>
133
- static void c_increment_ia_after(YALE_STORAGE* s, size_t ija_size, size_t i, size_t n) {
134
- increment_ia_after<IType>(s, ija_size, i, n);
135
- }
136
-
137
- template <typename IType>
138
- static IType insert_search(YALE_STORAGE* s, IType left, IType right, IType key, bool* found);
139
-
140
- template <typename DType, typename IType>
141
- static char vector_insert(YALE_STORAGE* s, size_t pos, size_t* j, void* val_, size_t n, bool struct_only);
142
-
143
- template <typename DType, typename IType>
144
- static char vector_insert_resize(YALE_STORAGE* s, size_t current_size, size_t pos, size_t* j, size_t n, bool struct_only);
145
-
146
-
147
- /*
148
- * Functions
149
- */
150
-
151
- /*
152
- * Copy a vector from one IType or DType to another.
153
- */
154
- template <typename LType, typename RType>
155
- static inline void copy_recast_vector(const void* in_, void* out_, size_t length) {
156
- const RType* in = reinterpret_cast<const RType*>(in_);
157
- LType* out = reinterpret_cast<LType*>(out_);
158
- for (size_t i = 0; i < length; ++i) {
159
- out[i] = in[i];
160
- }
161
- out;
162
- }
163
-
164
-
165
- static inline void copy_recast_itype_vector(const void* in, nm::itype_t in_itype, void* out, nm::itype_t out_itype, size_t length) {
166
- NAMED_LR_ITYPE_TEMPLATE_TABLE(ttable, copy_recast_vector, void, const void* in_, void* out_, size_t length);
167
-
168
- ttable[out_itype][in_itype](in, out, length);
169
- }
170
-
171
-
172
- /*
173
- * Create Yale storage from IA, JA, and A vectors given in Old Yale format (probably from a file, since NMatrix only uses
174
- * new Yale for its storage).
175
- *
176
- * This function is needed for Matlab .MAT v5 IO.
177
- */
178
- template <typename LDType, typename RDType, typename IType>
179
- YALE_STORAGE* create_from_old_yale(dtype_t dtype, size_t* shape, void* r_ia, void* r_ja, void* r_a) {
180
- IType* ir = reinterpret_cast<IType*>(r_ia);
181
- IType* jr = reinterpret_cast<IType*>(r_ja);
182
- RDType* ar = reinterpret_cast<RDType*>(r_a);
183
-
184
- // Read through ia and ja and figure out the ndnz (non-diagonal non-zeros) count.
185
- size_t ndnz = 0, i, p, p_next;
186
-
187
- for (i = 0; i < shape[0]; ++i) { // Walk down rows
188
- for (p = ir[i], p_next = ir[i+1]; p < p_next; ++p) { // Now walk through columns
189
-
190
- if (i != jr[p]) ++ndnz; // entry is non-diagonal and probably nonzero
191
-
192
- }
193
- }
194
-
195
- // Having walked through the matrix, we now go about allocating the space for it.
196
- YALE_STORAGE* s = alloc(dtype, shape, 2, UINT8);
197
-
198
- s->capacity = shape[0] + ndnz + 1;
199
- s->ndnz = ndnz;
200
-
201
- // Setup IJA and A arrays
202
- s->ija = ALLOC_N( IType, s->capacity );
203
- s->a = ALLOC_N( LDType, s->capacity );
204
- IType* ijl = reinterpret_cast<IType*>(s->ija);
205
- LDType* al = reinterpret_cast<LDType*>(s->a);
206
-
207
- // set the diagonal to zero -- this prevents uninitialized values from popping up.
208
- for (size_t index = 0; index < shape[0]; ++index) {
209
- al[index] = 0;
210
- }
211
-
212
- // Figure out where to start writing JA in IJA:
213
- size_t pp = s->shape[0]+1;
214
-
215
- // Find beginning of first row
216
- p = ir[0];
217
-
218
- // Now fill the arrays
219
- for (i = 0; i < s->shape[0]; ++i) {
220
-
221
- // Set the beginning of the row (of output)
222
- ijl[i] = pp;
223
-
224
- // Now walk through columns, starting at end of row (of input)
225
- for (size_t p_next = ir[i+1]; p < p_next; ++p, ++pp) {
226
-
227
- if (i == jr[p]) { // diagonal
228
-
229
- al[i] = ar[p];
230
- --pp;
231
-
232
- } else { // nondiagonal
233
-
234
- ijl[pp] = jr[p];
235
- al[pp] = ar[p];
236
-
237
- }
238
- }
239
- }
240
-
241
- ijl[i] = pp; // Set the end of the last row
242
-
243
- // Set the zero position for our output matrix
244
- al[i] = 0;
245
-
246
- return s;
247
- }
248
-
249
-
250
- /*
251
- * Empty the matrix by initializing the IJA vector and setting the diagonal to 0.
252
- *
253
- * Called when most YALE_STORAGE objects are created.
254
- */
255
- template <typename DType, typename IType>
256
- void init(YALE_STORAGE* s, void* init_val) {
257
- IType IA_INIT = s->shape[0] + 1;
258
-
259
- IType* ija = reinterpret_cast<IType*>(s->ija);
260
- // clear out IJA vector
261
- for (IType i = 0; i < IA_INIT; ++i) {
262
- ija[i] = IA_INIT; // set initial values for IJA
263
- }
264
-
265
- clear_diagonal_and_zero<DType>(s, init_val);
266
- }
267
-
268
- size_t max_size(YALE_STORAGE* s) {
269
- size_t result = s->shape[0]*s->shape[1] + 1;
270
- if (s->shape[0] > s->shape[1])
271
- result += s->shape[0] - s->shape[1];
272
-
273
- return result;
274
- }
275
-
276
-
277
- ///////////////
278
- // Accessors //
279
- ///////////////
280
-
281
-
282
- /*
283
- * Determine the number of non-diagonal non-zeros in a not-yet-created copy of a slice or matrix.
284
- */
285
- template <typename DType, typename IType>
286
- static size_t count_slice_copy_ndnz(const YALE_STORAGE* s, size_t* offset, size_t* shape) {
287
- IType* ija = reinterpret_cast<IType*>(s->ija);
288
- DType* a = reinterpret_cast<DType*>(s->a);
289
-
290
- DType ZERO(*reinterpret_cast<DType*>(default_value_ptr(s)));
291
-
292
- // Calc ndnz for the destination
293
- size_t ndnz = 0;
294
- size_t i, j; // indexes of destination matrix
295
- size_t k, l; // indexes of source matrix
296
- for (i = 0; i < shape[0]; i++) {
297
- k = i + offset[0];
298
- for (j = 0; j < shape[1]; j++) {
299
- l = j + offset[1];
300
-
301
- if (j == i) continue;
302
-
303
- if (k == l) { // for diagonal element of source
304
- if (a[k] != ZERO) ++ndnz;
305
- } else { // for non-diagonal element
306
- for (size_t c = ija[k]; c < ija[k+1]; c++) {
307
- if (ija[c] == l) {
308
- ++ndnz;
309
- break;
310
- }
311
- }
312
- }
313
- }
314
- }
315
-
316
- return ndnz;
317
- }
318
-
319
-
320
-
321
- /*
322
- * Copy some portion of a matrix into a new matrix.
323
- */
324
- template <typename LDType, typename RDType, typename IType>
325
- static void slice_copy(YALE_STORAGE* ns, const YALE_STORAGE* s, size_t* offset, size_t* lengths, dtype_t new_dtype) {
326
-
327
- IType* src_ija = reinterpret_cast<IType*>(s->ija);
328
- RDType* src_a = reinterpret_cast<RDType*>(s->a);
329
-
330
- RDType RZERO(*reinterpret_cast<RDType*>(default_value_ptr(s)));
331
-
332
- // Initialize the A and IJA arrays
333
- LDType val(RZERO); // need default value for init. Can't use ns default value because it's not initialized yet
334
- init<LDType,IType>(ns, &val);
335
- IType* dst_ija = reinterpret_cast<IType*>(ns->ija);
336
- LDType* dst_a = reinterpret_cast<LDType*>(ns->a);
337
-
338
- size_t ija = lengths[0] + 1;
339
-
340
- size_t i, j; // indexes of destination matrix
341
- size_t k, l; // indexes of source matrix
342
-
343
- for (i = 0; i < lengths[0]; ++i) {
344
- k = i + offset[0];
345
- for (j = 0; j < lengths[1]; ++j) {
346
- bool found = false;
347
- l = j + offset[1];
348
-
349
- // Get value from source matrix
350
- if (k == l) { // source diagonal
351
- if (src_a[k] != RZERO) { // don't bother copying non-zero values from the diagonal
352
- val = src_a[k];
353
- found = true;
354
- }
355
- } else {
356
- // copy one non-diagonal element
357
- for (size_t c = src_ija[k]; !found && c < src_ija[k+1]; ++c) {
358
- if (src_ija[c] == l) {
359
- val = src_a[c];
360
- found = true;
361
- }
362
- }
363
- }
364
-
365
- if (found) {
366
- // Set value in destination matrix
367
- if (i == j) {
368
- dst_a[i] = val;
369
- } else {
370
- // copy non-diagonal element
371
- dst_ija[ija] = j;
372
- dst_a[ija] = val;
373
- ++ija;
374
- for (size_t c = i + 1; c <= lengths[0]; ++c) {
375
- dst_ija[c] = ija;
376
- }
377
- }
378
- }
379
- }
380
- }
381
-
382
- dst_ija[lengths[0]] = ija; // indicate the end of the last row
383
- ns->ndnz = ija - lengths[0] - 1; // update ndnz count
384
- }
385
-
386
-
387
- /*
388
- * Get a single element of a yale storage object
389
- */
390
- template <typename DType, typename IType>
391
- static void* get_single(YALE_STORAGE* storage, SLICE* slice) {
392
-
393
- DType* a = reinterpret_cast<DType*>(storage->a);
394
- IType* ija = reinterpret_cast<IType*>(storage->ija);
395
-
396
- size_t coord0 = storage->offset[0] + slice->coords[0];
397
- size_t coord1 = storage->offset[1] + slice->coords[1];
398
-
399
- if (coord0 == coord1)
400
- return &(a[ coord0 ]); // return diagonal entry
401
-
402
- if (ija[coord0] == ija[coord0+1])
403
- return &(a[ storage->src->shape[0] ]); // return zero pointer
404
-
405
- // binary search for the column's location
406
- int pos = binary_search<IType>(storage, ija[coord0], ija[coord0+1]-1, coord1);
407
-
408
- if (pos != -1 && ija[pos] == coord1)
409
- return &(a[pos]); // found exact value
410
-
411
- return &(a[ storage->src->shape[0] ]); // return a pointer that happens to be zero
412
- }
413
-
414
-
415
- /*
416
- * Returns a pointer to the correct location in the A vector of a YALE_STORAGE object, given some set of coordinates
417
- * (the coordinates are stored in slice).
418
- */
419
- template <typename DType,typename IType>
420
- void* ref(YALE_STORAGE* s, SLICE* slice) {
421
-
422
- YALE_STORAGE* ns = ALLOC( YALE_STORAGE );
423
-
424
- ns->dim = s->dim;
425
- ns->offset = ALLOC_N(size_t, ns->dim);
426
- ns->shape = ALLOC_N(size_t, ns->dim);
427
-
428
- for (size_t i = 0; i < ns->dim; ++i) {
429
- ns->offset[i] = slice->coords[i] + s->offset[i];
430
- ns->shape[i] = slice->lengths[i];
431
- }
432
-
433
- ns->dtype = s->dtype;
434
- ns->itype = s->itype; // or should we go by shape?
435
-
436
- ns->a = s->a;
437
- ns->ija = s->ija;
438
-
439
- ns->src = s->src;
440
- s->src->count++;
441
-
442
- ns->ndnz = 0;
443
- ns->capacity= 0;
444
-
445
- return ns;
446
-
447
- }
448
-
449
- /*
450
- * Attempt to set some cell in a YALE_STORAGE object. Must supply coordinates and a pointer to a value (which will be
451
- * copied into the storage object).
452
- */
453
- template <typename DType, typename IType>
454
- char set(YALE_STORAGE* storage, SLICE* slice, void* value) {
455
- DType* v = reinterpret_cast<DType*>(value);
456
- size_t coord0 = storage->offset[0] + slice->coords[0],
457
- coord1 = storage->offset[1] + slice->coords[1];
458
-
459
- bool found = false;
460
- char ins_type;
461
-
462
- if (coord0 == coord1) {
463
- reinterpret_cast<DType*>(storage->a)[coord0] = *v; // set diagonal
464
- return 'r';
465
- }
466
-
467
- // Get IJA positions of the beginning and end of the row
468
- if (reinterpret_cast<IType*>(storage->ija)[coord0] == reinterpret_cast<IType*>(storage->ija)[coord0+1]) {
469
- // empty row
470
- ins_type = vector_insert<DType,IType>(storage, reinterpret_cast<IType*>(storage->ija)[coord0], &(coord1), v, 1, false);
471
- increment_ia_after<IType>(storage, storage->shape[0], coord0, 1);
472
- reinterpret_cast<YALE_STORAGE*>(storage->src)->ndnz++;
473
-
474
- return ins_type;
475
- }
476
-
477
- // non-empty row. search for coords[1] in the IJA array, between ija and ija_next
478
- // (including ija, not including ija_next)
479
- //ija_size = get_size<IType>(storage);
480
-
481
- // Do a binary search for the column
482
- size_t pos = insert_search<IType>(storage,
483
- reinterpret_cast<IType*>(storage->ija)[coord0],
484
- reinterpret_cast<IType*>(storage->ija)[coord0+1]-1,
485
- coord1, &found);
486
-
487
- if (found) { // replace
488
- reinterpret_cast<IType*>(storage->ija)[pos] = coord1;
489
- reinterpret_cast<DType*>(storage->a)[pos] = *v;
490
- return 'r';
491
- }
492
-
493
- ins_type = vector_insert<DType,IType>(storage, pos, &(coord1), v, 1, false);
494
- increment_ia_after<IType>(storage, storage->shape[0], coord0, 1);
495
- reinterpret_cast<YALE_STORAGE*>(storage->src)->ndnz++;
496
-
497
- return ins_type;
498
- }
499
-
500
- ///////////
501
- // Tests //
502
- ///////////
503
-
504
- /*
505
- * Yale eql? -- for whole-matrix comparison returning a single value.
506
- */
507
- template <typename LDType, typename RDType, typename IType>
508
- static bool eqeq(const YALE_STORAGE* left, const YALE_STORAGE* right) {
509
- LDType l_init = reinterpret_cast<LDType*>(left->a )[left->shape[0] ];
510
- RDType r_init = reinterpret_cast<RDType*>(right->a)[right->shape[0]];
511
-
512
- // If the defaults are different between the two matrices, or if slicing is involved, use this other function instead:
513
- if (l_init != r_init || left->src != left || right->src != right)
514
- return eqeq_different_defaults<LDType,RDType,IType>(left, l_init, right, r_init);
515
-
516
- LDType* la = reinterpret_cast<LDType*>(left->a);
517
- RDType* ra = reinterpret_cast<RDType*>(right->a);
518
-
519
- // Compare the diagonals first.
520
- for (size_t index = 0; index < left->shape[0]; ++index) {
521
- if (la[index] != ra[index]) return false;
522
- }
523
-
524
- IType* lij = reinterpret_cast<IType*>(left->ija);
525
- IType* rij = reinterpret_cast<IType*>(right->ija);
526
-
527
- for (IType i = 0; i < left->shape[0]; ++i) {
528
-
529
- // Get start and end positions of row
530
- IType l_ija = lij[i],
531
- l_ija_next = lij[i+1],
532
- r_ija = rij[i],
533
- r_ija_next = rij[i+1];
534
-
535
- // Check to see if one row is empty and the other isn't.
536
- if (ndrow_is_empty<LDType,IType>(left, l_ija, l_ija_next)) {
537
- if (!ndrow_is_empty<RDType,IType>(right, r_ija, r_ija_next)) {
538
- return false;
539
- }
540
-
541
- } else if (ndrow_is_empty<RDType,IType>(right, r_ija, r_ija_next)) {
542
- // one is empty but the other isn't
543
- return false;
544
-
545
- } else if (!ndrow_eqeq_ndrow<LDType,RDType,IType>(left, right, l_ija, l_ija_next, r_ija, r_ija_next)) {
546
- // Neither row is empty. Must compare the rows directly.
547
- return false;
548
- }
549
-
550
- }
551
-
552
- return true;
553
- }
554
-
555
-
556
-
557
- /*
558
- * Are two non-diagonal rows the same? We already know.
559
- */
560
- template <typename LDType, typename RDType, typename IType>
561
- 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) {
562
- bool l_no_more = false, r_no_more = false;
563
-
564
- IType *lij = reinterpret_cast<IType*>(l->ija),
565
- *rij = reinterpret_cast<IType*>(r->ija);
566
-
567
- LDType* la = reinterpret_cast<LDType*>(l->a);
568
- RDType* ra = reinterpret_cast<RDType*>(r->a);
569
-
570
- IType l_ja = lij[l_ija],
571
- r_ja = rij[r_ija];
572
-
573
- IType ja = std::min(l_ja, r_ja);
574
-
575
- LDType LZERO = la[l->shape[0]];
576
- RDType RZERO = ra[r->shape[0]];
577
-
578
- while (!(l_no_more && r_no_more)) {
579
- if (l_ja == r_ja) {
580
-
581
- if (ra[r_ija] != la[l_ija]) return false; // Direct comparison
582
-
583
- ++l_ija;
584
- ++r_ija;
585
-
586
- if (l_ija < l_ija_next) {
587
- l_ja = lij[l_ija];
588
-
589
- } else {
590
- l_no_more = true;
591
- }
592
-
593
- if (r_ija < r_ija_next) {
594
- r_ja = rij[r_ija];
595
-
596
- } else {
597
- r_no_more = true;
598
- }
599
-
600
- ja = std::min(l_ja, r_ja);
601
-
602
- } else if (l_no_more || ja < l_ja) {
603
-
604
- if (ra[r_ija] != RZERO) return false;
605
-
606
- ++r_ija;
607
- if (r_ija < r_ija_next) {
608
- // get next column
609
- r_ja = rij[r_ija];
610
- ja = std::min(l_ja, r_ja);
611
-
612
- } else {
613
- l_no_more = true;
614
- }
615
-
616
- } else if (r_no_more || ja < r_ja) {
617
-
618
- if (la[l_ija] != LZERO) return false;
619
-
620
- ++l_ija;
621
- if (l_ija < l_ija_next) {
622
- // get next column
623
- l_ja = lij[l_ija];
624
- ja = std::min(l_ja, r_ja);
625
- } else {
626
- l_no_more = true;
627
- }
628
-
629
- } else {
630
- std::fprintf(stderr, "Unhandled in eqeq: l_ja=%d, r_ja=%d\n", (int)l_ja, (int)r_ja);
631
- }
632
- }
633
-
634
- // every item matched
635
- return true;
636
- }
637
-
638
- /*
639
- * Is the non-diagonal portion of the row empty?
640
- */
641
- template <typename DType, typename IType>
642
- static bool ndrow_is_empty(const YALE_STORAGE* s, IType ija, const IType ija_next) {
643
- if (ija == ija_next) return true;
644
-
645
- DType* a = reinterpret_cast<DType*>(s->a);
646
-
647
- // do all the entries = zero?
648
- for (; ija < ija_next; ++ija) {
649
- if (a[ija] != 0) return false;
650
- }
651
-
652
- return true;
653
- }
654
-
655
- //////////
656
- // Math //
657
- //////////
658
-
659
- #define YALE_IA(s) (reinterpret_cast<IType*>(s->ija))
660
- #define YALE_IJ(s) (reinterpret_cast<IType*>(s->ija) + s->shape[0] + 1)
661
- #define YALE_COUNT(yale) (yale->ndnz + yale->shape[0])
662
-
663
- /////////////
664
- // Utility //
665
- /////////////
666
-
667
-
668
- /*
669
- * Binary search for finding the beginning of a slice. Returns the position of the first element which is larger than
670
- * bound.
671
- */
672
- template <typename IType>
673
- IType binary_search_left_boundary(const YALE_STORAGE* s, IType left, IType right, IType bound) {
674
- if (left > right) return -1;
675
-
676
- IType* ija = reinterpret_cast<IType*>(s->ija);
677
-
678
- if (ija[left] >= bound) return left; // shortcut
679
-
680
- IType mid = (left + right) / 2;
681
- IType mid_j = ija[mid];
682
-
683
- if (mid_j == bound)
684
- return mid;
685
- else if (mid_j > bound) { // eligible! don't exclude it.
686
- return binary_search_left_boundary<IType>(s, left, mid, bound);
687
- } else // (mid_j < bound)
688
- return binary_search_left_boundary<IType>(s, mid + 1, right, bound);
689
- }
690
-
691
-
692
- /*
693
- * Binary search for returning stored values. Returns a non-negative position, or -1 for not found.
694
- */
695
- template <typename IType>
696
- int binary_search(YALE_STORAGE* s, IType left, IType right, IType key) {
697
-
698
- if (left > right) return -1;
699
-
700
- IType* ija = reinterpret_cast<IType*>(s->ija);
701
-
702
- IType mid = (left + right)/2;
703
- IType mid_j = ija[mid];
704
-
705
- if (mid_j == key)
706
- return mid;
707
-
708
- else if (mid_j > key)
709
- return binary_search<IType>(s, left, mid - 1, key);
710
-
711
- else
712
- return binary_search<IType>(s, mid + 1, right, key);
713
- }
714
-
715
-
716
- /*
717
- * Resize yale storage vectors A and IJA, copying values.
718
- */
719
- static void vector_grow(YALE_STORAGE* s) {
720
- if (s->src != s) throw; // need to correct this quickly.
721
-
722
- size_t new_capacity = s->capacity * GROWTH_CONSTANT;
723
- size_t max_capacity = max_size(s);
724
-
725
- if (new_capacity > max_capacity) new_capacity = max_capacity;
726
-
727
- void* new_ija = ALLOC_N(char, ITYPE_SIZES[s->itype] * new_capacity);
728
- NM_CHECK_ALLOC(new_ija);
729
-
730
- void* new_a = ALLOC_N(char, DTYPE_SIZES[s->dtype] * new_capacity);
731
- NM_CHECK_ALLOC(new_a);
732
-
733
- void* old_ija = s->ija;
734
- void* old_a = s->a;
735
-
736
- memcpy(new_ija, old_ija, s->capacity * ITYPE_SIZES[s->itype]);
737
- memcpy(new_a, old_a, s->capacity * DTYPE_SIZES[s->dtype]);
738
-
739
- s->capacity = new_capacity;
740
-
741
- xfree(old_ija);
742
- xfree(old_a);
743
-
744
- s->ija = new_ija;
745
- s->a = new_a;
746
- }
747
-
748
-
749
- /*
750
- * Resize yale storage vectors A and IJA in preparation for an insertion.
751
- */
752
- template <typename DType, typename IType>
753
- static char vector_insert_resize(YALE_STORAGE* s, size_t current_size, size_t pos, size_t* j, size_t n, bool struct_only) {
754
- if (s != s->src) throw;
755
-
756
- // Determine the new capacity for the IJA and A vectors.
757
- size_t new_capacity = s->capacity * GROWTH_CONSTANT;
758
- size_t max_capacity = max_size(s);
759
-
760
- if (new_capacity > max_capacity) {
761
- new_capacity = max_capacity;
762
-
763
- if (current_size + n > max_capacity) rb_raise(rb_eNoMemError, "insertion size exceeded maximum yale matrix size");
764
- }
765
-
766
- if (new_capacity < current_size + n)
767
- new_capacity = current_size + n;
768
-
769
- // Allocate the new vectors.
770
- IType* new_ija = ALLOC_N( IType, new_capacity );
771
- NM_CHECK_ALLOC(new_ija);
772
-
773
- DType* new_a = ALLOC_N( DType, new_capacity );
774
- NM_CHECK_ALLOC(new_a);
775
-
776
- IType* old_ija = reinterpret_cast<IType*>(s->ija);
777
- DType* old_a = reinterpret_cast<DType*>(s->a);
778
-
779
- // Copy all values prior to the insertion site to the new IJA and new A
780
- if (struct_only) {
781
- for (size_t i = 0; i < pos; ++i) {
782
- new_ija[i] = old_ija[i];
783
- }
784
- } else {
785
- for (size_t i = 0; i < pos; ++i) {
786
- new_ija[i] = old_ija[i];
787
- new_a[i] = old_a[i];
788
- }
789
- }
790
-
791
-
792
- // Copy all values subsequent to the insertion site to the new IJA and new A, leaving room (size n) for insertion.
793
- if (struct_only) {
794
- for (size_t i = pos; i < current_size; ++i) {
795
- new_ija[i+n] = old_ija[i];
796
- }
797
- } else {
798
- for (size_t i = pos; i < current_size; ++i) {
799
- new_ija[i+n] = old_ija[i];
800
- new_a[i+n] = old_a[i];
801
- }
802
- }
803
-
804
- s->capacity = new_capacity;
805
-
806
- xfree(s->ija);
807
- xfree(s->a);
808
-
809
- s->ija = reinterpret_cast<void*>(new_ija);
810
- s->a = reinterpret_cast<void*>(new_a);
811
-
812
- return 'i';
813
- }
814
-
815
- /*
816
- * Insert a value or contiguous values in the ija and a vectors (after ja and
817
- * diag). Does not free anything; you are responsible!
818
- *
819
- * TODO: Improve this so it can handle non-contiguous element insertions
820
- * efficiently. For now, we can just sort the elements in the row in
821
- * question.)
822
- */
823
- template <typename DType, typename IType>
824
- static char vector_insert(YALE_STORAGE* s, size_t pos, size_t* j, void* val_, size_t n, bool struct_only) {
825
- if (pos < s->shape[0]) {
826
- rb_raise(rb_eArgError, "vector insert pos (%d) is before beginning of ja (%d); this should not happen", pos, s->shape[0]);
827
- }
828
-
829
- DType* val = reinterpret_cast<DType*>(val_);
830
-
831
- size_t size = get_size<IType>(s);
832
-
833
- IType* ija = reinterpret_cast<IType*>(s->ija);
834
- DType* a = reinterpret_cast<DType*>(s->a);
835
-
836
- if (size + n > s->capacity) {
837
- vector_insert_resize<DType,IType>(s, size, pos, j, n, struct_only);
838
-
839
- // Need to get the new locations for ija and a.
840
- ija = reinterpret_cast<IType*>(s->ija);
841
- a = reinterpret_cast<DType*>(s->a);
842
-
843
- } else {
844
- /*
845
- * No resize required:
846
- * easy (but somewhat slow), just copy elements to the tail, starting at
847
- * the end, one element at a time.
848
- *
849
- * TODO: This can be made slightly more efficient, but only after the tests
850
- * are written.
851
- */
852
-
853
- if (struct_only) {
854
- for (size_t i = 0; i < size - pos; ++i) {
855
- ija[size+n-1-i] = ija[size-1-i];
856
- }
857
- } else {
858
- for (size_t i = 0; i < size - pos; ++i) {
859
- ija[size+n-1-i] = ija[size-1-i];
860
- a[size+n-1-i] = a[size-1-i];
861
- }
862
- }
863
- }
864
-
865
- // Now insert the new values.
866
- if (struct_only) {
867
- for (size_t i = 0; i < n; ++i) {
868
- ija[pos+i] = j[i];
869
- }
870
- } else {
871
- for (size_t i = 0; i < n; ++i) {
872
- ija[pos+i] = j[i];
873
- a[pos+i] = val[i];
874
- }
875
- }
876
-
877
- return 'i';
878
- }
879
-
880
- /*
881
- * If we add n items to row i, we need to increment ija[i+1] and onward.
882
- */
883
- template <typename IType>
884
- static void increment_ia_after(YALE_STORAGE* s, IType ija_size, IType i, IType n) {
885
- IType* ija = reinterpret_cast<IType*>(s->ija);
886
-
887
- ++i;
888
- for (; i <= ija_size; ++i) {
889
- ija[i] += n;
890
- }
891
- }
892
-
893
- /*
894
- * Binary search for returning insertion points.
895
- */
896
- template <typename IType>
897
- static IType insert_search(YALE_STORAGE* s, IType left, IType right, IType key, bool* found) {
898
-
899
- if (left > right) {
900
- *found = false;
901
- return left;
902
- }
903
-
904
- IType* ija = reinterpret_cast<IType*>(s->ija);
905
- IType mid = (left + right)/2;
906
- IType mid_j = ija[mid];
907
-
908
- if (mid_j == key) {
909
- *found = true;
910
- return mid;
911
-
912
- } else if (mid_j > key) {
913
- return insert_search<IType>(s, left, mid-1, key, found);
914
-
915
- } else {
916
- return insert_search<IType>(s, mid+1, right, key, found);
917
- }
918
- }
919
-
920
- /////////////////////////
921
- // Copying and Casting //
922
- /////////////////////////
923
-
924
- /*
925
- * Templated copy constructor for changing dtypes.
926
- */
927
- template <typename LDType, typename RDType, typename IType>
928
- YALE_STORAGE* cast_copy(const YALE_STORAGE* rhs, dtype_t new_dtype) {
929
-
930
- YALE_STORAGE* lhs;
931
-
932
- if (rhs->src != rhs) { // copy the reference
933
- // Copy shape for yale construction
934
- size_t* shape = ALLOC_N(size_t, 2);
935
- shape[0] = rhs->shape[0];
936
- shape[1] = rhs->shape[1];
937
- size_t ndnz = src_ndnz(rhs);
938
- if (shape[0] != rhs->src->shape[0] || shape[1] != rhs->src->shape[1])
939
- ndnz = count_slice_copy_ndnz<RDType,IType>(rhs, rhs->offset, rhs->shape); // expensive, avoid if possible
940
- size_t request_capacity = shape[0] + ndnz + 1;
941
- // FIXME: Should we use a different itype? Or same?
942
- lhs = nm_yale_storage_create(new_dtype, shape, 2, request_capacity, rhs->itype);
943
-
944
- // This check probably isn't necessary.
945
- if (lhs->capacity < request_capacity)
946
- rb_raise(nm_eStorageTypeError, "conversion failed; capacity of %ld requested, max allowable is %ld", request_capacity, lhs->capacity);
947
-
948
- slice_copy<LDType, RDType, IType>(lhs, rhs, rhs->offset, rhs->shape, new_dtype);
949
- } else { // regular copy
950
-
951
- // Allocate a new structure
952
- size_t size = get_size<IType>(rhs);
953
- lhs = copy_alloc_struct<IType>(rhs, new_dtype, rhs->capacity, size);
954
-
955
- LDType* la = reinterpret_cast<LDType*>(lhs->a);
956
- RDType* ra = reinterpret_cast<RDType*>(rhs->a);
957
-
958
- for (size_t index = 0; index < size; ++index) {
959
- la[index] = ra[index];
960
- }
961
- }
962
-
963
- return lhs;
964
- }
965
-
966
- /*
967
- * Template access for getting the size of Yale storage.
968
- */
969
- template <typename IType>
970
- static inline size_t get_size(const YALE_STORAGE* storage) {
971
- return static_cast<size_t>(reinterpret_cast<IType*>(storage->ija)[ storage->shape[0] ]);
972
- }
973
-
974
-
975
- /*
976
- * Allocate for a copy or copy-cast operation, and copy the IJA portion of the
977
- * matrix (the structure).
978
- */
979
- template <typename IType>
980
- static YALE_STORAGE* copy_alloc_struct(const YALE_STORAGE* rhs, const dtype_t new_dtype, const size_t new_capacity, const size_t new_size) {
981
- YALE_STORAGE* lhs = ALLOC( YALE_STORAGE );
982
- lhs->dim = rhs->dim;
983
- lhs->shape = ALLOC_N( size_t, lhs->dim );
984
- lhs->offset = ALLOC_N( size_t, lhs->dim );
985
- memcpy(lhs->shape, rhs->shape, lhs->dim * sizeof(size_t));
986
- //memcpy(lhs->offset, rhs->offset, lhs->dim * sizeof(size_t));
987
- lhs->offset[0] = 0;
988
- lhs->offset[1] = 0;
989
-
990
- lhs->itype = rhs->itype;
991
- lhs->capacity = new_capacity;
992
- lhs->dtype = new_dtype;
993
- lhs->ndnz = rhs->ndnz;
994
-
995
- lhs->ija = ALLOC_N( IType, lhs->capacity );
996
- lhs->a = ALLOC_N( char, DTYPE_SIZES[new_dtype] * lhs->capacity );
997
- lhs->src = lhs;
998
- lhs->count = 1;
999
-
1000
- // Now copy the contents -- but only within the boundaries set by the size. Leave
1001
- // the rest uninitialized.
1002
- if (!rhs->offset[0] && !rhs->offset[1]) {
1003
- for (size_t i = 0; i < get_size<IType>(rhs); ++i)
1004
- reinterpret_cast<IType*>(lhs->ija)[i] = reinterpret_cast<IType*>(rhs->ija)[i]; // copy indices
1005
- } else {
1006
- rb_raise(rb_eNotImpError, "cannot copy struct due to different offsets");
1007
- }
1008
- return lhs;
1009
- }
1010
-
1011
- template <typename DType, typename IType>
1012
- static STORAGE* matrix_multiply(const STORAGE_PAIR& casted_storage, size_t* resulting_shape, bool vector, nm::itype_t result_itype) {
1013
- YALE_STORAGE *left = (YALE_STORAGE*)(casted_storage.left),
1014
- *right = (YALE_STORAGE*)(casted_storage.right);
1015
-
1016
- // We can safely get dtype from the casted matrices; post-condition of binary_storage_cast_alloc is that dtype is the
1017
- // same for left and right.
1018
- // int8_t dtype = left->dtype;
1019
-
1020
- // Massage the IType arrays into the correct form.
1021
-
1022
- IType* ijl;
1023
- if (left->itype == result_itype) ijl = reinterpret_cast<IType*>(left->ija);
1024
- else { // make a temporary copy of the IJA vector for L with the correct itype
1025
- size_t length = nm_yale_storage_get_size(left);
1026
- ijl = ALLOCA_N(IType, length);
1027
- copy_recast_itype_vector(reinterpret_cast<void*>(left->ija), left->itype, reinterpret_cast<void*>(ijl), result_itype, length);
1028
- }
1029
-
1030
- IType* ijr;
1031
- if (right->itype == result_itype) ijr = reinterpret_cast<IType*>(right->ija);
1032
- else { // make a temporary copy of the IJA vector for R with the correct itype
1033
- size_t length = nm_yale_storage_get_size(right);
1034
- ijr = ALLOCA_N(IType, length);
1035
- copy_recast_itype_vector(reinterpret_cast<void*>(right->ija), right->itype, reinterpret_cast<void*>(ijr), result_itype, length);
1036
- }
1037
-
1038
- // First, count the ndnz of the result.
1039
- // TODO: This basically requires running symbmm twice to get the exact ndnz size. That's frustrating. Are there simple
1040
- // cases where we can avoid running it?
1041
- size_t result_ndnz = nm::math::symbmm<IType>(resulting_shape[0], left->shape[1], resulting_shape[1], ijl, ijl, true, ijr, ijr, true, NULL, true);
1042
-
1043
- // Create result storage.
1044
- YALE_STORAGE* result = nm_yale_storage_create(left->dtype, resulting_shape, 2, result_ndnz, result_itype);
1045
- init<DType,IType>(result, NULL);
1046
- IType* ija = reinterpret_cast<IType*>(result->ija);
1047
-
1048
- // Symbolic multiplication step (build the structure)
1049
- nm::math::symbmm<IType>(resulting_shape[0], left->shape[1], resulting_shape[1], ijl, ijl, true, ijr, ijr, true, ija, true);
1050
-
1051
- // Numeric multiplication step (fill in the elements)
1052
-
1053
- nm::math::numbmm<DType,IType>(result->shape[0], left->shape[1], result->shape[1],
1054
- ijl, ijl, reinterpret_cast<DType*>(left->a), true,
1055
- ijr, ijr, reinterpret_cast<DType*>(right->a), true,
1056
- ija, ija, reinterpret_cast<DType*>(result->a), true);
1057
-
1058
-
1059
- // Sort the columns
1060
- nm::math::smmp_sort_columns<DType,IType>(result->shape[0], ija, ija, reinterpret_cast<DType*>(result->a));
1061
-
1062
- return reinterpret_cast<STORAGE*>(result);
1063
- }
1064
-
1065
-
1066
- /*
1067
- * Get the sum of offsets from the original matrix (for sliced iteration).
1068
- */
1069
- static std::array<size_t,2> get_offsets(YALE_STORAGE* x) {
1070
- std::array<size_t, 2> offsets{ {0,0} };
1071
- while (x != x->src) {
1072
- offsets[0] += x->offset[0];
1073
- offsets[1] += x->offset[1];
1074
- x = reinterpret_cast<YALE_STORAGE*>(x->src);
1075
- }
1076
- return offsets;
1077
- }
1078
-
1079
-
1080
- template <typename IType>
1081
- class IJAManager {
1082
- protected:
1083
- bool needs_free;
1084
-
1085
- public:
1086
- IType* ija;
1087
-
1088
- IJAManager(YALE_STORAGE* s, itype_t temp_itype) : needs_free(false), ija(reinterpret_cast<IType*>(s->ija)) {
1089
- if (s->itype != temp_itype) {
1090
- size_t len = nm_yale_storage_get_size(s);
1091
- needs_free = true;
1092
- ija = ALLOC_N(IType, len);
1093
- copy_recast_itype_vector(s->ija, s->itype, reinterpret_cast<void*>(ija), temp_itype, len);
1094
- }
1095
- }
1096
-
1097
- ~IJAManager() {
1098
- if (needs_free) xfree(ija);
1099
- }
1100
- };
1101
-
1102
-
1103
- template <typename IType>
1104
- class RowIterator {
1105
- protected:
1106
- YALE_STORAGE* s;
1107
- IType* ija;
1108
- void* a;
1109
- IType i, k, k_end;
1110
- size_t j_offset, j_shape;
1111
- bool diag, End;
1112
- VALUE init;
1113
- public:
1114
- RowIterator(YALE_STORAGE* s_, IType* ija_, IType i_, size_t j_shape_, size_t j_offset_ = 0)
1115
- : s(s_),
1116
- ija(ija_),
1117
- a(s->a),
1118
- i(i_),
1119
- k(ija[i]),
1120
- k_end(ija[i+1]),
1121
- j_offset(j_offset_),
1122
- j_shape(j_shape_),
1123
- diag(row_has_no_nd() || diag_is_first()),
1124
- End(false),
1125
- init(default_value(s))
1126
- { }
1127
-
1128
- RowIterator(YALE_STORAGE* s_, IType i_, size_t j_shape_, size_t j_offset_ = 0)
1129
- : s(s_),
1130
- ija(reinterpret_cast<IType*>(s->ija)),
1131
- a(s->a),
1132
- i(i_),
1133
- k(ija[i]),
1134
- k_end(ija[i+1]),
1135
- j_offset(j_offset_),
1136
- j_shape(j_shape_),
1137
- diag(row_has_no_nd() || diag_is_first()),
1138
- End(false),
1139
- init(default_value(s))
1140
- { }
1141
-
1142
- RowIterator(const RowIterator& rhs) : s(rhs.s), ija(rhs.ija), a(s->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) { }
1143
-
1144
- VALUE obj() const {
1145
- return diag ? obj_at(s, i) : obj_at(s, k);
1146
- }
1147
-
1148
- template <typename T>
1149
- T cobj() const {
1150
- if (typeid(T) == typeid(RubyObject)) return obj();
1151
- return diag ? reinterpret_cast<T*>(s->a)[i] : reinterpret_cast<T*>(s->a)[k];
1152
- }
1153
-
1154
- inline IType proper_j() const {
1155
- return diag ? i : ija[k];
1156
- }
1157
-
1158
- inline IType offset_j() const {
1159
- return proper_j() - j_offset;
1160
- }
1161
-
1162
- /* Returns true if an additional value is inserted, false if it goes on the diagonal */
1163
- bool insert(IType j, VALUE v) {
1164
- if (j == i) { // insert regardless on diagonal
1165
- reinterpret_cast<VALUE*>(a)[j] = v;
1166
- return false;
1167
-
1168
- } else {
1169
- if (rb_funcall(v, rb_intern("!="), 1, init) == Qtrue) {
1170
- if (k >= s->capacity) {
1171
- vector_grow(s);
1172
- ija = reinterpret_cast<IType*>(s->ija);
1173
- a = s->a;
1174
- }
1175
- reinterpret_cast<VALUE*>(a)[k] = v;
1176
- ija[k] = j;
1177
- k++;
1178
- return true;
1179
- }
1180
- return false;
1181
- }
1182
- }
1183
-
1184
- void update_row_end() {
1185
- ija[i+1] = k;
1186
- k_end = k;
1187
- }
1188
-
1189
- /* Past the j_shape? */
1190
- inline bool end() const {
1191
- if (End) return true;
1192
- //if (diag) return i - j_offset >= j_shape;
1193
- //else return k >= s->capacity || ija[k] - j_offset >= j_shape;
1194
- return (diag ? i : ija[k]) - j_offset >= j_shape;
1195
- }
1196
-
1197
- inline bool row_has_no_nd() const { return ija[i] == k_end; /* k_start == k_end */ }
1198
- inline bool diag_is_first() const { return i < ija[ija[i]]; }
1199
- inline bool diag_is_last() const { return i > ija[k_end-1]; } // only works if !row_has_no_nd()
1200
- inline bool k_is_last_nd() const { return k == k_end-1; }
1201
- inline bool k_is_last() const { return k_is_last_nd() && !diag_is_last(); }
1202
- inline bool diag_is_ahead() const { return i > ija[k]; }
1203
- inline bool row_has_diag() const { return i < s->shape[1]; }
1204
- inline bool diag_is_next() const { // assumes we've already tested for diag, row_has_no_nd(), diag_is_first()
1205
- if (i == ija[k]+1) return true; // definite next
1206
- else if (k+1 < k_end && i >= ija[k+1]+1) return false; // at least one item before it
1207
- else return true;
1208
- }
1209
-
1210
- RowIterator<IType>& operator++() {
1211
- if (diag) { // we're at the diagonal
1212
- if (row_has_no_nd() || diag_is_last()) End = true; // and there are no non-diagonals (or none still to visit)
1213
- diag = false;
1214
- } else if (!row_has_diag()) { // row has no diagonal entries
1215
- if (row_has_no_nd() || k_is_last_nd()) End = true; // row is totally empty, or we're at last entry
1216
- else k++; // still entries to visit
1217
- } else { // not at diag but it exists somewhere in the row, and row has at least one nd entry
1218
- if (diag_is_ahead()) { // diag is ahead
1219
- if (k_is_last_nd()) diag = true; // diag is next and last
1220
- else if (diag_is_next()) { // diag is next and not last
1221
- diag = true;
1222
- k++;
1223
- } else k++; // diag is not next
1224
- } else { // diag is past
1225
- if (k_is_last_nd()) End = true; // and we're at the end
1226
- else k++; // and we're not at the end
1227
- }
1228
- }
1229
-
1230
- return *this;
1231
- }
1232
-
1233
-
1234
- RowIterator<IType> operator++(int unused) {
1235
- RowIterator<IType> x(*this);
1236
- ++(*this);
1237
- return x;
1238
- }
1239
- };
1240
-
1241
-
1242
-
1243
- template <typename IType>
1244
- static VALUE map_stored(VALUE self) {
1245
-
1246
- YALE_STORAGE* s = NM_STORAGE_YALE(self);
1247
-
1248
- size_t* shape = ALLOC_N(size_t, 2);
1249
- shape[0] = s->shape[0];
1250
- shape[1] = s->shape[1];
1251
-
1252
- std::array<size_t,2> s_offsets = get_offsets(s);
1253
-
1254
- RETURN_SIZED_ENUMERATOR(self, 0, 0, nm_yale_enumerator_length);
1255
- VALUE init = rb_yield(default_value(s));
1256
-
1257
- // Try to find a reasonable capacity to request when creating the matrix
1258
- size_t ndnz = src_ndnz(s);
1259
- if (s->src != s) // need to guess capacity
1260
- ndnz = yale_count_slice_copy_ndnz(s, s->offset, s->shape);
1261
- size_t request_capacity = s->shape[0] + ndnz + 1;
1262
-
1263
- YALE_STORAGE* r = nm_yale_storage_create(nm::RUBYOBJ, shape, 2, request_capacity, NM_ITYPE(self));
1264
- if (r->capacity < request_capacity)
1265
- rb_raise(nm_eStorageTypeError, "conversion failed; capacity of %ld requested, max allowable is %ld", request_capacity, r->capacity);
1266
- nm_yale_storage_init(r, &init);
1267
-
1268
- for (IType ri = 0; ri < shape[0]; ++ri) {
1269
- RowIterator<IType> sit(s, ri + s_offsets[0], shape[1], s_offsets[1]);
1270
- RowIterator<IType> rit(r, ri, shape[1]);
1271
-
1272
- while (!sit.end()) {
1273
- VALUE rv = rb_yield(sit.obj());
1274
- VALUE rj = sit.offset_j();
1275
- rit.insert(rj, rv);
1276
- ++sit;
1277
- }
1278
- // Update the row end information.
1279
- rit.update_row_end();
1280
- }
1281
-
1282
- NMATRIX* m = nm_create(nm::YALE_STORE, reinterpret_cast<STORAGE*>(r));
1283
- return Data_Wrap_Struct(CLASS_OF(self), nm_yale_storage_mark, nm_delete, m);
1284
- }
1285
-
1286
-
1287
- /*
1288
- * eqeq function for slicing and different defaults.
1289
- */
1290
- template <typename LDType, typename RDType, typename IType>
1291
- static bool eqeq_different_defaults(const YALE_STORAGE* s, const LDType& s_init, const YALE_STORAGE* t, const RDType& t_init) {
1292
-
1293
- std::array<size_t,2> s_offsets = get_offsets(const_cast<YALE_STORAGE*>(s)),
1294
- t_offsets = get_offsets(const_cast<YALE_STORAGE*>(t));
1295
-
1296
- for (IType ri = 0; ri < s->shape[0]; ++ri) {
1297
- RowIterator<IType> sit(const_cast<YALE_STORAGE*>(s), reinterpret_cast<IType*>(s->ija), ri + s_offsets[0], s->shape[1], s_offsets[1]);
1298
- RowIterator<IType> tit(const_cast<YALE_STORAGE*>(t), reinterpret_cast<IType*>(t->ija), ri + t_offsets[0], s->shape[1], t_offsets[1]);
1299
-
1300
- while (!sit.end() || !tit.end()) {
1301
-
1302
- // Perform the computation. Use a default value if the matrix doesn't have some value stored.
1303
- if (tit.end() || (!sit.end() && sit.offset_j() < tit.offset_j())) {
1304
- if (sit.template cobj<LDType>() != t_init) return false;
1305
- ++sit;
1306
-
1307
- } else if (sit.end() || (!tit.end() && sit.offset_j() > tit.offset_j())) {
1308
- if (s_init != tit.template cobj<RDType>()) return false;
1309
- ++tit;
1310
-
1311
- } else { // same index
1312
- if (sit.template cobj<LDType>() != tit.template cobj<RDType>()) return false;
1313
- ++sit;
1314
- ++tit;
1315
- }
1316
- }
1317
- }
1318
- return true;
1319
- }
1320
-
1321
-
1322
- template <typename IType>
1323
- static VALUE map_merged_stored(VALUE left, VALUE right, VALUE init, nm::itype_t itype) {
1324
-
1325
- YALE_STORAGE *s = NM_STORAGE_YALE(left),
1326
- *t = NM_STORAGE_YALE(right);
1327
-
1328
- size_t* shape = ALLOC_N(size_t, 2);
1329
- shape[0] = s->shape[0];
1330
- shape[1] = s->shape[1];
1331
-
1332
- std::array<size_t,2> s_offsets = get_offsets(s),
1333
- t_offsets = get_offsets(t);
1334
-
1335
- VALUE s_init = default_value(s),
1336
- t_init = default_value(t);
1337
-
1338
- RETURN_SIZED_ENUMERATOR(left, 0, 0, 0);
1339
-
1340
- if (init == Qnil)
1341
- init = rb_yield_values(2, s_init, t_init);
1342
-
1343
- // Make a reasonable approximation of the resulting capacity
1344
- size_t s_ndnz = src_ndnz(s), t_ndnz = src_ndnz(t);
1345
- if (s->src != s) s_ndnz = yale_count_slice_copy_ndnz(s, s->offset, s->shape);
1346
- if (t->src != t) t_ndnz = yale_count_slice_copy_ndnz(t, t->offset, t->shape);
1347
- size_t request_capacity = shape[0] + NM_MAX(s_ndnz, t_ndnz) + 1;
1348
-
1349
- YALE_STORAGE* r = nm_yale_storage_create(nm::RUBYOBJ, shape, 2, request_capacity, itype);
1350
- if (r->capacity < request_capacity)
1351
- rb_raise(nm_eStorageTypeError, "conversion failed; capacity of %ld requested, max allowable is %ld", request_capacity, r->capacity);
1352
-
1353
- nm_yale_storage_init(r, &init);
1354
-
1355
- IJAManager<IType> sm(s, itype),
1356
- tm(t, itype);
1357
-
1358
- for (IType ri = 0; ri < shape[0]; ++ri) {
1359
- RowIterator<IType> sit(s, sm.ija, ri + s_offsets[0], shape[1], s_offsets[1]);
1360
- RowIterator<IType> tit(t, tm.ija, ri + t_offsets[0], shape[1], t_offsets[1]);
1361
-
1362
- RowIterator<IType> rit(r, reinterpret_cast<IType*>(r->ija), ri, shape[1]);
1363
- while (!sit.end() || !tit.end()) {
1364
- VALUE rv;
1365
- IType rj;
1366
-
1367
- // Perform the computation. Use a default value if the matrix doesn't have some value stored.
1368
- if (tit.end() || (!sit.end() && sit.offset_j() < tit.offset_j())) {
1369
- rv = rb_yield_values(2, sit.obj(), t_init);
1370
- rj = sit.offset_j();
1371
- ++sit;
1372
-
1373
- } else if (sit.end() || (!tit.end() && sit.offset_j() > tit.offset_j())) {
1374
- rv = rb_yield_values(2, s_init, tit.obj());
1375
- rj = tit.offset_j();
1376
- ++tit;
1377
-
1378
- } else { // same index
1379
- rv = rb_yield_values(2, sit.obj(), tit.obj());
1380
- rj = sit.offset_j();
1381
- ++sit;
1382
- ++tit;
1383
- }
1384
-
1385
- rit.insert(rj, rv); // handles increment (and testing for default, etc)
1386
-
1387
- }
1388
-
1389
- // Update the row end information.
1390
- rit.update_row_end();
1391
- }
1392
-
1393
- NMATRIX* m = nm_create(nm::YALE_STORE, reinterpret_cast<STORAGE*>(r));
1394
- return Data_Wrap_Struct(CLASS_OF(left), nm_yale_storage_mark, nm_delete, m);
1395
- }
1396
-
1397
-
1398
- /*
1399
- * This function and the two helper structs enable us to use partial template specialization.
1400
- * See also: http://stackoverflow.com/questions/6623375/c-template-specialization-on-functions
1401
- */
1402
- template <typename DType, typename IType>
1403
- static VALUE each_stored_with_indices(VALUE nm) {
1404
- YALE_STORAGE* s = NM_STORAGE_YALE(nm);
1405
- DType* a = reinterpret_cast<DType*>(s->a);
1406
- IType* ija = reinterpret_cast<IType*>(s->ija);
1407
-
1408
- // If we don't have a block, return an enumerator.
1409
- RETURN_SIZED_ENUMERATOR(nm, 0, 0, nm_yale_stored_enumerator_length);
1410
-
1411
- // Iterate along diagonal
1412
- for (size_t sk = NM_MAX(s->offset[0], s->offset[1]); sk < NM_MIN(s->shape[0] + s->offset[0], s->shape[1] + s->offset[1]); ++sk) {
1413
- VALUE ii = LONG2NUM(sk - s->offset[0]),
1414
- jj = LONG2NUM(sk - s->offset[1]);
1415
-
1416
- rb_yield_values(3, obj_at(s, sk), ii, jj);
1417
- }
1418
-
1419
- // Iterate through non-diagonal elements, row by row
1420
- for (long ri = 0; ri < s->shape[0]; ++ri) {
1421
- long si = ri + s->offset[0];
1422
- IType p = ija[si],
1423
- next_p = ija[si+1];
1424
-
1425
- // if this is a reference to another matrix, we should find the left boundary of the slice
1426
- if (s != s->src && p < next_p)
1427
- p = binary_search_left_boundary<IType>(s, p, next_p-1, s->offset[1]);
1428
-
1429
- for (; p < next_p; ++p) {
1430
- long sj = static_cast<long>(ija[p]),
1431
- rj = sj - s->offset[1];
1432
- if (rj < 0) continue;
1433
-
1434
- if (rj >= s->shape[1]) break;
1435
-
1436
- rb_yield_values(3, obj_at(s, p), LONG2NUM(ri), LONG2NUM(rj));
1437
- }
1438
- }
1439
-
1440
- return nm;
1441
- }
1442
-
1443
- template <typename DType, typename IType>
1444
- static VALUE each_with_indices(VALUE nm) {
1445
- YALE_STORAGE* s = NM_STORAGE_YALE(nm);
1446
- DType* a = reinterpret_cast<DType*>(s->a);
1447
- IType* ija = reinterpret_cast<IType*>(s->ija);
1448
-
1449
- // If we don't have a block, return an enumerator.
1450
- RETURN_SIZED_ENUMERATOR(nm, 0, 0, nm_yale_enumerator_length);
1451
-
1452
- // Iterate in two dimensions.
1453
- // s stands for src, r stands for ref (for ri, rj, si, sj)
1454
- for (long ri = 0; ri < s->shape[0]; ++ri) {
1455
- long si = ri + s->offset[0];
1456
- VALUE ii = LONG2NUM(ri + s->offset[0]);
1457
-
1458
- IType k = ija[si], k_next = ija[si+1];
1459
-
1460
- for (long rj = 0; rj < s->shape[1]; ++rj) {
1461
- long sj = rj + s->offset[1];
1462
- VALUE v, jj = LONG2NUM(rj);
1463
-
1464
- // zero is stored in s->shape[0]
1465
- if (si == sj) {
1466
- v = obj_at(s, si);
1467
- } else {
1468
- // Walk through the row until we find the correct location.
1469
- while (ija[k] < sj && k < k_next) ++k;
1470
- if (k < k_next && ija[k] == sj) {
1471
- v = obj_at(s, k);
1472
- ++k;
1473
- } else v = default_value(s); // rubyobj_from_cval(&(a[s->shape[0]]), NM_DTYPE(nm)).rval;
1474
- }
1475
- rb_yield_values(3, v, ii, jj);
1476
- }
1477
- }
1478
-
1479
- return nm;
1480
- }
1481
-
1482
-
1483
- } // end of namespace nm::yale_storage
1484
-
1485
-
1486
- // Helper function used only for the RETURN_SIZED_ENUMERATOR macro. Returns the length of
1487
- // the matrix's storage.
1488
- static VALUE nm_yale_stored_enumerator_length(VALUE nmatrix) {
1489
- long len = nm_yale_storage_get_size(NM_STORAGE_YALE(nmatrix));
1490
- return LONG2NUM(len);
1491
- }
1492
-
1493
-
1494
- } // end of namespace nm.
1495
-
1496
- ///////////////////
1497
- // Ruby Bindings //
1498
- ///////////////////
1499
-
1500
- /* These bindings are mostly only for debugging Yale. They are called from Init_nmatrix. */
1501
-
1502
- extern "C" {
1503
-
1504
- void nm_init_yale_functions() {
1505
- /*
1506
- * This module stores methods that are useful for debugging Yale matrices,
1507
- * i.e. the ones with +:yale+ stype.
1508
- */
1509
- cNMatrix_YaleFunctions = rb_define_module_under(cNMatrix, "YaleFunctions");
1510
-
1511
- rb_define_method(cNMatrix_YaleFunctions, "yale_ija", (METHOD)nm_ija, -1);
1512
- rb_define_method(cNMatrix_YaleFunctions, "yale_a", (METHOD)nm_a, -1);
1513
- rb_define_method(cNMatrix_YaleFunctions, "yale_size", (METHOD)nm_size, 0);
1514
- rb_define_method(cNMatrix_YaleFunctions, "yale_ia", (METHOD)nm_ia, 0);
1515
- rb_define_method(cNMatrix_YaleFunctions, "yale_ja", (METHOD)nm_ja, 0);
1516
- rb_define_method(cNMatrix_YaleFunctions, "yale_d", (METHOD)nm_d, -1);
1517
- rb_define_method(cNMatrix_YaleFunctions, "yale_lu", (METHOD)nm_lu, 0);
1518
-
1519
- rb_define_method(cNMatrix_YaleFunctions, "yale_nd_row", (METHOD)nm_nd_row, -1);
1520
-
1521
- rb_define_const(cNMatrix_YaleFunctions, "YALE_GROWTH_CONSTANT", rb_float_new(nm::yale_storage::GROWTH_CONSTANT));
1522
- }
1523
-
1524
-
1525
- /////////////////
1526
- // C ACCESSORS //
1527
- /////////////////
1528
-
1529
-
1530
- /* C interface for NMatrix#each_with_indices (Yale) */
1531
- VALUE nm_yale_each_with_indices(VALUE nmatrix) {
1532
- nm::dtype_t d = NM_DTYPE(nmatrix);
1533
- nm::itype_t i = NM_ITYPE(nmatrix);
1534
-
1535
- NAMED_LI_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::each_with_indices, VALUE, VALUE)
1536
-
1537
- return ttable[d][i](nmatrix);
1538
- }
1539
-
1540
-
1541
- /* C interface for NMatrix#each_stored_with_indices (Yale) */
1542
- VALUE nm_yale_each_stored_with_indices(VALUE nmatrix) {
1543
- nm::dtype_t d = NM_DTYPE(nmatrix);
1544
- nm::itype_t i = NM_ITYPE(nmatrix);
1545
-
1546
- NAMED_LI_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::each_stored_with_indices, VALUE, VALUE)
1547
-
1548
- return ttable[d][i](nmatrix);
1549
- }
1550
-
1551
-
1552
-
1553
- /*
1554
- * C accessor for inserting some value in a matrix (or replacing an existing cell).
1555
- */
1556
- char nm_yale_storage_set(STORAGE* storage, SLICE* slice, void* v) {
1557
- NAMED_LI_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::set, char, YALE_STORAGE* storage, SLICE* slice, void* value);
1558
-
1559
- YALE_STORAGE* casted_storage = (YALE_STORAGE*)storage;
1560
-
1561
- return ttable[casted_storage->dtype][casted_storage->itype](casted_storage, slice, v);
1562
- }
1563
-
1564
-
1565
- /*
1566
- * Determine the number of non-diagonal non-zeros in a not-yet-created copy of a slice or matrix.
1567
- */
1568
- static size_t yale_count_slice_copy_ndnz(const YALE_STORAGE* s, size_t* offset, size_t* shape) {
1569
- NAMED_LI_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::count_slice_copy_ndnz, size_t, const YALE_STORAGE*, size_t*, size_t*)
1570
-
1571
- return ttable[s->dtype][s->itype](s, offset, shape);
1572
- }
1573
-
1574
-
1575
- /*
1576
- * C accessor for yale_storage::get, which returns a slice of YALE_STORAGE object by copy
1577
- *
1578
- * Slicing-related.
1579
- */
1580
- void* nm_yale_storage_get(STORAGE* storage, SLICE* slice) {
1581
- YALE_STORAGE* casted_storage = (YALE_STORAGE*)storage;
1582
-
1583
- if (slice->single) {
1584
- NAMED_LI_DTYPE_TEMPLATE_TABLE(elem_copy_table, nm::yale_storage::get_single, void*, YALE_STORAGE*, SLICE*)
1585
-
1586
- return elem_copy_table[casted_storage->dtype][casted_storage->itype](casted_storage, slice);
1587
- } else {
1588
- // Copy shape for yale construction
1589
- size_t* shape = ALLOC_N(size_t, 2);
1590
- shape[0] = slice->lengths[0];
1591
- shape[1] = slice->lengths[1];
1592
-
1593
- // only count ndnz if our slice is smaller, otherwise use the given value
1594
- size_t ndnz = src_ndnz(casted_storage);
1595
- if (shape[0] != casted_storage->shape[0] || shape[1] != casted_storage->shape[1])
1596
- ndnz = yale_count_slice_copy_ndnz(casted_storage, slice->coords, shape); // expensive operation
1597
-
1598
- size_t request_capacity = shape[0] + ndnz + 1; // capacity of new matrix
1599
- YALE_STORAGE* ns = nm_yale_storage_create(casted_storage->dtype, shape, 2, request_capacity, casted_storage->itype);
1600
-
1601
- // This check probably isn't necessary.
1602
- if (ns->capacity < request_capacity)
1603
- rb_raise(nm_eStorageTypeError, "conversion failed; capacity of %ld requested, max allowable is %ld", request_capacity, ns->capacity);
1604
-
1605
- NAMED_LRI_DTYPE_TEMPLATE_TABLE(slice_copy_table, nm::yale_storage::slice_copy, void, YALE_STORAGE* ns, const YALE_STORAGE* s, size_t*, size_t*, nm::dtype_t)
1606
-
1607
- slice_copy_table[ns->dtype][casted_storage->dtype][casted_storage->itype](ns, casted_storage, slice->coords, slice->lengths, casted_storage->dtype);
1608
-
1609
- return ns;
1610
- }
1611
- }
1612
-
1613
- /*
1614
- * C accessor for yale_storage::vector_insert
1615
- */
1616
- 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, nm::itype_t itype) {
1617
- NAMED_LI_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::vector_insert, char, YALE_STORAGE*, size_t, size_t*, void*, size_t, bool);
1618
-
1619
- return ttable[dtype][itype](s, pos, js, vals, n, struct_only);
1620
- }
1621
-
1622
- /*
1623
- * C accessor for yale_storage::increment_ia_after, typically called after ::vector_insert
1624
- */
1625
- static void nm_yale_storage_increment_ia_after(YALE_STORAGE* s, size_t ija_size, size_t i, size_t n, nm::itype_t itype) {
1626
- NAMED_ITYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::c_increment_ia_after, void, YALE_STORAGE*, size_t, size_t, size_t);
1627
-
1628
- ttable[itype](s, ija_size, i, n);
1629
- }
1630
-
1631
-
1632
- /*
1633
- * C accessor for yale_storage::ref, which returns a pointer to the correct location in a YALE_STORAGE object
1634
- * for some set of coordinates.
1635
- */
1636
- void* nm_yale_storage_ref(STORAGE* storage, SLICE* slice) {
1637
- YALE_STORAGE* casted_storage = (YALE_STORAGE*)storage;
1638
-
1639
- if (slice->single) {
1640
- NAMED_LI_DTYPE_TEMPLATE_TABLE(elem_copy_table, nm::yale_storage::get_single, void*, YALE_STORAGE*, SLICE*)
1641
- return elem_copy_table[casted_storage->dtype][casted_storage->itype](casted_storage, slice);
1642
- } else {
1643
- NAMED_LI_DTYPE_TEMPLATE_TABLE(ref_table, nm::yale_storage::ref, void*, YALE_STORAGE* storage, SLICE* slice)
1644
- return ref_table[casted_storage->dtype][casted_storage->itype](casted_storage, slice);
1645
- }
1646
- }
1647
-
1648
-
1649
- /*
1650
- * C accessor for determining whether two YALE_STORAGE objects have the same contents.
1651
- */
1652
- bool nm_yale_storage_eqeq(const STORAGE* left, const STORAGE* right) {
1653
- NAMED_LRI_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::eqeq, bool, const YALE_STORAGE* left, const YALE_STORAGE* right);
1654
-
1655
- const YALE_STORAGE* casted_left = reinterpret_cast<const YALE_STORAGE*>(left);
1656
-
1657
- return ttable[casted_left->dtype][right->dtype][casted_left->itype](casted_left, (const YALE_STORAGE*)right);
1658
- }
1659
-
1660
-
1661
- /*
1662
- * Copy constructor for changing dtypes. (C accessor)
1663
- */
1664
- STORAGE* nm_yale_storage_cast_copy(const STORAGE* rhs, nm::dtype_t new_dtype, void* dummy) {
1665
- NAMED_LRI_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::cast_copy, YALE_STORAGE*, const YALE_STORAGE* rhs, nm::dtype_t new_dtype);
1666
-
1667
- const YALE_STORAGE* casted_rhs = reinterpret_cast<const YALE_STORAGE*>(rhs);
1668
-
1669
- return (STORAGE*)ttable[new_dtype][casted_rhs->dtype][casted_rhs->itype](casted_rhs, new_dtype);
1670
- }
1671
-
1672
-
1673
- /*
1674
- * Returns size of Yale storage as a size_t (no matter what the itype is). (C accessor)
1675
- */
1676
- size_t nm_yale_storage_get_size(const YALE_STORAGE* storage) {
1677
- NAMED_ITYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::get_size, size_t, const YALE_STORAGE* storage);
1678
-
1679
- return ttable[storage->itype](storage);
1680
- }
1681
-
1682
-
1683
-
1684
- /*
1685
- * Return a void pointer to the matrix's default value entry.
1686
- */
1687
- static void* default_value_ptr(const YALE_STORAGE* s) {
1688
- return reinterpret_cast<void*>(reinterpret_cast<char*>(s->a) + (s->src->shape[0] * DTYPE_SIZES[s->dtype]));
1689
- }
1690
-
1691
- /*
1692
- * Return the Ruby object at a given location in storage.
1693
- */
1694
- static VALUE obj_at(YALE_STORAGE* s, size_t k) {
1695
- if (s->dtype == nm::RUBYOBJ) return reinterpret_cast<VALUE*>(s->a)[k];
1696
- else return rubyobj_from_cval(reinterpret_cast<void*>(reinterpret_cast<char*>(s->a) + k * DTYPE_SIZES[s->dtype]), s->dtype).rval;
1697
- }
1698
-
1699
-
1700
- /*
1701
- * Return the matrix's default value as a Ruby VALUE.
1702
- */
1703
- static VALUE default_value(const YALE_STORAGE* s) {
1704
- if (s->dtype == nm::RUBYOBJ) return *reinterpret_cast<VALUE*>(default_value_ptr(s));
1705
- else return rubyobj_from_cval(default_value_ptr(s), s->dtype).rval;
1706
- }
1707
-
1708
-
1709
- /*
1710
- * Check to see if a default value is some form of zero. Easy for non-Ruby object matrices, which should always be 0.
1711
- */
1712
- static bool default_value_is_numeric_zero(const YALE_STORAGE* s) {
1713
- return rb_funcall(default_value(s), rb_intern("=="), 1, INT2FIX(0)) == Qtrue;
1714
- }
1715
-
1716
-
1717
- /*
1718
- * C accessor for allocating a yale storage object for cast-copying. Copies the IJA vector, does not copy the A vector.
1719
- */
1720
- static YALE_STORAGE* nm_copy_alloc_struct(const YALE_STORAGE* rhs, const nm::dtype_t new_dtype, const size_t new_capacity, const size_t new_size) {
1721
- NAMED_ITYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::copy_alloc_struct, YALE_STORAGE*, const YALE_STORAGE* rhs, const nm::dtype_t new_dtype, const size_t new_capacity, const size_t new_size);
1722
-
1723
- return ttable[rhs->itype](rhs, new_dtype, new_capacity, new_size);
1724
- }
1725
-
1726
- /*
1727
- * Transposing copy constructor.
1728
- */
1729
- STORAGE* nm_yale_storage_copy_transposed(const STORAGE* rhs_base) {
1730
- YALE_STORAGE* rhs = (YALE_STORAGE*)rhs_base;
1731
-
1732
- size_t* shape = ALLOC_N(size_t, 2);
1733
- shape[0] = rhs->shape[1];
1734
- shape[1] = rhs->shape[0];
1735
-
1736
- size_t size = nm_yale_storage_get_size(rhs);
1737
-
1738
- YALE_STORAGE* lhs = nm_yale_storage_create(rhs->dtype, shape, 2, size, rhs->itype);
1739
- nm_yale_storage_init(lhs, default_value_ptr(rhs));
1740
-
1741
- NAMED_LI_DTYPE_TEMPLATE_TABLE(transp, nm::math::transpose_yale, void, const size_t n, const size_t m, const void* ia_, const void* ja_, const void* a_, const bool diaga, void* ib_, void* jb_, void* b_, const bool move);
1742
-
1743
- transp[lhs->dtype][lhs->itype](rhs->shape[0], rhs->shape[1], rhs->ija, rhs->ija, rhs->a, true, lhs->ija, lhs->ija, lhs->a, true);
1744
-
1745
- return (STORAGE*)lhs;
1746
- }
1747
-
1748
- /*
1749
- * C accessor for multiplying two YALE_STORAGE matrices, which have already been casted to the same dtype.
1750
- *
1751
- * FIXME: There should be some mathematical way to determine the worst-case IType based on the input ITypes. Right now
1752
- * it just uses the default.
1753
- */
1754
- STORAGE* nm_yale_storage_matrix_multiply(const STORAGE_PAIR& casted_storage, size_t* resulting_shape, bool vector) {
1755
- LI_DTYPE_TEMPLATE_TABLE(nm::yale_storage::matrix_multiply, STORAGE*, const STORAGE_PAIR& casted_storage, size_t* resulting_shape, bool vector, nm::itype_t resulting_itype);
1756
-
1757
- YALE_STORAGE* left = reinterpret_cast<YALE_STORAGE*>(casted_storage.left);
1758
- YALE_STORAGE* right = reinterpret_cast<YALE_STORAGE*>(casted_storage.right);
1759
-
1760
- if (!default_value_is_numeric_zero(left) || !default_value_is_numeric_zero(right)) {
1761
- rb_raise(rb_eNotImpError, "matrix default value must be some form of zero (not false or nil) for multiplication");
1762
- return NULL;
1763
- }
1764
-
1765
- // Determine the itype for the matrix that will be returned.
1766
- nm::itype_t itype = nm_yale_storage_itype_by_shape(resulting_shape),
1767
- max_itype = NM_MAX_ITYPE(left->itype, right->itype);
1768
- if (static_cast<int8_t>(itype) < static_cast<int8_t>(max_itype)) itype = max_itype;
1769
-
1770
- return ttable[left->dtype][itype](casted_storage, resulting_shape, vector, itype);
1771
- }
1772
-
1773
-
1774
- ///////////////
1775
- // Lifecycle //
1776
- ///////////////
1777
-
1778
- /*
1779
- * C accessor function for creating a YALE_STORAGE object. Prior to calling this function, you MUST
1780
- * allocate shape (should be size_t * 2) -- don't use use a regular size_t array!
1781
- *
1782
- * For this type, dim must always be 2. The final argument is the initial capacity with which to
1783
- * create the storage.
1784
- */
1785
-
1786
- YALE_STORAGE* nm_yale_storage_create(nm::dtype_t dtype, size_t* shape, size_t dim, size_t init_capacity, nm::itype_t min_itype) {
1787
- YALE_STORAGE* s;
1788
- size_t max_capacity;
1789
-
1790
- // FIXME: This error should be handled in the nmatrix.c file.
1791
- if (dim != 2) {
1792
- rb_raise(rb_eNotImpError, "Can only support 2D matrices");
1793
- }
1794
-
1795
- s = alloc(dtype, shape, dim, min_itype);
1796
- max_capacity = nm::yale_storage::max_size(s);
1797
-
1798
- // Set matrix capacity (and ensure its validity)
1799
- if (init_capacity < NM_YALE_MINIMUM(s)) {
1800
- s->capacity = NM_YALE_MINIMUM(s);
1801
-
1802
- } else if (init_capacity > max_capacity) {
1803
- // Don't allow storage to be created larger than necessary
1804
- s->capacity = max_capacity;
1805
-
1806
- } else {
1807
- s->capacity = init_capacity;
1808
-
1809
- }
1810
-
1811
- s->ija = ALLOC_N( char, ITYPE_SIZES[s->itype] * s->capacity );
1812
- s->a = ALLOC_N( char, DTYPE_SIZES[s->dtype] * s->capacity );
1813
-
1814
- return s;
1815
- }
1816
-
1817
- /*
1818
- * Destructor for yale storage (C-accessible).
1819
- */
1820
- void nm_yale_storage_delete(STORAGE* s) {
1821
- if (s) {
1822
- YALE_STORAGE* storage = (YALE_STORAGE*)s;
1823
- if (storage->count-- == 1) {
1824
- xfree(storage->shape);
1825
- xfree(storage->offset);
1826
- xfree(storage->ija);
1827
- xfree(storage->a);
1828
- xfree(storage);
1829
- }
1830
- }
1831
- }
1832
-
1833
- /*
1834
- * Destructor for the yale storage ref
1835
- */
1836
- void nm_yale_storage_delete_ref(STORAGE* s) {
1837
- if (s) {
1838
- YALE_STORAGE* storage = (YALE_STORAGE*)s;
1839
- nm_yale_storage_delete( reinterpret_cast<STORAGE*>(storage->src) );
1840
- xfree(storage->shape);
1841
- xfree(storage->offset);
1842
- xfree(s);
1843
- }
1844
- }
1845
-
1846
- /*
1847
- * C accessor for yale_storage::init, a templated function.
1848
- *
1849
- * Initializes the IJA vector of the YALE_STORAGE matrix.
1850
- */
1851
- void nm_yale_storage_init(YALE_STORAGE* s, void* init_val) {
1852
- NAMED_LI_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::init, void, YALE_STORAGE*, void*);
1853
-
1854
- ttable[s->dtype][s->itype](s, init_val);
1855
- }
1856
-
1857
-
1858
- /*
1859
- * Ruby GC mark function for YALE_STORAGE. C accessible.
1860
- */
1861
- void nm_yale_storage_mark(void* storage_base) {
1862
- YALE_STORAGE* storage = (YALE_STORAGE*)storage_base;
1863
- size_t i;
1864
-
1865
- if (storage && storage->dtype == nm::RUBYOBJ) {
1866
- for (i = storage->capacity; i-- > 0;) {
1867
- rb_gc_mark(*((VALUE*)((char*)(storage->a) + i*DTYPE_SIZES[nm::RUBYOBJ])));
1868
- }
1869
- }
1870
- }
1871
-
1872
-
1873
- /*
1874
- * Allocates and initializes the basic struct (but not the IJA or A vectors).
1875
- */
1876
- static YALE_STORAGE* alloc(nm::dtype_t dtype, size_t* shape, size_t dim, nm::itype_t min_itype) {
1877
- YALE_STORAGE* s;
1878
-
1879
- s = ALLOC( YALE_STORAGE );
1880
-
1881
- s->ndnz = 0;
1882
- s->dtype = dtype;
1883
- s->shape = shape;
1884
- s->offset = ALLOC_N(size_t, dim);
1885
- for (size_t i = 0; i < dim; ++i)
1886
- s->offset[i] = 0;
1887
- s->dim = dim;
1888
- s->itype = nm_yale_storage_itype_by_shape(shape);
1889
- s->src = reinterpret_cast<STORAGE*>(s);
1890
- s->count = 1;
1891
-
1892
- // See if a higher itype has been requested.
1893
- if (static_cast<int8_t>(s->itype) < static_cast<int8_t>(min_itype))
1894
- s->itype = min_itype;
1895
-
1896
- return s;
1897
- }
1898
-
1899
- YALE_STORAGE* nm_yale_storage_create_from_old_yale(nm::dtype_t dtype, size_t* shape, void* ia, void* ja, void* a, nm::dtype_t from_dtype) {
1900
-
1901
- NAMED_LRI_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::create_from_old_yale, YALE_STORAGE*, nm::dtype_t dtype, size_t* shape, void* r_ia, void* r_ja, void* r_a);
1902
-
1903
- // With C++ templates, we don't want to have a 4-parameter template. That would be LDType, RDType, LIType, RIType.
1904
- // We can prevent that by copying ia and ja into the correct itype (if necessary) before passing them to the yale
1905
- // copy constructor.
1906
- nm::itype_t to_itype = nm_yale_storage_itype_by_shape(shape);
1907
-
1908
- return ttable[dtype][from_dtype][to_itype](dtype, shape, ia, ja, a);
1909
-
1910
- }
1911
-
1912
- //////////////////////////////////////////////
1913
- // YALE-SPECIFIC FUNCTIONS (RUBY ACCESSORS) //
1914
- //////////////////////////////////////////////
1915
-
1916
- /*
1917
- * call-seq:
1918
- * yale_size -> Integer
1919
- *
1920
- * Get the size of a Yale matrix (the number of elements actually stored).
1921
- *
1922
- * For capacity (the maximum number of elements that can be stored without a resize), use capacity instead.
1923
- */
1924
- static VALUE nm_size(VALUE self) {
1925
- YALE_STORAGE* s = (YALE_STORAGE*)NM_STORAGE(self);
1926
-
1927
- return rubyobj_from_cval_by_itype((char*)(s->ija) + ITYPE_SIZES[s->itype]*(s->shape[0]), s->itype).rval;
1928
- }
1929
-
1930
-
1931
- /*
1932
- * call-seq:
1933
- * yale_a -> Array
1934
- * yale_d(index) -> ...
1935
- *
1936
- * Get the A array of a Yale matrix (which stores the diagonal and the LU portions of the matrix).
1937
- */
1938
- static VALUE nm_a(int argc, VALUE* argv, VALUE self) {
1939
- VALUE idx;
1940
- rb_scan_args(argc, argv, "01", &idx);
1941
-
1942
- YALE_STORAGE* s = NM_STORAGE_YALE(self);
1943
- size_t size = nm_yale_storage_get_size(s);
1944
-
1945
- if (idx == Qnil) {
1946
- VALUE* vals = ALLOCA_N(VALUE, size);
1947
-
1948
- if (NM_DTYPE(self) == nm::RUBYOBJ) {
1949
- for (size_t i = 0; i < size; ++i) {
1950
- vals[i] = reinterpret_cast<VALUE*>(s->a)[i];
1951
- }
1952
- } else {
1953
- for (size_t i = 0; i < size; ++i) {
1954
- vals[i] = rubyobj_from_cval((char*)(s->a) + DTYPE_SIZES[s->dtype]*i, s->dtype).rval;
1955
- }
1956
- }
1957
- VALUE ary = rb_ary_new4(size, vals);
1958
-
1959
- for (size_t i = size; i < reinterpret_cast<YALE_STORAGE*>(s->src)->capacity; ++i)
1960
- rb_ary_push(ary, Qnil);
1961
-
1962
- return ary;
1963
- } else {
1964
- size_t index = FIX2INT(idx);
1965
- if (index >= size) rb_raise(rb_eRangeError, "out of range");
1966
-
1967
- return rubyobj_from_cval((char*)(s->a) + DTYPE_SIZES[s->dtype] * index, s->dtype).rval;
1968
- }
1969
- }
1970
-
1971
-
1972
- /*
1973
- * call-seq:
1974
- * yale_d -> Array
1975
- * yale_d(index) -> ...
1976
- *
1977
- * Get the diagonal ("D") portion of the A array of a Yale matrix.
1978
- */
1979
- static VALUE nm_d(int argc, VALUE* argv, VALUE self) {
1980
- VALUE idx;
1981
- rb_scan_args(argc, argv, "01", &idx);
1982
-
1983
- YALE_STORAGE* s = NM_STORAGE_YALE(self);
1984
-
1985
- if (idx == Qnil) {
1986
- VALUE* vals = ALLOCA_N(VALUE, s->shape[0]);
1987
-
1988
- if (NM_DTYPE(self) == nm::RUBYOBJ) {
1989
- for (size_t i = 0; i < s->shape[0]; ++i) {
1990
- vals[i] = reinterpret_cast<VALUE*>(s->a)[i];
1991
- }
1992
- } else {
1993
- for (size_t i = 0; i < s->shape[0]; ++i) {
1994
- vals[i] = rubyobj_from_cval((char*)(s->a) + DTYPE_SIZES[s->dtype]*i, s->dtype).rval;
1995
- }
1996
- }
1997
-
1998
- return rb_ary_new4(s->shape[0], vals);
1999
- } else {
2000
- size_t index = FIX2INT(idx);
2001
- if (index >= s->shape[0]) rb_raise(rb_eRangeError, "out of range");
2002
-
2003
- return rubyobj_from_cval((char*)(s->a) + DTYPE_SIZES[s->dtype] * index, s->dtype).rval;
2004
- }
2005
- }
2006
-
2007
- /*
2008
- * call-seq:
2009
- * yale_lu -> Array
2010
- *
2011
- * Get the non-diagonal ("LU") portion of the A array of a Yale matrix.
2012
- */
2013
- static VALUE nm_lu(VALUE self) {
2014
- YALE_STORAGE* s = NM_STORAGE_YALE(self);
2015
-
2016
- size_t size = nm_yale_storage_get_size(s);
2017
-
2018
- VALUE* vals = ALLOCA_N(VALUE, size - s->shape[0] - 1);
2019
-
2020
- if (NM_DTYPE(self) == nm::RUBYOBJ) {
2021
- for (size_t i = 0; i < size - s->shape[0] - 1; ++i) {
2022
- vals[i] = reinterpret_cast<VALUE*>(s->a)[s->shape[0] + 1 + i];
2023
- }
2024
- } else {
2025
- for (size_t i = 0; i < size - s->shape[0] - 1; ++i) {
2026
- vals[i] = rubyobj_from_cval((char*)(s->a) + DTYPE_SIZES[s->dtype]*(s->shape[0] + 1 + i), s->dtype).rval;
2027
- }
2028
- }
2029
-
2030
- VALUE ary = rb_ary_new4(size - s->shape[0] - 1, vals);
2031
-
2032
- for (size_t i = size; i < reinterpret_cast<YALE_STORAGE*>(s->src)->capacity; ++i)
2033
- rb_ary_push(ary, Qnil);
2034
-
2035
- return ary;
2036
- }
2037
-
2038
- /*
2039
- * call-seq:
2040
- * yale_ia -> Array
2041
- *
2042
- * Get the IA portion of the IJA array of a Yale matrix. This gives the start and end positions of rows in the
2043
- * JA and LU portions of the IJA and A arrays, respectively.
2044
- */
2045
- static VALUE nm_ia(VALUE self) {
2046
- YALE_STORAGE* s = NM_STORAGE_YALE(self);
2047
-
2048
- VALUE* vals = ALLOCA_N(VALUE, s->shape[0] + 1);
2049
-
2050
- for (size_t i = 0; i < s->shape[0] + 1; ++i) {
2051
- vals[i] = rubyobj_from_cval_by_itype((char*)(s->ija) + ITYPE_SIZES[s->itype]*i, s->itype).rval;
2052
- }
2053
-
2054
- return rb_ary_new4(s->shape[0]+1, vals);
2055
- }
2056
-
2057
- /*
2058
- * call-seq:
2059
- * yale_ja -> Array
2060
- *
2061
- * Get the JA portion of the IJA array of a Yale matrix. This gives the column indices for entries in corresponding
2062
- * positions in the LU portion of the A array.
2063
- */
2064
- static VALUE nm_ja(VALUE self) {
2065
- YALE_STORAGE* s = NM_STORAGE_YALE(self);
2066
-
2067
- size_t size = nm_yale_storage_get_size(s);
2068
-
2069
- VALUE* vals = ALLOCA_N(VALUE, size - s->shape[0] - 1);
2070
-
2071
- for (size_t i = 0; i < size - s->shape[0] - 1; ++i) {
2072
- vals[i] = rubyobj_from_cval_by_itype((char*)(s->ija) + ITYPE_SIZES[s->itype]*(s->shape[0] + 1 + i), s->itype).rval;
2073
- }
2074
-
2075
- VALUE ary = rb_ary_new4(size - s->shape[0] - 1, vals);
2076
-
2077
- for (size_t i = size; i < reinterpret_cast<YALE_STORAGE*>(s->src)->capacity; ++i)
2078
- rb_ary_push(ary, Qnil);
2079
-
2080
- return ary;
2081
- }
2082
-
2083
- /*
2084
- * call-seq:
2085
- * yale_ija -> Array
2086
- * yale_ija(index) -> ...
2087
- *
2088
- * Get the IJA array of a Yale matrix (or a component of the IJA array).
2089
- */
2090
- static VALUE nm_ija(int argc, VALUE* argv, VALUE self) {
2091
- VALUE idx;
2092
- rb_scan_args(argc, argv, "01", &idx);
2093
-
2094
- YALE_STORAGE* s = NM_STORAGE_YALE(self);
2095
- size_t size = nm_yale_storage_get_size(s);
2096
-
2097
- if (idx == Qnil) {
2098
-
2099
- VALUE* vals = ALLOCA_N(VALUE, size);
2100
-
2101
- for (size_t i = 0; i < size; ++i) {
2102
- vals[i] = rubyobj_from_cval_by_itype((char*)(s->ija) + ITYPE_SIZES[s->itype]*i, s->itype).rval;
2103
- }
2104
-
2105
- VALUE ary = rb_ary_new4(size, vals);
2106
-
2107
- for (size_t i = size; i < reinterpret_cast<YALE_STORAGE*>(s->src)->capacity; ++i)
2108
- rb_ary_push(ary, Qnil);
2109
-
2110
- return ary;
2111
-
2112
- } else {
2113
- size_t index = FIX2INT(idx);
2114
- if (index >= size) rb_raise(rb_eRangeError, "out of range");
2115
-
2116
- return rubyobj_from_cval_by_itype((char*)(s->ija) + ITYPE_SIZES[s->itype] * index, s->itype).rval;
2117
- }
2118
- }
2119
-
2120
-
2121
- /*
2122
- * call-seq:
2123
- * yale_nd_row -> ...
2124
- *
2125
- * This function gets the non-diagonal contents of a Yale matrix row.
2126
- * The first argument should be the row index. The optional second argument may be :hash or :keys, but defaults
2127
- * to :hash. If :keys is given, it will only return the Hash keys (the column indices).
2128
- *
2129
- * This function is meant to accomplish its purpose as efficiently as possible. It does not check for appropriate
2130
- * range.
2131
- */
2132
- static VALUE nm_nd_row(int argc, VALUE* argv, VALUE self) {
2133
- VALUE i_, as;
2134
- rb_scan_args(argc, argv, "11", &i_, &as);
2135
-
2136
- bool keys = false;
2137
- if (as != Qnil && rb_to_id(as) != nm_rb_hash) keys = true;
2138
-
2139
- size_t i = FIX2INT(i_);
2140
-
2141
- YALE_STORAGE* s = NM_STORAGE_YALE(self);
2142
- nm::dtype_t dtype = NM_DTYPE(self);
2143
- nm::itype_t itype = NM_ITYPE(self);
2144
-
2145
- // get the position as a size_t
2146
- // TODO: Come up with a faster way to get this than transforming to a Ruby object first.
2147
- size_t pos = FIX2INT(rubyobj_from_cval_by_itype((char*)(s->ija) + ITYPE_SIZES[itype]*i, itype).rval);
2148
- size_t nextpos = FIX2INT(rubyobj_from_cval_by_itype((char*)(s->ija) + ITYPE_SIZES[itype]*(i+1), itype).rval);
2149
- size_t diff = nextpos - pos;
2150
-
2151
- VALUE ret;
2152
- if (keys) {
2153
- ret = rb_ary_new3(diff);
2154
-
2155
- for (size_t idx = pos; idx < nextpos; ++idx) {
2156
- rb_ary_store(ret, idx - pos, rubyobj_from_cval_by_itype((char*)(s->ija) + ITYPE_SIZES[s->itype]*idx, s->itype).rval);
2157
- }
2158
-
2159
- } else {
2160
- ret = rb_hash_new();
2161
-
2162
- for (size_t idx = pos; idx < nextpos; ++idx) {
2163
- rb_hash_aset(ret, rubyobj_from_cval_by_itype((char*)(s->ija) + ITYPE_SIZES[s->itype]*idx, s->itype).rval,
2164
- rubyobj_from_cval((char*)(s->a) + DTYPE_SIZES[s->dtype]*idx, s->dtype).rval);
2165
- }
2166
- }
2167
-
2168
- return ret;
2169
- }
2170
-
2171
- /*
2172
- * call-seq:
2173
- * yale_vector_set(i, column_index_array, cell_contents_array, pos) -> Fixnum
2174
- *
2175
- * Insert at position pos an array of non-diagonal elements with column indices given. Note that the column indices and values
2176
- * must be storage-contiguous -- that is, you can't insert them around existing elements in some row, only amid some
2177
- * elements in some row. You *can* insert them around a diagonal element, since this is stored separately. This function
2178
- * may not be used for the insertion of diagonal elements in most cases, as these are already present in the data
2179
- * structure and are typically modified by replacement rather than insertion.
2180
- *
2181
- * The last argument, pos, may be nil if you want to insert at the beginning of a row. Otherwise it needs to be provided.
2182
- * Don't expect this function to know the difference. It really does very little checking, because its goal is to make
2183
- * multiple contiguous insertion as quick as possible.
2184
- *
2185
- * You should also not attempt to insert values which are the default (0). These are not supposed to be stored, and may
2186
- * lead to undefined behavior.
2187
- *
2188
- * Example:
2189
- * m.yale_vector_set(3, [0,3,4], [1,1,1], 15)
2190
- *
2191
- * The example above inserts the values 1, 1, and 1 in columns 0, 3, and 4, assumed to be located at position 15 (which
2192
- * corresponds to row 3).
2193
- *
2194
- * Example:
2195
- * next = m.yale_vector_set(3, [0,3,4], [1,1,1])
2196
- *
2197
- * This example determines that i=3 is at position 15 automatically. The value returned, next, is the position where the
2198
- * next value(s) should be inserted.
2199
- */
2200
- VALUE nm_vector_set(int argc, VALUE* argv, VALUE self) { //, VALUE i_, VALUE jv, VALUE vv, VALUE pos_) {
2201
-
2202
- // i, jv, vv are mandatory; pos is optional; thus "31"
2203
- VALUE i_, jv, vv, pos_;
2204
- rb_scan_args(argc, argv, "31", &i_, &jv, &vv, &pos_);
2205
-
2206
- size_t len = RARRAY_LEN(jv); // need length in order to read the arrays in
2207
- size_t vvlen = RARRAY_LEN(vv);
2208
- if (len != vvlen)
2209
- rb_raise(rb_eArgError, "lengths must match between j array (%d) and value array (%d)", len, vvlen);
2210
-
2211
- YALE_STORAGE* s = NM_STORAGE_YALE(self);
2212
- nm::dtype_t dtype = NM_DTYPE(self);
2213
- nm::itype_t itype = NM_ITYPE(self);
2214
-
2215
- size_t i = FIX2INT(i_); // get the row
2216
-
2217
- // get the position as a size_t
2218
- // TODO: Come up with a faster way to get this than transforming to a Ruby object first.
2219
- if (pos_ == Qnil) pos_ = rubyobj_from_cval_by_itype((char*)(s->ija) + ITYPE_SIZES[itype]*i, itype).rval;
2220
- size_t pos = FIX2INT(pos_);
2221
-
2222
- // Allocate the j array and the values array
2223
- size_t* j = ALLOCA_N(size_t, len);
2224
- void* vals = ALLOCA_N(char, DTYPE_SIZES[dtype] * len);
2225
-
2226
- // Copy array contents
2227
- for (size_t idx = 0; idx < len; ++idx) {
2228
- j[idx] = FIX2INT(rb_ary_entry(jv, idx));
2229
- rubyval_to_cval(rb_ary_entry(vv, idx), dtype, (char*)vals + idx * DTYPE_SIZES[dtype]);
2230
- }
2231
-
2232
- char ins_type = nm_yale_storage_vector_insert(s, pos, j, vals, len, false, dtype, itype);
2233
- nm_yale_storage_increment_ia_after(s, s->shape[0], i, len, itype);
2234
- reinterpret_cast<YALE_STORAGE*>(s->src)->ndnz += len;
2235
-
2236
- // Return the updated position
2237
- pos += len;
2238
- return INT2FIX(pos);
2239
- }
2240
-
2241
-
2242
-
2243
-
2244
- /*
2245
- * call-seq:
2246
- * __yale_default_value__ -> ...
2247
- *
2248
- * Get the default_value property from a yale matrix.
2249
- */
2250
- VALUE nm_yale_default_value(VALUE self) {
2251
- return default_value(NM_STORAGE_YALE(self));
2252
- }
2253
-
2254
-
2255
- /*
2256
- * call-seq:
2257
- * __yale_map_merged_stored__(right) -> Enumerator
2258
- *
2259
- * A map operation on two Yale matrices which only iterates across the stored indices.
2260
- */
2261
- VALUE nm_yale_map_merged_stored(VALUE left, VALUE right, VALUE init) {
2262
- YALE_STORAGE *s = NM_STORAGE_YALE(left),
2263
- *t = NM_STORAGE_YALE(right);
2264
-
2265
- ITYPE_TEMPLATE_TABLE(nm::yale_storage::map_merged_stored, VALUE, VALUE l, VALUE r, VALUE init, nm::itype_t)
2266
-
2267
- nm::itype_t itype = NM_MAX_ITYPE(s->itype, t->itype);
2268
- return ttable[itype](left, right, init, itype);
2269
- }
2270
-
2271
-
2272
- /*
2273
- * call-seq:
2274
- * __yale_map_stored__ -> Enumerator
2275
- *
2276
- * A map operation on two Yale matrices which only iterates across the stored indices.
2277
- */
2278
- VALUE nm_yale_map_stored(VALUE self) {
2279
- ITYPE_TEMPLATE_TABLE(nm::yale_storage::map_stored, VALUE, VALUE)
2280
-
2281
- return ttable[NM_ITYPE(self)](self);
2282
- }
2283
-
2284
- } // end of extern "C" block