scran_blocks
Blocking utilities for libscran
Loading...
Searching...
No Matches
parallel_quantiles.hpp
Go to the documentation of this file.
1#ifndef SCRAN_BLOCKS_PARALLEL_QUANTILES_HPP
2#define SCRAN_BLOCKS_PARALLEL_QUANTILES_HPP
3
4#include <vector>
5#include <limits>
6#include <algorithm>
7#include <vector>
8#include <cstddef>
9#include <cmath>
10#include <optional>
11#include <cassert>
12
13#include "utils.hpp"
14
15#include "sanisizer/sanisizer.hpp"
16
22namespace scran_blocks {
23
34template<typename Output_, class Iterator_>
36public:
41 SingleQuantile(const std::size_t len, const double quantile) {
42 assert(len > 0);
43 sanisizer::can_ptrdiff<Iterator_>(len);
44 const Output_ frac = static_cast<Output_>(len - 1) * static_cast<Output_>(quantile);
45 const Output_ base = std::floor(frac);
46 my_lower = base; // cast is known-safe if can_ptrdiff passes and 0 <= quantile <= 1.
47 my_upper_fraction = frac - base;
48 my_lower_fraction = static_cast<Output_>(1) - my_upper_fraction;
49 my_skip_upper = my_upper_fraction == 0;
50 }
51
52private:
53 std::size_t my_lower;
54 Output_ my_upper_fraction;
55 Output_ my_lower_fraction;
56 bool my_skip_upper;
57
58public:
68 Output_ operator()(Iterator_ begin, Iterator_ end) const {
69 assert(sanisizer::is_less_than(my_lower, end - begin));
70 auto target = begin + my_lower;
71 std::nth_element(begin, target, end);
72
73 // Avoid looking at target+1 if don't need it - in particular, we'd get a out-of-bounds access if quantile = 1.
74 if (my_skip_upper) {
75 return *target;
76 } else {
77 const auto next = std::min_element(target + 1, end);
78 return static_cast<Output_>(*target) * my_lower_fraction + static_cast<Output_>(*next) * my_upper_fraction;
79 }
80 }
81};
82
93template<typename Output_, class Iterator_>
95public:
101 SingleQuantileVariable(const std::size_t max_len, const double quantile) : my_quantile(quantile) {
102 if (max_len >= 2) {
103 sanisizer::resize(my_choices, max_len - 1);
104 }
105 }
106
107private:
108 std::vector<std::optional<SingleQuantile<Output_, Iterator_> > > my_choices;
109 double my_quantile;
110
111public:
126 Output_ operator()(const std::size_t len, Iterator_ begin, Iterator_ end) {
127 assert(sanisizer::is_equal(len, end - begin));
128 if (len == 0) {
129 return std::numeric_limits<Output_>::quiet_NaN();
130 } else if (len == 1) {
131 return *begin;
132 } else {
133 auto& ocalc = my_choices[len - 2];
134 if (!ocalc.has_value()) { // Instantiating them on demand.
135 ocalc.emplace(len, my_quantile);
136 }
137 return (*ocalc)(begin, end);
138 }
139 }
140
153 Output_ operator()(Iterator_ begin, Iterator_ end) {
154 return this->operator()(end - begin, begin, end);
155 }
156};
157
175template<typename Stat_, typename Output_>
176void parallel_quantiles(const std::size_t n, const std::vector<Stat_*>& in, const double quantile, Output_* const out, const bool skip_nan) {
177 const auto nblocks = in.size();
178 if (nblocks == 0) {
179 std::fill_n(out, n, std::numeric_limits<Output_>::quiet_NaN());
180 return;
181 } else if (nblocks == 1) {
182 std::copy_n(in[0], n, out);
183 return;
184 }
185
186 std::vector<Stat_> tmp_buffer;
187 tmp_buffer.reserve(nblocks);
188
189 if (skip_nan) {
190 SingleQuantileVariable<Output_, I<decltype(tmp_buffer.begin())> > calcs(nblocks, quantile);
191 for (std::size_t g = 0; g < n; ++g) {
192 tmp_buffer.clear();
193 for (I<decltype(nblocks)> b = 0; b < nblocks; ++b) {
194 const auto val = in[b][g];
195 if (!std::isnan(val)) {
196 tmp_buffer.push_back(val);
197 }
198 }
199 out[g] = calcs(tmp_buffer.size(), tmp_buffer.begin(), tmp_buffer.end());
200 }
201
202 } else {
203
204 SingleQuantile<Output_, I<decltype(tmp_buffer.begin())> > calc(nblocks, quantile);
205 for (std::size_t g = 0; g < n; ++g) {
206 tmp_buffer.clear();
207 for (I<decltype(nblocks)> b = 0; b < nblocks; ++b) {
208 tmp_buffer.push_back(in[b][g]);
209 }
210 out[g] = calc(tmp_buffer.begin(), tmp_buffer.end());
211 }
212 }
213}
214
230template<typename Output_ = double, typename Stat_>
231std::vector<Output_> parallel_quantiles(const std::size_t n, const std::vector<Stat_*>& in, const double quantile, const bool skip_nan) {
232 auto out = sanisizer::create<std::vector<Output_> >(n);
233 parallel_quantiles(n, in, quantile, out.data(), skip_nan);
234 return out;
235}
236
237}
238
239#endif
240
Calculate a single quantile for containers of variable length.
Definition parallel_quantiles.hpp:94
Output_ operator()(const std::size_t len, Iterator_ begin, Iterator_ end)
Definition parallel_quantiles.hpp:126
SingleQuantileVariable(const std::size_t max_len, const double quantile)
Definition parallel_quantiles.hpp:101
Output_ operator()(Iterator_ begin, Iterator_ end)
Definition parallel_quantiles.hpp:153
Calculate a single quantile from a container.
Definition parallel_quantiles.hpp:35
SingleQuantile(const std::size_t len, const double quantile)
Definition parallel_quantiles.hpp:41
Output_ operator()(Iterator_ begin, Iterator_ end) const
Definition parallel_quantiles.hpp:68
Blocking utilities for libscran.
Definition block_weights.hpp:17
void parallel_quantiles(const std::size_t n, const std::vector< Stat_ * > &in, const double quantile, Output_ *const out, const bool skip_nan)
Definition parallel_quantiles.hpp:176