Skip to content

Commit 5e7f70c

Browse files
committed
Yale insertion refactored, including slice-setting. Now just need to
enable to spec and fix the bugs.
1 parent 96feb8b commit 5e7f70c

File tree

9 files changed

+81
-101
lines changed

9 files changed

+81
-101
lines changed

.rspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
--color
2-
--format nested
2+
--format RSpec::Longrun::Formatter

ext/nmatrix/storage/yale/class.h

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ class YaleStorage {
312312
*/
313313
void insert(row_iterator i, size_t j, size_t* lengths, D* const v, size_t v_size) {
314314
// Expensive pre-processing step: find all the information we need in order to do insertions.
315-
multi_row_insertion_plan p = multi_row_insertion_plan(i, j, lengths, v, v_size);
315+
multi_row_insertion_plan p = insertion_plan(i, j, lengths, v, v_size);
316316

317317
// There are more efficient ways to do this, but this is the low hanging fruit version of the algorithm.
318318
// Here's the full problem: http://stackoverflow.com/questions/18753375/algorithm-for-merging-short-lists-into-a-long-vector
@@ -331,7 +331,7 @@ class YaleStorage {
331331
size_t v_offset = 0;
332332
int accum = 0;
333333
for (size_t ii = 0; ii < lengths[0]; ++ii, ++i) {
334-
i.insert(row_stored_nd_iterator(&i, p.pos[ii]), j, lengths[1], v, v_size, v_offset);
334+
i.insert(row_stored_nd_iterator(i, p.pos[ii]), j, lengths[1], v, v_size, v_offset);
335335
}
336336
}
337337
}
@@ -343,18 +343,17 @@ class YaleStorage {
343343
* +right+ and determine what other functions to call in order to properly handle
344344
* it.
345345
*/
346-
row_nd_iter_pair insert(SLICE* slice, VALUE right) {
346+
void insert(SLICE* slice, VALUE right) {
347347
if (TYPE(right) == T_DATA) {
348348
if (RDATA(right)->dfree == (RUBY_DATA_FUNC)nm_delete || RDATA(right)->dfree == (RUBY_DATA_FUNC)nm_delete_ref) {
349349
rb_raise(rb_eNotImpError, "this type of slicing not yet supported");
350350
} else {
351351
rb_raise(rb_eTypeError, "unrecognized type for slice assignment");
352352
}
353-
row_iterator it = riend();
354-
return std::make_pair(it, it.ndend());
355353
} else {
356354

357355
D* v;
356+
358357
size_t v_size = 1;
359358
bool v_alloc = false;
360359

@@ -372,21 +371,15 @@ class YaleStorage {
372371

373372
row_iterator i = ribegin(slice->coords[0]);
374373

375-
376-
377374
if (slice->single || (slice->lengths[0] == 1 && slice->lengths[1] == 1)) { // single entry
378-
row_stored_nd_iterator j = i.insert(slice->coords[1], *v);
379-
if (v_alloc) xfree(v);
380-
return std::make_pair(i,j);
375+
i.insert(slice->coords[1], *v);
381376
} else if (slice->lengths[0] == 1) { // single row, multiple entries
382-
row_stored_nd_iterator j = i.insert(slice->coords[1], slice->lengths[1], v, v_size);
383-
if (v_alloc) xfree(v);
384-
return std::make_pair(i,j);
377+
i.insert(slice->coords[1], slice->lengths[1], v, v_size);
385378
} else { // multiple rows, unknown number of entries
386-
row_nd_iter_pair ij = insert(i, slice->coords[1], slice->lengths, v, v_size);
387-
if (v_alloc) xfree(v);
388-
return ij;
379+
insert(i, slice->coords[1], slice->lengths, v, v_size);
389380
}
381+
382+
if (v_alloc) xfree(v);
390383
}
391384
}
392385

@@ -676,18 +669,15 @@ class YaleStorage {
676669
for (const_row_iterator it = cribegin(); it != criend(); ++it) {
677670
for (auto jt = it.begin(); !jt.end(); ++jt) {
678671
if (it.i() == jt.j()) {
679-
std::cerr << "copy(ns): writing to diag pos " << it.i() << std::endl;
680672
if (Yield) ns_a[it.i()] = rb_yield(~jt);
681673
else ns_a[it.i()] = static_cast<E>(*jt);
682674
} else if (*jt != const_default_obj()) {
683-
std::cerr << "copy(ns): writing to pos " << sz << std::endl;
684675
if (Yield) ns_a[sz] = rb_yield(~jt);
685676
else ns_a[sz] = static_cast<E>(*jt);
686677
ns.ija[sz] = jt.j();
687678
++sz;
688679
}
689680
}
690-
std::cerr << "copy(ns): updating row end pointer for row " << it.i() << " to " << sz << std::endl;
691681
ns.ija[it.i()+1] = sz;
692682
}
693683

@@ -713,7 +703,7 @@ class YaleStorage {
713703
size_t ndnz = count_copy_ndnz();
714704
size_t reserve = shape(0) + ndnz + 1;
715705

716-
std::cerr << "reserve = " << reserve << std::endl;
706+
// std::cerr << "reserve = " << reserve << std::endl;
717707

718708
lhs = YaleStorage<E>::create(xshape, reserve);
719709

@@ -904,6 +894,7 @@ class YaleStorage {
904894
void move_right(row_stored_nd_iterator position, size_t n) {
905895
size_t sz = size();
906896
for (size_t m = 0; m < sz - position.p(); ++m) {
897+
//std::cerr << "moving from " << sz-1-m << " to " << sz+n-1-m << std::endl;
907898
ija(sz+n-1-m) = ija(sz-1-m);
908899
a(sz+n-1-m) = a(sz-1-m);
909900
}
@@ -1022,13 +1013,13 @@ class YaleStorage {
10221013
}
10231014

10241015
// Now update row pointers following the changed row as we copy the additional values.
1025-
for (size_t m = real_i + 1; m < real_shape(0); ++m) {
1016+
for (size_t m = real_i + 1; m <= real_shape(0); ++m) {
10261017
new_ija[m] = ija(m) + n;
10271018
new_a[m] = a(m);
10281019
}
10291020

10301021
// Copy all remaining prior to insertion/removal site
1031-
for (size_t m = real_shape(0); m < position.p(); ++m) {
1022+
for (size_t m = real_shape(0) + 1; m < position.p(); ++m) {
10321023
new_ija[m] = ija(m);
10331024
new_a[m] = a(m);
10341025
}

ext/nmatrix/storage/yale/iterators/row.h

Lines changed: 48 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -236,12 +236,6 @@ class row_iterator_T {
236236

237237
inline VALUE rb_i() const { return LONG2NUM(i()); }
238238

239-
row_stored_nd_iterator_T<D,RefType,YaleRef> ndfind(size_t j) {
240-
if (j == 0) return ndbegin();
241-
size_t p = y.real_find_left_boundary_pos(p_first, p_last, j + y.offset(1));
242-
return row_stored_nd_iterator_T<D,RefType,YaleRef>(*this, p);
243-
}
244-
245239
row_stored_iterator_T<D,RefType,YaleRef> begin() { return row_stored_iterator_T<D,RefType,YaleRef>(*this, p_first); }
246240
row_stored_nd_iterator_T<D,RefType,YaleRef> ndbegin() { return row_stored_nd_iterator_T<D,RefType,YaleRef>(*this, p_first); }
247241
row_stored_iterator_T<D,RefType,YaleRef> end() { return row_stored_iterator_T<D,RefType,YaleRef>(*this, p_last+1, true); }
@@ -257,6 +251,21 @@ class row_iterator_T {
257251
row_stored_nd_iterator_T<D,RefType,YaleRef>(*this, y.real_find_left_boundary_pos(p_first, p_last, y.offset(1)));
258252
}
259253

254+
row_stored_nd_iterator_T<D,RefType,YaleRef> ndfind(size_t j) {
255+
if (j == 0) return ndbegin();
256+
std::cerr << "ndfind: p_first = " << p_first << " " << p_last << std::endl;
257+
size_t p = p_first > p_last ? p_first : y.real_find_left_boundary_pos(p_first, p_last, j + y.offset(1));
258+
std::cerr << "ndfind(" << j << ")" << " = " << p << "\t(max_p = " << y.ija(y.real_shape(0)) << ")" << std::endl;
259+
row_stored_nd_iterator iter = row_stored_nd_iterator_T<D,RefType,YaleRef>(*this, p);
260+
std::cerr << "iter.end = " << std::boolalpha << iter.end() << ", p = " << iter.p() << std::endl;
261+
return iter;
262+
}
263+
264+
row_stored_iterator_T<D,RefType,YaleRef> find(size_t j) {
265+
if (j == 0) return begin(); // may or may not be on the diagonal
266+
else return row_stored_iterator_T<D,RefType,YaleRef>(*this, ndfind(j).p(), false); // is on the diagonal, definitely
267+
}
268+
260269
/*
261270
* Remove an entry from an already found non-diagonal position. Adjust this row appropriately so we can continue to
262271
* use it.
@@ -265,9 +274,10 @@ class row_iterator_T {
265274
row_stored_nd_iterator erase(row_stored_nd_iterator position) {
266275
size_t sz = y.size();
267276
if (y.capacity() / nm::yale_storage::GROWTH_CONSTANT <= sz - 1) {
268-
y.update_resize_move(position, i() + offset(0), -1);
277+
y.update_resize_move(position, real_i(), -1);
269278
} else {
270279
y.move_left(position, 1);
280+
y.update_real_row_sizes_from(real_i(), -1);
271281
}
272282
adjust_length(-1);
273283
return row_stored_nd_iterator(*this, position.p()-1);
@@ -287,41 +297,20 @@ class row_iterator_T {
287297
}
288298
}
289299

290-
//template <typename = typename std::enable_if<!std::is_const<RefType>::value>::type>
291-
template <typename T = typename std::conditional<std::is_const<RefType>::value,void,row_stored_nd_iterator>::type>
292-
row_stored_nd_iterator insert(row_stored_iterator position, size_t jj, const D& val) {
293-
if (position.diag()) {
294-
*position = val; // simply replace existing, regardless of whether it's 0 or not
295-
++position;
296-
return row_stored_nd_iterator(*this, position.p());
297-
} else {
298-
row_stored_nd_iterator jt(*this, position.p());
299-
return insert(jt, jj, val);
300-
}
301-
}
302300

303-
/*
304-
* Insert an element in column j, using position's p() as the location to insert the new column. i and j will be the
305-
* coordinates. This also does a replace if column j is already present.
306-
*
307-
* Returns true if a new entry was added and false if an entry was replaced.
308-
*
309-
* Pre-conditions:
310-
* - position.p() must be between ija(real_i) and ija(real_i+1), inclusive, where real_i = i + offset(0)
311-
* - real_i and real_j must not be equal
312-
*/
301+
313302
//template <typename = typename std::enable_if<!std::is_const<RefType>::value>::type>
314303
row_stored_nd_iterator insert(row_stored_nd_iterator position, size_t jj, const D& val) {
315304
size_t sz = y.size();
305+
while (!position.end() && position.j() < jj) ++position; // position is just a hint. (This loop ideally only has to happen once.)
306+
316307
if (!position.end() && position.j() == jj) {
317-
std::cerr << "insert: *position = val at " << i_ << "," << jj << "\tp=" << position.p() << std::endl;
318308
*position = val; // replace existing
319309
} else {
310+
320311
if (sz + 1 > y.capacity()) {
321-
std::cerr << "insert: update_resize_move " << i_ << "," << jj << "\tp=" << position.p() << std::endl;
322312
y.update_resize_move(position, real_i(), 1);
323313
} else {
324-
std::cerr << "insert: move_right at " << i_ << "," << jj << "\tp=" << position.p() << std::endl;
325314
y.move_right(position, 1);
326315
y.update_real_row_sizes_from(real_i(), 1);
327316
}
@@ -333,26 +322,44 @@ class row_iterator_T {
333322
return position++;
334323
}
335324

325+
326+
/*
327+
* This version of insert doesn't return anything. Why, when the others do?
328+
*
329+
* Well, mainly because j here can be a diagonal entry. Most of the inserters return the *next* element following
330+
* the insertion, but to do that, we have to create a row_stored_nd_iterator, which requires at least one binary
331+
* search for the location following the diagonal (and as of the writing of this, two binary searches). There's no
332+
* reason to do that when we never actually *use* the return value. So instead we just have void.
333+
*/
336334
//template <typename = typename std::enable_if<!std::is_const<RefType>::value>::type>
337-
row_stored_nd_iterator insert(size_t j, const D& val) {
338-
return insert(ndfind(j), j, val);
335+
void insert(size_t j, const D& val) {
336+
if (j + y.offset(1) == real_i()) a(real_i()) = val;
337+
else {
338+
row_stored_nd_iterator jt = ndfind(j);
339+
if (!jt.end() && jt.j() == j) {
340+
if (val == y.const_default_obj()) erase(jt); // erase
341+
else insert(jt, j, val); // replace
342+
} else { // only insert if it's not the default
343+
if (val != y.const_default_obj()) insert(jt, j, val);
344+
}
345+
}
339346
}
340347

341348

342349
/*
343350
* Determines a plan for inserting a single row. Returns an integer giving the amount of the row change.
344351
*/
345-
int single_row_insertion_plan(row_stored_nd_iterator position, size_t jj, size_t length, D const* v, size_t v_size, const size_t& v_offset) {
352+
int single_row_insertion_plan(row_stored_nd_iterator position, size_t jj, size_t length, D const* v, size_t v_size, size_t v_offset) {
346353
int nd_change;
347-
size_t m = v_offset;
348-
for (size_t jc = jj; jc < jj + length; ++jc, ++m) {
349-
if (m >= v_size) m %= v_size; // reset v position.
354+
355+
for (size_t jc = jj; jc < jj + length; ++jc, ++v_offset) {
356+
if (v_offset >= v_size) v_offset %= v_size; // reset v position.
350357

351358
if (jc + y.offset(1) != real_i()) { // diagonal -- no nd_change here
352359
if (position.j() != jc) { // not present -- do we need to add it?
353-
if (v[m] != y.const_default_obj()) nd_change++;
360+
if (v[v_offset] != y.const_default_obj()) nd_change++;
354361
} else { // position.j() == jc
355-
if (v[m] == y.const_default_obj()) nd_change--;
362+
if (v[v_offset] == y.const_default_obj()) nd_change--;
356363
++position; // move iterator forward.
357364
}
358365
}
@@ -378,7 +385,7 @@ class row_iterator_T {
378385
* Insert elements into a single row. Returns an iterator to the end of the insertion range.
379386
*/
380387
row_stored_nd_iterator insert(row_stored_nd_iterator position, size_t jj, size_t length, D const* v, size_t v_size, size_t& v_offset) {
381-
int nd_change = single_row_insertion_plan(position, jj, length, v, v_size);
388+
int nd_change = single_row_insertion_plan(position, jj, length, v, v_size, v_offset);
382389

383390
// First record the position, just in case our iterator becomes invalid.
384391
size_t pp = position.p();

ext/nmatrix/storage/yale/iterators/row_stored.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,13 @@ class row_stored_iterator_T : public row_stored_nd_iterator_T<D,RefType,YaleRef,
7272
{
7373
}
7474

75+
/* Diagonal constructor. Puts us on the diagonal (unless end is true) */
76+
/*row_stored_iterator_T(RowRef& row, bool end_, size_t j)
77+
: row_stored_nd_iterator_T<D,RefType,YaleRef,RowRef>(row.ndfind(j)),
78+
d_visited(false),
79+
d(!end_ && j + row.offset(1) == row.real_i())
80+
{ }*/
81+
7582
virtual bool diag() const {
7683
return d;
7784
}

ext/nmatrix/storage/yale/iterators/row_stored_nd.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ class row_stored_nd_iterator_T {
7575
}
7676

7777
// DO NOT IMPLEMENT THESE FUNCTIONS. They prevent C++ virtual slicing
78-
template <typename T> row_stored_nd_iterator_T(T const& rhs);
79-
template <typename T> row_stored_nd_iterator_T<D,RefType,YaleRef,RowRef> const& operator=(T const& rhs);
78+
//template <typename T> row_stored_nd_iterator_T(T const& rhs);
79+
//template <typename T> row_stored_nd_iterator_T<D,RefType,YaleRef,RowRef> const& operator=(T const& rhs);
8080

8181
// Next two functions are to ensure we can still cast between nd iterators.
8282
row_stored_nd_iterator_T(row_stored_nd_iterator_T<D,RefType,YaleRef,RowRef> const& rhs)
@@ -108,7 +108,7 @@ class row_stored_nd_iterator_T {
108108
}
109109

110110
virtual size_t j() const {
111-
if (end()) throw std::out_of_range("cannot dereference an end pointer");
111+
if (end()) throw std::out_of_range("cannot dereference (get j()) for an end pointer");
112112
return r.ija(p_) - r.offset(1);
113113
}
114114

ext/nmatrix/storage/yale/yale.cpp

Lines changed: 2 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -704,38 +704,9 @@ static void set_single_cell(YALE_STORAGE* storage, size_t* coords, DType& v) {
704704
template <typename DType>
705705
void set(VALUE left, SLICE* slice, VALUE right) {
706706
YALE_STORAGE* storage = NM_STORAGE_YALE(left);
707+
YaleStorage<DType> y(storage);
707708

708-
// TODO: Easily modified to accept pass dense storage elements in instead of v (below). Won't work with slices.
709-
if (TYPE(right) == T_DATA) {
710-
if (RDATA(right)->dfree == (RUBY_DATA_FUNC)nm_delete || RDATA(right)->dfree == (RUBY_DATA_FUNC)nm_delete_ref) {
711-
rb_raise(rb_eNotImpError, "this type of slicing not yet supported");
712-
} else {
713-
rb_raise(rb_eTypeError, "unrecognized type for slice assignment");
714-
}
715-
716-
} else {
717-
YaleStorage<DType> s(storage);
718-
719-
DType* v;
720-
size_t v_size = 1;
721-
if (TYPE(right) == T_ARRAY) { // Allow the user to pass in an array
722-
v_size = RARRAY_LEN(right);
723-
v = ALLOC_N(DType, v_size);
724-
for (size_t m = 0; m < RARRAY_LEN(right); ++m) {
725-
rubyval_to_cval(rb_ary_entry(right, m), storage->dtype, &(v[m]));
726-
}
727-
} else {
728-
v = reinterpret_cast<DType*>(rubyobj_to_cval(right, storage->dtype));
729-
}
730-
731-
if (slice->single || (slice->lengths[0] == 1 && slice->lengths[1] == 1)) { // set a single cell
732-
set_single_cell<DType>(storage, slice->coords, *v);
733-
} else {
734-
set_multiple_cells<DType>(storage, slice->coords, slice->lengths, v, v_size);
735-
}
736-
737-
xfree(v);
738-
}
709+
y.insert(slice, right);
739710
}
740711

741712
///////////

nmatrix.gemspec

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,11 @@ EOF
5050

5151
gem.add_dependency 'rdoc', '>=4.0.1'
5252

53-
gem.add_development_dependency 'rake' #, '~>0.9'
53+
gem.add_development_dependency 'rake'
5454
gem.add_development_dependency 'bundler'
55-
gem.add_development_dependency 'rspec', '~>2.9.0'
55+
gem.add_development_dependency 'rspec'
56+
gem.add_development_dependency 'rspec-longrun'
5657
gem.add_development_dependency 'pry'
57-
gem.add_development_dependency 'guard-rspec', '~>0.7.0'
5858
gem.add_development_dependency 'rake-compiler', '~>0.8.1'
5959
end
6060

spec/elementwise_spec.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
context "yale" do
3434
before :each do
3535
@n = NMatrix.new(:yale, 3, :int64)
36+
@n.extend NMatrix::YaleFunctions
3637
@m = NMatrix.new(:yale, 3, :int64)
3738
@n[0,0] = 52
3839
@n[0,2] = 5

spec/enum_spec.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,14 @@
7373
vv = []
7474
ii = []
7575
jj = []
76+
require 'pry'
77+
binding.pry
7678
@n.send :__yale_stored_nondiagonal_each_with_indices__ do |v,i,j|
7779
vv << v
7880
ii << i
7981
jj << j
8082
end
83+
8184
vv.should == [2,3,4,5, 6,8,9,10, 11,12,14,15, 16,17,18,20]
8285
ii.should == [[0]*4, [1]*4, [2]*4, [4]*4].flatten
8386
jj.should == [1,2,3,4, 0,2,3,5, 0,1,4,5, 0,2,3,5]

0 commit comments

Comments
 (0)