1#ifndef SCRAN_AGGREGATE_AGGREGATE_ACROSS_GENES_HPP
2#define SCRAN_AGGREGATE_AGGREGATE_ACROSS_GENES_HPP
6#include <unordered_set>
11#include "quickstats/quickstats.hpp"
12#include "sanisizer/sanisizer.hpp"
44template <
typename Sum_>
51 std::vector<Sum_*>
sum;
59template<
typename Gene_,
typename Weight_>
102template <
typename Sum_>
109 std::vector<std::vector<Sum_> >
sum;
115template<
typename Data_,
typename Index_,
typename Gene_,
typename Weight_,
typename Sum_>
116void aggregate_across_genes_by_column(
122 const auto NR = p.
nrow();
123 const auto num_sets = gene_sets.size();
126 std::vector<Index_> subset;
130 for (
const auto& set : gene_sets) {
131 for (std::size_t i = 0; i < set.number; ++i) {
132 const auto g = set.gene[i];
133 if (g < 0 || sanisizer::is_greater_than_or_equal(g, NR)) {
134 throw std::runtime_error(
"set indices are out of range");
143 subset.reserve(used);
144 for (Index_ r = 0; r < NR; ++r) {
154 const std::vector<AggregateAcrossGenesSet<Gene_, Weight_> >* gene_sets_ptr = &gene_sets;
155 std::optional<std::vector<AggregateAcrossGenesSet<Gene_, Weight_> > > remapped_gene_sets;
157 class RemappedGeneSetLiberator {
159 RemappedGeneSetLiberator(std::optional<std::vector<AggregateAcrossGenesSet<Gene_, Weight_> > >& host) : my_host(host) {}
160 ~RemappedGeneSetLiberator() {
162 if (my_host.has_value()) {
163 for (
auto& rset : *my_host) {
171 std::optional<std::vector<AggregateAcrossGenesSet<Gene_, Weight_> > >& my_host;
173 RemappedGeneSetLiberator lib(remapped_gene_sets);
175 const auto nsubs = subset.size();
177 const Index_ offset = subset.front();
178 const Index_ span = subset.back() - offset + 1;
180 if (offset || !sanisizer::is_equal(span, nsubs)) {
182 for (I<
decltype(nsubs)> i = 0; i < nsubs; ++i) {
183 mapping[subset[i] - offset] = i;
186 remapped_gene_sets.emplace(num_sets);
187 gene_sets_ptr = &(*remapped_gene_sets);
189 for (I<
decltype(num_sets)> s = 0; s < num_sets; ++s) {
190 const auto& set = gene_sets[s];
191 auto& remapped = (*remapped_gene_sets)[s];
192 remapped.number = set.number;
193 remapped.weight = set.weight;
195 const auto rgene =
new Gene_ [set.number];
196 remapped.gene = rgene;
197 for (std::size_t g = 0; g < set.number; ++g) {
198 rgene[g] = mapping[set.gene[g] - offset];
208 const Index_ offset = subset.front();
209 if (offset == 0 && sanisizer::is_equal(nsubs, NR)) {
212 const Index_ span = subset.back() - offset + 1;
213 if (sanisizer::is_equal(span, nsubs)) {
222 quickstats::PairwiseSumWorkspace<Sum_> pswrk;
223 quickstats::PairwiseSumOptions psopt;
225 for (Index_ x = start, end = start + length; x < end; ++x) {
226 const auto ptr = ext->fetch(vbuffer.data());
227 for (std::size_t s = 0; s < num_sets; ++s) {
228 const auto& set = (*gene_sets_ptr)[s];
231 buffers.
sum[s][x] = quickstats::pairwise_sum_abstract(
233 [&](std::size_t i) -> Sum_ {
234 return ptr[set.gene[i]] * set.weight[i];
240 buffers.
sum[s][x] = quickstats::pairwise_sum_abstract(
242 [&](std::size_t i) -> Sum_ {
243 return ptr[set.gene[i]];
255template<
typename Data_,
typename Index_,
typename Gene_,
typename Weight_,
typename Sum_>
256void aggregate_across_genes_by_row(
258 const std::vector<AggregateAcrossGenesSet<Gene_, Weight_> >& gene_sets,
259 const AggregateAcrossGenesBuffers<Sum_>& buffers,
260 const AggregateAcrossGenesOptions& options
262 const auto NR = p.
nrow();
263 const auto NC = p.
ncol();
264 const auto num_sets = gene_sets.size();
265 typedef I<
decltype(num_sets)> SetIndex;
268 std::vector<Index_> subset;
269 std::vector<std::pair<std::vector<SetIndex>, std::vector<Weight_> > > revmapping;
273 for (
const auto& set : gene_sets) {
274 for (std::size_t i = 0; i < set.number; ++i) {
275 const auto g = set.gene[i];
276 if (g < 0 || sanisizer::is_greater_than_or_equal(g, NR)) {
277 throw std::runtime_error(
"set indices are out of range");
279 used += (occupancy[g] == 0);
284 subset.reserve(used);
286 for (Index_ r = 0; r < NR; ++r) {
288 auto& revmap_dest = revmapping[subset.size()];
289 revmap_dest.first.reserve(occupancy[r]);
290 revmap_dest.second.reserve(occupancy[r]);
297 const Index_ nsubs = subset.size();
299 const Index_ offset = subset.front();
300 const Index_ span = subset.back() - offset + 1;
302 if (!sanisizer::is_equal(span, nsubs)) {
304 for (I<
decltype(nsubs)> i = 0; i < nsubs; ++i) {
305 mapping[subset[i] - offset] = i;
308 for (I<
decltype(num_sets)> s = 0; s < num_sets; ++s) {
309 const auto& set = gene_sets[s];
311 for (std::size_t g = 0; g < set.number; ++g) {
312 auto& dest = revmapping[mapping[set.gene[g] - offset]];
313 dest.first.push_back(s);
314 dest.second.push_back(set.weight[g]);
317 for (std::size_t g = 0; g < set.number; ++g) {
318 auto& dest = revmapping[mapping[set.gene[g] - offset]];
319 dest.first.push_back(s);
320 dest.second.push_back(1);
326 for (I<
decltype(num_sets)> s = 0; s < num_sets; ++s) {
327 const auto& set = gene_sets[s];
329 for (std::size_t g = 0; g < set.number; ++g) {
330 auto& dest = revmapping[set.gene[g] - offset];
331 dest.first.push_back(s);
332 dest.second.push_back(set.weight[g]);
335 for (std::size_t g = 0; g < set.number; ++g) {
336 auto& dest = revmapping[set.gene[g] - offset];
337 dest.first.push_back(s);
338 dest.second.push_back(1);
345 for (I<
decltype(num_sets)> s = 0; s < num_sets; ++s) {
346 std::fill_n(buffers.sum[s], NC, 0);
349 const bool do_parallel = options.num_threads > 1;
350 std::optional<std::vector<std::optional<std::vector<std::vector<Sum_> > > > > per_thread_sums;
352 per_thread_sums.emplace(sanisizer::cast<I<
decltype(per_thread_sums->size())> >(options.num_threads - 1));
356 const auto nused =
tatami::parallelize([&](
const int t,
const Index_ start,
const Index_ length) ->
void {
357 auto sub_oracle = std::make_shared<tatami::FixedViewOracle<Index_> >(subset.data() + start, length);
358 std::optional<std::vector<std::vector<Sum_> > > tmp_sums;
360 tmp_sums.emplace(sanisizer::cast<I<
decltype(tmp_sums->size())> >(num_sets));
363 auto get_output_ptr = [&](SetIndex curset) -> Sum_* {
365 return buffers.sum[curset];
371 if ((*tmp_sums)[curset].empty()) {
374 return (*tmp_sums)[curset].data();
382 for (Index_ g = 0; g < length; ++g) {
383 const auto range = ext->fetch(vbuffer.data(), ibuffer.data());
384 const auto& cursets = revmapping[start + g];
385 const auto ncursets = cursets.first.size();
387 for (I<
decltype(ncursets)> s = 0; s < ncursets; ++s) {
388 const auto curset = cursets.first[s];
389 const auto curw = cursets.second[s];
390 const auto outptr = get_output_ptr(curset);
392 for (Index_ i = 0; i < range.number; ++i) {
393 outptr[range.index[i]] += range.value[i] * curw;
396 for (Index_ i = 0; i < range.number; ++i) {
397 outptr[range.index[i]] += range.value[i];
407 for (Index_ g = 0; g < length; ++g) {
408 const auto ptr = ext->fetch(vbuffer.data());
409 const auto& cursets = revmapping[start + g];
410 const auto ncursets = cursets.first.size();
412 for (I<
decltype(ncursets)> s = 0; s < ncursets; ++s) {
413 const auto curset = cursets.first[s];
414 const auto curw = cursets.second[s];
415 const auto outptr = get_output_ptr(curset);
417 for (Index_ c = 0; c < NC; ++c) {
418 outptr[c] += ptr[c] * curw;
421 for (Index_ c = 0; c < NC; ++c) {
430 (*per_thread_sums)[t - 1] = std::move(tmp_sums);
432 },
static_cast<Index_
>(nsubs), options.num_threads);
435 for (
int u = 1; u < nused; ++u) {
436 const auto& thread_sums = *((*per_thread_sums)[u - 1]);
437 for (SetIndex s = 0; s < num_sets; ++s) {
438 const auto& thread_sum = thread_sums[s];
439 if (thread_sum.empty()) {
442 const auto outptr = buffers.sum[s];
443 for (Index_ c = 0; c < NC; ++c) {
444 outptr[c] += thread_sum[c];
472template<
typename Data_,
typename Index_,
typename Gene_,
typename Weight_,
typename Sum_>
480 aggregate_across_genes_by_row(input, gene_sets, buffers, options);
482 aggregate_across_genes_by_column(input, gene_sets, buffers, options);
486 const auto nsets = gene_sets.size();
488 const Index_ NC = input.
ncol();
489 quickstats::PairwiseSumWorkspace<Sum_> pswrk;
490 quickstats::PairwiseSumOptions psopt;
492 for (Index_ s = start, end = start + length; s < end; ++s) {
493 const auto& set = gene_sets[s];
496 denom = quickstats::pairwise_sum(set.number, set.weight, pswrk, psopt);
501 const auto current = buffers.
sum[s];
502 for (Index_ c = 0; c < NC; ++c) {
525template<
typename Sum_ =
double,
typename Data_,
typename Index_,
typename Gene_,
typename Weight_>
534 const Index_ NC = input.
ncol();
535 const auto nsets = gene_sets.size();
536 sanisizer::resize(output.
sum, nsets);
537 sanisizer::resize(buffers.
sum, nsets);
539 for (I<
decltype(nsets)> s = 0; s < nsets; ++s) {
543#ifdef SCRAN_AGGREGATE_TEST_INIT
544 , SCRAN_AGGREGATE_TEST_INIT
547 buffers.
sum[s] = output.
sum[s].data();
virtual Index_ ncol() const=0
virtual Index_ nrow() const=0
virtual bool prefer_rows() const=0
virtual bool is_sparse() const=0
Aggregate single-cell expression values.
Definition aggregate_across_cells.hpp:22
void aggregate_across_genes(const tatami::Matrix< Data_, Index_ > &input, const std::vector< AggregateAcrossGenesSet< Gene_, Weight_ > > &gene_sets, const AggregateAcrossGenesBuffers< Sum_ > &buffers, const AggregateAcrossGenesOptions &options)
Definition aggregate_across_genes.hpp:473
auto new_extractor(const Matrix< Value_, Index_ > &matrix, const bool row, MaybeOracle< oracle_, Index_ > oracle, Args_ &&... args)
std::shared_ptr< const std::vector< Index_ > > VectorPtr
void resize_container_to_Index_size(Container_ &container, const Index_ x, Args_ &&... args)
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)
Buffers for aggregate_across_genes().
Definition aggregate_across_genes.hpp:45
std::vector< Sum_ * > sum
Definition aggregate_across_genes.hpp:51
Options for aggregate_across_genes().
Definition aggregate_across_genes.hpp:26
bool average
Definition aggregate_across_genes.hpp:37
int num_threads
Definition aggregate_across_genes.hpp:31
Results of aggregate_across_genes().
Definition aggregate_across_genes.hpp:103
std::vector< std::vector< Sum_ > > sum
Definition aggregate_across_genes.hpp:109
Gene set to use in aggregate_across_genes().
Definition aggregate_across_genes.hpp:60
AggregateAcrossGenesSet()=default
const Gene_ * gene
Definition aggregate_across_genes.hpp:89
const Weight_ * weight
Definition aggregate_across_genes.hpp:95
std::size_t number
Definition aggregate_across_genes.hpp:84
AggregateAcrossGenesSet(std::size_t number, const Gene_ *gene, const Weight_ *weight)
Definition aggregate_across_genes.hpp:75