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
@@ -40,13 +40,9 @@
40
40
  */
41
41
 
42
42
  #include "types.h"
43
-
44
43
  #include "data/data.h"
45
-
46
44
  #include "common.h"
47
-
48
45
  #include "util/sl_list.h"
49
-
50
46
  #include "nmatrix.h"
51
47
 
52
48
  /*
@@ -76,17 +72,18 @@ extern "C" {
76
72
  LIST_STORAGE* nm_list_storage_create(nm::dtype_t dtype, size_t* shape, size_t dim, void* init_val);
77
73
  void nm_list_storage_delete(STORAGE* s);
78
74
  void nm_list_storage_delete_ref(STORAGE* s);
79
- void nm_list_storage_mark(void*);
75
+ void nm_list_storage_mark(STORAGE*);
80
76
 
81
77
  ///////////////
82
78
  // Accessors //
83
79
  ///////////////
84
80
 
85
81
  VALUE nm_list_each_with_indices(VALUE nmatrix, bool stored);
86
- void* nm_list_storage_ref(STORAGE* s, SLICE* slice);
87
- void* nm_list_storage_get(STORAGE* s, SLICE* slice);
88
- void* nm_list_storage_insert(STORAGE* s, SLICE* slice, void* val);
89
- void* nm_list_storage_remove(STORAGE* s, SLICE* slice);
82
+ void* nm_list_storage_ref(const STORAGE* s, SLICE* slice);
83
+ void* nm_list_storage_get(const STORAGE* s, SLICE* slice);
84
+ NODE* nm_list_storage_insert(STORAGE* s, SLICE* slice, void* val);
85
+ void nm_list_storage_set(VALUE left, SLICE* slice, VALUE right);
86
+ void nm_list_storage_remove(STORAGE* s, SLICE* slice);
90
87
 
91
88
  ///////////
92
89
  // Tests //
@@ -55,12 +55,6 @@ const char* const STYPE_NAMES[nm::NUM_STYPES] = {
55
55
  "yale"
56
56
  };
57
57
 
58
- void (* const STYPE_MARK[nm::NUM_STYPES])(void*) = {
59
- nm_dense_storage_mark,
60
- nm_list_storage_mark,
61
- nm_yale_storage_mark
62
- };
63
-
64
58
  } // end extern "C" block
65
59
 
66
60
  /*
@@ -130,12 +124,12 @@ DENSE_STORAGE* create_from_list_storage(const LIST_STORAGE* rhs, dtype_t l_dtype
130
124
  /*
131
125
  * Create/allocate dense storage, copying into it the contents of a Yale matrix.
132
126
  */
133
- template <typename LDType, typename RDType, typename RIType>
127
+ template <typename LDType, typename RDType>
134
128
  DENSE_STORAGE* create_from_yale_storage(const YALE_STORAGE* rhs, dtype_t l_dtype) {
135
129
 
136
130
  // Position in rhs->elements.
137
- RIType* rhs_ija = reinterpret_cast<RIType*>(rhs->ija);
138
- RDType* rhs_a = reinterpret_cast<RDType*>(rhs->a);
131
+ IType* rhs_ija = reinterpret_cast<YALE_STORAGE*>(rhs->src)->ija;
132
+ RDType* rhs_a = reinterpret_cast<RDType*>(reinterpret_cast<YALE_STORAGE*>(rhs->src)->a);
139
133
 
140
134
  // Allocate and set shape.
141
135
  size_t* shape = ALLOC_N(size_t, rhs->dim);
@@ -152,7 +146,7 @@ DENSE_STORAGE* create_from_yale_storage(const YALE_STORAGE* rhs, dtype_t l_dtype
152
146
 
153
147
  // Walk through rows. For each entry we set in dense, increment pos.
154
148
  for (size_t i = 0; i < shape[0]; ++i) {
155
- RIType ri = i + rhs->offset[0];
149
+ IType ri = i + rhs->offset[0];
156
150
 
157
151
  if (rhs_ija[ri] == rhs_ija[ri+1]) { // Check boundaries of row: is row empty? (Yes.)
158
152
 
@@ -169,13 +163,13 @@ DENSE_STORAGE* create_from_yale_storage(const YALE_STORAGE* rhs, dtype_t l_dtype
169
163
  } else { // Row contains entries: write those in each column, interspersed with zeros.
170
164
 
171
165
  // Get the first ija position of the row (as sliced)
172
- RIType ija = nm::yale_storage::binary_search_left_boundary<RIType>(rhs, rhs_ija[ri], rhs_ija[ri+1]-1, rhs->offset[1]);
166
+ IType ija = nm::yale_storage::binary_search_left_boundary(rhs, rhs_ija[ri], rhs_ija[ri+1]-1, rhs->offset[1]);
173
167
 
174
168
  // What column is it?
175
- RIType next_stored_rj = rhs_ija[ija];
169
+ IType next_stored_rj = rhs_ija[ija];
176
170
 
177
171
  for (size_t j = 0; j < shape[1]; ++j) {
178
- RIType rj = j + rhs->offset[1];
172
+ IType rj = j + rhs->offset[1];
179
173
 
180
174
  if (rj == ri) { // at a diagonal in RHS
181
175
  lhs_elements[pos] = static_cast<LDType>(rhs_a[ri]);
@@ -317,13 +311,13 @@ LIST_STORAGE* create_from_dense_storage(const DENSE_STORAGE* rhs, dtype_t l_dtyp
317
311
  /*
318
312
  * Creation of list storage from yale storage.
319
313
  */
320
- template <typename LDType, typename RDType, typename RIType>
314
+ template <typename LDType, typename RDType>
321
315
  LIST_STORAGE* create_from_yale_storage(const YALE_STORAGE* rhs, dtype_t l_dtype) {
322
316
  // allocate and copy shape
323
317
  size_t *shape = ALLOC_N(size_t, rhs->dim);
324
318
  shape[0] = rhs->shape[0]; shape[1] = rhs->shape[1];
325
319
 
326
- RDType* rhs_a = reinterpret_cast<RDType*>(rhs->a);
320
+ RDType* rhs_a = reinterpret_cast<RDType*>(reinterpret_cast<YALE_STORAGE*>(rhs->src)->a);
327
321
  RDType R_ZERO = rhs_a[ rhs->src->shape[0] ];
328
322
 
329
323
  // copy default value from the zero location in the Yale matrix
@@ -334,25 +328,25 @@ LIST_STORAGE* create_from_yale_storage(const YALE_STORAGE* rhs, dtype_t l_dtype)
334
328
 
335
329
  if (rhs->dim != 2) rb_raise(nm_eStorageTypeError, "Can only convert matrices of dim 2 from yale.");
336
330
 
337
- RIType* rhs_ija = reinterpret_cast<RIType*>(rhs->ija);
331
+ IType* rhs_ija = reinterpret_cast<YALE_STORAGE*>(rhs->src)->ija;
338
332
 
339
333
  NODE *last_row_added = NULL;
340
334
  // Walk through rows and columns as if RHS were a dense matrix
341
- for (RIType i = 0; i < shape[0]; ++i) {
342
- RIType ri = i + rhs->offset[0];
335
+ for (IType i = 0; i < shape[0]; ++i) {
336
+ IType ri = i + rhs->offset[0];
343
337
 
344
338
  NODE *last_added = NULL;
345
339
 
346
340
  // Get boundaries of beginning and end of row
347
- RIType ija = rhs_ija[ri],
348
- ija_next = rhs_ija[ri+1];
341
+ IType ija = rhs_ija[ri],
342
+ ija_next = rhs_ija[ri+1];
349
343
 
350
344
  // Are we going to need to add a diagonal for this row?
351
345
  bool add_diag = false;
352
346
  if (rhs_a[ri] != R_ZERO) add_diag = true; // non-zero and located within the bounds of the slice
353
347
 
354
348
  if (ija < ija_next || add_diag) {
355
- ija = nm::yale_storage::binary_search_left_boundary<RIType>(rhs, ija, ija_next-1, rhs->offset[1]);
349
+ ija = nm::yale_storage::binary_search_left_boundary(rhs, ija, ija_next-1, rhs->offset[1]);
356
350
 
357
351
  LIST* curr_row = list::create();
358
352
 
@@ -360,8 +354,8 @@ LIST_STORAGE* create_from_yale_storage(const YALE_STORAGE* rhs, dtype_t l_dtype)
360
354
 
361
355
  while (ija < ija_next) {
362
356
  // Find first column in slice
363
- RIType rj = rhs_ija[ija];
364
- RIType j = rj - rhs->offset[1];
357
+ IType rj = rhs_ija[ija];
358
+ IType j = rj - rhs->offset[1];
365
359
 
366
360
  // Is there a nonzero diagonal item between the previously added item and the current one?
367
361
  if (rj > ri && add_diag) {
@@ -472,19 +466,19 @@ namespace yale_storage { // FIXME: Move to yale.cpp
472
466
  /*
473
467
  * Creation of yale storage from dense storage.
474
468
  */
475
- template <typename LDType, typename RDType, typename LIType>
469
+ template <typename LDType, typename RDType>
476
470
  YALE_STORAGE* create_from_dense_storage(const DENSE_STORAGE* rhs, dtype_t l_dtype, void* init) {
477
471
 
478
472
  if (rhs->dim != 2) rb_raise(nm_eStorageTypeError, "can only convert matrices of dim 2 to yale");
479
473
 
480
- LIType pos = 0;
481
- LIType ndnz = 0;
474
+ IType pos = 0;
475
+ IType ndnz = 0;
482
476
 
483
477
  // We need a zero value. This should nearly always be zero, but sometimes you might want false or nil.
484
478
  LDType L_INIT(0);
485
479
  if (init) {
486
480
  if (l_dtype == RUBYOBJ) L_INIT = *reinterpret_cast<VALUE*>(init);
487
- else L_INIT = rubyobj_from_cval(init, rhs->dtype);
481
+ else L_INIT = *reinterpret_cast<LDType*>(init);
488
482
  }
489
483
  RDType R_INIT = static_cast<RDType>(L_INIT);
490
484
 
@@ -508,27 +502,27 @@ namespace yale_storage { // FIXME: Move to yale.cpp
508
502
  size_t request_capacity = shape[0] + ndnz + 1;
509
503
 
510
504
  // Create with minimum possible capacity -- just enough to hold all of the entries
511
- YALE_STORAGE* lhs = nm_yale_storage_create(l_dtype, shape, 2, request_capacity, UINT8);
505
+ YALE_STORAGE* lhs = nm_yale_storage_create(l_dtype, shape, 2, request_capacity);
512
506
 
513
507
  if (lhs->capacity < request_capacity)
514
508
  rb_raise(nm_eStorageTypeError, "conversion failed; capacity of %ld requested, max allowable is %ld", (unsigned long)request_capacity, (unsigned long)(lhs->capacity));
515
509
 
516
510
  LDType* lhs_a = reinterpret_cast<LDType*>(lhs->a);
517
- LIType* lhs_ija = reinterpret_cast<LIType*>(lhs->ija);
511
+ IType* lhs_ija = lhs->ija;
518
512
 
519
513
  // Set the zero position in the yale matrix
520
514
  lhs_a[shape[0]] = L_INIT;
521
515
 
522
516
  // Start just after the zero position.
523
- LIType ija = shape[0]+1;
524
- pos = 0;
517
+ IType ija = shape[0]+1;
518
+ pos = 0;
525
519
 
526
520
  // Copy contents
527
- for (LIType i = 0; i < rhs->shape[0]; ++i) {
521
+ for (IType i = 0; i < rhs->shape[0]; ++i) {
528
522
  // indicate the beginning of a row in the IJA array
529
523
  lhs_ija[i] = ija;
530
524
 
531
- for (LIType j = 0; j < rhs->shape[1]; ++j) {
525
+ for (IType j = 0; j < rhs->shape[1]; ++j) {
532
526
  pos = rhs->stride[0] * (i + rhs->offset[0]) + rhs->stride[1] * (j + rhs->offset[1]); // calc position with offsets
533
527
 
534
528
  if (i == j) { // copy to diagonal
@@ -551,7 +545,7 @@ namespace yale_storage { // FIXME: Move to yale.cpp
551
545
  /*
552
546
  * Creation of yale storage from list storage.
553
547
  */
554
- template <typename LDType, typename RDType, typename LIType>
548
+ template <typename LDType, typename RDType>
555
549
  YALE_STORAGE* create_from_list_storage(const LIST_STORAGE* rhs, nm::dtype_t l_dtype) {
556
550
  if (rhs->dim != 2) rb_raise(nm_eStorageTypeError, "can only convert matrices of dim 2 to yale");
557
551
 
@@ -570,18 +564,18 @@ namespace yale_storage { // FIXME: Move to yale.cpp
570
564
  shape[1] = rhs->shape[1];
571
565
 
572
566
  size_t request_capacity = shape[0] + ndnz + 1;
573
- YALE_STORAGE* lhs = nm_yale_storage_create(l_dtype, shape, 2, request_capacity, UINT8);
567
+ YALE_STORAGE* lhs = nm_yale_storage_create(l_dtype, shape, 2, request_capacity);
574
568
 
575
569
  if (lhs->capacity < request_capacity)
576
570
  rb_raise(nm_eStorageTypeError, "conversion failed; capacity of %ld requested, max allowable is %ld", (unsigned long)request_capacity, (unsigned long)(lhs->capacity));
577
571
 
578
572
  // Initialize the A and IJA arrays
579
- init<LDType,LIType>(lhs, rhs->default_val);
573
+ init<LDType>(lhs, rhs->default_val);
580
574
 
581
- LIType* lhs_ija = reinterpret_cast<LIType*>(lhs->ija);
575
+ IType* lhs_ija = lhs->ija;
582
576
  LDType* lhs_a = reinterpret_cast<LDType*>(lhs->a);
583
577
 
584
- LIType ija = lhs->shape[0]+1;
578
+ IType ija = lhs->shape[0]+1;
585
579
 
586
580
  // Copy contents
587
581
  for (NODE* i_curr = rhs->rows->first; i_curr; i_curr = i_curr->next) {
@@ -634,29 +628,25 @@ extern "C" {
634
628
 
635
629
 
636
630
  STORAGE* nm_yale_storage_from_dense(const STORAGE* right, nm::dtype_t l_dtype, void* init) {
637
- NAMED_LRI_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::create_from_dense_storage, YALE_STORAGE*, const DENSE_STORAGE* rhs, nm::dtype_t l_dtype, void*);
638
-
639
- nm::itype_t itype = nm_yale_storage_default_itype((const YALE_STORAGE*)right);
631
+ NAMED_LR_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::create_from_dense_storage, YALE_STORAGE*, const DENSE_STORAGE* rhs, nm::dtype_t l_dtype, void*);
640
632
 
641
- if (!ttable[l_dtype][right->dtype][itype]) {
633
+ if (!ttable[l_dtype][right->dtype]) {
642
634
  rb_raise(nm_eDataTypeError, "casting between these dtypes is undefined");
643
635
  return NULL;
644
636
  }
645
637
 
646
- return (STORAGE*)ttable[l_dtype][right->dtype][itype]((const DENSE_STORAGE*)right, l_dtype, init);
638
+ return (STORAGE*)ttable[l_dtype][right->dtype]((const DENSE_STORAGE*)right, l_dtype, init);
647
639
  }
648
640
 
649
641
  STORAGE* nm_yale_storage_from_list(const STORAGE* right, nm::dtype_t l_dtype, void* dummy) {
650
- NAMED_LRI_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::create_from_list_storage, YALE_STORAGE*, const LIST_STORAGE* rhs, nm::dtype_t l_dtype);
642
+ NAMED_LR_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::create_from_list_storage, YALE_STORAGE*, const LIST_STORAGE* rhs, nm::dtype_t l_dtype);
651
643
 
652
- nm::itype_t itype = nm_yale_storage_default_itype((const YALE_STORAGE*)right);
653
-
654
- if (!ttable[l_dtype][right->dtype][itype]) {
644
+ if (!ttable[l_dtype][right->dtype]) {
655
645
  rb_raise(nm_eDataTypeError, "casting between these dtypes is undefined");
656
646
  return NULL;
657
647
  }
658
648
 
659
- return (STORAGE*)ttable[l_dtype][right->dtype][itype]((const LIST_STORAGE*)right, l_dtype);
649
+ return (STORAGE*)ttable[l_dtype][right->dtype]((const LIST_STORAGE*)right, l_dtype);
660
650
  }
661
651
 
662
652
  STORAGE* nm_dense_storage_from_list(const STORAGE* right, nm::dtype_t l_dtype, void* dummy) {
@@ -671,16 +661,16 @@ extern "C" {
671
661
  }
672
662
 
673
663
  STORAGE* nm_dense_storage_from_yale(const STORAGE* right, nm::dtype_t l_dtype, void* dummy) {
674
- NAMED_LRI_DTYPE_TEMPLATE_TABLE(ttable, nm::dense_storage::create_from_yale_storage, DENSE_STORAGE*, const YALE_STORAGE* rhs, nm::dtype_t l_dtype);
664
+ NAMED_LR_DTYPE_TEMPLATE_TABLE(ttable, nm::dense_storage::create_from_yale_storage, DENSE_STORAGE*, const YALE_STORAGE* rhs, nm::dtype_t l_dtype);
675
665
 
676
666
  const YALE_STORAGE* casted_right = reinterpret_cast<const YALE_STORAGE*>(right);
677
667
 
678
- if (!ttable[l_dtype][right->dtype][casted_right->itype]) {
668
+ if (!ttable[l_dtype][right->dtype]) {
679
669
  rb_raise(nm_eDataTypeError, "casting between these dtypes is undefined");
680
670
  return NULL;
681
671
  }
682
672
 
683
- return reinterpret_cast<STORAGE*>(ttable[l_dtype][right->dtype][casted_right->itype](casted_right, l_dtype));
673
+ return reinterpret_cast<STORAGE*>(ttable[l_dtype][right->dtype](casted_right, l_dtype));
684
674
  }
685
675
 
686
676
  STORAGE* nm_list_storage_from_dense(const STORAGE* right, nm::dtype_t l_dtype, void* init) {
@@ -695,16 +685,16 @@ extern "C" {
695
685
  }
696
686
 
697
687
  STORAGE* nm_list_storage_from_yale(const STORAGE* right, nm::dtype_t l_dtype, void* dummy) {
698
- NAMED_LRI_DTYPE_TEMPLATE_TABLE(ttable, nm::list_storage::create_from_yale_storage, LIST_STORAGE*, const YALE_STORAGE* rhs, nm::dtype_t l_dtype);
688
+ NAMED_LR_DTYPE_TEMPLATE_TABLE(ttable, nm::list_storage::create_from_yale_storage, LIST_STORAGE*, const YALE_STORAGE* rhs, nm::dtype_t l_dtype);
699
689
 
700
690
  const YALE_STORAGE* casted_right = reinterpret_cast<const YALE_STORAGE*>(right);
701
691
 
702
- if (!ttable[l_dtype][right->dtype][casted_right->itype]) {
692
+ if (!ttable[l_dtype][right->dtype]) {
703
693
  rb_raise(nm_eDataTypeError, "casting between these dtypes is undefined");
704
694
  return NULL;
705
695
  }
706
696
 
707
- return (STORAGE*)ttable[l_dtype][right->dtype][casted_right->itype](casted_right, l_dtype);
697
+ return (STORAGE*)ttable[l_dtype][right->dtype](casted_right, l_dtype);
708
698
  }
709
699
 
710
700
  } // end of extern "C"
@@ -47,7 +47,7 @@
47
47
  #include "common.h"
48
48
  #include "dense.h"
49
49
  #include "list.h"
50
- #include "yale.h"
50
+ #include "yale/yale.h"
51
51
 
52
52
  /*
53
53
  * Macros
@@ -76,7 +76,7 @@ namespace nm {
76
76
  extern "C" {
77
77
 
78
78
  extern const char* const STYPE_NAMES[nm::NUM_STYPES];
79
- extern void (* const STYPE_MARK[nm::NUM_STYPES])(void*);
79
+ extern void (* const STYPE_MARK[nm::NUM_STYPES])(STORAGE*);
80
80
 
81
81
  /*
82
82
  * Functions
@@ -0,0 +1,1070 @@
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
+ // == class.h
25
+ //
26
+ // Object-oriented interface for Yale.
27
+ //
28
+
29
+ #ifndef YALE_CLASS_H
30
+ # define YALE_CLASS_H
31
+
32
+ #include "../dense.h"
33
+ #include "math/transpose.h"
34
+
35
+ namespace nm {
36
+
37
+
38
+ /*
39
+ * This class is basically an intermediary for YALE_STORAGE objects which enables us to treat it like a C++ object. It
40
+ * keeps the src pointer as its s, along with other relevant slice information.
41
+ *
42
+ * It's useful for creating iterators and such. It isn't responsible for allocating or freeing its YALE_STORAGE* pointers.
43
+ */
44
+
45
+ template <typename D>
46
+ class YaleStorage {
47
+ public:
48
+ YaleStorage(const YALE_STORAGE* storage)
49
+ : s(reinterpret_cast<YALE_STORAGE*>(storage->src)),
50
+ slice(storage != storage->src),
51
+ slice_shape(storage->shape),
52
+ slice_offset(storage->offset)
53
+ { }
54
+
55
+ YaleStorage(const STORAGE* storage)
56
+ : s(reinterpret_cast<YALE_STORAGE*>(storage->src)),
57
+ slice(storage != storage->src),
58
+ slice_shape(storage->shape),
59
+ slice_offset(storage->offset)
60
+ { }
61
+
62
+ /* Allows us to do YaleStorage<uint8>::dtype() to get an nm::dtype_t */
63
+ static nm::dtype_t dtype() {
64
+ return nm::ctype_to_dtype_enum<D>::value_type;
65
+ }
66
+
67
+
68
+ bool is_ref() const { return slice; }
69
+
70
+ inline D* default_obj_ptr() { return &(a(s->shape[0])); }
71
+ inline D& default_obj() { return a(s->shape[0]); }
72
+ inline const D& default_obj() const { return a(s->shape[0]); }
73
+ inline const D& const_default_obj() const { return a(s->shape[0]); }
74
+
75
+ /*
76
+ * Return a Ruby VALUE representation of default_obj()
77
+ */
78
+ VALUE const_default_value() const {
79
+ return nm::yale_storage::nm_rb_dereference(a(s->shape[0]));
80
+ }
81
+
82
+ inline size_t* ija_p() const { return reinterpret_cast<size_t*>(s->ija); }
83
+ inline const size_t& ija(size_t p) const { return ija_p()[p]; }
84
+ inline size_t& ija(size_t p) { return ija_p()[p]; }
85
+ inline D* a_p() const { return reinterpret_cast<D*>(s->a); }
86
+ inline const D& a(size_t p) const { return a_p()[p]; }
87
+ inline D& a(size_t p) { return a_p()[p]; }
88
+
89
+ bool real_row_empty(size_t i) const { return ija(i+1) - ija(i) == 0 ? true : false; }
90
+
91
+ inline size_t* shape_p() const { return slice_shape; }
92
+ inline size_t shape(uint8_t d) const { return slice_shape[d]; }
93
+ inline size_t* real_shape_p() const { return s->shape; }
94
+ inline size_t real_shape(uint8_t d) const { return s->shape[d]; }
95
+ inline size_t* offset_p() const { return slice_offset; }
96
+ inline size_t offset(uint8_t d) const { return slice_offset[d]; }
97
+ inline size_t capacity() const { return s->capacity; }
98
+ inline size_t size() const { return ija(real_shape(0)); }
99
+
100
+
101
+ /*
102
+ * Given a size-2 array of size_t, representing the shape, determine
103
+ * the maximum size of YaleStorage arrays.
104
+ */
105
+ static size_t max_size(const size_t* shape) {
106
+ size_t result = shape[0] * shape[1] + 1;
107
+ if (shape[0] > shape[1])
108
+ result += shape[0] - shape[1];
109
+ return result;
110
+ }
111
+
112
+ /*
113
+ * Minimum size of Yale Storage arrays given some shape.
114
+ */
115
+ static size_t min_size(const size_t* shape) {
116
+ return shape[0]*2 + 1;
117
+ }
118
+
119
+ /*
120
+ * This is the guaranteed maximum size of the IJA/A arrays of the matrix given its shape.
121
+ */
122
+ inline size_t real_max_size() const {
123
+ return YaleStorage<D>::max_size(real_shape_p());
124
+ }
125
+
126
+ // Binary search between left and right in IJA for column ID real_j. Returns left if not found.
127
+ size_t real_find_pos(size_t left, size_t right, size_t real_j, bool& found) const {
128
+ if (left > right) {
129
+ found = false;
130
+ return left;
131
+ }
132
+
133
+ size_t mid = (left + right) / 2;
134
+ size_t mid_j = ija(mid);
135
+
136
+ if (mid_j == real_j) {
137
+ found = true;
138
+ return mid;
139
+ } else if (mid_j > real_j) return real_find_pos(left, mid - 1, real_j, found);
140
+ else return real_find_pos(mid + 1, right, real_j, found);
141
+ }
142
+
143
+ // Binary search between left and right in IJA for column ID real_j. Essentially finds where the slice should begin,
144
+ // with no guarantee that there's anything in there.
145
+ size_t real_find_left_boundary_pos(size_t left, size_t right, size_t real_j) const {
146
+ if (left > right) return right;
147
+ if (ija(left) >= real_j) return left;
148
+
149
+ size_t mid = (left + right) / 2;
150
+ size_t mid_j = ija(mid);
151
+
152
+ if (mid_j == real_j) return mid;
153
+ else if (mid_j > real_j) return real_find_left_boundary_pos(left, mid, real_j);
154
+ else return real_find_left_boundary_pos(mid + 1, right, real_j);
155
+ }
156
+
157
+ // Binary search between left and right in IJA for column ID real_j. Essentially finds where the slice should begin,
158
+ // with no guarantee that there's anything in there.
159
+ size_t real_find_right_boundary_pos(size_t left, size_t right, size_t real_j) const {
160
+ if (left > right) return right;
161
+ if (ija(right) <= real_j) return right;
162
+
163
+ size_t mid = (left + right) / 2;
164
+ size_t mid_j = ija(mid);
165
+
166
+ if (mid_j == real_j) return mid;
167
+ else if (mid_j > real_j) return real_find_right_boundary_pos(left, mid, real_j);
168
+ else return real_find_right_boundary_pos(mid + 1, right, real_j);
169
+ }
170
+
171
+
172
+ // Binary search for coordinates i,j in the slice. If not found, return -1.
173
+ std::pair<size_t,bool> find_pos(const std::pair<size_t,size_t>& ij) const {
174
+ size_t left = ija(ij.first + offset(0));
175
+ size_t right = ija(ij.first + offset(0) + 1) - 1;
176
+
177
+ std::pair<size_t, bool> result;
178
+ result.first = real_find_pos(left, right, ij.second + offset(1), result.second);
179
+ return result;
180
+ }
181
+
182
+ // Binary search for coordinates i,j in the slice, and return the first position >= j in row i.
183
+ size_t find_pos_for_insertion(size_t i, size_t j) const {
184
+ size_t left = ija(i + offset(0));
185
+ size_t right = ija(i + offset(0) + 1) - 1;
186
+
187
+ // Check that the right search point is valid. rflbp will check to make sure the left is valid relative to left.
188
+ if (right > ija(real_shape(0))) {
189
+ right = ija(real_shape(0))-1;
190
+ }
191
+ size_t result = real_find_left_boundary_pos(left, right, j + offset(1));
192
+ return result;
193
+ }
194
+
195
+ typedef yale_storage::basic_iterator_T<D,D,YaleStorage<D> > basic_iterator;
196
+ typedef yale_storage::basic_iterator_T<D,const D,const YaleStorage<D> > const_basic_iterator;
197
+
198
+ typedef yale_storage::stored_diagonal_iterator_T<D,D,YaleStorage<D> > stored_diagonal_iterator;
199
+ typedef yale_storage::stored_diagonal_iterator_T<D,const D,const YaleStorage<D> > const_stored_diagonal_iterator;
200
+
201
+ typedef yale_storage::iterator_T<D,D,YaleStorage<D> > iterator;
202
+ typedef yale_storage::iterator_T<D,const D,const YaleStorage<D> > const_iterator;
203
+
204
+
205
+ friend class yale_storage::row_iterator_T<D,D,YaleStorage<D> >;
206
+ typedef yale_storage::row_iterator_T<D,D,YaleStorage<D> > row_iterator;
207
+ typedef yale_storage::row_iterator_T<D,const D,const YaleStorage<D> > const_row_iterator;
208
+
209
+ typedef yale_storage::row_stored_iterator_T<D,D,YaleStorage<D>,row_iterator> row_stored_iterator;
210
+ typedef yale_storage::row_stored_nd_iterator_T<D,D,YaleStorage<D>,row_iterator> row_stored_nd_iterator;
211
+ typedef yale_storage::row_stored_iterator_T<D,const D,const YaleStorage<D>,const_row_iterator> const_row_stored_iterator;
212
+ typedef yale_storage::row_stored_nd_iterator_T<D,const D,const YaleStorage<D>,const_row_iterator> const_row_stored_nd_iterator;
213
+ typedef std::pair<row_iterator,row_stored_nd_iterator> row_nd_iter_pair;
214
+
215
+ // Variety of iterator begin and end functions.
216
+ iterator begin(size_t row = 0) { return iterator(*this, row); }
217
+ iterator row_end(size_t row) { return begin(row+1); }
218
+ iterator end() { return iterator(*this, shape(0)); }
219
+ const_iterator cbegin(size_t row = 0) const { return const_iterator(*this, row); }
220
+ const_iterator crow_end(size_t row) const { return cbegin(row+1); }
221
+ const_iterator cend() const { return const_iterator(*this, shape(0)); }
222
+
223
+ stored_diagonal_iterator sdbegin(size_t d = 0) { return stored_diagonal_iterator(*this, d); }
224
+ stored_diagonal_iterator sdend() {
225
+ return stored_diagonal_iterator(*this, std::min( shape(0) + offset(0), shape(1) + offset(1) ) - std::max(offset(0), offset(1)) );
226
+ }
227
+ const_stored_diagonal_iterator csdbegin(size_t d = 0) const { return const_stored_diagonal_iterator(*this, d); }
228
+ const_stored_diagonal_iterator csdend() const {
229
+ return const_stored_diagonal_iterator(*this, std::min( shape(0) + offset(0), shape(1) + offset(1) ) - std::max(offset(0), offset(1)) );
230
+ }
231
+ row_iterator ribegin(size_t row = 0) { return row_iterator(*this, row); }
232
+ row_iterator riend() { return row_iterator(*this, shape(0)); }
233
+ const_row_iterator cribegin(size_t row = 0) const { return const_row_iterator(*this, row); }
234
+ const_row_iterator criend() const { return const_row_iterator(*this, shape(0)); }
235
+
236
+
237
+ /*
238
+ * Get a count of the ndnz in the slice as if it were its own matrix.
239
+ */
240
+ size_t count_copy_ndnz() const {
241
+ if (!slice) return s->ndnz; // easy way -- not a slice.
242
+ size_t count = 0;
243
+
244
+ // Visit all stored entries.
245
+ for (const_row_iterator it = cribegin(); it != criend(); ++it){
246
+ for (auto jt = it.begin(); jt != it.end(); ++jt) {
247
+ if (it.i() != jt.j() && *jt != const_default_obj()) ++count;
248
+ }
249
+ }
250
+
251
+ return count;
252
+ }
253
+
254
+ /*
255
+ * Returns the iterator for i,j or snd_end() if not found.
256
+ */
257
+ /* stored_nondiagonal_iterator find(const std::pair<size_t,size_t>& ij) {
258
+ std::pair<size_t,bool> find_pos_result = find_pos(ij);
259
+ if (!find_pos_result.second) return sndend();
260
+ else return stored_nondiagonal_iterator(*this, ij.first, find_pos_result.first);
261
+ } */
262
+
263
+ /*
264
+ * Returns a stored_nondiagonal_iterator pointing to the location where some coords i,j should go, or returns their
265
+ * location if present.
266
+ */
267
+ /*std::pair<row_iterator, row_stored_nd_iterator> lower_bound(const std::pair<size_t,size_t>& ij) {
268
+ row_iterator it = ribegin(ij.first);
269
+ row_stored_nd_iterator jt = it.lower_bound(ij.second);
270
+ return std::make_pair(it,jt);
271
+ } */
272
+
273
+ class multi_row_insertion_plan {
274
+ public:
275
+ std::vector<size_t> pos;
276
+ std::vector<int> change;
277
+ int total_change; // the net change occurring
278
+ size_t num_changes; // the total number of rows that need to change size
279
+ multi_row_insertion_plan(size_t rows_in_slice) : pos(rows_in_slice), change(rows_in_slice), total_change(0), num_changes(0) { }
280
+
281
+ void add(size_t i, const std::pair<int,size_t>& change_and_pos) {
282
+ pos[i] = change_and_pos.second;
283
+ change[i] = change_and_pos.first;
284
+ total_change += change_and_pos.first;
285
+ if (change_and_pos.first != 0) num_changes++;
286
+ }
287
+ };
288
+
289
+
290
+ /*
291
+ * Find all the information we need in order to modify multiple rows.
292
+ */
293
+ multi_row_insertion_plan insertion_plan(row_iterator i, size_t j, size_t* lengths, D* const v, size_t v_size) const {
294
+ multi_row_insertion_plan p(lengths[0]);
295
+
296
+ // v_offset is our offset in the array v. If the user wants to change two elements in each of three rows,
297
+ // but passes an array of size 3, we need to know that the second insertion plan must start at position
298
+ // 2 instead of 0; and then the third must start at 1.
299
+ size_t v_offset = 0;
300
+ for (size_t m = 0; m < lengths[0]; ++m, ++i) {
301
+ p.add(m, i.single_row_insertion_plan(j, lengths[1], v, v_size, v_offset));
302
+ }
303
+
304
+ return p;
305
+ }
306
+
307
+
308
+
309
+ /*
310
+ * Insert entries in multiple rows. Slice-setting.
311
+ */
312
+ void insert(row_iterator i, size_t j, size_t* lengths, D* const v, size_t v_size) {
313
+ // Expensive pre-processing step: find all the information we need in order to do insertions.
314
+ multi_row_insertion_plan p = insertion_plan(i, j, lengths, v, v_size);
315
+
316
+ // There are more efficient ways to do this, but this is the low hanging fruit version of the algorithm.
317
+ // Here's the full problem: http://stackoverflow.com/questions/18753375/algorithm-for-merging-short-lists-into-a-long-vector
318
+ // --JW
319
+
320
+ bool resize = false;
321
+ size_t sz = size();
322
+ if (p.num_changes > 1) resize = true; // TODO: There are surely better ways to do this, but I've gone for the low-hanging fruit
323
+ else if (sz + p.total_change > capacity() || sz + p.total_change <= capacity() / nm::yale_storage::GROWTH_CONSTANT) resize = true;
324
+
325
+ if (resize) {
326
+ update_resize_move_insert(i.i() + offset(0), j + offset(1), lengths, v, v_size, p);
327
+ } else {
328
+
329
+ // Make the necessary modifications, which hopefully can be done in-place.
330
+ size_t v_offset = 0;
331
+ int accum = 0;
332
+ for (size_t ii = 0; ii < lengths[0]; ++ii, ++i) {
333
+ i.insert(row_stored_nd_iterator(i, p.pos[ii]), j, lengths[1], v, v_size, v_offset);
334
+ }
335
+ }
336
+ }
337
+
338
+
339
+ /*
340
+ * Most Ruby-centric insert function. Accepts coordinate information in slice,
341
+ * and value information of various types in +right+. This function must evaluate
342
+ * +right+ and determine what other functions to call in order to properly handle
343
+ * it.
344
+ */
345
+ void insert(SLICE* slice, VALUE right) {
346
+
347
+ std::pair<NMATRIX*,bool> nm_and_free =
348
+ interpret_arg_as_dense_nmatrix(right, dtype());
349
+ // Map the data onto D* v
350
+
351
+ D* v;
352
+ size_t v_size = 1;
353
+
354
+ if (nm_and_free.first) {
355
+ DENSE_STORAGE* s = reinterpret_cast<DENSE_STORAGE*>(nm_and_free.first->storage);
356
+ v = reinterpret_cast<D*>(s->elements);
357
+ v_size = nm_storage_count_max_elements(s);
358
+
359
+ } else if (TYPE(right) == T_ARRAY) {
360
+ v_size = RARRAY_LEN(right);
361
+ v = ALLOC_N(D, v_size);
362
+ for (size_t m = 0; m < v_size; ++m) {
363
+ rubyval_to_cval(rb_ary_entry(right, m), s->dtype, &(v[m]));
364
+ }
365
+ } else {
366
+ v = reinterpret_cast<D*>(rubyobj_to_cval(right, dtype()));
367
+ }
368
+
369
+ row_iterator i = ribegin(slice->coords[0]);
370
+
371
+ if (slice->single || (slice->lengths[0] == 1 && slice->lengths[1] == 1)) { // single entry
372
+ i.insert(slice->coords[1], *v);
373
+ } else if (slice->lengths[0] == 1) { // single row, multiple entries
374
+ i.insert(slice->coords[1], slice->lengths[1], v, v_size);
375
+ } else { // multiple rows, unknown number of entries
376
+ insert(i, slice->coords[1], slice->lengths, v, v_size);
377
+ }
378
+
379
+ // Only free v if it was allocated in this function.
380
+ if (nm_and_free.first) {
381
+ if (nm_and_free.second) {
382
+ nm_delete(nm_and_free.first);
383
+ }
384
+ } else xfree(v);
385
+ }
386
+
387
+
388
+ /*
389
+ * Remove an entry from an already found non-diagonal position.
390
+ */
391
+ row_iterator erase(row_iterator it, const row_stored_nd_iterator& position) {
392
+ it.erase(position);
393
+ return it;
394
+ }
395
+
396
+
397
+ /*
398
+ * Remove an entry from the matrix at the already-located position. If diagonal, just sets to default; otherwise,
399
+ * actually removes the entry.
400
+ */
401
+ row_iterator erase(row_iterator it, const row_stored_iterator& jt) {
402
+ it.erase((const row_stored_nd_iterator&)jt);
403
+ return it;
404
+ }
405
+
406
+
407
+ row_iterator insert(row_iterator it, row_stored_iterator position, size_t j, const D& val) {
408
+ it.insert(position, j, val);
409
+ return it;
410
+ }
411
+
412
+
413
+ /*
414
+ * Insert an element in column j, using position's p() as the location to insert the new column. i and j will be the
415
+ * coordinates. This also does a replace if column j is already present.
416
+ *
417
+ * Returns true if a new entry was added and false if an entry was replaced.
418
+ *
419
+ * Pre-conditions:
420
+ * - position.p() must be between ija(real_i) and ija(real_i+1), inclusive, where real_i = i + offset(0)
421
+ * - real_i and real_j must not be equal
422
+ */
423
+ row_iterator insert(row_iterator it, row_stored_nd_iterator position, size_t j, const D& val) {
424
+ it.insert(position, j, val);
425
+ return it;
426
+ }
427
+
428
+
429
+ /*
430
+ * Insert n elements v in columns j, using position as a guide. i gives the starting row. If at any time a value in j
431
+ * decreases,
432
+ */
433
+ /*bool insert(stored_iterator position, size_t n, size_t i, size_t* j, DType* v) {
434
+
435
+ } */
436
+
437
+ /*
438
+ * A pseudo-insert operation, since the diagonal portion of the A array is constant size.
439
+ */
440
+ stored_diagonal_iterator insert(stored_diagonal_iterator position, const D& val) {
441
+ *position = val;
442
+ return position;
443
+ }
444
+
445
+
446
+ /* iterator insert(iterator position, size_t j, const D& val) {
447
+ if (position.real_i() == position.real_j()) {
448
+ s->a(position.real_i()) = val;
449
+ return position;
450
+ } else {
451
+ row_iterator it = ribegin(position.i());
452
+ row_stored_nd_iterator position = it.ndbegin(j);
453
+ return insert(it, position, j, val);
454
+ }
455
+ }*/
456
+
457
+
458
+
459
+
460
+ /*
461
+ * Returns a pointer to the location of some entry in the matrix.
462
+ *
463
+ * This is needed for backwards compatibility. We don't really want anyone
464
+ * to modify the contents of that pointer, because it might be the ZERO location.
465
+ *
466
+ * TODO: Change all storage_get functions to return a VALUE once we've put list and
467
+ * dense in OO mode. ???
468
+ */
469
+ inline D* get_single_p(SLICE* slice) {
470
+ size_t real_i = offset(0) + slice->coords[0],
471
+ real_j = offset(1) + slice->coords[1];
472
+
473
+ if (real_i == real_j)
474
+ return &(a(real_i));
475
+
476
+ if (ija(real_i) == ija(real_i+1))
477
+ return default_obj_ptr(); // zero pointer
478
+
479
+ // binary search for a column's location
480
+ std::pair<size_t,bool> p = find_pos(std::make_pair(slice->coords[0], slice->coords[1]));
481
+ if (p.second)
482
+ return &(a(p.first));
483
+ // not found: return default
484
+ return default_obj_ptr(); // zero pointer
485
+ }
486
+
487
+
488
+ /*
489
+ * Allocate a reference pointing to s. Note that even if +this+ is a reference,
490
+ * we can create a reference within it.
491
+ *
492
+ * Note: Make sure you xfree() the result of this call. You can't just cast it
493
+ * directly into a YaleStorage<D> class.
494
+ */
495
+ YALE_STORAGE* alloc_ref(SLICE* slice) {
496
+ YALE_STORAGE* ns = ALLOC( YALE_STORAGE );
497
+
498
+ ns->dim = s->dim;
499
+ ns->offset = ALLOC_N(size_t, ns->dim);
500
+ ns->shape = ALLOC_N(size_t, ns->dim);
501
+
502
+ for (size_t d = 0; d < ns->dim; ++d) {
503
+ ns->offset[d] = slice->coords[d] + offset(d);
504
+ ns->shape[d] = slice->lengths[d];
505
+ }
506
+
507
+ ns->dtype = s->dtype;
508
+ ns->a = a_p();
509
+ ns->ija = ija_p();
510
+
511
+ ns->src = s;
512
+ s->count++;
513
+
514
+ ns->ndnz = 0;
515
+ ns->capacity = 0;
516
+
517
+ return ns;
518
+ }
519
+
520
+
521
+ /*
522
+ * Allocates and initializes the basic struct (but not IJA or A vectors).
523
+ */
524
+ static YALE_STORAGE* alloc(size_t* shape, size_t dim = 2) {
525
+ YALE_STORAGE* s = ALLOC( YALE_STORAGE );
526
+
527
+ s->ndnz = 0;
528
+ s->dtype = dtype();
529
+ s->shape = shape;
530
+ s->offset = ALLOC_N(size_t, dim);
531
+ for (size_t d = 0; d < dim; ++d)
532
+ s->offset[d] = 0;
533
+ s->dim = dim;
534
+ s->src = reinterpret_cast<STORAGE*>(s);
535
+ s->count = 1;
536
+
537
+ return s;
538
+ }
539
+
540
+
541
+ /*
542
+ * Create basic storage of same dtype as YaleStorage<D>. Allocates it,
543
+ * reserves necessary space, but doesn't fill structure at all.
544
+ */
545
+ static YALE_STORAGE* create(size_t* shape, size_t reserve) {
546
+
547
+ YALE_STORAGE* s = alloc( shape, 2 );
548
+ size_t max_sz = YaleStorage<D>::max_size(shape),
549
+ min_sz = YaleStorage<D>::min_size(shape);
550
+
551
+ if (reserve < min_sz) {
552
+ s->capacity = min_sz;
553
+ } else if (reserve > max_sz) {
554
+ s->capacity = max_sz;
555
+ } else {
556
+ s->capacity = reserve;
557
+ }
558
+
559
+ s->ija = ALLOC_N( size_t, s->capacity );
560
+ s->a = ALLOC_N( D, s->capacity );
561
+
562
+ return s;
563
+ }
564
+
565
+
566
+ /*
567
+ * Clear out the D portion of the A vector (clearing the diagonal and setting
568
+ * the zero value).
569
+ */
570
+ static void clear_diagonal_and_zero(YALE_STORAGE& s, D* init_val = NULL) {
571
+ D* a = reinterpret_cast<D*>(s.a);
572
+
573
+ // Clear out the diagonal + one extra entry
574
+ if (init_val) {
575
+ for (size_t i = 0; i <= s.shape[0]; ++i)
576
+ a[i] = *init_val;
577
+ } else {
578
+ for (size_t i = 0; i <= s.shape[0]; ++i)
579
+ a[i] = 0;
580
+ }
581
+ }
582
+
583
+
584
+ /*
585
+ * Empty the matrix by initializing the IJA vector and setting the diagonal to 0.
586
+ *
587
+ * Called when most YALE_STORAGE objects are created.
588
+ *
589
+ * Can't go inside of class YaleStorage because YaleStorage creation requires that
590
+ * IJA already be initialized.
591
+ */
592
+ static void init(YALE_STORAGE& s, D* init_val) {
593
+ size_t IA_INIT = s.shape[0] + 1;
594
+ for (size_t m = 0; m < IA_INIT; ++m) {
595
+ s.ija[m] = IA_INIT;
596
+ }
597
+
598
+ clear_diagonal_and_zero(s, init_val);
599
+ }
600
+
601
+
602
+ /*
603
+ * Make a very basic allocation. No structure or copy or anything. It'll be shaped like this
604
+ * matrix.
605
+ *
606
+ * TODO: Combine this with ::create()'s ::alloc(). These are redundant.
607
+ */
608
+ template <typename E>
609
+ YALE_STORAGE* alloc_basic_copy(size_t new_capacity, size_t new_ndnz) const {
610
+ nm::dtype_t new_dtype = nm::ctype_to_dtype_enum<E>::value_type;
611
+ YALE_STORAGE* lhs = ALLOC( YALE_STORAGE );
612
+ lhs->dim = s->dim;
613
+ lhs->shape = ALLOC_N( size_t, lhs->dim );
614
+
615
+ lhs->shape[0] = shape(0);
616
+ lhs->shape[1] = shape(1);
617
+
618
+ lhs->offset = ALLOC_N( size_t, lhs->dim );
619
+
620
+ lhs->offset[0] = 0;
621
+ lhs->offset[1] = 0;
622
+
623
+ lhs->capacity = new_capacity;
624
+ lhs->dtype = new_dtype;
625
+ lhs->ndnz = new_ndnz;
626
+ lhs->ija = ALLOC_N( size_t, new_capacity );
627
+ lhs->a = ALLOC_N( E, new_capacity );
628
+ lhs->src = lhs;
629
+ lhs->count = 1;
630
+
631
+ return lhs;
632
+ }
633
+
634
+
635
+ /*
636
+ * Make a full matrix structure copy (entries remain uninitialized). Remember to xfree()!
637
+ */
638
+ template <typename E>
639
+ YALE_STORAGE* alloc_struct_copy(size_t new_capacity) const {
640
+ YALE_STORAGE* lhs = alloc_basic_copy<E>(new_capacity, count_copy_ndnz());
641
+ // Now copy the IJA contents
642
+ if (slice) {
643
+ rb_raise(rb_eNotImpError, "cannot copy struct due to different offsets");
644
+ } else {
645
+ for (size_t m = 0; m < size(); ++m) {
646
+ lhs->ija[m] = ija(m); // copy indices
647
+ }
648
+ }
649
+ return lhs;
650
+ }
651
+
652
+
653
+ /*
654
+ * Copy this slice (or the full matrix if it isn't a slice) into a new matrix which is already allocated, ns.
655
+ */
656
+ template <typename E, bool Yield=false>
657
+ void copy(YALE_STORAGE& ns) const {
658
+ nm::dtype_t new_dtype = nm::ctype_to_dtype_enum<E>::value_type;
659
+ // get the default value for initialization (we'll re-use val for other copies after this)
660
+ E val = static_cast<E>(const_default_obj());
661
+
662
+ // initialize the matrix structure and clear the diagonal so we don't have to
663
+ // keep track of unwritten entries.
664
+ YaleStorage<E>::init(ns, &val);
665
+
666
+ E* ns_a = reinterpret_cast<E*>(ns.a);
667
+ size_t sz = shape(0) + 1; // current used size of ns
668
+
669
+ // FIXME: If diagonals line up, it's probably faster to do this with stored diagonal and stored non-diagonal iterators
670
+ for (const_row_iterator it = cribegin(); it != criend(); ++it) {
671
+ for (auto jt = it.begin(); !jt.end(); ++jt) {
672
+ if (it.i() == jt.j()) {
673
+ if (Yield) ns_a[it.i()] = rb_yield(~jt);
674
+ else ns_a[it.i()] = static_cast<E>(*jt);
675
+ } else if (*jt != const_default_obj()) {
676
+ if (Yield) ns_a[sz] = rb_yield(~jt);
677
+ else ns_a[sz] = static_cast<E>(*jt);
678
+ ns.ija[sz] = jt.j();
679
+ ++sz;
680
+ }
681
+ }
682
+ ns.ija[it.i()+1] = sz;
683
+ }
684
+
685
+ //ns.ija[shape(0)] = sz; // indicate end of last row
686
+ ns.ndnz = sz - shape(0) - 1; // update ndnz count
687
+ }
688
+
689
+
690
+ /*
691
+ * Allocate a casted copy of this matrix/reference. Remember to xfree() the result!
692
+ *
693
+ * If Yield is true, E must be nm::RubyObject, and it will call an rb_yield upon the stored value.
694
+ */
695
+ template <typename E, bool Yield = false>
696
+ YALE_STORAGE* alloc_copy() const {
697
+ nm::dtype_t new_dtype = nm::ctype_to_dtype_enum<E>::value_type;
698
+
699
+ YALE_STORAGE* lhs;
700
+ if (slice) {
701
+ size_t* xshape = ALLOC_N(size_t, 2);
702
+ xshape[0] = shape(0);
703
+ xshape[1] = shape(1);
704
+ size_t ndnz = count_copy_ndnz();
705
+ size_t reserve = shape(0) + ndnz + 1;
706
+
707
+ // std::cerr << "reserve = " << reserve << std::endl;
708
+
709
+ lhs = YaleStorage<E>::create(xshape, reserve);
710
+
711
+ if (lhs->capacity < reserve)
712
+ rb_raise(nm_eStorageTypeError, "conversion failed; capacity of %lu requested, max allowable is %lu", reserve, lhs->capacity);
713
+
714
+ // Fill lhs with what's in our current matrix.
715
+ copy<E, Yield>(*lhs);
716
+ } else {
717
+ // Copy the structure and setup the IJA structure.
718
+ lhs = alloc_struct_copy<E>(s->capacity);
719
+
720
+ E* la = reinterpret_cast<E*>(lhs->a);
721
+ for (size_t m = 0; m < size(); ++m) {
722
+ if (Yield) la[m] = rb_yield(nm::yale_storage::nm_rb_dereference(a(m)));
723
+ else la[m] = static_cast<E>(a(m));
724
+ }
725
+
726
+ }
727
+
728
+ return lhs;
729
+ }
730
+
731
+ /*
732
+ * Allocate a transposed copy of the matrix
733
+ */
734
+ /*
735
+ * Allocate a casted copy of this matrix/reference. Remember to xfree() the result!
736
+ *
737
+ * If Yield is true, E must be nm::RubyObject, and it will call an rb_yield upon the stored value.
738
+ */
739
+ template <typename E, bool Yield = false>
740
+ YALE_STORAGE* alloc_copy_transposed() const {
741
+
742
+ if (slice) {
743
+ rb_raise(rb_eNotImpError, "please make a copy before transposing");
744
+ } else {
745
+ // Copy the structure and setup the IJA structure.
746
+ size_t* xshape = ALLOC_N(size_t, 2);
747
+ xshape[0] = shape(1);
748
+ xshape[1] = shape(0);
749
+
750
+ // Take a stab at the number of non-diagonal stored entries we'll have.
751
+ size_t reserve = size() - xshape[1] + xshape[0];
752
+ YALE_STORAGE* lhs = YaleStorage<E>::create(xshape, reserve);
753
+ E r_init = static_cast<E>(const_default_obj());
754
+ YaleStorage<E>::init(*lhs, &r_init);
755
+
756
+ nm::yale_storage::transpose_yale<D,E,true,true>(shape(0), shape(1), ija_p(), ija_p(), a_p(), const_default_obj(),
757
+ lhs->ija, lhs->ija, reinterpret_cast<E*>(lhs->a), r_init);
758
+ return lhs;
759
+ }
760
+
761
+ return NULL;
762
+ }
763
+
764
+
765
+ /*
766
+ * Comparison between two matrices. Does not check size and such -- assumption is that they are the same shape.
767
+ */
768
+ template <typename E>
769
+ bool operator==(const YaleStorage<E>& rhs) const {
770
+ for (size_t i = 0; i < shape(0); ++i) {
771
+ typename YaleStorage<D>::const_row_iterator li = cribegin(i);
772
+ typename YaleStorage<E>::const_row_iterator ri = rhs.cribegin(i);
773
+
774
+ size_t j = 0; // keep track of j so we can compare different defaults
775
+
776
+ auto lj = li.begin();
777
+ auto rj = ri.begin();
778
+ while (!lj.end() || !rj.end()) {
779
+ if (lj < rj) {
780
+ if (*lj != rhs.const_default_obj()) return false;
781
+ ++lj;
782
+ } else if (rj < lj) {
783
+ if (const_default_obj() != *rj) return false;
784
+ ++rj;
785
+ } else { // rj == lj
786
+ if (*lj != *rj) return false;
787
+ ++lj;
788
+ ++rj;
789
+ }
790
+ ++j;
791
+ }
792
+
793
+ // if we skip an entry (because it's an ndnz in BOTH matrices), we need to compare defaults.
794
+ // (We know we skipped if lj and rj hit end before j does.)
795
+ if (j < shape(1) && const_default_obj() != rhs.const_default_obj()) return false;
796
+
797
+ ++li;
798
+ ++ri;
799
+ }
800
+
801
+ return true;
802
+ }
803
+
804
+ /*
805
+ * Necessary for element-wise operations. The return dtype will be nm::RUBYOBJ.
806
+ */
807
+ template <typename E>
808
+ VALUE map_merged_stored(VALUE klass, nm::YaleStorage<E>& t, VALUE r_init) const {
809
+ VALUE s_init = const_default_value(),
810
+ t_init = t.const_default_value();
811
+
812
+ // Make a reasonable approximation of the resulting capacity
813
+ size_t s_ndnz = count_copy_ndnz(),
814
+ t_ndnz = t.count_copy_ndnz();
815
+ size_t reserve = shape(0) + std::max(s_ndnz, t_ndnz) + 1;
816
+
817
+ size_t* xshape = ALLOC_N(size_t, 2);
818
+ xshape[0] = shape(0);
819
+ xshape[1] = shape(1);
820
+
821
+ YALE_STORAGE* rs= YaleStorage<nm::RubyObject>::create(xshape, reserve);
822
+
823
+ if (r_init == Qnil)
824
+ r_init = rb_yield_values(2, s_init, t_init);
825
+
826
+ nm::RubyObject r_init_obj(r_init);
827
+
828
+ // Prepare the matrix structure
829
+ YaleStorage<nm::RubyObject>::init(*rs, &r_init_obj);
830
+ NMATRIX* m = nm_create(nm::YALE_STORE, reinterpret_cast<STORAGE*>(rs));
831
+ VALUE result = Data_Wrap_Struct(klass, nm_mark, nm_delete, m);
832
+
833
+ // No obvious, efficient way to pass a length function as the fourth argument here:
834
+ RETURN_SIZED_ENUMERATOR(result, 0, 0, 0);
835
+
836
+ // Create an object for us to iterate over.
837
+ YaleStorage<nm::RubyObject> r(rs);
838
+
839
+ // Walk down our new matrix, inserting values as we go.
840
+ for (size_t i = 0; i < xshape[0]; ++i) {
841
+ YaleStorage<nm::RubyObject>::row_iterator ri = r.ribegin(i);
842
+ typename YaleStorage<D>::const_row_iterator si = cribegin(i);
843
+ typename YaleStorage<E>::const_row_iterator ti = t.cribegin(i);
844
+
845
+ auto sj = si.begin();
846
+ auto tj = ti.begin();
847
+ auto rj = ri.ndbegin();
848
+
849
+ while (sj != si.end() || tj != ti.end()) {
850
+ VALUE v;
851
+ size_t j;
852
+
853
+ if (sj < tj) {
854
+ v = rb_yield_values(2, ~sj, t_init);
855
+ j = sj.j();
856
+ ++sj;
857
+ } else if (tj < sj) {
858
+ v = rb_yield_values(2, s_init, ~tj);
859
+ j = tj.j();
860
+ ++tj;
861
+ } else {
862
+ v = rb_yield_values(2, ~sj, ~tj);
863
+ j = sj.j();
864
+ ++sj;
865
+ ++tj;
866
+ }
867
+
868
+ // FIXME: This can be sped up by inserting all at the same time
869
+ // since it's a new matrix. But that function isn't quite ready
870
+ // yet.
871
+ if (j == i) r.a(i) = v;
872
+ else rj = ri.insert(rj, j, v);
873
+ //RB_P(rb_funcall(result, rb_intern("yale_ija"), 0));
874
+ }
875
+ }
876
+
877
+ return result;
878
+ }
879
+
880
+ protected:
881
+ /*
882
+ * Update row sizes starting with row i
883
+ */
884
+ void update_real_row_sizes_from(size_t real_i, int change) {
885
+ ++real_i;
886
+ for (; real_i <= real_shape(0); ++real_i) {
887
+ ija(real_i) += change;
888
+ }
889
+ }
890
+
891
+
892
+ /*
893
+ * Like move_right, but also involving a resize. This updates row sizes as well. This version also takes a plan for
894
+ * multiple rows, and tries to do them all in one copy. It's used for multi-row slice-setting.
895
+ *
896
+ * This also differs from update_resize_move in that it resizes to the exact requested size instead of reserving space.
897
+ */
898
+ void update_resize_move_insert(size_t real_i, size_t real_j, size_t* lengths, D* const v, size_t v_size, multi_row_insertion_plan p) {
899
+ size_t sz = size(); // current size of the storage vectors
900
+ size_t new_cap = sz + p.total_change;
901
+
902
+ if (new_cap > real_max_size()) {
903
+ xfree(v);
904
+ rb_raise(rb_eStandardError, "resize caused by insertion of size %d (on top of current size %lu) would have caused yale matrix size to exceed its maximum (%lu)", p.total_change, sz, real_max_size());
905
+ }
906
+
907
+ size_t* new_ija = ALLOC_N( size_t,new_cap );
908
+ D* new_a = ALLOC_N( D, new_cap );
909
+
910
+ // Copy unchanged row pointers first.
911
+ size_t m = 0;
912
+ for (; m <= real_i; ++m) {
913
+ new_ija[m] = ija(m);
914
+ new_a[m] = a(m);
915
+ }
916
+
917
+ // Now copy unchanged locations in IJA and A.
918
+ size_t q = real_shape(0)+1; // q is the copy-to position.
919
+ size_t r = real_shape(0)+1; // r is the copy-from position.
920
+ for (; r < p.pos[0]; ++r, ++q) {
921
+ new_ija[q] = ija(r);
922
+ new_a[q] = a(r);
923
+ }
924
+
925
+ // For each pos and change in the slice, copy the information prior to the insertion point. Then insert the necessary
926
+ // information.
927
+ size_t v_offset = 0;
928
+ int accum = 0; // keep track of the total change as we go so we can update row information.
929
+ for (size_t i = 0; i < lengths[0]; ++i, ++m) {
930
+ for (; r < p.pos[i]; ++r, ++q) {
931
+ new_ija[q] = ija(r);
932
+ new_a[q] = a(r);
933
+ }
934
+
935
+ // Insert slice data for a single row.
936
+ for (size_t j = 0; j < lengths[1]; ++j, ++v_offset) {
937
+ if (v_offset >= v_size) v_offset %= v_size;
938
+
939
+ if (j + real_j == i + real_i) { // modify diagonal
940
+ new_a[real_i + i] = v[v_offset];
941
+ } else if (v[v_offset] != const_default_obj()) {
942
+ new_ija[q] = j + real_j;
943
+ new_a[q] = v[v_offset];
944
+ ++q; // move on to next q location
945
+ }
946
+
947
+ if (r < ija(real_shape(0)) && ija(r) == j + real_j) ++r; // move r forward if the column matches.
948
+ }
949
+
950
+ // Update the row pointer for the current row.
951
+ accum += p.change[i];
952
+ new_ija[m] = ija(m) + accum;
953
+ new_a[m] = a(m); // copy diagonal for this row
954
+ }
955
+
956
+ // Now copy everything subsequent to the last insertion point.
957
+ for (; r < size(); ++r, ++q) {
958
+ new_ija[q] = ija(r);
959
+ new_a[q] = a(r);
960
+ }
961
+
962
+ // Update the remaining row pointers and copy remaining diagonals
963
+ for (; m <= real_shape(0); ++m) {
964
+ new_ija[m] = ija(m) + accum;
965
+ new_a[m] = a(m);
966
+ }
967
+
968
+ s->capacity = new_cap;
969
+
970
+ xfree(s->ija);
971
+ xfree(s->a);
972
+
973
+ s->ija = new_ija;
974
+ s->a = reinterpret_cast<void*>(new_a);
975
+ }
976
+
977
+
978
+
979
+
980
+ /*
981
+ * Like move_right, but also involving a resize. This updates row sizes as well.
982
+ */
983
+ void update_resize_move(row_stored_nd_iterator position, size_t real_i, int n) {
984
+ size_t sz = size(); // current size of the storage vectors
985
+ size_t new_cap = n > 0 ? capacity() * nm::yale_storage::GROWTH_CONSTANT
986
+ : capacity() / nm::yale_storage::GROWTH_CONSTANT;
987
+ size_t max_cap = real_max_size();
988
+
989
+ if (new_cap > max_cap) {
990
+ new_cap = max_cap;
991
+ if (sz + n > max_cap)
992
+ rb_raise(rb_eStandardError, "resize caused by insertion/deletion of size %d (on top of current size %lu) would have caused yale matrix size to exceed its maximum (%lu)", n, sz, real_max_size());
993
+ }
994
+
995
+ if (new_cap < sz + n) new_cap = sz + n;
996
+
997
+ size_t* new_ija = ALLOC_N( size_t,new_cap );
998
+ D* new_a = ALLOC_N( D, new_cap );
999
+
1000
+ // Copy unchanged row pointers first.
1001
+ for (size_t m = 0; m <= real_i; ++m) {
1002
+ new_ija[m] = ija(m);
1003
+ new_a[m] = a(m);
1004
+ }
1005
+
1006
+ // Now update row pointers following the changed row as we copy the additional values.
1007
+ for (size_t m = real_i + 1; m <= real_shape(0); ++m) {
1008
+ new_ija[m] = ija(m) + n;
1009
+ new_a[m] = a(m);
1010
+ }
1011
+
1012
+ // Copy all remaining prior to insertion/removal site
1013
+ for (size_t m = real_shape(0) + 1; m < position.p(); ++m) {
1014
+ new_ija[m] = ija(m);
1015
+ new_a[m] = a(m);
1016
+ }
1017
+
1018
+ // Copy all subsequent to insertion/removal site
1019
+ size_t m = position.p();
1020
+ if (n < 0) m -= n;
1021
+
1022
+ for (; m < sz; ++m) {
1023
+ new_ija[m+n] = ija(m);
1024
+ new_a[m+n] = a(m);
1025
+ }
1026
+
1027
+
1028
+ s->capacity = new_cap;
1029
+
1030
+ xfree(s->ija);
1031
+ xfree(s->a);
1032
+
1033
+ s->ija = new_ija;
1034
+ s->a = reinterpret_cast<void*>(new_a);
1035
+ }
1036
+
1037
+
1038
+ /*
1039
+ * Move elements in the IJA and A arrays by n (to the right).
1040
+ * Does not update row sizes.
1041
+ */
1042
+ void move_right(row_stored_nd_iterator position, size_t n) {
1043
+ size_t sz = size();
1044
+ for (size_t m = 0; m < sz - position.p(); ++m) {
1045
+ ija(sz+n-1-m) = ija(sz-1-m);
1046
+ a(sz+n-1-m) = a(sz-1-m);
1047
+ }
1048
+ }
1049
+
1050
+ /*
1051
+ * Move elements in the IJA and A arrays by n (to the left). Here position gives
1052
+ * the location to move to, and they should come from n to the right.
1053
+ */
1054
+ void move_left(row_stored_nd_iterator position, size_t n) {
1055
+ size_t sz = size();
1056
+ for (size_t m = position.p() + n; m < sz; ++m) { // work backwards
1057
+ ija(m-n) = ija(m);
1058
+ a(m-n) = a(m);
1059
+ }
1060
+ }
1061
+
1062
+ YALE_STORAGE* s;
1063
+ bool slice;
1064
+ size_t* slice_shape;
1065
+ size_t* slice_offset;
1066
+ };
1067
+
1068
+ } // end of nm namespace
1069
+
1070
+ #endif // YALE_CLASS_H