nmatrix 0.0.4 → 0.0.5

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.
@@ -323,6 +323,8 @@ NM_DEF_STRUCT_POST(NMATRIX); // };
323
323
 
324
324
  #define NM_CHECK_ALLOC(x) if (!x) rb_raise(rb_eNoMemError, "insufficient memory");
325
325
 
326
+ #define RB_FILE_EXISTS(fn) (rb_funcall(rb_const_get(rb_cObject, rb_intern("File")), rb_intern("exists?"), 1, (fn)) == Qtrue)
327
+
326
328
  #define CheckNMatrixType(v) if (TYPE(v) != T_DATA || (RDATA(v)->dfree != (RUBY_DATA_FUNC)nm_delete && RDATA(v)->dfree != (RUBY_DATA_FUNC)nm_delete_ref)) rb_raise(rb_eTypeError, "expected NMatrix on left-hand side of operation");
327
329
 
328
330
  #define NM_IsNMatrix(obj) \
@@ -55,6 +55,9 @@ ID nm_rb_real,
55
55
  nm_rb_list,
56
56
  nm_rb_yale,
57
57
 
58
+ nm_rb_row,
59
+ nm_rb_column,
60
+
58
61
  nm_rb_add,
59
62
  nm_rb_sub,
60
63
  nm_rb_mul,
@@ -68,7 +71,9 @@ ID nm_rb_real,
68
71
  nm_rb_eql,
69
72
  nm_rb_neql,
70
73
  nm_rb_gte,
71
- nm_rb_lte;
74
+ nm_rb_lte,
75
+
76
+ nm_rb_hash;
72
77
 
73
78
  VALUE cNMatrix,
74
79
  cNMatrix_IO,
@@ -122,4 +127,9 @@ void nm_init_ruby_constants(void) {
122
127
  nm_rb_lower = rb_intern("lower");
123
128
  nm_rb_unit = rb_intern("unit");
124
129
  nm_rb_nonunit = rb_intern("nonunit");
130
+
131
+ nm_rb_hash = rb_intern("hash");
132
+
133
+ nm_rb_column = rb_intern("column");
134
+ nm_rb_row = rb_intern("row");
125
135
  }
@@ -57,6 +57,9 @@ extern ID nm_rb_real,
57
57
  nm_rb_dense,
58
58
  nm_rb_list,
59
59
  nm_rb_yale,
60
+
61
+ nm_rb_row,
62
+ nm_rb_column,
60
63
 
61
64
  nm_rb_add,
62
65
  nm_rb_sub,
@@ -71,7 +74,9 @@ extern ID nm_rb_real,
71
74
  nm_rb_eql,
72
75
  nm_rb_neql,
73
76
  nm_rb_gte,
74
- nm_rb_lte;
77
+ nm_rb_lte,
78
+
79
+ nm_rb_hash;
75
80
 
76
81
  extern VALUE cNMatrix,
77
82
  cNMatrix_IO,
@@ -253,7 +253,7 @@ VALUE nm_dense_each_with_indices(VALUE nmatrix) {
253
253
 
254
254
  nm_dense_storage_delete(sliced_dummy);
255
255
 
256
- return Qnil;
256
+ return nmatrix;
257
257
 
258
258
  }
259
259
 
@@ -299,7 +299,7 @@ VALUE nm_dense_each(VALUE nmatrix) {
299
299
 
300
300
  nm_dense_storage_delete(sliced_dummy);
301
301
 
302
- return Qnil;
302
+ return nmatrix;
303
303
 
304
304
  }
305
305
 
@@ -68,6 +68,11 @@
68
68
  #define NM_MIN(a,b) (((a)<(b))?(a):(b))
69
69
  #endif
70
70
 
71
+ #ifndef NM_MAX_ITYPE
72
+ #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))
73
+ #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))
74
+ #endif
75
+
71
76
  /*
72
77
  * Forward Declarations
73
78
  */
@@ -78,12 +83,16 @@ extern "C" {
78
83
 
79
84
  /* Ruby-accessible functions */
80
85
  static VALUE nm_size(VALUE self);
81
- static VALUE nm_a(VALUE self);
82
- static VALUE nm_d(VALUE self);
86
+ static VALUE nm_a(int argc, VALUE* argv, VALUE self);
87
+ static VALUE nm_d(int argc, VALUE* argv, VALUE self);
83
88
  static VALUE nm_lu(VALUE self);
84
89
  static VALUE nm_ia(VALUE self);
85
90
  static VALUE nm_ja(VALUE self);
86
- static VALUE nm_ija(VALUE self);
91
+ static VALUE nm_ija(int argc, VALUE* argv, VALUE self);
92
+
93
+ static VALUE nm_nd_row(int argc, VALUE* argv, VALUE self);
94
+ static VALUE nm_vector_insert(int argc, VALUE* argv, VALUE self);
95
+
87
96
 
88
97
  } // end extern "C" block
89
98
 
@@ -104,11 +113,16 @@ static YALE_STORAGE* copy_alloc_struct(const YALE_STORAGE* rhs, const dtype_t ne
104
113
  template <typename IType>
105
114
  static void increment_ia_after(YALE_STORAGE* s, IType ija_size, IType i, IType n);
106
115
 
116
+ template <typename IType>
117
+ static void c_increment_ia_after(YALE_STORAGE* s, size_t ija_size, size_t i, size_t n) {
118
+ increment_ia_after<IType>(s, ija_size, i, n);
119
+ }
120
+
107
121
  template <typename IType>
108
122
  static IType insert_search(YALE_STORAGE* s, IType left, IType right, IType key, bool* found);
109
123
 
110
124
  template <typename DType, typename IType>
111
- static char vector_insert(YALE_STORAGE* s, size_t pos, size_t* j, DType* val, size_t n, bool struct_only);
125
+ static char vector_insert(YALE_STORAGE* s, size_t pos, size_t* j, void* val_, size_t n, bool struct_only);
112
126
 
113
127
  template <typename DType, typename IType>
114
128
  static char vector_insert_resize(YALE_STORAGE* s, size_t current_size, size_t pos, size_t* j, size_t n, bool struct_only);
@@ -120,6 +134,27 @@ YALE_STORAGE* ew_op(const YALE_STORAGE* left, const YALE_STORAGE* right, dtype_t
120
134
  * Functions
121
135
  */
122
136
 
137
+ /*
138
+ * Copy a vector from one IType or DType to another.
139
+ */
140
+ template <typename LType, typename RType>
141
+ static inline void copy_recast_vector(const void* in_, void* out_, size_t length) {
142
+ const RType* in = reinterpret_cast<const RType*>(in_);
143
+ LType* out = reinterpret_cast<LType*>(out_);
144
+ for (size_t i = 0; i < length; ++i) {
145
+ out[i] = in[i];
146
+ }
147
+ out;
148
+ }
149
+
150
+
151
+ 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) {
152
+ NAMED_LR_ITYPE_TEMPLATE_TABLE(ttable, copy_recast_vector, void, const void* in_, void* out_, size_t length);
153
+
154
+ ttable[out_itype][in_itype](in, out, length);
155
+ }
156
+
157
+
123
158
  /*
124
159
  * Create Yale storage from IA, JA, and A vectors given in Old Yale format (probably from a file, since NMatrix only uses
125
160
  * new Yale for its storage).
@@ -647,7 +682,7 @@ YALE_STORAGE* ew_op(const YALE_STORAGE* left, const YALE_STORAGE* right, dtype_t
647
682
 
648
683
  YALE_STORAGE* dest;
649
684
 
650
- new_shape = reinterpret_cast<size_t*>(calloc(2, sizeof(size_t)));
685
+ new_shape = reinterpret_cast<size_t*>(ALLOC_N(size_t, 2));
651
686
  new_shape[0] = left->shape[0];
652
687
  new_shape[1] = left->shape[1];
653
688
 
@@ -932,11 +967,11 @@ static char vector_insert_resize(YALE_STORAGE* s, size_t current_size, size_t po
932
967
 
933
968
  // Copy all values subsequent to the insertion site to the new IJA and new A, leaving room (size n) for insertion.
934
969
  if (struct_only) {
935
- for (size_t i = pos; i < current_size - pos + n - 1; ++i) {
970
+ for (size_t i = pos; i < current_size; ++i) {
936
971
  new_ija[i+n] = old_ija[i];
937
972
  }
938
973
  } else {
939
- for (size_t i = pos; i < current_size - pos + n - 1; ++i) {
974
+ for (size_t i = pos; i < current_size; ++i) {
940
975
  new_ija[i+n] = old_ija[i];
941
976
  new_a[i+n] = old_a[i];
942
977
  }
@@ -964,11 +999,13 @@ static char vector_insert_resize(YALE_STORAGE* s, size_t current_size, size_t po
964
999
  * question.)
965
1000
  */
966
1001
  template <typename DType, typename IType>
967
- static char vector_insert(YALE_STORAGE* s, size_t pos, size_t* j, DType* val, size_t n, bool struct_only) {
1002
+ static char vector_insert(YALE_STORAGE* s, size_t pos, size_t* j, void* val_, size_t n, bool struct_only) {
968
1003
  if (pos < s->shape[0]) {
969
- rb_raise(rb_eArgError, "vector insert pos is before beginning of ja; this should not happen");
1004
+ rb_raise(rb_eArgError, "vector insert pos (%d) is before beginning of ja (%d); this should not happen", pos, s->shape[0]);
970
1005
  }
971
1006
 
1007
+ DType* val = reinterpret_cast<DType*>(val_);
1008
+
972
1009
  size_t size = get_size<IType>(s);
973
1010
 
974
1011
  IType* ija = reinterpret_cast<IType*>(s->ija);
@@ -1098,6 +1135,7 @@ static inline size_t get_size(const YALE_STORAGE* storage) {
1098
1135
  return static_cast<size_t>(reinterpret_cast<IType*>(storage->ija)[ storage->shape[0] ]);
1099
1136
  }
1100
1137
 
1138
+
1101
1139
  /*
1102
1140
  * Allocate for a copy or copy-cast operation, and copy the IJA portion of the
1103
1141
  * matrix (the structure).
@@ -1125,7 +1163,7 @@ static YALE_STORAGE* copy_alloc_struct(const YALE_STORAGE* rhs, const dtype_t ne
1125
1163
  }
1126
1164
 
1127
1165
  template <typename DType, typename IType>
1128
- static STORAGE* matrix_multiply(const STORAGE_PAIR& casted_storage, size_t* resulting_shape, bool vector) {
1166
+ static STORAGE* matrix_multiply(const STORAGE_PAIR& casted_storage, size_t* resulting_shape, bool vector, nm::itype_t result_itype) {
1129
1167
  YALE_STORAGE *left = (YALE_STORAGE*)(casted_storage.left),
1130
1168
  *right = (YALE_STORAGE*)(casted_storage.right);
1131
1169
 
@@ -1133,24 +1171,49 @@ static STORAGE* matrix_multiply(const STORAGE_PAIR& casted_storage, size_t* resu
1133
1171
  // same for left and right.
1134
1172
  // int8_t dtype = left->dtype;
1135
1173
 
1174
+ // Massage the IType arrays into the correct form.
1175
+
1176
+ IType* ijl;
1177
+ if (left->itype == result_itype) ijl = reinterpret_cast<IType*>(left->ija);
1178
+ else { // make a temporary copy of the IJA vector for L with the correct itype
1179
+ std::cerr << "changing left itype from " << static_cast<uint8_t>(left->itype) << " to " << static_cast<int8_t>(result_itype) << std::endl;
1180
+ size_t length = nm_yale_storage_get_size(left);
1181
+ std::cerr << "length = " << length << std::endl;
1182
+ ijl = ALLOCA_N(IType, length);
1183
+ copy_recast_itype_vector(reinterpret_cast<void*>(left->ija), left->itype, reinterpret_cast<void*>(ijl), result_itype, length);
1184
+ }
1185
+
1186
+ IType* ijr;
1187
+ if (right->itype == result_itype) ijr = reinterpret_cast<IType*>(right->ija);
1188
+ else { // make a temporary copy of the IJA vector for R with the correct itype
1189
+ std::cerr << "changing right itype from " << static_cast<uint8_t>(right->itype) << " to " << static_cast<int8_t>(result_itype) << std::endl;
1190
+ size_t length = nm_yale_storage_get_size(right);
1191
+ std::cerr << "length = " << length << std::endl;
1192
+ ijr = ALLOCA_N(IType, length);
1193
+ copy_recast_itype_vector(reinterpret_cast<void*>(right->ija), right->itype, reinterpret_cast<void*>(ijr), result_itype, length);
1194
+ }
1195
+
1196
+ // First, count the ndnz of the result.
1197
+ // TODO: This basically requires running symbmm twice to get the exact ndnz size. That's frustrating. Are there simple
1198
+ // cases where we can avoid running it?
1199
+ 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);
1200
+
1136
1201
  // Create result storage.
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);
1202
+ YALE_STORAGE* result = nm_yale_storage_create(left->dtype, resulting_shape, 2, result_ndnz, result_itype);
1139
1203
  init<DType,IType>(result);
1140
-
1141
- IType* ijl = reinterpret_cast<IType*>(left->ija);
1142
- IType* ijr = reinterpret_cast<IType*>(right->ija);
1143
1204
  IType* ija = reinterpret_cast<IType*>(result->ija);
1144
1205
 
1145
1206
  // Symbolic multiplication step (build the structure)
1146
- nm::math::symbmm<IType>(result->shape[0], result->shape[1], ijl, ijl, true, ijr, ijr, true, ija, true);
1207
+ nm::math::symbmm<IType>(resulting_shape[0], left->shape[1], resulting_shape[1], ijl, ijl, true, ijr, ijr, true, ija, true);
1147
1208
 
1148
1209
  // Numeric multiplication step (fill in the elements)
1149
- nm::math::numbmm<DType,IType>(result->shape[0], result->shape[1],
1210
+
1211
+ nm::math::numbmm<DType,IType>(result->shape[0], left->shape[1], result->shape[1],
1150
1212
  ijl, ijl, reinterpret_cast<DType*>(left->a), true,
1151
1213
  ijr, ijr, reinterpret_cast<DType*>(right->a), true,
1152
1214
  ija, ija, reinterpret_cast<DType*>(result->a), true);
1153
1215
 
1216
+
1154
1217
  // Sort the columns
1155
1218
  nm::math::smmp_sort_columns<DType,IType>(result->shape[0], ija, ija, reinterpret_cast<DType*>(result->a));
1156
1219
 
@@ -1273,13 +1336,17 @@ void nm_init_yale_functions() {
1273
1336
  */
1274
1337
  cNMatrix_YaleFunctions = rb_define_module_under(cNMatrix, "YaleFunctions");
1275
1338
 
1276
- rb_define_method(cNMatrix_YaleFunctions, "yale_ija", (METHOD)nm_ija, 0);
1277
- rb_define_method(cNMatrix_YaleFunctions, "yale_a", (METHOD)nm_a, 0);
1339
+ rb_define_method(cNMatrix_YaleFunctions, "yale_ija", (METHOD)nm_ija, -1);
1340
+ rb_define_method(cNMatrix_YaleFunctions, "yale_a", (METHOD)nm_a, -1);
1278
1341
  rb_define_method(cNMatrix_YaleFunctions, "yale_size", (METHOD)nm_size, 0);
1279
1342
  rb_define_method(cNMatrix_YaleFunctions, "yale_ia", (METHOD)nm_ia, 0);
1280
1343
  rb_define_method(cNMatrix_YaleFunctions, "yale_ja", (METHOD)nm_ja, 0);
1281
- rb_define_method(cNMatrix_YaleFunctions, "yale_d", (METHOD)nm_d, 0);
1344
+ rb_define_method(cNMatrix_YaleFunctions, "yale_d", (METHOD)nm_d, -1);
1282
1345
  rb_define_method(cNMatrix_YaleFunctions, "yale_lu", (METHOD)nm_lu, 0);
1346
+
1347
+ rb_define_method(cNMatrix_YaleFunctions, "yale_nd_row", (METHOD)nm_nd_row, -1);
1348
+ rb_define_method(cNMatrix_YaleFunctions, "yale_vector_insert", (METHOD)nm_vector_insert, -1);
1349
+
1283
1350
  rb_define_const(cNMatrix_YaleFunctions, "YALE_GROWTH_CONSTANT", rb_float_new(nm::yale_storage::GROWTH_CONSTANT));
1284
1351
  }
1285
1352
 
@@ -1325,6 +1392,25 @@ void* nm_yale_storage_get(STORAGE* storage, SLICE* slice) {
1325
1392
  return ttable[casted_storage->dtype][casted_storage->itype](casted_storage, slice);
1326
1393
  }
1327
1394
 
1395
+ /*
1396
+ * C accessor for yale_storage::vector_insert
1397
+ */
1398
+ 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) {
1399
+ NAMED_LI_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::vector_insert, char, YALE_STORAGE*, size_t, size_t*, void*, size_t, bool);
1400
+
1401
+ return ttable[dtype][itype](s, pos, js, vals, n, struct_only);
1402
+ }
1403
+
1404
+ /*
1405
+ * C accessor for yale_storage::increment_ia_after, typically called after ::vector_insert
1406
+ */
1407
+ 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) {
1408
+ NAMED_ITYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::c_increment_ia_after, void, YALE_STORAGE*, size_t, size_t, size_t);
1409
+
1410
+ ttable[itype](s, ija_size, i, n);
1411
+ }
1412
+
1413
+
1328
1414
  /*
1329
1415
  * C accessor for yale_storage::ref, which returns a pointer to the correct location in a YALE_STORAGE object
1330
1416
  * for some set of coordinates.
@@ -1403,14 +1489,21 @@ STORAGE* nm_yale_storage_copy_transposed(const STORAGE* rhs_base) {
1403
1489
  /*
1404
1490
  * C accessor for multiplying two YALE_STORAGE matrices, which have already been casted to the same dtype.
1405
1491
  *
1406
- * FIXME: What happens if the two matrices have different itypes?
1492
+ * FIXME: There should be some mathematical way to determine the worst-case IType based on the input ITypes. Right now
1493
+ * it just uses the default.
1407
1494
  */
1408
1495
  STORAGE* nm_yale_storage_matrix_multiply(const STORAGE_PAIR& casted_storage, size_t* resulting_shape, bool vector) {
1409
- LI_DTYPE_TEMPLATE_TABLE(nm::yale_storage::matrix_multiply, STORAGE*, const STORAGE_PAIR& casted_storage, size_t* resulting_shape, bool vector);
1496
+ 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);
1410
1497
 
1411
- YALE_STORAGE* storage_access = (YALE_STORAGE*)(casted_storage.left);
1498
+ YALE_STORAGE* left = reinterpret_cast<YALE_STORAGE*>(casted_storage.left);
1499
+ YALE_STORAGE* right = reinterpret_cast<YALE_STORAGE*>(casted_storage.right);
1412
1500
 
1413
- return ttable[storage_access->dtype][storage_access->itype](casted_storage, resulting_shape, vector);
1501
+ // Determine the itype for the matrix that will be returned.
1502
+ nm::itype_t itype = nm_yale_storage_itype_by_shape(resulting_shape),
1503
+ max_itype = NM_MAX_ITYPE(left->itype, right->itype);
1504
+ if (static_cast<int8_t>(itype) < static_cast<int8_t>(max_itype)) itype = max_itype;
1505
+
1506
+ return ttable[left->dtype][itype](casted_storage, resulting_shape, vector, itype);
1414
1507
  }
1415
1508
 
1416
1509
  /*
@@ -1545,6 +1638,7 @@ void nm_yale_storage_init(YALE_STORAGE* s) {
1545
1638
  ttable[s->dtype][s->itype](s);
1546
1639
  }
1547
1640
 
1641
+
1548
1642
  /*
1549
1643
  * Ruby GC mark function for YALE_STORAGE. C accessible.
1550
1644
  */
@@ -1615,42 +1709,64 @@ static VALUE nm_size(VALUE self) {
1615
1709
  /*
1616
1710
  * call-seq:
1617
1711
  * yale_a -> Array
1712
+ * yale_d(index) -> ...
1618
1713
  *
1619
1714
  * Get the A array of a Yale matrix (which stores the diagonal and the LU portions of the matrix).
1620
1715
  */
1621
- static VALUE nm_a(VALUE self) {
1622
- YALE_STORAGE* s = NM_STORAGE_YALE(self);
1716
+ static VALUE nm_a(int argc, VALUE* argv, VALUE self) {
1717
+ VALUE idx;
1718
+ rb_scan_args(argc, argv, "01", &idx);
1623
1719
 
1720
+ YALE_STORAGE* s = NM_STORAGE_YALE(self);
1624
1721
  size_t size = nm_yale_storage_get_size(s);
1625
- VALUE* vals = ALLOCA_N(VALUE, size);
1626
1722
 
1627
- for (size_t i = 0; i < size; ++i) {
1628
- vals[i] = rubyobj_from_cval((char*)(s->a) + DTYPE_SIZES[s->dtype]*i, s->dtype).rval;
1629
- }
1630
- VALUE ary = rb_ary_new4(size, vals);
1723
+ if (idx == Qnil) {
1724
+ VALUE* vals = ALLOCA_N(VALUE, size);
1631
1725
 
1632
- for (size_t i = size; i < s->capacity; ++i)
1633
- rb_ary_push(ary, Qnil);
1726
+ for (size_t i = 0; i < size; ++i) {
1727
+ vals[i] = rubyobj_from_cval((char*)(s->a) + DTYPE_SIZES[s->dtype]*i, s->dtype).rval;
1728
+ }
1729
+ VALUE ary = rb_ary_new4(size, vals);
1634
1730
 
1635
- return ary;
1731
+ for (size_t i = size; i < s->capacity; ++i)
1732
+ rb_ary_push(ary, Qnil);
1733
+
1734
+ return ary;
1735
+ } else {
1736
+ size_t index = FIX2INT(idx);
1737
+ if (index >= size) rb_raise(rb_eRangeError, "out of range");
1738
+
1739
+ return rubyobj_from_cval((char*)(s->a) + DTYPE_SIZES[s->dtype] * index, s->dtype).rval;
1740
+ }
1636
1741
  }
1637
1742
 
1638
1743
 
1639
1744
  /*
1640
1745
  * call-seq:
1641
1746
  * yale_d -> Array
1747
+ * yale_d(index) -> ...
1642
1748
  *
1643
1749
  * Get the diagonal ("D") portion of the A array of a Yale matrix.
1644
1750
  */
1645
- static VALUE nm_d(VALUE self) {
1751
+ static VALUE nm_d(int argc, VALUE* argv, VALUE self) {
1752
+ VALUE idx;
1753
+ rb_scan_args(argc, argv, "01", &idx);
1754
+
1646
1755
  YALE_STORAGE* s = NM_STORAGE_YALE(self);
1647
1756
 
1648
- VALUE* vals = ALLOCA_N(VALUE, s->shape[0]);
1757
+ if (idx == Qnil) {
1758
+ VALUE* vals = ALLOCA_N(VALUE, s->shape[0]);
1759
+
1760
+ for (size_t i = 0; i < s->shape[0]; ++i) {
1761
+ vals[i] = rubyobj_from_cval((char*)(s->a) + DTYPE_SIZES[s->dtype]*i, s->dtype).rval;
1762
+ }
1763
+ return rb_ary_new4(s->shape[0], vals);
1764
+ } else {
1765
+ size_t index = FIX2INT(idx);
1766
+ if (index >= s->shape[0]) rb_raise(rb_eRangeError, "out of range");
1649
1767
 
1650
- for (size_t i = 0; i < s->shape[0]; ++i) {
1651
- vals[i] = rubyobj_from_cval((char*)(s->a) + DTYPE_SIZES[s->dtype]*i, s->dtype).rval;
1768
+ return rubyobj_from_cval((char*)(s->a) + DTYPE_SIZES[s->dtype] * index, s->dtype).rval;
1652
1769
  }
1653
- return rb_ary_new4(s->shape[0], vals);
1654
1770
  }
1655
1771
 
1656
1772
  /*
@@ -1726,26 +1842,164 @@ static VALUE nm_ja(VALUE self) {
1726
1842
  /*
1727
1843
  * call-seq:
1728
1844
  * yale_ija -> Array
1845
+ * yale_ija(index) -> ...
1729
1846
  *
1730
- * Get the IJA array of a Yale matrix.
1847
+ * Get the IJA array of a Yale matrix (or a component of the IJA array).
1731
1848
  */
1732
- static VALUE nm_ija(VALUE self) {
1733
- YALE_STORAGE* s = NM_STORAGE_YALE(self);
1849
+ static VALUE nm_ija(int argc, VALUE* argv, VALUE self) {
1850
+ VALUE idx;
1851
+ rb_scan_args(argc, argv, "01", &idx);
1734
1852
 
1853
+ YALE_STORAGE* s = NM_STORAGE_YALE(self);
1735
1854
  size_t size = nm_yale_storage_get_size(s);
1736
1855
 
1737
- VALUE* vals = ALLOCA_N(VALUE, size);
1856
+ if (idx == Qnil) {
1738
1857
 
1739
- for (size_t i = 0; i < size; ++i) {
1740
- vals[i] = rubyobj_from_cval_by_itype((char*)(s->ija) + ITYPE_SIZES[s->itype]*i, s->itype).rval;
1858
+ VALUE* vals = ALLOCA_N(VALUE, size);
1859
+
1860
+ for (size_t i = 0; i < size; ++i) {
1861
+ vals[i] = rubyobj_from_cval_by_itype((char*)(s->ija) + ITYPE_SIZES[s->itype]*i, s->itype).rval;
1862
+ }
1863
+
1864
+ VALUE ary = rb_ary_new4(size, vals);
1865
+
1866
+ for (size_t i = size; i < s->capacity; ++i)
1867
+ rb_ary_push(ary, Qnil);
1868
+
1869
+ return ary;
1870
+
1871
+ } else {
1872
+ size_t index = FIX2INT(idx);
1873
+ if (index >= size) rb_raise(rb_eRangeError, "out of range");
1874
+
1875
+ return rubyobj_from_cval_by_itype((char*)(s->ija) + ITYPE_SIZES[s->itype] * index, s->itype).rval;
1741
1876
  }
1877
+ }
1742
1878
 
1743
- VALUE ary = rb_ary_new4(size, vals);
1744
1879
 
1745
- for (size_t i = size; i < s->capacity; ++i)
1746
- rb_ary_push(ary, Qnil);
1880
+ /*
1881
+ * call-seq:
1882
+ * yale_nd_row -> ...
1883
+ *
1884
+ * This function gets the non-diagonal contents of a Yale matrix row.
1885
+ * The first argument should be the row index. The optional second argument may be :hash or :array, but defaults
1886
+ * to :hash. If :array is given, it will only return the Hash keys (the column indices).
1887
+ *
1888
+ * This function is meant to accomplish its purpose as efficiently as possible. It does not check for appropriate
1889
+ * range.
1890
+ *
1891
+ * FIXME: :array doesn't make sense. This should be :keys or :values to indicate which array we want.
1892
+ */
1893
+ static VALUE nm_nd_row(int argc, VALUE* argv, VALUE self) {
1894
+ VALUE i_, as;
1895
+ rb_scan_args(argc, argv, "11", &i_, &as);
1747
1896
 
1748
- return ary;
1897
+ bool array = false;
1898
+ if (as != Qnil && rb_to_id(as) != nm_rb_hash) array = true;
1899
+
1900
+ size_t i = FIX2INT(i_);
1901
+
1902
+ YALE_STORAGE* s = NM_STORAGE_YALE(self);
1903
+ nm::dtype_t dtype = NM_DTYPE(self);
1904
+ nm::itype_t itype = NM_ITYPE(self);
1905
+
1906
+ // get the position as a size_t
1907
+ // TODO: Come up with a faster way to get this than transforming to a Ruby object first.
1908
+ size_t pos = FIX2INT(rubyobj_from_cval_by_itype((char*)(s->ija) + ITYPE_SIZES[itype]*i, itype).rval);
1909
+ size_t nextpos = FIX2INT(rubyobj_from_cval_by_itype((char*)(s->ija) + ITYPE_SIZES[itype]*(i+1), itype).rval);
1910
+ size_t diff = nextpos - pos;
1911
+
1912
+ //std::cerr << "diff = " << diff << "\tpos = " << pos << "\tnextpos = " << nextpos << std::endl;
1913
+
1914
+ VALUE ret; // HERE
1915
+ if (array) {
1916
+ ret = rb_ary_new3(diff);
1917
+
1918
+ for (size_t idx = pos; idx < nextpos; ++idx) {
1919
+ rb_ary_store(ret, idx - pos, rubyobj_from_cval_by_itype((char*)(s->ija) + ITYPE_SIZES[s->itype]*idx, s->itype).rval);
1920
+ }
1921
+
1922
+ } else {
1923
+ ret = rb_hash_new();
1924
+
1925
+ for (size_t idx = pos; idx < nextpos; ++idx) {
1926
+ rb_hash_aset(ret, rubyobj_from_cval_by_itype((char*)(s->ija) + ITYPE_SIZES[s->itype]*idx, s->itype).rval,
1927
+ rubyobj_from_cval((char*)(s->a) + DTYPE_SIZES[s->dtype]*idx, s->dtype).rval);
1928
+ }
1929
+ }
1930
+
1931
+ return ret;
1932
+ }
1933
+
1934
+ /*
1935
+ * call-seq:
1936
+ * yale_vector_insert -> Fixnum
1937
+ *
1938
+ * Insert at position pos an array of non-diagonal elements with column indices given. Note that the column indices and values
1939
+ * must be storage-contiguous -- that is, you can't insert them around existing elements in some row, only amid some
1940
+ * elements in some row. You *can* insert them around a diagonal element, since this is stored separately. This function
1941
+ * may not be used for the insertion of diagonal elements in most cases, as these are already present in the data
1942
+ * structure and are typically modified by replacement rather than insertion.
1943
+ *
1944
+ * The last argument, pos, may be nil if you want to insert at the beginning of a row. Otherwise it needs to be provided.
1945
+ * Don't expect this function to know the difference. It really does very little checking, because its goal is to make
1946
+ * multiple contiguous insertion as quick as possible.
1947
+ *
1948
+ * You should also not attempt to insert values which are the default (0). These are not supposed to be stored, and may
1949
+ * lead to undefined behavior.
1950
+ *
1951
+ * Example:
1952
+ * m.yale_vector_insert(3, [0,3,4], [1,1,1], 15)
1953
+ *
1954
+ * The example above inserts the values 1, 1, and 1 in columns 0, 3, and 4, assumed to be located at position 15 (which
1955
+ * corresponds to row 3).
1956
+ *
1957
+ * Example:
1958
+ * next = m.yale_vector_insert(3, [0,3,4], [1,1,1])
1959
+ *
1960
+ * This example determines that i=3 is at position 15 automatically. The value returned, next, is the position where the
1961
+ * next value(s) should be inserted.
1962
+ */
1963
+ static VALUE nm_vector_insert(int argc, VALUE* argv, VALUE self) { //, VALUE i_, VALUE jv, VALUE vv, VALUE pos_) {
1964
+
1965
+ // i, jv, vv are mandatory; pos is optional; thus "31"
1966
+ VALUE i_, jv, vv, pos_;
1967
+ rb_scan_args(argc, argv, "31", &i_, &jv, &vv, &pos_);
1968
+
1969
+ size_t len = RARRAY_LEN(jv); // need length in order to read the arrays in
1970
+ size_t vvlen = RARRAY_LEN(vv);
1971
+ if (len != vvlen)
1972
+ rb_raise(rb_eArgError, "lengths must match between j array (%d) and value array (%d)", len, vvlen);
1973
+
1974
+ YALE_STORAGE* s = NM_STORAGE_YALE(self);
1975
+ nm::dtype_t dtype = NM_DTYPE(self);
1976
+ nm::itype_t itype = NM_ITYPE(self);
1977
+
1978
+ size_t i = FIX2INT(i_); // get the row
1979
+
1980
+ // get the position as a size_t
1981
+ // TODO: Come up with a faster way to get this than transforming to a Ruby object first.
1982
+ if (pos_ == Qnil) pos_ = rubyobj_from_cval_by_itype((char*)(s->ija) + ITYPE_SIZES[itype]*i, itype).rval;
1983
+ size_t pos = FIX2INT(pos_);
1984
+
1985
+ // Allocate the j array and the values array
1986
+ size_t* j = ALLOCA_N(size_t, len);
1987
+ void* vals = ALLOCA_N(char, DTYPE_SIZES[dtype] * len);
1988
+
1989
+ // Copy array contents
1990
+ for (size_t idx = 0; idx < len; ++idx) {
1991
+ j[idx] = FIX2INT(rb_ary_entry(jv, idx));
1992
+ rubyval_to_cval(rb_ary_entry(vv, idx), dtype, (char*)vals + idx * DTYPE_SIZES[dtype]);
1993
+ }
1994
+
1995
+ char ins_type = nm_yale_storage_vector_insert(s, pos, j, vals, len, false, dtype, itype);
1996
+ nm_yale_storage_increment_ia_after(s, s->shape[0], i, len, itype);
1997
+ s->ndnz += len;
1998
+
1999
+ // Return the updated position
2000
+ pos += len;
2001
+ return INT2FIX(pos);
1749
2002
  }
1750
2003
 
2004
+
1751
2005
  } // end of extern "C" block