tatami
C++ API for different matrix representations
Loading...
Searching...
No Matches
convert_to_compressed_sparse.hpp
Go to the documentation of this file.
1#ifndef TATAMI_CONVERT_TO_COMPRESSED_SPARSE_H
2#define TATAMI_CONVERT_TO_COMPRESSED_SPARSE_H
3
8
9#include <memory>
10#include <vector>
11#include <cstddef>
12
19namespace tatami {
20
24namespace convert_to_compressed_sparse_internal {
25
26template<typename Value_, typename Index_, typename Count_>
27void count_compressed_sparse_non_zeros_consistent(const tatami::Matrix<Value_, Index_>& matrix, Index_ primary, Index_ secondary, bool row, Count_* output, int threads) {
28 if (matrix.is_sparse()) {
29 Options opt;
30 opt.sparse_extract_value = false;
31 opt.sparse_extract_index = false;
32 opt.sparse_ordered_index = false;
33
34 parallelize([&](int, Index_ start, Index_ length) -> void {
35 auto wrk = consecutive_extractor<true>(matrix, row, start, length, opt);
36 for (Index_ x = 0; x < length; ++x) {
37 auto range = wrk->fetch(NULL, NULL);
38 output[start + x] = range.number;
39 }
40 }, primary, threads);
41
42 } else {
43 parallelize([&](int, Index_ start, Index_ length) -> void {
44 std::vector<Value_> buffer_v(secondary);
45 auto wrk = consecutive_extractor<false>(matrix, row, start, length);
46 for (Index_ p = start, pe = start + length; p < pe; ++p) {
47 auto ptr = wrk->fetch(buffer_v.data());
48 Count_ count = 0;
49 for (Index_ s = 0; s < secondary; ++s) {
50 count += (ptr[s] != 0);
51 }
52 output[p] = count;
53 }
54 }, primary, threads);
55 }
56}
57
58template<typename Value_, typename Index_, typename Count_>
59void count_compressed_sparse_non_zeros_inconsistent(const tatami::Matrix<Value_, Index_>& matrix, Index_ primary, Index_ secondary, bool row, Count_* output, int threads) {
60 std::vector<std::vector<Count_> > nz_counts(threads - 1);
61 for (auto& x : nz_counts) {
62 x.resize(primary);
63 }
64
65 if (matrix.is_sparse()) {
66 Options opt;
67 opt.sparse_extract_value = false;
68 opt.sparse_ordered_index = false;
69
70 parallelize([&](int t, Index_ start, Index_ length) -> void {
71 std::vector<Index_> buffer_i(primary);
72 auto wrk = consecutive_extractor<true>(matrix, !row, start, length, opt);
73 auto my_counts = (t > 0 ? nz_counts[t - 1].data() : output);
74
75 for (Index_ x = 0; x < length; ++x) {
76 auto range = wrk->fetch(NULL, buffer_i.data());
77 for (Index_ i = 0; i < range.number; ++i) {
78 ++my_counts[range.index[i]];
79 }
80 }
81 }, secondary, threads);
82
83 } else {
84 parallelize([&](int t, Index_ start, Index_ length) -> void {
85 auto wrk = consecutive_extractor<false>(matrix, !row, start, length);
86 std::vector<Value_> buffer_v(primary);
87 auto my_counts = (t > 0 ? nz_counts[t - 1].data() : output);
88
89 for (Index_ x = 0; x < length; ++x) {
90 auto ptr = wrk->fetch(buffer_v.data());
91 for (Index_ p = 0; p < primary; ++p) {
92 my_counts[p] += (ptr[p] != 0);
93 }
94 }
95 }, secondary, threads);
96 }
97
98 for (auto& y : nz_counts) {
99 for (Index_ p = 0; p < primary; ++p) {
100 output[p] += y[p];
101 }
102 }
103}
104
105template<typename InputValue_, typename InputIndex_, typename Pointer_, typename StoredValue_, typename StoredIndex_>
106void fill_compressed_sparse_matrix_consistent(
108 InputIndex_ primary,
109 InputIndex_ secondary,
110 bool row,
111 const Pointer_* pointers,
112 StoredValue_* output_value,
113 StoredIndex_* output_index,
114 int threads)
115{
116 if (matrix.is_sparse()) {
117 Options opt;
118 opt.sparse_ordered_index = false;
119
120 parallelize([&](int, InputIndex_ start, InputIndex_ length) -> void {
121 std::vector<InputValue_> buffer_v(secondary);
122 std::vector<InputIndex_> buffer_i(secondary);
123 auto wrk = consecutive_extractor<true>(matrix, row, start, length, opt);
124
125 for (InputIndex_ p = start, pe = start + length; p < pe; ++p) {
126 // Resist the urge to `fetch()` straight into 'output_v'
127 // and 'output_i', as implementations may assume that they
128 // have the entire 'length' length to play with, and the
129 // output vectors only have whatever is allocated from the
130 // first pass (which might be nothing for an all-zero matrix).
131 auto range = wrk->fetch(buffer_v.data(), buffer_i.data());
132 auto offset = pointers[p];
133 std::copy_n(range.value, range.number, output_value + offset);
134 std::copy_n(range.index, range.number, output_index + offset);
135 }
136 }, primary, threads);
137
138 } else {
139 parallelize([&](int, InputIndex_ start, InputIndex_ length) -> void {
140 std::vector<InputValue_> buffer_v(secondary);
141 auto wrk = consecutive_extractor<false>(matrix, row, start, length);
142
143 for (InputIndex_ p = start, pe = start + length; p < pe; ++p) {
144 auto ptr = wrk->fetch(buffer_v.data());
145 auto offset = pointers[p];
146 for (InputIndex_ s = 0; s < secondary; ++s) {
147 auto val = ptr[s];
148 if (val != 0) {
149 output_value[offset] = val;
150 output_index[offset] = s;
151 ++offset;
152 }
153 }
154 }
155 }, primary, threads);
156 }
157}
158
159template<typename InputValue_, typename InputIndex_, typename Pointer_, typename StoredValue_, typename StoredIndex_>
160void fill_compressed_sparse_matrix_inconsistent(
162 InputIndex_ primary,
163 InputIndex_ secondary,
164 bool row,
165 const Pointer_* pointers,
166 StoredValue_* output_value,
167 StoredIndex_* output_index,
168 int threads)
169{
170 if (matrix.is_sparse()) {
171 Options opt;
172 opt.sparse_ordered_index = false;
173
174 parallelize([&](int, InputIndex_ start, InputIndex_ length) -> void {
175 std::vector<InputValue_> buffer_v(length);
176 std::vector<InputIndex_> buffer_i(length);
177 auto wrk = consecutive_extractor<true>(matrix, !row, static_cast<InputIndex_>(0), secondary, start, length, opt);
178 std::vector<Pointer_> offset_copy(pointers + start, pointers + start + length);
179
180 for (InputIndex_ x = 0; x < secondary; ++x) {
181 auto range = wrk->fetch(buffer_v.data(), buffer_i.data());
182 for (InputIndex_ i = 0; i < range.number; ++i) {
183 auto& pos = offset_copy[range.index[i] - start];
184 output_value[pos] = range.value[i];
185 output_index[pos] = x;
186 ++pos;
187 }
188 }
189 }, primary, threads);
190
191 } else {
192 parallelize([&](int, InputIndex_ start, InputIndex_ length) -> void {
193 std::vector<InputValue_> buffer_v(length);
194 auto wrk = consecutive_extractor<false>(matrix, !row, static_cast<InputIndex_>(0), secondary, start, length);
195 std::vector<Pointer_> offset_copy(pointers + start, pointers + start + length);
196
197 for (InputIndex_ x = 0; x < secondary; ++x) {
198 auto ptr = wrk->fetch(buffer_v.data());
199 for (InputIndex_ p = 0; p < length; ++p) {
200 auto val = ptr[p];
201 if (val != 0) {
202 auto& pos = offset_copy[p];
203 output_value[pos] = val;
204 output_index[pos] = x;
205 ++pos;
206 }
207 }
208 }
209 }, primary, threads);
210 }
211}
212
213}
227
243template<typename Value_, typename Index_, typename Count_>
245 Index_ NR = matrix.nrow();
246 Index_ NC = matrix.ncol();
247 Index_ primary = (row ? NR : NC);
248 Index_ secondary = (row ? NC : NR);
249 std::fill_n(output, primary, 0);
250
251 if (row == matrix.prefer_rows()) {
252 convert_to_compressed_sparse_internal::count_compressed_sparse_non_zeros_consistent(matrix, primary, secondary, row, output, options.num_threads);
253 } else {
254 convert_to_compressed_sparse_internal::count_compressed_sparse_non_zeros_inconsistent(matrix, primary, secondary, row, output, options.num_threads);
255 }
256}
257
267
287template<typename InputValue_, typename InputIndex_, typename Pointer_, typename StoredValue_, typename StoredIndex_>
290 bool row,
291 const Pointer_* pointers,
292 StoredValue_* output_value,
293 StoredIndex_* output_index,
295{
296 InputIndex_ NR = matrix.nrow();
297 InputIndex_ NC = matrix.ncol();
298 InputIndex_ primary = (row ? NR : NC);
299 InputIndex_ secondary = (row ? NC : NR);
300
301 if (row == matrix.prefer_rows()) {
302 convert_to_compressed_sparse_internal::fill_compressed_sparse_matrix_consistent(matrix, primary, secondary, row, pointers, output_value, output_index, options.num_threads);
303 } else {
304 convert_to_compressed_sparse_internal::fill_compressed_sparse_matrix_inconsistent(matrix, primary, secondary, row, pointers, output_value, output_index, options.num_threads);
305 }
306}
307
318template<typename Value_, typename Index_, typename Pointer_>
323 std::vector<Value_> value;
324
328 std::vector<Index_> index;
329
333 std::vector<Pointer_> pointers;
334};
335
351
369template<typename StoredValue_, typename StoredIndex_, typename StoredPointer_ = std::size_t, typename InputValue_, typename InputIndex_>
372 bool row,
374{
375 // We use size_t as the default pointer type here, as our output consists of vectors
376 // with the default allocator, for which the size_type is unlikely to be bigger than size_t.
378 auto& output_v = output.value;
379 auto& output_i = output.index;
380 auto& output_p = output.pointers;
381
382 InputIndex_ NR = matrix.nrow();
383 InputIndex_ NC = matrix.ncol();
384 InputIndex_ primary = (row ? NR : NC);
385 InputIndex_ secondary = (row ? NC : NR);
386
387 if (!options.two_pass) {
388 // Doing a single fragmented run and then concatenating everything together.
390 matrix,
391 row,
392 [&]{
394 roptions.num_threads = options.num_threads;
395 return roptions;
396 }()
397 );
398 const auto& store_v = frag.value;
399 const auto& store_i = frag.index;
400
401 output_p.resize(static_cast<std::size_t>(primary) + 1);
402 for (InputIndex_ p = 0; p < primary; ++p) {
403 output_p[p + 1] = output_p[p] + store_v[p].size();
404 }
405
406 output_v.reserve(output_p.back());
407 output_i.reserve(output_p.back());
408 for (InputIndex_ p = 0; p < primary; ++p) {
409 output_v.insert(output_v.end(), store_v[p].begin(), store_v[p].end());
410 output_i.insert(output_i.end(), store_i[p].begin(), store_i[p].end());
411 }
412
413 } else if (row == matrix.prefer_rows()) {
414 // First pass to figure out how many non-zeros there are.
415 output_p.resize(static_cast<std::size_t>(primary) + 1);
416 convert_to_compressed_sparse_internal::count_compressed_sparse_non_zeros_consistent(matrix, primary, secondary, row, output_p.data() + 1, options.num_threads);
417 for (InputIndex_ i = 1; i <= primary; ++i) {
418 output_p[i] += output_p[i - 1];
419 }
420
421 // Second pass to actually fill our vectors.
422 output_v.resize(output_p.back());
423 output_i.resize(output_p.back());
424 convert_to_compressed_sparse_internal::fill_compressed_sparse_matrix_consistent(
425 matrix,
426 primary,
427 secondary,
428 row,
429 output_p.data(),
430 output_v.data(),
431 output_i.data(),
432 options.num_threads
433 );
434
435 } else {
436 // First pass to figure out how many non-zeros there are.
437 output_p.resize(static_cast<std::size_t>(primary) + 1);
438 convert_to_compressed_sparse_internal::count_compressed_sparse_non_zeros_inconsistent(matrix, primary, secondary, row, output_p.data() + 1, options.num_threads);
439 for (InputIndex_ i = 1; i <= primary; ++i) {
440 output_p[i] += output_p[i - 1];
441 }
442
443 // Second pass to actually fill our vectors.
444 output_v.resize(output_p.back());
445 output_i.resize(output_p.back());
446 convert_to_compressed_sparse_internal::fill_compressed_sparse_matrix_inconsistent(
447 matrix,
448 primary,
449 secondary,
450 row,
451 output_p.data(),
452 output_v.data(),
453 output_i.data(),
454 options.num_threads
455 );
456 }
457
458 return output;
459}
460
469 bool two_pass = false;
470
474 int num_threads = 1;
475};
476
494template<
495 typename Value_,
496 typename Index_,
497 typename StoredValue_ = Value_,
498 typename StoredIndex_ = Index_,
499 typename StoredPointer_ = std::size_t,
500 typename InputValue_,
501 typename InputIndex_
502>
503std::shared_ptr<Matrix<Value_, Index_> > convert_to_compressed_sparse(const Matrix<InputValue_, InputIndex_>& matrix, bool row, const ConvertToCompressedSparseOptions& options) {
505 matrix,
506 row,
507 [&]{
509 ropt.two_pass = options.two_pass;
510 ropt.num_threads = options.num_threads;
511 return ropt;
512 }()
513 );
514 return std::shared_ptr<Matrix<Value_, Index_> >(
516 Value_,
517 Index_,
518 std::vector<StoredValue_>,
519 std::vector<StoredIndex_>,
520 std::vector<StoredPointer_>
521 >(
522 matrix.nrow(),
523 matrix.ncol(),
524 std::move(comp.value),
525 std::move(comp.index),
526 std::move(comp.pointers),
527 row,
528 []{
529 CompressedSparseMatrixOptions copt;
530 copt.check = false; // no need for checks, as we guarantee correctness.
531 return copt;
532 }()
533 )
534 );
535}
536
540// Backwards compatbility.
541template<typename Value_, typename Index_, typename Count_>
542void count_compressed_sparse_non_zeros(const tatami::Matrix<Value_, Index_>* matrix, bool row, Count_* output, int threads) {
544 *matrix,
545 row,
546 output,
547 [&]{
548 CountCompressedSparseNonZerosOptions copt;
549 copt.num_threads = threads;
550 return copt;
551 }()
552 );
553}
554
555template<typename InputValue_, typename InputIndex_, typename Pointer_, typename StoredValue_, typename StoredIndex_>
557 bool row,
558 const Pointer_* pointers,
559 StoredValue_* output_value,
560 StoredIndex_* output_index,
561 int threads)
562{
564 *matrix,
565 row,
566 pointers,
567 output_value,
568 output_index,
569 [&]{
570 FillCompressedSparseContentsOptions fopt;
571 fopt.num_threads = threads;
572 return fopt;
573 }()
574 );
575}
576
577template<typename StoredValue_, typename StoredIndex_, typename StoredPointer_ = std::size_t, typename InputValue_, typename InputIndex_>
578CompressedSparseContents<StoredValue_, StoredIndex_, StoredPointer_> retrieve_compressed_sparse_contents(const Matrix<InputValue_, InputIndex_>* matrix, bool row, bool two_pass, int threads = 1) {
580 *matrix,
581 row,
582 [&]{
583 RetrieveCompressedSparseContentsOptions opt;
584 opt.two_pass = two_pass;
585 opt.num_threads = threads;
586 return opt;
587 }()
588 );
589}
590
591template<typename Value_ = double, typename Index_ = int, typename StoredValue_ = Value_, typename StoredIndex_ = Index_, typename InputValue_, typename InputIndex_>
592std::shared_ptr<Matrix<Value_, Index_> > convert_to_compressed_sparse(const Matrix<InputValue_, InputIndex_>* matrix, bool row, bool two_pass = false, int threads = 1) {
594 *matrix,
595 row,
596 [&]{
597 ConvertToCompressedSparseOptions opt;
598 opt.two_pass = two_pass;
599 opt.num_threads = threads;
600 return opt;
601 }()
602 );
603}
604
605template <bool row_, typename Value_, typename Index_, typename InputValue_, typename InputIndex_>
606CompressedSparseContents<Value_, Index_, std::size_t> retrieve_compressed_sparse_contents(const Matrix<InputValue_, InputIndex_>* matrix, bool two_pass, int threads = 1) {
607 return retrieve_compressed_sparse_contents<Value_, Index_>(matrix, row_, two_pass, threads);
608}
609
610template <bool row_, typename Value_, typename Index_, typename StoredValue_ = Value_, typename StoredIndex_ = Index_, typename InputValue_, typename InputIndex_>
611std::shared_ptr<Matrix<Value_, Index_> > convert_to_compressed_sparse(const Matrix<InputValue_, InputIndex_>* matrix, bool two_pass = false, int threads = 1) {
613}
618}
619
620#endif
Compressed sparse matrix representation.
Compressed sparse matrix representation.
Definition CompressedSparseMatrix.hpp:503
Virtual class for a matrix.
Definition Matrix.hpp:59
virtual Index_ ncol() const =0
virtual Index_ nrow() const =0
virtual bool prefer_rows() const =0
virtual bool is_sparse() const =0
Templated construction of a new consecutive extractor.
Convert a matrix into a fragmented sparse format.
Flexible representations for matrix data.
Definition Extractor.hpp:15
CompressedSparseContents< StoredValue_, StoredIndex_, StoredPointer_ > retrieve_compressed_sparse_contents(const Matrix< InputValue_, InputIndex_ > &matrix, bool row, const RetrieveCompressedSparseContentsOptions &options)
Definition convert_to_compressed_sparse.hpp:370
std::shared_ptr< Matrix< Value_, Index_ > > convert_to_compressed_sparse(const Matrix< InputValue_, InputIndex_ > &matrix, bool row, const ConvertToCompressedSparseOptions &options)
Definition convert_to_compressed_sparse.hpp:503
void parallelize(Function_ fun, Index_ tasks, int threads)
Definition parallelize.hpp:42
FragmentedSparseContents< StoredValue_, StoredIndex_ > retrieve_fragmented_sparse_contents(const Matrix< InputValue_, InputIndex_ > &matrix, bool row, const RetrieveFragmentedSparseContentsOptions &options)
Definition convert_to_fragmented_sparse.hpp:77
void fill_compressed_sparse_contents(const tatami::Matrix< InputValue_, InputIndex_ > &matrix, bool row, const Pointer_ *pointers, StoredValue_ *output_value, StoredIndex_ *output_index, const FillCompressedSparseContentsOptions &options)
Definition convert_to_compressed_sparse.hpp:288
void count_compressed_sparse_non_zeros(const tatami::Matrix< Value_, Index_ > &matrix, bool row, Count_ *output, const CountCompressedSparseNonZerosOptions &options)
Definition convert_to_compressed_sparse.hpp:244
Parallelized iteration over a tatami::Matrix.
Compressed sparse contents.
Definition convert_to_compressed_sparse.hpp:319
std::vector< Index_ > index
Definition convert_to_compressed_sparse.hpp:328
std::vector< Value_ > value
Definition convert_to_compressed_sparse.hpp:323
std::vector< Pointer_ > pointers
Definition convert_to_compressed_sparse.hpp:333
Options for convert_to_compressed_sparse().
Definition convert_to_compressed_sparse.hpp:464
bool two_pass
Definition convert_to_compressed_sparse.hpp:469
int num_threads
Definition convert_to_compressed_sparse.hpp:474
Options for count_compressed_sparse_non_zeros().
Definition convert_to_compressed_sparse.hpp:221
int num_threads
Definition convert_to_compressed_sparse.hpp:225
Options for fill_compressed_sparse_contents().
Definition convert_to_compressed_sparse.hpp:261
int num_threads
Definition convert_to_compressed_sparse.hpp:265
Options for retrieve_compressed_sparse_contents().
Definition convert_to_compressed_sparse.hpp:339
int num_threads
Definition convert_to_compressed_sparse.hpp:349
bool two_pass
Definition convert_to_compressed_sparse.hpp:344
Options for retrieve_fragmented_sparse_contents().
Definition convert_to_fragmented_sparse.hpp:57
int num_threads
Definition convert_to_fragmented_sparse.hpp:61