tatami_mult
Multiply tatami matrices
Loading...
Searching...
No Matches
sparse_row.hpp
Go to the documentation of this file.
1#ifndef TATAMI_MULT_MULTIPLE_VECTORS_SPARSE_ROW_HPP
2#define TATAMI_MULT_MULTIPLE_VECTORS_SPARSE_ROW_HPP
3
4#include <cstddef>
5#include <vector>
6
7#include "tatami/tatami.hpp"
8
9#include "../utils.hpp"
10#include "../sparse_dot_product.hpp"
11
17namespace tatami_mult {
18
19/* See https://github.com/tatami-inc/test-multiplication/tree/master/sparse_row/multiple_vectors
20 * for an explanation of the choice of algorithm.
21 */
22
39
60template<std::size_t accumulators_ = 4, typename LeftValue_, typename LeftIndex_, typename RightVectors_, typename GetRightVector_, typename GetOutputVector_>
63 const RightVectors_ right_vectors,
64 GetRightVector_ get_right_vector,
65 GetOutputVector_ get_output_vector,
67) {
68 const auto left_NR = left.nrow();
69 const auto common_dim = left.ncol();
70 const auto right_NC = right_vectors; // using an alias just for consistent terminology.
71 typedef I<decltype(get_output_vector(0)[0])> Output;;
72
73 if (options.block_size == 1) {
74 tatami::parallelize([&](int, LeftIndex_ start, LeftIndex_ length) -> void {
75 auto ext = tatami::consecutive_extractor<true>(left, true, start, length);
78
79 for (LeftIndex_ lr = 0; lr < length; ++lr) {
80 const auto range = ext->fetch(vbuffer.data(), ibuffer.data());
81 if (range.number == 0) {
82 for (RightVectors_ rv = 0; rv < right_NC; ++rv) {
83 get_output_vector(rv)[start + lr] = 0;
84 }
85 continue;
86 }
87
88 for (RightVectors_ rv = 0; rv < right_NC; ++rv) {
89 get_output_vector(rv)[start + lr] = sparse_dot_product<accumulators_>(
90 range.number, // Implicit cast to size_t is safe, as per the tatami contract.
91 range.value,
92 range.index,
93 get_right_vector(rv),
94 static_cast<Output>(0)
95 );
96 }
97 }
98 }, left_NR, options.num_threads);
99
100 } else {
101 tatami::parallelize([&](int, LeftIndex_ start, LeftIndex_ length) -> void {
102 auto ext = tatami::consecutive_extractor<true>(left, true, start, length);
103
104 std::vector<std::vector<LeftValue_> > left_vbuffers;
105 std::vector<std::vector<LeftIndex_> > left_ibuffers;
106 std::vector<tatami::SparseRange<LeftValue_, LeftIndex_> > left_ranges;
107 {
108 const LeftIndex_ max_block_rows = sanisizer::min(length, options.block_size);
109 left_vbuffers.reserve(max_block_rows);
110 left_ibuffers.reserve(max_block_rows);
111 for (LeftIndex_ lr = 0; lr < max_block_rows; ++lr) {
112 left_vbuffers.emplace_back(tatami::cast_Index_to_container_size<std::vector<LeftValue_> >(common_dim));
113 left_ibuffers.emplace_back(tatami::cast_Index_to_container_size<std::vector<LeftIndex_> >(common_dim));
114 }
115 sanisizer::resize(left_ranges, max_block_rows);
116 }
117
118 LeftIndex_ lr = 0;
119 while (lr < length) {
120 // No point skipping the LHS rows with no structural non-zeros.
121 // We still need to set the corresponding entry of 'outvec' to zero, so we'd end up having to loop through the LHS rows anyway.
122 // We might as well just let it be set to zero naturally in the existing loop below.
123 const LeftIndex_ lr_num = sanisizer::min(options.block_size, length - lr);
124 for (LeftIndex_ lr_counter = 0; lr_counter < lr_num; ++lr_counter) {
125 left_ranges[lr_counter] = ext->fetch(left_vbuffers[lr_counter].data(), left_ibuffers[lr_counter].data());
126 }
127
128 for (RightVectors_ rv = 0; rv < right_NC; ++rv) {
129 const auto rightvec = get_right_vector(rv);
130 const auto outvec = get_output_vector(rv);
131 for (LeftIndex_ lr_counter = 0; lr_counter < lr_num; ++lr_counter) {
132 const auto& currange = left_ranges[lr_counter];
133 outvec[start + lr + lr_counter] = sparse_dot_product<accumulators_>(
134 currange.number, // Implicit cast of range.number to size_t is safe, as per the tatami contract.
135 currange.value,
136 currange.index,
137 rightvec,
138 static_cast<Output>(0)
139 );
140 }
141 }
142
143 lr += lr_num;
144 }
145 }, left_NR, options.num_threads);
146 }
147}
148
168template<std::size_t accumulators_ = 4, typename LeftValue_, typename LeftIndex_, typename RightValue_, typename Output_>
171 const std::vector<RightValue_*>& right,
172 const std::vector<Output_*>& output,
174) {
175 const auto right_vectors = right.size();
176 typedef I<decltype(right_vectors)> RightVectors;
178 left,
179 right_vectors,
180 [&](const RightVectors rc) -> const RightValue_* {
181 return right[rc];
182 },
183 [&](const RightVectors rc) -> Output_* {
184 return output[rc];
185 },
186 options
187 );
188}
189
190}
191
192#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_multiple_vectors(const tatami::Matrix< LeftValue_, LeftIndex_ > &left, const RightVectors_ right_vectors, GetRightVector_ get_right_vector, GetOutputVector_ get_output_vector, const MultiplySparseRowWithMultipleVectorsOptions &options)
Definition sparse_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_multiple_vectors().
Definition sparse_row.hpp:26