44template <
typename Scalar>
53 if (x == -Eigen::NumTraits<Scalar>::infinity())
59 m_sum += Eigen::numext::exp(x - m_max);
63 m_sum = m_sum * Eigen::numext::exp(m_max - x) + Scalar(1);
74 template <
typename Derived>
76 add(Eigen::ArrayBase<Derived>
const & xs)
78 if constexpr (std::floating_point<Scalar>)
84 Scalar
const max = xs.template maxCoeff<Eigen::PropagateNaN>();
85 if (max == -Eigen::NumTraits<Scalar>::infinity())
92 range.m_sum = max == Eigen::NumTraits<Scalar>::infinity() ? Scalar((xs == max).count())
93 : Scalar((xs - max).exp().sum());
98 for (Eigen::Index i = 0; i < xs.size(); ++i)
111 if (other.m_sum == Scalar(0))
115 if (m_sum == Scalar(0))
120 if (other.m_max <= m_max)
122 m_sum += other.m_sum * Eigen::numext::exp(other.m_max - m_max);
126 m_sum = m_sum * Eigen::numext::exp(m_max - other.m_max) + other.m_sum;
135 return m_sum == Scalar(0) ? -Eigen::NumTraits<Scalar>::infinity() : m_max + Eigen::numext::log(m_sum);
140 Scalar m_max = -Eigen::NumTraits<Scalar>::infinity();
149template <
typename Scalar, ExecutionPolicy P,
typename LogAt>
151logSumExp(P && policy, Eigen::Index count, LogAt
const & logAt)
153 return internal::reduceRanges(
154 std::forward<P>(policy),
156 [&](Eigen::Index begin, Eigen::Index length) {
158 for (Eigen::Index i = begin; i < begin + length; ++i)
160 sum.add(Scalar(logAt(i)));
174template <
typename Scalar,
typename LogAt>
187template <ExecutionPolicy P,
typename Derived>
188[[nodiscard]]
typename Derived::Scalar
189logSumExp(P && policy, Eigen::ArrayBase<Derived>
const & logs)
191 using Scalar =
typename Derived::Scalar;
192 return internal::reduceRanges(
193 std::forward<P>(policy),
195 [&](Eigen::Index begin, Eigen::Index length) {
196 auto sum = LogSumExp<Scalar>{};
197 sum.add(logs.derived().segment(begin, length));
200 [](LogSumExp<Scalar> a, LogSumExp<Scalar>
const & b) {
210template <
typename Derived>
211[[nodiscard]]
typename Derived::Scalar
220template <
typename Scalar, ExecutionPolicy P>
221[[nodiscard]] std::expected<Scalar, WeightError>
222logTotalWeight(P && policy, std::span<Scalar const> logWeights);
228template <
typename Scalar, std::ranges::input_range R>
229[[nodiscard]] std::expected<Scalar, WeightError>
230logTotalWeight(R && logWeights)
232 if constexpr (std::ranges::contiguous_range<R>
233 && std::same_as<std::remove_cv_t<std::ranges::range_value_t<R>>, Scalar>)
235 return logTotalWeight<Scalar>(execution::sequential, std::span<Scalar const>{logWeights});
239 auto total = LogSumExp<Scalar>{};
241 for (Scalar
const w : logWeights)
243 if (
isNaN(w) || w == Eigen::NumTraits<Scalar>::infinity())
245 return std::unexpected{WeightError::NonFinite};
252 return std::unexpected{WeightError::Empty};
254 Scalar
const result = total.value();
255 if (result == -Eigen::NumTraits<Scalar>::infinity())
257 return std::unexpected{WeightError::ZeroSum};
266template <
typename Scalar, ExecutionPolicy P>
267[[nodiscard]] std::expected<Scalar, WeightError>
268logTotalWeight(P && policy, std::span<Scalar const> logWeights)
273 LogSumExp<Scalar> sum;
274 bool nonFinite =
false;
277 if (logWeights.empty())
279 return std::unexpected{WeightError::Empty};
281 Total
const total = reduceRanges(
282 std::forward<P>(policy),
283 Eigen::Index(logWeights.size()),
284 [&](Eigen::Index begin, Eigen::Index length) {
285 auto range = Total{};
286 auto const weights = logWeights.subspan(std::size_t(begin), std::size_t(length));
287 if constexpr (std::floating_point<Scalar>)
289 auto const map = Eigen::Map<Eigen::Array<Scalar, Eigen::Dynamic, 1>
const>{weights.data(), length};
291 range.nonFinite = !(map < Eigen::NumTraits<Scalar>::infinity()).all();
292 if (!range.nonFinite)
299 for (Scalar
const w : weights)
301 if (
isNaN(w) || w == Eigen::NumTraits<Scalar>::infinity())
303 range.nonFinite =
true;
311 [](Total a, Total
const & b) {
313 a.nonFinite = a.nonFinite || b.nonFinite;
319 return std::unexpected{WeightError::NonFinite};
321 Scalar
const result = total.sum.value();
322 if (result == -Eigen::NumTraits<Scalar>::infinity())
324 return std::unexpected{WeightError::ZeroSum};
334template <
typename Scalar>
340 PruneCut() =
default;
345 PruneCut(Scalar cutoff, Eigen::Index keepTies)
347 , m_keepTies{keepTies}
352 removes(Scalar logWeight)
354 if (logWeight < m_cutoff)
358 if (logWeight == m_cutoff)
360 return m_tiesSeen++ >= m_keepTies;
367 Scalar m_cutoff = -Eigen::NumTraits<Scalar>::infinity();
368 Eigen::Index m_keepTies = std::numeric_limits<Eigen::Index>::max();
369 Eigen::Index m_tiesSeen = 0;
376template <
typename Scalar>
377[[nodiscard]] PruneCut<Scalar>
378lightestCut(std::span<Scalar const> sorted, Eigen::Index count)
384 Scalar
const cutoff = sorted[std::size_t(count - 1)];
385 auto const [first, last] = std::equal_range(sorted.begin(), sorted.end(), cutoff);
386 auto const lighter = Eigen::Index(first - sorted.begin());
387 auto const ties = Eigen::Index(last - first);
388 return {cutoff, ties - (count - lighter)};
394template <
typename Scalar>
400 template <
typename LogAt,
typename Scratch>
401 [[nodiscard]] PruneCut<Scalar>
402 operator()(Eigen::Index count, LogAt
const & logAt, Scalar logTotal, Scratch & )
const
404 Scalar
const cut = logThreshold + logTotal;
405 Scalar heaviest = -Eigen::NumTraits<Scalar>::infinity();
406 for (Eigen::Index i = 0; i < count; ++i)
408 heaviest = std::max(heaviest, Scalar(logAt(i)));
413 return {heaviest, 1};
423template <
typename Scalar>
431 template <
typename LogAt,
typename Scratch>
432 [[nodiscard]] PruneCut<Scalar>
433 operator()(Eigen::Index count, LogAt
const & logAt, Scalar logTotal, Scratch & scratch)
const
435 EMRYS_FILTER_ASSERT(logFraction < Scalar(0) &&
"the fraction pruned is below one");
436 scratch.resize(std::size_t(count));
437 for (Eigen::Index i = 0; i < count; ++i)
439 scratch[std::size_t(i)] = Scalar(logAt(i));
441 std::sort(scratch.begin(), scratch.end());
442 Scalar
const budget = logFraction + logTotal;
443 auto removed = LogSumExp<Scalar>{};
444 Eigen::Index taken = 0;
445 while (taken < count - 1)
448 next.add(scratch[std::size_t(taken)]);
449 if (!(next.value() < budget))
456 return lightestCut(std::span<Scalar const>{scratch.data(), scratch.size()}, taken);
463struct PruneToSizeRule
465 Eigen::Index maxSize;
470 template <
typename Scalar,
typename LogAt,
typename Scratch>
471 [[nodiscard]] PruneCut<Scalar>
472 operator()(Eigen::Index count, LogAt
const & logAt, Scalar , Scratch & scratch)
const
474 EMRYS_FILTER_ASSERT(maxSize >= 1 &&
"a prune leaves at least one element");
475 if (count <= maxSize)
479 scratch.resize(std::size_t(count));
480 for (Eigen::Index i = 0; i < count; ++i)
482 scratch[std::size_t(i)] = Scalar(logAt(i));
486 Eigen::Index
const removed = count - maxSize;
487 auto const at = scratch.begin() + std::ptrdiff_t(removed - 1);
488 std::nth_element(scratch.begin(), at, scratch.end());
489 Scalar
const cutoff = *
at;
490 auto const lighter = Eigen::Index(std::count_if(scratch.begin(), scratch.end(), [&](Scalar w) {
493 auto const ties = Eigen::Index(std::count(scratch.begin(), scratch.end(), cutoff));
494 return {cutoff, ties - (removed - lighter)};
503template <
typename Scalar,
typename Weight, std::uniform_random_bit_generator G>
504[[nodiscard]] Eigen::Index
505drawIndex(Eigen::Index count, Weight
const & weight, G & rng)
507 static_assert(std::floating_point<Scalar>,
"sampling requires a standard floating-point scalar");
508 EMRYS_FILTER_ASSERT(count > 0 &&
"draw from at least one element");
509 Scalar
const u = std::uniform_real_distribution<Scalar>{}(rng);
511 Eigen::Index last = 0;
512 for (Eigen::Index i = 0; i < count; ++i)
514 Scalar
const w = weight(i);
Execution policies (decisions.md [execution-policies]): execution::sequential, the ExecutionPolicy co...
The macros every header includes first.
Mathematical constants and scalar functions that work in every scalar type the library accepts,...
NumericError, the reasons a factorization or a normalization can fail, and its two names,...
Streaming log-sum-exp: accumulates without overflow.
Definition Weights.h:46
void add(Scalar x)
Adds x to the sum, rescaling the running sum if x is the new maximum.
Definition Weights.h:51
Scalar value() const
Definition Weights.h:133
void add(LogSumExp const &other)
Adds everything added to other, as if its inputs had been added one by one, up to rounding.
Definition Weights.h:109
void add(Eigen::ArrayBase< Derived > const &xs)
Adds every element of xs, as if they had been added one by one, up to rounding.
Definition Weights.h:76
Scalar logSumExp(P &&policy, Eigen::Index count, LogAt const &logAt)
Definition Weights.h:151
bool isNaN(T const &x)
Definition Math.h:329
constexpr AxisIndex< Path, sizeof...(I)> at(I... indices)
Definition Discrete.h:1071
constexpr Sequential sequential
The default execution policy: plain loops on the calling thread, in index order (Sequential).
Definition Execution.h:128