nmatrix 0.0.3 → 0.0.4

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 (63) hide show
  1. data/.gitignore +3 -0
  2. data/CONTRIBUTING.md +66 -0
  3. data/Gemfile +1 -1
  4. data/History.txt +68 -10
  5. data/LICENSE.txt +2 -2
  6. data/Manifest.txt +2 -0
  7. data/README.rdoc +90 -69
  8. data/Rakefile +18 -9
  9. data/ext/nmatrix/data/complex.h +7 -7
  10. data/ext/nmatrix/data/data.cpp +2 -7
  11. data/ext/nmatrix/data/data.h +7 -4
  12. data/ext/nmatrix/data/rational.h +2 -2
  13. data/ext/nmatrix/data/ruby_object.h +3 -10
  14. data/ext/nmatrix/extconf.rb +79 -54
  15. data/ext/nmatrix/new_extconf.rb +11 -12
  16. data/ext/nmatrix/nmatrix.cpp +94 -125
  17. data/ext/nmatrix/nmatrix.h +38 -17
  18. data/ext/nmatrix/ruby_constants.cpp +2 -15
  19. data/ext/nmatrix/ruby_constants.h +2 -14
  20. data/ext/nmatrix/storage/common.cpp +2 -2
  21. data/ext/nmatrix/storage/common.h +2 -2
  22. data/ext/nmatrix/storage/dense.cpp +206 -31
  23. data/ext/nmatrix/storage/dense.h +5 -2
  24. data/ext/nmatrix/storage/list.cpp +52 -4
  25. data/ext/nmatrix/storage/list.h +3 -2
  26. data/ext/nmatrix/storage/storage.cpp +6 -6
  27. data/ext/nmatrix/storage/storage.h +2 -2
  28. data/ext/nmatrix/storage/yale.cpp +202 -49
  29. data/ext/nmatrix/storage/yale.h +5 -4
  30. data/ext/nmatrix/ttable_helper.rb +108 -108
  31. data/ext/nmatrix/types.h +2 -15
  32. data/ext/nmatrix/util/io.cpp +2 -2
  33. data/ext/nmatrix/util/io.h +2 -2
  34. data/ext/nmatrix/util/lapack.h +2 -2
  35. data/ext/nmatrix/util/math.cpp +14 -14
  36. data/ext/nmatrix/util/math.h +2 -2
  37. data/ext/nmatrix/util/sl_list.cpp +2 -2
  38. data/ext/nmatrix/util/sl_list.h +2 -2
  39. data/ext/nmatrix/util/util.h +2 -2
  40. data/lib/nmatrix.rb +13 -35
  41. data/lib/nmatrix/blas.rb +182 -56
  42. data/lib/nmatrix/io/market.rb +38 -14
  43. data/lib/nmatrix/io/mat5_reader.rb +393 -278
  44. data/lib/nmatrix/io/mat_reader.rb +121 -107
  45. data/lib/nmatrix/lapack.rb +59 -14
  46. data/lib/nmatrix/monkeys.rb +32 -30
  47. data/lib/nmatrix/nmatrix.rb +204 -100
  48. data/lib/nmatrix/nvector.rb +166 -57
  49. data/lib/nmatrix/shortcuts.rb +364 -231
  50. data/lib/nmatrix/version.rb +8 -4
  51. data/nmatrix.gemspec +5 -3
  52. data/scripts/mac-brew-gcc.sh +1 -1
  53. data/spec/blas_spec.rb +80 -2
  54. data/spec/math_spec.rb +78 -32
  55. data/spec/nmatrix_list_spec.rb +55 -55
  56. data/spec/nmatrix_spec.rb +60 -117
  57. data/spec/nmatrix_yale_resize_test_associations.yaml +2802 -0
  58. data/spec/nmatrix_yale_spec.rb +214 -198
  59. data/spec/nvector_spec.rb +58 -2
  60. data/spec/shortcuts_spec.rb +156 -32
  61. data/spec/slice_spec.rb +229 -178
  62. data/spec/spec_helper.rb +2 -2
  63. metadata +71 -21
@@ -9,8 +9,8 @@
9
9
  //
10
10
  // == Copyright Information
11
11
  //
12
- // SciRuby is Copyright (c) 2010 - 2012, Ruby Science Foundation
13
- // NMatrix is Copyright (c) 2012, Ruby Science Foundation
12
+ // SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
13
+ // NMatrix is Copyright (c) 2013, Ruby Science Foundation
14
14
  //
15
15
  // Please see LICENSE.txt for additional copyright notices.
16
16
  //
@@ -78,6 +78,8 @@ void nm_dense_storage_mark(void*);
78
78
  // Accessors //
79
79
  ///////////////
80
80
 
81
+ VALUE nm_dense_each(VALUE nmatrix);
82
+ VALUE nm_dense_each_with_indices(VALUE nmatrix);
81
83
  void* nm_dense_storage_get(STORAGE* s, SLICE* slice);
82
84
  void* nm_dense_storage_ref(STORAGE* s, SLICE* slice);
83
85
  void nm_dense_storage_set(STORAGE* s, SLICE* slice, void* val);
@@ -102,6 +104,7 @@ STORAGE* nm_dense_storage_matrix_multiply(const STORAGE_PAIR& casted_storage, si
102
104
  /////////////
103
105
 
104
106
  size_t nm_dense_storage_pos(const DENSE_STORAGE* s, const size_t* coords);
107
+ void nm_dense_storage_coords(const DENSE_STORAGE* s, const size_t slice_pos, size_t* coords_out);
105
108
 
106
109
  /////////////////////////
107
110
  // Copying and Casting //
@@ -9,8 +9,8 @@
9
9
  //
10
10
  // == Copyright Information
11
11
  //
12
- // SciRuby is Copyright (c) 2010 - 2012, Ruby Science Foundation
13
- // NMatrix is Copyright (c) 2012, Ruby Science Foundation
12
+ // SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
13
+ // NMatrix is Copyright (c) 2013, Ruby Science Foundation
14
14
  //
15
15
  // Please see LICENSE.txt for additional copyright notices.
16
16
  //
@@ -182,9 +182,57 @@ NODE* list_storage_get_single_node(LIST_STORAGE* s, SLICE* slice)
182
182
  return n;
183
183
  }
184
184
 
185
+ /*
186
+ * Each stored iterator, brings along the indices
187
+ */
188
+ VALUE nm_list_each_stored_with_indices(VALUE nmatrix) {
185
189
 
186
- static LIST* slice_copy(const LIST_STORAGE *src, LIST *src_rows, size_t *coords, size_t *lengths, size_t n)
187
- {
190
+ // If we don't have a block, return an enumerator.
191
+ RETURN_SIZED_ENUMERATOR(nmatrix, 0, 0, 0);
192
+
193
+ LIST_STORAGE* s = NM_STORAGE_LIST(nmatrix);
194
+ // Create indices and initialize to zero
195
+ size_t* coords = ALLOCA_N(size_t, s->dim);
196
+ memset(coords, 0, sizeof(size_t) * s->dim);
197
+
198
+ // Set up the LIST and NODE
199
+ LIST* l = s->rows;
200
+ NODE* curr = l->first;
201
+
202
+ // Levels...
203
+ while (curr) { // LOOPS through the rows
204
+ NODE* subnode = reinterpret_cast<LIST*>(curr->val)->first;
205
+ size_t row = curr->key;
206
+ // Iterate along each row, yielding the val, row, col for each non-default entry
207
+ while (subnode) {
208
+ VALUE ary = rb_ary_new();
209
+ size_t col = subnode->key;
210
+
211
+ // Conditional type handling
212
+ if (NM_DTYPE(nmatrix) == nm::RUBYOBJ) {
213
+ rb_ary_push(ary, *reinterpret_cast<VALUE*>(subnode->val));
214
+ } else {
215
+ rb_ary_push(ary, rubyobj_from_cval((char*)(subnode->val ) , NM_DTYPE(nmatrix)).rval);
216
+ }
217
+
218
+ // Push the coordinate values into the ary
219
+ rb_ary_push(ary, INT2FIX(row));
220
+ rb_ary_push(ary, INT2FIX(col));
221
+
222
+ // Yield the ary
223
+ rb_yield(ary);
224
+
225
+ // Update the col position
226
+ subnode = subnode->next;
227
+ }
228
+ // Update the row node
229
+ curr = curr->next;
230
+ }
231
+ return nmatrix;
232
+ }
233
+
234
+
235
+ static LIST* slice_copy(const LIST_STORAGE *src, LIST *src_rows, size_t *coords, size_t *lengths, size_t n) {
188
236
  NODE *src_node;
189
237
  LIST *dst_rows = NULL;
190
238
  void *val = NULL;
@@ -9,8 +9,8 @@
9
9
  //
10
10
  // == Copyright Information
11
11
  //
12
- // SciRuby is Copyright (c) 2010 - 2012, Ruby Science Foundation
13
- // NMatrix is Copyright (c) 2012, Ruby Science Foundation
12
+ // SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
13
+ // NMatrix is Copyright (c) 2013, Ruby Science Foundation
14
14
  //
15
15
  // Please see LICENSE.txt for additional copyright notices.
16
16
  //
@@ -82,6 +82,7 @@ extern "C" {
82
82
  // Accessors //
83
83
  ///////////////
84
84
 
85
+ VALUE nm_list_each_stored_with_indices(VALUE nmatrix);
85
86
  void* nm_list_storage_ref(STORAGE* s, SLICE* slice);
86
87
  void* nm_list_storage_get(STORAGE* s, SLICE* slice);
87
88
  void* nm_list_storage_insert(STORAGE* s, SLICE* slice, void* val);
@@ -9,8 +9,8 @@
9
9
  //
10
10
  // == Copyright Information
11
11
  //
12
- // SciRuby is Copyright (c) 2010 - 2012, Ruby Science Foundation
13
- // NMatrix is Copyright (c) 2012, Ruby Science Foundation
12
+ // SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
13
+ // NMatrix is Copyright (c) 2013, Ruby Science Foundation
14
14
  //
15
15
  // Please see LICENSE.txt for additional copyright notices.
16
16
  //
@@ -485,7 +485,7 @@ namespace yale_storage { // FIXME: Move to yale.cpp
485
485
  size_t request_capacity = shape[0] + ndnz + 1;
486
486
 
487
487
  // Create with minimum possible capacity -- just enough to hold all of the entries
488
- YALE_STORAGE* lhs = nm_yale_storage_create(l_dtype, shape, 2, request_capacity);
488
+ YALE_STORAGE* lhs = nm_yale_storage_create(l_dtype, shape, 2, request_capacity, UINT8);
489
489
 
490
490
  if (lhs->capacity < request_capacity)
491
491
  rb_raise(nm_eStorageTypeError, "conversion failed; capacity of %ld requested, max allowable is %ld", (unsigned long)request_capacity, (unsigned long)(lhs->capacity));
@@ -546,7 +546,7 @@ namespace yale_storage { // FIXME: Move to yale.cpp
546
546
  shape[1] = rhs->shape[1];
547
547
 
548
548
  size_t request_capacity = shape[0] + ndnz + 1;
549
- YALE_STORAGE* lhs = nm_yale_storage_create(l_dtype, shape, 2, request_capacity);
549
+ YALE_STORAGE* lhs = nm_yale_storage_create(l_dtype, shape, 2, request_capacity, UINT8);
550
550
 
551
551
  if (lhs->capacity < request_capacity)
552
552
  rb_raise(nm_eStorageTypeError, "conversion failed; capacity of %ld requested, max allowable is %ld", (unsigned long)request_capacity, (unsigned long)(lhs->capacity));
@@ -614,7 +614,7 @@ extern "C" {
614
614
  STORAGE* nm_yale_storage_from_dense(const STORAGE* right, nm::dtype_t l_dtype) {
615
615
  NAMED_LRI_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::create_from_dense_storage, YALE_STORAGE*, const DENSE_STORAGE* rhs, nm::dtype_t l_dtype);
616
616
 
617
- nm::itype_t itype = nm_yale_storage_itype((const YALE_STORAGE*)right);
617
+ nm::itype_t itype = nm_yale_storage_default_itype((const YALE_STORAGE*)right);
618
618
 
619
619
  return (STORAGE*)ttable[l_dtype][right->dtype][itype]((const DENSE_STORAGE*)right, l_dtype);
620
620
  }
@@ -622,7 +622,7 @@ extern "C" {
622
622
  STORAGE* nm_yale_storage_from_list(const STORAGE* right, nm::dtype_t l_dtype) {
623
623
  NAMED_LRI_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::create_from_list_storage, YALE_STORAGE*, const LIST_STORAGE* rhs, nm::dtype_t l_dtype);
624
624
 
625
- nm::itype_t itype = nm_yale_storage_itype((const YALE_STORAGE*)right);
625
+ nm::itype_t itype = nm_yale_storage_default_itype((const YALE_STORAGE*)right);
626
626
 
627
627
  return (STORAGE*)ttable[l_dtype][right->dtype][itype]((const LIST_STORAGE*)right, l_dtype);
628
628
  }
@@ -9,8 +9,8 @@
9
9
  //
10
10
  // == Copyright Information
11
11
  //
12
- // SciRuby is Copyright (c) 2010 - 2012, Ruby Science Foundation
13
- // NMatrix is Copyright (c) 2012, Ruby Science Foundation
12
+ // SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
13
+ // NMatrix is Copyright (c) 2013, Ruby Science Foundation
14
14
  //
15
15
  // Please see LICENSE.txt for additional copyright notices.
16
16
  //
@@ -9,8 +9,8 @@
9
9
  //
10
10
  // == Copyright Information
11
11
  //
12
- // SciRuby is Copyright (c) 2010 - 2012, Ruby Science Foundation
13
- // NMatrix is Copyright (c) 2012, Ruby Science Foundation
12
+ // SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
13
+ // NMatrix is Copyright (c) 2013, Ruby Science Foundation
14
14
  //
15
15
  // Please see LICENSE.txt for additional copyright notices.
16
16
  //
@@ -42,6 +42,7 @@
42
42
  #include <ruby.h>
43
43
  #include <algorithm> // std::min
44
44
  #include <cstdio> // std::fprintf
45
+ #include <iostream>
45
46
 
46
47
  /*
47
48
  * Project Includes
@@ -61,23 +62,19 @@
61
62
  /*
62
63
  * Macros
63
64
  */
65
+
64
66
  #ifndef NM_MAX
65
67
  #define NM_MAX(a,b) (((a)>(b))?(a):(b))
66
68
  #define NM_MIN(a,b) (((a)<(b))?(a):(b))
67
69
  #endif
68
70
 
69
-
70
- /*
71
- * Global Variables
72
- */
73
-
74
71
  /*
75
72
  * Forward Declarations
76
73
  */
77
74
 
78
75
  extern "C" {
79
76
  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);
80
- static YALE_STORAGE* alloc(nm::dtype_t dtype, size_t* shape, size_t dim);
77
+ static YALE_STORAGE* alloc(nm::dtype_t dtype, size_t* shape, size_t dim, nm::itype_t min_itype);
81
78
 
82
79
  /* Ruby-accessible functions */
83
80
  static VALUE nm_size(VALUE self);
@@ -90,8 +87,6 @@ extern "C" {
90
87
 
91
88
  } // end extern "C" block
92
89
 
93
-
94
-
95
90
  namespace nm { namespace yale_storage {
96
91
 
97
92
  template <typename DType, typename IType>
@@ -149,7 +144,7 @@ YALE_STORAGE* create_from_old_yale(dtype_t dtype, size_t* shape, void* r_ia, voi
149
144
  }
150
145
 
151
146
  // Having walked through the matrix, we now go about allocating the space for it.
152
- YALE_STORAGE* s = alloc(dtype, shape, 2);
147
+ YALE_STORAGE* s = alloc(dtype, shape, 2, UINT8);
153
148
 
154
149
  s->capacity = shape[0] + ndnz + 1;
155
150
  s->ndnz = ndnz;
@@ -299,9 +294,7 @@ size_t max_size(YALE_STORAGE* s) {
299
294
  ///////////////
300
295
 
301
296
  /*
302
- * Returns a slice of YALE_STORAGE object by coppy
303
- *
304
- * Slicing-related.
297
+ * Returns a slice of YALE_STORAGE object by copy
305
298
  */
306
299
  template <typename DType,typename IType>
307
300
  void* get(YALE_STORAGE* storage, SLICE* slice) {
@@ -312,10 +305,10 @@ void* get(YALE_STORAGE* storage, SLICE* slice) {
312
305
  shape[0] = slice->lengths[0];
313
306
  shape[1] = slice->lengths[1];
314
307
 
315
- IType *src_ija = reinterpret_cast<IType*>(storage->ija);
316
- DType *src_a = reinterpret_cast<DType*>(storage->a);
308
+ IType* src_ija = reinterpret_cast<IType*>(storage->ija);
309
+ DType* src_a = reinterpret_cast<DType*>(storage->a);
317
310
 
318
- // Calc ndnz
311
+ // Calc ndnz for the destination
319
312
  size_t ndnz = 0;
320
313
  size_t i,j; // indexes of destination matrix
321
314
  size_t k,l; // indexes of source matrix
@@ -326,54 +319,71 @@ void* get(YALE_STORAGE* storage, SLICE* slice) {
326
319
 
327
320
  if (j == i) continue;
328
321
 
329
- if (k == l && src_a[k] != 0) ndnz++; // for diagonal element of source
330
- else { // for non-diagonal element
331
- for (size_t c = src_ija[k]; c < src_ija[k+1]; c++)
332
- if (src_ija[c] == l) { ndnz++; break; }
322
+ if (k == l) { // for diagonal element of source
323
+ if (src_a[k] != 0) ++ndnz;
324
+ } else { // for non-diagonal element
325
+ for (size_t c = src_ija[k]; c < src_ija[k+1]; c++) {
326
+ if (src_ija[c] == l) {
327
+ ++ndnz;
328
+ break;
329
+ }
330
+ }
333
331
  }
334
332
 
335
333
  }
336
334
  }
337
335
 
338
336
  size_t request_capacity = shape[0] + ndnz + 1;
339
- YALE_STORAGE* ns = nm_yale_storage_create(storage->dtype, shape, 2, request_capacity);
337
+ //fprintf(stderr, "yale get copy: shape0=%d, shape1=%d, ndnz=%d, request_capacity=%d\n", shape[0], shape[1], ndnz, request_capacity);
338
+ YALE_STORAGE* ns = nm_yale_storage_create(storage->dtype, shape, 2, request_capacity, storage->itype);
340
339
 
341
340
  if (ns->capacity < request_capacity)
342
- rb_raise(nm_eStorageTypeError, "conversion failed; capacity of %ld requested, max allowable is %ld", request_capacity, ns->capacity);
341
+ rb_raise(nm_eStorageTypeError, "conversion failed; capacity of %ld requested, max allowable is %ld", request_capacity, ns->capacity);
343
342
 
344
343
  // Initialize the A and IJA arrays
345
344
  init<DType,IType>(ns);
346
- IType *dst_ija = reinterpret_cast<IType*>(ns->ija);
347
- DType *dst_a = reinterpret_cast<DType*>(ns->a);
345
+ IType* dst_ija = reinterpret_cast<IType*>(ns->ija);
346
+ DType* dst_a = reinterpret_cast<DType*>(ns->a);
348
347
 
349
348
  size_t ija = shape[0] + 1;
350
- DType val;
349
+ DType val = src_a[storage->shape[0]]; // use 0 as the default for copy
351
350
  for (i = 0; i < shape[0]; ++i) {
352
351
  k = i + offset[0];
353
352
  for (j = 0; j < shape[1]; ++j) {
353
+ bool found = false;
354
354
  l = j + offset[1];
355
355
 
356
356
  // Get value from source matrix
357
- if (k == l) val = src_a[k];
358
- else {
359
- // copy non-diagonal element
360
- for (size_t c = src_ija[k]; c < src_ija[k+1]; ++c) {
361
- if (src_ija[c] == l) val = src_a[c];
357
+ if (k == l) { // source diagonal
358
+ if (src_a[k] != 0) { // don't bother copying non-zero values from the diagonal
359
+ val = src_a[k];
360
+ found = true;
361
+ }
362
+ } else {
363
+ // copy one non-diagonal element
364
+ for (size_t c = src_ija[k]; !found && c < src_ija[k+1]; ++c) {
365
+ if (src_ija[c] == l) {
366
+ val = src_a[c];
367
+ found = true;
368
+ }
362
369
  }
363
370
  }
364
371
 
365
- // Set value to destination matrix
366
- if (i == j) dst_a[i] = val;
367
- else {
368
- // copy non-diagonal element
369
- dst_ija[ija] = j;
370
- dst_a[ija] = val;
372
+ if (found) {
373
+ // Set value in destination matrix
374
+ if (i == j) {
375
+ dst_a[i] = val;
376
+ } else {
377
+ // copy non-diagonal element
378
+ dst_ija[ija] = j;
379
+ dst_a[ija] = val;
371
380
 
372
- ++ija;
373
- for (size_t c = i + 1; c <= shape[0]; ++c) {
374
- dst_ija[c] = ija;
381
+ ++ija;
382
+ for (size_t c = i + 1; c <= shape[0]; ++c) {
383
+ dst_ija[c] = ija;
384
+ }
375
385
  }
376
- }
386
+ }
377
387
  }
378
388
  }
379
389
 
@@ -643,7 +653,7 @@ YALE_STORAGE* ew_op(const YALE_STORAGE* left, const YALE_STORAGE* right, dtype_t
643
653
 
644
654
  init_capacity = std::min(left->ndnz + right->ndnz + new_shape[0], new_shape[0] * new_shape[1]);
645
655
 
646
- dest = nm_yale_storage_create(dtype, new_shape, 2, init_capacity);
656
+ dest = nm_yale_storage_create(dtype, new_shape, 2, init_capacity, left->itype);
647
657
  da = reinterpret_cast<DType*>(dest->a);
648
658
 
649
659
  // Calculate diagonal values.
@@ -838,7 +848,7 @@ YALE_STORAGE* ew_op(const YALE_STORAGE* left, const YALE_STORAGE* right, dtype_t
838
848
  // Set the number of non-diagonal non-zero entries in the destination matrix.
839
849
  dest->ndnz = da_index;
840
850
 
841
- printf("Number of non-diagonal non-zero entires: %ld\n\n", (unsigned long)(dest->ndnz));
851
+ printf("Number of non-diagonal non-zero entries: %ld\n\n", (unsigned long)(dest->ndnz));
842
852
 
843
853
  // Set the capacity of the destination matrix.
844
854
  dest->capacity = dest->shape[0] + dest->ndnz + 1;
@@ -940,6 +950,8 @@ static char vector_insert_resize(YALE_STORAGE* s, size_t current_size, size_t po
940
950
  s->ija = reinterpret_cast<void*>(new_ija);
941
951
  s->a = reinterpret_cast<void*>(new_a);
942
952
 
953
+ fprintf(stderr, "resize\n");
954
+
943
955
  return 'i';
944
956
  }
945
957
 
@@ -1122,7 +1134,8 @@ static STORAGE* matrix_multiply(const STORAGE_PAIR& casted_storage, size_t* resu
1122
1134
  // int8_t dtype = left->dtype;
1123
1135
 
1124
1136
  // Create result storage.
1125
- YALE_STORAGE* result = nm_yale_storage_create(left->dtype, resulting_shape, 2, left->capacity + right->capacity);
1137
+ nm::itype_t result_itype = static_cast<uint8_t>(left->itype) < static_cast<uint8_t>(right->itype) ? right->itype : left->itype;
1138
+ YALE_STORAGE* result = nm_yale_storage_create(left->dtype, resulting_shape, 2, left->capacity + right->capacity, result_itype);
1126
1139
  init<DType,IType>(result);
1127
1140
 
1128
1141
  IType* ijl = reinterpret_cast<IType*>(left->ija);
@@ -1144,7 +1157,106 @@ static STORAGE* matrix_multiply(const STORAGE_PAIR& casted_storage, size_t* resu
1144
1157
  return reinterpret_cast<STORAGE*>(result);
1145
1158
  }
1146
1159
 
1147
- }} // end of namespace nm::yale_storage.
1160
+
1161
+ } // end of namespace nm::yale_storage
1162
+
1163
+
1164
+ // Helper function used only for the RETURN_SIZED_ENUMERATOR macro. Returns the length of
1165
+ // the matrix's storage.
1166
+ static VALUE nm_yale_enumerator_length(VALUE nmatrix) {
1167
+ long len = nm_yale_storage_get_size(NM_STORAGE_YALE(nmatrix));
1168
+ return LONG2NUM(len);
1169
+ }
1170
+
1171
+
1172
+ template <typename DType, typename IType>
1173
+ struct yale_each_stored_with_indices_helper {
1174
+ static VALUE iterate(VALUE nm) {
1175
+
1176
+ YALE_STORAGE* s = NM_STORAGE_YALE(nm);
1177
+ DType* a = reinterpret_cast<DType*>(s->a);
1178
+ IType* ija = reinterpret_cast<IType*>(s->ija);
1179
+
1180
+ // If we don't have a block, return an enumerator.
1181
+ RETURN_SIZED_ENUMERATOR(nm, 0, 0, nm_yale_enumerator_length);
1182
+
1183
+ // Iterate along diagonal
1184
+ for (size_t k = 0; k < s->shape[0]; ++k) {
1185
+ VALUE ii = LONG2NUM(k),
1186
+ jj = LONG2NUM(k);
1187
+
1188
+ VALUE v = rubyobj_from_cval(&(a[k]), NM_DTYPE(nm)).rval;
1189
+ rb_yield_values(3, v, ii, jj );
1190
+ }
1191
+
1192
+ // Iterate through non-diagonal elements, row by row
1193
+ for (long i = 0; i < s->shape[0]; ++i) {
1194
+ long p = static_cast<long>( ija[i] ),
1195
+ next_p = static_cast<long>( ija[i+1] );
1196
+
1197
+ for (; p < next_p; ++p) {
1198
+ long j = static_cast<long>(ija[p]);
1199
+ VALUE ii = LONG2NUM(i),
1200
+ jj = LONG2NUM(j);
1201
+
1202
+ VALUE v = rubyobj_from_cval(&(a[p]), NM_DTYPE(nm)).rval;
1203
+ rb_yield_values(3, v, ii, jj );
1204
+ }
1205
+ }
1206
+
1207
+ return nm;
1208
+ }
1209
+ };
1210
+
1211
+
1212
+ template <typename IType>
1213
+ struct yale_each_stored_with_indices_helper<RubyObject, IType> {
1214
+ static VALUE iterate(VALUE nm) {
1215
+
1216
+ YALE_STORAGE* s = NM_STORAGE_YALE(nm);
1217
+ RubyObject* a = reinterpret_cast<RubyObject*>(s->a);
1218
+ IType* ija = reinterpret_cast<IType*>(s->ija);
1219
+
1220
+ // If we don't have a block, return an enumerator.
1221
+ RETURN_SIZED_ENUMERATOR(nm, 0, 0, nm_yale_enumerator_length);
1222
+
1223
+ // Iterate along diagonal
1224
+ for (size_t k = 0; k < s->shape[0]; ++k) {
1225
+ VALUE ii = LONG2NUM(k),
1226
+ jj = LONG2NUM(k);
1227
+ rb_yield_values(3, a[k].rval, ii, jj ); // yield element, i, j
1228
+ }
1229
+
1230
+ // Iterate through non-diagonal elements, row by row
1231
+ for (long i = 0; i < s->shape[0]; ++i) {
1232
+ long p = static_cast<long>( ija[i] ),
1233
+ next_p = static_cast<long>( ija[i+1] );
1234
+
1235
+ for (; p < next_p; ++p) {
1236
+ long j = static_cast<long>(ija[p]);
1237
+ VALUE ii = LONG2NUM(i),
1238
+ jj = LONG2NUM(j);
1239
+
1240
+ rb_yield_values(3, a[p].rval, ii, jj );
1241
+ }
1242
+ }
1243
+
1244
+ return nm;
1245
+ }
1246
+ };
1247
+
1248
+
1249
+ /*
1250
+ * This function and the two helper structs enable us to use partial template specialization.
1251
+ * See also: http://stackoverflow.com/questions/6623375/c-template-specialization-on-functions
1252
+ */
1253
+ template <typename DType, typename IType>
1254
+ static VALUE yale_each_stored_with_indices(VALUE nm) {
1255
+ return yale_each_stored_with_indices_helper<DType, IType>::iterate(nm);
1256
+ }
1257
+
1258
+
1259
+ } // end of namespace nm.
1148
1260
 
1149
1261
  ///////////////////
1150
1262
  // Ruby Bindings //
@@ -1155,6 +1267,10 @@ static STORAGE* matrix_multiply(const STORAGE_PAIR& casted_storage, size_t* resu
1155
1267
  extern "C" {
1156
1268
 
1157
1269
  void nm_init_yale_functions() {
1270
+ /*
1271
+ * This module stores methods that are useful for debugging Yale matrices,
1272
+ * i.e. the ones with +:yale+ stype.
1273
+ */
1158
1274
  cNMatrix_YaleFunctions = rb_define_module_under(cNMatrix, "YaleFunctions");
1159
1275
 
1160
1276
  rb_define_method(cNMatrix_YaleFunctions, "yale_ija", (METHOD)nm_ija, 0);
@@ -1172,6 +1288,18 @@ void nm_init_yale_functions() {
1172
1288
  // C ACCESSORS //
1173
1289
  /////////////////
1174
1290
 
1291
+
1292
+
1293
+ VALUE nm_yale_each_stored_with_indices(VALUE nmatrix) {
1294
+ nm::dtype_t d = NM_DTYPE(nmatrix);
1295
+ nm::itype_t i = NM_ITYPE(nmatrix);
1296
+
1297
+ NAMED_LI_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_each_stored_with_indices, VALUE, VALUE)
1298
+
1299
+ return ttable[d][i](nmatrix);
1300
+ }
1301
+
1302
+
1175
1303
  /*
1176
1304
  * C accessor for inserting some value in a matrix (or replacing an existing cell).
1177
1305
  */
@@ -1262,7 +1390,7 @@ STORAGE* nm_yale_storage_copy_transposed(const STORAGE* rhs_base) {
1262
1390
 
1263
1391
  size_t size = nm_yale_storage_get_size(rhs);
1264
1392
 
1265
- YALE_STORAGE* lhs = nm_yale_storage_create(rhs->dtype, shape, 2, size);
1393
+ YALE_STORAGE* lhs = nm_yale_storage_create(rhs->dtype, shape, 2, size, nm::UINT8);
1266
1394
  nm_yale_storage_init(lhs);
1267
1395
 
1268
1396
  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);
@@ -1362,7 +1490,7 @@ STORAGE* nm_yale_storage_ew_op(nm::ewop_t op, const STORAGE* left, const STORAGE
1362
1490
  * create the storage.
1363
1491
  */
1364
1492
 
1365
- YALE_STORAGE* nm_yale_storage_create(nm::dtype_t dtype, size_t* shape, size_t dim, size_t init_capacity) {
1493
+ 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) {
1366
1494
  YALE_STORAGE* s;
1367
1495
  size_t max_capacity;
1368
1496
 
@@ -1371,7 +1499,7 @@ YALE_STORAGE* nm_yale_storage_create(nm::dtype_t dtype, size_t* shape, size_t di
1371
1499
  rb_raise(rb_eNotImpError, "Can only support 2D matrices");
1372
1500
  }
1373
1501
 
1374
- s = alloc(dtype, shape, dim);
1502
+ s = alloc(dtype, shape, dim, min_itype);
1375
1503
  max_capacity = nm::yale_storage::max_size(s);
1376
1504
 
1377
1505
  // Set matrix capacity (and ensure its validity)
@@ -1434,7 +1562,7 @@ void nm_yale_storage_mark(void* storage_base) {
1434
1562
  /*
1435
1563
  * Allocates and initializes the basic struct (but not the IJA or A vectors).
1436
1564
  */
1437
- static YALE_STORAGE* alloc(nm::dtype_t dtype, size_t* shape, size_t dim) {
1565
+ static YALE_STORAGE* alloc(nm::dtype_t dtype, size_t* shape, size_t dim, nm::itype_t min_itype) {
1438
1566
  YALE_STORAGE* s;
1439
1567
 
1440
1568
  s = ALLOC( YALE_STORAGE );
@@ -1445,6 +1573,10 @@ static YALE_STORAGE* alloc(nm::dtype_t dtype, size_t* shape, size_t dim) {
1445
1573
  s->dim = dim;
1446
1574
  s->itype = nm_yale_storage_itype_by_shape(shape);
1447
1575
 
1576
+ // See if a higher itype has been requested.
1577
+ if (static_cast<int8_t>(s->itype) < static_cast<int8_t>(min_itype))
1578
+ s->itype = min_itype;
1579
+
1448
1580
  return s;
1449
1581
  }
1450
1582
 
@@ -1466,6 +1598,9 @@ YALE_STORAGE* nm_yale_storage_create_from_old_yale(nm::dtype_t dtype, size_t* sh
1466
1598
  //////////////////////////////////////////////
1467
1599
 
1468
1600
  /*
1601
+ * call-seq:
1602
+ * yale_size -> Integer
1603
+ *
1469
1604
  * Get the size of a Yale matrix (the number of elements actually stored).
1470
1605
  *
1471
1606
  * For capacity (the maximum number of elements that can be stored without a resize), use capacity instead.
@@ -1478,6 +1613,9 @@ static VALUE nm_size(VALUE self) {
1478
1613
 
1479
1614
 
1480
1615
  /*
1616
+ * call-seq:
1617
+ * yale_a -> Array
1618
+ *
1481
1619
  * Get the A array of a Yale matrix (which stores the diagonal and the LU portions of the matrix).
1482
1620
  */
1483
1621
  static VALUE nm_a(VALUE self) {
@@ -1499,6 +1637,9 @@ static VALUE nm_a(VALUE self) {
1499
1637
 
1500
1638
 
1501
1639
  /*
1640
+ * call-seq:
1641
+ * yale_d -> Array
1642
+ *
1502
1643
  * Get the diagonal ("D") portion of the A array of a Yale matrix.
1503
1644
  */
1504
1645
  static VALUE nm_d(VALUE self) {
@@ -1513,6 +1654,9 @@ static VALUE nm_d(VALUE self) {
1513
1654
  }
1514
1655
 
1515
1656
  /*
1657
+ * call-seq:
1658
+ * yale_lu -> Array
1659
+ *
1516
1660
  * Get the non-diagonal ("LU") portion of the A array of a Yale matrix.
1517
1661
  */
1518
1662
  static VALUE nm_lu(VALUE self) {
@@ -1535,6 +1679,9 @@ static VALUE nm_lu(VALUE self) {
1535
1679
  }
1536
1680
 
1537
1681
  /*
1682
+ * call-seq:
1683
+ * yale_ia -> Array
1684
+ *
1538
1685
  * Get the IA portion of the IJA array of a Yale matrix. This gives the start and end positions of rows in the
1539
1686
  * JA and LU portions of the IJA and A arrays, respectively.
1540
1687
  */
@@ -1551,6 +1698,9 @@ static VALUE nm_ia(VALUE self) {
1551
1698
  }
1552
1699
 
1553
1700
  /*
1701
+ * call-seq:
1702
+ * yale_ja -> Array
1703
+ *
1554
1704
  * Get the JA portion of the IJA array of a Yale matrix. This gives the column indices for entries in corresponding
1555
1705
  * positions in the LU portion of the A array.
1556
1706
  */
@@ -1574,6 +1724,9 @@ static VALUE nm_ja(VALUE self) {
1574
1724
  }
1575
1725
 
1576
1726
  /*
1727
+ * call-seq:
1728
+ * yale_ija -> Array
1729
+ *
1577
1730
  * Get the IJA array of a Yale matrix.
1578
1731
  */
1579
1732
  static VALUE nm_ija(VALUE self) {