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
12#include "utils.hpp"
13
14#include "sanisizer/sanisizer.hpp"
15
21namespace scran_blocks {
22
33template<typename Output_, class Iterator_>
35public:
40 SingleQuantile(const std::size_t len, const double quantile) {
41 sanisizer::can_ptrdiff<Iterator_>(len);
42 const Output_ frac = static_cast<Output_>(len - 1) * static_cast<Output_>(quantile);
43 const Output_ base = std::floor(frac);
44 my_lower = base; // cast is known-safe if can_ptrdiff passes and 0 <= quantile <= 1.
45 my_upper_fraction = frac - base;
46 my_lower_fraction = static_cast<Output_>(1) - my_upper_fraction;
47 my_skip_upper = my_upper_fraction == 0;
48 }
49
50private:
51 std::size_t my_lower;
52 Output_ my_upper_fraction;
53 Output_ my_lower_fraction;
54 bool my_skip_upper;
55
56public:
66 Output_ operator()(Iterator_ begin, Iterator_ end) const {
67 auto target = begin + my_lower;
68 std::nth_element(begin, target, end);
69
70 // Avoid looking at target+1 if don't need it - in particular, we'd get a out-of-bounds access if quantile = 1.
71 if (my_skip_upper) {
72 return *target;
73 } else {
74 const auto next = std::min_element(target + 1, end);
75 return static_cast<Output_>(*target) * my_lower_fraction + static_cast<Output_>(*next) * my_upper_fraction;
76 }
77 }
78};
79
90template<typename Output_, class Iterator_>
92public:
98 SingleQuantileVariable(const std::size_t max_len, const double quantile) : my_quantile(quantile) {
99 if (max_len >= 2) {
100 sanisizer::resize(my_choices, max_len - 1);
101 }
102 }
103
104private:
105 std::vector<std::optional<SingleQuantile<Output_, Iterator_> > > my_choices;
106 double my_quantile;
107
108public:
123 Output_ operator()(const std::size_t len, Iterator_ begin, Iterator_ end) {
124 if (len == 0) {
125 return std::numeric_limits<Output_>::quiet_NaN();
126 } else if (len == 1) {
127 return *begin;
128 } else {
129 auto& ocalc = my_choices[len - 2];
130 if (!ocalc.has_value()) { // Instantiating them on demand.
131 ocalc.emplace(len, my_quantile);
132 }
133 return (*ocalc)(begin, end);
134 }
135 }
136
149 Output_ operator()(Iterator_ begin, Iterator_ end) {
150 return this->operator()(end - begin, begin, end);
151 }
152};
153
171template<typename Stat_, typename Output_>
172void parallel_quantiles(const std::size_t n, const std::vector<Stat_*>& in, const double quantile, Output_* const out, const bool skip_nan) {
173 const auto nblocks = in.size();
174 if (nblocks == 0) {
175 std::fill_n(out, n, std::numeric_limits<Output_>::quiet_NaN());
176 return;
177 } else if (nblocks == 1) {
178 std::copy_n(in[0], n, out);
179 return;
180 }
181
182 std::vector<Stat_> tmp_buffer;
183 tmp_buffer.reserve(nblocks);
184
185 if (skip_nan) {
186 SingleQuantileVariable<Output_, I<decltype(tmp_buffer.begin())> > calcs(nblocks, quantile);
187 for (std::size_t g = 0; g < n; ++g) {
188 tmp_buffer.clear();
189 for (I<decltype(nblocks)> b = 0; b < nblocks; ++b) {
190 const auto val = in[b][g];
191 if (!std::isnan(val)) {
192 tmp_buffer.push_back(val);
193 }
194 }
195 out[g] = calcs(tmp_buffer.size(), tmp_buffer.begin(), tmp_buffer.end());
196 }
197
198 } else {
199
200 SingleQuantile<Output_, I<decltype(tmp_buffer.begin())> > calc(nblocks, quantile);
201 for (std::size_t g = 0; g < n; ++g) {
202 tmp_buffer.clear();
203 for (I<decltype(nblocks)> b = 0; b < nblocks; ++b) {
204 tmp_buffer.push_back(in[b][g]);
205 }
206 out[g] = calc(tmp_buffer.begin(), tmp_buffer.end());
207 }
208 }
209}
210
226template<typename Output_ = double, typename Stat_>
227std::vector<Output_> parallel_quantiles(const std::size_t n, const std::vector<Stat_*>& in, const double quantile, const bool skip_nan) {
228 auto out = sanisizer::create<std::vector<Output_> >(n);
229 parallel_quantiles(n, in, quantile, out.data(), skip_nan);
230 return out;
231}
232
233}
234
235#endif
236
Calculate a single quantile for containers of variable length.
Definition parallel_quantiles.hpp:91
Output_ operator()(const std::size_t len, Iterator_ begin, Iterator_ end)
Definition parallel_quantiles.hpp:123
SingleQuantileVariable(const std::size_t max_len, const double quantile)
Definition parallel_quantiles.hpp:98
Output_ operator()(Iterator_ begin, Iterator_ end)
Definition parallel_quantiles.hpp:149
Calculate a single quantile from a container.
Definition parallel_quantiles.hpp:34
SingleQuantile(const std::size_t len, const double quantile)
Definition parallel_quantiles.hpp:40
Output_ operator()(Iterator_ begin, Iterator_ end) const
Definition parallel_quantiles.hpp:66
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:172