pnmatrix 1.2.4 → 2.0.1
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/ext/nmatrix/extconf.rb +6 -6
- data/ext/nmatrix/math/math.h +2 -2
- data/ext/nmatrix/nmatrix.h +10 -1
- data/ext/nmatrix/ruby_nmatrix.c +44 -9
- data/ext/nmatrix/storage/dense/dense.cpp +35 -12
- data/ext/nmatrix/storage/list/list.cpp +6 -2
- data/ext/nmatrix/storage/yale/class.h +71 -16
- data/ext/nmatrix/storage/yale/iterators/row.h +5 -5
- data/ext/nmatrix/storage/yale/yale.cpp +23 -4
- data/lib/nmatrix/homogeneous.rb +3 -3
- data/lib/nmatrix/mkmf.rb +6 -3
- data/lib/nmatrix/nmatrix.rb +3 -3
- data/lib/nmatrix/shortcuts.rb +12 -15
- data/lib/nmatrix/version.rb +3 -4
- data/spec/io_spec.rb +3 -1
- data/spec/lapack_core_spec.rb +13 -8
- data/spec/math_spec.rb +33 -13
- data/spec/stress_test_spec.rb +181 -0
- metadata +14 -14
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a13074d0b3e5e2bbee2a456f42fe5d5d7a4b4e581e2ae42e149d74f4a1452211
|
|
4
|
+
data.tar.gz: 917446ac40ebe45b13a62c6c0e1e927227beee199a411e2c0cc1fa83f5e9f1bc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 697596b71a0d302a785d4fe503676fe89392b60e84354fba5151b873f2bbf570e11f6086972c30bdf610eb9a2621387b5bf469c82280e8586888241773a3668f
|
|
7
|
+
data.tar.gz: 2a42bf5f5e4c97748e6d44aa631086e94c18bdb0b6f56503ce236d97a57cb8da0a272a016686b88d656f67f86d82124f9bef5bb7fbbfe39b91deefd6c657810d
|
data/ext/nmatrix/extconf.rb
CHANGED
|
@@ -54,13 +54,13 @@ $srcs = basenames.map { |b| "#{b}.cpp" }
|
|
|
54
54
|
create_conf_h("nmatrix_config.h")
|
|
55
55
|
create_makefile("nmatrix")
|
|
56
56
|
|
|
57
|
-
Dir.mkdir("data") unless Dir.
|
|
58
|
-
Dir.mkdir("util") unless Dir.
|
|
59
|
-
Dir.mkdir("storage") unless Dir.
|
|
57
|
+
Dir.mkdir("data") unless Dir.exist?("data")
|
|
58
|
+
Dir.mkdir("util") unless Dir.exist?("util")
|
|
59
|
+
Dir.mkdir("storage") unless Dir.exist?("storage")
|
|
60
60
|
Dir.chdir("storage") do
|
|
61
|
-
Dir.mkdir("yale") unless Dir.
|
|
62
|
-
Dir.mkdir("list") unless Dir.
|
|
63
|
-
Dir.mkdir("dense") unless Dir.
|
|
61
|
+
Dir.mkdir("yale") unless Dir.exist?("yale")
|
|
62
|
+
Dir.mkdir("list") unless Dir.exist?("list")
|
|
63
|
+
Dir.mkdir("dense") unless Dir.exist?("dense")
|
|
64
64
|
end
|
|
65
65
|
|
|
66
66
|
# to clean up object files in subdirectories:
|
data/ext/nmatrix/math/math.h
CHANGED
|
@@ -339,12 +339,12 @@ namespace smmp_sort {
|
|
|
339
339
|
if (a < b) {
|
|
340
340
|
if (b < c) return b; // a b c
|
|
341
341
|
if (a < c) return c; // a c b
|
|
342
|
-
|
|
342
|
+
return a; // c a b
|
|
343
343
|
|
|
344
344
|
} else { // a > b
|
|
345
345
|
if (a < c) return a; // b a c
|
|
346
346
|
if (b < c) return c; // b c a
|
|
347
|
-
|
|
347
|
+
return b; // c b a
|
|
348
348
|
}
|
|
349
349
|
}
|
|
350
350
|
|
data/ext/nmatrix/nmatrix.h
CHANGED
|
@@ -338,6 +338,7 @@ NM_DEF_STRUCT_POST(NMATRIX); // };
|
|
|
338
338
|
/* Structs for dealing with VALUEs in use so that they don't get GC'd */
|
|
339
339
|
|
|
340
340
|
NM_DEF_STRUCT_PRE(NM_GC_LL_NODE); // struct NM_GC_LL_NODE {
|
|
341
|
+
VALUE* key; // VALUE* key;
|
|
341
342
|
VALUE* val; // VALUE* val;
|
|
342
343
|
size_t n; // size_t n;
|
|
343
344
|
NM_DECL_STRUCT(NM_GC_LL_NODE*, next); // NM_GC_LL_NODE* next;
|
|
@@ -375,6 +376,14 @@ NM_DEF_STRUCT_POST(NM_GC_HOLDER); // };
|
|
|
375
376
|
// enum for a list of possible storage types.
|
|
376
377
|
#define NM_STYPE(val) (NM_STRUCT(val)->stype)
|
|
377
378
|
|
|
379
|
+
/*
|
|
380
|
+
* Object dtype storage keeps Ruby VALUEs in malloc-backed arrays/lists, not in
|
|
381
|
+
* Ruby object fields. After mutating that native storage, make the wrapper
|
|
382
|
+
* object write-barrier-unprotected so minor GC runs nm_mark and sees newly
|
|
383
|
+
* assigned VALUEs before they can be collected.
|
|
384
|
+
*/
|
|
385
|
+
#define NM_OBJECT_STORAGE_WB_UNPROTECT(val) RB_OBJ_WB_UNPROTECT(val)
|
|
386
|
+
|
|
378
387
|
// Get the shape of the ith dimension (int)
|
|
379
388
|
#define NM_SHAPE(val,i) (NM_STORAGE(val)->shape[(i)])
|
|
380
389
|
|
|
@@ -401,7 +410,7 @@ NM_DEF_STRUCT_POST(NM_GC_HOLDER); // };
|
|
|
401
410
|
|
|
402
411
|
#define NM_CHECK_ALLOC(x) if (!x) rb_raise(rb_eNoMemError, "insufficient memory");
|
|
403
412
|
|
|
404
|
-
#define RB_FILE_EXISTS(fn) (rb_funcall(rb_const_get(rb_cObject, rb_intern("File")), rb_intern("
|
|
413
|
+
#define RB_FILE_EXISTS(fn) (rb_funcall(rb_const_get(rb_cObject, rb_intern("File")), rb_intern("exist?"), 1, (fn)) == Qtrue)
|
|
405
414
|
|
|
406
415
|
#define IsNMatrixType(v) (RB_TYPE_P(v, T_DATA) && (RDATA(v)->dfree == (RUBY_DATA_FUNC)nm_delete || RDATA(v)->dfree == (RUBY_DATA_FUNC)nm_delete_ref))
|
|
407
416
|
#define CheckNMatrixType(v) if (!IsNMatrixType(v)) rb_raise(rb_eTypeError, "expected NMatrix on left-hand side of operation");
|
data/ext/nmatrix/ruby_nmatrix.c
CHANGED
|
@@ -409,10 +409,17 @@ void Init_nmatrix() {
|
|
|
409
409
|
*/
|
|
410
410
|
static VALUE nm_alloc(VALUE klass) {
|
|
411
411
|
NMATRIX* mat = NM_ALLOC(NMATRIX);
|
|
412
|
+
mat->stype = nm::DENSE_STORE;
|
|
412
413
|
mat->storage = NULL;
|
|
413
414
|
|
|
414
|
-
|
|
415
|
-
|
|
415
|
+
/*
|
|
416
|
+
* Newly allocated NMatrix wrappers do not have storage yet, but nm_mark is
|
|
417
|
+
* explicitly null-storage safe. Installing it here is required for object
|
|
418
|
+
* dtype matrices after initialization: otherwise Ruby never asks the matrix
|
|
419
|
+
* to mark VALUEs stored in native dense/list/Yale memory, so a later GC can
|
|
420
|
+
* reclaim objects that were assigned into the matrix.
|
|
421
|
+
*/
|
|
422
|
+
return Data_Wrap_Struct(klass, nm_mark, nm_delete, mat);
|
|
416
423
|
}
|
|
417
424
|
|
|
418
425
|
/*
|
|
@@ -454,6 +461,8 @@ static VALUE nm_capacity(VALUE self) {
|
|
|
454
461
|
* Mark function.
|
|
455
462
|
*/
|
|
456
463
|
void nm_mark(NMATRIX* mat) {
|
|
464
|
+
if (!mat || !mat->storage) return;
|
|
465
|
+
|
|
457
466
|
STYPE_MARK_TABLE(mark)
|
|
458
467
|
mark[mat->stype](mat->storage);
|
|
459
468
|
}
|
|
@@ -463,12 +472,17 @@ void nm_mark(NMATRIX* mat) {
|
|
|
463
472
|
* Destructor.
|
|
464
473
|
*/
|
|
465
474
|
void nm_delete(NMATRIX* mat) {
|
|
475
|
+
if (!mat) return;
|
|
476
|
+
|
|
466
477
|
static void (*ttable[nm::NUM_STYPES])(STORAGE*) = {
|
|
467
478
|
nm_dense_storage_delete,
|
|
468
479
|
nm_list_storage_delete,
|
|
469
480
|
nm_yale_storage_delete
|
|
470
481
|
};
|
|
471
|
-
|
|
482
|
+
|
|
483
|
+
if (mat->storage && mat->stype < nm::NUM_STYPES) {
|
|
484
|
+
ttable[mat->stype](mat->storage);
|
|
485
|
+
}
|
|
472
486
|
|
|
473
487
|
NM_FREE(mat);
|
|
474
488
|
}
|
|
@@ -477,12 +491,17 @@ void nm_delete(NMATRIX* mat) {
|
|
|
477
491
|
* Slicing destructor.
|
|
478
492
|
*/
|
|
479
493
|
void nm_delete_ref(NMATRIX* mat) {
|
|
494
|
+
if (!mat) return;
|
|
495
|
+
|
|
480
496
|
static void (*ttable[nm::NUM_STYPES])(STORAGE*) = {
|
|
481
497
|
nm_dense_storage_delete_ref,
|
|
482
498
|
nm_list_storage_delete_ref,
|
|
483
499
|
nm_yale_storage_delete_ref
|
|
484
500
|
};
|
|
485
|
-
|
|
501
|
+
|
|
502
|
+
if (mat->storage && mat->stype < nm::NUM_STYPES) {
|
|
503
|
+
ttable[mat->stype](mat->storage);
|
|
504
|
+
}
|
|
486
505
|
|
|
487
506
|
NM_FREE(mat);
|
|
488
507
|
}
|
|
@@ -503,7 +522,9 @@ static void __nm_mark_value_container(NM_GC_HOLDER* gc_value_holder_struct) {
|
|
|
503
522
|
if (gc_value_holder_struct && gc_value_holder_struct->start) {
|
|
504
523
|
NM_GC_LL_NODE* curr = gc_value_holder_struct->start;
|
|
505
524
|
while (curr) {
|
|
506
|
-
|
|
525
|
+
if (curr->val && curr->n) {
|
|
526
|
+
rb_gc_mark_locations(curr->val, curr->val + curr->n);
|
|
527
|
+
}
|
|
507
528
|
curr = curr->next;
|
|
508
529
|
}
|
|
509
530
|
}
|
|
@@ -540,7 +561,13 @@ void nm_register_values(VALUE* values, size_t n) {
|
|
|
540
561
|
} else {
|
|
541
562
|
to_insert = NM_ALLOC_NONRUBY(NM_GC_LL_NODE);
|
|
542
563
|
}
|
|
543
|
-
to_insert->
|
|
564
|
+
to_insert->key = values;
|
|
565
|
+
if (n) {
|
|
566
|
+
to_insert->val = NM_ALLOC_N(VALUE, n);
|
|
567
|
+
memcpy(to_insert->val, values, sizeof(VALUE) * n);
|
|
568
|
+
} else {
|
|
569
|
+
to_insert->val = NULL;
|
|
570
|
+
}
|
|
544
571
|
to_insert->n = n;
|
|
545
572
|
to_insert->next = gc_value_holder_struct->start;
|
|
546
573
|
gc_value_holder_struct->start = to_insert;
|
|
@@ -557,13 +584,17 @@ void nm_unregister_values(VALUE* values, size_t n) {
|
|
|
557
584
|
NM_GC_LL_NODE* curr = gc_value_holder_struct->start;
|
|
558
585
|
NM_GC_LL_NODE* last = NULL;
|
|
559
586
|
while (curr) {
|
|
560
|
-
if (curr->
|
|
587
|
+
if (curr->key == values) {
|
|
561
588
|
if (last) {
|
|
562
589
|
last->next = curr->next;
|
|
563
590
|
} else {
|
|
564
591
|
gc_value_holder_struct->start = curr->next;
|
|
565
592
|
}
|
|
593
|
+
if (curr->val) {
|
|
594
|
+
NM_FREE(curr->val);
|
|
595
|
+
}
|
|
566
596
|
curr->next = allocated_pool->start;
|
|
597
|
+
curr->key = NULL;
|
|
567
598
|
curr->val = NULL;
|
|
568
599
|
curr->n = 0;
|
|
569
600
|
allocated_pool->start = curr;
|
|
@@ -602,14 +633,18 @@ void nm_completely_unregister_value(VALUE* val) {
|
|
|
602
633
|
NM_GC_LL_NODE* curr = gc_value_holder_struct->start;
|
|
603
634
|
NM_GC_LL_NODE* last = NULL;
|
|
604
635
|
while (curr) {
|
|
605
|
-
if (curr->
|
|
636
|
+
if (curr->key == val) {
|
|
606
637
|
if (last) {
|
|
607
638
|
last->next = curr->next;
|
|
608
639
|
} else {
|
|
609
640
|
gc_value_holder_struct->start = curr->next;
|
|
610
641
|
}
|
|
611
642
|
NM_GC_LL_NODE* temp_next = curr->next;
|
|
643
|
+
if (curr->val) {
|
|
644
|
+
NM_FREE(curr->val);
|
|
645
|
+
}
|
|
612
646
|
curr->next = allocated_pool->start;
|
|
647
|
+
curr->key = NULL;
|
|
613
648
|
curr->val = NULL;
|
|
614
649
|
curr->n = 0;
|
|
615
650
|
allocated_pool->start = curr;
|
|
@@ -1144,7 +1179,7 @@ static VALUE nm_init_new_version(int argc, VALUE* argv, VALUE self) {
|
|
|
1144
1179
|
// Get the shape.
|
|
1145
1180
|
size_t dim;
|
|
1146
1181
|
size_t* shape = interpret_shape(shape_ary, &dim);
|
|
1147
|
-
void* init;
|
|
1182
|
+
void* init = NULL;
|
|
1148
1183
|
void* v = NULL;
|
|
1149
1184
|
size_t v_size = 0;
|
|
1150
1185
|
|
|
@@ -102,17 +102,21 @@ namespace nm { namespace dense_storage {
|
|
|
102
102
|
* Recursive function, sets multiple values in a matrix from a single source value. Same basic pattern as slice_copy.
|
|
103
103
|
*/
|
|
104
104
|
template <typename D>
|
|
105
|
-
static void slice_set(DENSE_STORAGE* dest, size_t* lengths, size_t pdest, size_t rank, D* const v, size_t v_size, size_t& v_offset) {
|
|
105
|
+
static void slice_set(VALUE owner, DENSE_STORAGE* dest, size_t* lengths, size_t pdest, size_t rank, D* const v, size_t v_size, size_t& v_offset) {
|
|
106
106
|
if (dest->dim - rank > 1) {
|
|
107
107
|
for (size_t i = 0; i < lengths[rank]; ++i) {
|
|
108
|
-
slice_set<D>(dest, lengths, pdest + dest->stride[rank] * i, rank + 1, v, v_size, v_offset);
|
|
108
|
+
slice_set<D>(owner, dest, lengths, pdest + dest->stride[rank] * i, rank + 1, v, v_size, v_offset);
|
|
109
109
|
}
|
|
110
110
|
} else {
|
|
111
111
|
for (size_t p = 0; p < lengths[rank]; ++p, ++v_offset) {
|
|
112
112
|
if (v_offset >= v_size) v_offset %= v_size;
|
|
113
113
|
|
|
114
114
|
D* elem = reinterpret_cast<D*>(dest->elements);
|
|
115
|
-
|
|
115
|
+
if (dest->dtype == nm::RUBYOBJ) {
|
|
116
|
+
RB_OBJ_WRITE(owner, reinterpret_cast<VALUE*>(elem) + p + pdest, reinterpret_cast<VALUE*>(v)[v_offset]);
|
|
117
|
+
} else {
|
|
118
|
+
elem[p + pdest] = v[v_offset];
|
|
119
|
+
}
|
|
116
120
|
}
|
|
117
121
|
}
|
|
118
122
|
}
|
|
@@ -127,6 +131,9 @@ namespace nm { namespace dense_storage {
|
|
|
127
131
|
NM_CONSERVATIVE(nm_register_value(&right));
|
|
128
132
|
|
|
129
133
|
DENSE_STORAGE* s = NM_STORAGE_DENSE(left);
|
|
134
|
+
if (s->dtype == nm::RUBYOBJ) {
|
|
135
|
+
NM_OBJECT_STORAGE_WB_UNPROTECT(left);
|
|
136
|
+
}
|
|
130
137
|
|
|
131
138
|
std::pair<NMATRIX*,bool> nm_and_free =
|
|
132
139
|
interpret_arg_as_dense_nmatrix(right, s->dtype);
|
|
@@ -158,10 +165,15 @@ namespace nm { namespace dense_storage {
|
|
|
158
165
|
}
|
|
159
166
|
|
|
160
167
|
if (slice->single) {
|
|
161
|
-
|
|
168
|
+
size_t pos = nm_dense_storage_pos(s, slice->coords);
|
|
169
|
+
if (s->dtype == nm::RUBYOBJ) {
|
|
170
|
+
RB_OBJ_WRITE(left, reinterpret_cast<VALUE*>(s->elements) + pos, *reinterpret_cast<VALUE*>(v));
|
|
171
|
+
} else {
|
|
172
|
+
reinterpret_cast<D*>(s->elements)[pos] = *v;
|
|
173
|
+
}
|
|
162
174
|
} else {
|
|
163
175
|
size_t v_offset = 0;
|
|
164
|
-
slice_set(s, slice->lengths, nm_dense_storage_pos(s, slice->coords), 0, v, v_size, v_offset);
|
|
176
|
+
slice_set(left, s, slice->lengths, nm_dense_storage_pos(s, slice->coords), 0, v, v_size, v_offset);
|
|
165
177
|
}
|
|
166
178
|
|
|
167
179
|
// Only free v if it was allocated in this function.
|
|
@@ -311,14 +323,25 @@ void nm_dense_storage_mark(STORAGE* storage_base) {
|
|
|
311
323
|
DENSE_STORAGE* storage = (DENSE_STORAGE*)storage_base;
|
|
312
324
|
|
|
313
325
|
if (storage && storage->dtype == nm::RUBYOBJ) {
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
326
|
+
/*
|
|
327
|
+
* Dense slice references share the source storage's elements pointer and
|
|
328
|
+
* may start at a non-zero offset. Marking only the reference's element
|
|
329
|
+
* count from offset zero can miss Ruby objects that are visible through
|
|
330
|
+
* the reference. Mark the backing source storage so every object that a
|
|
331
|
+
* live reference can expose stays alive.
|
|
332
|
+
*/
|
|
333
|
+
DENSE_STORAGE* src = reinterpret_cast<DENSE_STORAGE*>(storage->src);
|
|
334
|
+
|
|
335
|
+
size_t count = src ? nm_storage_count_max_elements(src) : 0;
|
|
336
|
+
if (src && src->elements && count > 0) {
|
|
337
|
+
VALUE* els = reinterpret_cast<VALUE*>(src->elements);
|
|
338
|
+
/*
|
|
339
|
+
* This storage holds exact Ruby VALUEs, not arbitrary machine words.
|
|
340
|
+
* Mark each slot explicitly so objects assigned into malloc-backed
|
|
341
|
+
* matrix memory are kept alive just like Ruby object fields would be.
|
|
342
|
+
*/
|
|
343
|
+
for (size_t i = 0; i < count; ++i) rb_gc_mark(els[i]);
|
|
318
344
|
}
|
|
319
|
-
//for (size_t index = nm_storage_count_max_elements(storage); index-- > 0;) {
|
|
320
|
-
// rb_gc_mark(els[index]);
|
|
321
|
-
//}
|
|
322
345
|
}
|
|
323
346
|
}
|
|
324
347
|
|
|
@@ -538,6 +538,9 @@ void set(VALUE left, SLICE* slice, VALUE right) {
|
|
|
538
538
|
NM_CONSERVATIVE(nm_register_value(&left));
|
|
539
539
|
NM_CONSERVATIVE(nm_register_value(&right));
|
|
540
540
|
LIST_STORAGE* s = NM_STORAGE_LIST(left);
|
|
541
|
+
if (s->dtype == nm::RUBYOBJ) {
|
|
542
|
+
NM_OBJECT_STORAGE_WB_UNPROTECT(left);
|
|
543
|
+
}
|
|
541
544
|
|
|
542
545
|
std::pair<NMATRIX*,bool> nm_and_free =
|
|
543
546
|
interpret_arg_as_dense_nmatrix(right, NM_DTYPE(left));
|
|
@@ -779,7 +782,7 @@ void nm_list_storage_unregister(const STORAGE* s) {
|
|
|
779
782
|
*/
|
|
780
783
|
static NODE* list_storage_get_single_node(LIST_STORAGE* s, SLICE* slice) {
|
|
781
784
|
LIST* l = s->rows;
|
|
782
|
-
NODE* n;
|
|
785
|
+
NODE* n = NULL;
|
|
783
786
|
|
|
784
787
|
for (size_t r = 0; r < s->dim; r++) {
|
|
785
788
|
n = nm::list::find(l, s->offset[r] + slice->coords[r]);
|
|
@@ -1160,11 +1163,12 @@ void* nm_list_storage_get(const STORAGE* storage, SLICE* slice) {
|
|
|
1160
1163
|
void* nm_list_storage_ref(const STORAGE* storage, SLICE* slice) {
|
|
1161
1164
|
LIST_STORAGE* s = (LIST_STORAGE*)storage;
|
|
1162
1165
|
LIST_STORAGE* ns = NULL;
|
|
1166
|
+
NODE* n = NULL;
|
|
1163
1167
|
nm_list_storage_register(s);
|
|
1164
1168
|
|
|
1165
1169
|
//TODO: It needs a refactoring.
|
|
1166
1170
|
if (slice->single) {
|
|
1167
|
-
|
|
1171
|
+
n = list_storage_get_single_node(s, slice);
|
|
1168
1172
|
nm_list_storage_unregister(s);
|
|
1169
1173
|
return (n ? n->val : s->default_val);
|
|
1170
1174
|
} else {
|
|
@@ -50,7 +50,18 @@ public:
|
|
|
50
50
|
: s(reinterpret_cast<YALE_STORAGE*>(storage->src)),
|
|
51
51
|
slice(storage != storage->src),
|
|
52
52
|
slice_shape(storage->shape),
|
|
53
|
-
slice_offset(storage->offset)
|
|
53
|
+
slice_offset(storage->offset),
|
|
54
|
+
owner(Qnil)
|
|
55
|
+
{
|
|
56
|
+
nm_yale_storage_register(storage->src);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
YaleStorage(const YALE_STORAGE* storage, VALUE owner_)
|
|
60
|
+
: s(reinterpret_cast<YALE_STORAGE*>(storage->src)),
|
|
61
|
+
slice(storage != storage->src),
|
|
62
|
+
slice_shape(storage->shape),
|
|
63
|
+
slice_offset(storage->offset),
|
|
64
|
+
owner(owner_)
|
|
54
65
|
{
|
|
55
66
|
nm_yale_storage_register(storage->src);
|
|
56
67
|
}
|
|
@@ -59,7 +70,8 @@ public:
|
|
|
59
70
|
: s(reinterpret_cast<YALE_STORAGE*>(storage->src)),
|
|
60
71
|
slice(storage != storage->src),
|
|
61
72
|
slice_shape(storage->shape),
|
|
62
|
-
slice_offset(storage->offset)
|
|
73
|
+
slice_offset(storage->offset),
|
|
74
|
+
owner(Qnil)
|
|
63
75
|
{
|
|
64
76
|
nm_yale_storage_register(reinterpret_cast<STORAGE*>(storage->src));
|
|
65
77
|
}
|
|
@@ -96,6 +108,17 @@ public:
|
|
|
96
108
|
inline const D& a(size_t p) const { return a_p()[p]; }
|
|
97
109
|
inline D& a(size_t p) { return a_p()[p]; }
|
|
98
110
|
|
|
111
|
+
// Object matrices store Ruby VALUEs in native memory, so replacing a value must
|
|
112
|
+
// go through Ruby's write barrier. Without it, generational GC can collect a
|
|
113
|
+
// newly assigned object before the matrix is marked again.
|
|
114
|
+
inline void write_a(size_t p, const D& val) {
|
|
115
|
+
if (dtype() == nm::RUBYOBJ && owner != Qnil) {
|
|
116
|
+
RB_OBJ_WRITE(owner, reinterpret_cast<VALUE*>(s->a) + p, reinterpret_cast<const VALUE*>(&val)[0]);
|
|
117
|
+
} else {
|
|
118
|
+
a(p) = val;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
99
122
|
bool real_row_empty(size_t i) const { return ija(i+1) - ija(i) == 0 ? true : false; }
|
|
100
123
|
|
|
101
124
|
inline size_t* shape_p() const { return slice_shape; }
|
|
@@ -467,7 +490,7 @@ public:
|
|
|
467
490
|
* A pseudo-insert operation, since the diagonal portion of the A array is constant size.
|
|
468
491
|
*/
|
|
469
492
|
stored_diagonal_iterator insert(stored_diagonal_iterator position, const D& val) {
|
|
470
|
-
|
|
493
|
+
write_a(position.p(), val);
|
|
471
494
|
return position;
|
|
472
495
|
}
|
|
473
496
|
|
|
@@ -694,24 +717,37 @@ public:
|
|
|
694
717
|
|
|
695
718
|
E* ns_a = reinterpret_cast<E*>(ns.a);
|
|
696
719
|
size_t sz = shape(0) + 1; // current used size of ns
|
|
697
|
-
|
|
720
|
+
E converted = val;
|
|
721
|
+
if (ns.dtype == nm::RUBYOBJ) {
|
|
722
|
+
nm_register_value(reinterpret_cast<VALUE*>(&converted));
|
|
723
|
+
}
|
|
698
724
|
|
|
699
725
|
// FIXME: If diagonals line up, it's probably faster to do this with stored diagonal and stored non-diagonal iterators
|
|
700
726
|
for (const_row_iterator it = cribegin(); it != criend(); ++it) {
|
|
701
727
|
for (auto jt = it.begin(); !jt.end(); ++jt) {
|
|
702
728
|
if (it.i() == jt.j()) {
|
|
703
|
-
if (Yield)
|
|
704
|
-
else
|
|
729
|
+
if (Yield) converted = E(rb_yield(~jt));
|
|
730
|
+
else converted = static_cast<E>(*jt);
|
|
731
|
+
if (ns.dtype == nm::RUBYOBJ) {
|
|
732
|
+
nm_register_value(reinterpret_cast<VALUE*>(&converted));
|
|
733
|
+
}
|
|
734
|
+
ns_a[it.i()] = converted;
|
|
705
735
|
} else if (*jt != const_default_obj()) {
|
|
706
|
-
if (Yield)
|
|
707
|
-
else
|
|
736
|
+
if (Yield) converted = E(rb_yield(~jt));
|
|
737
|
+
else converted = static_cast<E>(*jt);
|
|
738
|
+
if (ns.dtype == nm::RUBYOBJ) {
|
|
739
|
+
nm_register_value(reinterpret_cast<VALUE*>(&converted));
|
|
740
|
+
}
|
|
741
|
+
ns_a[sz] = converted;
|
|
708
742
|
ns.ija[sz] = jt.j();
|
|
709
743
|
++sz;
|
|
710
744
|
}
|
|
711
745
|
}
|
|
712
746
|
ns.ija[it.i()+1] = sz;
|
|
713
747
|
}
|
|
714
|
-
|
|
748
|
+
if (ns.dtype == nm::RUBYOBJ) {
|
|
749
|
+
nm_unregister_value(reinterpret_cast<VALUE*>(&converted));
|
|
750
|
+
}
|
|
715
751
|
|
|
716
752
|
//ns.ija[shape(0)] = sz; // indicate end of last row
|
|
717
753
|
ns.ndnz = sz - shape(0) - 1; // update ndnz count
|
|
@@ -751,14 +787,24 @@ public:
|
|
|
751
787
|
|
|
752
788
|
E* la = reinterpret_cast<E*>(lhs->a);
|
|
753
789
|
|
|
754
|
-
|
|
790
|
+
E converted;
|
|
791
|
+
if (lhs->dtype == nm::RUBYOBJ) {
|
|
792
|
+
nm_register_value(reinterpret_cast<VALUE*>(&converted));
|
|
793
|
+
}
|
|
755
794
|
for (size_t m = 0; m < size(); ++m) {
|
|
756
795
|
if (Yield) {
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
796
|
+
converted = E(rb_yield(nm::yale_storage::nm_rb_dereference(a(m))));
|
|
797
|
+
} else {
|
|
798
|
+
converted = static_cast<E>(a(m));
|
|
799
|
+
}
|
|
800
|
+
if (lhs->dtype == nm::RUBYOBJ) {
|
|
801
|
+
nm_register_value(reinterpret_cast<VALUE*>(&converted));
|
|
802
|
+
}
|
|
803
|
+
la[m] = converted;
|
|
804
|
+
}
|
|
805
|
+
if (lhs->dtype == nm::RUBYOBJ) {
|
|
806
|
+
nm_unregister_value(reinterpret_cast<VALUE*>(&converted));
|
|
760
807
|
}
|
|
761
|
-
nm_yale_storage_unregister(lhs);
|
|
762
808
|
|
|
763
809
|
}
|
|
764
810
|
|
|
@@ -995,10 +1041,18 @@ protected:
|
|
|
995
1041
|
if (v_offset >= v_size) v_offset %= v_size;
|
|
996
1042
|
|
|
997
1043
|
if (j + real_j == i + real_i) { // modify diagonal
|
|
998
|
-
|
|
1044
|
+
if (s->dtype == nm::RUBYOBJ && owner != Qnil) {
|
|
1045
|
+
RB_OBJ_WRITE(owner, reinterpret_cast<VALUE*>(new_a) + real_i + i, reinterpret_cast<const VALUE*>(v)[v_offset]);
|
|
1046
|
+
} else {
|
|
1047
|
+
new_a[real_i + i] = v[v_offset];
|
|
1048
|
+
}
|
|
999
1049
|
} else if (v[v_offset] != const_default_obj()) {
|
|
1000
1050
|
new_ija[q] = j + real_j;
|
|
1001
|
-
|
|
1051
|
+
if (s->dtype == nm::RUBYOBJ && owner != Qnil) {
|
|
1052
|
+
RB_OBJ_WRITE(owner, reinterpret_cast<VALUE*>(new_a) + q, reinterpret_cast<const VALUE*>(v)[v_offset]);
|
|
1053
|
+
} else {
|
|
1054
|
+
new_a[q] = v[v_offset];
|
|
1055
|
+
}
|
|
1002
1056
|
++q; // move on to next q location
|
|
1003
1057
|
}
|
|
1004
1058
|
|
|
@@ -1132,6 +1186,7 @@ protected:
|
|
|
1132
1186
|
bool slice;
|
|
1133
1187
|
size_t* slice_shape;
|
|
1134
1188
|
size_t* slice_offset;
|
|
1189
|
+
VALUE owner;
|
|
1135
1190
|
};
|
|
1136
1191
|
|
|
1137
1192
|
} // end of nm namespace
|
|
@@ -303,7 +303,7 @@ public:
|
|
|
303
303
|
while (!position.end() && position.j() < jj) ++position; // position is just a hint. (This loop ideally only has to happen once.)
|
|
304
304
|
|
|
305
305
|
if (!position.end() && position.j() == jj) {
|
|
306
|
-
|
|
306
|
+
y.write_a(position.p(), val); // replace existing
|
|
307
307
|
} else {
|
|
308
308
|
|
|
309
309
|
if (sz + 1 > y.capacity()) {
|
|
@@ -313,7 +313,7 @@ public:
|
|
|
313
313
|
y.update_real_row_sizes_from(real_i(), 1);
|
|
314
314
|
}
|
|
315
315
|
ija(position.p()) = jj + y.offset(1); // set column ID
|
|
316
|
-
|
|
316
|
+
y.write_a(position.p(), val);
|
|
317
317
|
adjust_length(1);
|
|
318
318
|
}
|
|
319
319
|
|
|
@@ -331,7 +331,7 @@ public:
|
|
|
331
331
|
*/
|
|
332
332
|
//template <typename = typename std::enable_if<!std::is_const<RefType>::value>::type>
|
|
333
333
|
void insert(size_t j, const D& val) {
|
|
334
|
-
if (j + y.offset(1) == real_i())
|
|
334
|
+
if (j + y.offset(1) == real_i()) y.write_a(real_i(), val);
|
|
335
335
|
else {
|
|
336
336
|
row_stored_nd_iterator jt = ndfind(j);
|
|
337
337
|
if (!jt.end() && jt.j() == j) {
|
|
@@ -404,10 +404,10 @@ public:
|
|
|
404
404
|
if (v_offset >= v_size) v_offset %= v_size; // reset v position.
|
|
405
405
|
|
|
406
406
|
if (jc + y.offset(1) == real_i()) {
|
|
407
|
-
y.
|
|
407
|
+
y.write_a(real_i(), v[v_offset]); // modify diagonal
|
|
408
408
|
} else if (v[v_offset] != y.const_default_obj()) {
|
|
409
409
|
y.ija(pp) = jc; // modify non-diagonal
|
|
410
|
-
y.
|
|
410
|
+
y.write_a(pp, v[v_offset]);
|
|
411
411
|
++pp;
|
|
412
412
|
}
|
|
413
413
|
}
|
|
@@ -369,7 +369,10 @@ YALE_STORAGE* ref(YALE_STORAGE* s, SLICE* slice) {
|
|
|
369
369
|
template <typename DType>
|
|
370
370
|
void set(VALUE left, SLICE* slice, VALUE right) {
|
|
371
371
|
YALE_STORAGE* storage = NM_STORAGE_YALE(left);
|
|
372
|
-
|
|
372
|
+
if (storage->dtype == nm::RUBYOBJ) {
|
|
373
|
+
NM_OBJECT_STORAGE_WB_UNPROTECT(left);
|
|
374
|
+
}
|
|
375
|
+
YaleStorage<DType> y(storage, left);
|
|
373
376
|
y.insert(slice, right);
|
|
374
377
|
}
|
|
375
378
|
|
|
@@ -1456,9 +1459,25 @@ void nm_yale_storage_mark(STORAGE* storage_base) {
|
|
|
1456
1459
|
YALE_STORAGE* storage = (YALE_STORAGE*)storage_base;
|
|
1457
1460
|
|
|
1458
1461
|
if (storage && storage->dtype == nm::RUBYOBJ) {
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
+
/*
|
|
1463
|
+
* Yale slice references are lightweight wrappers around another
|
|
1464
|
+
* YALE_STORAGE. They may have no A array or capacity of their own, but
|
|
1465
|
+
* Ruby objects returned through the reference still live in the source
|
|
1466
|
+
* storage. Mark the source's initialized A entries so GC cannot reclaim
|
|
1467
|
+
* those objects while the reference is alive.
|
|
1468
|
+
*/
|
|
1469
|
+
YALE_STORAGE* src = reinterpret_cast<YALE_STORAGE*>(storage->src);
|
|
1470
|
+
|
|
1471
|
+
size_t size = src ? nm::yale_storage::get_size(src) : 0;
|
|
1472
|
+
if (src && src->a && size > 0) {
|
|
1473
|
+
VALUE* a = reinterpret_cast<VALUE*>(src->a);
|
|
1474
|
+
/*
|
|
1475
|
+
* Yale object storage keeps real Ruby VALUEs in the native A array.
|
|
1476
|
+
* Marking them exactly is important after mutation, because otherwise
|
|
1477
|
+
* GC may reclaim recently assigned objects before later iteration.
|
|
1478
|
+
*/
|
|
1479
|
+
for (size_t i = 0; i < size; ++i) rb_gc_mark(a[i]);
|
|
1480
|
+
}
|
|
1462
1481
|
}
|
|
1463
1482
|
}
|
|
1464
1483
|
|
data/lib/nmatrix/homogeneous.rb
CHANGED
|
@@ -69,7 +69,7 @@ class NMatrix
|
|
|
69
69
|
NMatrix.new(4, [1.0, 0.0, 0.0, 0.0,
|
|
70
70
|
0.0, c, -s, 0.0,
|
|
71
71
|
0.0, s, c, 0.0,
|
|
72
|
-
0.0, 0.0, 0.0, 1.0], {dtype: :float64}.merge(opts))
|
|
72
|
+
0.0, 0.0, 0.0, 1.0], **{dtype: :float64}.merge(opts))
|
|
73
73
|
end
|
|
74
74
|
|
|
75
75
|
def y_rotation angle_in_radians, opts={}
|
|
@@ -78,7 +78,7 @@ class NMatrix
|
|
|
78
78
|
NMatrix.new(4, [ c, 0.0, s, 0.0,
|
|
79
79
|
0.0, 1.0, 0.0, 0.0,
|
|
80
80
|
-s, 0.0, c, 0.0,
|
|
81
|
-
0.0, 0.0, 0.0, 1.0], {dtype: :float64}.merge(opts))
|
|
81
|
+
0.0, 0.0, 0.0, 1.0], **{dtype: :float64}.merge(opts))
|
|
82
82
|
end
|
|
83
83
|
|
|
84
84
|
def z_rotation angle_in_radians, opts={}
|
|
@@ -87,7 +87,7 @@ class NMatrix
|
|
|
87
87
|
NMatrix.new(4, [ c, -s, 0.0, 0.0,
|
|
88
88
|
s, c, 0.0, 0.0,
|
|
89
89
|
0.0, 0.0, 1.0, 0.0,
|
|
90
|
-
0.0, 0.0, 0.0, 1.0], {dtype: :float64}.merge(opts))
|
|
90
|
+
0.0, 0.0, 0.0, 1.0], **{dtype: :float64}.merge(opts))
|
|
91
91
|
end
|
|
92
92
|
|
|
93
93
|
|
data/lib/nmatrix/mkmf.rb
CHANGED
|
@@ -32,7 +32,7 @@ def find_newer_gplusplus #:nodoc:
|
|
|
32
32
|
[9,8,7,6,5,4,3].each do |minor|
|
|
33
33
|
ver = "4.#{minor}"
|
|
34
34
|
gpp = "g++-#{ver}"
|
|
35
|
-
result = `
|
|
35
|
+
result = `type #{gpp}`
|
|
36
36
|
next if result.empty?
|
|
37
37
|
CONFIG['CXX'] = gpp
|
|
38
38
|
puts ver
|
|
@@ -69,14 +69,17 @@ if CONFIG['CXX'] == 'clang++'
|
|
|
69
69
|
$CXX_STANDARD = 'c++11'
|
|
70
70
|
else
|
|
71
71
|
version = gplusplus_version
|
|
72
|
-
|
|
72
|
+
major_version = version.split('.')[0].to_i # string comparison fails past 10.0.0
|
|
73
|
+
|
|
74
|
+
if major_version < 5 and
|
|
75
|
+
version < '4.3.0' && CONFIG['CXX'] == 'g++' # see if we can find a newer G++, unless it's been overridden by user
|
|
73
76
|
if !find_newer_gplusplus
|
|
74
77
|
raise("You need a version of g++ which supports -std=c++0x or -std=c++11. If you're on a Mac and using Homebrew, we recommend using mac-brew-gcc.sh to install a more recent g++.")
|
|
75
78
|
end
|
|
76
79
|
version = gplusplus_version
|
|
77
80
|
end
|
|
78
81
|
|
|
79
|
-
if version < '4.7.0'
|
|
82
|
+
if major_version < 5 and version < '4.7.0'
|
|
80
83
|
$CXX_STANDARD = 'c++0x'
|
|
81
84
|
else
|
|
82
85
|
$CXX_STANDARD = 'c++11'
|
data/lib/nmatrix/nmatrix.rb
CHANGED
|
@@ -727,7 +727,7 @@ class NMatrix
|
|
|
727
727
|
end
|
|
728
728
|
|
|
729
729
|
# Do the actual construction.
|
|
730
|
-
n = NMatrix.new(new_shape, opts)
|
|
730
|
+
n = NMatrix.new(new_shape, **opts)
|
|
731
731
|
|
|
732
732
|
# Figure out where to start concatenation. We don't know where it will end,
|
|
733
733
|
# because each matrix may have own size along concat dimension.
|
|
@@ -1034,7 +1034,7 @@ class NMatrix
|
|
|
1034
1034
|
def clone_structure(capacity = nil)
|
|
1035
1035
|
opts = {stype: self.stype, default: self.default_value, dtype: self.dtype}
|
|
1036
1036
|
opts = {capacity: capacity}.merge(opts) if self.yale?
|
|
1037
|
-
NMatrix.new(self.shape, opts)
|
|
1037
|
+
NMatrix.new(self.shape, **opts)
|
|
1038
1038
|
end
|
|
1039
1039
|
|
|
1040
1040
|
#
|
|
@@ -1099,7 +1099,7 @@ protected
|
|
|
1099
1099
|
# We can generally predict the change in capacity for Yale.
|
|
1100
1100
|
opts = {capacity: self.capacity - self.shape[0] + new_shape[0]}.merge(opts)
|
|
1101
1101
|
end
|
|
1102
|
-
NMatrix.new(new_shape, opts)
|
|
1102
|
+
NMatrix.new(new_shape, **opts)
|
|
1103
1103
|
end
|
|
1104
1104
|
|
|
1105
1105
|
|