nmatrix 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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