tatami
C++ API for different matrix representations
Loading...
Searching...
No Matches
convert_to_dense.hpp
Go to the documentation of this file.
1#ifndef TATAMI_CONVERT_TO_DENSE_H
2#define TATAMI_CONVERT_TO_DENSE_H
3
4#include "./DenseMatrix.hpp"
5#include "./transpose.hpp"
6
9#include "../utils/copy.hpp"
11
12#include <memory>
13#include <vector>
14#include <cstddef>
15
16#include "sanisizer/sanisizer.hpp"
17
24namespace tatami {
25
35
39template <typename StoredValue_, typename InputValue_, typename InputIndex_>
40void convert_to_dense_direct(const Matrix<InputValue_, InputIndex_>& matrix, const bool row, StoredValue_* const store, const ConvertToDenseOptions& options) {
41 const InputIndex_ NR = matrix.nrow();
42 const InputIndex_ NC = matrix.ncol();
43 const auto primary = (row ? NR : NC);
44 const auto secondary = (row ? NC : NR);
45
46 constexpr bool same_type = std::is_same<InputValue_, StoredValue_>::value;
48
49 parallelize([&](const int, const InputIndex_ start, const InputIndex_ length) -> void {
50 auto wrk = consecutive_extractor<false, InputValue_, InputIndex_>(matrix, row, start, length);
51 auto temp = [&]{
52 if constexpr(same_type) {
53 return false;
54 } else {
55 return std::vector<InputValue_>(secondary);
56 }
57 }();
58
59 for (InputIndex_ x = 0; x < length; ++x) {
60 const auto store_copy = store + sanisizer::product_unsafe<std::size_t>(secondary, start + x);
61 if constexpr(same_type) {
62 auto ptr = wrk->fetch(store_copy);
63 copy_n(ptr, secondary, store_copy);
64 } else {
65 auto ptr = wrk->fetch(temp.data());
66 std::copy_n(ptr, secondary, store_copy);
67 }
68 }
69 }, primary, options.num_threads);
70}
71
72template <typename StoredValue_, typename InputValue_, typename InputIndex_>
73void convert_to_dense_running(const Matrix<InputValue_, InputIndex_>& matrix, const bool row, StoredValue_* const store, const ConvertToDenseOptions& options) {
74 const InputIndex_ NR = matrix.nrow();
75 const InputIndex_ NC = matrix.ncol();
76 const auto primary = (row ? NR : NC);
77 const auto secondary = (row ? NC : NR);
78
79 // We're going to completely ignore the potential for false sharing here.
80 // False sharing would only be a risk for very fat/thin matrices (depending on row= and num_threads=),
81 // and if the input matrix is sparse, this further lowers the chance of contention between threads.
82 // The alternative would be to allocate a per-thread buffer to store all of the values,
83 // but then we need to do lots of interleaved copies and at that point the cure is worse than the disease.
84
85 if (matrix.is_sparse()) {
86 // We assume that 'store' was allocated correctly, in which case the product of 'primary' and 'secondary' is known to fit inside a std::size_t.
87 // This saves us from various checks when computing related products.
88 std::fill_n(store, sanisizer::product_unsafe<std::size_t>(primary, secondary), 0);
89
90 parallelize([&](const int, const InputIndex_ start, const InputIndex_ length) -> void {
91 auto wrk = consecutive_extractor<true, InputValue_, InputIndex_>(matrix, !row, start, length);
94 for (InputIndex_ x = 0; x < length; ++x) {
95 const auto range = wrk->fetch(vtemp.data(), itemp.data());
96 for (InputIndex_ i = 0; i < range.number; ++i) {
97 store[sanisizer::nd_offset<std::size_t>(start + x, secondary, range.index[i])] = range.value[i];
98 }
99 }
100 }, secondary, options.num_threads);
101
102 } else {
103 parallelize([&](const int, const InputIndex_ start, const InputIndex_ length) -> void {
104 auto wrk = consecutive_extractor<false, InputValue_, InputIndex_>(matrix, !row, start, length);
105
106 // Performing a blocked transposition to be more cache-friendly.
107 // This involves collecting several consecutive primary dimension elements so that we can transpose by blocks along the secondary dimension.
108 constexpr InputIndex_ block_size = 16;
109 const InputIndex_ alloc = std::min(length, block_size);
110 std::vector<InputValue_> bigbuffer(sanisizer::product_unsafe<typename std::vector<InputValue_>::size_type>(primary, alloc));
111 std::vector<const InputValue_*> ptrs(alloc); // no need for protection here, we know that alloc <= 16.
112
113 InputIndex_ sec_i = 0;
114 while (sec_i < length) {
115 const InputIndex_ sec_to_process = std::min(static_cast<InputIndex_>(length - sec_i), block_size);
116 for (InputIndex_ x = 0; x < sec_to_process; ++x) {
117 ptrs[x] = wrk->fetch(bigbuffer.data() + sanisizer::product_unsafe<std::size_t>(primary, x));
118 }
119
120 InputIndex_ prim_i = 0;
121 while (prim_i < primary) {
122 const InputIndex_ prim_end = prim_i + std::min(static_cast<InputIndex_>(primary - prim_i), block_size);
123 for (InputIndex_ x = 0; x < sec_to_process; ++x) {
124 const auto input = ptrs[x];
125 for (InputIndex_ p = prim_i; p < prim_end; ++p) {
126 store[sanisizer::nd_offset<std::size_t>(start + sec_i + x, secondary, p)] = input[p];
127 }
128 }
129 prim_i = prim_end;
130 }
131 sec_i += sec_to_process;
132 }
133 }, secondary, options.num_threads);
134 }
135}
152template <typename StoredValue_, typename InputValue_, typename InputIndex_>
153void convert_to_dense(const Matrix<InputValue_, InputIndex_>& matrix, const bool row_major, StoredValue_* const store, const ConvertToDenseOptions& options) {
154 if (row_major == matrix.prefer_rows()) {
155 convert_to_dense_direct(matrix, row_major, store, options);
156 } else {
157 convert_to_dense_running(matrix, row_major, store, options);
158 }
159}
160
175template <
176 typename Value_,
177 typename Index_,
178 typename StoredValue_ = Value_,
179 typename InputValue_,
180 typename InputIndex_
181>
182std::shared_ptr<Matrix<Value_, Index_> > convert_to_dense(const Matrix<InputValue_, InputIndex_>& matrix, const bool row_major, const ConvertToDenseOptions& options) {
183 const auto NR = matrix.nrow();
184 const auto NC = matrix.ncol();
185 const auto buffer_size = sanisizer::product<typename std::vector<StoredValue_>::size_type>(attest_for_Index(NR), attest_for_Index(NC));
186 std::vector<StoredValue_> buffer(buffer_size);
187 convert_to_dense(matrix, row_major, buffer.data(), options);
188
189 return std::shared_ptr<Matrix<Value_, Index_> >(
190 new DenseMatrix<Value_, Index_, I<decltype(buffer)> >(
191 sanisizer::cast<Index_>(attest_for_Index(NR)),
192 sanisizer::cast<Index_>(attest_for_Index(NC)),
193 std::move(buffer),
194 row_major
195 )
196 );
197}
198
202// Backwards compatbility.
203template <typename StoredValue_, typename InputValue_, typename InputIndex_>
204void convert_to_dense(const Matrix<InputValue_, InputIndex_>* matrix, bool row_major, StoredValue_* store, int threads = 1) {
206 *matrix,
207 row_major,
208 store,
209 [&]{
210 ConvertToDenseOptions options;
211 options.num_threads = threads;
212 return options;
213 }()
214 );
215}
216
217template <typename Value_ = double, typename Index_ = int, typename StoredValue_ = Value_, typename InputValue_, typename InputIndex_>
218inline std::shared_ptr<Matrix<Value_, Index_> > convert_to_dense(const Matrix<InputValue_, InputIndex_>* matrix, bool row_major, int threads = 1) {
219 ConvertToDenseOptions options;
220 options.num_threads = threads;
222 *matrix,
223 row_major,
224 [&]{
225 ConvertToDenseOptions options;
226 options.num_threads = threads;
227 return options;
228 }()
229 );
230}
231
232template<bool row_, typename StoredValue_, typename InputValue_, typename InputIndex_>
233void convert_to_dense(const Matrix<InputValue_, InputIndex_>* matrix, StoredValue_* store, int threads = 1) {
234 convert_to_dense(matrix, row_, store, threads);
235}
236
237template<bool row_, typename Value_, typename Index_, typename StoredValue_ = Value_, typename InputValue_, typename InputIndex_>
238inline std::shared_ptr<Matrix<Value_, Index_> > convert_to_dense(const Matrix<InputValue_, InputIndex_>* matrix, int threads = 1) {
239 return convert_to_dense<Value_, Index_, StoredValue_>(matrix, row_, threads);
240}
245}
246
247#endif
Dense matrix representation.
Convert index type to container size.
Dense matrix representation.
Definition DenseMatrix.hpp:180
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
Templated construction of a new consecutive extractor.
Copy data from one buffer to another.
Flexible representations for matrix data.
Definition Extractor.hpp:15
Index_ can_cast_Index_to_container_size(const Index_ x)
Definition Index_to_container.hpp:49
int parallelize(Function_ fun, const Index_ tasks, const int workers)
Definition parallelize.hpp:58
void convert_to_dense(const Matrix< InputValue_, InputIndex_ > &matrix, const bool row_major, StoredValue_ *const store, const ConvertToDenseOptions &options)
Definition convert_to_dense.hpp:153
Value_ * copy_n(const Value_ *const input, const Size_ n, Value_ *const output)
Definition copy.hpp:37
Container_ create_container_of_Index_size(const Index_ x, Args_ &&... args)
Definition Index_to_container.hpp:82
auto consecutive_extractor(const Matrix< Value_, Index_ > &matrix, const bool row, const Index_ iter_start, const Index_ iter_length, Args_ &&... args)
Definition consecutive_extractor.hpp:35
Parallelized iteration over a tatami::Matrix.
Options for convert_to_dense().
Definition convert_to_dense.hpp:29
int num_threads
Definition convert_to_dense.hpp:33
Transpose a dense array.