tatami_mult
Multiply tatami matrices
Loading...
Searching...
No Matches
column_to_row.hpp
Go to the documentation of this file.
1#ifndef TATAMI_MULT_DENSE_MATRIX_SPARSE_COLUMN_COLUMN_TO_ROW_HPP
2#define TATAMI_MULT_DENSE_MATRIX_SPARSE_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/sparse_column/dense_matrix
22 * for an explanation of the choice of algorithm.
23 */
24
35
53template<typename LeftValue_, typename LeftIndex_, typename RightColumns_, class GetRightColumn_, typename Output_>
56 const RightColumns_ right_columns,
57 GetRightColumn_ get_right_column,
58 Output_* const output,
60) {
61 const auto left_NR = left.nrow();
62 const auto common_dim = left.ncol();
63
64 // Product must fit in a size_t in order for output to have been allocated correctly in the first place.
65 // Technically, right_columns could be larger than a size_t if left_NR == 0, but the product after wraparound would still be zero, so it's fine.
66 std::fill_n(output, sanisizer::product<std::size_t>(left_NR, right_columns), 0);
67
68 const bool do_parallel = options.num_threads > 1;
69 std::optional<std::vector<std::optional<std::vector<Output_> > > > tmp_results;
70 if (do_parallel) {
71 tmp_results.emplace(sanisizer::cast<I<decltype(tmp_results->size())> >(options.num_threads - 1));
72 }
73
74 const auto num_used = tatami::parallelize([&](int t, LeftIndex_ start, LeftIndex_ length) -> void {
75 auto left_ext = tatami::consecutive_extractor<true>(left, false, start, length);
76
79
80 // Don't use tatami::create_container here, as RightColumns_ might not satisfy the tatami Index_ contract.
81 typedef I<decltype(get_right_column(0)[0])> RightValue;
82 auto rbuffer = sanisizer::create<std::vector<RightValue> >(right_columns);
83
84 std::optional<std::vector<Output_> > tmp_output;
85 Output_* outptr;
86 if (!do_parallel || t == 0) {
87 outptr = output;
88 } else {
89 tmp_output.emplace(sanisizer::product<I<decltype(tmp_output->size())> >(left_NR, right_columns));
90 outptr = tmp_output->data();
91 }
92
93 for (LeftIndex_ cd = 0; cd < length; ++cd) {
94 const auto lrange = left_ext->fetch(vbuffer.data(), ibuffer.data());
95 if (lrange.number == 0) {
96 continue;
97 }
98
99 // Transposing the conceptual RHS row to a dense buffer for more friendly inner loops.
100 // We could probably do this transposition once in the serial section, which would be more cache-friendly but require an extra allocation.
101 // But, it's not in the hot loop so we'll save ourselves a bit of memory and do it one at a time instead.
102 for (RightColumns_ rc = 0; rc < right_columns; ++rc) {
103 rbuffer[rc] = get_right_column(rc)[start + cd];
104 }
105
106 for (LeftIndex_ x = 0; x < lrange.number; ++x) {
107 const Output_ mult = lrange.value[x];
108 const auto curout = outptr + sanisizer::product_unsafe<std::size_t>(lrange.index[x], right_columns);
109 for (RightColumns_ rc = 0; rc < right_columns; ++rc) {
110 curout[rc] += mult * static_cast<Output_>(rbuffer[rc]);
111 }
112 }
113 }
114
115 if (do_parallel && t > 0) {
116 (*tmp_results)[t - 1] = std::move(tmp_output);
117 }
118 }, common_dim, options.num_threads);
119
120 if (do_parallel) {
121 for (int u = 1; u < num_used; ++u) {
122 const auto& tmp = *((*tmp_results)[u - 1]);
123 const auto N = tmp.size();
124 for (I<decltype(N)> x = 0; x < N; ++x) {
125 output[x] += tmp[x];
126 }
127 }
128 }
129}
130
151template<typename LeftValue_, typename LeftIndex_, typename RightValue_, typename RightIndex_, typename Output_>
155 Output_* const output,
157) {
158 const auto right_NC = right.ncol();
161 const auto common_dim = left.ncol();
162 populate_dense_buffers(false, right_NC, common_dim, right, right_buffers, right_ptrs, options.num_threads);
163
165 left,
166 right_NC,
167 [&](const RightIndex_ rc) -> const RightValue_* {
168 return right_ptrs[rc];
169 },
170 output,
171 options
172 );
173}
174
175}
176
177#endif
virtual Index_ ncol() const=0
virtual Index_ nrow() const=0
Multiplication of tatami matrices.
Definition column_to_column.hpp:19
void multiply_sparse_column_with_dense_column_matrix_to_row_output(const tatami::Matrix< LeftValue_, LeftIndex_ > &left, const RightColumns_ right_columns, GetRightColumn_ get_right_column, Output_ *const output, const MultiplySparseColumnWithDenseColumnMatrixToRowOutputOptions &options)
Definition column_to_row.hpp:54
int parallelize(Function_ fun, const Index_ tasks, const int workers)
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_sparse_column_with_dense_column_matrix_to_row_output().
Definition column_to_row.hpp:28