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_ROW_COLUMN_TO_ROW_HPP
2#define TATAMI_MULT_DENSE_MATRIX_SPARSE_ROW_COLUMN_TO_ROW_HPP
3
4#include <cstddef>
5#include <vector>
6
7#include "tatami/tatami.hpp"
8
9#include "../utils.hpp"
10#include "../../utils.hpp"
11#include "../../sparse_dot_product.hpp"
12
18namespace tatami_mult {
19
20/* See https://github.com/tatami-inc/test-multiplication/tree/master/sparse_row/dense_matrix
21 * for an explanation of the choice of algorithm.
22 */
23
40
60template<std::size_t accumulators_ = 4, typename LeftValue_, typename LeftIndex_, typename RightColumns_, class GetRightColumn_, typename Output_>
63 const RightColumns_ right_columns,
64 GetRightColumn_ get_right_column,
65 Output_* const output,
67) {
68 const auto left_NR = left.nrow();
69 const auto common_dim = left.ncol();
70
71 if (options.block_size == 1) {
72 tatami::parallelize([&](int, LeftIndex_ start, LeftIndex_ length) -> void {
73 auto ext = tatami::consecutive_extractor<true>(left, true, start, length);
76
77 for (LeftIndex_ lr = 0; lr < length; ++lr) {
78 const auto range = ext->fetch(vbuffer.data(), ibuffer.data());
79 for (RightColumns_ rc = 0; rc < right_columns; ++rc) {
80 output[sanisizer::nd_offset<std::size_t>(rc, right_columns, start + lr)] = sparse_dot_product<accumulators_>(
81 range.number, // implicit cast of range.number to size_t is safe, as per the tatami contract.
82 range.value,
83 range.index,
84 get_right_column(rc),
85 static_cast<Output_>(0)
86 );
87 }
88 }
89 }, left_NR, options.num_threads);
90 return;
91 }
92
93 tatami::parallelize([&](int, LeftIndex_ start, LeftIndex_ length) -> void {
94 auto ext = tatami::consecutive_extractor<true>(left, true, start, length);
95
96 std::vector<std::vector<LeftValue_> > left_vbuffers;
97 std::vector<std::vector<LeftIndex_> > left_ibuffers;
98 std::vector<tatami::SparseRange<LeftValue_, LeftIndex_> > left_ranges;
99 std::vector<LeftIndex_> left_non_empty;
100 {
101 const LeftIndex_ max_block_rows = sanisizer::min(length, options.block_size);
102 left_vbuffers.reserve(max_block_rows);
103 left_ibuffers.reserve(max_block_rows);
104 for (LeftIndex_ lr = 0; lr < max_block_rows; ++lr) {
105 left_vbuffers.emplace_back(tatami::cast_Index_to_container_size<std::vector<LeftValue_> >(common_dim));
106 left_ibuffers.emplace_back(tatami::cast_Index_to_container_size<std::vector<LeftIndex_> >(common_dim));
107 }
108 sanisizer::resize(left_ranges, max_block_rows);
109 left_non_empty.reserve(max_block_rows);
110 }
111
112 LeftIndex_ lr = 0;
113 while (lr < length) {
114 // We only consider the LHS rows with at least one structural non-zero.
115 // Thus, our block consists of 'options.block_size' non-empty LHS rows, rather than fixed row-wise chunks of the LHS matrix.
116 // This ensures that we don't waste iterations on LHS rows that will only have zeros in the output matrix (and are filled as such).
117 const auto left_block_info = fetch_non_empty_sparse_block(
118 *ext,
119 left_vbuffers,
120 left_ibuffers,
121 left_ranges,
122 left_non_empty,
123 lr,
124 length,
125 options.block_size,
126 /* zero = */ [&](const LeftIndex_ lr_copy) -> void {
127 std::fill_n(output + sanisizer::product_unsafe<std::size_t>(start + lr_copy, right_columns), right_columns, 0);
128 }
129 );
130 const auto lr_num = left_block_info.num_non_empty;
131
132 // If the LHS columns are all non-empty, we can speed up the loops by just using a simple counter to get the column indices.
133 // Otherwise, we'll have to access the 'left_non_empty' vector to figure out the indices of each non-empty column.
134 if (left_block_info.all_non_empty) {
135 const auto lr_base = lr + start;
136
137 // Yes, we deliberately iterate over the RHS columns in the outer loop to keep the dense column in cache across multiple LHS vectors.
138 // If we did it the other way around, this would defeat the purpose of blocking.
139 for (RightColumns_ rc = 0; rc < right_columns; ++rc) {
140 const auto rcol = get_right_column(rc);
141 for (LeftIndex_ lr_counter = 0; lr_counter < lr_num; ++lr_counter) {
142 const auto& currange = left_ranges[lr_counter];
143 output[sanisizer::nd_offset<std::size_t>(rc, right_columns, lr_base + lr_counter)] = sparse_dot_product<accumulators_>(
144 currange.number, // Implicit cast of range.number to size_t is safe, as per the tatami contract.
145 currange.value,
146 currange.index,
147 rcol,
148 static_cast<Output_>(0)
149 );
150 }
151 }
152
153 } else {
154 for (auto& lrne : left_non_empty) {
155 lrne += start;
156 }
157 for (RightColumns_ rc = 0; rc < right_columns; ++rc) { // again, iterating over the RHS columns in the outer loop, see above.
158 const auto rcol = get_right_column(rc);
159 for (LeftIndex_ lr_counter = 0; lr_counter < lr_num; ++lr_counter) {
160 const auto& currange = left_ranges[lr_counter];
161 output[sanisizer::nd_offset<std::size_t>(rc, right_columns, left_non_empty[lr_counter])] = sparse_dot_product<accumulators_>(
162 currange.number, // see above.
163 currange.value,
164 currange.index,
165 rcol,
166 static_cast<Output_>(0)
167 );
168 }
169 }
170 }
171
172 lr = left_block_info.position;
173 }
174 }, left_NR, options.num_threads);
175}
176
199template<std::size_t accumulators_ = 4, typename LeftValue_, typename LeftIndex_, typename RightValue_, typename RightIndex_, typename Output_>
203 Output_* const output,
205) {
206 const auto right_NC = right.ncol();
209 const auto common_dim = left.ncol();
210 populate_dense_buffers(false, right_NC, common_dim, right, right_buffers, right_ptrs, options.num_threads);
211
213 left,
214 right_NC,
215 [&](const RightIndex_ rc) -> const RightValue_* {
216 return right_ptrs[rc];
217 },
218 output,
219 options
220 );
221}
222
223
224}
225
226#endif
virtual Index_ ncol() const=0
virtual Index_ nrow() const=0
Multiplication of tatami matrices.
Definition column_to_column.hpp:19
void multiply_sparse_row_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 MultiplySparseRowWithDenseColumnMatrixToRowOutputOptions &options)
Definition column_to_row.hpp:61
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_sparse_row_with_dense_column_matrix_to_row_output().
Definition column_to_row.hpp:27