tatami
C++ API for different matrix representations
Loading...
Searching...
No Matches
DelayedBind.hpp
Go to the documentation of this file.
1#ifndef TATAMI_DELAYED_BIND_HPP
2#define TATAMI_DELAYED_BIND_HPP
3
4#include "../base/Matrix.hpp"
9#include "../utils/copy.hpp"
11
12#include <numeric>
13#include <algorithm>
14#include <memory>
15#include <array>
16#include <type_traits>
17#include <cstddef>
18#include <limits>
19#include <cassert>
20
29namespace tatami {
30
34namespace DelayedBind_internal {
35
36/*
37 * Consider the problem of mapping an index 'i' of the combined dimension of the DelayedBind object to the component matrix.
38 * The simplest approach is to do a binary search on 'cumulative' but that has O(log(n)) scaling for 'n' component matrices.
39 * Another approach is to create a look-up table that can be indexed directly by 'i', which has O(1) runtime but consumes a lot of memory.
40 *
41 * The CompressedMapping class is a compromise that breaks up the combined dimensions into bins of regular size no greater than the extent of the smallest component matrix.
42 * Each bin stores the identity the component matrix in which it starts, plus the length of that component matrix covered by that bin.
43 * Given 'i', we can divide by the bin size to find its matching bin; we then compare the remainder to the covered length to assign it to the starting or next matrix.
44 * This has O(1) runtime while being much more memory efficient for a minimum bin size >> 1.
45 *
46 * In practice, the bin size is set to the largest power of 2 that is no greater than the small matrix extent.
47 * This allows for (i) some very fast division via bit shifting, and (ii) bitpacking to store both the matrix identity and the covered length in a single integer.
48 */
49template<typename Index_>
50class CompressedMapping {
51public:
52 CompressedMapping() = default;
53
54 CompressedMapping(Index_ min_size, const std::vector<Index_>& cumulative) {
55 // Define bin size as the smallest power of 2 that is no greater than 'min_size'.
56 // This means that 'my_shift' must be less than the number of binary digits of 'Index_'.
57 while (min_size > 1) {
58 ++my_shift;
59 min_size /= 2;
60 }
61 assert(sanisizer::is_less_than(min_size, std::numeric_limits<Index_>::digits));
62 const auto bin_size = static_cast<Index_>(1) << my_shift;
63 my_mask = (bin_size - 1);
64
65 const auto total_dim = cumulative.back();
66 assert(min_size <= total_dim);
67 const auto total_bins = (total_dim >> my_shift) + ((total_dim & my_mask) > 0);
68 my_bins.reserve(total_bins);
69
70 Index_ pos = 1;
71 for (I<decltype(total_bins)> b = 0; b < total_bins; ++b) {
72 // Find the first cumulative boundary that exceeds the start of the bin.
73 // 'pos - 1' is the index of the matrix covering the start of the bin.
74 Index_ lower = b << my_shift;
75 while (cumulative[pos] <= lower) {
76 ++pos;
77 }
78
79 // We compute the gap between the start of the bin and the cumulative boundary immediately to its right.
80 // This is the number of elements in the bin that are assigned to the 'pos - 1'-th matrix.
81 const auto until_next = sanisizer::min(cumulative[pos] - lower, bin_size);
82
83 // Now we pack both of these values into a single integer to save some space.
84 // This is possible based on the following logic:
85 //
86 // - The total cumulative dimension is less than '2^X' where 'X' is the number of digits of 'Index_'.
87 // - The minimum size of each matrix is greater than or equal to '2^Y', where 'Y' is 'my_shift'.
88 // We note that 'X > Y' as the minimum size cannot be greater than the total cumulative dimension.
89 // - Thus, the maximum possible value of the number of matrices must be less than '2^(X - Y)'.
90 // This implies that the matrix index must fit into a '(X - Y)'-bit unsigned integer.
91 // - We know that 'until_next' must be less than or equal to '2^Y'.
92 // We also know it must be positive as the cumulative boundary exceeds the bin start.
93 // This implies that 'until_next - 1' must fit into a Y-bit unsigned integer.
94 //
95 // Thus, we can shift the matrix index by 'Y' and use the cleared bits to store 'until_next - 1'.
96 const Index_ encoding = (static_cast<Index_>(pos - 1) << my_shift) | static_cast<Index_>(until_next - 1);
97 my_bins.push_back(encoding);
98 }
99 }
100
101 Index_ operator[](const Index_ i) const {
102 const auto encoded = my_bins[i >> my_shift];
103 // 'encoded >> my_shift' recovers the matrix index.
104 // 'encoded & my_mask' recovers 'until_next - 1'.
105 // We want to know if 'i & my_mask >= until_next', which is the same as 'i & my_mask > until_next - 1', saving us an addition.
106 return sanisizer::sum_unsafe<Index_>(encoded >> my_shift, sanisizer::is_greater_than(i & my_mask, encoded & my_mask));
107 }
108
109public:
110 std::vector<Index_> my_bins;
111 Index_ my_shift = 0;
112 Index_ my_mask;
113};
114
115/**********************
116 *** Dense parallel ***
117 **********************/
118
119template<typename Index_, class Initialize_>
120Index_ initialize_parallel_block(
121 const std::vector<Index_>& cumulative,
122 const CompressedMapping<Index_>& mapping,
123 const Index_ block_start,
124 const Index_ block_length,
125 const Initialize_ init
126) {
127 const Index_ nmats = cumulative.size() - 1; // Number of matrices is guaranteed to fit in Index_, see reasoning in the DelayedBind constructor.
128 if (nmats == 0) {
129 return 0;
130 }
131
132 const Index_ start_index = mapping[block_start];
133 Index_ actual_start = block_start - cumulative[start_index];
134 const Index_ block_end = block_start + block_length;
135
136 for (Index_ index = start_index; index < nmats; ++index) {
137 const Index_ submat_end = cumulative[index + 1];
138 bool not_final = (block_end > submat_end);
139 const Index_ actual_end = (not_final ? submat_end : block_end) - cumulative[index];
140 init(index, actual_start, actual_end - actual_start);
141 if (!not_final) {
142 break;
143 }
144 actual_start = 0;
145 }
146
147 return start_index;
148}
149
150template<typename Index_, class Initialize_>
151void initialize_parallel_index(
152 const std::vector<Index_>& cumulative,
153 const CompressedMapping<Index_>& mapping,
154 const std::vector<Index_>& indices,
155 const Initialize_ init
156) {
157 Index_ counter = 0;
158 const Index_ il = indices.size();
159 while (counter < il) {
160 const Index_ first_index = indices[counter];
161 const Index_ bind_index = mapping[first_index];
162 const Index_ lower = cumulative[bind_index];
163 const Index_ upper = cumulative[bind_index + 1];
164
165 // Creating the slice with one element already.
166 auto slice_ptr = std::make_shared<std::vector<Index_> >(1, first_index - lower);
167 ++counter;
168
169 while (counter < il && indices[counter] < upper) {
170 slice_ptr->push_back(indices[counter] - lower);
171 ++counter;
172 }
173
174 init(bind_index, std::move(slice_ptr));
175 }
176}
177
178template<bool oracle_, typename Value_, typename Index_>
179class ParallelDense final : public DenseExtractor<oracle_, Value_, Index_> {
180public:
181 ParallelDense(
182 const std::vector<Index_>&, // Not used, just provided for consistency with other constructors.
183 const CompressedMapping<Index_>&, // Ditto.
184 const std::vector<std::shared_ptr<const Matrix<Value_, Index_> > >& matrices,
185 const bool row,
186 MaybeOracle<oracle_, Index_> oracle,
187 const Options& opt
188 ) {
189 my_exts.reserve(matrices.size());
190 my_count.reserve(matrices.size());
191 for (const auto& m : matrices) {
192 my_count.emplace_back(row ? m->ncol() : m->nrow());
193 my_exts.emplace_back(new_extractor<false, oracle_>(m.get(), row, oracle, opt));
194 }
195 }
196
197 ParallelDense(
198 const std::vector<Index_>& cumulative,
199 const CompressedMapping<Index_>& mapping,
200 const std::vector<std::shared_ptr<const Matrix<Value_, Index_> > >& matrices,
201 const bool row,
202 MaybeOracle<oracle_, Index_> oracle,
203 const Index_ block_start,
204 const Index_ block_length,
205 const Options& opt
206 ) {
207 my_exts.reserve(matrices.size());
208 my_count.reserve(matrices.size());
209 initialize_parallel_block(
210 cumulative,
211 mapping,
212 block_start,
213 block_length,
214 [&](const Index_ i, const Index_ sub_block_start, const Index_ sub_block_length) -> void {
215 my_count.emplace_back(sub_block_length);
216 my_exts.emplace_back(new_extractor<false, oracle_>(matrices[i].get(), row, oracle, sub_block_start, sub_block_length, opt));
217 }
218 );
219 }
220
221 ParallelDense(
222 const std::vector<Index_>& cumulative,
223 const CompressedMapping<Index_>& mapping,
224 const std::vector<std::shared_ptr<const Matrix<Value_, Index_> > >& matrices,
225 const bool row,
226 MaybeOracle<oracle_, Index_> oracle,
227 VectorPtr<Index_> indices_ptr,
228 const Options& opt
229 ) {
230 my_exts.reserve(matrices.size());
231 my_count.reserve(matrices.size());
232 initialize_parallel_index(
233 cumulative,
234 mapping,
235 *indices_ptr,
236 [&](const Index_ i, VectorPtr<Index_> sub_indices_ptr) -> void {
237 my_count.emplace_back(sub_indices_ptr->size());
238 my_exts.emplace_back(new_extractor<false, oracle_>(matrices[i].get(), row, oracle, std::move(sub_indices_ptr), opt));
239 }
240 );
241 }
242
243public:
244 const Value_* fetch(const Index_ i, Value_* const buffer) {
245 auto copy = buffer;
246 const Index_ nmats = my_count.size();
247 for (Index_ x = 0; x < nmats; ++x) {
248 const auto ptr = my_exts[x]->fetch(i, copy);
249 const auto num = my_count[x];
250 copy_n(ptr, num, copy);
251 copy += num;
252 }
253 return buffer;
254 }
255
256private:
257 std::vector<std::unique_ptr<DenseExtractor<oracle_, Value_, Index_> > > my_exts;
258 std::vector<Index_> my_count;
259};
260
261/***********************
262 *** Sparse parallel ***
263 ***********************/
264
265template<bool oracle_, typename Value_, typename Index_>
266class ParallelFullSparse final : public SparseExtractor<oracle_, Value_, Index_> {
267public:
268 ParallelFullSparse(
269 const std::vector<Index_>& cumulative,
270 const CompressedMapping<Index_>&, // not actually used, just provided for consistency with the other constructors.
271 const std::vector<std::shared_ptr<const Matrix<Value_, Index_> > >& matrices,
272 const bool row,
273 MaybeOracle<oracle_, Index_> oracle,
274 const Options& opt
275 ) :
276 my_cumulative(cumulative),
277 my_needs_value(opt.sparse_extract_value),
278 my_needs_index(opt.sparse_extract_index)
279 {
280 my_exts.reserve(matrices.size());
281 for (const auto& m : matrices) {
282 my_exts.emplace_back(new_extractor<true, oracle_>(m.get(), row, oracle, opt));
283 }
284 }
285
286 SparseRange<Value_, Index_> fetch(const Index_ i, Value_* const value_buffer, Index_* const index_buffer) {
287 auto vcopy = value_buffer;
288 auto icopy = index_buffer;
289 Index_ accumulated = 0;
290
291 const Index_ nmats = my_exts.size();
292 for (Index_ x = 0; x < nmats; ++x) {
293 const auto range = my_exts[x]->fetch(i, vcopy, icopy);
294 accumulated += range.number;
295 if (my_needs_value) {
296 copy_n(range.value, range.number, vcopy);
297 vcopy += range.number;
298 }
299 if (my_needs_index) {
300 const auto offset = my_cumulative[x];
301 for (Index_ y = 0; y < range.number; ++y) {
302 icopy[y] = range.index[y] + offset;
303 }
304 icopy += range.number;
305 }
306 }
307
308 return SparseRange<Value_, Index_>(accumulated, (my_needs_value ? value_buffer : NULL), (my_needs_index ? index_buffer : NULL));
309 }
310
311private:
312 const std::vector<Index_>& my_cumulative;
313 bool my_needs_value, my_needs_index;
314 std::vector<std::unique_ptr<SparseExtractor<oracle_, Value_, Index_> > > my_exts;
315};
316
317template<bool oracle_, typename Value_, typename Index_>
318class ParallelBlockSparse final : public SparseExtractor<oracle_, Value_, Index_> {
319public:
320 ParallelBlockSparse(
321 const std::vector<Index_>& cumulative,
322 const CompressedMapping<Index_>& mapping,
323 const std::vector<std::shared_ptr<const Matrix<Value_, Index_> > >& matrices,
324 const bool row,
325 MaybeOracle<oracle_, Index_> oracle,
326 const Index_ block_start,
327 const Index_ block_length,
328 const Options& opt
329 ) :
330 my_cumulative(cumulative),
331 my_needs_value(opt.sparse_extract_value),
332 my_needs_index(opt.sparse_extract_index)
333 {
334 my_exts.reserve(matrices.size());
335 my_start_matrix = initialize_parallel_block(
336 my_cumulative,
337 mapping,
338 block_start,
339 block_length,
340 [&](const Index_ i, const Index_ sub_block_start, const Index_ sub_block_length) -> void {
341 my_exts.emplace_back(new_extractor<true, oracle_>(matrices[i].get(), row, oracle, sub_block_start, sub_block_length, opt));
342 }
343 );
344 }
345
346 SparseRange<Value_, Index_> fetch(const Index_ i, Value_* const value_buffer, Index_* const index_buffer) {
347 auto vcopy = value_buffer;
348 auto icopy = index_buffer;
349 Index_ count = 0;
350
351 const Index_ nmats = my_exts.size();
352 for (Index_ x = 0; x < nmats; ++x) {
353 const auto range = my_exts[x]->fetch(i, vcopy, icopy);
354 count += range.number;
355 if (my_needs_value) {
356 copy_n(range.value, range.number, vcopy);
357 vcopy += range.number;
358 }
359 if (my_needs_index) {
360 const Index_ offset = my_cumulative[x + my_start_matrix];
361 for (Index_ y = 0; y < range.number; ++y) {
362 icopy[y] = range.index[y] + offset;
363 }
364 icopy += range.number;
365 }
366 }
367
368 return SparseRange<Value_, Index_>(count, (my_needs_value ? value_buffer : NULL), (my_needs_index ? index_buffer : NULL));
369 }
370
371private:
372 const std::vector<Index_>& my_cumulative;
373 bool my_needs_value, my_needs_index;
374 std::vector<std::unique_ptr<SparseExtractor<oracle_, Value_, Index_> > > my_exts;
375 Index_ my_start_matrix;
376};
377
378template<bool oracle_, typename Value_, typename Index_>
379class ParallelIndexSparse final : public SparseExtractor<oracle_, Value_, Index_> {
380public:
381 ParallelIndexSparse(
382 const std::vector<Index_>& cumulative,
383 const CompressedMapping<Index_>& mapping,
384 const std::vector<std::shared_ptr<const Matrix<Value_, Index_> > >& matrices,
385 const bool row,
386 MaybeOracle<oracle_, Index_> oracle,
387 VectorPtr<Index_> indices_ptr,
388 const Options& opt
389 ) :
390 my_cumulative(cumulative),
391 my_needs_value(opt.sparse_extract_value),
392 my_needs_index(opt.sparse_extract_index)
393 {
394 my_exts.reserve(matrices.size());
395 my_which_matrix.reserve(matrices.size());
396 initialize_parallel_index(
397 my_cumulative,
398 mapping,
399 *indices_ptr,
400 [&](const Index_ i, VectorPtr<Index_> sub_indices_ptr) -> void {
401 my_which_matrix.emplace_back(i);
402 my_exts.emplace_back(new_extractor<true, oracle_>(matrices[i].get(), row, oracle, std::move(sub_indices_ptr), opt));
403 }
404 );
405 }
406
407 SparseRange<Value_, Index_> fetch(const Index_ i, Value_* const value_buffer, Index_* const index_buffer) {
408 auto vcopy = value_buffer;
409 auto icopy = index_buffer;
410 Index_ count = 0;
411
412 const Index_ nmats = my_which_matrix.size();
413 for (Index_ x = 0; x < nmats; ++x) {
414 const auto range = my_exts[x]->fetch(i, vcopy, icopy);
415 count += range.number;
416 if (my_needs_value) {
417 copy_n(range.value, range.number, vcopy);
418 vcopy += range.number;
419 }
420
421 if (my_needs_index) {
422 const Index_ offset = my_cumulative[my_which_matrix[x]];
423 for (Index_ y = 0; y < range.number; ++y) {
424 icopy[y] = range.index[y] + offset;
425 }
426 icopy += range.number;
427 }
428 }
429
430 return SparseRange<Value_, Index_>(count, (my_needs_value ? value_buffer : NULL), (my_needs_index ? index_buffer : NULL));
431 }
432
433private:
434 const std::vector<Index_>& my_cumulative;
435 bool my_needs_value, my_needs_index;
436 std::vector<std::unique_ptr<SparseExtractor<oracle_, Value_, Index_> > > my_exts;
437 std::vector<Index_> my_which_matrix;
438};
439
440/*********************
441 *** Perpendicular ***
442 *********************/
443
444template<typename Value_, typename Index_>
445class MyopicPerpendicularDense final : public MyopicDenseExtractor<Value_, Index_> {
446public:
447 template<typename ... Args_>
448 MyopicPerpendicularDense(
449 const std::vector<Index_>& cumulative,
450 const CompressedMapping<Index_>& mapping,
451 const std::vector<std::shared_ptr<const Matrix<Value_, Index_> > >& matrices,
452 const bool row,
453 const Args_& ... args
454 ) :
455 my_cumulative(cumulative),
456 my_mapping(mapping)
457 {
458 my_exts.reserve(matrices.size());
459 for (const auto& m : matrices) {
460 my_exts.emplace_back(m->dense(row, args...));
461 }
462 }
463
464 const Value_* fetch(const Index_ i, Value_* const buffer) {
465 const Index_ chosen = my_mapping[i];
466 return my_exts[chosen]->fetch(i - my_cumulative[chosen], buffer);
467 }
468
469private:
470 const std::vector<Index_>& my_cumulative;
471 const CompressedMapping<Index_>& my_mapping;
472 std::vector<std::unique_ptr<MyopicDenseExtractor<Value_, Index_> > > my_exts;
473};
474
475template<typename Value_, typename Index_>
476class MyopicPerpendicularSparse final : public MyopicSparseExtractor<Value_, Index_> {
477public:
478 template<typename ... Args_>
479 MyopicPerpendicularSparse(
480 const std::vector<Index_>& cumulative,
481 const CompressedMapping<Index_>& mapping,
482 const std::vector<std::shared_ptr<const Matrix<Value_, Index_> > >& matrices,
483 const bool row,
484 const Args_& ... args
485 ) :
486 my_cumulative(cumulative),
487 my_mapping(mapping)
488 {
489 my_exts.reserve(matrices.size());
490 for (const auto& m : matrices) {
491 my_exts.emplace_back(m->sparse(row, args...));
492 }
493 }
494
495 SparseRange<Value_, Index_> fetch(const Index_ i, Value_* const vbuffer, Index_* const ibuffer) {
496 const Index_ chosen = my_mapping[i];
497 return my_exts[chosen]->fetch(i - my_cumulative[chosen], vbuffer, ibuffer);
498 }
499
500private:
501 const std::vector<Index_>& my_cumulative;
502 const CompressedMapping<Index_>& my_mapping;
503 std::vector<std::unique_ptr<MyopicSparseExtractor<Value_, Index_> > > my_exts;
504};
505
506template<typename Index_, class Initialize_>
507void initialize_perp_oracular(
508 const std::vector<Index_>& cumulative,
509 const CompressedMapping<Index_>& mapping,
510 const Oracle<Index_>& oracle,
511 std::vector<Index_>& chosen,
512 Initialize_ init
513) {
514 const auto ntotal = oracle.total();
515 chosen.reserve(ntotal);
516
517 struct Predictions {
518 bool consecutive = true;
519 Index_ start = 0;
520 Index_ number = 0;
521 std::vector<Index_> predictions;
522
523 void add(const Index_ p) {
524 if (consecutive) {
525 if (number == 0) {
526 start = p;
527 number = 1;
528 return;
529 }
530 if (number + start == p) {
531 ++number;
532 return;
533 }
534 consecutive = false;
535 resize_container_to_Index_size(predictions, number);
536 std::iota(predictions.begin(), predictions.end(), start);
537 }
538
539 predictions.push_back(p);
540 }
541 };
542
543 const auto nmats = cumulative.size() - 1;
544 auto predictions = create_container_of_Index_size<std::vector<Predictions> >(nmats); // nmats should fit in an Index_, so this call is legal.
545 for (I<decltype(ntotal)> i = 0; i < ntotal; ++i) {
546 const auto prediction = oracle.get(i);
547 const Index_ choice = mapping[prediction];
548 chosen.push_back(choice);
549 predictions[choice].add(prediction - cumulative[choice]);
550 }
551
552 for (I<decltype(nmats)> x = 0; x < nmats; ++x) {
553 auto& current = predictions[x];
554 if (current.consecutive) {
555 if (current.number) {
556 init(x, std::make_shared<ConsecutiveOracle<Index_> >(current.start, current.number));
557 }
558 } else {
559 if (!current.predictions.empty()) {
560 init(x, std::make_shared<FixedVectorOracle<Index_> >(std::move(current.predictions)));
561 }
562 }
563 }
564}
565
566template<typename Value_, typename Index_>
567class OracularPerpendicularDense final : public OracularDenseExtractor<Value_, Index_> {
568public:
569 template<typename ... Args_>
570 OracularPerpendicularDense(
571 const std::vector<Index_>& cumulative,
572 const CompressedMapping<Index_>& mapping,
573 const std::vector<std::shared_ptr<const Matrix<Value_, Index_> > >& matrices,
574 const bool row,
575 std::shared_ptr<const Oracle<Index_> > ora,
576 const Args_& ... args
577 ) {
578 resize_container_to_Index_size(my_exts, matrices.size()); // number of matrices should fit in an I ndex_, so this call is allowed.
579 initialize_perp_oracular(
580 cumulative,
581 mapping,
582 *ora,
583 my_segments,
584 [&](const Index_ x, std::shared_ptr<const Oracle<Index_> > subora) -> void {
585 my_exts[x] = matrices[x]->dense(row, std::move(subora), args...);
586 }
587 );
588 }
589
590 const Value_* fetch(const Index_ i, Value_* const buffer) {
591 const auto chosen = my_segments[my_used];
592 const auto output = my_exts[chosen]->fetch(i, buffer);
593 ++my_used;
594 return output;
595 }
596
597private:
598 std::vector<Index_> my_segments;
599 std::vector<std::unique_ptr<OracularDenseExtractor<Value_, Index_> > > my_exts;
600 PredictionIndex my_used = 0;
601};
602
603template<typename Value_, typename Index_>
604class OracularPerpendicularSparse final : public OracularSparseExtractor<Value_, Index_> {
605public:
606 template<typename ... Args_>
607 OracularPerpendicularSparse(
608 const std::vector<Index_>& cumulative,
609 const CompressedMapping<Index_>& mapping,
610 const std::vector<std::shared_ptr<const Matrix<Value_, Index_> > >& matrices,
611 const bool row,
612 std::shared_ptr<const Oracle<Index_> > ora,
613 const Args_& ... args
614 ) {
615 resize_container_to_Index_size(my_exts, matrices.size()); // number of matrices should fit in an Index_, so this call is legal.
616 initialize_perp_oracular(
617 cumulative,
618 mapping,
619 *ora,
620 my_segments,
621 [&](const Index_ x, std::shared_ptr<const Oracle<Index_> > subora) -> void {
622 my_exts[x] = matrices[x]->sparse(row, std::move(subora), args...);
623 }
624 );
625 }
626
627 SparseRange<Value_, Index_> fetch(Index_ i, Value_* vbuffer, Index_* ibuffer) {
628 const auto chosen = my_segments[my_used];
629 const auto output = my_exts[chosen]->fetch(i, vbuffer, ibuffer);
630 ++my_used;
631 return output;
632 }
633
634private:
635 std::vector<Index_> my_segments;
636 std::vector<std::unique_ptr<OracularSparseExtractor<Value_, Index_> > > my_exts;
637 PredictionIndex my_used = 0;
638};
639
640}
654template<typename Value_, typename Index_>
655class DelayedBind final : public Matrix<Value_, Index_> {
656public:
663 DelayedBind(std::vector<std::shared_ptr<const Matrix<Value_, Index_> > > matrices, const bool by_row) :
664 my_matrices(std::move(matrices)), my_by_row(by_row)
665 {
666 auto nmats = my_matrices.size();
667 my_cumulative.reserve(sanisizer::sum<I<decltype(my_cumulative.size())> >(nmats, 1));
668 I<decltype(nmats)> sofar = 0;
669 my_cumulative.push_back(0);
670 Index_ min_extent = std::numeric_limits<Index_>::max();
671
672 for (I<decltype(nmats)> i = 0; i < nmats; ++i) {
673 auto& current = my_matrices[i];
674 Index_ primary, secondary;
675 if (my_by_row) {
676 primary = current->nrow();
677 secondary = current->ncol();
678 } else {
679 primary = current->ncol();
680 secondary = current->nrow();
681 }
682
683 if (i == 0) {
684 my_otherdim = secondary;
685 } else if (my_otherdim != secondary) {
686 throw std::runtime_error("all 'matrices' should have the same number of " + (my_by_row ? std::string("columns") : std::string("rows")));
687 }
688
689 // Removing the matrices that don't contribute anything,
690 // so we don't have to deal with their overhead.
691 if (primary > 0) {
692 if (sofar != i) {
693 my_matrices[sofar] = std::move(current);
694 }
695 my_cumulative.push_back(sanisizer::sum<Index_>(attest_for_Index(my_cumulative.back()), attest_for_Index(primary)));
696 min_extent = std::min(min_extent, primary);
697 ++sofar;
698 }
699 }
700
701 my_matrices.resize(sofar);
702 nmats = sofar;
703
704 if (nmats == 0) {
705 min_extent = 0;
706 }
707 my_mapping = DelayedBind_internal::CompressedMapping(min_extent, my_cumulative);
708
709 double denom = 0;
710 for (const auto& x : my_matrices) {
711 const double total = static_cast<double>(x->nrow()) * static_cast<double>(x->ncol());
712 denom += total;
713 my_sparse_prop += total * x->is_sparse_proportion();
714 my_by_row_prop += total * x->prefer_rows_proportion();
715 }
716 if (denom) {
717 my_sparse_prop /= denom;
718 my_by_row_prop /= denom;
719 }
720
721 for (int d = 0; d < 2; ++d) {
722 my_uses_oracle[d] = false;
723 for (const auto& x : my_matrices) {
724 if (x->uses_oracle(d)) {
725 my_uses_oracle[d] = true;
726 break;
727 }
728 }
729 }
730 }
731
735 // Soft-deprecated.
736 DelayedBind(const std::vector<std::shared_ptr<Matrix<Value_, Index_> > >& matrices, const bool by_row) :
737 DelayedBind(std::vector<std::shared_ptr<const Matrix<Value_, Index_> > >(matrices.begin(), matrices.end()), by_row) {}
742private:
743 std::vector<std::shared_ptr<const Matrix<Value_, Index_> > > my_matrices;
744 bool my_by_row;
745
746 Index_ my_otherdim = 0;
747 std::vector<Index_> my_cumulative;
748 DelayedBind_internal::CompressedMapping<Index_> my_mapping;
749
750 double my_sparse_prop = 0, my_by_row_prop = 0;
751 std::array<bool, 2> my_uses_oracle;
752
753public:
754 Index_ nrow() const {
755 if (my_by_row) {
756 return my_cumulative.back();
757 } else {
758 return my_otherdim;
759 }
760 }
761
762 Index_ ncol() const {
763 if (my_by_row) {
764 return my_otherdim;
765 } else {
766 return my_cumulative.back();
767 }
768 }
769
770 bool is_sparse() const {
771 return my_sparse_prop > 0.5;
772 }
773
774 double is_sparse_proportion() const {
775 return my_sparse_prop;
776 }
777
778 bool prefer_rows() const {
779 return my_by_row_prop > 0.5;
780 }
781
782 double prefer_rows_proportion() const {
783 return my_by_row_prop;
784 }
785
786 bool uses_oracle(const bool row) const {
787 return my_uses_oracle[row];
788 }
789
790 using Matrix<Value_, Index_>::dense;
791
792 using Matrix<Value_, Index_>::sparse;
793
794 /**********************************
795 ********** Myopic dense **********
796 **********************************/
797public:
798 std::unique_ptr<MyopicDenseExtractor<Value_, Index_> > dense(
799 const bool row,
800 const Options& opt
801 ) const {
802 if (my_matrices.size() == 1) {
803 return my_matrices[0]->dense(row, opt);
804 } else if (row == my_by_row) {
805 return std::make_unique<DelayedBind_internal::MyopicPerpendicularDense<Value_, Index_> >(
806 my_cumulative,
807 my_mapping,
808 my_matrices,
809 row,
810 opt
811 );
812 } else {
813 return std::make_unique<DelayedBind_internal::ParallelDense<false, Value_, Index_> >(
814 my_cumulative,
815 my_mapping,
816 my_matrices,
817 row,
818 false,
819 opt
820 );
821 }
822 }
823
824 std::unique_ptr<MyopicDenseExtractor<Value_, Index_> > dense(
825 const bool row,
826 const Index_ block_start,
827 const Index_ block_length,
828 const Options& opt
829 ) const {
830 if (my_matrices.size() == 1) {
831 return my_matrices[0]->dense(row, block_start, block_length, opt);
832 } else if (row == my_by_row) {
833 return std::make_unique<DelayedBind_internal::MyopicPerpendicularDense<Value_, Index_> >(
834 my_cumulative,
835 my_mapping,
836 my_matrices,
837 row,
838 block_start,
839 block_length,
840 opt
841 );
842 } else {
843 return std::make_unique<DelayedBind_internal::ParallelDense<false, Value_, Index_> >(
844 my_cumulative,
845 my_mapping,
846 my_matrices,
847 row,
848 false,
849 block_start,
850 block_length,
851 opt
852 );
853 }
854 }
855
856 std::unique_ptr<MyopicDenseExtractor<Value_, Index_> > dense(
857 const bool row,
858 VectorPtr<Index_> indices_ptr,
859 const Options& opt
860 ) const {
861 if (my_matrices.size() == 1) {
862 return my_matrices[0]->dense(row, std::move(indices_ptr), opt);
863 } else if (row == my_by_row) {
864 return std::make_unique<DelayedBind_internal::MyopicPerpendicularDense<Value_, Index_> >(
865 my_cumulative,
866 my_mapping,
867 my_matrices,
868 row,
869 std::move(indices_ptr),
870 opt
871 );
872 } else {
873 return std::make_unique<DelayedBind_internal::ParallelDense<false, Value_, Index_> >(
874 my_cumulative,
875 my_mapping,
876 my_matrices,
877 row,
878 false,
879 std::move(indices_ptr),
880 opt
881 );
882 }
883 }
884
885 /***********************************
886 ********** Myopic sparse **********
887 ***********************************/
888private:
889 std::unique_ptr<MyopicSparseExtractor<Value_, Index_> > sparse(
890 const bool row,
891 const Options& opt
892 ) const {
893 if (my_matrices.size() == 1) {
894 return my_matrices[0]->sparse(row, opt);
895 } else if (row == my_by_row) {
896 return std::make_unique<DelayedBind_internal::MyopicPerpendicularSparse<Value_, Index_> >(
897 my_cumulative,
898 my_mapping,
899 my_matrices,
900 row,
901 opt
902 );
903 } else {
904 return std::make_unique<DelayedBind_internal::ParallelFullSparse<false, Value_, Index_> >(
905 my_cumulative,
906 my_mapping,
907 my_matrices,
908 row,
909 false,
910 opt
911 );
912 }
913 }
914
915 std::unique_ptr<MyopicSparseExtractor<Value_, Index_> > sparse(
916 const bool row,
917 const Index_ block_start,
918 const Index_ block_length,
919 const Options& opt
920 ) const {
921 if (my_matrices.size() == 1) {
922 return my_matrices[0]->sparse(row, block_start, block_length, opt);
923 } else if (row == my_by_row) {
924 return std::make_unique<DelayedBind_internal::MyopicPerpendicularSparse<Value_, Index_> >(
925 my_cumulative,
926 my_mapping,
927 my_matrices,
928 row,
929 block_start,
930 block_length,
931 opt
932 );
933 } else {
934 return std::make_unique<DelayedBind_internal::ParallelBlockSparse<false, Value_, Index_> >(
935 my_cumulative,
936 my_mapping,
937 my_matrices,
938 row,
939 false,
940 block_start,
941 block_length,
942 opt
943 );
944 }
945 }
946
947 std::unique_ptr<MyopicSparseExtractor<Value_, Index_> > sparse(
948 const bool row,
949 VectorPtr<Index_> indices_ptr,
950 const Options& opt
951 ) const {
952 if (my_matrices.size() == 1) {
953 return my_matrices[0]->sparse(row, std::move(indices_ptr), opt);
954 } else if (row == my_by_row) {
955 return std::make_unique<DelayedBind_internal::MyopicPerpendicularSparse<Value_, Index_> >(
956 my_cumulative,
957 my_mapping,
958 my_matrices,
959 row,
960 std::move(indices_ptr),
961 opt
962 );
963 } else {
964 return std::make_unique<DelayedBind_internal::ParallelIndexSparse<false, Value_, Index_> >(
965 my_cumulative,
966 my_mapping,
967 my_matrices,
968 row,
969 false,
970 std::move(indices_ptr),
971 opt
972 );
973 }
974 }
975
976 /************************************
977 ********** Oracular dense **********
978 ************************************/
979public:
980 std::unique_ptr<OracularDenseExtractor<Value_, Index_> > dense(
981 const bool row,
982 std::shared_ptr<const Oracle<Index_> > oracle,
983 const Options& opt
984 ) const {
985 if (my_matrices.size() == 1) {
986 return my_matrices[0]->dense(row, std::move(oracle), opt);
987 } else if (!my_uses_oracle[row]) {
988 return std::make_unique<PseudoOracularDenseExtractor<Value_, Index_> >(
989 std::move(oracle),
990 dense(row, opt)
991 );
992 } else if (row == my_by_row) {
993 return std::make_unique<DelayedBind_internal::OracularPerpendicularDense<Value_, Index_> >(
994 my_cumulative,
995 my_mapping,
996 my_matrices,
997 row,
998 std::move(oracle),
999 opt
1000 );
1001 } else {
1002 return std::make_unique<DelayedBind_internal::ParallelDense<true, Value_, Index_> >(
1003 my_cumulative,
1004 my_mapping,
1005 my_matrices,
1006 row,
1007 std::move(oracle),
1008 opt
1009 );
1010 }
1011 }
1012
1013 std::unique_ptr<OracularDenseExtractor<Value_, Index_> > dense(
1014 const bool row,
1015 std::shared_ptr<const Oracle<Index_> > oracle,
1016 const Index_ block_start,
1017 const Index_ block_length,
1018 const Options& opt
1019 ) const {
1020 if (my_matrices.size() == 1) {
1021 return my_matrices[0]->dense(row, std::move(oracle), block_start, block_length, opt);
1022 } else if (!my_uses_oracle[row]) {
1023 return std::make_unique<PseudoOracularDenseExtractor<Value_, Index_> >(
1024 std::move(oracle),
1025 dense(row, block_start, block_length, opt)
1026 );
1027 } else if (row == my_by_row) {
1028 return std::make_unique<DelayedBind_internal::OracularPerpendicularDense<Value_, Index_> >(
1029 my_cumulative,
1030 my_mapping,
1031 my_matrices,
1032 row,
1033 std::move(oracle),
1034 block_start,
1035 block_length,
1036 opt
1037 );
1038 } else {
1039 return std::make_unique<DelayedBind_internal::ParallelDense<true, Value_, Index_> >(
1040 my_cumulative,
1041 my_mapping,
1042 my_matrices,
1043 row,
1044 std::move(oracle),
1045 block_start,
1046 block_length,
1047 opt
1048 );
1049 }
1050 }
1051
1052 std::unique_ptr<OracularDenseExtractor<Value_, Index_> > dense(
1053 const bool row,
1054 std::shared_ptr<const Oracle<Index_> > oracle,
1055 VectorPtr<Index_> indices_ptr,
1056 const Options& opt
1057 ) const {
1058 if (my_matrices.size() == 1) {
1059 return my_matrices[0]->dense(row, std::move(oracle), std::move(indices_ptr), opt);
1060 } else if (!my_uses_oracle[row]) {
1061 return std::make_unique<PseudoOracularDenseExtractor<Value_, Index_> >(
1062 std::move(oracle),
1063 dense(row, std::move(indices_ptr), opt)
1064 );
1065 } else if (row == my_by_row) {
1066 return std::make_unique<DelayedBind_internal::OracularPerpendicularDense<Value_, Index_> >(
1067 my_cumulative,
1068 my_mapping,
1069 my_matrices,
1070 row,
1071 std::move(oracle),
1072 std::move(indices_ptr),
1073 opt
1074 );
1075 } else {
1076 return std::make_unique<DelayedBind_internal::ParallelDense<true, Value_, Index_> >(
1077 my_cumulative,
1078 my_mapping,
1079 my_matrices,
1080 row,
1081 std::move(oracle),
1082 std::move(indices_ptr),
1083 opt
1084 );
1085 }
1086 }
1087
1088 /*************************************
1089 ********** Oracular sparse **********
1090 *************************************/
1091private:
1092 std::unique_ptr<OracularSparseExtractor<Value_, Index_> > sparse(
1093 const bool row,
1094 std::shared_ptr<const Oracle<Index_> > oracle,
1095 const Options& opt
1096 ) const {
1097 if (my_matrices.size() == 1) {
1098 return my_matrices[0]->sparse(row, std::move(oracle), opt);
1099 } else if (!my_uses_oracle[row]) {
1100 return std::make_unique<PseudoOracularSparseExtractor<Value_, Index_> >(
1101 std::move(oracle),
1102 sparse(row, opt)
1103 );
1104 } else if (row == my_by_row) {
1105 return std::make_unique<DelayedBind_internal::OracularPerpendicularSparse<Value_, Index_> >(
1106 my_cumulative,
1107 my_mapping,
1108 my_matrices,
1109 row,
1110 std::move(oracle),
1111 opt
1112 );
1113 } else {
1114 return std::make_unique<DelayedBind_internal::ParallelFullSparse<true, Value_, Index_> >(
1115 my_cumulative,
1116 my_mapping,
1117 my_matrices,
1118 row,
1119 std::move(oracle),
1120 opt
1121 );
1122 }
1123 }
1124
1125 std::unique_ptr<OracularSparseExtractor<Value_, Index_> > sparse(
1126 const bool row,
1127 std::shared_ptr<const Oracle<Index_> > oracle,
1128 const Index_ block_start,
1129 const Index_ block_length,
1130 const Options& opt
1131 ) const {
1132 if (my_matrices.size() == 1) {
1133 return my_matrices[0]->sparse(row, std::move(oracle), block_start, block_length, opt);
1134 } else if (!my_uses_oracle[row]) {
1135 return std::make_unique<PseudoOracularSparseExtractor<Value_, Index_> >(
1136 std::move(oracle),
1137 sparse(row, block_start, block_length, opt)
1138 );
1139 } else if (row == my_by_row) {
1140 return std::make_unique<DelayedBind_internal::OracularPerpendicularSparse<Value_, Index_> >(
1141 my_cumulative,
1142 my_mapping,
1143 my_matrices,
1144 row,
1145 std::move(oracle),
1146 block_start,
1147 block_length,
1148 opt
1149 );
1150 } else {
1151 return std::make_unique<DelayedBind_internal::ParallelBlockSparse<true, Value_, Index_> >(
1152 my_cumulative,
1153 my_mapping,
1154 my_matrices,
1155 row,
1156 std::move(oracle),
1157 block_start,
1158 block_length,
1159 opt
1160 );
1161 }
1162 }
1163
1164 std::unique_ptr<OracularSparseExtractor<Value_, Index_> > sparse(
1165 const bool row,
1166 std::shared_ptr<const Oracle<Index_> > oracle,
1167 VectorPtr<Index_> indices_ptr,
1168 const Options& opt
1169 ) const {
1170 if (my_matrices.size() == 1) {
1171 return my_matrices[0]->sparse(row, std::move(oracle), std::move(indices_ptr), opt);
1172 } else if (!my_uses_oracle[row]) {
1173 return std::make_unique<PseudoOracularSparseExtractor<Value_, Index_> >(
1174 std::move(oracle),
1175 sparse(row, std::move(indices_ptr), opt)
1176 );
1177 } else if (row == my_by_row) {
1178 return std::make_unique<DelayedBind_internal::OracularPerpendicularSparse<Value_, Index_> >(
1179 my_cumulative,
1180 my_mapping,
1181 my_matrices,
1182 row,
1183 std::move(oracle),
1184 std::move(indices_ptr),
1185 opt
1186 );
1187 } else {
1188 return std::make_unique<DelayedBind_internal::ParallelIndexSparse<true, Value_, Index_> >(
1189 my_cumulative,
1190 my_mapping,
1191 my_matrices,
1192 row,
1193 std::move(oracle),
1194 std::move(indices_ptr),
1195 opt
1196 );
1197 }
1198 }
1199};
1200
1204// These methods are soft-deprecated: kept around for back-compatibility only.
1205template<typename Value_, typename Index_>
1206std::shared_ptr<Matrix<Value_, Index_> > make_DelayedBind(std::vector<std::shared_ptr<const Matrix<Value_, Index_> > > matrices, bool row) {
1207 return std::shared_ptr<Matrix<Value_, Index_> >(new DelayedBind<Value_, Index_>(std::move(matrices), row));
1208}
1209
1210template<typename Value_, typename Index_>
1211std::shared_ptr<Matrix<Value_, Index_> > make_DelayedBind(std::vector<std::shared_ptr<Matrix<Value_, Index_> > > matrices, bool row) {
1212 return std::shared_ptr<Matrix<Value_, Index_> >(new DelayedBind<Value_, Index_>(std::move(matrices), row));
1213}
1214
1215template<int margin_, typename Value_, typename Index_>
1216std::shared_ptr<Matrix<Value_, Index_> > make_DelayedBind(std::vector<std::shared_ptr<const Matrix<Value_, Index_> > > matrices) {
1217 return make_DelayedBind(std::move(matrices), margin_ == 0);
1218}
1219
1220template<int margin_, typename Value_, typename Index_>
1221std::shared_ptr<Matrix<Value_, Index_> > make_DelayedBind(std::vector<std::shared_ptr<Matrix<Value_, Index_> > > matrices) {
1222 return make_DelayedBind(std::move(matrices), margin_ == 0);
1223}
1228}
1229
1230#endif
Iterate across consecutive elements of the target dimension.
Iterate across a fixed sequence of elements on the target dimension.
Convert index type to container size.
Virtual class for a matrix of some numeric type.
Mimic the oracle-aware extractor interface.
Delayed combining of a matrix.
Definition DelayedBind.hpp:655
bool uses_oracle(const bool row) const
Definition DelayedBind.hpp:786
std::unique_ptr< OracularDenseExtractor< Value_, Index_ > > dense(const bool row, std::shared_ptr< const Oracle< Index_ > > oracle, VectorPtr< Index_ > indices_ptr, const Options &opt) const
Definition DelayedBind.hpp:1052
DelayedBind(std::vector< std::shared_ptr< const Matrix< Value_, Index_ > > > matrices, const bool by_row)
Definition DelayedBind.hpp:663
std::unique_ptr< MyopicDenseExtractor< Value_, Index_ > > dense(const bool row, const Options &opt) const
Definition DelayedBind.hpp:798
double is_sparse_proportion() const
Definition DelayedBind.hpp:774
std::unique_ptr< MyopicDenseExtractor< Value_, Index_ > > dense(const bool row, const Index_ block_start, const Index_ block_length, const Options &opt) const
Definition DelayedBind.hpp:824
Index_ nrow() const
Definition DelayedBind.hpp:754
std::unique_ptr< MyopicDenseExtractor< Value_, Index_ > > dense(const bool row, VectorPtr< Index_ > indices_ptr, const Options &opt) const
Definition DelayedBind.hpp:856
bool is_sparse() const
Definition DelayedBind.hpp:770
std::unique_ptr< OracularDenseExtractor< Value_, Index_ > > dense(const bool row, std::shared_ptr< const Oracle< Index_ > > oracle, const Options &opt) const
Definition DelayedBind.hpp:980
Index_ ncol() const
Definition DelayedBind.hpp:762
bool prefer_rows() const
Definition DelayedBind.hpp:778
double prefer_rows_proportion() const
Definition DelayedBind.hpp:782
std::unique_ptr< OracularDenseExtractor< Value_, Index_ > > dense(const bool row, std::shared_ptr< const Oracle< Index_ > > oracle, const Index_ block_start, const Index_ block_length, const Options &opt) const
Definition DelayedBind.hpp:1013
Virtual class for a matrix.
Definition Matrix.hpp:59
Predict future access requests on the target dimension.
Definition Oracle.hpp:29
Copy data from one buffer to another.
Flexible representations for matrix data.
Definition Extractor.hpp:15
std::shared_ptr< const std::vector< Index_ > > VectorPtr
Definition Matrix.hpp:26
void resize_container_to_Index_size(Container_ &container, const Index_ x, Args_ &&... args)
Definition Index_to_container.hpp:99
typename std::conditional< oracle_, OracularDenseExtractor< Value_, Index_ >, MyopicDenseExtractor< Value_, Index_ > >::type DenseExtractor
Definition Extractor.hpp:302
Value_ * copy_n(const Value_ *const input, const Size_ n, Value_ *const output)
Definition copy.hpp:37
std::size_t PredictionIndex
Definition Oracle.hpp:18
Templated construction of a new extractor.
Options for accessing data from a Matrix instance.
Definition Options.hpp:30