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#include <optional>
8
14#include "../utils/copy.hpp"
15
22namespace tatami {
23
27namespace convert_to_compressed_sparse_internal {
28
29template<typename Value_, typename Index_, typename Count_>
30void count_compressed_sparse_non_zeros_consistent(
32 const Index_ primary,
33 const Index_ secondary,
34 const bool row,
35 Count_* const output,
36 const int threads)
37{
38 if (matrix.is_sparse()) {
39 Options opt;
40 opt.sparse_extract_value = false;
41 opt.sparse_extract_index = false;
42 opt.sparse_ordered_index = false;
43
44 parallelize([&](const int, const Index_ start, const Index_ length) -> void {
45 auto wrk = consecutive_extractor<true>(matrix, row, start, length, opt);
46 for (Index_ x = 0; x < length; ++x) {
47 const auto range = wrk->fetch(NULL, NULL);
48 output[start + x] = range.number;
49 }
50 }, primary, threads);
51
52 } else {
53 parallelize([&](const int, const Index_ start, const Index_ length) -> void {
54 auto buffer_v = create_container_of_Index_size<std::vector<Value_> >(secondary);
55 auto wrk = consecutive_extractor<false>(matrix, row, start, length);
56 for (Index_ p = start, pe = start + length; p < pe; ++p) {
57 const auto ptr = wrk->fetch(buffer_v.data());
58 Count_ count = 0;
59 for (Index_ s = 0; s < secondary; ++s) {
60 count += (ptr[s] != 0);
61 }
62 output[p] = count;
63 }
64 }, primary, threads);
65 }
66}
67
68template<typename Value_, typename Index_, typename Count_>
69void count_compressed_sparse_non_zeros_inconsistent(
71 const Index_ primary,
72 const Index_ secondary,
73 const bool row,
74 Count_* const output,
75 const int threads
76) {
77 // To minimize false sharing, we allocate each buffer as a per-thread vector before moving it into the nz_counts for serial use.
78 // We skip the allocation for the first thread as this is allowed to use the (presumably zeroed) output array directly.
79 // Needless to say, the number of threads had better be positive.
80 auto nz_counts = sanisizer::create<std::vector<std::optional<std::vector<Count_> > > >(threads - 1);
81 const auto get_ptr = [&](const int t, std::optional<std::vector<Count_> >& nz_tmp) -> Count_* {
82 if (t) {
83 nz_tmp.emplace(cast_Index_to_container_size<std::vector<Count_> >(primary));
84 return nz_tmp->data();
85 } else {
86 return output;
87 }
88 };
89 const auto save_output = [&](const int t, std::optional<std::vector<Count_> >& nz_tmp) {
90 if (t) {
91 nz_counts[t - 1] = std::move(nz_tmp);
92 }
93 };
94 int num_used;
95
96 if (matrix.is_sparse()) {
97 Options opt;
98 opt.sparse_extract_value = false;
99 opt.sparse_ordered_index = false;
100
101 num_used = parallelize([&](const int t, const Index_ start, const Index_ length) -> void {
102 std::optional<std::vector<Count_> > nz_tmp;
103 Count_* cur_counts = get_ptr(t, nz_tmp);
104
105 auto wrk = consecutive_extractor<true>(matrix, !row, start, length, opt);
106 auto buffer_i = create_container_of_Index_size<std::vector<Index_> >(primary);
107 for (Index_ x = 0; x < length; ++x) {
108 const auto range = wrk->fetch(NULL, buffer_i.data());
109 for (Index_ i = 0; i < range.number; ++i) {
110 ++cur_counts[range.index[i]];
111 }
112 }
113
114 save_output(t, nz_tmp);
115 }, secondary, threads);
116
117 } else {
118 num_used = parallelize([&](const int t, const Index_ start, const Index_ length) -> void {
119 std::optional<std::vector<Count_> > nz_tmp;
120 Count_* cur_counts = get_ptr(t, nz_tmp);
121
122 auto wrk = consecutive_extractor<false>(matrix, !row, start, length);
123 auto buffer_v = create_container_of_Index_size<std::vector<Value_> >(primary);
124 for (Index_ x = 0; x < length; ++x) {
125 const auto ptr = wrk->fetch(buffer_v.data());
126 for (Index_ p = 0; p < primary; ++p) {
127 cur_counts[p] += (ptr[p] != 0);
128 }
129 }
130
131 save_output(t, nz_tmp);
132 }, secondary, threads);
133 }
134
135 for (int t = 1; t < num_used; ++t) {
136 const auto& y = *(nz_counts[t - 1]);
137 for (Index_ p = 0; p < primary; ++p) {
138 output[p] += y[p];
139 }
140 }
141}
142
143template<typename InputValue_, typename InputIndex_, typename Pointer_, typename StoredValue_, typename StoredIndex_>
144void fill_compressed_sparse_matrix_consistent(
146 const InputIndex_ primary,
147 const InputIndex_ secondary,
148 const bool row,
149 const Pointer_* const pointers,
150 StoredValue_* const output_value,
151 StoredIndex_* const output_index,
152 const int threads)
153{
154 if (matrix.is_sparse()) {
155 Options opt;
156 opt.sparse_ordered_index = false;
157
158 parallelize([&](const int, const InputIndex_ start, const InputIndex_ length) -> void {
159 auto wrk = consecutive_extractor<true>(matrix, row, start, length, opt);
160 auto buffer_v = create_container_of_Index_size<std::vector<InputValue_> >(secondary);
161 auto buffer_i = create_container_of_Index_size<std::vector<InputIndex_> >(secondary);
162
163 for (InputIndex_ p = start, pe = start + length; p < pe; ++p) {
164 // Resist the urge to `fetch()` straight into 'output_v'
165 // and 'output_i', as implementations may assume that they
166 // have the entire 'length' length to play with, and the
167 // output vectors only have whatever is allocated from the
168 // first pass (which might be nothing for an all-zero matrix).
169 const auto range = wrk->fetch(buffer_v.data(), buffer_i.data());
170 const auto offset = pointers[p];
171 std::copy_n(range.value, range.number, output_value + offset);
172 std::copy_n(range.index, range.number, output_index + offset);
173 }
174 }, primary, threads);
175
176 } else {
177 parallelize([&](const int, const InputIndex_ start, const InputIndex_ length) -> void {
178 auto buffer_v = create_container_of_Index_size<std::vector<InputValue_> >(secondary);
179 auto wrk = consecutive_extractor<false>(matrix, row, start, length);
180
181 for (InputIndex_ p = start, pe = start + length; p < pe; ++p) {
182 const auto ptr = wrk->fetch(buffer_v.data());
183 auto offset = pointers[p];
184 for (InputIndex_ s = 0; s < secondary; ++s) {
185 const auto val = ptr[s];
186 if (val != 0) {
187 output_value[offset] = val;
188 output_index[offset] = s;
189 ++offset;
190 }
191 }
192 }
193 }, primary, threads);
194 }
195}
196
197template<typename InputValue_, typename InputIndex_, typename Pointer_, typename StoredValue_, typename StoredIndex_>
198void fill_compressed_sparse_matrix_inconsistent(
200 const InputIndex_ primary,
201 const InputIndex_ secondary,
202 const bool row,
203 const Pointer_* const pointers,
204 StoredValue_* const output_value,
205 StoredIndex_* const output_index,
206 const int threads)
207{
208 if (matrix.is_sparse()) {
209 Options opt;
210 opt.sparse_ordered_index = false;
211
212 parallelize([&](const int, const InputIndex_ start, const InputIndex_ length) -> void {
213 auto wrk = consecutive_extractor<true>(matrix, !row, static_cast<InputIndex_>(0), secondary, start, length, opt);
214 auto buffer_v = create_container_of_Index_size<std::vector<InputValue_> >(length);
215 auto buffer_i = create_container_of_Index_size<std::vector<InputIndex_> >(length);
216 std::vector<Pointer_> offset_copy(pointers + start, pointers + start + length);
217
218 for (InputIndex_ x = 0; x < secondary; ++x) {
219 const auto range = wrk->fetch(buffer_v.data(), buffer_i.data());
220 for (InputIndex_ i = 0; i < range.number; ++i) {
221 auto& pos = offset_copy[range.index[i] - start];
222 output_value[pos] = range.value[i];
223 output_index[pos] = x;
224 ++pos;
225 }
226 }
227 }, primary, threads);
228
229 } else {
230 parallelize([&](const int, const InputIndex_ start, const InputIndex_ length) -> void {
231 auto wrk = consecutive_extractor<false>(matrix, !row, static_cast<InputIndex_>(0), secondary, start, length);
232 auto buffer_v = create_container_of_Index_size<std::vector<InputValue_> >(length);
233 std::vector<Pointer_> offset_copy(pointers + start, pointers + start + length);
234
235 for (InputIndex_ x = 0; x < secondary; ++x) {
236 const auto ptr = wrk->fetch(buffer_v.data());
237 for (InputIndex_ p = 0; p < length; ++p) {
238 const auto val = ptr[p];
239 if (val != 0) {
240 auto& pos = offset_copy[p];
241 output_value[pos] = val;
242 output_index[pos] = x;
243 ++pos;
244 }
245 }
246 }
247 }, primary, threads);
248 }
249}
250
251}
265
281template<typename Value_, typename Index_, typename Count_>
283 const tatami::Matrix<Value_, Index_>& matrix,
284 const bool row,
285 Count_* const output,
287) {
288 const Index_ NR = matrix.nrow();
289 const Index_ NC = matrix.ncol();
290 const Index_ primary = (row ? NR : NC);
291 const Index_ secondary = (row ? NC : NR);
292 std::fill_n(output, primary, 0);
293
294 if (row == matrix.prefer_rows()) {
295 convert_to_compressed_sparse_internal::count_compressed_sparse_non_zeros_consistent(matrix, primary, secondary, row, output, options.num_threads);
296 } else {
297 convert_to_compressed_sparse_internal::count_compressed_sparse_non_zeros_inconsistent(matrix, primary, secondary, row, output, options.num_threads);
298 }
299}
300
310
330template<typename InputValue_, typename InputIndex_, typename Pointer_, typename StoredValue_, typename StoredIndex_>
333 const bool row,
334 const Pointer_* const pointers,
335 StoredValue_* const output_value,
336 StoredIndex_* const output_index,
338) {
339 const InputIndex_ NR = matrix.nrow();
340 const InputIndex_ NC = matrix.ncol();
341 const InputIndex_ primary = (row ? NR : NC);
342 const InputIndex_ secondary = (row ? NC : NR);
343
344 if (row == matrix.prefer_rows()) {
345 convert_to_compressed_sparse_internal::fill_compressed_sparse_matrix_consistent(matrix, primary, secondary, row, pointers, output_value, output_index, options.num_threads);
346 } else {
347 convert_to_compressed_sparse_internal::fill_compressed_sparse_matrix_inconsistent(matrix, primary, secondary, row, pointers, output_value, output_index, options.num_threads);
348 }
349}
350
361template<typename Value_, typename Index_, typename Pointer_>
366 std::vector<Value_> value;
367
371 std::vector<Index_> index;
372
376 std::vector<Pointer_> pointers;
377};
378
394
412template<typename StoredValue_, typename StoredIndex_, typename StoredPointer_ = std::size_t, typename InputValue_, typename InputIndex_>
415 const bool row,
417{
418 // We use size_t as the default pointer type here, as our output consists of vectors
419 // with the default allocator, for which the size_type is unlikely to be bigger than size_t.
421 auto& output_v = output.value;
422 auto& output_i = output.index;
423 auto& output_p = output.pointers;
424
425 const InputIndex_ NR = matrix.nrow();
426 const InputIndex_ NC = matrix.ncol();
427 const InputIndex_ primary = (row ? NR : NC);
428 const InputIndex_ secondary = (row ? NC : NR);
429
430 output_p.resize(sanisizer::sum<I<decltype(output_p.size())> >(attest_for_Index(primary), 1));
431
432 if (!options.two_pass) {
433 // Doing a single fragmented run and then concatenating everything together.
435 matrix,
436 row,
437 [&]{
439 roptions.num_threads = options.num_threads;
440 return roptions;
441 }()
442 );
443 const auto& store_v = frag.value;
444 const auto& store_i = frag.index;
445
446 for (InputIndex_ p = 0; p < primary; ++p) {
447 output_p[p + 1] = output_p[p] + store_v[p].size();
448 }
449
450 output_v.reserve(output_p.back());
451 output_i.reserve(output_p.back());
452 for (InputIndex_ p = 0; p < primary; ++p) {
453 output_v.insert(output_v.end(), store_v[p].begin(), store_v[p].end());
454 output_i.insert(output_i.end(), store_i[p].begin(), store_i[p].end());
455 }
456
457 } else if (row == matrix.prefer_rows()) {
458 // First pass to figure out how many non-zeros there are.
459 convert_to_compressed_sparse_internal::count_compressed_sparse_non_zeros_consistent(matrix, primary, secondary, row, output_p.data() + 1, options.num_threads);
460 for (InputIndex_ i = 1; i <= primary; ++i) {
461 output_p[i] += output_p[i - 1];
462 }
463
464 // Second pass to actually fill our vectors.
465 output_v.resize(output_p.back());
466 output_i.resize(output_p.back());
467 convert_to_compressed_sparse_internal::fill_compressed_sparse_matrix_consistent(
468 matrix,
469 primary,
470 secondary,
471 row,
472 output_p.data(),
473 output_v.data(),
474 output_i.data(),
475 options.num_threads
476 );
477
478 } else {
479 // First pass to figure out how many non-zeros there are.
480 convert_to_compressed_sparse_internal::count_compressed_sparse_non_zeros_inconsistent(matrix, primary, secondary, row, output_p.data() + 1, options.num_threads);
481 for (InputIndex_ i = 1; i <= primary; ++i) {
482 output_p[i] += output_p[i - 1];
483 }
484
485 // Second pass to actually fill our vectors.
486 output_v.resize(output_p.back());
487 output_i.resize(output_p.back());
488 convert_to_compressed_sparse_internal::fill_compressed_sparse_matrix_inconsistent(
489 matrix,
490 primary,
491 secondary,
492 row,
493 output_p.data(),
494 output_v.data(),
495 output_i.data(),
496 options.num_threads
497 );
498 }
499
500 return output;
501}
502
511 bool two_pass = false;
512
516 int num_threads = 1;
517};
518
536template<
537 typename Value_,
538 typename Index_,
539 typename StoredValue_ = Value_,
540 typename StoredIndex_ = Index_,
541 typename StoredPointer_ = std::size_t,
542 typename InputValue_,
543 typename InputIndex_
544>
545std::shared_ptr<Matrix<Value_, Index_> > convert_to_compressed_sparse(
547 const bool row,
549) {
551 matrix,
552 row,
553 [&]{
555 ropt.two_pass = options.two_pass;
556 ropt.num_threads = options.num_threads;
557 return ropt;
558 }()
559 );
560 return std::shared_ptr<Matrix<Value_, Index_> >(
562 Value_,
563 Index_,
564 std::vector<StoredValue_>,
565 std::vector<StoredIndex_>,
566 std::vector<StoredPointer_>
567 >(
568 matrix.nrow(),
569 matrix.ncol(),
570 std::move(comp.value),
571 std::move(comp.index),
572 std::move(comp.pointers),
573 row,
574 []{
575 CompressedSparseMatrixOptions copt;
576 copt.check = false; // no need for checks, as we guarantee correctness.
577 return copt;
578 }()
579 )
580 );
581}
582
586// Backwards compatbility.
587template<typename Value_, typename Index_, typename Count_>
588void count_compressed_sparse_non_zeros(const tatami::Matrix<Value_, Index_>* matrix, bool row, Count_* output, int threads) {
590 *matrix,
591 row,
592 output,
593 [&]{
594 CountCompressedSparseNonZerosOptions copt;
595 copt.num_threads = threads;
596 return copt;
597 }()
598 );
599}
600
601template<typename InputValue_, typename InputIndex_, typename Pointer_, typename StoredValue_, typename StoredIndex_>
603 bool row,
604 const Pointer_* pointers,
605 StoredValue_* output_value,
606 StoredIndex_* output_index,
607 int threads)
608{
610 *matrix,
611 row,
612 pointers,
613 output_value,
614 output_index,
615 [&]{
616 FillCompressedSparseContentsOptions fopt;
617 fopt.num_threads = threads;
618 return fopt;
619 }()
620 );
621}
622
623template<typename StoredValue_, typename StoredIndex_, typename StoredPointer_ = std::size_t, typename InputValue_, typename InputIndex_>
624CompressedSparseContents<StoredValue_, StoredIndex_, StoredPointer_> retrieve_compressed_sparse_contents(const Matrix<InputValue_, InputIndex_>* matrix, bool row, bool two_pass, int threads = 1) {
626 *matrix,
627 row,
628 [&]{
629 RetrieveCompressedSparseContentsOptions opt;
630 opt.two_pass = two_pass;
631 opt.num_threads = threads;
632 return opt;
633 }()
634 );
635}
636
637template<typename Value_ = double, typename Index_ = int, typename StoredValue_ = Value_, typename StoredIndex_ = Index_, typename InputValue_, typename InputIndex_>
638std::shared_ptr<Matrix<Value_, Index_> > convert_to_compressed_sparse(const Matrix<InputValue_, InputIndex_>* matrix, bool row, bool two_pass = false, int threads = 1) {
640 *matrix,
641 row,
642 [&]{
643 ConvertToCompressedSparseOptions opt;
644 opt.two_pass = two_pass;
645 opt.num_threads = threads;
646 return opt;
647 }()
648 );
649}
650
651template <bool row_, typename Value_, typename Index_, typename InputValue_, typename InputIndex_>
652CompressedSparseContents<Value_, Index_, std::size_t> retrieve_compressed_sparse_contents(const Matrix<InputValue_, InputIndex_>* matrix, bool two_pass, int threads = 1) {
653 return retrieve_compressed_sparse_contents<Value_, Index_>(matrix, row_, two_pass, threads);
654}
655
656template <bool row_, typename Value_, typename Index_, typename StoredValue_ = Value_, typename StoredIndex_ = Index_, typename InputValue_, typename InputIndex_>
657std::shared_ptr<Matrix<Value_, Index_> > convert_to_compressed_sparse(const Matrix<InputValue_, InputIndex_>* matrix, bool two_pass = false, int threads = 1) {
659}
664}
665
666#endif
Compressed sparse matrix representation.
Convert index type to container size.
Compressed sparse matrix representation.
Definition CompressedSparseMatrix.hpp:580
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:282
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:413
int parallelize(Function_ fun, const Index_ tasks, const int workers)
Definition parallelize.hpp:58
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:331
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:545
Parallelized iteration over a tatami::Matrix.
Compressed sparse contents.
Definition convert_to_compressed_sparse.hpp:362
std::vector< Index_ > index
Definition convert_to_compressed_sparse.hpp:371
std::vector< Value_ > value
Definition convert_to_compressed_sparse.hpp:366
std::vector< Pointer_ > pointers
Definition convert_to_compressed_sparse.hpp:376
Options for convert_to_compressed_sparse().
Definition convert_to_compressed_sparse.hpp:506
bool two_pass
Definition convert_to_compressed_sparse.hpp:511
int num_threads
Definition convert_to_compressed_sparse.hpp:516
Options for count_compressed_sparse_non_zeros().
Definition convert_to_compressed_sparse.hpp:259
int num_threads
Definition convert_to_compressed_sparse.hpp:263
Options for fill_compressed_sparse_contents().
Definition convert_to_compressed_sparse.hpp:304
int num_threads
Definition convert_to_compressed_sparse.hpp:308
Options for retrieve_compressed_sparse_contents().
Definition convert_to_compressed_sparse.hpp:382
int num_threads
Definition convert_to_compressed_sparse.hpp:392
bool two_pass
Definition convert_to_compressed_sparse.hpp:387
Options for retrieve_fragmented_sparse_contents().
Definition convert_to_fragmented_sparse.hpp:62
int num_threads
Definition convert_to_fragmented_sparse.hpp:66