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
4#include <memory>
5#include <vector>
6#include <cstddef>
7
12#include "../utils/Index_to_container.hpp"
13#include "../utils/copy.hpp"
14
21namespace tatami {
22
26namespace convert_to_compressed_sparse_internal {
27
28template<typename Value_, typename Index_, typename Count_>
29void count_compressed_sparse_non_zeros_consistent(
31 const Index_ primary,
32 const Index_ secondary,
33 const bool row,
34 Count_* const output,
35 const int threads)
36{
37 if (matrix.is_sparse()) {
38 Options opt;
39 opt.sparse_extract_value = false;
40 opt.sparse_extract_index = false;
41 opt.sparse_ordered_index = false;
42
43 parallelize([&](const int, const Index_ start, const Index_ length) -> void {
44 auto wrk = consecutive_extractor<true>(matrix, row, start, length, opt);
45 for (Index_ x = 0; x < length; ++x) {
46 const auto range = wrk->fetch(NULL, NULL);
47 output[start + x] = range.number;
48 }
49 }, primary, threads);
50
51 } else {
52 parallelize([&](const int, const Index_ start, const Index_ length) -> void {
53 auto buffer_v = create_container_of_Index_size<std::vector<Value_> >(secondary);
54 auto wrk = consecutive_extractor<false>(matrix, row, start, length);
55 for (Index_ p = start, pe = start + length; p < pe; ++p) {
56 const auto ptr = wrk->fetch(buffer_v.data());
57 Count_ count = 0;
58 for (Index_ s = 0; s < secondary; ++s) {
59 count += (ptr[s] != 0);
60 }
61 output[p] = count;
62 }
63 }, primary, threads);
64 }
65}
66
67template<typename Value_, typename Index_, typename Count_>
68void count_compressed_sparse_non_zeros_inconsistent(
70 const Index_ primary,
71 const Index_ secondary,
72 const bool row,
73 Count_* const output,
74 const int threads
75) {
76 auto nz_counts = sanisizer::create<std::vector<std::vector<Count_> > >(threads - 1);
77 for (auto& x : nz_counts) {
78 x.resize(primary);
79 }
80
81 if (matrix.is_sparse()) {
82 Options opt;
83 opt.sparse_extract_value = false;
84 opt.sparse_ordered_index = false;
85
86 parallelize([&](const int t, const Index_ start, const Index_ length) -> void {
87 auto wrk = consecutive_extractor<true>(matrix, !row, start, length, opt);
88 auto buffer_i = create_container_of_Index_size<std::vector<Index_> >(primary);
89 const auto my_counts = (t > 0 ? nz_counts[t - 1].data() : output);
90
91 for (Index_ x = 0; x < length; ++x) {
92 const auto range = wrk->fetch(NULL, buffer_i.data());
93 for (Index_ i = 0; i < range.number; ++i) {
94 ++my_counts[range.index[i]];
95 }
96 }
97 }, secondary, threads);
98
99 } else {
100 parallelize([&](const int t, const Index_ start, const Index_ length) -> void {
101 auto wrk = consecutive_extractor<false>(matrix, !row, start, length);
102 auto buffer_v = create_container_of_Index_size<std::vector<Value_> >(primary);
103 const auto my_counts = (t > 0 ? nz_counts[t - 1].data() : output);
104
105 for (Index_ x = 0; x < length; ++x) {
106 const auto ptr = wrk->fetch(buffer_v.data());
107 for (Index_ p = 0; p < primary; ++p) {
108 my_counts[p] += (ptr[p] != 0);
109 }
110 }
111 }, secondary, threads);
112 }
113
114 for (auto& y : nz_counts) {
115 for (Index_ p = 0; p < primary; ++p) {
116 output[p] += y[p];
117 }
118 }
119}
120
121template<typename InputValue_, typename InputIndex_, typename Pointer_, typename StoredValue_, typename StoredIndex_>
122void fill_compressed_sparse_matrix_consistent(
124 const InputIndex_ primary,
125 const InputIndex_ secondary,
126 const bool row,
127 const Pointer_* const pointers,
128 StoredValue_* const output_value,
129 StoredIndex_* const output_index,
130 const int threads)
131{
132 if (matrix.is_sparse()) {
133 Options opt;
134 opt.sparse_ordered_index = false;
135
136 parallelize([&](const int, const InputIndex_ start, const InputIndex_ length) -> void {
137 auto wrk = consecutive_extractor<true>(matrix, row, start, length, opt);
138 auto buffer_v = create_container_of_Index_size<std::vector<InputValue_> >(secondary);
139 auto buffer_i = create_container_of_Index_size<std::vector<InputIndex_> >(secondary);
140
141 for (InputIndex_ p = start, pe = start + length; p < pe; ++p) {
142 // Resist the urge to `fetch()` straight into 'output_v'
143 // and 'output_i', as implementations may assume that they
144 // have the entire 'length' length to play with, and the
145 // output vectors only have whatever is allocated from the
146 // first pass (which might be nothing for an all-zero matrix).
147 const auto range = wrk->fetch(buffer_v.data(), buffer_i.data());
148 const auto offset = pointers[p];
149 std::copy_n(range.value, range.number, output_value + offset);
150 std::copy_n(range.index, range.number, output_index + offset);
151 }
152 }, primary, threads);
153
154 } else {
155 parallelize([&](const int, const InputIndex_ start, const InputIndex_ length) -> void {
156 auto buffer_v = create_container_of_Index_size<std::vector<InputValue_> >(secondary);
157 auto wrk = consecutive_extractor<false>(matrix, row, start, length);
158
159 for (InputIndex_ p = start, pe = start + length; p < pe; ++p) {
160 const auto ptr = wrk->fetch(buffer_v.data());
161 auto offset = pointers[p];
162 for (InputIndex_ s = 0; s < secondary; ++s) {
163 const auto val = ptr[s];
164 if (val != 0) {
165 output_value[offset] = val;
166 output_index[offset] = s;
167 ++offset;
168 }
169 }
170 }
171 }, primary, threads);
172 }
173}
174
175template<typename InputValue_, typename InputIndex_, typename Pointer_, typename StoredValue_, typename StoredIndex_>
176void fill_compressed_sparse_matrix_inconsistent(
178 const InputIndex_ primary,
179 const InputIndex_ secondary,
180 const bool row,
181 const Pointer_* const pointers,
182 StoredValue_* const output_value,
183 StoredIndex_* const output_index,
184 const int threads)
185{
186 if (matrix.is_sparse()) {
187 Options opt;
188 opt.sparse_ordered_index = false;
189
190 parallelize([&](const int, const InputIndex_ start, const InputIndex_ length) -> void {
191 auto wrk = consecutive_extractor<true>(matrix, !row, static_cast<InputIndex_>(0), secondary, start, length, opt);
192 auto buffer_v = create_container_of_Index_size<std::vector<InputValue_> >(length);
193 auto buffer_i = create_container_of_Index_size<std::vector<InputIndex_> >(length);
194 std::vector<Pointer_> offset_copy(pointers + start, pointers + start + length);
195
196 for (InputIndex_ x = 0; x < secondary; ++x) {
197 const auto range = wrk->fetch(buffer_v.data(), buffer_i.data());
198 for (InputIndex_ i = 0; i < range.number; ++i) {
199 auto& pos = offset_copy[range.index[i] - start];
200 output_value[pos] = range.value[i];
201 output_index[pos] = x;
202 ++pos;
203 }
204 }
205 }, primary, threads);
206
207 } else {
208 parallelize([&](const int, const InputIndex_ start, const InputIndex_ length) -> void {
209 auto wrk = consecutive_extractor<false>(matrix, !row, static_cast<InputIndex_>(0), secondary, start, length);
210 auto buffer_v = create_container_of_Index_size<std::vector<InputValue_> >(length);
211 std::vector<Pointer_> offset_copy(pointers + start, pointers + start + length);
212
213 for (InputIndex_ x = 0; x < secondary; ++x) {
214 const auto ptr = wrk->fetch(buffer_v.data());
215 for (InputIndex_ p = 0; p < length; ++p) {
216 const auto val = ptr[p];
217 if (val != 0) {
218 auto& pos = offset_copy[p];
219 output_value[pos] = val;
220 output_index[pos] = x;
221 ++pos;
222 }
223 }
224 }
225 }, primary, threads);
226 }
227}
228
229}
243
259template<typename Value_, typename Index_, typename Count_>
261 const tatami::Matrix<Value_, Index_>& matrix,
262 const bool row,
263 Count_* const output,
265) {
266 const Index_ NR = matrix.nrow();
267 const Index_ NC = matrix.ncol();
268 const Index_ primary = (row ? NR : NC);
269 const Index_ secondary = (row ? NC : NR);
270 std::fill_n(output, primary, 0);
271
272 if (row == matrix.prefer_rows()) {
273 convert_to_compressed_sparse_internal::count_compressed_sparse_non_zeros_consistent(matrix, primary, secondary, row, output, options.num_threads);
274 } else {
275 convert_to_compressed_sparse_internal::count_compressed_sparse_non_zeros_inconsistent(matrix, primary, secondary, row, output, options.num_threads);
276 }
277}
278
288
308template<typename InputValue_, typename InputIndex_, typename Pointer_, typename StoredValue_, typename StoredIndex_>
311 const bool row,
312 const Pointer_* const pointers,
313 StoredValue_* const output_value,
314 StoredIndex_* const output_index,
316) {
317 const InputIndex_ NR = matrix.nrow();
318 const InputIndex_ NC = matrix.ncol();
319 const InputIndex_ primary = (row ? NR : NC);
320 const InputIndex_ secondary = (row ? NC : NR);
321
322 if (row == matrix.prefer_rows()) {
323 convert_to_compressed_sparse_internal::fill_compressed_sparse_matrix_consistent(matrix, primary, secondary, row, pointers, output_value, output_index, options.num_threads);
324 } else {
325 convert_to_compressed_sparse_internal::fill_compressed_sparse_matrix_inconsistent(matrix, primary, secondary, row, pointers, output_value, output_index, options.num_threads);
326 }
327}
328
339template<typename Value_, typename Index_, typename Pointer_>
344 std::vector<Value_> value;
345
349 std::vector<Index_> index;
350
354 std::vector<Pointer_> pointers;
355};
356
372
390template<typename StoredValue_, typename StoredIndex_, typename StoredPointer_ = std::size_t, typename InputValue_, typename InputIndex_>
393 const bool row,
395{
396 // We use size_t as the default pointer type here, as our output consists of vectors
397 // with the default allocator, for which the size_type is unlikely to be bigger than size_t.
399 auto& output_v = output.value;
400 auto& output_i = output.index;
401 auto& output_p = output.pointers;
402
403 const InputIndex_ NR = matrix.nrow();
404 const InputIndex_ NC = matrix.ncol();
405 const InputIndex_ primary = (row ? NR : NC);
406 const InputIndex_ secondary = (row ? NC : NR);
407
408 output_p.resize(sanisizer::sum<I<decltype(output_p.size())> >(primary, 1));
409
410 if (!options.two_pass) {
411 // Doing a single fragmented run and then concatenating everything together.
413 matrix,
414 row,
415 [&]{
417 roptions.num_threads = options.num_threads;
418 return roptions;
419 }()
420 );
421 const auto& store_v = frag.value;
422 const auto& store_i = frag.index;
423
424 for (InputIndex_ p = 0; p < primary; ++p) {
425 output_p[p + 1] = output_p[p] + store_v[p].size();
426 }
427
428 output_v.reserve(output_p.back());
429 output_i.reserve(output_p.back());
430 for (InputIndex_ p = 0; p < primary; ++p) {
431 output_v.insert(output_v.end(), store_v[p].begin(), store_v[p].end());
432 output_i.insert(output_i.end(), store_i[p].begin(), store_i[p].end());
433 }
434
435 } else if (row == matrix.prefer_rows()) {
436 // First pass to figure out how many non-zeros there are.
437 convert_to_compressed_sparse_internal::count_compressed_sparse_non_zeros_consistent(matrix, primary, secondary, row, output_p.data() + 1, options.num_threads);
438 for (InputIndex_ i = 1; i <= primary; ++i) {
439 output_p[i] += output_p[i - 1];
440 }
441
442 // Second pass to actually fill our vectors.
443 output_v.resize(output_p.back());
444 output_i.resize(output_p.back());
445 convert_to_compressed_sparse_internal::fill_compressed_sparse_matrix_consistent(
446 matrix,
447 primary,
448 secondary,
449 row,
450 output_p.data(),
451 output_v.data(),
452 output_i.data(),
453 options.num_threads
454 );
455
456 } else {
457 // First pass to figure out how many non-zeros there are.
458 convert_to_compressed_sparse_internal::count_compressed_sparse_non_zeros_inconsistent(matrix, primary, secondary, row, output_p.data() + 1, options.num_threads);
459 for (InputIndex_ i = 1; i <= primary; ++i) {
460 output_p[i] += output_p[i - 1];
461 }
462
463 // Second pass to actually fill our vectors.
464 output_v.resize(output_p.back());
465 output_i.resize(output_p.back());
466 convert_to_compressed_sparse_internal::fill_compressed_sparse_matrix_inconsistent(
467 matrix,
468 primary,
469 secondary,
470 row,
471 output_p.data(),
472 output_v.data(),
473 output_i.data(),
474 options.num_threads
475 );
476 }
477
478 return output;
479}
480
489 bool two_pass = false;
490
494 int num_threads = 1;
495};
496
514template<
515 typename Value_,
516 typename Index_,
517 typename StoredValue_ = Value_,
518 typename StoredIndex_ = Index_,
519 typename StoredPointer_ = std::size_t,
520 typename InputValue_,
521 typename InputIndex_
522>
523std::shared_ptr<Matrix<Value_, Index_> > convert_to_compressed_sparse(
525 const bool row,
527) {
529 matrix,
530 row,
531 [&]{
533 ropt.two_pass = options.two_pass;
534 ropt.num_threads = options.num_threads;
535 return ropt;
536 }()
537 );
538 return std::shared_ptr<Matrix<Value_, Index_> >(
540 Value_,
541 Index_,
542 std::vector<StoredValue_>,
543 std::vector<StoredIndex_>,
544 std::vector<StoredPointer_>
545 >(
546 matrix.nrow(),
547 matrix.ncol(),
548 std::move(comp.value),
549 std::move(comp.index),
550 std::move(comp.pointers),
551 row,
552 []{
553 CompressedSparseMatrixOptions copt;
554 copt.check = false; // no need for checks, as we guarantee correctness.
555 return copt;
556 }()
557 )
558 );
559}
560
564// Backwards compatbility.
565template<typename Value_, typename Index_, typename Count_>
566void count_compressed_sparse_non_zeros(const tatami::Matrix<Value_, Index_>* matrix, bool row, Count_* output, int threads) {
568 *matrix,
569 row,
570 output,
571 [&]{
572 CountCompressedSparseNonZerosOptions copt;
573 copt.num_threads = threads;
574 return copt;
575 }()
576 );
577}
578
579template<typename InputValue_, typename InputIndex_, typename Pointer_, typename StoredValue_, typename StoredIndex_>
581 bool row,
582 const Pointer_* pointers,
583 StoredValue_* output_value,
584 StoredIndex_* output_index,
585 int threads)
586{
588 *matrix,
589 row,
590 pointers,
591 output_value,
592 output_index,
593 [&]{
594 FillCompressedSparseContentsOptions fopt;
595 fopt.num_threads = threads;
596 return fopt;
597 }()
598 );
599}
600
601template<typename StoredValue_, typename StoredIndex_, typename StoredPointer_ = std::size_t, typename InputValue_, typename InputIndex_>
602CompressedSparseContents<StoredValue_, StoredIndex_, StoredPointer_> retrieve_compressed_sparse_contents(const Matrix<InputValue_, InputIndex_>* matrix, bool row, bool two_pass, int threads = 1) {
604 *matrix,
605 row,
606 [&]{
607 RetrieveCompressedSparseContentsOptions opt;
608 opt.two_pass = two_pass;
609 opt.num_threads = threads;
610 return opt;
611 }()
612 );
613}
614
615template<typename Value_ = double, typename Index_ = int, typename StoredValue_ = Value_, typename StoredIndex_ = Index_, typename InputValue_, typename InputIndex_>
616std::shared_ptr<Matrix<Value_, Index_> > convert_to_compressed_sparse(const Matrix<InputValue_, InputIndex_>* matrix, bool row, bool two_pass = false, int threads = 1) {
618 *matrix,
619 row,
620 [&]{
621 ConvertToCompressedSparseOptions opt;
622 opt.two_pass = two_pass;
623 opt.num_threads = threads;
624 return opt;
625 }()
626 );
627}
628
629template <bool row_, typename Value_, typename Index_, typename InputValue_, typename InputIndex_>
630CompressedSparseContents<Value_, Index_, std::size_t> retrieve_compressed_sparse_contents(const Matrix<InputValue_, InputIndex_>* matrix, bool two_pass, int threads = 1) {
631 return retrieve_compressed_sparse_contents<Value_, Index_>(matrix, row_, two_pass, threads);
632}
633
634template <bool row_, typename Value_, typename Index_, typename StoredValue_ = Value_, typename StoredIndex_ = Index_, typename InputValue_, typename InputIndex_>
635std::shared_ptr<Matrix<Value_, Index_> > convert_to_compressed_sparse(const Matrix<InputValue_, InputIndex_>* matrix, bool two_pass = false, int threads = 1) {
637}
642}
643
644#endif
Compressed sparse matrix representation.
Compressed sparse matrix representation.
Definition CompressedSparseMatrix.hpp:581
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.
Copy data from one buffer to another.
Flexible representations for matrix data.
Definition Extractor.hpp:15
void count_compressed_sparse_non_zeros(const tatami::Matrix< Value_, Index_ > &matrix, const bool row, Count_ *const output, const CountCompressedSparseNonZerosOptions &options)
Definition convert_to_compressed_sparse.hpp:260
void parallelize(Function_ fun, const Index_ tasks, const int threads)
Definition parallelize.hpp:42
FragmentedSparseContents< StoredValue_, StoredIndex_ > retrieve_fragmented_sparse_contents(const Matrix< InputValue_, InputIndex_ > &matrix, const bool row, const RetrieveFragmentedSparseContentsOptions &options)
Definition convert_to_fragmented_sparse.hpp:82
CompressedSparseContents< StoredValue_, StoredIndex_, StoredPointer_ > retrieve_compressed_sparse_contents(const Matrix< InputValue_, InputIndex_ > &matrix, const bool row, const RetrieveCompressedSparseContentsOptions &options)
Definition convert_to_compressed_sparse.hpp:391
void fill_compressed_sparse_contents(const tatami::Matrix< InputValue_, InputIndex_ > &matrix, const bool row, const Pointer_ *const pointers, StoredValue_ *const output_value, StoredIndex_ *const output_index, const FillCompressedSparseContentsOptions &options)
Definition convert_to_compressed_sparse.hpp:309
std::shared_ptr< Matrix< Value_, Index_ > > convert_to_compressed_sparse(const Matrix< InputValue_, InputIndex_ > &matrix, const bool row, const ConvertToCompressedSparseOptions &options)
Definition convert_to_compressed_sparse.hpp:523
Parallelized iteration over a tatami::Matrix.
Compressed sparse contents.
Definition convert_to_compressed_sparse.hpp:340
std::vector< Index_ > index
Definition convert_to_compressed_sparse.hpp:349
std::vector< Value_ > value
Definition convert_to_compressed_sparse.hpp:344
std::vector< Pointer_ > pointers
Definition convert_to_compressed_sparse.hpp:354
Options for convert_to_compressed_sparse().
Definition convert_to_compressed_sparse.hpp:484
bool two_pass
Definition convert_to_compressed_sparse.hpp:489
int num_threads
Definition convert_to_compressed_sparse.hpp:494
Options for count_compressed_sparse_non_zeros().
Definition convert_to_compressed_sparse.hpp:237
int num_threads
Definition convert_to_compressed_sparse.hpp:241
Options for fill_compressed_sparse_contents().
Definition convert_to_compressed_sparse.hpp:282
int num_threads
Definition convert_to_compressed_sparse.hpp:286
Options for retrieve_compressed_sparse_contents().
Definition convert_to_compressed_sparse.hpp:360
int num_threads
Definition convert_to_compressed_sparse.hpp:370
bool two_pass
Definition convert_to_compressed_sparse.hpp:365
Options for retrieve_fragmented_sparse_contents().
Definition convert_to_fragmented_sparse.hpp:62
int num_threads
Definition convert_to_fragmented_sparse.hpp:66