EmrysFilter 0.1.0
State estimation utilities in C++23 using Eigen
Loading...
Searching...
No Matches
ImmFilter.h
Go to the documentation of this file.
1#pragma once
2
13
14#include <cstddef>
15#include <expected>
16#include <optional>
17#include <span>
18#include <type_traits>
19#include <utility>
20
39
40namespace Emrys
41{
42
61template <typename ModeUpdate, typename Scalar_ = double, typename Allocator = DefaultAllocator>
63{
64 using Scalar = Scalar_;
65 using ModeUpdateType = ModeUpdate;
66 using ModeUpdatesType = internal::Buffer<ModeUpdate, Allocator>;
67
68 ImmFilterUpdate() = default;
69
71 explicit ImmFilterUpdate(Allocator const & allocator)
72 : modes(internal::ReBound<Allocator, ModeUpdate>(allocator))
73 {}
74
76 [[nodiscard]] Allocator
78 {
79 return Allocator(modes.get_allocator());
80 }
81
83 [[nodiscard]] Scalar
85 {
86 return m_logLikelihood;
87 }
88
90 bool accepted = false;
91
92private:
93
94 friend struct internal::UpdateAccess;
95
96 Scalar m_logLikelihood = 0;
97};
98
99namespace internal
100{
101
103template <typename ModeUpdate, typename Scalar, typename Old, typename Allocator>
104struct RebindUpdateAllocatorOf<ImmFilterUpdate<ModeUpdate, Scalar, Old>, Allocator>
105{
107};
108
110
111} // namespace internal
112
136template <Space Space_, DiscreteSpace ModeSpace_, RealScalar Scalar_ = double, typename Allocator_ = DefaultAllocator>
138{
139 using Scalar = Scalar_;
140 using SpaceType = Space_;
141 using ModeSpaceType = ModeSpace_;
144 using JointsType = internal::Buffer<JointType, Allocator_>;
146 using PredictionsType = internal::Buffer<GaussianParams<Space_, Scalar_>, Allocator_>;
147
151
156
161 [[nodiscard]] GaussianParams<Space_, Scalar_> const &
162 predictedThrough(Eigen::Index from, Eigen::Index model) const
163 {
164 if (from == model)
165 {
166 return joints[std::size_t(from)].predicted;
167 }
168 Eigen::Index const count = Eigen::Index(joints.size());
169 return throughOtherModels[std::size_t(from * (count - 1) + (model < from ? model : model - 1))];
170 }
171};
172
173namespace internal
174{
175
179template <typename C, typename M, bool = isOtherGaussianForm<C>>
180struct ImmMeasured
181{
183 using type = typename decltype(Emrys::update(std::declval<C const &>(), std::declval<M const &>()))::value_type;
184};
185
189template <typename C, typename M>
190struct ImmMeasured<C, M, true>
191{
193 using type = typename decltype(updateInPlace<true>(std::declval<C &>(), std::declval<M const &>()))::value_type;
194};
195
201template <typename Lazy, typename Policy>
202struct UnlessGivenBlock
203{
204 using type = typename Lazy::type;
205};
206
208template <typename Lazy, typename Space, typename P, typename Value>
209struct UnlessGivenBlock<Lazy, GivenBlock<Space, P, Value>>
210{};
211
213
217template <typename M>
218struct MeasurementModeAxisOf
219{
220 using type = void;
221};
222
224template <typename M>
225 requires(M::ModelType::template has<PolicyKind::additiveNoise>)
226struct MeasurementModeAxisOf<M>
227{
229 using type = NoiseModeAxis<typename M::ModelType::template Policy<PolicyKind::additiveNoise>>;
230};
231
233
235template <typename M>
236inline constexpr bool isByMode = !std::is_void_v<typename MeasurementModeAxisOf<M>::type>;
237
242template <typename Space, typename P, typename One, typename Value>
243struct GivenComponent
244{
246 GivenComponent(One one, Space whole, Value const & given)
247 : measurement{std::move(one)}
248 , space{std::move(whole)}
249 , pinned{space, measurement}
250 , value{given}
251 {}
252
254 GivenComponent(GivenComponent const & other)
255 : GivenComponent{other.measurement, other.space, other.value}
256 {}
257
258 One measurement;
259 Space space;
260 PinnedMeasurement<Space, P, One> pinned;
261 Value const & value;
262};
263
264} // namespace internal
265
307template <
308 Component Component_,
309 typename Model_,
310 typename Method_ = DefaultMethodFor<Model_>,
311 typename Allocator_ = DefaultAllocator>
313{
314 static_assert(
315 internal::predictsBy<Model_, Method_>,
316 "a filter with fixed-step models predicts each mode by its model's own method, so it takes "
317 "ModelsMethod, the default, as its method"
318 );
319 static_assert(internal::checkPredictionMethod<Method_>());
320
322 static constexpr bool IsForm = internal::isOtherGaussianForm<Params<Component_>>;
323
324public:
325
328 using ModelType = Model_;
329 using MethodType = Method_;
330 using ModeSpaceType = typename Model_::ModeSpaceType;
333 using allocator_type = Allocator_;
334
337
338 static constexpr bool Reentrant = false;
339
343 template <Measurement M>
344 using MeasuredType = typename internal::ImmMeasured<ComponentType, M>::type;
345
349 template <Measurement M>
351 std::conditional_t<
352 internal::isByMode<M>,
354 std::conditional_t<
355 IsForm,
357 internal::FilterUpdateWithScratch<
358 SpaceType,
361 Scalar,
362 Allocator_>>>,
363 Scalar,
364 Allocator_>;
365
367 template <Measurement M>
368 using UpdateType = std::expected<ResultType<M>, NumericError>;
369
371 template <Measurement M>
373 {
375 };
376
380 template <typename S>
382
386 template <typename S>
387 using SwitchingUpdateType = std::expected<SwitchingResultType<S>, NumericError>;
388
392 template <Measurement M, typename Space, typename P>
393 using GivenMeasuredType = typename decltype(internal::updateBranchesWith<true>(
394 std::declval<ComponentType const &>(),
395 std::declval<internal::PinnedMeasurement<Space, P, M> const &>(),
396 std::declval<typename Moments<internal::PathTag<P>, Scalar>::VectorType const &>()
397 ))::value_type;
398
402 template <Measurement M, typename Space, typename P>
404 std::conditional_t<
405 internal::isByMode<M>,
407 internal::FilterUpdateWithScratch<
408 SpaceType,
411 Scalar,
412 Allocator_>>,
413 Scalar,
414 Allocator_>;
415
417 template <Measurement M, typename Space, typename P>
418 using GivenUpdateType = std::expected<GivenResultType<M, Space, P>, NumericError>;
419
424 explicit ImmRecursion(Model_ model, MethodFor<Model_, Method_> method = {}, Allocator_ const & allocator = {})
425 : m_model{std::move(model)}
426 , m_method{internal::predictionMethod(m_model, std::move(method))}
427 , m_predicted(ComponentAllocator(allocator))
428 , m_posterior(ComponentAllocator(allocator))
429 {
430 reserveScratch();
431 }
432
434 [[nodiscard]] allocator_type
436 {
437 return allocator_type(m_predicted.get_allocator());
438 }
439
443 void
444 resetScratch(Allocator_ const & allocator)
445 {
446 m_predicted = ConditionalsType(ComponentAllocator(allocator));
447 m_posterior = ConditionalsType(ComponentAllocator(allocator));
448 reserveScratch();
449 }
450
452 [[nodiscard]] Model_ const &
453 model() const
454 {
455 return m_model;
456 }
457
459 [[nodiscard]] Model_ &
461 {
462 return m_model;
463 }
464
468 [[nodiscard]] Method_ const &
469 method() const
470 {
471 return m_method;
472 }
473
475 [[nodiscard]] ModeSpaceType const &
476 modeSpace() const
477 {
478 return m_model.modeSpace();
479 }
480
482 [[nodiscard]] bool
483 partitions(SpaceType const & space) const
484 {
485 return m_model.partitions(space);
486 }
487
496 template <typename... Inputs>
497 requires(!isFixedStep<Model_>)
498 [[nodiscard]] auto
499 predict(StateType & state, TimeStep<Scalar> auto const & dt, Inputs const &... inputs)
500 {
501 Scalar const step = internal::toScalarTime<Scalar>(dt);
502 if constexpr (IsForm)
503 {
504 return moveModes(state, [&](ComponentType & moved, auto const & model, auto const & method) {
505 return internal::predictInPlace(method, moved, model, step, inputs...);
506 });
507 }
508 else
509 {
510 moveModes(state, [&](ComponentType const & mixed, auto const & model, auto const & method) {
511 return Emrys::predict(method, mixed, model, step, inputs...);
512 });
513 }
514 }
515
520 template <typename... Inputs>
521 requires isFixedStep<Model_>
522 [[nodiscard]] auto
523 predict(StateType & state, Inputs const &... inputs)
524 {
525 if constexpr (IsForm)
526 {
527 return moveModes(state, [&](ComponentType & moved, auto const & model, auto const &) {
528 return internal::predictInPlace(moved, model, inputs...);
529 });
530 }
531 else
532 {
533 moveModes(state, [&](ComponentType const & mixed, auto const & model, auto const &) {
534 return Emrys::predict(mixed, model, inputs...);
535 });
536 }
537 }
538
544 template <typename... Inputs>
545 requires(!isFixedStep<Model_> && !IsForm)
546 [[nodiscard]] JointType
547 predictJoint(StateType & state, TimeStep<Scalar> auto const & dt, Inputs const &... inputs)
548 {
549 auto joint = emptyJoint(state);
550 predictJoint(state, dt, joint, inputs...);
551 return joint;
552 }
553
557 template <typename JointAllocator, typename... Inputs>
558 requires(!isFixedStep<Model_> && !IsForm)
559 void
561 StateType & state,
562 TimeStep<Scalar> auto const & dt,
564 Inputs const &... inputs
565 )
566 {
567 Scalar const step = internal::toScalarTime<Scalar>(dt);
568 recordJoint(
569 state,
570 joint,
571 [&](ComponentType const & filtered, auto const & model, auto const & method) {
572 return Emrys::predictJoint(method, filtered, model, step, inputs...);
573 },
574 [&](ComponentType const & filtered, auto const & model, auto const & method) {
575 return Emrys::predict(method, filtered, model, step, inputs...);
576 }
577 );
578 predict(state, step, inputs...);
579 }
580
585 template <typename... Inputs>
586 requires(isFixedStep<Model_> && !IsForm)
587 [[nodiscard]] JointType
588 predictJoint(StateType & state, Inputs const &... inputs)
589 {
590 auto joint = emptyJoint(state);
591 predictJoint(state, joint, inputs...);
592 return joint;
593 }
594
596 template <typename JointAllocator, typename... Inputs>
597 requires(isFixedStep<Model_> && !IsForm)
598 void
600 StateType & state,
602 Inputs const &... inputs
603 )
604 {
605 recordJoint(
606 state,
607 joint,
608 [&](ComponentType const & filtered, auto const & model, auto const &) {
609 return Emrys::predictJoint(filtered, model, inputs...);
610 },
611 [&](ComponentType const & filtered, auto const & model, auto const &) {
612 return Emrys::predict(filtered, model, inputs...);
613 }
614 );
615 predict(state, inputs...);
616 }
617
633 template <Measurement M, typename Policy = reductions::Collapse>
634 requires(!internal::isGivenBlock<Policy>)
635 [[nodiscard]] typename internal::UnlessGivenBlock<UpdateTypeOf<M>, Policy>::type
636 update(StateType & state, M const & measurement, Policy const & policy = {})
637 {
638 auto result = emptyUpdate<M>(get_allocator(), state.size());
639 auto const updated = update(state, measurement, policy, result);
640 if (!updated)
641 {
642 return std::unexpected{updated.error()};
643 }
644 return result;
645 }
646
653 template <Measurement M, typename Policy, typename ModeUpdate, typename ResultAllocator>
654 [[nodiscard]] std::expected<void, NumericError>
656 StateType & state,
657 M const & measurement,
658 Policy const & policy,
660 )
661 {
662 if constexpr (internal::isByMode<M>)
663 {
664 return updateByAxis(state, measurement, policy, result, [](auto one) {
665 return one;
666 });
667 }
668 else
669 {
670 return updateModes(
671 state,
672 policy,
673 result,
674 [&](Eigen::Index, auto const & updateMode) {
675 return updateMode(measurement);
676 },
677 [](auto &, Eigen::Index) {}
678 );
679 }
680 }
681
687 template <typename P, Measurement... Ms, typename Policy = reductions::Collapse>
688 [[nodiscard]] SwitchingUpdateType<SwitchingMeasurement<P, Ms...>>
689 update(StateType & state, SwitchingMeasurement<P, Ms...> const & measurement, Policy const & policy = {})
690 {
691 auto result = SwitchingResultType<SwitchingMeasurement<P, Ms...>>{get_allocator()};
692 result.modes.reserve(std::size_t(state.size()));
693 auto const updated = update(state, measurement, policy, result);
694 if (!updated)
695 {
696 return std::unexpected{updated.error()};
697 }
698 return result;
699 }
700
706 template <typename P, Measurement... Ms, typename Policy, typename ModeUpdate, typename ResultAllocator>
707 [[nodiscard]] std::expected<void, NumericError>
709 StateType & state,
710 SwitchingMeasurement<P, Ms...> const & measurement,
711 Policy const & policy,
713 )
714 {
715 static_assert(
716 internal::pathResolves<P, ModeSpaceType>(),
717 "a switching measurement is selected by a mode axis that the filter's mode space does not have"
718 );
719 static_assert(
720 !(internal::isByMode<Ms> || ...),
721 "a measurement of a switching measurement has noise by mode axis, byMode<Axis>(): give each value's "
722 "measurement the noise it has in the modes with that value"
723 );
724 static_assert(
725 (std::is_same_v<ResultType<Ms>, SwitchingResultType<SwitchingMeasurement<P, Ms...>>> && ...),
726 "the measurements of a switching measurement update a mode into one type: give them one measurement "
727 "space, and noise that is Gaussian in all of them or a mixture in all of them"
728 );
729 if constexpr (internal::pathResolves<P, ModeSpaceType>())
730 {
731 auto const [stride, axis] = internal::locateAxisByPath<P>(state.modeSpace());
732 Eigen::Index const values = axis->cardinality();
733 EMRYS_FILTER_ASSERT(
734 values == Eigen::Index(sizeof...(Ms)) && "give one measurement per value of the axis that selects it"
735 );
736 return updateModes(
737 state,
738 policy,
739 result,
740 [&, stride](Eigen::Index m, auto const & updateMode) {
741 return measurement.visit(std::size_t((m / stride) % values), updateMode);
742 },
743 [](auto &, Eigen::Index) {}
744 );
745 }
746 else
747 {
748 return std::expected<void, NumericError>{};
749 }
750 }
751
752private:
753
759 template <Measurement M, typename Policy, typename ModeUpdate, typename ResultAllocator, typename Wrap>
760 [[nodiscard]] std::expected<void, NumericError>
761 updateByAxis(
762 StateType & state,
763 M const & measurement,
764 Policy const & policy,
766 Wrap const & wrap
767 )
768 {
769 using Axis = typename internal::MeasurementModeAxisOf<M>::type;
770 static_assert(
771 internal::pathResolves<Axis, ModeSpaceType>(),
772 "the measurement's noise is by a mode axis, byMode<Axis>(), that the filter's mode space does not have"
773 );
774 if constexpr (internal::pathResolves<Axis, ModeSpaceType>())
775 {
776 return updateByAxisIn<Axis>(state, measurement, policy, result, wrap);
777 }
778 else
779 {
780 return std::expected<void, NumericError>{};
781 }
782 }
783
785 template <
786 typename Axis,
787 Measurement M,
788 typename Policy,
789 typename ModeUpdate,
790 typename ResultAllocator,
791 typename Wrap>
792 [[nodiscard]] std::expected<void, NumericError>
793 updateByAxisIn(
794 StateType & state,
795 M const & measurement,
796 Policy const & policy,
797 ImmFilterUpdate<ModeUpdate, Scalar, ResultAllocator> & result,
798 Wrap const & wrap
799 )
800 {
801 auto const [stride, axis] = internal::locateAxisByPath<Axis>(state.modeSpace());
802 Eigen::Index const values = axis->cardinality();
803 auto const & noise = measurement.model().template policy<internal::PolicyKind::additiveNoise>().distribution();
804 EMRYS_FILTER_ASSERT(noise.size() == values && "the noise needs one component per value of the mode axis");
805 using Component = std::remove_cvref_t<decltype(noise.begin()->component)>;
806 using One = internal::WithNoiseComponent<M, Component>;
807 // Each mode is updated with the one component its value names, not with every component.
808 auto const componentOf = [=](Eigen::Index m) {
809 return (m / stride) % values;
810 };
811 return updateModes(
812 state,
813 policy,
814 result,
815 [&](Eigen::Index m, auto const & updateMode) {
816 return updateMode(wrap(One{measurement, noise[componentOf(m)].component}));
817 },
818 [&](auto & measured, Eigen::Index m) {
819 auto & branch = measured.branches.front();
820 branch.component = componentOf(m);
821 branch.logWeight = Scalar(0);
822 }
823 );
824 }
825
826public:
827
836 template <Measurement M, typename Space, typename P, typename Value, typename Policy = reductions::Collapse>
837 requires(!IsForm)
838 [[nodiscard]] GivenUpdateType<M, Space, P>
840 StateType & state,
841 M const & measurement,
842 GivenBlock<Space, P, Value> const & pinned,
843 Policy const & policy = {}
844 )
845 {
846 auto result = emptyGivenUpdate<M, Space, P>(get_allocator(), state.size());
847 auto const updated = update(state, measurement, pinned, policy, result);
848 if (!updated)
849 {
850 return std::unexpected{updated.error()};
851 }
852 return result;
853 }
854
859 template <
860 Measurement M,
861 typename Space,
862 typename P,
863 typename Value,
864 typename Policy,
865 typename ModeUpdate,
866 typename ResultAllocator>
867 requires(!IsForm)
868 [[nodiscard]] std::expected<void, NumericError>
870 StateType & state,
871 M const & measurement,
872 GivenBlock<Space, P, Value> const & pinned,
873 Policy const & policy,
875 )
876 {
877 if constexpr (internal::isByMode<M>)
878 {
879 // Each mode's measurement holds its own noise component, so each resolves its own.
880 return updateByAxis(state, measurement, policy, result, [&]<typename One>(One one) {
881 return internal::GivenComponent<Space, P, One, Value>{std::move(one), pinned.space, pinned.value};
882 });
883 }
884 else
885 {
886 auto const f = internal::PinnedMeasurement<Space, P, M>{pinned.space, measurement};
887 return updateGiven(state, f, pinned.value, policy, result);
888 }
889 }
890
897 template <
898 typename Space,
899 typename P,
900 typename M,
901 typename Value,
902 typename Policy,
903 typename ModeUpdate,
904 typename ResultAllocator>
905 requires(!IsForm && !internal::isByMode<M>)
906 [[nodiscard]] std::expected<void, NumericError>
908 StateType & state,
909 internal::PinnedMeasurement<Space, P, M> const & f,
910 Value const & value,
911 Policy const & policy,
913 )
914 {
915 using Given = internal::GivenMeasurement<internal::PinnedMeasurement<Space, P, M> const &, Value>;
916 return updateModes(
917 state,
918 policy,
919 result,
920 [&](Eigen::Index, auto const & updateMode) {
921 return updateMode(Given{f, value});
922 },
923 [](auto &, Eigen::Index) {}
924 );
925 }
926
930 template <Measurement M>
931 [[nodiscard]] ResultType<M>
932 emptyUpdate(Allocator_ const & allocator, Eigen::Index modes) const
933 {
934 using Result = ResultType<M>;
935 auto result = Result{allocator};
936 result.modes.reserve(std::size_t(modes));
937 return result;
938 }
939
943 template <Measurement M, typename Space, typename P>
944 [[nodiscard]] GivenResultType<M, Space, P>
945 emptyGivenUpdate(Allocator_ const & allocator, Eigen::Index modes) const
946 {
947 auto result = GivenResultType<M, Space, P>{allocator};
948 result.modes.reserve(std::size_t(modes));
949 return result;
950 }
951
953 [[nodiscard]] JointType
954 emptyJoint(StateType const & state) const
955 {
956 auto joint = JointType{
957 state.modes(),
958 typename JointType::JointsType(state.get_allocator()),
959 m_model.chain().matrix(),
960 typename JointType::PredictionsType(state.get_allocator())
961 };
962 joint.joints.reserve(std::size_t(state.size()));
963 joint.throughOtherModels.reserve(std::size_t(state.size() * (state.size() - 1)));
964 return joint;
965 }
966
967private:
968
969 using ConditionalsType = typename StateType::ConditionalsType;
970 using ComponentAllocator = typename ConditionalsType::allocator_type;
971
973 void
974 reserveScratch()
975 {
976 auto const modes = std::size_t(m_model.modeSpace().cardinality());
977 m_predicted.reserve(modes);
978 if constexpr (!IsForm)
979 {
980 m_posterior.reserve(modes);
981 }
982 }
983
993 template <typename PredictMode>
994 [[nodiscard]] auto
995 moveModes(StateType & state, PredictMode const & predictMode)
996 {
997 if constexpr (IsForm)
998 {
999 auto failure = std::expected<void, NumericError>{};
1000 auto const keepFirst = [&](std::expected<void, NumericError> const & moved) {
1001 if (!moved && failure)
1002 {
1003 failure = moved;
1004 }
1005 };
1006 moveModesBy(
1007 state,
1008 [&](Eigen::Index j, auto const & model, auto const & method) {
1009 keepFirst(state.updateConditional(j, [&](ComponentType & own) {
1010 return predictMode(own, model, method);
1011 }));
1012 },
1013 [&](ComponentType && mixed, auto const & model, auto const & method) {
1014 m_predicted.push_back(std::move(mixed));
1015 keepFirst(predictMode(m_predicted.back(), model, method));
1016 }
1017 );
1018 return failure;
1019 }
1020 else
1021 {
1022 moveModesBy(
1023 state,
1024 [&](Eigen::Index j, auto const & model, auto const & method) {
1025 m_predicted.push_back(predictMode(state.conditional(j), model, method));
1026 },
1027 [&](ComponentType && mixed, auto const & model, auto const & method) {
1028 m_predicted.push_back(predictMode(mixed, model, method));
1029 }
1030 );
1031 }
1032 }
1033
1038 template <typename MoveOwn, typename MoveMixed>
1039 void
1040 moveModesBy(StateType & state, MoveOwn const & moveOwn, MoveMixed const & moveMixed)
1041 {
1042 Eigen::Index const count = state.size();
1043 auto const & logPrior = state.modes().logWeights();
1044 auto const & transitions = m_model.chain().matrix();
1045 m_predicted.clear();
1046 if (transitions.isIdentity(Scalar(0)))
1047 {
1048 // The autonomous multiple model estimator: each mode's mixing puts all its weight on
1049 // its own component, so each is predicted as it is, and the probabilities carry over.
1050 for (Eigen::Index j = 0; j < count; ++j)
1051 {
1052 m_model.visit(j, m_method, [&](auto const & model, auto const & method) {
1053 moveOwn(j, model, method);
1054 });
1055 }
1056 if constexpr (!IsForm)
1057 {
1058 state.swapConditionals(m_predicted);
1059 }
1060 return;
1061 }
1062 CategoricalParams<ModeSpaceType, Scalar> const predictedModes = Emrys::predict(state.modes(), m_model.chain());
1063
1064 auto const conditional = [&](Eigen::Index i) -> ComponentType const & {
1065 return state.conditional(i);
1066 };
1067 auto logMixing = typename StateType::ModesType::ArrayType{};
1068 logMixing.resize(count);
1069 for (Eigen::Index j = 0; j < count; ++j)
1070 {
1071 // A mode the chain can't reach starts from its own component, which is never weighted.
1072 bool const reachable = predictedModes.logWeight(j) > -Eigen::NumTraits<Scalar>::infinity();
1073 for (Eigen::Index i = 0; i < count; ++i)
1074 {
1075 logMixing(i) = reachable ? Eigen::numext::log(transitions(i, j)) + logPrior(i)
1076 : (i == j ? Scalar(0) : -Eigen::NumTraits<Scalar>::infinity());
1077 }
1078 ComponentType mixed = ComponentTraits<ComponentType>::collapse(count, conditional, [&](Eigen::Index i) {
1079 return logMixing(i);
1080 });
1081 m_model.visit(j, m_method, [&](auto const & model, auto const & method) {
1082 moveMixed(std::move(mixed), model, method);
1083 });
1084 }
1085 state.swapConditionals(m_predicted);
1086 state.setModes(predictedModes);
1087 }
1088
1094 template <typename JointAllocator, typename PredictMode, typename PredictThrough>
1095 void
1096 recordJoint(
1097 StateType const & state,
1098 HybridPredictedJoint<SpaceType, ModeSpaceType, Scalar, JointAllocator> & joint,
1099 PredictMode const & predictMode,
1100 PredictThrough const & predictThrough
1101 ) const
1102 {
1103 Eigen::Index const count = state.size();
1104 joint.modes = state.modes();
1105 joint.transitions = m_model.chain().matrix();
1106 joint.joints.clear();
1107 joint.joints.reserve(std::size_t(count));
1108 joint.throughOtherModels.clear();
1109 joint.throughOtherModels.reserve(std::size_t(count * (count - 1)));
1110 for (Eigen::Index j = 0; j < count; ++j)
1111 {
1112 m_model.visit(j, m_method, [&](auto const & model, auto const & method) {
1113 joint.joints.push_back(predictMode(state.conditional(j), model, method));
1114 });
1115 for (Eigen::Index i = 0; i < count; ++i)
1116 {
1117 if (i != j)
1118 {
1119 m_model.visit(i, m_method, [&](auto const & model, auto const & method) {
1120 joint.throughOtherModels.push_back(predictThrough(state.conditional(j), model, method));
1121 });
1122 }
1123 }
1124 }
1125 }
1126
1145 template <
1146 typename Policy,
1147 typename ModeUpdate,
1148 typename ResultAllocator,
1149 typename WithMeasurement,
1150 typename Relabel>
1151 [[nodiscard]] std::expected<void, NumericError>
1152 updateModes(
1153 StateType & state,
1154 Policy const & policy,
1155 ImmFilterUpdate<ModeUpdate, Scalar, ResultAllocator> & result,
1156 WithMeasurement const & withMeasurement,
1157 Relabel const & relabel
1158 )
1159 {
1160 // Mode probabilities the caller left undefined fail the update before any mode is written.
1161 auto const logTotal = state.modes().logTotalWeight();
1162 if (!logTotal)
1163 {
1164 return std::unexpected{logTotal.error()};
1165 }
1166 Eigen::Index const count = state.size();
1167 Eigen::Index const held = std::min(Eigen::Index(result.modes.size()), count);
1168 result.modes.erase(result.modes.begin() + held, result.modes.end());
1169 internal::UpdateAccess::logLikelihood(result) = Scalar(0);
1170 result.accepted = false;
1171 m_posterior.clear();
1172 auto logLikelihoods = typename StateType::ModesType::ArrayType{};
1173 logLikelihoods.resize(count);
1174 for (Eigen::Index m = 0; m < count; ++m)
1175 {
1176 auto const updateMode = [&](auto const & measurement) -> std::expected<void, NumericError> {
1177 if constexpr (IsForm)
1178 {
1179 static_assert(
1180 internal::isCollapsePolicy<Policy>,
1181 "a square-root form has one branch and takes the Collapse policy alone; gating and selection "
1182 "among noise components are the covariance form's (decisions.md [gaussian-forms])"
1183 );
1184 auto updated = state.updateConditional(m, [&](ComponentType & conditional) {
1185 return internal::updateInPlace<true>(conditional, measurement);
1186 });
1187 if (!updated)
1188 {
1189 result.accepted = false;
1190 return std::unexpected{updated.error()};
1191 }
1192 relabel(*updated, m);
1193 updated->branches.front().mode = m;
1194 logLikelihoods(m) = updated->logLikelihood();
1195 result.accepted = true;
1196 if (std::size_t(m) == result.modes.size())
1197 {
1198 result.modes.push_back(std::move(*updated));
1199 }
1200 else
1201 {
1202 result.modes[std::size_t(m)] = std::move(*updated);
1203 }
1204 }
1205 else
1206 {
1207 auto const updated = internal::updateSlot<true>(
1208 result.modes,
1209 std::size_t(m),
1210 ResultAllocator(result.modes.get_allocator()),
1211 state.conditional(m),
1212 measurement,
1213 [&](auto & measured) {
1214 relabel(measured, m);
1215 for (auto & branch : measured.branches)
1216 {
1217 branch.mode = m;
1218 }
1219 static_assert(
1220 ReductionPolicy<Policy, std::remove_cvref_t<decltype(measured)>, ComponentType>,
1221 "an IMM filter keeps one component per mode, so its policy must keep one: Collapse, "
1222 "Select, or a Gate over them"
1223 );
1224 auto kept = std::optional<ComponentType>{};
1225 auto out =
1226 internal::OneComponentOutput<ComponentType, Scalar, std::optional<ComponentType>>{kept};
1227 internal::reduceInto(policy, std::move(measured.branches), out);
1228 // The mode is weighted by the likelihood of the branches what it kept stands for.
1229 logLikelihoods(m) = out.added ? measured.logLikelihood() + out.logWeight
1230 : -Eigen::NumTraits<Scalar>::infinity();
1231 m_posterior.push_back(out.added ? std::move(*kept) : state.conditional(m));
1232 result.accepted = result.accepted || out.added;
1233 return out.added;
1234 }
1235 );
1236 if (!updated)
1237 {
1238 result.accepted = false;
1239 return updated;
1240 }
1241 }
1242 return {};
1243 };
1244 if (auto const updated = withMeasurement(m, updateMode); !updated)
1245 {
1246 return updated;
1247 }
1248 }
1249 auto evidence = LogSumExp<Scalar>{};
1250 for (Eigen::Index m = 0; m < count; ++m)
1251 {
1252 evidence.add(state.modes().logWeight(m) + result.modes[std::size_t(m)].logLikelihood());
1253 }
1254 internal::UpdateAccess::logLikelihood(result) = evidence.value() - *logTotal;
1255 if (!result.accepted)
1256 {
1257 return {};
1258 }
1259
1260 CategoricalParams<ModeSpaceType, Scalar> modes = state.modes();
1261 if (!modes.update(logLikelihoods))
1262 {
1263 // Every accepting mode has probability zero: the measurement has no density under the
1264 // modes that could hold it, so it is rejected [update-outcomes].
1265 result.accepted = false;
1266 for (auto & mode : result.modes)
1267 {
1268 mode.accepted = false;
1269 }
1270 return {};
1271 }
1272 if constexpr (!IsForm)
1273 {
1274 state.swapConditionals(m_posterior);
1275 }
1276 state.setModes(std::move(modes));
1277 return {};
1278 }
1279
1280 Model_ m_model;
1281 EMRYS_FILTER_NO_UNIQUE_ADDRESS Method_ m_method;
1282
1283 // Scratch, exchanged with the state's components: the predicted ones and the posterior ones.
1284 ConditionalsType m_predicted;
1285
1286 ConditionalsType m_posterior;
1287};
1288
1289namespace internal
1290{
1291
1293template <
1294 Component Component_,
1295 DiscreteSpace ModeSpace_,
1296 typename HybridAllocator,
1297 typename Model,
1298 typename Method,
1299 typename Allocator>
1300struct RecursionFor<Hybrid<Component_, ModeSpace_, HybridAllocator>, Model, Method, Allocator>
1301{
1302 using type = ImmRecursion<Component_, Model, Method, HybridAllocator>;
1303};
1304
1306
1307} // namespace internal
1308
1375template <
1376 Component Component_,
1377 typename Model_,
1378 typename Method_ = DefaultMethodFor<Model_>,
1379 typename Allocator_ = DefaultAllocator,
1380 typename Upkeep_ = upkeep::None>
1381class ImmFilter : public internal::HeldUpkeep<ImmFilter<Component_, Model_, Method_, Allocator_, Upkeep_>, Upkeep_>
1382{
1383 using UpkeepBase = internal::HeldUpkeep<ImmFilter, Upkeep_>;
1384
1385public:
1386
1389
1392 using ModelType = Model_;
1393 using MethodType = Method_;
1397 using allocator_type = Allocator_;
1399
1405 Model_ model,
1408 std::type_identity_t<Upkeep_> upkeep = {}
1409 )
1410 : UpkeepBase{std::move(upkeep)}
1411 , m_state{std::move(state)}
1412 , m_recursion{std::move(model), std::move(method), m_state.get_allocator()}
1413 {
1414 EMRYS_FILTER_ASSERT(
1415 m_recursion.modeSpace() == m_state.modeSpace() && "the model and the state have different modes"
1416 );
1417 internal::assertPartitions(
1418 m_recursion.model(),
1419 m_state.space(),
1420 "every process model must write every coordinate of the state space once"
1421 );
1422 }
1423
1427 ImmFilter(Model_ model, StateType state, std::type_identity_t<Upkeep_> upkeep)
1428 requires(!std::same_as<Upkeep_, Emrys::upkeep::None>)
1429 : ImmFilter{std::move(model), std::move(state), MethodFor<Model_, Method_>{}, std::move(upkeep)}
1430 {}
1431
1433 [[nodiscard]] allocator_type
1435 {
1436 return m_state.get_allocator();
1437 }
1438
1440 [[nodiscard]] StateType const &
1441 state() const
1442 {
1443 return m_state;
1444 }
1445
1450 [[nodiscard]] StateType &
1452 {
1453 return m_state;
1454 }
1455
1460 void
1462 {
1463 EMRYS_FILTER_ASSERT(state.modeSpace() == m_recursion.modeSpace() && "the state has different modes");
1464 internal::assertPartitions(
1465 m_recursion.model(),
1466 state.space(),
1467 "every process model must write every coordinate of the state space once"
1468 );
1469 m_state = std::move(state);
1470 if (m_state.get_allocator() != m_recursion.get_allocator())
1471 {
1472 // The assignment carried the new state's allocator in, so the scratch follows it.
1473 m_recursion.resetScratch(m_state.get_allocator());
1474 }
1475 }
1476
1478 [[nodiscard]] RecursionType const &
1480 {
1481 return m_recursion;
1482 }
1483
1485 [[nodiscard]] Model_ const &
1486 model() const
1487 {
1488 return m_recursion.model();
1489 }
1490
1492 [[nodiscard]] Model_ &
1494 {
1495 return m_recursion.model();
1496 }
1497
1501 [[nodiscard]] Method_ const &
1502 method() const
1503 {
1504 return m_recursion.method();
1505 }
1506
1514 template <typename... Inputs>
1515 requires(!isFixedStep<Model_>)
1516 [[nodiscard]] auto
1517 predict(TimeStep<Scalar> auto const & dt, Inputs const &... inputs)
1518 {
1519 return m_recursion.predict(m_state, internal::toScalarTime<Scalar>(dt), inputs...);
1520 }
1521
1526 template <typename... Inputs>
1527 requires(isFixedStep<Model_> && !internal::leadsWithSteps<Inputs...>)
1528 [[nodiscard]] auto
1529 predict(Inputs const &... inputs)
1530 {
1531 internal::rejectTimeStep<Inputs...>();
1532 return m_recursion.predict(m_state, inputs...);
1533 }
1534
1539 template <typename... Inputs>
1540 requires isFixedStep<Model_>
1541 [[nodiscard]] auto
1542 predict(Steps steps, Inputs const &... inputs)
1543 {
1544 EMRYS_FILTER_ASSERT(steps.count >= 0 && "the number of steps must be nonnegative");
1545 using Result = decltype(m_recursion.predict(m_state, inputs...));
1546 if constexpr (std::is_void_v<Result>)
1547 {
1548 for (Eigen::Index i = 0; i < steps.count; ++i)
1549 {
1550 m_recursion.predict(m_state, inputs...);
1551 }
1552 }
1553 else
1554 {
1555 for (Eigen::Index i = 0; i < steps.count; ++i)
1556 {
1557 if (Result result = m_recursion.predict(m_state, inputs...); !result)
1558 {
1559 return result;
1560 }
1561 }
1562 return Result{};
1563 }
1564 }
1565
1571 template <typename... Inputs>
1572 requires(!isFixedStep<Model_>)
1573 [[nodiscard]] JointType
1574 predictJoint(TimeStep<Scalar> auto const & dt, Inputs const &... inputs)
1575 {
1576 return m_recursion.predictJoint(m_state, internal::toScalarTime<Scalar>(dt), inputs...);
1577 }
1578
1582 template <typename JointAllocator, typename... Inputs>
1583 requires(!isFixedStep<Model_>)
1584 void
1586 TimeStep<Scalar> auto const & dt,
1588 Inputs const &... inputs
1589 )
1590 {
1591 m_recursion.predictJoint(m_state, internal::toScalarTime<Scalar>(dt), joint, inputs...);
1592 }
1593
1598 template <typename... Inputs>
1599 requires(isFixedStep<Model_> && !internal::leadsWithSteps<Inputs...>)
1600 [[nodiscard]] JointType
1601 predictJoint(Inputs const &... inputs)
1602 {
1603 internal::rejectTimeStep<Inputs...>();
1604 return m_recursion.predictJoint(m_state, inputs...);
1605 }
1606
1608 template <typename JointAllocator, typename... Inputs>
1609 requires isFixedStep<Model_>
1610 void
1613 Inputs const &... inputs
1614 )
1615 {
1616 m_recursion.predictJoint(m_state, joint, inputs...);
1617 }
1618
1628 template <Measurement M, typename Policy = reductions::Collapse>
1629 [[nodiscard]] auto
1630 update(M const & measurement, Policy const & policy = {})
1631 {
1632 return m_recursion.update(m_state, measurement, policy);
1633 }
1634
1640 template <Measurement M, typename Policy, typename ModeUpdate, typename ResultAllocator>
1641 [[nodiscard]] std::expected<void, NumericError>
1642 update(M const & measurement, Policy const & policy, ImmFilterUpdate<ModeUpdate, Scalar, ResultAllocator> & result)
1643 {
1644 return m_recursion.update(m_state, measurement, policy, result);
1645 }
1646
1651 template <typename P, Measurement... Ms, typename Policy = reductions::Collapse>
1652 [[nodiscard]] auto
1653 update(SwitchingMeasurement<P, Ms...> const & measurement, Policy const & policy = {})
1654 {
1655 return m_recursion.update(m_state, measurement, policy);
1656 }
1657
1663 template <typename P, Measurement... Ms, typename Policy, typename ModeUpdate, typename ResultAllocator>
1664 [[nodiscard]] std::expected<void, NumericError>
1666 SwitchingMeasurement<P, Ms...> const & measurement,
1667 Policy const & policy,
1669 )
1670 {
1671 return m_recursion.update(m_state, measurement, policy, result);
1672 }
1673
1674private:
1675
1676 StateType m_state;
1677 RecursionType m_recursion;
1678};
1679
1681// One guide with a defaulted method, its parameters in the class's order.
1682template <
1683 Component C,
1684 typename Model,
1685 typename Method = DefaultMethodFor<Model>,
1686 typename Allocator,
1687 typename ModeSpace>
1688 requires(!internal::UpkeepArgument<Method>)
1689ImmFilter(Model, HybridParams<C, ModeSpace, Allocator>, Method = {}) -> ImmFilter<
1690 C,
1691 internal::BoundStep<Model, typename Params<C>::SpaceType, typename Params<C>::Scalar>,
1692 Method,
1693 Allocator>;
1694
1695template <Component C, typename Model, typename Allocator, typename ModeSpace, typename U>
1696 requires internal::UpkeepArgument<U>
1697ImmFilter(Model, HybridParams<C, ModeSpace, Allocator>, U) -> ImmFilter<
1698 C,
1699 internal::BoundStep<Model, typename Params<C>::SpaceType, typename Params<C>::Scalar>,
1700 DefaultMethodFor<Model>,
1701 Allocator,
1702 U>;
1703
1704template <Component C, typename Model, typename Method, typename Allocator, typename ModeSpace, typename U>
1705ImmFilter(Model, HybridParams<C, ModeSpace, Allocator>, Method, U) -> ImmFilter<
1706 C,
1707 internal::BoundStep<Model, typename Params<C>::SpaceType, typename Params<C>::Scalar>,
1708 Method,
1709 Allocator,
1710 U>;
1712
1713} // namespace Emrys
1714
1715#ifndef EMRYS_FILTER_NO_IO
1716
1717namespace Emrys
1718{
1719
1720namespace internal
1721{
1722
1724template <typename Component_, typename Model_, typename Method_, typename Allocator_, typename Upkeep_>
1725void
1726describe(
1727 std::ostream & stream,
1728 ImmFilter<Component_, Model_, Method_, Allocator_, Upkeep_> const & filter,
1729 Indent indent
1730)
1731{
1732 describeFilter(stream, "interacting multiple model filter", filter.state(), indent);
1733}
1734
1735} // namespace internal
1736
1737} // namespace Emrys
1738
1739#endif // EMRYS_FILTER_NO_IO
The allocator conventions of decisions.md [allocation]: DefaultAllocator, Capacity,...
The distribution over a discrete space (decisions.md [discrete-spaces], [params-and-density]): Catego...
What a container of several Gaussians needs of each one (decisions.md [component-first]): the Compone...
Discrete spaces (decisions.md [discrete-spaces]): the DiscreteSpace concept; Finite,...
The upkeep a filter holds (decisions.md [upkeep]): internal::HeldUpkeep, the base that gives every fi...
The Gaussian: its parameters GaussianParams, the Gaussian that evaluates the density,...
The hybrid state of a discrete mode and a component per mode (decisions.md [imm], [component-first]):...
KalmanRecursion and KalmanFilter (decisions.md [templated-measurements] and [filters-and-recursions])...
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]...
Printing: operator<< and std::formatter for every distribution, every filter (which prints its name a...
Process models as functions with reads and writes (decisions.md [model-policies], [continuous-time-no...
Reducing an update's branches (decisions.md [weighted-branches], [reduction-policies],...
The Space concept and what every continuous space shares (decisions.md [sizes-and-dimensions]): the t...
SwitchingModel (decisions.md [switching-models], [imm]): a Markov chain on a discrete mode space and ...
Propagating a distribution through a function (decisions.md [method-per-measurement],...
Log-weights: the WeightError a normalization can fail with (NumericError.h), and the streaming log-su...
The parameters of a probability distribution over a discrete space: one weight per value.
Definition Categorical.h:257
A Gaussian distribution: a mean and covariance in a space.
Definition Gaussian.h:259
The parameters of a hybrid state: a discrete mode, and a component conditioned on each mode.
Definition Hybrid.h:86
internal::Buffer< ComponentType, Allocator_ > ConditionalsType
Definition Hybrid.h:105
RecursionType const & recursion() const
Definition ImmFilter.h:1479
Model_ & model()
Definition ImmFilter.h:1493
void setState(StateType state)
Replaces the state, as when reinitializing, with one that has the model's modes and whose space every...
Definition ImmFilter.h:1461
typename RecursionType::StateType StateType
Definition ImmFilter.h:1395
void predictJoint(TimeStep< Scalar > auto const &dt, HybridPredictedJoint< SpaceType, ModeSpaceType, Scalar, JointAllocator > &joint, Inputs const &... inputs)
As predictJoint(dt, inputs...), but into joint, whose storage is reused: with the same joint at every...
Definition ImmFilter.h:1585
Model_ const & model() const
Definition ImmFilter.h:1486
auto predict(TimeStep< Scalar > auto const &dt, Inputs const &... inputs)
Mixes the modes' components through the chain, moves each by dt, any TimeStep (see Time steps),...
Definition ImmFilter.h:1517
StateType const & state() const
Definition ImmFilter.h:1441
typename RecursionType::Scalar Scalar
Definition ImmFilter.h:1390
typename RecursionType::ModeSpaceType ModeSpaceType
Definition ImmFilter.h:1394
typename RecursionType::SpaceType SpaceType
Definition ImmFilter.h:1391
ImmRecursion< Component_, Model_, Method_, Allocator_ > RecursionType
Definition ImmFilter.h:1388
Method_ const & method() const
Definition ImmFilter.h:1502
JointType predictJoint(Inputs const &... inputs)
Moves the state forward as predict(inputs...) does with fixed-step models.
Definition ImmFilter.h:1601
auto predict(Steps steps, Inputs const &... inputs)
Moves the state forward by steps of the fixed-step models, as that many calls of predict(inputs....
Definition ImmFilter.h:1542
auto update(SwitchingMeasurement< P, Ms... > const &measurement, Policy const &policy={})
As update(measurement, policy), but with a measurement per value of the mode axis that measurement na...
Definition ImmFilter.h:1653
allocator_type get_allocator() const
Definition ImmFilter.h:1434
Params< Component_ > ComponentType
Definition ImmFilter.h:1396
std::expected< void, NumericError > update(SwitchingMeasurement< P, Ms... > const &measurement, Policy const &policy, ImmFilterUpdate< ModeUpdate, Scalar, ResultAllocator > &result)
As update(measurement, policy) with a SwitchingMeasurement, but into result, whose storage is reused.
Definition ImmFilter.h:1665
auto predict(Inputs const &... inputs)
Mixes the modes' components through the chain, moves each by its mode's fixed-step model's step,...
Definition ImmFilter.h:1529
auto update(M const &measurement, Policy const &policy={})
Conditions each mode's component on measurement, by the measurement's own method, reduces its branche...
Definition ImmFilter.h:1630
StateType & state()
Definition ImmFilter.h:1451
typename RecursionType::JointType JointType
Definition ImmFilter.h:1398
void predictJoint(HybridPredictedJoint< SpaceType, ModeSpaceType, Scalar, JointAllocator > &joint, Inputs const &... inputs)
As predictJoint(inputs...), but into joint, whose storage is reused.
Definition ImmFilter.h:1611
JointType predictJoint(TimeStep< Scalar > auto const &dt, Inputs const &... inputs)
Moves the state forward as predict(dt, inputs...) does.
Definition ImmFilter.h:1574
ImmFilter(Model_ model, StateType state, std::type_identity_t< Upkeep_ > upkeep)
A filter with switching model model, initial state state, the default prediction method,...
Definition ImmFilter.h:1427
ImmFilter(Model_ model, StateType state, MethodFor< Model_, Method_ > method={}, std::type_identity_t< Upkeep_ > upkeep={})
A filter with switching model model, initial state state, prediction method method,...
Definition ImmFilter.h:1404
std::expected< void, NumericError > update(M const &measurement, Policy const &policy, ImmFilterUpdate< ModeUpdate, Scalar, ResultAllocator > &result)
As update(measurement, policy), but into result, whose storage is reused: with the same result at eve...
Definition ImmFilter.h:1642
The interacting multiple model (IMM) filter's steps over a hybrid state, without the state: a switchi...
Definition ImmFilter.h:313
std::expected< SwitchingResultType< S >, NumericError > SwitchingUpdateType
What update() with the SwitchingMeasurement S returns: the SwitchingResultType in a std::expected.
Definition ImmFilter.h:387
ResultType< M > emptyUpdate(Allocator_ const &allocator, Eigen::Index modes) const
Definition ImmFilter.h:932
allocator_type get_allocator() const
Definition ImmFilter.h:435
Model_ const & model() const
Definition ImmFilter.h:453
ModeSpaceType const & modeSpace() const
Definition ImmFilter.h:476
typename Model_::ModeSpaceType ModeSpaceType
the discrete mode space
Definition ImmFilter.h:330
std::expected< void, NumericError > update(StateType &state, SwitchingMeasurement< P, Ms... > const &measurement, Policy const &policy, ImmFilterUpdate< ModeUpdate, Scalar, ResultAllocator > &result)
As update(state, measurement, policy) with a SwitchingMeasurement, but into result,...
Definition ImmFilter.h:708
Method_ const & method() const
Definition ImmFilter.h:469
std::expected< void, NumericError > updateGiven(StateType &state, internal::PinnedMeasurement< Space, P, M > const &f, Value const &value, Policy const &policy, ImmFilterUpdate< ModeUpdate, Scalar, ResultAllocator > &result)
Definition ImmFilter.h:907
Model_ ModelType
the switching model
Definition ImmFilter.h:328
Allocator_ allocator_type
the scratch's allocator
Definition ImmFilter.h:333
ImmFilterUpdate< std::conditional_t< internal::isByMode< M >, FilterUpdate< typename MeasuredType< M >::MeasurementSpaceType, 1, Scalar >, std::conditional_t< IsForm, MeasuredType< M >, internal::FilterUpdateWithScratch< SpaceType, typename MeasuredType< M >::MeasurementSpaceType, MeasuredType< M >::BranchCount, Scalar, Allocator_ > > >, Scalar, Allocator_ > ResultType
What update() with M fills when given a result: one branch per mode for noise by mode axis,...
Definition ImmFilter.h:350
auto predict(StateType &state, TimeStep< Scalar > auto const &dt, Inputs const &... inputs)
Mixes state's components through the chain, moves each by dt, any TimeStep (see Time steps),...
Definition ImmFilter.h:499
typename Params< Component_ >::SpaceType SpaceType
the state space
Definition ImmFilter.h:327
void predictJoint(StateType &state, TimeStep< Scalar > auto const &dt, HybridPredictedJoint< SpaceType, ModeSpaceType, Scalar, JointAllocator > &joint, Inputs const &... inputs)
As predictJoint(state, dt, inputs...), but into joint, whose storage is reused: with the same joint a...
Definition ImmFilter.h:560
typename Params< Component_ >::Scalar Scalar
the scalar type
Definition ImmFilter.h:326
JointType predictJoint(StateType &state, Inputs const &... inputs)
Moves state forward as predict(state, inputs...) does with fixed-step models.
Definition ImmFilter.h:588
Model_ & model()
Definition ImmFilter.h:460
void predictJoint(StateType &state, HybridPredictedJoint< SpaceType, ModeSpaceType, Scalar, JointAllocator > &joint, Inputs const &... inputs)
As predictJoint(state, inputs...), but into joint, whose storage is reused.
Definition ImmFilter.h:599
auto predict(StateType &state, Inputs const &... inputs)
Mixes state's components through the chain, moves each by its mode's fixed-step model's step,...
Definition ImmFilter.h:523
Params< Component_ > ComponentType
one mode's component
Definition ImmFilter.h:331
std::expected< void, NumericError > update(StateType &state, M const &measurement, GivenBlock< Space, P, Value > const &pinned, Policy const &policy, ImmFilterUpdate< ModeUpdate, Scalar, ResultAllocator > &result)
As update(state, measurement, pinned, policy), but into result, whose storage is reused.
Definition ImmFilter.h:869
HybridPredictedJoint< SpaceType, ModeSpaceType, Scalar, Allocator_ > JointType
what predictJoint() records
Definition ImmFilter.h:336
typename internal::ImmMeasured< ComponentType, M >::type MeasuredType
The MeasurementUpdate of M on one mode's component, or, for a square-root form, the FilterUpdate of i...
Definition ImmFilter.h:344
static constexpr bool Reentrant
Definition ImmFilter.h:338
ResultType< typename S::template MeasurementType< 0 > > SwitchingResultType
What update() with the SwitchingMeasurement S fills when given a result: the ResultType of its first ...
Definition ImmFilter.h:381
SwitchingUpdateType< SwitchingMeasurement< P, Ms... > > update(StateType &state, SwitchingMeasurement< P, Ms... > const &measurement, Policy const &policy={})
As update(state, measurement, policy), but with a measurement per value of the mode axis that measure...
Definition ImmFilter.h:689
void resetScratch(Allocator_ const &allocator)
Replaces the scratch with storage from allocator, when a state from another allocator arrives.
Definition ImmFilter.h:444
JointType emptyJoint(StateType const &state) const
Definition ImmFilter.h:954
std::expected< GivenResultType< M, Space, P >, NumericError > GivenUpdateType
What update(state, measurement, pinned, policy) with M returns.
Definition ImmFilter.h:418
GivenUpdateType< M, Space, P > update(StateType &state, M const &measurement, GivenBlock< Space, P, Value > const &pinned, Policy const &policy={})
As update(state, measurement, policy), but with state a hybrid over the rest of a state and the block...
Definition ImmFilter.h:839
typename decltype(internal::updateBranchesWith< true >( std::declval< ComponentType const & >(), std::declval< internal::PinnedMeasurement< Space, P, M > const & >(), std::declval< typename Moments< internal::PathTag< P >, Scalar >::VectorType const & >()))::value_type GivenMeasuredType
The MeasurementUpdate of M on one mode's Gaussian, with the block named by the path P of a state in S...
Definition ImmFilter.h:393
ImmRecursion(Model_ model, MethodFor< Model_, Method_ > method={}, Allocator_ const &allocator={})
The recursion with switching model model and prediction method method, which the modes whose models c...
Definition ImmFilter.h:424
bool partitions(SpaceType const &space) const
Definition ImmFilter.h:483
JointType predictJoint(StateType &state, TimeStep< Scalar > auto const &dt, Inputs const &... inputs)
Moves state forward as predict(state, dt, inputs...) does.
Definition ImmFilter.h:547
ImmFilterUpdate< std::conditional_t< internal::isByMode< M >, FilterUpdate< typename GivenMeasuredType< M, Space, P >::MeasurementSpaceType, 1, Scalar >, internal::FilterUpdateWithScratch< SpaceType, typename GivenMeasuredType< M, Space, P >::MeasurementSpaceType, GivenMeasuredType< M, Space, P >::BranchCount, Scalar, Allocator_ > >, Scalar, Allocator_ > GivenResultType
What update(state, measurement, pinned, policy) with M fills when given a result: one branch per mode...
Definition ImmFilter.h:403
Method_ MethodType
the prediction method
Definition ImmFilter.h:329
GivenResultType< M, Space, P > emptyGivenUpdate(Allocator_ const &allocator, Eigen::Index modes) const
Definition ImmFilter.h:945
HybridParams< Component_, ModeSpaceType, Allocator_ > StateType
the state the steps act on
Definition ImmFilter.h:332
internal::UnlessGivenBlock< UpdateTypeOf< M >, Policy >::type update(StateType &state, M const &measurement, Policy const &policy={})
Conditions each of state's components on measurement, by the measurement's own method,...
Definition ImmFilter.h:636
std::expected< ResultType< M >, NumericError > UpdateType
What update() with M returns: the ResultType in a std::expected (decisions.md [innovation-cholesky]).
Definition ImmFilter.h:368
std::expected< void, NumericError > update(StateType &state, M const &measurement, Policy const &policy, ImmFilterUpdate< ModeUpdate, Scalar, ResultAllocator > &result)
As update(state, measurement, policy), but into result, whose storage is reused: with the same result...
Definition ImmFilter.h:655
Eigen::Matrix< Scalar_, CardinalityAtCompileTime, CardinalityAtCompileTime, Eigen::RowMajor, MaxCardinalityAtCompileTime, MaxCardinalityAtCompileTime > MatrixType
The transition matrix's type: a row per value moved from, a column per value moved to.
Definition Categorical.h:838
First and second moments (mean and covariance) of a distribution.
Definition Moments.h:55
A measurement that switches between modes: one Measurement per value of a mode axis,...
Definition SwitchingModel.h:542
decltype(auto) visit(std::size_t value, F &&f) const
Definition SwitchingModel.h:579
The distribution that a container of several holds one of per element (decisions.md [component-first]...
Definition Component.h:299
A measurement: an observed value with its model and the call's inputs, and the method a Kalman update...
Definition MeasurementModel.h:1337
A type usable as the space of a distribution.
Definition Space.h:186
A step of time that converts to Scalar: Scalar itself, or any type with a TimeTraits,...
Definition Time.h:128
std::allocator< std::byte > DefaultAllocator
The allocator a type with a run-time count uses unless given one.
Definition Allocation.h:47
#define EMRYS_FILTER_NO_UNIQUE_ADDRESS
Assertion used for programmer errors (misuse of the API).
Definition Macros.h:38
NumericError
Reasons a computation from parameters the caller gave can fail: a matrix that does not factorize or a...
Definition NumericError.h:30
CategoricalParams< Space, Scalar > predict(CategoricalParams< Space, Scalar > const &prior, MarkovChain< Space, Scalar > const &chain)
Definition Categorical.h:1049
typename ParamsOf< D >::type Params
The parameters of the density D: GaussianParams for a Gaussian, and D itself for a distribution whose...
Definition Params.h:42
typename internal::DefaultMethodOf< Model >::type DefaultMethodFor
The prediction method of a filter with process model Model, unless it is given one: Linearized,...
Definition ProcessModel.h:3220
GivenBlock< Space, P, Value > given(Space const &space, Value const &value)
Definition Measurement.h:917
constexpr bool isFixedStep
Whether Model is a FixedStepModel, whose predictions take no time step.
Definition ProcessModel.h:2776
PredictedJoint< typename Prior::SpaceType, typename Prior::Scalar > predictJoint(Method const &method, Prior const &prior, Model const &model, TimeStep< typename Prior::Scalar > auto const &time, Args const &... args)
Definition ProcessModel.h:3362
std::expected< CategoricalUpdate< Space, Scalar >, WeightError > update(CategoricalParams< Space, Scalar > const &prior, E const &emission)
Definition Emission.h:97
Eigen::Array< typename Derived::Scalar, Eigen::Dynamic, 1 > logLikelihoods(M const &measurement, Space const &space, Eigen::MatrixBase< Derived > const &states)
Definition Likelihood.h:408
std::conditional_t< isFixedStep< Model >, ModelsMethod, Method > MethodFor
The type of a filter constructor's method argument: Method, or ModelsMethod for a fixed-step Model,...
Definition ProcessModel.h:3236
The upkeep operations, each made by its factory (decisions.md [policy-factories], [upkeep]): the acti...
Definition Upkeep.h:380
What a filter's update did: the branches of the update before reduction, with their weights and innov...
Definition MeasurementUpdate.h:251
A block of a state given from outside the Gaussian being updated, as given() makes it,...
Definition Measurement.h:723
Value const & value
the block's value, known or an uncertain input
Definition Measurement.h:725
Space space
the whole state space
Definition Measurement.h:724
One IMM prediction as its smoother needs it: the mode probabilities the step started from,...
Definition ImmFilter.h:138
internal::Buffer< JointType, Allocator_ > JointsType
the modes' joints
Definition ImmFilter.h:144
CategoricalParams< ModeSpace_, Scalar_ > ModesType
the mode weights' type
Definition ImmFilter.h:142
internal::Buffer< GaussianParams< Space_, Scalar_ >, Allocator_ > PredictionsType
predictions alone
Definition ImmFilter.h:146
PredictedJoint< Space_, Scalar_ > JointType
one mode's joint
Definition ImmFilter.h:143
typename MarkovChain< ModeSpace_, Scalar_ >::MatrixType TransitionsType
the chain's matrix
Definition ImmFilter.h:145
ModeSpace_ ModeSpaceType
the discrete mode space
Definition ImmFilter.h:141
Scalar_ Scalar
the scalar type
Definition ImmFilter.h:139
Space_ SpaceType
the continuous state space
Definition ImmFilter.h:140
GaussianParams< Space_, Scalar_ > const & predictedThrough(Eigen::Index from, Eigen::Index model) const
Definition ImmFilter.h:162
What an IMM filter's update did: each mode's update before reduction, the observation's log-likelihoo...
Definition ImmFilter.h:63
Scalar logLikelihood() const
Definition ImmFilter.h:84
internal::Buffer< ModeUpdate, Allocator > ModeUpdatesType
the modes' container
Definition ImmFilter.h:66
ModeUpdate ModeUpdateType
one mode's update
Definition ImmFilter.h:65
Scalar_ Scalar
the scalar type
Definition ImmFilter.h:64
ImmFilterUpdate()=default
an empty result, from a default-constructed allocator
ModeUpdatesType modes
one per mode, in flat-index order
Definition ImmFilter.h:89
Allocator get_allocator() const
Definition ImmFilter.h:77
bool accepted
whether any mode accepted the measurement
Definition ImmFilter.h:90
ImmFilterUpdate(Allocator const &allocator)
An empty result whose modes' updates come from allocator.
Definition ImmFilter.h:71
UpdateType<M>, named without being formed (see internal::UnlessGivenBlock).
Definition ImmFilter.h:373
UpdateType< M > type
the update's type
Definition ImmFilter.h:374
One prediction step as a joint: the Gaussian the step started from, the Gaussian it predicted,...
Definition ProcessModel.h:2817
A count of a fixed-step model's steps, for predict(Steps{n}).
Definition Time.h:136
Eigen::Index count
the number of steps, at least zero
Definition Time.h:137
Reduction policy that moment-matches an update's branches into one Gaussian: soft outlier rejection,...
Definition Reduction.h:241