EmrysFilter 0.1.0
State estimation utilities in C++23 using Eigen
Loading...
Searching...
No Matches
Reduction.h
Go to the documentation of this file.
1#pragma once
2
11
12#include <array>
13#include <concepts>
14#include <cstddef>
15#include <expected>
16#include <iterator>
17#include <optional>
18#include <ranges>
19#include <span>
20#include <tuple>
21#include <type_traits>
22#include <utility>
23#include <vector>
24
36
37namespace Emrys
38{
39
40namespace internal
41{
42
44template <typename T>
45inline constexpr bool isUpdateBranch = false;
46
48template <typename Space, typename MeasurementSpace, typename Scalar>
49inline constexpr bool isUpdateBranch<UpdateBranch<Space, MeasurementSpace, Scalar>> = true;
50
52
56template <typename R>
57concept BranchRange = std::ranges::random_access_range<R> && std::ranges::sized_range<R>
58 && isUpdateBranch<std::remove_cvref_t<std::ranges::range_reference_t<R>>>;
59
61template <typename R>
62using BranchOf = std::remove_cvref_t<std::ranges::range_reference_t<R>>;
63
65template <typename R>
66using BranchScalarOf = decltype(BranchOf<R>::logWeight);
67
69template <typename R>
70using PosteriorOf = decltype(BranchOf<R>::posterior);
71
73template <BranchRange R>
74[[nodiscard]] BranchScalarOf<R>
75logTotalWeight(R const & branches)
76{
77 auto total = LogSumExp<BranchScalarOf<R>>{};
78 for (auto const & branch : branches)
79 {
80 total.add(branch.logWeight);
81 }
82 return total.value();
83}
84
88template <typename Out>
89inline constexpr bool holdsSeveral = false;
90
92template <typename Out>
93 requires requires { Out::HoldsSeveral; }
94inline constexpr bool holdsSeveral<Out> = Out::HoldsSeveral;
95
97
104template <typename Component, typename Scalar, typename Target = Component>
105struct OneComponentOutput
106{
108 using ComponentType = Component;
109
110 Target & target;
111 Scalar logWeight = 0;
112 bool added = false;
113
115 void
116 add(Scalar weight, Component && component)
117 {
118 EMRYS_FILTER_ASSERT(!added && "this filter keeps one component per update, so a policy adds at most one");
119 target = std::move(component);
120 logWeight = weight;
121 added = true;
122 }
123
125 void
126 add(Scalar weight, Component const & component)
127 {
128 add(weight, Component(component));
129 }
130};
131
135template <typename Component, typename Scalar>
136struct SeveralComponentOutput
137{
139 using ComponentType = Component;
141 static constexpr bool HoldsSeveral = true;
142
144 void
145 add(Scalar, Component &&)
146 {}
147
149 void
150 add(Scalar, Component const &)
151 {}
152};
153
157template <typename P, typename Branches, typename Component>
158concept ReturnsComponent = requires(P const & policy, Branches && branches) {
159 { policy(static_cast<Branches &&>(branches)) } -> std::same_as<std::optional<Component>>;
160};
161
165template <typename P, typename Branches, typename Out>
166concept ReducesInto =
167 std::invocable<P const &, Branches, Out &> || ReturnsComponent<P, Branches, typename Out::ComponentType>;
168
172template <typename P, BranchRange Branches, typename Out>
173 requires ReducesInto<P, Branches, Out>
174void
175reduceInto(P const & policy, Branches && branches, Out & out)
176{
177 if constexpr (std::invocable<P const &, Branches, Out &>)
178 {
179 policy(std::forward<Branches>(branches), out);
180 }
181 else
182 {
183 auto const logTotal = logTotalWeight(branches);
184 if (auto reduced = policy(std::forward<Branches>(branches)))
185 {
186 out.add(logTotal, std::move(*reduced));
187 }
188 }
189}
190
194template <typename Iterator>
195struct BranchAt
196{
197 Iterator first;
198
200 [[nodiscard]] decltype(auto)
201 operator()(std::size_t i) const
202 {
203 return first[std::iter_difference_t<Iterator>(i)];
204 }
205};
206
210template <typename R>
211using PassingBranches =
212 ForwardedLike<R, std::ranges::transform_view<std::span<std::size_t const>, BranchAt<std::ranges::iterator_t<R>>>>;
213
214} // namespace internal
215
221namespace reductions
222{
223
241{
245 template <typename Branches>
246 requires internal::BranchRange<Branches>
247 [[nodiscard]] std::optional<internal::PosteriorOf<Branches>>
248 operator()(Branches && branches) const
249 {
250 using Posterior = internal::PosteriorOf<Branches>;
251 using Scalar = internal::BranchScalarOf<Branches>;
252 using MomentsType = typename Posterior::MomentsType;
253 EMRYS_FILTER_ASSERT(!std::ranges::empty(branches) && "an update has at least one branch");
254 auto const at = std::ranges::begin(branches);
255 auto && first = at[0].posterior;
256 Eigen::Index const count = Eigen::Index(std::ranges::size(branches));
257 if (count == 1)
258 {
259 return internal::forwardLike<Branches>(first);
260 }
261
262 Scalar const logTotal = internal::logTotalWeight(branches);
263 EMRYS_FILTER_ASSERT(isFinite(logTotal) && "the branches' weights must be valid");
264 auto const branch = [&](Eigen::Index i) -> auto const & {
265 return at[i];
266 };
267 return Posterior{
268 first.space(),
269 internal::mixtureMomentsIn<MomentsType>(
270 first.space(),
271 count,
272 [&](Eigen::Index i) -> decltype(auto) {
273 return branch(i).posterior.mean();
274 },
275 [&](Eigen::Index i) -> decltype(auto) {
276 return branch(i).posterior.covarianceLower();
277 },
278 [&](Eigen::Index i) {
279 return branch(i).logWeight;
280 },
281 logTotal
282 )
283 };
284 }
285};
286
290[[nodiscard]] constexpr Collapse
292{
293 return {};
294}
295
305struct Select
306{
310 template <typename Branches>
311 requires internal::BranchRange<Branches>
312 [[nodiscard]] std::optional<internal::PosteriorOf<Branches>>
313 operator()(Branches && branches) const
314 {
315 EMRYS_FILTER_ASSERT(!std::ranges::empty(branches) && "an update has at least one branch");
316 auto const at = std::ranges::begin(branches);
317 std::ptrdiff_t const count = std::ptrdiff_t(std::ranges::size(branches));
318 std::ptrdiff_t best = 0;
319 for (std::ptrdiff_t i = 1; i < count; ++i)
320 {
321 if (at[i].logWeight > at[best].logWeight)
322 {
323 best = i;
324 }
325 }
326 return internal::forwardLike<Branches>(at[best].posterior);
327 }
328};
329
333[[nodiscard]] constexpr Select
335{
336 return {};
337}
338
350struct Keep
351{
355 template <typename Branches, typename Out>
356 requires internal::BranchRange<Branches> && internal::holdsSeveral<Out>
357 void
358 operator()(Branches && branches, Out & out) const
359 {
360 for (auto && branch : branches)
361 {
362 out.add(branch.logWeight, internal::forwardLike<Branches>(branch.posterior));
363 }
364 }
365};
366
370[[nodiscard]] constexpr Keep
372{
373 return {};
374}
375
376} // namespace reductions
377
384template <RealScalar Scalar = double>
386{
387 Scalar value;
388};
389
391template <typename Value>
392 requires std::is_arithmetic_v<Value>
394
396
397namespace reductions
398{
399
437template <typename Inner = Collapse, RealScalar Scalar_ = double>
438class Gate
439{
440public:
441
443 using Scalar = Scalar_;
444
446 explicit Gate(Scalar probability, Inner inner = {})
447 : m_probability{probability}
448 , m_inner{std::move(inner)}
449 {
450 EMRYS_FILTER_ASSERT(probability > Scalar(0) && probability < Scalar(1) && "the probability must be in (0, 1)");
451 }
452
455 : m_threshold{threshold.value}
456 , m_inner{std::move(inner)}
457 {
458 EMRYS_FILTER_ASSERT(threshold.value >= Scalar(0) && "the threshold must be nonnegative");
459 }
460
464 template <RealScalar Result = Scalar>
465 [[nodiscard]] Result
466 threshold(Eigen::Index dimension) const
467 {
468 if (m_probability)
469 {
470 return chiSquaredQuantile<Result>(Result(*m_probability), internal::scalarFromCount<Result>(dimension));
471 }
472 return Result(m_threshold);
473 }
474
476 [[nodiscard]] Inner const &
477 inner() const
478 {
479 return m_inner;
480 }
481
488 template <typename Branches, typename Out>
489 requires internal::BranchRange<Branches> && internal::ReducesInto<Inner, Branches, Out>
490 && internal::ReducesInto<Inner, internal::PassingBranches<Branches>, Out>
491 void
492 operator()(Branches && branches, Out & out) const
493 {
494 using UpdateScalar = internal::BranchScalarOf<Branches>;
495 EMRYS_FILTER_ASSERT(!std::ranges::empty(branches) && "an update has at least one branch");
496 auto const at = std::ranges::begin(branches);
497 std::size_t const count = std::size_t(std::ranges::size(branches));
498 auto const limit = threshold<UpdateScalar>(at[0].innovation.differenceDimension());
499 std::size_t passed = 0;
500 for (auto const & branch : branches)
501 {
502 passed += branch.nis() <= limit ? 1 : 0;
503 }
504 if (passed == 0)
505 {
506 return;
507 }
508 if (passed == count)
509 {
510 internal::reduceInto(m_inner, std::forward<Branches>(branches), out);
511 return;
512 }
513
514 auto const reducePassing = [&](auto & indices) {
515 std::size_t n = 0;
516 for (std::size_t k = 0; k < count; ++k)
517 {
518 if (at[std::ptrdiff_t(k)].nis() <= limit)
519 {
520 indices[n++] = k;
521 }
522 }
523 auto passing = std::ranges::transform_view{
524 std::span<std::size_t const>{indices.data(), passed},
525 internal::BranchAt<std::ranges::iterator_t<Branches>>{at}
526 };
527 internal::reduceInto(m_inner, internal::forwardLike<Branches>(passing), out);
528 };
529 using B = std::remove_cvref_t<Branches>;
530 if constexpr (requires { std::tuple_size<B>::value; })
531 {
532 auto indices = std::array<std::size_t, std::tuple_size_v<B>>{};
533 reducePassing(indices);
534 }
535 else if (count <= InlinePassing)
536 {
537 auto indices = std::array<std::size_t, InlinePassing>{};
538 reducePassing(indices);
539 }
540 else
541 {
542 auto indices = internal::bufferOf<std::size_t>(allocatorOf(branches));
543 indices.resize(passed);
544 reducePassing(indices);
545 }
546 }
547
549 static constexpr std::size_t InlinePassing = 32;
550
551private:
552
554 template <typename Branches>
555 [[nodiscard]] static auto
556 allocatorOf(Branches const & branches)
557 {
558 if constexpr (requires { branches.get_allocator(); })
559 {
560 return branches.get_allocator();
561 }
562 else
563 {
564 return DefaultAllocator{};
565 }
566 }
567
568 std::optional<Scalar> m_probability;
569 Scalar m_threshold = 0;
570 EMRYS_FILTER_NO_UNIQUE_ADDRESS Inner m_inner;
571};
572
579template <typename Value, typename Inner = Collapse>
580 requires std::is_arithmetic_v<Value>
581[[nodiscard]] Gate<Inner, internal::DeducedScalar<Value>>
582gate(Value probability, Inner inner = {})
583{
584 using Scalar = internal::DeducedScalar<Value>;
585 return Gate<Inner, Scalar>{Scalar(probability), std::move(inner)};
586}
587
592template <RealScalar Scalar, typename Inner = Collapse>
593[[nodiscard]] Gate<Inner, Scalar>
594gate(NisThreshold<Scalar> threshold, Inner inner = {})
595{
596 return Gate<Inner, Scalar>{threshold, std::move(inner)};
597}
598
599} // namespace reductions
600
631template <typename P, typename Update, typename State>
632concept ReductionPolicy = internal::ReducesInto<
633 P,
634 typename std::remove_cvref_t<Update>::Container,
635 internal::OneComponentOutput<State, typename std::remove_cvref_t<Update>::Scalar>>;
636
642template <typename P, typename Update, typename Component_>
643concept MixtureReductionPolicy = internal::ReducesInto<
644 P,
645 typename std::remove_cvref_t<Update>::Container,
646 internal::SeveralComponentOutput<Params<Component_>, typename std::remove_cvref_t<Update>::Scalar>>;
647
654template <typename State, typename Policy, typename Branches>
655 requires internal::BranchRange<Branches>
656 && internal::ReducesInto<
657 Policy,
658 Branches,
659 internal::OneComponentOutput<State, internal::BranchScalarOf<Branches>, std::optional<State>>>
660[[nodiscard]] std::optional<State>
661reduceToOne(Policy const & policy, Branches && branches)
662{
663 auto kept = std::optional<State>{};
664 auto out = internal::OneComponentOutput<State, internal::BranchScalarOf<Branches>, std::optional<State>>{kept};
665 internal::reduceInto(policy, std::forward<Branches>(branches), out);
666 return kept;
667}
668
669namespace internal
670{
671
676template <typename Policy, typename Branches, typename Component>
677inline constexpr bool keepsOne = ReturnsComponent<Policy, Branches, Component>;
678
680template <typename Inner, typename Scalar, typename Branches, typename Component>
681inline constexpr bool keepsOne<reductions::Gate<Inner, Scalar>, Branches, Component> =
682 keepsOne<Inner, Branches, Component>;
683
685
690template <typename Policy>
691inline constexpr bool policyReadsStatistics = true;
692
694template <>
695inline constexpr bool policyReadsStatistics<reductions::Collapse> = false;
696
697template <>
698inline constexpr bool policyReadsStatistics<reductions::Select> = false;
699
700template <>
701inline constexpr bool policyReadsStatistics<reductions::Keep> = false;
702
704
710template <typename Policy>
711struct WithStatistics
712{
713 Policy const & policy;
714
716 template <typename Branches, typename Out>
717 requires BranchRange<Branches> && ReducesInto<Policy, Branches, Out>
718 void
719 operator()(Branches && branches, Out & out) const
720 {
721 reduceInto(policy, std::forward<Branches>(branches), out);
722 }
723};
724
726template <typename Policy>
727inline constexpr bool policyReadsStatistics<WithStatistics<Policy>> = true;
728
730
734template <typename Policy>
735inline constexpr bool isCollapsePolicy = false;
736
738template <>
739inline constexpr bool isCollapsePolicy<reductions::Collapse> = true;
740
741template <typename Policy>
742inline constexpr bool isCollapsePolicy<WithStatistics<Policy>> = isCollapsePolicy<Policy>;
743
745
751template <typename Policy>
752inline constexpr bool isOneBranchInPlacePolicy = false;
753
755template <>
756inline constexpr bool isOneBranchInPlacePolicy<reductions::Collapse> = true;
757
758template <>
759inline constexpr bool isOneBranchInPlacePolicy<reductions::Select> = true;
760
761template <typename Scalar>
762inline constexpr bool isOneBranchInPlacePolicy<reductions::Gate<reductions::Collapse, Scalar>> = true;
763
764template <typename Scalar>
765inline constexpr bool isOneBranchInPlacePolicy<reductions::Gate<reductions::Select, Scalar>> = true;
766
767template <typename Policy>
768inline constexpr bool isOneBranchInPlacePolicy<WithStatistics<Policy>> = isOneBranchInPlacePolicy<Policy>;
769
771
776template <typename Gate>
777struct AcceptByGate
778{
779 Gate const & gate;
780
782 template <typename Scalar>
783 [[nodiscard]] bool
784 operator()(Scalar nis, Eigen::Index dimension) const
785 {
786 return nis <= gate.template threshold<Scalar>(dimension);
787 }
788};
789
794template <typename Policy>
795[[nodiscard]] auto
796oneBranchAcceptor(Policy const & policy)
797{
798 static_assert(isOneBranchInPlacePolicy<Policy>);
799 if constexpr (requires { policy.policy; })
800 {
801 return oneBranchAcceptor(policy.policy);
802 }
803 else if constexpr (requires { policy.inner(); })
804 {
805 return AcceptByGate<Policy>{policy};
806 }
807 else
808 {
809 return AcceptEvery{};
810 }
811}
812
816template <typename Scalar, typename Space, typename MeasurementSpace, std::size_t Count, typename Allocator>
817[[nodiscard]] FilterUpdate<MeasurementSpace, Count, Scalar, Allocator>
818filterUpdate(MeasurementUpdate<Space, MeasurementSpace, Count, Scalar, Allocator> && measured, bool accepted)
819{
820 using Result = FilterUpdate<MeasurementSpace, Count, Scalar, Allocator>;
821 using Branch = typename Result::BranchType;
822 auto const strip = [](auto & branch) {
823 return Branch{
824 branch.logWeight,
825 std::move(branch.innovation),
826 branch.mode,
827 branch.component,
828 branch.iterations,
829 branch.converged
830 };
831 };
832 if constexpr (Count == std::dynamic_extent)
833 {
834 auto result = Result{
835 typename Result::Container(measured.get_allocator()),
836 MixtureEvidenceAccess::mixtureLogLikelihood(measured),
837 accepted
838 };
839 result.branches.reserve(measured.branches.size());
840 for (auto & branch : measured.branches)
841 {
842 result.branches.push_back(strip(branch));
843 }
844 return result;
845 }
846 else
847 {
848 return [&]<std::size_t... I>(std::index_sequence<I...>) {
849 return Result{
850 {strip(measured.branches[I])...},
851 MixtureEvidenceAccess::mixtureLogLikelihood(measured),
852 accepted
853 };
854 }(std::make_index_sequence<Count>{});
855 }
856}
857
862template <
863 typename Scalar,
864 typename Space,
865 typename MeasurementSpace,
866 std::size_t Count,
867 typename MeasuredAllocator,
868 typename ResultAllocator>
869void
870filterUpdateInto(
871 MeasurementUpdate<Space, MeasurementSpace, Count, Scalar, MeasuredAllocator> & measured,
872 bool accepted,
873 FilterUpdate<MeasurementSpace, Count, Scalar, ResultAllocator> & result
874)
875{
876 if constexpr (Count == std::dynamic_extent)
877 {
879 result.branches.clear();
880 result.branches.reserve(measured.branches.size());
881 for (auto & branch : measured.branches)
882 {
883 result.branches.push_back(
884 Branch{
885 branch.logWeight,
886 std::move(branch.innovation),
887 branch.mode,
888 branch.component,
889 branch.iterations,
890 branch.converged
891 }
892 );
893 }
894 MixtureEvidenceAccess::setMixtureLogLikelihood(result, MixtureEvidenceAccess::mixtureLogLikelihood(measured));
895 MixtureEvidenceAccess::setLogKeptWeight(result, Scalar(0));
896 result.accepted = accepted;
897 }
898 else
899 {
900 result = filterUpdate(std::move(measured), accepted);
901 }
902}
903
913template <bool Statistics, typename Slots, typename Allocator, UncertainInput Prior, Measurement M, typename Reduce>
914[[nodiscard]] std::expected<void, GaussianError>
915updateSlot(
916 Slots & slots,
917 std::size_t i,
918 Allocator const & allocator,
919 Prior const & prior,
920 M const & measurement,
921 Reduce const & reduce
922)
923{
924 using Update = typename Slots::value_type;
925 EMRYS_FILTER_ASSERT(i <= slots.size() && "the slots before this one must exist");
926 if constexpr (requires(Update & update) { update.measured; })
927 {
928 if (i == slots.size())
929 {
930 slots.push_back(Update(allocator));
931 }
932 auto & slot = slots[i];
933 auto const measured = updateBranchesInto<Statistics>(prior, measurement, slot.measured);
934 if (!measured)
935 {
936 return measured;
937 }
938 bool const accepted = reduce(slot.measured);
939 filterUpdateInto(slot.measured, accepted, slot);
940 }
941 else
942 {
943 auto measured = updateBranches<Statistics>(prior, measurement);
944 if (!measured)
945 {
946 return std::unexpected{measured.error()};
947 }
948 bool const accepted = reduce(*measured);
949 auto update = filterUpdate(std::move(*measured), accepted);
950 if (i == slots.size())
951 {
952 slots.push_back(std::move(update));
953 }
954 else
955 {
956 slots[i] = std::move(update);
957 }
958 }
959 return {};
960}
961
968template <
969 bool Statistics,
970 typename Slots,
971 typename Allocator,
972 UncertainInput Prior,
973 GivenMeasurementLike G,
974 typename Reduce>
975[[nodiscard]] std::expected<void, GaussianError>
976updateSlot(
977 Slots & slots,
978 std::size_t i,
979 Allocator const & allocator,
980 Prior const & prior,
981 G const & given,
982 Reduce const & reduce
983)
984{
985 using Update = typename Slots::value_type;
986 EMRYS_FILTER_ASSERT(i <= slots.size() && "the slots before this one must exist");
987 auto measured = updateBranchesWith<Statistics>(prior, given.pinned, given.value);
988 if (!measured)
989 {
990 return std::unexpected{measured.error()};
991 }
992 bool const accepted = reduce(*measured);
993 if constexpr (requires(Update & update) { update.measured; })
994 {
995 if (i == slots.size())
996 {
997 slots.push_back(Update(allocator));
998 }
999 filterUpdateInto(*measured, accepted, slots[i]);
1000 }
1001 else
1002 {
1003 auto update = filterUpdate(std::move(*measured), accepted);
1004 if (i == slots.size())
1005 {
1006 slots.push_back(std::move(update));
1007 }
1008 else
1009 {
1010 slots[i] = std::move(update);
1011 }
1012 }
1013 return {};
1014}
1015
1016} // namespace internal
1017
1018} // namespace Emrys
The allocator conventions of decisions.md [allocation]: DefaultAllocator, Capacity,...
Consistency checks (decisions.md [composed-features]): NisMonitor, a windowed chi-squared test on the...
How a function that stores a matrix argument takes it (decisions.md [forwarded-matrices]): the concep...
The Gaussian: its parameters GaussianParams, the Gaussian that evaluates the density,...
The macros every header includes first.
Mathematical constants and scalar functions that work in every scalar type the library accepts,...
The update of a Gaussian by a measurement (decisions.md [templated-measurements], [weighted-branches]...
The mixture: the parameters MixtureParams of weighted components of one type (decisions....
SampleSet: a matrix whose columns are samples in a space, which the samplers return and the unscented...
Weighted statistics over points, all in internal, which the containers of weighted elements (Gaussian...
Log-weights: the WeightError a normalization can fail with (NumericError.h), and the streaming log-su...
Reduction policy that rejects branches by a chi-squared test on their normalized innovation squared,...
Definition Reduction.h:439
static constexpr std::size_t InlinePassing
The most branches of a run-time number whose passing indices a gate holds on the stack.
Definition Reduction.h:549
Result threshold(Eigen::Index dimension) const
Definition Reduction.h:466
Gate(NisThreshold< Scalar > threshold, Inner inner={})
Passes branches whose NIS is at most threshold.
Definition Reduction.h:454
Inner const & inner() const
Definition Reduction.h:477
void operator()(Branches &&branches, Out &out) const
Reduces the branches of branches that pass the gate into out by Inner, or adds nothing when none pass...
Definition Reduction.h:492
Scalar_ Scalar
The type of the probability or threshold it holds.
Definition Reduction.h:443
Gate(Scalar probability, Inner inner={})
Passes branches whose NIS is within the probability chi-squared quantile.
Definition Reduction.h:446
A reduction policy a MixtureFilter over Component_ takes for Update: one that reduces the branches in...
Definition Reduction.h:643
A reduction policy for a filter that keeps a State per update: it reduces an update's branches to at ...
Definition Reduction.h:632
std::allocator< std::byte > DefaultAllocator
The allocator a type with a run-time count uses unless given one.
Definition Allocation.h:47
Scalar chiSquaredQuantile(std::type_identity_t< Scalar > const &probability, std::type_identity_t< Scalar > const &dof)
Definition ChiSquared.h:148
#define EMRYS_FILTER_NO_UNIQUE_ADDRESS
Assertion used for programmer errors (misuse of the API).
Definition Macros.h:38
bool isFinite(T const &x)
Definition Math.h:305
constexpr AxisIndex< Path, sizeof...(I)> at(I... indices)
Definition Discrete.h:1071
constexpr Select select()
Definition Reduction.h:334
std::optional< State > reduceToOne(Policy const &policy, Branches &&branches)
Definition Reduction.h:661
Gate< Inner, internal::DeducedScalar< Value > > gate(Value probability, Inner inner={})
Definition Reduction.h:582
constexpr Collapse collapse()
Definition Reduction.h:291
constexpr Keep keep()
Definition Reduction.h:371
GivenBlock< Space, P, Value > given(Space const &space, Value const &value)
Definition Measurement.h:917
std::expected< CategoricalUpdate< Space, Scalar >, WeightError > update(CategoricalParams< Space, Scalar > const &prior, E const &emission)
Definition Emission.h:97
The reduction policies, each made by its factory (decisions.md [policy-factories],...
Definition Reduction.h:222
FilterBranch< MeasurementSpace, Scalar > BranchType
one branch
Definition MeasurementUpdate.h:254
A Gate's threshold given directly as a normalized innovation squared, rather than as a probability,...
Definition Reduction.h:386
Scalar value
the largest NIS that passes
Definition Reduction.h:387
Reduction policy that moment-matches an update's branches into one Gaussian: soft outlier rejection,...
Definition Reduction.h:241
std::optional< internal::PosteriorOf< Branches > > operator()(Branches &&branches) const
Definition Reduction.h:248
Reduction policy that keeps every branch, each added to the filter's output with its own posterior pr...
Definition Reduction.h:351
void operator()(Branches &&branches, Out &out) const
Adds each of branches to out with its log-weight, its posterior moved out of branches given as an rva...
Definition Reduction.h:358
Reduction policy that keeps the most probable branch: hard rejection by comparing the noise component...
Definition Reduction.h:306
std::optional< internal::PosteriorOf< Branches > > operator()(Branches &&branches) const
Definition Reduction.h:313