nmatrix 0.0.8 → 0.0.9
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.
- checksums.yaml +4 -4
- data/.gitignore +3 -8
- data/.rspec +1 -1
- data/.travis.yml +12 -0
- data/CONTRIBUTING.md +27 -12
- data/Gemfile +1 -0
- data/History.txt +38 -0
- data/Manifest.txt +15 -15
- data/README.rdoc +7 -6
- data/Rakefile +40 -5
- data/ext/nmatrix/data/data.cpp +2 -37
- data/ext/nmatrix/data/data.h +19 -121
- data/ext/nmatrix/data/meta.h +70 -0
- data/ext/nmatrix/extconf.rb +40 -12
- data/ext/nmatrix/math/math.h +13 -103
- data/ext/nmatrix/nmatrix.cpp +10 -2018
- data/ext/nmatrix/nmatrix.h +16 -13
- data/ext/nmatrix/ruby_constants.cpp +12 -1
- data/ext/nmatrix/ruby_constants.h +7 -1
- data/ext/nmatrix/ruby_nmatrix.c +2169 -0
- data/ext/nmatrix/storage/dense.cpp +123 -14
- data/ext/nmatrix/storage/dense.h +10 -4
- data/ext/nmatrix/storage/list.cpp +265 -48
- data/ext/nmatrix/storage/list.h +6 -9
- data/ext/nmatrix/storage/storage.cpp +44 -54
- data/ext/nmatrix/storage/storage.h +2 -2
- data/ext/nmatrix/storage/yale/class.h +1070 -0
- data/ext/nmatrix/storage/yale/iterators/base.h +142 -0
- data/ext/nmatrix/storage/yale/iterators/iterator.h +130 -0
- data/ext/nmatrix/storage/yale/iterators/row.h +449 -0
- data/ext/nmatrix/storage/yale/iterators/row_stored.h +139 -0
- data/ext/nmatrix/storage/yale/iterators/row_stored_nd.h +167 -0
- data/ext/nmatrix/storage/yale/iterators/stored_diagonal.h +123 -0
- data/ext/nmatrix/storage/yale/math/transpose.h +110 -0
- data/ext/nmatrix/storage/yale/yale.cpp +1785 -0
- data/ext/nmatrix/storage/{yale.h → yale/yale.h} +23 -55
- data/ext/nmatrix/types.h +2 -0
- data/ext/nmatrix/util/io.cpp +27 -45
- data/ext/nmatrix/util/io.h +0 -2
- data/ext/nmatrix/util/sl_list.cpp +169 -28
- data/ext/nmatrix/util/sl_list.h +9 -3
- data/lib/nmatrix/blas.rb +20 -20
- data/lib/nmatrix/enumerate.rb +1 -1
- data/lib/nmatrix/io/mat5_reader.rb +8 -14
- data/lib/nmatrix/lapack.rb +3 -3
- data/lib/nmatrix/math.rb +3 -3
- data/lib/nmatrix/nmatrix.rb +19 -5
- data/lib/nmatrix/nvector.rb +2 -0
- data/lib/nmatrix/shortcuts.rb +90 -125
- data/lib/nmatrix/version.rb +1 -1
- data/nmatrix.gemspec +7 -8
- data/spec/{nmatrix_spec.rb → 00_nmatrix_spec.rb} +45 -208
- data/spec/01_enum_spec.rb +184 -0
- data/spec/{slice_spec.rb → 02_slice_spec.rb} +55 -39
- data/spec/blas_spec.rb +22 -54
- data/spec/elementwise_spec.rb +9 -8
- data/spec/io_spec.rb +6 -4
- data/spec/lapack_spec.rb +26 -26
- data/spec/math_spec.rb +9 -5
- data/spec/nmatrix_yale_spec.rb +29 -61
- data/spec/shortcuts_spec.rb +34 -22
- data/spec/slice_set_spec.rb +157 -0
- data/spec/spec_helper.rb +42 -2
- data/spec/stat_spec.rb +192 -0
- metadata +52 -55
- data/ext/nmatrix/storage/yale.cpp +0 -2284
- data/spec/nmatrix_list_spec.rb +0 -113
- data/spec/nvector_spec.rb +0 -112
@@ -1,2284 +0,0 @@
|
|
1
|
-
/////////////////////////////////////////////////////////////////////
|
2
|
-
// = NMatrix
|
3
|
-
//
|
4
|
-
// A linear algebra library for scientific computation in Ruby.
|
5
|
-
// NMatrix is part of SciRuby.
|
6
|
-
//
|
7
|
-
// NMatrix was originally inspired by and derived from NArray, by
|
8
|
-
// Masahiro Tanaka: http://narray.rubyforge.org
|
9
|
-
//
|
10
|
-
// == Copyright Information
|
11
|
-
//
|
12
|
-
// SciRuby is Copyright (c) 2010 - 2013, Ruby Science Foundation
|
13
|
-
// NMatrix is Copyright (c) 2013, Ruby Science Foundation
|
14
|
-
//
|
15
|
-
// Please see LICENSE.txt for additional copyright notices.
|
16
|
-
//
|
17
|
-
// == Contributing
|
18
|
-
//
|
19
|
-
// By contributing source code to SciRuby, you agree to be bound by
|
20
|
-
// our Contributor Agreement:
|
21
|
-
//
|
22
|
-
// * https://github.com/SciRuby/sciruby/wiki/Contributor-Agreement
|
23
|
-
//
|
24
|
-
// == yale.c
|
25
|
-
//
|
26
|
-
// "new yale" storage format for 2D matrices (like yale, but with
|
27
|
-
// the diagonal pulled out for O(1) access).
|
28
|
-
//
|
29
|
-
// Specifications:
|
30
|
-
// * dtype and index dtype must necessarily differ
|
31
|
-
// * index dtype is defined by whatever unsigned type can store
|
32
|
-
// max(rows,cols)
|
33
|
-
// * that means vector ija stores only index dtype, but a stores
|
34
|
-
// dtype
|
35
|
-
// * vectors must be able to grow as necessary
|
36
|
-
// * maximum size is rows*cols+1
|
37
|
-
|
38
|
-
/*
|
39
|
-
* Standard Includes
|
40
|
-
*/
|
41
|
-
|
42
|
-
#include <ruby.h>
|
43
|
-
#include <algorithm> // std::min
|
44
|
-
#include <cstdio> // std::fprintf
|
45
|
-
#include <iostream>
|
46
|
-
#include <array>
|
47
|
-
#include <typeinfo>
|
48
|
-
|
49
|
-
#define RB_P(OBJ) \
|
50
|
-
rb_funcall(rb_stderr, rb_intern("print"), 1, rb_funcall(OBJ, rb_intern("object_id"), 0)); \
|
51
|
-
rb_funcall(rb_stderr, rb_intern("puts"), 1, rb_funcall(OBJ, rb_intern("inspect"), 0));
|
52
|
-
|
53
|
-
/*
|
54
|
-
* Project Includes
|
55
|
-
*/
|
56
|
-
|
57
|
-
// #include "types.h"
|
58
|
-
#include "data/data.h"
|
59
|
-
#include "math/math.h"
|
60
|
-
|
61
|
-
#include "common.h"
|
62
|
-
#include "yale.h"
|
63
|
-
|
64
|
-
#include "nmatrix.h"
|
65
|
-
#include "ruby_constants.h"
|
66
|
-
|
67
|
-
/*
|
68
|
-
* Macros
|
69
|
-
*/
|
70
|
-
|
71
|
-
#ifndef NM_MAX
|
72
|
-
#define NM_MAX(a,b) (((a)>(b))?(a):(b))
|
73
|
-
#define NM_MIN(a,b) (((a)<(b))?(a):(b))
|
74
|
-
#endif
|
75
|
-
|
76
|
-
#ifndef NM_MAX_ITYPE
|
77
|
-
#define NM_MAX_ITYPE(a,b) ((static_cast<int8_t>(a) > static_cast<int8_t>(b)) ? static_cast<nm::itype_t>(a) : static_cast<nm::itype_t>(b))
|
78
|
-
#define NM_MIN_ITYPE(a,b) ((static_cast<int8_t>(a) < static_cast<int8_t>(b)) ? static_cast<nm::itype_t>(a) : static_cast<nm::itype_t>(b))
|
79
|
-
#endif
|
80
|
-
|
81
|
-
/*
|
82
|
-
* Forward Declarations
|
83
|
-
*/
|
84
|
-
|
85
|
-
extern "C" {
|
86
|
-
static YALE_STORAGE* nm_copy_alloc_struct(const YALE_STORAGE* rhs, const nm::dtype_t new_dtype, const size_t new_capacity, const size_t new_size);
|
87
|
-
static YALE_STORAGE* alloc(nm::dtype_t dtype, size_t* shape, size_t dim, nm::itype_t min_itype);
|
88
|
-
|
89
|
-
static size_t yale_count_slice_copy_ndnz(const YALE_STORAGE* s, size_t*, size_t*);
|
90
|
-
|
91
|
-
static void* default_value_ptr(const YALE_STORAGE* s);
|
92
|
-
static VALUE default_value(const YALE_STORAGE* s);
|
93
|
-
static VALUE obj_at(YALE_STORAGE* s, size_t k);
|
94
|
-
|
95
|
-
/* Ruby-accessible functions */
|
96
|
-
static VALUE nm_size(VALUE self);
|
97
|
-
static VALUE nm_a(int argc, VALUE* argv, VALUE self);
|
98
|
-
static VALUE nm_d(int argc, VALUE* argv, VALUE self);
|
99
|
-
static VALUE nm_lu(VALUE self);
|
100
|
-
static VALUE nm_ia(VALUE self);
|
101
|
-
static VALUE nm_ja(VALUE self);
|
102
|
-
static VALUE nm_ija(int argc, VALUE* argv, VALUE self);
|
103
|
-
|
104
|
-
static VALUE nm_nd_row(int argc, VALUE* argv, VALUE self);
|
105
|
-
|
106
|
-
static inline size_t src_ndnz(const YALE_STORAGE* s) {
|
107
|
-
return reinterpret_cast<YALE_STORAGE*>(s->src)->ndnz;
|
108
|
-
}
|
109
|
-
|
110
|
-
} // end extern "C" block
|
111
|
-
|
112
|
-
namespace nm { namespace yale_storage {
|
113
|
-
|
114
|
-
template <typename DType, typename IType>
|
115
|
-
static bool ndrow_is_empty(const YALE_STORAGE* s, IType ija, const IType ija_next);
|
116
|
-
|
117
|
-
template <typename LDType, typename RDType, typename IType>
|
118
|
-
static bool ndrow_eqeq_ndrow(const YALE_STORAGE* l, const YALE_STORAGE* r, IType l_ija, const IType l_ija_next, IType r_ija, const IType r_ija_next);
|
119
|
-
|
120
|
-
template <typename LDType, typename RDType, typename IType>
|
121
|
-
static bool eqeq(const YALE_STORAGE* left, const YALE_STORAGE* right);
|
122
|
-
|
123
|
-
template <typename LDType, typename RDType, typename IType>
|
124
|
-
static bool eqeq_different_defaults(const YALE_STORAGE* s, const LDType& s_init, const YALE_STORAGE* t, const RDType& t_init);
|
125
|
-
|
126
|
-
template <typename IType>
|
127
|
-
static YALE_STORAGE* copy_alloc_struct(const YALE_STORAGE* rhs, const dtype_t new_dtype, const size_t new_capacity, const size_t new_size);
|
128
|
-
|
129
|
-
template <typename IType>
|
130
|
-
static void increment_ia_after(YALE_STORAGE* s, IType ija_size, IType i, IType n);
|
131
|
-
|
132
|
-
template <typename IType>
|
133
|
-
static void c_increment_ia_after(YALE_STORAGE* s, size_t ija_size, size_t i, size_t n) {
|
134
|
-
increment_ia_after<IType>(s, ija_size, i, n);
|
135
|
-
}
|
136
|
-
|
137
|
-
template <typename IType>
|
138
|
-
static IType insert_search(YALE_STORAGE* s, IType left, IType right, IType key, bool* found);
|
139
|
-
|
140
|
-
template <typename DType, typename IType>
|
141
|
-
static char vector_insert(YALE_STORAGE* s, size_t pos, size_t* j, void* val_, size_t n, bool struct_only);
|
142
|
-
|
143
|
-
template <typename DType, typename IType>
|
144
|
-
static char vector_insert_resize(YALE_STORAGE* s, size_t current_size, size_t pos, size_t* j, size_t n, bool struct_only);
|
145
|
-
|
146
|
-
|
147
|
-
/*
|
148
|
-
* Functions
|
149
|
-
*/
|
150
|
-
|
151
|
-
/*
|
152
|
-
* Copy a vector from one IType or DType to another.
|
153
|
-
*/
|
154
|
-
template <typename LType, typename RType>
|
155
|
-
static inline void copy_recast_vector(const void* in_, void* out_, size_t length) {
|
156
|
-
const RType* in = reinterpret_cast<const RType*>(in_);
|
157
|
-
LType* out = reinterpret_cast<LType*>(out_);
|
158
|
-
for (size_t i = 0; i < length; ++i) {
|
159
|
-
out[i] = in[i];
|
160
|
-
}
|
161
|
-
out;
|
162
|
-
}
|
163
|
-
|
164
|
-
|
165
|
-
static inline void copy_recast_itype_vector(const void* in, nm::itype_t in_itype, void* out, nm::itype_t out_itype, size_t length) {
|
166
|
-
NAMED_LR_ITYPE_TEMPLATE_TABLE(ttable, copy_recast_vector, void, const void* in_, void* out_, size_t length);
|
167
|
-
|
168
|
-
ttable[out_itype][in_itype](in, out, length);
|
169
|
-
}
|
170
|
-
|
171
|
-
|
172
|
-
/*
|
173
|
-
* Create Yale storage from IA, JA, and A vectors given in Old Yale format (probably from a file, since NMatrix only uses
|
174
|
-
* new Yale for its storage).
|
175
|
-
*
|
176
|
-
* This function is needed for Matlab .MAT v5 IO.
|
177
|
-
*/
|
178
|
-
template <typename LDType, typename RDType, typename IType>
|
179
|
-
YALE_STORAGE* create_from_old_yale(dtype_t dtype, size_t* shape, void* r_ia, void* r_ja, void* r_a) {
|
180
|
-
IType* ir = reinterpret_cast<IType*>(r_ia);
|
181
|
-
IType* jr = reinterpret_cast<IType*>(r_ja);
|
182
|
-
RDType* ar = reinterpret_cast<RDType*>(r_a);
|
183
|
-
|
184
|
-
// Read through ia and ja and figure out the ndnz (non-diagonal non-zeros) count.
|
185
|
-
size_t ndnz = 0, i, p, p_next;
|
186
|
-
|
187
|
-
for (i = 0; i < shape[0]; ++i) { // Walk down rows
|
188
|
-
for (p = ir[i], p_next = ir[i+1]; p < p_next; ++p) { // Now walk through columns
|
189
|
-
|
190
|
-
if (i != jr[p]) ++ndnz; // entry is non-diagonal and probably nonzero
|
191
|
-
|
192
|
-
}
|
193
|
-
}
|
194
|
-
|
195
|
-
// Having walked through the matrix, we now go about allocating the space for it.
|
196
|
-
YALE_STORAGE* s = alloc(dtype, shape, 2, UINT8);
|
197
|
-
|
198
|
-
s->capacity = shape[0] + ndnz + 1;
|
199
|
-
s->ndnz = ndnz;
|
200
|
-
|
201
|
-
// Setup IJA and A arrays
|
202
|
-
s->ija = ALLOC_N( IType, s->capacity );
|
203
|
-
s->a = ALLOC_N( LDType, s->capacity );
|
204
|
-
IType* ijl = reinterpret_cast<IType*>(s->ija);
|
205
|
-
LDType* al = reinterpret_cast<LDType*>(s->a);
|
206
|
-
|
207
|
-
// set the diagonal to zero -- this prevents uninitialized values from popping up.
|
208
|
-
for (size_t index = 0; index < shape[0]; ++index) {
|
209
|
-
al[index] = 0;
|
210
|
-
}
|
211
|
-
|
212
|
-
// Figure out where to start writing JA in IJA:
|
213
|
-
size_t pp = s->shape[0]+1;
|
214
|
-
|
215
|
-
// Find beginning of first row
|
216
|
-
p = ir[0];
|
217
|
-
|
218
|
-
// Now fill the arrays
|
219
|
-
for (i = 0; i < s->shape[0]; ++i) {
|
220
|
-
|
221
|
-
// Set the beginning of the row (of output)
|
222
|
-
ijl[i] = pp;
|
223
|
-
|
224
|
-
// Now walk through columns, starting at end of row (of input)
|
225
|
-
for (size_t p_next = ir[i+1]; p < p_next; ++p, ++pp) {
|
226
|
-
|
227
|
-
if (i == jr[p]) { // diagonal
|
228
|
-
|
229
|
-
al[i] = ar[p];
|
230
|
-
--pp;
|
231
|
-
|
232
|
-
} else { // nondiagonal
|
233
|
-
|
234
|
-
ijl[pp] = jr[p];
|
235
|
-
al[pp] = ar[p];
|
236
|
-
|
237
|
-
}
|
238
|
-
}
|
239
|
-
}
|
240
|
-
|
241
|
-
ijl[i] = pp; // Set the end of the last row
|
242
|
-
|
243
|
-
// Set the zero position for our output matrix
|
244
|
-
al[i] = 0;
|
245
|
-
|
246
|
-
return s;
|
247
|
-
}
|
248
|
-
|
249
|
-
|
250
|
-
/*
|
251
|
-
* Empty the matrix by initializing the IJA vector and setting the diagonal to 0.
|
252
|
-
*
|
253
|
-
* Called when most YALE_STORAGE objects are created.
|
254
|
-
*/
|
255
|
-
template <typename DType, typename IType>
|
256
|
-
void init(YALE_STORAGE* s, void* init_val) {
|
257
|
-
IType IA_INIT = s->shape[0] + 1;
|
258
|
-
|
259
|
-
IType* ija = reinterpret_cast<IType*>(s->ija);
|
260
|
-
// clear out IJA vector
|
261
|
-
for (IType i = 0; i < IA_INIT; ++i) {
|
262
|
-
ija[i] = IA_INIT; // set initial values for IJA
|
263
|
-
}
|
264
|
-
|
265
|
-
clear_diagonal_and_zero<DType>(s, init_val);
|
266
|
-
}
|
267
|
-
|
268
|
-
size_t max_size(YALE_STORAGE* s) {
|
269
|
-
size_t result = s->shape[0]*s->shape[1] + 1;
|
270
|
-
if (s->shape[0] > s->shape[1])
|
271
|
-
result += s->shape[0] - s->shape[1];
|
272
|
-
|
273
|
-
return result;
|
274
|
-
}
|
275
|
-
|
276
|
-
|
277
|
-
///////////////
|
278
|
-
// Accessors //
|
279
|
-
///////////////
|
280
|
-
|
281
|
-
|
282
|
-
/*
|
283
|
-
* Determine the number of non-diagonal non-zeros in a not-yet-created copy of a slice or matrix.
|
284
|
-
*/
|
285
|
-
template <typename DType, typename IType>
|
286
|
-
static size_t count_slice_copy_ndnz(const YALE_STORAGE* s, size_t* offset, size_t* shape) {
|
287
|
-
IType* ija = reinterpret_cast<IType*>(s->ija);
|
288
|
-
DType* a = reinterpret_cast<DType*>(s->a);
|
289
|
-
|
290
|
-
DType ZERO(*reinterpret_cast<DType*>(default_value_ptr(s)));
|
291
|
-
|
292
|
-
// Calc ndnz for the destination
|
293
|
-
size_t ndnz = 0;
|
294
|
-
size_t i, j; // indexes of destination matrix
|
295
|
-
size_t k, l; // indexes of source matrix
|
296
|
-
for (i = 0; i < shape[0]; i++) {
|
297
|
-
k = i + offset[0];
|
298
|
-
for (j = 0; j < shape[1]; j++) {
|
299
|
-
l = j + offset[1];
|
300
|
-
|
301
|
-
if (j == i) continue;
|
302
|
-
|
303
|
-
if (k == l) { // for diagonal element of source
|
304
|
-
if (a[k] != ZERO) ++ndnz;
|
305
|
-
} else { // for non-diagonal element
|
306
|
-
for (size_t c = ija[k]; c < ija[k+1]; c++) {
|
307
|
-
if (ija[c] == l) {
|
308
|
-
++ndnz;
|
309
|
-
break;
|
310
|
-
}
|
311
|
-
}
|
312
|
-
}
|
313
|
-
}
|
314
|
-
}
|
315
|
-
|
316
|
-
return ndnz;
|
317
|
-
}
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
/*
|
322
|
-
* Copy some portion of a matrix into a new matrix.
|
323
|
-
*/
|
324
|
-
template <typename LDType, typename RDType, typename IType>
|
325
|
-
static void slice_copy(YALE_STORAGE* ns, const YALE_STORAGE* s, size_t* offset, size_t* lengths, dtype_t new_dtype) {
|
326
|
-
|
327
|
-
IType* src_ija = reinterpret_cast<IType*>(s->ija);
|
328
|
-
RDType* src_a = reinterpret_cast<RDType*>(s->a);
|
329
|
-
|
330
|
-
RDType RZERO(*reinterpret_cast<RDType*>(default_value_ptr(s)));
|
331
|
-
|
332
|
-
// Initialize the A and IJA arrays
|
333
|
-
LDType val(RZERO); // need default value for init. Can't use ns default value because it's not initialized yet
|
334
|
-
init<LDType,IType>(ns, &val);
|
335
|
-
IType* dst_ija = reinterpret_cast<IType*>(ns->ija);
|
336
|
-
LDType* dst_a = reinterpret_cast<LDType*>(ns->a);
|
337
|
-
|
338
|
-
size_t ija = lengths[0] + 1;
|
339
|
-
|
340
|
-
size_t i, j; // indexes of destination matrix
|
341
|
-
size_t k, l; // indexes of source matrix
|
342
|
-
|
343
|
-
for (i = 0; i < lengths[0]; ++i) {
|
344
|
-
k = i + offset[0];
|
345
|
-
for (j = 0; j < lengths[1]; ++j) {
|
346
|
-
bool found = false;
|
347
|
-
l = j + offset[1];
|
348
|
-
|
349
|
-
// Get value from source matrix
|
350
|
-
if (k == l) { // source diagonal
|
351
|
-
if (src_a[k] != RZERO) { // don't bother copying non-zero values from the diagonal
|
352
|
-
val = src_a[k];
|
353
|
-
found = true;
|
354
|
-
}
|
355
|
-
} else {
|
356
|
-
// copy one non-diagonal element
|
357
|
-
for (size_t c = src_ija[k]; !found && c < src_ija[k+1]; ++c) {
|
358
|
-
if (src_ija[c] == l) {
|
359
|
-
val = src_a[c];
|
360
|
-
found = true;
|
361
|
-
}
|
362
|
-
}
|
363
|
-
}
|
364
|
-
|
365
|
-
if (found) {
|
366
|
-
// Set value in destination matrix
|
367
|
-
if (i == j) {
|
368
|
-
dst_a[i] = val;
|
369
|
-
} else {
|
370
|
-
// copy non-diagonal element
|
371
|
-
dst_ija[ija] = j;
|
372
|
-
dst_a[ija] = val;
|
373
|
-
++ija;
|
374
|
-
for (size_t c = i + 1; c <= lengths[0]; ++c) {
|
375
|
-
dst_ija[c] = ija;
|
376
|
-
}
|
377
|
-
}
|
378
|
-
}
|
379
|
-
}
|
380
|
-
}
|
381
|
-
|
382
|
-
dst_ija[lengths[0]] = ija; // indicate the end of the last row
|
383
|
-
ns->ndnz = ija - lengths[0] - 1; // update ndnz count
|
384
|
-
}
|
385
|
-
|
386
|
-
|
387
|
-
/*
|
388
|
-
* Get a single element of a yale storage object
|
389
|
-
*/
|
390
|
-
template <typename DType, typename IType>
|
391
|
-
static void* get_single(YALE_STORAGE* storage, SLICE* slice) {
|
392
|
-
|
393
|
-
DType* a = reinterpret_cast<DType*>(storage->a);
|
394
|
-
IType* ija = reinterpret_cast<IType*>(storage->ija);
|
395
|
-
|
396
|
-
size_t coord0 = storage->offset[0] + slice->coords[0];
|
397
|
-
size_t coord1 = storage->offset[1] + slice->coords[1];
|
398
|
-
|
399
|
-
if (coord0 == coord1)
|
400
|
-
return &(a[ coord0 ]); // return diagonal entry
|
401
|
-
|
402
|
-
if (ija[coord0] == ija[coord0+1])
|
403
|
-
return &(a[ storage->src->shape[0] ]); // return zero pointer
|
404
|
-
|
405
|
-
// binary search for the column's location
|
406
|
-
int pos = binary_search<IType>(storage, ija[coord0], ija[coord0+1]-1, coord1);
|
407
|
-
|
408
|
-
if (pos != -1 && ija[pos] == coord1)
|
409
|
-
return &(a[pos]); // found exact value
|
410
|
-
|
411
|
-
return &(a[ storage->src->shape[0] ]); // return a pointer that happens to be zero
|
412
|
-
}
|
413
|
-
|
414
|
-
|
415
|
-
/*
|
416
|
-
* Returns a pointer to the correct location in the A vector of a YALE_STORAGE object, given some set of coordinates
|
417
|
-
* (the coordinates are stored in slice).
|
418
|
-
*/
|
419
|
-
template <typename DType,typename IType>
|
420
|
-
void* ref(YALE_STORAGE* s, SLICE* slice) {
|
421
|
-
|
422
|
-
YALE_STORAGE* ns = ALLOC( YALE_STORAGE );
|
423
|
-
|
424
|
-
ns->dim = s->dim;
|
425
|
-
ns->offset = ALLOC_N(size_t, ns->dim);
|
426
|
-
ns->shape = ALLOC_N(size_t, ns->dim);
|
427
|
-
|
428
|
-
for (size_t i = 0; i < ns->dim; ++i) {
|
429
|
-
ns->offset[i] = slice->coords[i] + s->offset[i];
|
430
|
-
ns->shape[i] = slice->lengths[i];
|
431
|
-
}
|
432
|
-
|
433
|
-
ns->dtype = s->dtype;
|
434
|
-
ns->itype = s->itype; // or should we go by shape?
|
435
|
-
|
436
|
-
ns->a = s->a;
|
437
|
-
ns->ija = s->ija;
|
438
|
-
|
439
|
-
ns->src = s->src;
|
440
|
-
s->src->count++;
|
441
|
-
|
442
|
-
ns->ndnz = 0;
|
443
|
-
ns->capacity= 0;
|
444
|
-
|
445
|
-
return ns;
|
446
|
-
|
447
|
-
}
|
448
|
-
|
449
|
-
/*
|
450
|
-
* Attempt to set some cell in a YALE_STORAGE object. Must supply coordinates and a pointer to a value (which will be
|
451
|
-
* copied into the storage object).
|
452
|
-
*/
|
453
|
-
template <typename DType, typename IType>
|
454
|
-
char set(YALE_STORAGE* storage, SLICE* slice, void* value) {
|
455
|
-
DType* v = reinterpret_cast<DType*>(value);
|
456
|
-
size_t coord0 = storage->offset[0] + slice->coords[0],
|
457
|
-
coord1 = storage->offset[1] + slice->coords[1];
|
458
|
-
|
459
|
-
bool found = false;
|
460
|
-
char ins_type;
|
461
|
-
|
462
|
-
if (coord0 == coord1) {
|
463
|
-
reinterpret_cast<DType*>(storage->a)[coord0] = *v; // set diagonal
|
464
|
-
return 'r';
|
465
|
-
}
|
466
|
-
|
467
|
-
// Get IJA positions of the beginning and end of the row
|
468
|
-
if (reinterpret_cast<IType*>(storage->ija)[coord0] == reinterpret_cast<IType*>(storage->ija)[coord0+1]) {
|
469
|
-
// empty row
|
470
|
-
ins_type = vector_insert<DType,IType>(storage, reinterpret_cast<IType*>(storage->ija)[coord0], &(coord1), v, 1, false);
|
471
|
-
increment_ia_after<IType>(storage, storage->shape[0], coord0, 1);
|
472
|
-
reinterpret_cast<YALE_STORAGE*>(storage->src)->ndnz++;
|
473
|
-
|
474
|
-
return ins_type;
|
475
|
-
}
|
476
|
-
|
477
|
-
// non-empty row. search for coords[1] in the IJA array, between ija and ija_next
|
478
|
-
// (including ija, not including ija_next)
|
479
|
-
//ija_size = get_size<IType>(storage);
|
480
|
-
|
481
|
-
// Do a binary search for the column
|
482
|
-
size_t pos = insert_search<IType>(storage,
|
483
|
-
reinterpret_cast<IType*>(storage->ija)[coord0],
|
484
|
-
reinterpret_cast<IType*>(storage->ija)[coord0+1]-1,
|
485
|
-
coord1, &found);
|
486
|
-
|
487
|
-
if (found) { // replace
|
488
|
-
reinterpret_cast<IType*>(storage->ija)[pos] = coord1;
|
489
|
-
reinterpret_cast<DType*>(storage->a)[pos] = *v;
|
490
|
-
return 'r';
|
491
|
-
}
|
492
|
-
|
493
|
-
ins_type = vector_insert<DType,IType>(storage, pos, &(coord1), v, 1, false);
|
494
|
-
increment_ia_after<IType>(storage, storage->shape[0], coord0, 1);
|
495
|
-
reinterpret_cast<YALE_STORAGE*>(storage->src)->ndnz++;
|
496
|
-
|
497
|
-
return ins_type;
|
498
|
-
}
|
499
|
-
|
500
|
-
///////////
|
501
|
-
// Tests //
|
502
|
-
///////////
|
503
|
-
|
504
|
-
/*
|
505
|
-
* Yale eql? -- for whole-matrix comparison returning a single value.
|
506
|
-
*/
|
507
|
-
template <typename LDType, typename RDType, typename IType>
|
508
|
-
static bool eqeq(const YALE_STORAGE* left, const YALE_STORAGE* right) {
|
509
|
-
LDType l_init = reinterpret_cast<LDType*>(left->a )[left->shape[0] ];
|
510
|
-
RDType r_init = reinterpret_cast<RDType*>(right->a)[right->shape[0]];
|
511
|
-
|
512
|
-
// If the defaults are different between the two matrices, or if slicing is involved, use this other function instead:
|
513
|
-
if (l_init != r_init || left->src != left || right->src != right)
|
514
|
-
return eqeq_different_defaults<LDType,RDType,IType>(left, l_init, right, r_init);
|
515
|
-
|
516
|
-
LDType* la = reinterpret_cast<LDType*>(left->a);
|
517
|
-
RDType* ra = reinterpret_cast<RDType*>(right->a);
|
518
|
-
|
519
|
-
// Compare the diagonals first.
|
520
|
-
for (size_t index = 0; index < left->shape[0]; ++index) {
|
521
|
-
if (la[index] != ra[index]) return false;
|
522
|
-
}
|
523
|
-
|
524
|
-
IType* lij = reinterpret_cast<IType*>(left->ija);
|
525
|
-
IType* rij = reinterpret_cast<IType*>(right->ija);
|
526
|
-
|
527
|
-
for (IType i = 0; i < left->shape[0]; ++i) {
|
528
|
-
|
529
|
-
// Get start and end positions of row
|
530
|
-
IType l_ija = lij[i],
|
531
|
-
l_ija_next = lij[i+1],
|
532
|
-
r_ija = rij[i],
|
533
|
-
r_ija_next = rij[i+1];
|
534
|
-
|
535
|
-
// Check to see if one row is empty and the other isn't.
|
536
|
-
if (ndrow_is_empty<LDType,IType>(left, l_ija, l_ija_next)) {
|
537
|
-
if (!ndrow_is_empty<RDType,IType>(right, r_ija, r_ija_next)) {
|
538
|
-
return false;
|
539
|
-
}
|
540
|
-
|
541
|
-
} else if (ndrow_is_empty<RDType,IType>(right, r_ija, r_ija_next)) {
|
542
|
-
// one is empty but the other isn't
|
543
|
-
return false;
|
544
|
-
|
545
|
-
} else if (!ndrow_eqeq_ndrow<LDType,RDType,IType>(left, right, l_ija, l_ija_next, r_ija, r_ija_next)) {
|
546
|
-
// Neither row is empty. Must compare the rows directly.
|
547
|
-
return false;
|
548
|
-
}
|
549
|
-
|
550
|
-
}
|
551
|
-
|
552
|
-
return true;
|
553
|
-
}
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
/*
|
558
|
-
* Are two non-diagonal rows the same? We already know.
|
559
|
-
*/
|
560
|
-
template <typename LDType, typename RDType, typename IType>
|
561
|
-
static bool ndrow_eqeq_ndrow(const YALE_STORAGE* l, const YALE_STORAGE* r, IType l_ija, const IType l_ija_next, IType r_ija, const IType r_ija_next) {
|
562
|
-
bool l_no_more = false, r_no_more = false;
|
563
|
-
|
564
|
-
IType *lij = reinterpret_cast<IType*>(l->ija),
|
565
|
-
*rij = reinterpret_cast<IType*>(r->ija);
|
566
|
-
|
567
|
-
LDType* la = reinterpret_cast<LDType*>(l->a);
|
568
|
-
RDType* ra = reinterpret_cast<RDType*>(r->a);
|
569
|
-
|
570
|
-
IType l_ja = lij[l_ija],
|
571
|
-
r_ja = rij[r_ija];
|
572
|
-
|
573
|
-
IType ja = std::min(l_ja, r_ja);
|
574
|
-
|
575
|
-
LDType LZERO = la[l->shape[0]];
|
576
|
-
RDType RZERO = ra[r->shape[0]];
|
577
|
-
|
578
|
-
while (!(l_no_more && r_no_more)) {
|
579
|
-
if (l_ja == r_ja) {
|
580
|
-
|
581
|
-
if (ra[r_ija] != la[l_ija]) return false; // Direct comparison
|
582
|
-
|
583
|
-
++l_ija;
|
584
|
-
++r_ija;
|
585
|
-
|
586
|
-
if (l_ija < l_ija_next) {
|
587
|
-
l_ja = lij[l_ija];
|
588
|
-
|
589
|
-
} else {
|
590
|
-
l_no_more = true;
|
591
|
-
}
|
592
|
-
|
593
|
-
if (r_ija < r_ija_next) {
|
594
|
-
r_ja = rij[r_ija];
|
595
|
-
|
596
|
-
} else {
|
597
|
-
r_no_more = true;
|
598
|
-
}
|
599
|
-
|
600
|
-
ja = std::min(l_ja, r_ja);
|
601
|
-
|
602
|
-
} else if (l_no_more || ja < l_ja) {
|
603
|
-
|
604
|
-
if (ra[r_ija] != RZERO) return false;
|
605
|
-
|
606
|
-
++r_ija;
|
607
|
-
if (r_ija < r_ija_next) {
|
608
|
-
// get next column
|
609
|
-
r_ja = rij[r_ija];
|
610
|
-
ja = std::min(l_ja, r_ja);
|
611
|
-
|
612
|
-
} else {
|
613
|
-
l_no_more = true;
|
614
|
-
}
|
615
|
-
|
616
|
-
} else if (r_no_more || ja < r_ja) {
|
617
|
-
|
618
|
-
if (la[l_ija] != LZERO) return false;
|
619
|
-
|
620
|
-
++l_ija;
|
621
|
-
if (l_ija < l_ija_next) {
|
622
|
-
// get next column
|
623
|
-
l_ja = lij[l_ija];
|
624
|
-
ja = std::min(l_ja, r_ja);
|
625
|
-
} else {
|
626
|
-
l_no_more = true;
|
627
|
-
}
|
628
|
-
|
629
|
-
} else {
|
630
|
-
std::fprintf(stderr, "Unhandled in eqeq: l_ja=%d, r_ja=%d\n", (int)l_ja, (int)r_ja);
|
631
|
-
}
|
632
|
-
}
|
633
|
-
|
634
|
-
// every item matched
|
635
|
-
return true;
|
636
|
-
}
|
637
|
-
|
638
|
-
/*
|
639
|
-
* Is the non-diagonal portion of the row empty?
|
640
|
-
*/
|
641
|
-
template <typename DType, typename IType>
|
642
|
-
static bool ndrow_is_empty(const YALE_STORAGE* s, IType ija, const IType ija_next) {
|
643
|
-
if (ija == ija_next) return true;
|
644
|
-
|
645
|
-
DType* a = reinterpret_cast<DType*>(s->a);
|
646
|
-
|
647
|
-
// do all the entries = zero?
|
648
|
-
for (; ija < ija_next; ++ija) {
|
649
|
-
if (a[ija] != 0) return false;
|
650
|
-
}
|
651
|
-
|
652
|
-
return true;
|
653
|
-
}
|
654
|
-
|
655
|
-
//////////
|
656
|
-
// Math //
|
657
|
-
//////////
|
658
|
-
|
659
|
-
#define YALE_IA(s) (reinterpret_cast<IType*>(s->ija))
|
660
|
-
#define YALE_IJ(s) (reinterpret_cast<IType*>(s->ija) + s->shape[0] + 1)
|
661
|
-
#define YALE_COUNT(yale) (yale->ndnz + yale->shape[0])
|
662
|
-
|
663
|
-
/////////////
|
664
|
-
// Utility //
|
665
|
-
/////////////
|
666
|
-
|
667
|
-
|
668
|
-
/*
|
669
|
-
* Binary search for finding the beginning of a slice. Returns the position of the first element which is larger than
|
670
|
-
* bound.
|
671
|
-
*/
|
672
|
-
template <typename IType>
|
673
|
-
IType binary_search_left_boundary(const YALE_STORAGE* s, IType left, IType right, IType bound) {
|
674
|
-
if (left > right) return -1;
|
675
|
-
|
676
|
-
IType* ija = reinterpret_cast<IType*>(s->ija);
|
677
|
-
|
678
|
-
if (ija[left] >= bound) return left; // shortcut
|
679
|
-
|
680
|
-
IType mid = (left + right) / 2;
|
681
|
-
IType mid_j = ija[mid];
|
682
|
-
|
683
|
-
if (mid_j == bound)
|
684
|
-
return mid;
|
685
|
-
else if (mid_j > bound) { // eligible! don't exclude it.
|
686
|
-
return binary_search_left_boundary<IType>(s, left, mid, bound);
|
687
|
-
} else // (mid_j < bound)
|
688
|
-
return binary_search_left_boundary<IType>(s, mid + 1, right, bound);
|
689
|
-
}
|
690
|
-
|
691
|
-
|
692
|
-
/*
|
693
|
-
* Binary search for returning stored values. Returns a non-negative position, or -1 for not found.
|
694
|
-
*/
|
695
|
-
template <typename IType>
|
696
|
-
int binary_search(YALE_STORAGE* s, IType left, IType right, IType key) {
|
697
|
-
|
698
|
-
if (left > right) return -1;
|
699
|
-
|
700
|
-
IType* ija = reinterpret_cast<IType*>(s->ija);
|
701
|
-
|
702
|
-
IType mid = (left + right)/2;
|
703
|
-
IType mid_j = ija[mid];
|
704
|
-
|
705
|
-
if (mid_j == key)
|
706
|
-
return mid;
|
707
|
-
|
708
|
-
else if (mid_j > key)
|
709
|
-
return binary_search<IType>(s, left, mid - 1, key);
|
710
|
-
|
711
|
-
else
|
712
|
-
return binary_search<IType>(s, mid + 1, right, key);
|
713
|
-
}
|
714
|
-
|
715
|
-
|
716
|
-
/*
|
717
|
-
* Resize yale storage vectors A and IJA, copying values.
|
718
|
-
*/
|
719
|
-
static void vector_grow(YALE_STORAGE* s) {
|
720
|
-
if (s->src != s) throw; // need to correct this quickly.
|
721
|
-
|
722
|
-
size_t new_capacity = s->capacity * GROWTH_CONSTANT;
|
723
|
-
size_t max_capacity = max_size(s);
|
724
|
-
|
725
|
-
if (new_capacity > max_capacity) new_capacity = max_capacity;
|
726
|
-
|
727
|
-
void* new_ija = ALLOC_N(char, ITYPE_SIZES[s->itype] * new_capacity);
|
728
|
-
NM_CHECK_ALLOC(new_ija);
|
729
|
-
|
730
|
-
void* new_a = ALLOC_N(char, DTYPE_SIZES[s->dtype] * new_capacity);
|
731
|
-
NM_CHECK_ALLOC(new_a);
|
732
|
-
|
733
|
-
void* old_ija = s->ija;
|
734
|
-
void* old_a = s->a;
|
735
|
-
|
736
|
-
memcpy(new_ija, old_ija, s->capacity * ITYPE_SIZES[s->itype]);
|
737
|
-
memcpy(new_a, old_a, s->capacity * DTYPE_SIZES[s->dtype]);
|
738
|
-
|
739
|
-
s->capacity = new_capacity;
|
740
|
-
|
741
|
-
xfree(old_ija);
|
742
|
-
xfree(old_a);
|
743
|
-
|
744
|
-
s->ija = new_ija;
|
745
|
-
s->a = new_a;
|
746
|
-
}
|
747
|
-
|
748
|
-
|
749
|
-
/*
|
750
|
-
* Resize yale storage vectors A and IJA in preparation for an insertion.
|
751
|
-
*/
|
752
|
-
template <typename DType, typename IType>
|
753
|
-
static char vector_insert_resize(YALE_STORAGE* s, size_t current_size, size_t pos, size_t* j, size_t n, bool struct_only) {
|
754
|
-
if (s != s->src) throw;
|
755
|
-
|
756
|
-
// Determine the new capacity for the IJA and A vectors.
|
757
|
-
size_t new_capacity = s->capacity * GROWTH_CONSTANT;
|
758
|
-
size_t max_capacity = max_size(s);
|
759
|
-
|
760
|
-
if (new_capacity > max_capacity) {
|
761
|
-
new_capacity = max_capacity;
|
762
|
-
|
763
|
-
if (current_size + n > max_capacity) rb_raise(rb_eNoMemError, "insertion size exceeded maximum yale matrix size");
|
764
|
-
}
|
765
|
-
|
766
|
-
if (new_capacity < current_size + n)
|
767
|
-
new_capacity = current_size + n;
|
768
|
-
|
769
|
-
// Allocate the new vectors.
|
770
|
-
IType* new_ija = ALLOC_N( IType, new_capacity );
|
771
|
-
NM_CHECK_ALLOC(new_ija);
|
772
|
-
|
773
|
-
DType* new_a = ALLOC_N( DType, new_capacity );
|
774
|
-
NM_CHECK_ALLOC(new_a);
|
775
|
-
|
776
|
-
IType* old_ija = reinterpret_cast<IType*>(s->ija);
|
777
|
-
DType* old_a = reinterpret_cast<DType*>(s->a);
|
778
|
-
|
779
|
-
// Copy all values prior to the insertion site to the new IJA and new A
|
780
|
-
if (struct_only) {
|
781
|
-
for (size_t i = 0; i < pos; ++i) {
|
782
|
-
new_ija[i] = old_ija[i];
|
783
|
-
}
|
784
|
-
} else {
|
785
|
-
for (size_t i = 0; i < pos; ++i) {
|
786
|
-
new_ija[i] = old_ija[i];
|
787
|
-
new_a[i] = old_a[i];
|
788
|
-
}
|
789
|
-
}
|
790
|
-
|
791
|
-
|
792
|
-
// Copy all values subsequent to the insertion site to the new IJA and new A, leaving room (size n) for insertion.
|
793
|
-
if (struct_only) {
|
794
|
-
for (size_t i = pos; i < current_size; ++i) {
|
795
|
-
new_ija[i+n] = old_ija[i];
|
796
|
-
}
|
797
|
-
} else {
|
798
|
-
for (size_t i = pos; i < current_size; ++i) {
|
799
|
-
new_ija[i+n] = old_ija[i];
|
800
|
-
new_a[i+n] = old_a[i];
|
801
|
-
}
|
802
|
-
}
|
803
|
-
|
804
|
-
s->capacity = new_capacity;
|
805
|
-
|
806
|
-
xfree(s->ija);
|
807
|
-
xfree(s->a);
|
808
|
-
|
809
|
-
s->ija = reinterpret_cast<void*>(new_ija);
|
810
|
-
s->a = reinterpret_cast<void*>(new_a);
|
811
|
-
|
812
|
-
return 'i';
|
813
|
-
}
|
814
|
-
|
815
|
-
/*
|
816
|
-
* Insert a value or contiguous values in the ija and a vectors (after ja and
|
817
|
-
* diag). Does not free anything; you are responsible!
|
818
|
-
*
|
819
|
-
* TODO: Improve this so it can handle non-contiguous element insertions
|
820
|
-
* efficiently. For now, we can just sort the elements in the row in
|
821
|
-
* question.)
|
822
|
-
*/
|
823
|
-
template <typename DType, typename IType>
|
824
|
-
static char vector_insert(YALE_STORAGE* s, size_t pos, size_t* j, void* val_, size_t n, bool struct_only) {
|
825
|
-
if (pos < s->shape[0]) {
|
826
|
-
rb_raise(rb_eArgError, "vector insert pos (%d) is before beginning of ja (%d); this should not happen", pos, s->shape[0]);
|
827
|
-
}
|
828
|
-
|
829
|
-
DType* val = reinterpret_cast<DType*>(val_);
|
830
|
-
|
831
|
-
size_t size = get_size<IType>(s);
|
832
|
-
|
833
|
-
IType* ija = reinterpret_cast<IType*>(s->ija);
|
834
|
-
DType* a = reinterpret_cast<DType*>(s->a);
|
835
|
-
|
836
|
-
if (size + n > s->capacity) {
|
837
|
-
vector_insert_resize<DType,IType>(s, size, pos, j, n, struct_only);
|
838
|
-
|
839
|
-
// Need to get the new locations for ija and a.
|
840
|
-
ija = reinterpret_cast<IType*>(s->ija);
|
841
|
-
a = reinterpret_cast<DType*>(s->a);
|
842
|
-
|
843
|
-
} else {
|
844
|
-
/*
|
845
|
-
* No resize required:
|
846
|
-
* easy (but somewhat slow), just copy elements to the tail, starting at
|
847
|
-
* the end, one element at a time.
|
848
|
-
*
|
849
|
-
* TODO: This can be made slightly more efficient, but only after the tests
|
850
|
-
* are written.
|
851
|
-
*/
|
852
|
-
|
853
|
-
if (struct_only) {
|
854
|
-
for (size_t i = 0; i < size - pos; ++i) {
|
855
|
-
ija[size+n-1-i] = ija[size-1-i];
|
856
|
-
}
|
857
|
-
} else {
|
858
|
-
for (size_t i = 0; i < size - pos; ++i) {
|
859
|
-
ija[size+n-1-i] = ija[size-1-i];
|
860
|
-
a[size+n-1-i] = a[size-1-i];
|
861
|
-
}
|
862
|
-
}
|
863
|
-
}
|
864
|
-
|
865
|
-
// Now insert the new values.
|
866
|
-
if (struct_only) {
|
867
|
-
for (size_t i = 0; i < n; ++i) {
|
868
|
-
ija[pos+i] = j[i];
|
869
|
-
}
|
870
|
-
} else {
|
871
|
-
for (size_t i = 0; i < n; ++i) {
|
872
|
-
ija[pos+i] = j[i];
|
873
|
-
a[pos+i] = val[i];
|
874
|
-
}
|
875
|
-
}
|
876
|
-
|
877
|
-
return 'i';
|
878
|
-
}
|
879
|
-
|
880
|
-
/*
|
881
|
-
* If we add n items to row i, we need to increment ija[i+1] and onward.
|
882
|
-
*/
|
883
|
-
template <typename IType>
|
884
|
-
static void increment_ia_after(YALE_STORAGE* s, IType ija_size, IType i, IType n) {
|
885
|
-
IType* ija = reinterpret_cast<IType*>(s->ija);
|
886
|
-
|
887
|
-
++i;
|
888
|
-
for (; i <= ija_size; ++i) {
|
889
|
-
ija[i] += n;
|
890
|
-
}
|
891
|
-
}
|
892
|
-
|
893
|
-
/*
|
894
|
-
* Binary search for returning insertion points.
|
895
|
-
*/
|
896
|
-
template <typename IType>
|
897
|
-
static IType insert_search(YALE_STORAGE* s, IType left, IType right, IType key, bool* found) {
|
898
|
-
|
899
|
-
if (left > right) {
|
900
|
-
*found = false;
|
901
|
-
return left;
|
902
|
-
}
|
903
|
-
|
904
|
-
IType* ija = reinterpret_cast<IType*>(s->ija);
|
905
|
-
IType mid = (left + right)/2;
|
906
|
-
IType mid_j = ija[mid];
|
907
|
-
|
908
|
-
if (mid_j == key) {
|
909
|
-
*found = true;
|
910
|
-
return mid;
|
911
|
-
|
912
|
-
} else if (mid_j > key) {
|
913
|
-
return insert_search<IType>(s, left, mid-1, key, found);
|
914
|
-
|
915
|
-
} else {
|
916
|
-
return insert_search<IType>(s, mid+1, right, key, found);
|
917
|
-
}
|
918
|
-
}
|
919
|
-
|
920
|
-
/////////////////////////
|
921
|
-
// Copying and Casting //
|
922
|
-
/////////////////////////
|
923
|
-
|
924
|
-
/*
|
925
|
-
* Templated copy constructor for changing dtypes.
|
926
|
-
*/
|
927
|
-
template <typename LDType, typename RDType, typename IType>
|
928
|
-
YALE_STORAGE* cast_copy(const YALE_STORAGE* rhs, dtype_t new_dtype) {
|
929
|
-
|
930
|
-
YALE_STORAGE* lhs;
|
931
|
-
|
932
|
-
if (rhs->src != rhs) { // copy the reference
|
933
|
-
// Copy shape for yale construction
|
934
|
-
size_t* shape = ALLOC_N(size_t, 2);
|
935
|
-
shape[0] = rhs->shape[0];
|
936
|
-
shape[1] = rhs->shape[1];
|
937
|
-
size_t ndnz = src_ndnz(rhs);
|
938
|
-
if (shape[0] != rhs->src->shape[0] || shape[1] != rhs->src->shape[1])
|
939
|
-
ndnz = count_slice_copy_ndnz<RDType,IType>(rhs, rhs->offset, rhs->shape); // expensive, avoid if possible
|
940
|
-
size_t request_capacity = shape[0] + ndnz + 1;
|
941
|
-
// FIXME: Should we use a different itype? Or same?
|
942
|
-
lhs = nm_yale_storage_create(new_dtype, shape, 2, request_capacity, rhs->itype);
|
943
|
-
|
944
|
-
// This check probably isn't necessary.
|
945
|
-
if (lhs->capacity < request_capacity)
|
946
|
-
rb_raise(nm_eStorageTypeError, "conversion failed; capacity of %ld requested, max allowable is %ld", request_capacity, lhs->capacity);
|
947
|
-
|
948
|
-
slice_copy<LDType, RDType, IType>(lhs, rhs, rhs->offset, rhs->shape, new_dtype);
|
949
|
-
} else { // regular copy
|
950
|
-
|
951
|
-
// Allocate a new structure
|
952
|
-
size_t size = get_size<IType>(rhs);
|
953
|
-
lhs = copy_alloc_struct<IType>(rhs, new_dtype, rhs->capacity, size);
|
954
|
-
|
955
|
-
LDType* la = reinterpret_cast<LDType*>(lhs->a);
|
956
|
-
RDType* ra = reinterpret_cast<RDType*>(rhs->a);
|
957
|
-
|
958
|
-
for (size_t index = 0; index < size; ++index) {
|
959
|
-
la[index] = ra[index];
|
960
|
-
}
|
961
|
-
}
|
962
|
-
|
963
|
-
return lhs;
|
964
|
-
}
|
965
|
-
|
966
|
-
/*
|
967
|
-
* Template access for getting the size of Yale storage.
|
968
|
-
*/
|
969
|
-
template <typename IType>
|
970
|
-
static inline size_t get_size(const YALE_STORAGE* storage) {
|
971
|
-
return static_cast<size_t>(reinterpret_cast<IType*>(storage->ija)[ storage->shape[0] ]);
|
972
|
-
}
|
973
|
-
|
974
|
-
|
975
|
-
/*
|
976
|
-
* Allocate for a copy or copy-cast operation, and copy the IJA portion of the
|
977
|
-
* matrix (the structure).
|
978
|
-
*/
|
979
|
-
template <typename IType>
|
980
|
-
static YALE_STORAGE* copy_alloc_struct(const YALE_STORAGE* rhs, const dtype_t new_dtype, const size_t new_capacity, const size_t new_size) {
|
981
|
-
YALE_STORAGE* lhs = ALLOC( YALE_STORAGE );
|
982
|
-
lhs->dim = rhs->dim;
|
983
|
-
lhs->shape = ALLOC_N( size_t, lhs->dim );
|
984
|
-
lhs->offset = ALLOC_N( size_t, lhs->dim );
|
985
|
-
memcpy(lhs->shape, rhs->shape, lhs->dim * sizeof(size_t));
|
986
|
-
//memcpy(lhs->offset, rhs->offset, lhs->dim * sizeof(size_t));
|
987
|
-
lhs->offset[0] = 0;
|
988
|
-
lhs->offset[1] = 0;
|
989
|
-
|
990
|
-
lhs->itype = rhs->itype;
|
991
|
-
lhs->capacity = new_capacity;
|
992
|
-
lhs->dtype = new_dtype;
|
993
|
-
lhs->ndnz = rhs->ndnz;
|
994
|
-
|
995
|
-
lhs->ija = ALLOC_N( IType, lhs->capacity );
|
996
|
-
lhs->a = ALLOC_N( char, DTYPE_SIZES[new_dtype] * lhs->capacity );
|
997
|
-
lhs->src = lhs;
|
998
|
-
lhs->count = 1;
|
999
|
-
|
1000
|
-
// Now copy the contents -- but only within the boundaries set by the size. Leave
|
1001
|
-
// the rest uninitialized.
|
1002
|
-
if (!rhs->offset[0] && !rhs->offset[1]) {
|
1003
|
-
for (size_t i = 0; i < get_size<IType>(rhs); ++i)
|
1004
|
-
reinterpret_cast<IType*>(lhs->ija)[i] = reinterpret_cast<IType*>(rhs->ija)[i]; // copy indices
|
1005
|
-
} else {
|
1006
|
-
rb_raise(rb_eNotImpError, "cannot copy struct due to different offsets");
|
1007
|
-
}
|
1008
|
-
return lhs;
|
1009
|
-
}
|
1010
|
-
|
1011
|
-
template <typename DType, typename IType>
|
1012
|
-
static STORAGE* matrix_multiply(const STORAGE_PAIR& casted_storage, size_t* resulting_shape, bool vector, nm::itype_t result_itype) {
|
1013
|
-
YALE_STORAGE *left = (YALE_STORAGE*)(casted_storage.left),
|
1014
|
-
*right = (YALE_STORAGE*)(casted_storage.right);
|
1015
|
-
|
1016
|
-
// We can safely get dtype from the casted matrices; post-condition of binary_storage_cast_alloc is that dtype is the
|
1017
|
-
// same for left and right.
|
1018
|
-
// int8_t dtype = left->dtype;
|
1019
|
-
|
1020
|
-
// Massage the IType arrays into the correct form.
|
1021
|
-
|
1022
|
-
IType* ijl;
|
1023
|
-
if (left->itype == result_itype) ijl = reinterpret_cast<IType*>(left->ija);
|
1024
|
-
else { // make a temporary copy of the IJA vector for L with the correct itype
|
1025
|
-
size_t length = nm_yale_storage_get_size(left);
|
1026
|
-
ijl = ALLOCA_N(IType, length);
|
1027
|
-
copy_recast_itype_vector(reinterpret_cast<void*>(left->ija), left->itype, reinterpret_cast<void*>(ijl), result_itype, length);
|
1028
|
-
}
|
1029
|
-
|
1030
|
-
IType* ijr;
|
1031
|
-
if (right->itype == result_itype) ijr = reinterpret_cast<IType*>(right->ija);
|
1032
|
-
else { // make a temporary copy of the IJA vector for R with the correct itype
|
1033
|
-
size_t length = nm_yale_storage_get_size(right);
|
1034
|
-
ijr = ALLOCA_N(IType, length);
|
1035
|
-
copy_recast_itype_vector(reinterpret_cast<void*>(right->ija), right->itype, reinterpret_cast<void*>(ijr), result_itype, length);
|
1036
|
-
}
|
1037
|
-
|
1038
|
-
// First, count the ndnz of the result.
|
1039
|
-
// TODO: This basically requires running symbmm twice to get the exact ndnz size. That's frustrating. Are there simple
|
1040
|
-
// cases where we can avoid running it?
|
1041
|
-
size_t result_ndnz = nm::math::symbmm<IType>(resulting_shape[0], left->shape[1], resulting_shape[1], ijl, ijl, true, ijr, ijr, true, NULL, true);
|
1042
|
-
|
1043
|
-
// Create result storage.
|
1044
|
-
YALE_STORAGE* result = nm_yale_storage_create(left->dtype, resulting_shape, 2, result_ndnz, result_itype);
|
1045
|
-
init<DType,IType>(result, NULL);
|
1046
|
-
IType* ija = reinterpret_cast<IType*>(result->ija);
|
1047
|
-
|
1048
|
-
// Symbolic multiplication step (build the structure)
|
1049
|
-
nm::math::symbmm<IType>(resulting_shape[0], left->shape[1], resulting_shape[1], ijl, ijl, true, ijr, ijr, true, ija, true);
|
1050
|
-
|
1051
|
-
// Numeric multiplication step (fill in the elements)
|
1052
|
-
|
1053
|
-
nm::math::numbmm<DType,IType>(result->shape[0], left->shape[1], result->shape[1],
|
1054
|
-
ijl, ijl, reinterpret_cast<DType*>(left->a), true,
|
1055
|
-
ijr, ijr, reinterpret_cast<DType*>(right->a), true,
|
1056
|
-
ija, ija, reinterpret_cast<DType*>(result->a), true);
|
1057
|
-
|
1058
|
-
|
1059
|
-
// Sort the columns
|
1060
|
-
nm::math::smmp_sort_columns<DType,IType>(result->shape[0], ija, ija, reinterpret_cast<DType*>(result->a));
|
1061
|
-
|
1062
|
-
return reinterpret_cast<STORAGE*>(result);
|
1063
|
-
}
|
1064
|
-
|
1065
|
-
|
1066
|
-
/*
|
1067
|
-
* Get the sum of offsets from the original matrix (for sliced iteration).
|
1068
|
-
*/
|
1069
|
-
static std::array<size_t,2> get_offsets(YALE_STORAGE* x) {
|
1070
|
-
std::array<size_t, 2> offsets{ {0,0} };
|
1071
|
-
while (x != x->src) {
|
1072
|
-
offsets[0] += x->offset[0];
|
1073
|
-
offsets[1] += x->offset[1];
|
1074
|
-
x = reinterpret_cast<YALE_STORAGE*>(x->src);
|
1075
|
-
}
|
1076
|
-
return offsets;
|
1077
|
-
}
|
1078
|
-
|
1079
|
-
|
1080
|
-
template <typename IType>
|
1081
|
-
class IJAManager {
|
1082
|
-
protected:
|
1083
|
-
bool needs_free;
|
1084
|
-
|
1085
|
-
public:
|
1086
|
-
IType* ija;
|
1087
|
-
|
1088
|
-
IJAManager(YALE_STORAGE* s, itype_t temp_itype) : needs_free(false), ija(reinterpret_cast<IType*>(s->ija)) {
|
1089
|
-
if (s->itype != temp_itype) {
|
1090
|
-
size_t len = nm_yale_storage_get_size(s);
|
1091
|
-
needs_free = true;
|
1092
|
-
ija = ALLOC_N(IType, len);
|
1093
|
-
copy_recast_itype_vector(s->ija, s->itype, reinterpret_cast<void*>(ija), temp_itype, len);
|
1094
|
-
}
|
1095
|
-
}
|
1096
|
-
|
1097
|
-
~IJAManager() {
|
1098
|
-
if (needs_free) xfree(ija);
|
1099
|
-
}
|
1100
|
-
};
|
1101
|
-
|
1102
|
-
|
1103
|
-
template <typename IType>
|
1104
|
-
class RowIterator {
|
1105
|
-
protected:
|
1106
|
-
YALE_STORAGE* s;
|
1107
|
-
IType* ija;
|
1108
|
-
void* a;
|
1109
|
-
IType i, k, k_end;
|
1110
|
-
size_t j_offset, j_shape;
|
1111
|
-
bool diag, End;
|
1112
|
-
VALUE init;
|
1113
|
-
public:
|
1114
|
-
RowIterator(YALE_STORAGE* s_, IType* ija_, IType i_, size_t j_shape_, size_t j_offset_ = 0)
|
1115
|
-
: s(s_),
|
1116
|
-
ija(ija_),
|
1117
|
-
a(s->a),
|
1118
|
-
i(i_),
|
1119
|
-
k(ija[i]),
|
1120
|
-
k_end(ija[i+1]),
|
1121
|
-
j_offset(j_offset_),
|
1122
|
-
j_shape(j_shape_),
|
1123
|
-
diag(row_has_no_nd() || diag_is_first()),
|
1124
|
-
End(false),
|
1125
|
-
init(default_value(s))
|
1126
|
-
{ }
|
1127
|
-
|
1128
|
-
RowIterator(YALE_STORAGE* s_, IType i_, size_t j_shape_, size_t j_offset_ = 0)
|
1129
|
-
: s(s_),
|
1130
|
-
ija(reinterpret_cast<IType*>(s->ija)),
|
1131
|
-
a(s->a),
|
1132
|
-
i(i_),
|
1133
|
-
k(ija[i]),
|
1134
|
-
k_end(ija[i+1]),
|
1135
|
-
j_offset(j_offset_),
|
1136
|
-
j_shape(j_shape_),
|
1137
|
-
diag(row_has_no_nd() || diag_is_first()),
|
1138
|
-
End(false),
|
1139
|
-
init(default_value(s))
|
1140
|
-
{ }
|
1141
|
-
|
1142
|
-
RowIterator(const RowIterator& rhs) : s(rhs.s), ija(rhs.ija), a(s->a), i(rhs.i), k(rhs.k), k_end(rhs.k_end), j_offset(rhs.j_offset), j_shape(rhs.j_shape), diag(rhs.diag), End(rhs.End), init(rhs.init) { }
|
1143
|
-
|
1144
|
-
VALUE obj() const {
|
1145
|
-
return diag ? obj_at(s, i) : obj_at(s, k);
|
1146
|
-
}
|
1147
|
-
|
1148
|
-
template <typename T>
|
1149
|
-
T cobj() const {
|
1150
|
-
if (typeid(T) == typeid(RubyObject)) return obj();
|
1151
|
-
return diag ? reinterpret_cast<T*>(s->a)[i] : reinterpret_cast<T*>(s->a)[k];
|
1152
|
-
}
|
1153
|
-
|
1154
|
-
inline IType proper_j() const {
|
1155
|
-
return diag ? i : ija[k];
|
1156
|
-
}
|
1157
|
-
|
1158
|
-
inline IType offset_j() const {
|
1159
|
-
return proper_j() - j_offset;
|
1160
|
-
}
|
1161
|
-
|
1162
|
-
/* Returns true if an additional value is inserted, false if it goes on the diagonal */
|
1163
|
-
bool insert(IType j, VALUE v) {
|
1164
|
-
if (j == i) { // insert regardless on diagonal
|
1165
|
-
reinterpret_cast<VALUE*>(a)[j] = v;
|
1166
|
-
return false;
|
1167
|
-
|
1168
|
-
} else {
|
1169
|
-
if (rb_funcall(v, rb_intern("!="), 1, init) == Qtrue) {
|
1170
|
-
if (k >= s->capacity) {
|
1171
|
-
vector_grow(s);
|
1172
|
-
ija = reinterpret_cast<IType*>(s->ija);
|
1173
|
-
a = s->a;
|
1174
|
-
}
|
1175
|
-
reinterpret_cast<VALUE*>(a)[k] = v;
|
1176
|
-
ija[k] = j;
|
1177
|
-
k++;
|
1178
|
-
return true;
|
1179
|
-
}
|
1180
|
-
return false;
|
1181
|
-
}
|
1182
|
-
}
|
1183
|
-
|
1184
|
-
void update_row_end() {
|
1185
|
-
ija[i+1] = k;
|
1186
|
-
k_end = k;
|
1187
|
-
}
|
1188
|
-
|
1189
|
-
/* Past the j_shape? */
|
1190
|
-
inline bool end() const {
|
1191
|
-
if (End) return true;
|
1192
|
-
//if (diag) return i - j_offset >= j_shape;
|
1193
|
-
//else return k >= s->capacity || ija[k] - j_offset >= j_shape;
|
1194
|
-
return (diag ? i : ija[k]) - j_offset >= j_shape;
|
1195
|
-
}
|
1196
|
-
|
1197
|
-
inline bool row_has_no_nd() const { return ija[i] == k_end; /* k_start == k_end */ }
|
1198
|
-
inline bool diag_is_first() const { return i < ija[ija[i]]; }
|
1199
|
-
inline bool diag_is_last() const { return i > ija[k_end-1]; } // only works if !row_has_no_nd()
|
1200
|
-
inline bool k_is_last_nd() const { return k == k_end-1; }
|
1201
|
-
inline bool k_is_last() const { return k_is_last_nd() && !diag_is_last(); }
|
1202
|
-
inline bool diag_is_ahead() const { return i > ija[k]; }
|
1203
|
-
inline bool row_has_diag() const { return i < s->shape[1]; }
|
1204
|
-
inline bool diag_is_next() const { // assumes we've already tested for diag, row_has_no_nd(), diag_is_first()
|
1205
|
-
if (i == ija[k]+1) return true; // definite next
|
1206
|
-
else if (k+1 < k_end && i >= ija[k+1]+1) return false; // at least one item before it
|
1207
|
-
else return true;
|
1208
|
-
}
|
1209
|
-
|
1210
|
-
RowIterator<IType>& operator++() {
|
1211
|
-
if (diag) { // we're at the diagonal
|
1212
|
-
if (row_has_no_nd() || diag_is_last()) End = true; // and there are no non-diagonals (or none still to visit)
|
1213
|
-
diag = false;
|
1214
|
-
} else if (!row_has_diag()) { // row has no diagonal entries
|
1215
|
-
if (row_has_no_nd() || k_is_last_nd()) End = true; // row is totally empty, or we're at last entry
|
1216
|
-
else k++; // still entries to visit
|
1217
|
-
} else { // not at diag but it exists somewhere in the row, and row has at least one nd entry
|
1218
|
-
if (diag_is_ahead()) { // diag is ahead
|
1219
|
-
if (k_is_last_nd()) diag = true; // diag is next and last
|
1220
|
-
else if (diag_is_next()) { // diag is next and not last
|
1221
|
-
diag = true;
|
1222
|
-
k++;
|
1223
|
-
} else k++; // diag is not next
|
1224
|
-
} else { // diag is past
|
1225
|
-
if (k_is_last_nd()) End = true; // and we're at the end
|
1226
|
-
else k++; // and we're not at the end
|
1227
|
-
}
|
1228
|
-
}
|
1229
|
-
|
1230
|
-
return *this;
|
1231
|
-
}
|
1232
|
-
|
1233
|
-
|
1234
|
-
RowIterator<IType> operator++(int unused) {
|
1235
|
-
RowIterator<IType> x(*this);
|
1236
|
-
++(*this);
|
1237
|
-
return x;
|
1238
|
-
}
|
1239
|
-
};
|
1240
|
-
|
1241
|
-
|
1242
|
-
|
1243
|
-
template <typename IType>
|
1244
|
-
static VALUE map_stored(VALUE self) {
|
1245
|
-
|
1246
|
-
YALE_STORAGE* s = NM_STORAGE_YALE(self);
|
1247
|
-
|
1248
|
-
size_t* shape = ALLOC_N(size_t, 2);
|
1249
|
-
shape[0] = s->shape[0];
|
1250
|
-
shape[1] = s->shape[1];
|
1251
|
-
|
1252
|
-
std::array<size_t,2> s_offsets = get_offsets(s);
|
1253
|
-
|
1254
|
-
RETURN_SIZED_ENUMERATOR(self, 0, 0, nm_yale_enumerator_length);
|
1255
|
-
VALUE init = rb_yield(default_value(s));
|
1256
|
-
|
1257
|
-
// Try to find a reasonable capacity to request when creating the matrix
|
1258
|
-
size_t ndnz = src_ndnz(s);
|
1259
|
-
if (s->src != s) // need to guess capacity
|
1260
|
-
ndnz = yale_count_slice_copy_ndnz(s, s->offset, s->shape);
|
1261
|
-
size_t request_capacity = s->shape[0] + ndnz + 1;
|
1262
|
-
|
1263
|
-
YALE_STORAGE* r = nm_yale_storage_create(nm::RUBYOBJ, shape, 2, request_capacity, NM_ITYPE(self));
|
1264
|
-
if (r->capacity < request_capacity)
|
1265
|
-
rb_raise(nm_eStorageTypeError, "conversion failed; capacity of %ld requested, max allowable is %ld", request_capacity, r->capacity);
|
1266
|
-
nm_yale_storage_init(r, &init);
|
1267
|
-
|
1268
|
-
for (IType ri = 0; ri < shape[0]; ++ri) {
|
1269
|
-
RowIterator<IType> sit(s, ri + s_offsets[0], shape[1], s_offsets[1]);
|
1270
|
-
RowIterator<IType> rit(r, ri, shape[1]);
|
1271
|
-
|
1272
|
-
while (!sit.end()) {
|
1273
|
-
VALUE rv = rb_yield(sit.obj());
|
1274
|
-
VALUE rj = sit.offset_j();
|
1275
|
-
rit.insert(rj, rv);
|
1276
|
-
++sit;
|
1277
|
-
}
|
1278
|
-
// Update the row end information.
|
1279
|
-
rit.update_row_end();
|
1280
|
-
}
|
1281
|
-
|
1282
|
-
NMATRIX* m = nm_create(nm::YALE_STORE, reinterpret_cast<STORAGE*>(r));
|
1283
|
-
return Data_Wrap_Struct(CLASS_OF(self), nm_yale_storage_mark, nm_delete, m);
|
1284
|
-
}
|
1285
|
-
|
1286
|
-
|
1287
|
-
/*
|
1288
|
-
* eqeq function for slicing and different defaults.
|
1289
|
-
*/
|
1290
|
-
template <typename LDType, typename RDType, typename IType>
|
1291
|
-
static bool eqeq_different_defaults(const YALE_STORAGE* s, const LDType& s_init, const YALE_STORAGE* t, const RDType& t_init) {
|
1292
|
-
|
1293
|
-
std::array<size_t,2> s_offsets = get_offsets(const_cast<YALE_STORAGE*>(s)),
|
1294
|
-
t_offsets = get_offsets(const_cast<YALE_STORAGE*>(t));
|
1295
|
-
|
1296
|
-
for (IType ri = 0; ri < s->shape[0]; ++ri) {
|
1297
|
-
RowIterator<IType> sit(const_cast<YALE_STORAGE*>(s), reinterpret_cast<IType*>(s->ija), ri + s_offsets[0], s->shape[1], s_offsets[1]);
|
1298
|
-
RowIterator<IType> tit(const_cast<YALE_STORAGE*>(t), reinterpret_cast<IType*>(t->ija), ri + t_offsets[0], s->shape[1], t_offsets[1]);
|
1299
|
-
|
1300
|
-
while (!sit.end() || !tit.end()) {
|
1301
|
-
|
1302
|
-
// Perform the computation. Use a default value if the matrix doesn't have some value stored.
|
1303
|
-
if (tit.end() || (!sit.end() && sit.offset_j() < tit.offset_j())) {
|
1304
|
-
if (sit.template cobj<LDType>() != t_init) return false;
|
1305
|
-
++sit;
|
1306
|
-
|
1307
|
-
} else if (sit.end() || (!tit.end() && sit.offset_j() > tit.offset_j())) {
|
1308
|
-
if (s_init != tit.template cobj<RDType>()) return false;
|
1309
|
-
++tit;
|
1310
|
-
|
1311
|
-
} else { // same index
|
1312
|
-
if (sit.template cobj<LDType>() != tit.template cobj<RDType>()) return false;
|
1313
|
-
++sit;
|
1314
|
-
++tit;
|
1315
|
-
}
|
1316
|
-
}
|
1317
|
-
}
|
1318
|
-
return true;
|
1319
|
-
}
|
1320
|
-
|
1321
|
-
|
1322
|
-
template <typename IType>
|
1323
|
-
static VALUE map_merged_stored(VALUE left, VALUE right, VALUE init, nm::itype_t itype) {
|
1324
|
-
|
1325
|
-
YALE_STORAGE *s = NM_STORAGE_YALE(left),
|
1326
|
-
*t = NM_STORAGE_YALE(right);
|
1327
|
-
|
1328
|
-
size_t* shape = ALLOC_N(size_t, 2);
|
1329
|
-
shape[0] = s->shape[0];
|
1330
|
-
shape[1] = s->shape[1];
|
1331
|
-
|
1332
|
-
std::array<size_t,2> s_offsets = get_offsets(s),
|
1333
|
-
t_offsets = get_offsets(t);
|
1334
|
-
|
1335
|
-
VALUE s_init = default_value(s),
|
1336
|
-
t_init = default_value(t);
|
1337
|
-
|
1338
|
-
RETURN_SIZED_ENUMERATOR(left, 0, 0, 0);
|
1339
|
-
|
1340
|
-
if (init == Qnil)
|
1341
|
-
init = rb_yield_values(2, s_init, t_init);
|
1342
|
-
|
1343
|
-
// Make a reasonable approximation of the resulting capacity
|
1344
|
-
size_t s_ndnz = src_ndnz(s), t_ndnz = src_ndnz(t);
|
1345
|
-
if (s->src != s) s_ndnz = yale_count_slice_copy_ndnz(s, s->offset, s->shape);
|
1346
|
-
if (t->src != t) t_ndnz = yale_count_slice_copy_ndnz(t, t->offset, t->shape);
|
1347
|
-
size_t request_capacity = shape[0] + NM_MAX(s_ndnz, t_ndnz) + 1;
|
1348
|
-
|
1349
|
-
YALE_STORAGE* r = nm_yale_storage_create(nm::RUBYOBJ, shape, 2, request_capacity, itype);
|
1350
|
-
if (r->capacity < request_capacity)
|
1351
|
-
rb_raise(nm_eStorageTypeError, "conversion failed; capacity of %ld requested, max allowable is %ld", request_capacity, r->capacity);
|
1352
|
-
|
1353
|
-
nm_yale_storage_init(r, &init);
|
1354
|
-
|
1355
|
-
IJAManager<IType> sm(s, itype),
|
1356
|
-
tm(t, itype);
|
1357
|
-
|
1358
|
-
for (IType ri = 0; ri < shape[0]; ++ri) {
|
1359
|
-
RowIterator<IType> sit(s, sm.ija, ri + s_offsets[0], shape[1], s_offsets[1]);
|
1360
|
-
RowIterator<IType> tit(t, tm.ija, ri + t_offsets[0], shape[1], t_offsets[1]);
|
1361
|
-
|
1362
|
-
RowIterator<IType> rit(r, reinterpret_cast<IType*>(r->ija), ri, shape[1]);
|
1363
|
-
while (!sit.end() || !tit.end()) {
|
1364
|
-
VALUE rv;
|
1365
|
-
IType rj;
|
1366
|
-
|
1367
|
-
// Perform the computation. Use a default value if the matrix doesn't have some value stored.
|
1368
|
-
if (tit.end() || (!sit.end() && sit.offset_j() < tit.offset_j())) {
|
1369
|
-
rv = rb_yield_values(2, sit.obj(), t_init);
|
1370
|
-
rj = sit.offset_j();
|
1371
|
-
++sit;
|
1372
|
-
|
1373
|
-
} else if (sit.end() || (!tit.end() && sit.offset_j() > tit.offset_j())) {
|
1374
|
-
rv = rb_yield_values(2, s_init, tit.obj());
|
1375
|
-
rj = tit.offset_j();
|
1376
|
-
++tit;
|
1377
|
-
|
1378
|
-
} else { // same index
|
1379
|
-
rv = rb_yield_values(2, sit.obj(), tit.obj());
|
1380
|
-
rj = sit.offset_j();
|
1381
|
-
++sit;
|
1382
|
-
++tit;
|
1383
|
-
}
|
1384
|
-
|
1385
|
-
rit.insert(rj, rv); // handles increment (and testing for default, etc)
|
1386
|
-
|
1387
|
-
}
|
1388
|
-
|
1389
|
-
// Update the row end information.
|
1390
|
-
rit.update_row_end();
|
1391
|
-
}
|
1392
|
-
|
1393
|
-
NMATRIX* m = nm_create(nm::YALE_STORE, reinterpret_cast<STORAGE*>(r));
|
1394
|
-
return Data_Wrap_Struct(CLASS_OF(left), nm_yale_storage_mark, nm_delete, m);
|
1395
|
-
}
|
1396
|
-
|
1397
|
-
|
1398
|
-
/*
|
1399
|
-
* This function and the two helper structs enable us to use partial template specialization.
|
1400
|
-
* See also: http://stackoverflow.com/questions/6623375/c-template-specialization-on-functions
|
1401
|
-
*/
|
1402
|
-
template <typename DType, typename IType>
|
1403
|
-
static VALUE each_stored_with_indices(VALUE nm) {
|
1404
|
-
YALE_STORAGE* s = NM_STORAGE_YALE(nm);
|
1405
|
-
DType* a = reinterpret_cast<DType*>(s->a);
|
1406
|
-
IType* ija = reinterpret_cast<IType*>(s->ija);
|
1407
|
-
|
1408
|
-
// If we don't have a block, return an enumerator.
|
1409
|
-
RETURN_SIZED_ENUMERATOR(nm, 0, 0, nm_yale_stored_enumerator_length);
|
1410
|
-
|
1411
|
-
// Iterate along diagonal
|
1412
|
-
for (size_t sk = NM_MAX(s->offset[0], s->offset[1]); sk < NM_MIN(s->shape[0] + s->offset[0], s->shape[1] + s->offset[1]); ++sk) {
|
1413
|
-
VALUE ii = LONG2NUM(sk - s->offset[0]),
|
1414
|
-
jj = LONG2NUM(sk - s->offset[1]);
|
1415
|
-
|
1416
|
-
rb_yield_values(3, obj_at(s, sk), ii, jj);
|
1417
|
-
}
|
1418
|
-
|
1419
|
-
// Iterate through non-diagonal elements, row by row
|
1420
|
-
for (long ri = 0; ri < s->shape[0]; ++ri) {
|
1421
|
-
long si = ri + s->offset[0];
|
1422
|
-
IType p = ija[si],
|
1423
|
-
next_p = ija[si+1];
|
1424
|
-
|
1425
|
-
// if this is a reference to another matrix, we should find the left boundary of the slice
|
1426
|
-
if (s != s->src && p < next_p)
|
1427
|
-
p = binary_search_left_boundary<IType>(s, p, next_p-1, s->offset[1]);
|
1428
|
-
|
1429
|
-
for (; p < next_p; ++p) {
|
1430
|
-
long sj = static_cast<long>(ija[p]),
|
1431
|
-
rj = sj - s->offset[1];
|
1432
|
-
if (rj < 0) continue;
|
1433
|
-
|
1434
|
-
if (rj >= s->shape[1]) break;
|
1435
|
-
|
1436
|
-
rb_yield_values(3, obj_at(s, p), LONG2NUM(ri), LONG2NUM(rj));
|
1437
|
-
}
|
1438
|
-
}
|
1439
|
-
|
1440
|
-
return nm;
|
1441
|
-
}
|
1442
|
-
|
1443
|
-
template <typename DType, typename IType>
|
1444
|
-
static VALUE each_with_indices(VALUE nm) {
|
1445
|
-
YALE_STORAGE* s = NM_STORAGE_YALE(nm);
|
1446
|
-
DType* a = reinterpret_cast<DType*>(s->a);
|
1447
|
-
IType* ija = reinterpret_cast<IType*>(s->ija);
|
1448
|
-
|
1449
|
-
// If we don't have a block, return an enumerator.
|
1450
|
-
RETURN_SIZED_ENUMERATOR(nm, 0, 0, nm_yale_enumerator_length);
|
1451
|
-
|
1452
|
-
// Iterate in two dimensions.
|
1453
|
-
// s stands for src, r stands for ref (for ri, rj, si, sj)
|
1454
|
-
for (long ri = 0; ri < s->shape[0]; ++ri) {
|
1455
|
-
long si = ri + s->offset[0];
|
1456
|
-
VALUE ii = LONG2NUM(ri + s->offset[0]);
|
1457
|
-
|
1458
|
-
IType k = ija[si], k_next = ija[si+1];
|
1459
|
-
|
1460
|
-
for (long rj = 0; rj < s->shape[1]; ++rj) {
|
1461
|
-
long sj = rj + s->offset[1];
|
1462
|
-
VALUE v, jj = LONG2NUM(rj);
|
1463
|
-
|
1464
|
-
// zero is stored in s->shape[0]
|
1465
|
-
if (si == sj) {
|
1466
|
-
v = obj_at(s, si);
|
1467
|
-
} else {
|
1468
|
-
// Walk through the row until we find the correct location.
|
1469
|
-
while (ija[k] < sj && k < k_next) ++k;
|
1470
|
-
if (k < k_next && ija[k] == sj) {
|
1471
|
-
v = obj_at(s, k);
|
1472
|
-
++k;
|
1473
|
-
} else v = default_value(s); // rubyobj_from_cval(&(a[s->shape[0]]), NM_DTYPE(nm)).rval;
|
1474
|
-
}
|
1475
|
-
rb_yield_values(3, v, ii, jj);
|
1476
|
-
}
|
1477
|
-
}
|
1478
|
-
|
1479
|
-
return nm;
|
1480
|
-
}
|
1481
|
-
|
1482
|
-
|
1483
|
-
} // end of namespace nm::yale_storage
|
1484
|
-
|
1485
|
-
|
1486
|
-
// Helper function used only for the RETURN_SIZED_ENUMERATOR macro. Returns the length of
|
1487
|
-
// the matrix's storage.
|
1488
|
-
static VALUE nm_yale_stored_enumerator_length(VALUE nmatrix) {
|
1489
|
-
long len = nm_yale_storage_get_size(NM_STORAGE_YALE(nmatrix));
|
1490
|
-
return LONG2NUM(len);
|
1491
|
-
}
|
1492
|
-
|
1493
|
-
|
1494
|
-
} // end of namespace nm.
|
1495
|
-
|
1496
|
-
///////////////////
|
1497
|
-
// Ruby Bindings //
|
1498
|
-
///////////////////
|
1499
|
-
|
1500
|
-
/* These bindings are mostly only for debugging Yale. They are called from Init_nmatrix. */
|
1501
|
-
|
1502
|
-
extern "C" {
|
1503
|
-
|
1504
|
-
void nm_init_yale_functions() {
|
1505
|
-
/*
|
1506
|
-
* This module stores methods that are useful for debugging Yale matrices,
|
1507
|
-
* i.e. the ones with +:yale+ stype.
|
1508
|
-
*/
|
1509
|
-
cNMatrix_YaleFunctions = rb_define_module_under(cNMatrix, "YaleFunctions");
|
1510
|
-
|
1511
|
-
rb_define_method(cNMatrix_YaleFunctions, "yale_ija", (METHOD)nm_ija, -1);
|
1512
|
-
rb_define_method(cNMatrix_YaleFunctions, "yale_a", (METHOD)nm_a, -1);
|
1513
|
-
rb_define_method(cNMatrix_YaleFunctions, "yale_size", (METHOD)nm_size, 0);
|
1514
|
-
rb_define_method(cNMatrix_YaleFunctions, "yale_ia", (METHOD)nm_ia, 0);
|
1515
|
-
rb_define_method(cNMatrix_YaleFunctions, "yale_ja", (METHOD)nm_ja, 0);
|
1516
|
-
rb_define_method(cNMatrix_YaleFunctions, "yale_d", (METHOD)nm_d, -1);
|
1517
|
-
rb_define_method(cNMatrix_YaleFunctions, "yale_lu", (METHOD)nm_lu, 0);
|
1518
|
-
|
1519
|
-
rb_define_method(cNMatrix_YaleFunctions, "yale_nd_row", (METHOD)nm_nd_row, -1);
|
1520
|
-
|
1521
|
-
rb_define_const(cNMatrix_YaleFunctions, "YALE_GROWTH_CONSTANT", rb_float_new(nm::yale_storage::GROWTH_CONSTANT));
|
1522
|
-
}
|
1523
|
-
|
1524
|
-
|
1525
|
-
/////////////////
|
1526
|
-
// C ACCESSORS //
|
1527
|
-
/////////////////
|
1528
|
-
|
1529
|
-
|
1530
|
-
/* C interface for NMatrix#each_with_indices (Yale) */
|
1531
|
-
VALUE nm_yale_each_with_indices(VALUE nmatrix) {
|
1532
|
-
nm::dtype_t d = NM_DTYPE(nmatrix);
|
1533
|
-
nm::itype_t i = NM_ITYPE(nmatrix);
|
1534
|
-
|
1535
|
-
NAMED_LI_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::each_with_indices, VALUE, VALUE)
|
1536
|
-
|
1537
|
-
return ttable[d][i](nmatrix);
|
1538
|
-
}
|
1539
|
-
|
1540
|
-
|
1541
|
-
/* C interface for NMatrix#each_stored_with_indices (Yale) */
|
1542
|
-
VALUE nm_yale_each_stored_with_indices(VALUE nmatrix) {
|
1543
|
-
nm::dtype_t d = NM_DTYPE(nmatrix);
|
1544
|
-
nm::itype_t i = NM_ITYPE(nmatrix);
|
1545
|
-
|
1546
|
-
NAMED_LI_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::each_stored_with_indices, VALUE, VALUE)
|
1547
|
-
|
1548
|
-
return ttable[d][i](nmatrix);
|
1549
|
-
}
|
1550
|
-
|
1551
|
-
|
1552
|
-
|
1553
|
-
/*
|
1554
|
-
* C accessor for inserting some value in a matrix (or replacing an existing cell).
|
1555
|
-
*/
|
1556
|
-
char nm_yale_storage_set(STORAGE* storage, SLICE* slice, void* v) {
|
1557
|
-
NAMED_LI_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::set, char, YALE_STORAGE* storage, SLICE* slice, void* value);
|
1558
|
-
|
1559
|
-
YALE_STORAGE* casted_storage = (YALE_STORAGE*)storage;
|
1560
|
-
|
1561
|
-
return ttable[casted_storage->dtype][casted_storage->itype](casted_storage, slice, v);
|
1562
|
-
}
|
1563
|
-
|
1564
|
-
|
1565
|
-
/*
|
1566
|
-
* Determine the number of non-diagonal non-zeros in a not-yet-created copy of a slice or matrix.
|
1567
|
-
*/
|
1568
|
-
static size_t yale_count_slice_copy_ndnz(const YALE_STORAGE* s, size_t* offset, size_t* shape) {
|
1569
|
-
NAMED_LI_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::count_slice_copy_ndnz, size_t, const YALE_STORAGE*, size_t*, size_t*)
|
1570
|
-
|
1571
|
-
return ttable[s->dtype][s->itype](s, offset, shape);
|
1572
|
-
}
|
1573
|
-
|
1574
|
-
|
1575
|
-
/*
|
1576
|
-
* C accessor for yale_storage::get, which returns a slice of YALE_STORAGE object by copy
|
1577
|
-
*
|
1578
|
-
* Slicing-related.
|
1579
|
-
*/
|
1580
|
-
void* nm_yale_storage_get(STORAGE* storage, SLICE* slice) {
|
1581
|
-
YALE_STORAGE* casted_storage = (YALE_STORAGE*)storage;
|
1582
|
-
|
1583
|
-
if (slice->single) {
|
1584
|
-
NAMED_LI_DTYPE_TEMPLATE_TABLE(elem_copy_table, nm::yale_storage::get_single, void*, YALE_STORAGE*, SLICE*)
|
1585
|
-
|
1586
|
-
return elem_copy_table[casted_storage->dtype][casted_storage->itype](casted_storage, slice);
|
1587
|
-
} else {
|
1588
|
-
// Copy shape for yale construction
|
1589
|
-
size_t* shape = ALLOC_N(size_t, 2);
|
1590
|
-
shape[0] = slice->lengths[0];
|
1591
|
-
shape[1] = slice->lengths[1];
|
1592
|
-
|
1593
|
-
// only count ndnz if our slice is smaller, otherwise use the given value
|
1594
|
-
size_t ndnz = src_ndnz(casted_storage);
|
1595
|
-
if (shape[0] != casted_storage->shape[0] || shape[1] != casted_storage->shape[1])
|
1596
|
-
ndnz = yale_count_slice_copy_ndnz(casted_storage, slice->coords, shape); // expensive operation
|
1597
|
-
|
1598
|
-
size_t request_capacity = shape[0] + ndnz + 1; // capacity of new matrix
|
1599
|
-
YALE_STORAGE* ns = nm_yale_storage_create(casted_storage->dtype, shape, 2, request_capacity, casted_storage->itype);
|
1600
|
-
|
1601
|
-
// This check probably isn't necessary.
|
1602
|
-
if (ns->capacity < request_capacity)
|
1603
|
-
rb_raise(nm_eStorageTypeError, "conversion failed; capacity of %ld requested, max allowable is %ld", request_capacity, ns->capacity);
|
1604
|
-
|
1605
|
-
NAMED_LRI_DTYPE_TEMPLATE_TABLE(slice_copy_table, nm::yale_storage::slice_copy, void, YALE_STORAGE* ns, const YALE_STORAGE* s, size_t*, size_t*, nm::dtype_t)
|
1606
|
-
|
1607
|
-
slice_copy_table[ns->dtype][casted_storage->dtype][casted_storage->itype](ns, casted_storage, slice->coords, slice->lengths, casted_storage->dtype);
|
1608
|
-
|
1609
|
-
return ns;
|
1610
|
-
}
|
1611
|
-
}
|
1612
|
-
|
1613
|
-
/*
|
1614
|
-
* C accessor for yale_storage::vector_insert
|
1615
|
-
*/
|
1616
|
-
static char nm_yale_storage_vector_insert(YALE_STORAGE* s, size_t pos, size_t* js, void* vals, size_t n, bool struct_only, nm::dtype_t dtype, nm::itype_t itype) {
|
1617
|
-
NAMED_LI_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::vector_insert, char, YALE_STORAGE*, size_t, size_t*, void*, size_t, bool);
|
1618
|
-
|
1619
|
-
return ttable[dtype][itype](s, pos, js, vals, n, struct_only);
|
1620
|
-
}
|
1621
|
-
|
1622
|
-
/*
|
1623
|
-
* C accessor for yale_storage::increment_ia_after, typically called after ::vector_insert
|
1624
|
-
*/
|
1625
|
-
static void nm_yale_storage_increment_ia_after(YALE_STORAGE* s, size_t ija_size, size_t i, size_t n, nm::itype_t itype) {
|
1626
|
-
NAMED_ITYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::c_increment_ia_after, void, YALE_STORAGE*, size_t, size_t, size_t);
|
1627
|
-
|
1628
|
-
ttable[itype](s, ija_size, i, n);
|
1629
|
-
}
|
1630
|
-
|
1631
|
-
|
1632
|
-
/*
|
1633
|
-
* C accessor for yale_storage::ref, which returns a pointer to the correct location in a YALE_STORAGE object
|
1634
|
-
* for some set of coordinates.
|
1635
|
-
*/
|
1636
|
-
void* nm_yale_storage_ref(STORAGE* storage, SLICE* slice) {
|
1637
|
-
YALE_STORAGE* casted_storage = (YALE_STORAGE*)storage;
|
1638
|
-
|
1639
|
-
if (slice->single) {
|
1640
|
-
NAMED_LI_DTYPE_TEMPLATE_TABLE(elem_copy_table, nm::yale_storage::get_single, void*, YALE_STORAGE*, SLICE*)
|
1641
|
-
return elem_copy_table[casted_storage->dtype][casted_storage->itype](casted_storage, slice);
|
1642
|
-
} else {
|
1643
|
-
NAMED_LI_DTYPE_TEMPLATE_TABLE(ref_table, nm::yale_storage::ref, void*, YALE_STORAGE* storage, SLICE* slice)
|
1644
|
-
return ref_table[casted_storage->dtype][casted_storage->itype](casted_storage, slice);
|
1645
|
-
}
|
1646
|
-
}
|
1647
|
-
|
1648
|
-
|
1649
|
-
/*
|
1650
|
-
* C accessor for determining whether two YALE_STORAGE objects have the same contents.
|
1651
|
-
*/
|
1652
|
-
bool nm_yale_storage_eqeq(const STORAGE* left, const STORAGE* right) {
|
1653
|
-
NAMED_LRI_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::eqeq, bool, const YALE_STORAGE* left, const YALE_STORAGE* right);
|
1654
|
-
|
1655
|
-
const YALE_STORAGE* casted_left = reinterpret_cast<const YALE_STORAGE*>(left);
|
1656
|
-
|
1657
|
-
return ttable[casted_left->dtype][right->dtype][casted_left->itype](casted_left, (const YALE_STORAGE*)right);
|
1658
|
-
}
|
1659
|
-
|
1660
|
-
|
1661
|
-
/*
|
1662
|
-
* Copy constructor for changing dtypes. (C accessor)
|
1663
|
-
*/
|
1664
|
-
STORAGE* nm_yale_storage_cast_copy(const STORAGE* rhs, nm::dtype_t new_dtype, void* dummy) {
|
1665
|
-
NAMED_LRI_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::cast_copy, YALE_STORAGE*, const YALE_STORAGE* rhs, nm::dtype_t new_dtype);
|
1666
|
-
|
1667
|
-
const YALE_STORAGE* casted_rhs = reinterpret_cast<const YALE_STORAGE*>(rhs);
|
1668
|
-
|
1669
|
-
return (STORAGE*)ttable[new_dtype][casted_rhs->dtype][casted_rhs->itype](casted_rhs, new_dtype);
|
1670
|
-
}
|
1671
|
-
|
1672
|
-
|
1673
|
-
/*
|
1674
|
-
* Returns size of Yale storage as a size_t (no matter what the itype is). (C accessor)
|
1675
|
-
*/
|
1676
|
-
size_t nm_yale_storage_get_size(const YALE_STORAGE* storage) {
|
1677
|
-
NAMED_ITYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::get_size, size_t, const YALE_STORAGE* storage);
|
1678
|
-
|
1679
|
-
return ttable[storage->itype](storage);
|
1680
|
-
}
|
1681
|
-
|
1682
|
-
|
1683
|
-
|
1684
|
-
/*
|
1685
|
-
* Return a void pointer to the matrix's default value entry.
|
1686
|
-
*/
|
1687
|
-
static void* default_value_ptr(const YALE_STORAGE* s) {
|
1688
|
-
return reinterpret_cast<void*>(reinterpret_cast<char*>(s->a) + (s->src->shape[0] * DTYPE_SIZES[s->dtype]));
|
1689
|
-
}
|
1690
|
-
|
1691
|
-
/*
|
1692
|
-
* Return the Ruby object at a given location in storage.
|
1693
|
-
*/
|
1694
|
-
static VALUE obj_at(YALE_STORAGE* s, size_t k) {
|
1695
|
-
if (s->dtype == nm::RUBYOBJ) return reinterpret_cast<VALUE*>(s->a)[k];
|
1696
|
-
else return rubyobj_from_cval(reinterpret_cast<void*>(reinterpret_cast<char*>(s->a) + k * DTYPE_SIZES[s->dtype]), s->dtype).rval;
|
1697
|
-
}
|
1698
|
-
|
1699
|
-
|
1700
|
-
/*
|
1701
|
-
* Return the matrix's default value as a Ruby VALUE.
|
1702
|
-
*/
|
1703
|
-
static VALUE default_value(const YALE_STORAGE* s) {
|
1704
|
-
if (s->dtype == nm::RUBYOBJ) return *reinterpret_cast<VALUE*>(default_value_ptr(s));
|
1705
|
-
else return rubyobj_from_cval(default_value_ptr(s), s->dtype).rval;
|
1706
|
-
}
|
1707
|
-
|
1708
|
-
|
1709
|
-
/*
|
1710
|
-
* Check to see if a default value is some form of zero. Easy for non-Ruby object matrices, which should always be 0.
|
1711
|
-
*/
|
1712
|
-
static bool default_value_is_numeric_zero(const YALE_STORAGE* s) {
|
1713
|
-
return rb_funcall(default_value(s), rb_intern("=="), 1, INT2FIX(0)) == Qtrue;
|
1714
|
-
}
|
1715
|
-
|
1716
|
-
|
1717
|
-
/*
|
1718
|
-
* C accessor for allocating a yale storage object for cast-copying. Copies the IJA vector, does not copy the A vector.
|
1719
|
-
*/
|
1720
|
-
static YALE_STORAGE* nm_copy_alloc_struct(const YALE_STORAGE* rhs, const nm::dtype_t new_dtype, const size_t new_capacity, const size_t new_size) {
|
1721
|
-
NAMED_ITYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::copy_alloc_struct, YALE_STORAGE*, const YALE_STORAGE* rhs, const nm::dtype_t new_dtype, const size_t new_capacity, const size_t new_size);
|
1722
|
-
|
1723
|
-
return ttable[rhs->itype](rhs, new_dtype, new_capacity, new_size);
|
1724
|
-
}
|
1725
|
-
|
1726
|
-
/*
|
1727
|
-
* Transposing copy constructor.
|
1728
|
-
*/
|
1729
|
-
STORAGE* nm_yale_storage_copy_transposed(const STORAGE* rhs_base) {
|
1730
|
-
YALE_STORAGE* rhs = (YALE_STORAGE*)rhs_base;
|
1731
|
-
|
1732
|
-
size_t* shape = ALLOC_N(size_t, 2);
|
1733
|
-
shape[0] = rhs->shape[1];
|
1734
|
-
shape[1] = rhs->shape[0];
|
1735
|
-
|
1736
|
-
size_t size = nm_yale_storage_get_size(rhs);
|
1737
|
-
|
1738
|
-
YALE_STORAGE* lhs = nm_yale_storage_create(rhs->dtype, shape, 2, size, rhs->itype);
|
1739
|
-
nm_yale_storage_init(lhs, default_value_ptr(rhs));
|
1740
|
-
|
1741
|
-
NAMED_LI_DTYPE_TEMPLATE_TABLE(transp, nm::math::transpose_yale, void, const size_t n, const size_t m, const void* ia_, const void* ja_, const void* a_, const bool diaga, void* ib_, void* jb_, void* b_, const bool move);
|
1742
|
-
|
1743
|
-
transp[lhs->dtype][lhs->itype](rhs->shape[0], rhs->shape[1], rhs->ija, rhs->ija, rhs->a, true, lhs->ija, lhs->ija, lhs->a, true);
|
1744
|
-
|
1745
|
-
return (STORAGE*)lhs;
|
1746
|
-
}
|
1747
|
-
|
1748
|
-
/*
|
1749
|
-
* C accessor for multiplying two YALE_STORAGE matrices, which have already been casted to the same dtype.
|
1750
|
-
*
|
1751
|
-
* FIXME: There should be some mathematical way to determine the worst-case IType based on the input ITypes. Right now
|
1752
|
-
* it just uses the default.
|
1753
|
-
*/
|
1754
|
-
STORAGE* nm_yale_storage_matrix_multiply(const STORAGE_PAIR& casted_storage, size_t* resulting_shape, bool vector) {
|
1755
|
-
LI_DTYPE_TEMPLATE_TABLE(nm::yale_storage::matrix_multiply, STORAGE*, const STORAGE_PAIR& casted_storage, size_t* resulting_shape, bool vector, nm::itype_t resulting_itype);
|
1756
|
-
|
1757
|
-
YALE_STORAGE* left = reinterpret_cast<YALE_STORAGE*>(casted_storage.left);
|
1758
|
-
YALE_STORAGE* right = reinterpret_cast<YALE_STORAGE*>(casted_storage.right);
|
1759
|
-
|
1760
|
-
if (!default_value_is_numeric_zero(left) || !default_value_is_numeric_zero(right)) {
|
1761
|
-
rb_raise(rb_eNotImpError, "matrix default value must be some form of zero (not false or nil) for multiplication");
|
1762
|
-
return NULL;
|
1763
|
-
}
|
1764
|
-
|
1765
|
-
// Determine the itype for the matrix that will be returned.
|
1766
|
-
nm::itype_t itype = nm_yale_storage_itype_by_shape(resulting_shape),
|
1767
|
-
max_itype = NM_MAX_ITYPE(left->itype, right->itype);
|
1768
|
-
if (static_cast<int8_t>(itype) < static_cast<int8_t>(max_itype)) itype = max_itype;
|
1769
|
-
|
1770
|
-
return ttable[left->dtype][itype](casted_storage, resulting_shape, vector, itype);
|
1771
|
-
}
|
1772
|
-
|
1773
|
-
|
1774
|
-
///////////////
|
1775
|
-
// Lifecycle //
|
1776
|
-
///////////////
|
1777
|
-
|
1778
|
-
/*
|
1779
|
-
* C accessor function for creating a YALE_STORAGE object. Prior to calling this function, you MUST
|
1780
|
-
* allocate shape (should be size_t * 2) -- don't use use a regular size_t array!
|
1781
|
-
*
|
1782
|
-
* For this type, dim must always be 2. The final argument is the initial capacity with which to
|
1783
|
-
* create the storage.
|
1784
|
-
*/
|
1785
|
-
|
1786
|
-
YALE_STORAGE* nm_yale_storage_create(nm::dtype_t dtype, size_t* shape, size_t dim, size_t init_capacity, nm::itype_t min_itype) {
|
1787
|
-
YALE_STORAGE* s;
|
1788
|
-
size_t max_capacity;
|
1789
|
-
|
1790
|
-
// FIXME: This error should be handled in the nmatrix.c file.
|
1791
|
-
if (dim != 2) {
|
1792
|
-
rb_raise(rb_eNotImpError, "Can only support 2D matrices");
|
1793
|
-
}
|
1794
|
-
|
1795
|
-
s = alloc(dtype, shape, dim, min_itype);
|
1796
|
-
max_capacity = nm::yale_storage::max_size(s);
|
1797
|
-
|
1798
|
-
// Set matrix capacity (and ensure its validity)
|
1799
|
-
if (init_capacity < NM_YALE_MINIMUM(s)) {
|
1800
|
-
s->capacity = NM_YALE_MINIMUM(s);
|
1801
|
-
|
1802
|
-
} else if (init_capacity > max_capacity) {
|
1803
|
-
// Don't allow storage to be created larger than necessary
|
1804
|
-
s->capacity = max_capacity;
|
1805
|
-
|
1806
|
-
} else {
|
1807
|
-
s->capacity = init_capacity;
|
1808
|
-
|
1809
|
-
}
|
1810
|
-
|
1811
|
-
s->ija = ALLOC_N( char, ITYPE_SIZES[s->itype] * s->capacity );
|
1812
|
-
s->a = ALLOC_N( char, DTYPE_SIZES[s->dtype] * s->capacity );
|
1813
|
-
|
1814
|
-
return s;
|
1815
|
-
}
|
1816
|
-
|
1817
|
-
/*
|
1818
|
-
* Destructor for yale storage (C-accessible).
|
1819
|
-
*/
|
1820
|
-
void nm_yale_storage_delete(STORAGE* s) {
|
1821
|
-
if (s) {
|
1822
|
-
YALE_STORAGE* storage = (YALE_STORAGE*)s;
|
1823
|
-
if (storage->count-- == 1) {
|
1824
|
-
xfree(storage->shape);
|
1825
|
-
xfree(storage->offset);
|
1826
|
-
xfree(storage->ija);
|
1827
|
-
xfree(storage->a);
|
1828
|
-
xfree(storage);
|
1829
|
-
}
|
1830
|
-
}
|
1831
|
-
}
|
1832
|
-
|
1833
|
-
/*
|
1834
|
-
* Destructor for the yale storage ref
|
1835
|
-
*/
|
1836
|
-
void nm_yale_storage_delete_ref(STORAGE* s) {
|
1837
|
-
if (s) {
|
1838
|
-
YALE_STORAGE* storage = (YALE_STORAGE*)s;
|
1839
|
-
nm_yale_storage_delete( reinterpret_cast<STORAGE*>(storage->src) );
|
1840
|
-
xfree(storage->shape);
|
1841
|
-
xfree(storage->offset);
|
1842
|
-
xfree(s);
|
1843
|
-
}
|
1844
|
-
}
|
1845
|
-
|
1846
|
-
/*
|
1847
|
-
* C accessor for yale_storage::init, a templated function.
|
1848
|
-
*
|
1849
|
-
* Initializes the IJA vector of the YALE_STORAGE matrix.
|
1850
|
-
*/
|
1851
|
-
void nm_yale_storage_init(YALE_STORAGE* s, void* init_val) {
|
1852
|
-
NAMED_LI_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::init, void, YALE_STORAGE*, void*);
|
1853
|
-
|
1854
|
-
ttable[s->dtype][s->itype](s, init_val);
|
1855
|
-
}
|
1856
|
-
|
1857
|
-
|
1858
|
-
/*
|
1859
|
-
* Ruby GC mark function for YALE_STORAGE. C accessible.
|
1860
|
-
*/
|
1861
|
-
void nm_yale_storage_mark(void* storage_base) {
|
1862
|
-
YALE_STORAGE* storage = (YALE_STORAGE*)storage_base;
|
1863
|
-
size_t i;
|
1864
|
-
|
1865
|
-
if (storage && storage->dtype == nm::RUBYOBJ) {
|
1866
|
-
for (i = storage->capacity; i-- > 0;) {
|
1867
|
-
rb_gc_mark(*((VALUE*)((char*)(storage->a) + i*DTYPE_SIZES[nm::RUBYOBJ])));
|
1868
|
-
}
|
1869
|
-
}
|
1870
|
-
}
|
1871
|
-
|
1872
|
-
|
1873
|
-
/*
|
1874
|
-
* Allocates and initializes the basic struct (but not the IJA or A vectors).
|
1875
|
-
*/
|
1876
|
-
static YALE_STORAGE* alloc(nm::dtype_t dtype, size_t* shape, size_t dim, nm::itype_t min_itype) {
|
1877
|
-
YALE_STORAGE* s;
|
1878
|
-
|
1879
|
-
s = ALLOC( YALE_STORAGE );
|
1880
|
-
|
1881
|
-
s->ndnz = 0;
|
1882
|
-
s->dtype = dtype;
|
1883
|
-
s->shape = shape;
|
1884
|
-
s->offset = ALLOC_N(size_t, dim);
|
1885
|
-
for (size_t i = 0; i < dim; ++i)
|
1886
|
-
s->offset[i] = 0;
|
1887
|
-
s->dim = dim;
|
1888
|
-
s->itype = nm_yale_storage_itype_by_shape(shape);
|
1889
|
-
s->src = reinterpret_cast<STORAGE*>(s);
|
1890
|
-
s->count = 1;
|
1891
|
-
|
1892
|
-
// See if a higher itype has been requested.
|
1893
|
-
if (static_cast<int8_t>(s->itype) < static_cast<int8_t>(min_itype))
|
1894
|
-
s->itype = min_itype;
|
1895
|
-
|
1896
|
-
return s;
|
1897
|
-
}
|
1898
|
-
|
1899
|
-
YALE_STORAGE* nm_yale_storage_create_from_old_yale(nm::dtype_t dtype, size_t* shape, void* ia, void* ja, void* a, nm::dtype_t from_dtype) {
|
1900
|
-
|
1901
|
-
NAMED_LRI_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::create_from_old_yale, YALE_STORAGE*, nm::dtype_t dtype, size_t* shape, void* r_ia, void* r_ja, void* r_a);
|
1902
|
-
|
1903
|
-
// With C++ templates, we don't want to have a 4-parameter template. That would be LDType, RDType, LIType, RIType.
|
1904
|
-
// We can prevent that by copying ia and ja into the correct itype (if necessary) before passing them to the yale
|
1905
|
-
// copy constructor.
|
1906
|
-
nm::itype_t to_itype = nm_yale_storage_itype_by_shape(shape);
|
1907
|
-
|
1908
|
-
return ttable[dtype][from_dtype][to_itype](dtype, shape, ia, ja, a);
|
1909
|
-
|
1910
|
-
}
|
1911
|
-
|
1912
|
-
//////////////////////////////////////////////
|
1913
|
-
// YALE-SPECIFIC FUNCTIONS (RUBY ACCESSORS) //
|
1914
|
-
//////////////////////////////////////////////
|
1915
|
-
|
1916
|
-
/*
|
1917
|
-
* call-seq:
|
1918
|
-
* yale_size -> Integer
|
1919
|
-
*
|
1920
|
-
* Get the size of a Yale matrix (the number of elements actually stored).
|
1921
|
-
*
|
1922
|
-
* For capacity (the maximum number of elements that can be stored without a resize), use capacity instead.
|
1923
|
-
*/
|
1924
|
-
static VALUE nm_size(VALUE self) {
|
1925
|
-
YALE_STORAGE* s = (YALE_STORAGE*)NM_STORAGE(self);
|
1926
|
-
|
1927
|
-
return rubyobj_from_cval_by_itype((char*)(s->ija) + ITYPE_SIZES[s->itype]*(s->shape[0]), s->itype).rval;
|
1928
|
-
}
|
1929
|
-
|
1930
|
-
|
1931
|
-
/*
|
1932
|
-
* call-seq:
|
1933
|
-
* yale_a -> Array
|
1934
|
-
* yale_d(index) -> ...
|
1935
|
-
*
|
1936
|
-
* Get the A array of a Yale matrix (which stores the diagonal and the LU portions of the matrix).
|
1937
|
-
*/
|
1938
|
-
static VALUE nm_a(int argc, VALUE* argv, VALUE self) {
|
1939
|
-
VALUE idx;
|
1940
|
-
rb_scan_args(argc, argv, "01", &idx);
|
1941
|
-
|
1942
|
-
YALE_STORAGE* s = NM_STORAGE_YALE(self);
|
1943
|
-
size_t size = nm_yale_storage_get_size(s);
|
1944
|
-
|
1945
|
-
if (idx == Qnil) {
|
1946
|
-
VALUE* vals = ALLOCA_N(VALUE, size);
|
1947
|
-
|
1948
|
-
if (NM_DTYPE(self) == nm::RUBYOBJ) {
|
1949
|
-
for (size_t i = 0; i < size; ++i) {
|
1950
|
-
vals[i] = reinterpret_cast<VALUE*>(s->a)[i];
|
1951
|
-
}
|
1952
|
-
} else {
|
1953
|
-
for (size_t i = 0; i < size; ++i) {
|
1954
|
-
vals[i] = rubyobj_from_cval((char*)(s->a) + DTYPE_SIZES[s->dtype]*i, s->dtype).rval;
|
1955
|
-
}
|
1956
|
-
}
|
1957
|
-
VALUE ary = rb_ary_new4(size, vals);
|
1958
|
-
|
1959
|
-
for (size_t i = size; i < reinterpret_cast<YALE_STORAGE*>(s->src)->capacity; ++i)
|
1960
|
-
rb_ary_push(ary, Qnil);
|
1961
|
-
|
1962
|
-
return ary;
|
1963
|
-
} else {
|
1964
|
-
size_t index = FIX2INT(idx);
|
1965
|
-
if (index >= size) rb_raise(rb_eRangeError, "out of range");
|
1966
|
-
|
1967
|
-
return rubyobj_from_cval((char*)(s->a) + DTYPE_SIZES[s->dtype] * index, s->dtype).rval;
|
1968
|
-
}
|
1969
|
-
}
|
1970
|
-
|
1971
|
-
|
1972
|
-
/*
|
1973
|
-
* call-seq:
|
1974
|
-
* yale_d -> Array
|
1975
|
-
* yale_d(index) -> ...
|
1976
|
-
*
|
1977
|
-
* Get the diagonal ("D") portion of the A array of a Yale matrix.
|
1978
|
-
*/
|
1979
|
-
static VALUE nm_d(int argc, VALUE* argv, VALUE self) {
|
1980
|
-
VALUE idx;
|
1981
|
-
rb_scan_args(argc, argv, "01", &idx);
|
1982
|
-
|
1983
|
-
YALE_STORAGE* s = NM_STORAGE_YALE(self);
|
1984
|
-
|
1985
|
-
if (idx == Qnil) {
|
1986
|
-
VALUE* vals = ALLOCA_N(VALUE, s->shape[0]);
|
1987
|
-
|
1988
|
-
if (NM_DTYPE(self) == nm::RUBYOBJ) {
|
1989
|
-
for (size_t i = 0; i < s->shape[0]; ++i) {
|
1990
|
-
vals[i] = reinterpret_cast<VALUE*>(s->a)[i];
|
1991
|
-
}
|
1992
|
-
} else {
|
1993
|
-
for (size_t i = 0; i < s->shape[0]; ++i) {
|
1994
|
-
vals[i] = rubyobj_from_cval((char*)(s->a) + DTYPE_SIZES[s->dtype]*i, s->dtype).rval;
|
1995
|
-
}
|
1996
|
-
}
|
1997
|
-
|
1998
|
-
return rb_ary_new4(s->shape[0], vals);
|
1999
|
-
} else {
|
2000
|
-
size_t index = FIX2INT(idx);
|
2001
|
-
if (index >= s->shape[0]) rb_raise(rb_eRangeError, "out of range");
|
2002
|
-
|
2003
|
-
return rubyobj_from_cval((char*)(s->a) + DTYPE_SIZES[s->dtype] * index, s->dtype).rval;
|
2004
|
-
}
|
2005
|
-
}
|
2006
|
-
|
2007
|
-
/*
|
2008
|
-
* call-seq:
|
2009
|
-
* yale_lu -> Array
|
2010
|
-
*
|
2011
|
-
* Get the non-diagonal ("LU") portion of the A array of a Yale matrix.
|
2012
|
-
*/
|
2013
|
-
static VALUE nm_lu(VALUE self) {
|
2014
|
-
YALE_STORAGE* s = NM_STORAGE_YALE(self);
|
2015
|
-
|
2016
|
-
size_t size = nm_yale_storage_get_size(s);
|
2017
|
-
|
2018
|
-
VALUE* vals = ALLOCA_N(VALUE, size - s->shape[0] - 1);
|
2019
|
-
|
2020
|
-
if (NM_DTYPE(self) == nm::RUBYOBJ) {
|
2021
|
-
for (size_t i = 0; i < size - s->shape[0] - 1; ++i) {
|
2022
|
-
vals[i] = reinterpret_cast<VALUE*>(s->a)[s->shape[0] + 1 + i];
|
2023
|
-
}
|
2024
|
-
} else {
|
2025
|
-
for (size_t i = 0; i < size - s->shape[0] - 1; ++i) {
|
2026
|
-
vals[i] = rubyobj_from_cval((char*)(s->a) + DTYPE_SIZES[s->dtype]*(s->shape[0] + 1 + i), s->dtype).rval;
|
2027
|
-
}
|
2028
|
-
}
|
2029
|
-
|
2030
|
-
VALUE ary = rb_ary_new4(size - s->shape[0] - 1, vals);
|
2031
|
-
|
2032
|
-
for (size_t i = size; i < reinterpret_cast<YALE_STORAGE*>(s->src)->capacity; ++i)
|
2033
|
-
rb_ary_push(ary, Qnil);
|
2034
|
-
|
2035
|
-
return ary;
|
2036
|
-
}
|
2037
|
-
|
2038
|
-
/*
|
2039
|
-
* call-seq:
|
2040
|
-
* yale_ia -> Array
|
2041
|
-
*
|
2042
|
-
* Get the IA portion of the IJA array of a Yale matrix. This gives the start and end positions of rows in the
|
2043
|
-
* JA and LU portions of the IJA and A arrays, respectively.
|
2044
|
-
*/
|
2045
|
-
static VALUE nm_ia(VALUE self) {
|
2046
|
-
YALE_STORAGE* s = NM_STORAGE_YALE(self);
|
2047
|
-
|
2048
|
-
VALUE* vals = ALLOCA_N(VALUE, s->shape[0] + 1);
|
2049
|
-
|
2050
|
-
for (size_t i = 0; i < s->shape[0] + 1; ++i) {
|
2051
|
-
vals[i] = rubyobj_from_cval_by_itype((char*)(s->ija) + ITYPE_SIZES[s->itype]*i, s->itype).rval;
|
2052
|
-
}
|
2053
|
-
|
2054
|
-
return rb_ary_new4(s->shape[0]+1, vals);
|
2055
|
-
}
|
2056
|
-
|
2057
|
-
/*
|
2058
|
-
* call-seq:
|
2059
|
-
* yale_ja -> Array
|
2060
|
-
*
|
2061
|
-
* Get the JA portion of the IJA array of a Yale matrix. This gives the column indices for entries in corresponding
|
2062
|
-
* positions in the LU portion of the A array.
|
2063
|
-
*/
|
2064
|
-
static VALUE nm_ja(VALUE self) {
|
2065
|
-
YALE_STORAGE* s = NM_STORAGE_YALE(self);
|
2066
|
-
|
2067
|
-
size_t size = nm_yale_storage_get_size(s);
|
2068
|
-
|
2069
|
-
VALUE* vals = ALLOCA_N(VALUE, size - s->shape[0] - 1);
|
2070
|
-
|
2071
|
-
for (size_t i = 0; i < size - s->shape[0] - 1; ++i) {
|
2072
|
-
vals[i] = rubyobj_from_cval_by_itype((char*)(s->ija) + ITYPE_SIZES[s->itype]*(s->shape[0] + 1 + i), s->itype).rval;
|
2073
|
-
}
|
2074
|
-
|
2075
|
-
VALUE ary = rb_ary_new4(size - s->shape[0] - 1, vals);
|
2076
|
-
|
2077
|
-
for (size_t i = size; i < reinterpret_cast<YALE_STORAGE*>(s->src)->capacity; ++i)
|
2078
|
-
rb_ary_push(ary, Qnil);
|
2079
|
-
|
2080
|
-
return ary;
|
2081
|
-
}
|
2082
|
-
|
2083
|
-
/*
|
2084
|
-
* call-seq:
|
2085
|
-
* yale_ija -> Array
|
2086
|
-
* yale_ija(index) -> ...
|
2087
|
-
*
|
2088
|
-
* Get the IJA array of a Yale matrix (or a component of the IJA array).
|
2089
|
-
*/
|
2090
|
-
static VALUE nm_ija(int argc, VALUE* argv, VALUE self) {
|
2091
|
-
VALUE idx;
|
2092
|
-
rb_scan_args(argc, argv, "01", &idx);
|
2093
|
-
|
2094
|
-
YALE_STORAGE* s = NM_STORAGE_YALE(self);
|
2095
|
-
size_t size = nm_yale_storage_get_size(s);
|
2096
|
-
|
2097
|
-
if (idx == Qnil) {
|
2098
|
-
|
2099
|
-
VALUE* vals = ALLOCA_N(VALUE, size);
|
2100
|
-
|
2101
|
-
for (size_t i = 0; i < size; ++i) {
|
2102
|
-
vals[i] = rubyobj_from_cval_by_itype((char*)(s->ija) + ITYPE_SIZES[s->itype]*i, s->itype).rval;
|
2103
|
-
}
|
2104
|
-
|
2105
|
-
VALUE ary = rb_ary_new4(size, vals);
|
2106
|
-
|
2107
|
-
for (size_t i = size; i < reinterpret_cast<YALE_STORAGE*>(s->src)->capacity; ++i)
|
2108
|
-
rb_ary_push(ary, Qnil);
|
2109
|
-
|
2110
|
-
return ary;
|
2111
|
-
|
2112
|
-
} else {
|
2113
|
-
size_t index = FIX2INT(idx);
|
2114
|
-
if (index >= size) rb_raise(rb_eRangeError, "out of range");
|
2115
|
-
|
2116
|
-
return rubyobj_from_cval_by_itype((char*)(s->ija) + ITYPE_SIZES[s->itype] * index, s->itype).rval;
|
2117
|
-
}
|
2118
|
-
}
|
2119
|
-
|
2120
|
-
|
2121
|
-
/*
|
2122
|
-
* call-seq:
|
2123
|
-
* yale_nd_row -> ...
|
2124
|
-
*
|
2125
|
-
* This function gets the non-diagonal contents of a Yale matrix row.
|
2126
|
-
* The first argument should be the row index. The optional second argument may be :hash or :keys, but defaults
|
2127
|
-
* to :hash. If :keys is given, it will only return the Hash keys (the column indices).
|
2128
|
-
*
|
2129
|
-
* This function is meant to accomplish its purpose as efficiently as possible. It does not check for appropriate
|
2130
|
-
* range.
|
2131
|
-
*/
|
2132
|
-
static VALUE nm_nd_row(int argc, VALUE* argv, VALUE self) {
|
2133
|
-
VALUE i_, as;
|
2134
|
-
rb_scan_args(argc, argv, "11", &i_, &as);
|
2135
|
-
|
2136
|
-
bool keys = false;
|
2137
|
-
if (as != Qnil && rb_to_id(as) != nm_rb_hash) keys = true;
|
2138
|
-
|
2139
|
-
size_t i = FIX2INT(i_);
|
2140
|
-
|
2141
|
-
YALE_STORAGE* s = NM_STORAGE_YALE(self);
|
2142
|
-
nm::dtype_t dtype = NM_DTYPE(self);
|
2143
|
-
nm::itype_t itype = NM_ITYPE(self);
|
2144
|
-
|
2145
|
-
// get the position as a size_t
|
2146
|
-
// TODO: Come up with a faster way to get this than transforming to a Ruby object first.
|
2147
|
-
size_t pos = FIX2INT(rubyobj_from_cval_by_itype((char*)(s->ija) + ITYPE_SIZES[itype]*i, itype).rval);
|
2148
|
-
size_t nextpos = FIX2INT(rubyobj_from_cval_by_itype((char*)(s->ija) + ITYPE_SIZES[itype]*(i+1), itype).rval);
|
2149
|
-
size_t diff = nextpos - pos;
|
2150
|
-
|
2151
|
-
VALUE ret;
|
2152
|
-
if (keys) {
|
2153
|
-
ret = rb_ary_new3(diff);
|
2154
|
-
|
2155
|
-
for (size_t idx = pos; idx < nextpos; ++idx) {
|
2156
|
-
rb_ary_store(ret, idx - pos, rubyobj_from_cval_by_itype((char*)(s->ija) + ITYPE_SIZES[s->itype]*idx, s->itype).rval);
|
2157
|
-
}
|
2158
|
-
|
2159
|
-
} else {
|
2160
|
-
ret = rb_hash_new();
|
2161
|
-
|
2162
|
-
for (size_t idx = pos; idx < nextpos; ++idx) {
|
2163
|
-
rb_hash_aset(ret, rubyobj_from_cval_by_itype((char*)(s->ija) + ITYPE_SIZES[s->itype]*idx, s->itype).rval,
|
2164
|
-
rubyobj_from_cval((char*)(s->a) + DTYPE_SIZES[s->dtype]*idx, s->dtype).rval);
|
2165
|
-
}
|
2166
|
-
}
|
2167
|
-
|
2168
|
-
return ret;
|
2169
|
-
}
|
2170
|
-
|
2171
|
-
/*
|
2172
|
-
* call-seq:
|
2173
|
-
* yale_vector_set(i, column_index_array, cell_contents_array, pos) -> Fixnum
|
2174
|
-
*
|
2175
|
-
* Insert at position pos an array of non-diagonal elements with column indices given. Note that the column indices and values
|
2176
|
-
* must be storage-contiguous -- that is, you can't insert them around existing elements in some row, only amid some
|
2177
|
-
* elements in some row. You *can* insert them around a diagonal element, since this is stored separately. This function
|
2178
|
-
* may not be used for the insertion of diagonal elements in most cases, as these are already present in the data
|
2179
|
-
* structure and are typically modified by replacement rather than insertion.
|
2180
|
-
*
|
2181
|
-
* The last argument, pos, may be nil if you want to insert at the beginning of a row. Otherwise it needs to be provided.
|
2182
|
-
* Don't expect this function to know the difference. It really does very little checking, because its goal is to make
|
2183
|
-
* multiple contiguous insertion as quick as possible.
|
2184
|
-
*
|
2185
|
-
* You should also not attempt to insert values which are the default (0). These are not supposed to be stored, and may
|
2186
|
-
* lead to undefined behavior.
|
2187
|
-
*
|
2188
|
-
* Example:
|
2189
|
-
* m.yale_vector_set(3, [0,3,4], [1,1,1], 15)
|
2190
|
-
*
|
2191
|
-
* The example above inserts the values 1, 1, and 1 in columns 0, 3, and 4, assumed to be located at position 15 (which
|
2192
|
-
* corresponds to row 3).
|
2193
|
-
*
|
2194
|
-
* Example:
|
2195
|
-
* next = m.yale_vector_set(3, [0,3,4], [1,1,1])
|
2196
|
-
*
|
2197
|
-
* This example determines that i=3 is at position 15 automatically. The value returned, next, is the position where the
|
2198
|
-
* next value(s) should be inserted.
|
2199
|
-
*/
|
2200
|
-
VALUE nm_vector_set(int argc, VALUE* argv, VALUE self) { //, VALUE i_, VALUE jv, VALUE vv, VALUE pos_) {
|
2201
|
-
|
2202
|
-
// i, jv, vv are mandatory; pos is optional; thus "31"
|
2203
|
-
VALUE i_, jv, vv, pos_;
|
2204
|
-
rb_scan_args(argc, argv, "31", &i_, &jv, &vv, &pos_);
|
2205
|
-
|
2206
|
-
size_t len = RARRAY_LEN(jv); // need length in order to read the arrays in
|
2207
|
-
size_t vvlen = RARRAY_LEN(vv);
|
2208
|
-
if (len != vvlen)
|
2209
|
-
rb_raise(rb_eArgError, "lengths must match between j array (%d) and value array (%d)", len, vvlen);
|
2210
|
-
|
2211
|
-
YALE_STORAGE* s = NM_STORAGE_YALE(self);
|
2212
|
-
nm::dtype_t dtype = NM_DTYPE(self);
|
2213
|
-
nm::itype_t itype = NM_ITYPE(self);
|
2214
|
-
|
2215
|
-
size_t i = FIX2INT(i_); // get the row
|
2216
|
-
|
2217
|
-
// get the position as a size_t
|
2218
|
-
// TODO: Come up with a faster way to get this than transforming to a Ruby object first.
|
2219
|
-
if (pos_ == Qnil) pos_ = rubyobj_from_cval_by_itype((char*)(s->ija) + ITYPE_SIZES[itype]*i, itype).rval;
|
2220
|
-
size_t pos = FIX2INT(pos_);
|
2221
|
-
|
2222
|
-
// Allocate the j array and the values array
|
2223
|
-
size_t* j = ALLOCA_N(size_t, len);
|
2224
|
-
void* vals = ALLOCA_N(char, DTYPE_SIZES[dtype] * len);
|
2225
|
-
|
2226
|
-
// Copy array contents
|
2227
|
-
for (size_t idx = 0; idx < len; ++idx) {
|
2228
|
-
j[idx] = FIX2INT(rb_ary_entry(jv, idx));
|
2229
|
-
rubyval_to_cval(rb_ary_entry(vv, idx), dtype, (char*)vals + idx * DTYPE_SIZES[dtype]);
|
2230
|
-
}
|
2231
|
-
|
2232
|
-
char ins_type = nm_yale_storage_vector_insert(s, pos, j, vals, len, false, dtype, itype);
|
2233
|
-
nm_yale_storage_increment_ia_after(s, s->shape[0], i, len, itype);
|
2234
|
-
reinterpret_cast<YALE_STORAGE*>(s->src)->ndnz += len;
|
2235
|
-
|
2236
|
-
// Return the updated position
|
2237
|
-
pos += len;
|
2238
|
-
return INT2FIX(pos);
|
2239
|
-
}
|
2240
|
-
|
2241
|
-
|
2242
|
-
|
2243
|
-
|
2244
|
-
/*
|
2245
|
-
* call-seq:
|
2246
|
-
* __yale_default_value__ -> ...
|
2247
|
-
*
|
2248
|
-
* Get the default_value property from a yale matrix.
|
2249
|
-
*/
|
2250
|
-
VALUE nm_yale_default_value(VALUE self) {
|
2251
|
-
return default_value(NM_STORAGE_YALE(self));
|
2252
|
-
}
|
2253
|
-
|
2254
|
-
|
2255
|
-
/*
|
2256
|
-
* call-seq:
|
2257
|
-
* __yale_map_merged_stored__(right) -> Enumerator
|
2258
|
-
*
|
2259
|
-
* A map operation on two Yale matrices which only iterates across the stored indices.
|
2260
|
-
*/
|
2261
|
-
VALUE nm_yale_map_merged_stored(VALUE left, VALUE right, VALUE init) {
|
2262
|
-
YALE_STORAGE *s = NM_STORAGE_YALE(left),
|
2263
|
-
*t = NM_STORAGE_YALE(right);
|
2264
|
-
|
2265
|
-
ITYPE_TEMPLATE_TABLE(nm::yale_storage::map_merged_stored, VALUE, VALUE l, VALUE r, VALUE init, nm::itype_t)
|
2266
|
-
|
2267
|
-
nm::itype_t itype = NM_MAX_ITYPE(s->itype, t->itype);
|
2268
|
-
return ttable[itype](left, right, init, itype);
|
2269
|
-
}
|
2270
|
-
|
2271
|
-
|
2272
|
-
/*
|
2273
|
-
* call-seq:
|
2274
|
-
* __yale_map_stored__ -> Enumerator
|
2275
|
-
*
|
2276
|
-
* A map operation on two Yale matrices which only iterates across the stored indices.
|
2277
|
-
*/
|
2278
|
-
VALUE nm_yale_map_stored(VALUE self) {
|
2279
|
-
ITYPE_TEMPLATE_TABLE(nm::yale_storage::map_stored, VALUE, VALUE)
|
2280
|
-
|
2281
|
-
return ttable[NM_ITYPE(self)](self);
|
2282
|
-
}
|
2283
|
-
|
2284
|
-
} // end of extern "C" block
|