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
@@ -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