tatami_mult
Multiply tatami matrices
Loading...
Searching...
No Matches
column_to_row.hpp
Go to the documentation of this file.
1#ifndef TATAMI_MULT_SPARSE_MATRIX_DENSE_COLUMN_COLUMN_TO_ROW_HPP
2#define TATAMI_MULT_SPARSE_MATRIX_DENSE_COLUMN_COLUMN_TO_ROW_HPP
3
4#include <cstddef>
5#include <vector>
6#include <optional>
7
8#include "tatami/tatami.hpp"
9#include "sanisizer/sanisizer.hpp"
10
11#include "../utils.hpp"
12#include "../../utils.hpp"
13
19namespace tatami_mult {
20
21/* See https://github.com/tatami-inc/test-multiplication/tree/master/dense_column/sparse_matrix
22 * for an explanation of the choice of algorithm.
23 */
24
41
61template<typename LeftValue_, typename LeftIndex_, typename RightValue_, typename RightIndex_, typename Output_>
65 Output_* const output,
67) {
68 const auto left_NR = left.nrow();
69 const auto common_dim = left.ncol();
70 const auto right_NC = right.ncol();
71
73 conv_opt.two_pass = false;
74 conv_opt.num_threads = options.num_threads;
76
77 // If there are any empty RHS rows, we only iterate over the non-empty ones in the outer loop.
78 auto right_non_empty = filter_non_empty_sparse(
79 rhs_data.index,
80 [&](const RightIndex_) -> void {}
81 );
82
83 const bool do_parallel = options.num_threads > 1;
84 std::optional<std::vector<std::optional<std::vector<Output_> > > > tmp_results;
85 if (do_parallel) {
86 tmp_results.emplace(sanisizer::cast<I<decltype(tmp_results->size())> >(options.num_threads - 1));
87 }
88
89 std::fill_n(output, sanisizer::product_unsafe<std::size_t>(left_NR, right_NC), 0);
90
91 // If we have empty RHS rows, we completely skip the corresponding LHS columns.
92 // Otherwise doing the easier approach of just looping with a counter.
93 const LeftIndex_ cd_total = (right_non_empty.has_value() ? static_cast<LeftIndex_>(right_non_empty->size()) : common_dim);
94 const int num_used = tatami::parallelize([&](int t, LeftIndex_ start, LeftIndex_ length) -> void {
95 std::optional<std::vector<Output_> > tmp_output;
96 Output_* outptr;
97 if (!do_parallel || t == 0) {
98 outptr = output;
99 } else {
100 tmp_output.emplace(sanisizer::product<I<decltype(tmp_output->size())> >(left_NR, right_NC));
101 outptr = tmp_output->data();
102 }
103
104 auto task = [&](std::unique_ptr<tatami::OracularDenseExtractor<LeftValue_, LeftIndex_> >& ext, auto converter) -> void {
105 if (options.block_size == 1) {
107 for (LeftIndex_ cd = 0; cd < length; ++cd) {
108 const auto lptr = ext->fetch(dbuffer.data());
109 const auto actual_cd = converter(cd);
110 const auto& right_values = rhs_data.value[actual_cd];
111 const auto& right_indices = rhs_data.index[actual_cd];
112 const RightIndex_ right_nnz = right_values.size();
113 for (LeftIndex_ lr = 0; lr < left_NR; ++lr) {
114 const Output_ mult = lptr[lr];
115 for (RightIndex_ x = 0; x < right_nnz; ++x) {
116 outptr[sanisizer::nd_offset<std::size_t>(right_indices[x], right_NC, lr)] += mult * static_cast<Output_>(right_values[x]);
117 }
118 }
119 }
120
121 } else {
122 std::vector<std::vector<LeftValue_> > left_buffers;
123 std::vector<const LeftValue_*> left_ptrs;
124 {
125 const LeftIndex_ max_block_cols = sanisizer::min(length, options.block_size);
126 left_buffers.reserve(max_block_cols);
127 for (LeftIndex_ cd = 0; cd < max_block_cols; ++cd) {
128 left_buffers.emplace_back(tatami::cast_Index_to_container_size<std::vector<LeftValue_> >(left_NR));
129 }
130 sanisizer::resize(left_ptrs, max_block_cols);
131 }
132
133 LeftIndex_ cd = 0;
134 while (cd < length) {
135 const auto cd_num = sanisizer::min(options.block_size, length - cd);
136 for (LeftIndex_ cd_counter = 0; cd_counter < cd_num; ++cd_counter) {
137 left_ptrs[cd_counter] = ext->fetch(left_buffers[cd_counter].data());
138 }
139
140 for (LeftIndex_ lr = 0; lr < left_NR; ++lr) {
141 for (LeftIndex_ cd_counter = 0; cd_counter < cd_num; ++cd_counter) {
142 const auto mult = left_ptrs[cd_counter][lr];
143 const auto actual_cd = converter(cd + cd_counter);
144 const auto& right_values = rhs_data.value[actual_cd];
145 const auto& right_indices = rhs_data.index[actual_cd];
146 const RightIndex_ right_nnz = right_values.size();
147 for (RightIndex_ x = 0; x < right_nnz; ++x) {
148 outptr[sanisizer::nd_offset<std::size_t>(right_indices[x], right_NC, lr)] += mult * static_cast<Output_>(right_values[x]);
149 }
150 }
151 }
152
153 cd += cd_num;
154 }
155 }
156 };
157
158 if (right_non_empty.has_value()) {
159 auto ext = tatami::new_extractor<false, true>(left, false, std::make_shared<tatami::FixedViewOracle<LeftIndex_> >(right_non_empty->data() + start, length));
160 task(
161 ext,
162 [&](const LeftIndex_ cd) -> LeftIndex_ {
163 return (*right_non_empty)[start + cd];
164 }
165 );
166 } else {
167 auto ext = tatami::consecutive_extractor<false>(left, false, start, length);
168 task(
169 ext,
170 [&](const LeftIndex_ cd) -> LeftIndex_ {
171 return cd + start;
172 }
173 );
174 }
175
176 if (do_parallel && t > 0) {
177 (*tmp_results)[t - 1] = std::move(tmp_output);
178 }
179 }, cd_total, options.num_threads);
180
181 if (do_parallel) {
182 for (int u = 1; u < num_used; ++u) {
183 const auto& tmp = *((*tmp_results)[u - 1]);
184 const auto N = tmp.size();
185 for (I<decltype(N)> x = 0; x < N; ++x) {
186 output[x] += tmp[x];
187 }
188 }
189 }
190}
191
192}
193
194#endif
virtual Index_ ncol() const=0
virtual Index_ nrow() const=0
Multiplication of tatami matrices.
Definition column_to_column.hpp:19
void multiply_dense_column_with_sparse_column_matrix_to_row_output(const tatami::Matrix< LeftValue_, LeftIndex_ > &left, const tatami::Matrix< RightValue_, RightIndex_ > &right, Output_ *const output, const MultiplyDenseColumnWithSparseColumnMatrixToRowOutputOptions &options)
Definition column_to_row.hpp:62
auto new_extractor(const Matrix< Value_, Index_ > &matrix, const bool row, MaybeOracle< oracle_, Index_ > oracle, Args_ &&... args)
FragmentedSparseContents< StoredValue_, StoredIndex_ > retrieve_fragmented_sparse_contents(const Matrix< InputValue_, InputIndex_ > &matrix, const bool row, const RetrieveFragmentedSparseContentsOptions &options)
int parallelize(Function_ fun, const Index_ tasks, const int workers)
I< decltype(std::declval< Container_ >().size())> cast_Index_to_container_size(const Index_ x)
Container_ create_container_of_Index_size(const Index_ x, Args_ &&... args)
auto consecutive_extractor(const Matrix< Value_, Index_ > &matrix, const bool row, const Index_ iter_start, const Index_ iter_length, Args_ &&... args)
Options for multiply_dense_column_with_sparse_column_matrix_to_row_output().
Definition column_to_row.hpp:28