60template <
typename R,
typename Scalar,
typename G>
61concept Resampler =
requires(R
const & r, std::span<Scalar const> weights, std::span<Eigen::Index> ancestors, G & rng) {
62 r(weights, ancestors, rng);
70template <
typename R,
typename Scalar,
typename G>
74 std::span<Scalar const> weights,
75 std::span<Eigen::Index> ancestors,
77 std::span<Scalar> scratch
78 ) { r(weights, ancestors, rng, scratch); };
85template <
typename R,
typename P,
typename Scalar,
typename G>
88 &&
requires(R
const & r, P && policy, std::span<Scalar const> weights, std::span<Eigen::Index> ancestors, G & rng) {
89 r(std::forward<P>(policy), weights, ancestors, rng);
96template <std::
floating_po
int Scalar, std::uniform_random_bit_generator G>
100 auto uniform = std::uniform_real_distribution<Scalar>{Scalar(0), Scalar(1)};
101 return std::min(uniform(rng), std::nextafter(Scalar(1), Scalar(0)));
107template <
typename Scalar,
typename Positions>
109invertCumulative(std::span<Scalar const> weights, std::span<Eigen::Index> ancestors, Positions u)
111 EMRYS_FILTER_ASSERT(!weights.empty());
113 std::size_t lastPositive = 0;
114 for (std::size_t i = 0; i < weights.size(); ++i)
116 EMRYS_FILTER_ASSERT(weights[i] >= Scalar(0) &&
"weights must be nonnegative");
118 if (weights[i] > Scalar(0))
123 EMRYS_FILTER_ASSERT(total > Scalar(0) &&
"weights must have a positive sum");
126 Scalar cumulative = weights[0];
127 for (std::size_t j = 0; j < ancestors.size(); ++j)
129 Scalar
const position = u(j) * total;
130 while (i < lastPositive && position >= cumulative)
132 cumulative += weights[++i];
134 ancestors[j] = Eigen::Index(i);
143template <ExecutionPolicy P,
typename Scalar,
typename Positions>
145invertCumulative(P && policy, std::span<Scalar const> weights, std::span<Eigen::Index> ancestors, Positions
const & u)
147 if constexpr (isSequential<P>)
149 invertCumulative(weights, ancestors, u);
153 EMRYS_FILTER_ASSERT(!weights.empty());
154 Eigen::Index
const count = Eigen::Index(weights.size());
155 auto cumulative = scratchVector<Scalar>(policy, weights.size());
156 cumulativeSum(policy, weights, std::span<Scalar>{cumulative});
158 Eigen::Index
const lastPositive = reduceRanges(
161 [&](Eigen::Index begin, Eigen::Index length) {
162 Eigen::Index last = -1;
163 for (Eigen::Index i = begin; i < begin + length; ++i)
165 EMRYS_FILTER_ASSERT(weights[std::size_t(i)] >= Scalar(0) &&
"weights must be nonnegative");
166 if (weights[std::size_t(i)] > Scalar(0))
173 [](Eigen::Index a, Eigen::Index b) {
174 return b >= 0 ? b : a;
177 Scalar
const total = cumulative.back();
178 EMRYS_FILTER_ASSERT(lastPositive >= 0 && total > Scalar(0) &&
"weights must have a positive sum");
179 forEachIndex(std::forward<P>(policy), Eigen::Index(ancestors.size()), [&](Eigen::Index j) {
180 Scalar const position = u(std::size_t(j)) * total;
181 auto const above = std::upper_bound(cumulative.begin(), cumulative.end(), position);
182 ancestors[std::size_t(j)] = std::min(Eigen::Index(above - cumulative.begin()), lastPositive);
190template <std::
floating_po
int Scalar,
typename P, std::uniform_random_bit_generator G>
192uniforms01(P
const & policy, std::size_t count, G & rng)
194 auto draws = scratchVector<Scalar>(policy, count);
195 for (Scalar & draw : draws)
197 draw = uniform01<Scalar>(rng);
224 template <std::
floating_po
int Scalar, std::uniform_random_bit_generator G>
227 std::span<Scalar const> weights,
228 std::span<Eigen::Index> ancestors,
230 std::span<Scalar> scratch
233 EMRYS_FILTER_ASSERT(scratch.size() >= ancestors.size() &&
"the scratch must hold a draw per ancestor");
234 auto const positions = scratch.first(ancestors.size());
235 for (
auto & p : positions)
237 p = internal::uniform01<Scalar>(rng);
239 std::ranges::sort(positions);
240 internal::invertCumulative(weights, ancestors, [&](std::size_t j) {
246 template <std::
floating_po
int Scalar, std::uniform_random_bit_generator G>
248 operator()(std::span<Scalar const> weights, std::span<Eigen::Index> ancestors, G & rng)
const
250 auto positions = std::vector<Scalar>(ancestors.size());
251 (*this)(weights, ancestors, rng, std::span<Scalar>{positions});
258 template <ExecutionPolicy P, std::
floating_po
int Scalar, std::uniform_random_bit_generator G>
260 operator()(P && policy, std::span<Scalar const> weights, std::span<Eigen::Index> ancestors, G & rng)
const
262 if constexpr (internal::isSequential<P>)
264 (*this)(weights, ancestors, rng);
268 auto const positions = internal::uniforms01<Scalar>(policy, ancestors.size(), rng);
269 internal::invertCumulative(std::forward<P>(policy), weights, ancestors, [&](std::size_t j) {
279[[nodiscard]]
constexpr Multinomial
297 template <std::
floating_po
int Scalar, std::uniform_random_bit_generator G>
299 operator()(std::span<Scalar const> weights, std::span<Eigen::Index> ancestors, G & rng)
const
301 Scalar
const m = Scalar(ancestors.size());
302 internal::invertCumulative(weights, ancestors, [&](std::size_t j) {
303 return (Scalar(j) + internal::uniform01<Scalar>(rng)) / m;
310 template <ExecutionPolicy P, std::
floating_po
int Scalar, std::uniform_random_bit_generator G>
312 operator()(P && policy, std::span<Scalar const> weights, std::span<Eigen::Index> ancestors, G & rng)
const
314 if constexpr (internal::isSequential<P>)
316 (*this)(weights, ancestors, rng);
320 Scalar
const m = Scalar(ancestors.size());
321 auto const offsets = internal::uniforms01<Scalar>(policy, ancestors.size(), rng);
322 internal::invertCumulative(std::forward<P>(policy), weights, ancestors, [&](std::size_t j) {
323 return (Scalar(j) + offsets[j]) / m;
332[[nodiscard]]
constexpr Stratified
355 template <std::
floating_po
int Scalar, std::uniform_random_bit_generator G>
357 operator()(std::span<Scalar const> weights, std::span<Eigen::Index> ancestors, G & rng)
const
359 Scalar
const m = Scalar(ancestors.size());
360 Scalar
const offset = internal::uniform01<Scalar>(rng);
361 internal::invertCumulative(weights, ancestors, [&](std::size_t j) {
362 return (Scalar(j) + offset) / m;
367 template <ExecutionPolicy P, std::
floating_po
int Scalar, std::uniform_random_bit_generator G>
369 operator()(P && policy, std::span<Scalar const> weights, std::span<Eigen::Index> ancestors, G & rng)
const
371 Scalar
const m = Scalar(ancestors.size());
372 Scalar
const offset = internal::uniform01<Scalar>(rng);
373 internal::invertCumulative(std::forward<P>(policy), weights, ancestors, [&](std::size_t j) {
374 return (Scalar(j) + offset) / m;
382[[nodiscard]]
constexpr Systematic
397template <
typename Inner = Multinomial>
407 template <std::
floating_po
int Scalar, std::uniform_random_bit_generator G>
410 std::span<Scalar const> weights,
411 std::span<Eigen::Index> ancestors,
413 std::span<Scalar> scratch
417 scratch.size() >= weights.size() + ancestors.size()
418 &&
"the scratch must hold a fraction per weight and a draw per ancestor"
421 for (Scalar
const w : weights)
423 EMRYS_FILTER_ASSERT(w >= Scalar(0) &&
"weights must be nonnegative");
426 EMRYS_FILTER_ASSERT(total > Scalar(0) &&
"weights must have a positive sum");
428 Scalar
const m = Scalar(ancestors.size());
429 auto const fractions = scratch.first(weights.size());
431 for (std::size_t i = 0; i < weights.size(); ++i)
433 Scalar
const expected = m * weights[i] / total;
434 Scalar
const copies = std::floor(expected);
435 fractions[i] = expected - copies;
437 std::size_t
const count = copies > Scalar(0) ? std::size_t(copies) : 0;
438 for (std::size_t k = 0; k < count && out < ancestors.size(); ++k)
440 ancestors[out++] = Eigen::Index(i);
443 if (out < ancestors.size())
448 std::span<Scalar const>{fractions},
449 ancestors.subspan(out),
451 scratch.subspan(weights.size())
456 fractional(std::span<Scalar const>{fractions}, ancestors.subspan(out), rng);
462 template <std::
floating_po
int Scalar, std::uniform_random_bit_generator G>
464 operator()(std::span<Scalar const> weights, std::span<Eigen::Index> ancestors, G & rng)
const
466 auto scratch = std::vector<Scalar>(weights.size() + ancestors.size());
467 (*this)(weights, ancestors, rng, std::span<Scalar>{scratch});
475 template <ExecutionPolicy P, std::
floating_po
int Scalar, std::uniform_random_bit_generator G>
477 operator()(P && policy, std::span<Scalar const> weights, std::span<Eigen::Index> ancestors, G & rng)
const
479 if constexpr (internal::isSequential<P>)
481 (*this)(weights, ancestors, rng);
485 Eigen::Index
const count = Eigen::Index(weights.size());
486 Scalar
const total = internal::reduceRanges(
489 [&](Eigen::Index begin, Eigen::Index length) {
491 for (Scalar
const w : weights.subspan(std::size_t(begin), std::size_t(length)))
497 [](Scalar a, Scalar b) {
501 EMRYS_FILTER_ASSERT(total > Scalar(0) &&
"weights must have a positive sum");
503 Scalar
const m = Scalar(ancestors.size());
504 auto fractions = internal::scratchVector<Scalar>(policy, weights.size());
505 auto ends = internal::scratchVector<Eigen::Index>(policy, weights.size());
506 internal::forEachIndex(policy, count, [&](Eigen::Index i) {
507 Scalar
const expected = m * weights[std::size_t(i)] / total;
508 Scalar
const copies = std::floor(expected);
509 fractions[std::size_t(i)] = expected - copies;
510 ends[std::size_t(i)] = Eigen::Index(copies);
512 internal::cumulativeSum(policy, std::span<Eigen::Index const>{ends}, std::span<Eigen::Index>{ends});
513 Eigen::Index
const size = Eigen::Index(ancestors.size());
514 internal::forEachIndex(policy, count, [&](Eigen::Index i) {
515 Eigen::Index
const begin = i == 0 ? 0 : ends[std::size_t(i - 1)];
516 for (Eigen::Index k = begin; k < std::min(ends[std::size_t(i)], size); ++k)
518 ancestors[std::size_t(k)] = i;
521 std::size_t
const out = std::size_t(std::min(ends.back(), size));
522 if (out < ancestors.size())
524 auto const remainder = std::span<Scalar const>{fractions};
527 fractional(std::forward<P>(policy), remainder, ancestors.subspan(out), rng);
531 fractional(remainder, ancestors.subspan(out), rng);
542template <
typename Inner = Multinomial>
543[[nodiscard]]
constexpr Residual<Inner>
546 return {std::move(inner)};
Execution policies (decisions.md [execution-policies]): execution::sequential, the ExecutionPolicy co...
The macros every header includes first.
An execution policy: execution::sequential, a policy of the standard library such as std::execution::...
Definition Execution.h:459
A Resampler that also runs under the execution policy P, called as r(policy, weights,...
Definition Resampling.h:86
A Resampler that also takes scratch, called as r(weights, ancestors, rng, scratch) with scratch a std...
Definition Resampling.h:71
Requirements on a resampler for scalar type Scalar and random generator G.
Definition Resampling.h:61
constexpr Systematic systematic()
Definition Resampling.h:383
constexpr Stratified stratified()
Definition Resampling.h:333
constexpr Multinomial multinomial()
Definition Resampling.h:280
constexpr Residual< Inner > residual(Inner inner={})
Definition Resampling.h:544
The resamplers, each made by its factory (decisions.md [policy-factories]): multinomial(),...
Definition Resampling.h:210
Draws each ancestor independently.
Definition Resampling.h:219
void operator()(std::span< Scalar const > weights, std::span< Eigen::Index > ancestors, G &rng, std::span< Scalar > scratch) const
Fills ancestors with indices into weights, each drawn independently in proportion to its weight,...
Definition Resampling.h:226
void operator()(std::span< Scalar const > weights, std::span< Eigen::Index > ancestors, G &rng) const
As the form with scratch, allocating the scratch for the draws.
Definition Resampling.h:248
void operator()(P &&policy, std::span< Scalar const > weights, std::span< Eigen::Index > ancestors, G &rng) const
As the sequential form, under policy: the uniforms are drawn as it draws them but not sorted,...
Definition Resampling.h:260
Chooses index i floor( ) times deterministically, then draws the remainder with Inner from the fracti...
Definition Resampling.h:399
void operator()(std::span< Scalar const > weights, std::span< Eigen::Index > ancestors, G &rng) const
As the form with scratch, allocating the scratch.
Definition Resampling.h:464
void operator()(std::span< Scalar const > weights, std::span< Eigen::Index > ancestors, G &rng, std::span< Scalar > scratch) const
Fills ancestors with floor( ) copies of each index i, then draws the rest with fractional from the fr...
Definition Resampling.h:409
void operator()(P &&policy, std::span< Scalar const > weights, std::span< Eigen::Index > ancestors, G &rng) const
As the sequential form, under policy: the total is a reduction, each index's copies and fraction a fo...
Definition Resampling.h:477
Inner fractional
the resampler for the remainder, applied to the fractional parts
Definition Resampling.h:400
Draws one uniform in each of M equal strata of [0, 1).
Definition Resampling.h:293
void operator()(std::span< Scalar const > weights, std::span< Eigen::Index > ancestors, G &rng) const
Fills ancestors with indices into weights, one drawn in each stratum of [0, 1), using rng.
Definition Resampling.h:299
void operator()(P &&policy, std::span< Scalar const > weights, std::span< Eigen::Index > ancestors, G &rng) const
As the sequential form, under policy, with the uniforms drawn first in stratum order,...
Definition Resampling.h:312
Uses one uniform offset for M evenly spaced positions in [0, 1).
Definition Resampling.h:351
void operator()(P &&policy, std::span< Scalar const > weights, std::span< Eigen::Index > ancestors, G &rng) const
As the sequential form, under policy.
Definition Resampling.h:369
void operator()(std::span< Scalar const > weights, std::span< Eigen::Index > ancestors, G &rng) const
Fills ancestors with indices into weights at evenly spaced positions offset by one uniform draw from...
Definition Resampling.h:357