EmrysFilter 0.1.0
State estimation utilities in C++23 using Eigen
Loading...
Searching...
No Matches
RbParticleFilter.h
Go to the documentation of this file.
1#pragma once
2
9
10#include <expected>
11#include <optional>
12#include <random>
13#include <type_traits>
14#include <utility>
15
16#include <Eigen/Cholesky>
17
41
42namespace Emrys
43{
44
45namespace internal
46{
47
53template <typename Step, typename Split, typename Pinned>
54class PinnedStep
55{
56public:
57
59 static constexpr bool EvaluatesBatches = requires { requires Step::EvaluatesBatches; };
60
62 PinnedStep(Step const & step, Split const & split, Pinned const & pinned)
63 : m_step{step}
64 , m_split{split}
65 , m_pinned{pinned}
66 {}
67
71 template <typename Rest, typename... Inputs>
72 [[nodiscard]] auto
73 operator()(Rest const & rest, Inputs const &... inputs) const
74 {
75 return m_step(m_split.assemble(rest, m_pinned), inputs...);
76 }
77
81 template <typename Y, typename Rests, typename... Inputs>
82 void
83 evaluateBatch(Y & y, Rests const & rests, Inputs const &... inputs) const
84 requires EvaluatesBatches
85 {
86 using T = typename Rests::Scalar;
87 using States =
88 MatrixOf<T, Y::RowsAtCompileTime, Y::ColsAtCompileTime, Y::MaxRowsAtCompileTime, Y::MaxColsAtCompileTime>;
89 auto states = States{};
90 states.resize(y.rows(), rests.cols());
91 for (Eigen::Index j = 0; j < rests.cols(); ++j)
92 {
93 states.col(j) = m_split.assemble(rests.col(j), m_pinned);
94 }
95 m_step.evaluateBatch(y, states, inputs...);
96 }
97
98private:
99
100 Step const & m_step;
101 Split const & m_split;
102 Pinned const & m_pinned;
103};
104
108template <typename Rest, typename Scalar, typename Allocator, typename Model, bool = isSwitchingPointModel<Model>>
109struct ParticleComponentOf
110{
111 using type = Gaussian<Rest, Scalar>;
112};
113
115template <typename Rest, typename Scalar, typename Allocator, typename Model>
116struct ParticleComponentOf<Rest, Scalar, Allocator, Model, true>
117{
118 using type = Hybrid<Gaussian<Rest, Scalar>, typename Model::ModeSpaceType, Allocator>;
119};
120
125template <typename Split, typename CovDerived, typename OffsetDerived>
126[[nodiscard]] std::expected<typename CovDerived::Scalar, NumericError>
127pinnedLogDensity(
128 Split const & split,
129 Eigen::MatrixBase<CovDerived> const & cov,
130 Eigen::MatrixBase<OffsetDerived> const & offset
131)
132{
133 using Scalar = typename CovDerived::Scalar;
136 BlockRows const rows = split.pinnedDifferenceRows();
137 PinnedMatrix const pinned = blockOf<PinnedRows, PinnedRows>(cov, rows.start, rows.start, rows.size, rows.size);
138 auto const llt = Eigen::LLT<PinnedMatrix>{pinned};
139 if (llt.info() != Eigen::Success)
140 {
141 return std::unexpected{NumericError::NotPositiveDefinite};
142 }
143 auto const whitened = llt.matrixL().solve(offset.derived()).eval();
144 return Scalar(-0.5) * whitened.squaredNorm() - llt.matrixLLT().diagonal().array().log().sum();
145}
146
147} // namespace internal
148
160template <typename Scalar = double, typename Allocator = DefaultAllocator>
162{
163public:
164
165 using ArrayType = Eigen::Map<Eigen::Array<Scalar, Eigen::Dynamic, 1> const>;
166 using allocator_type = Allocator;
167
169 explicit RbParticleFilterUpdate(Allocator const & allocator = {})
170 : m_points{allocator}
171 {}
172
174 [[nodiscard]] ArrayType
176 {
177 return m_points.logLikelihoods();
178 }
179
183 [[nodiscard]] ArrayType
184 nis() const
185 {
186 return m_points.nis();
187 }
188
190 [[nodiscard]] Eigen::Index
192 {
193 return m_points.innovationSize;
194 }
195
197 [[nodiscard]] allocator_type
199 {
200 return allocator_type(m_points.logLikelihoodData.get_allocator());
201 }
202
204 [[nodiscard]] Scalar
206 {
207 return m_logLikelihood;
208 }
209
210 bool accepted = false;
211
212private:
213
214 friend struct internal::PointLikelihoodsAccess;
215 friend struct internal::UpdateAccess;
216
217 Scalar m_logLikelihood = 0;
218
219 internal::PointLikelihoods<Scalar, Allocator> m_points;
220};
221
306template <
307 Space Space_,
308 typename Path_,
309 typename Model_,
310 typename Covariance_ = internal::PointCovarianceFor<Model_>,
311 typename Method_ = DefaultMethodFor<Model_>,
312 RealScalar Scalar_ = double,
313 typename Allocator_ = DefaultAllocator,
314 typename Upkeep_ = upkeep::None>
316 : public internal::HeldUpkeep<
317 RbParticleFilter<Space_, Path_, Model_, Covariance_, Method_, Scalar_, Allocator_, Upkeep_>,
318 Upkeep_>
319{
320 using UpkeepBase = internal::HeldUpkeep<RbParticleFilter, Upkeep_>;
321
322 static constexpr bool Shared = std::is_same_v<Covariance_, SharedCovariance>;
323
325 static constexpr bool Switching = internal::isSwitchingPointModel<Model_>;
326
327 using RestSpace = typename internal::BlockSplit<Space_, Path_>::RestSpace;
328
330 template <typename P>
331 static constexpr void
332 assertParticlesRunUnder()
333 {
334 static_assert(
335 !Switching || internal::isSequential<P>,
336 "a particle's IMM step writes the recursion's scratch, so the particles of a hybrid state run in "
337 "order: pass Sequential"
338 );
339 }
340
341 static_assert(
342 internal::predictsBy<Model_, Method_>,
343 "a filter with a fixed-step model predicts by the model's method"
344 );
345 static_assert(internal::checkPredictionMethod<Method_>());
346
347public:
348
349 using Scalar = Scalar_;
350 using SpaceType = Space_;
351 using ModelType = Model_;
352 using MethodType = Method_;
355 Space_,
356 Path_,
357 Covariance_,
358 Scalar_,
359 Allocator_,
360 typename internal::ParticleComponentOf<RestSpace, Scalar_, Allocator_, Model_>::type>;
362 using allocator_type = Allocator_;
363
371 std::type_identity_t<Model_> model,
374 std::type_identity_t<Upkeep_> upkeep = {}
375 )
376 : UpkeepBase{std::move(upkeep)}
377 , m_steps{std::move(model), std::move(method), state.get_allocator()}
378 , m_state{std::move(state)}
379 , m_posterior{m_state.m_conditionals, m_state.get_allocator()}
380 , m_normals(ScalarAllocator(m_state.get_allocator()))
381 , m_moved(ScalarAllocator(m_state.get_allocator()))
382 {
383 assertModelPartitions(m_state);
384 reserveScratch();
385 }
386
390 RbParticleFilter(std::type_identity_t<Model_> model, StateType state, std::type_identity_t<Upkeep_> upkeep)
391 requires(!std::same_as<Upkeep_, Emrys::upkeep::None>)
392 : RbParticleFilter{std::move(model), std::move(state), MethodFor<Model_, Method_>{}, std::move(upkeep)}
393 {}
394
396 [[nodiscard]] allocator_type
398 {
399 return m_state.get_allocator();
400 }
401
403 [[nodiscard]] StateType const &
404 state() const
405 {
406 return m_state;
407 }
408
412 [[nodiscard]] StateType &
414 {
415 return m_state;
416 }
417
422 void
424 {
425 assertModelPartitions(state);
426 m_state = std::move(state);
427 if (m_normals.get_allocator() != ScalarAllocator(m_state.get_allocator()))
428 {
429 // The assignment carried the new state's allocator in, so the scratch follows it.
430 m_posterior = decltype(m_posterior){m_state.m_conditionals, m_state.get_allocator()};
431 m_normals = internal::Buffer<Scalar_, Allocator_>(ScalarAllocator(m_state.get_allocator()));
432 m_moved = internal::Buffer<Scalar_, Allocator_>(ScalarAllocator(m_state.get_allocator()));
433 if constexpr (Switching)
434 {
435 m_steps.resetScratch(m_state.get_allocator());
436 }
437 }
438 reserveScratch();
439 }
440
442 [[nodiscard]] Model_ const &
443 model() const
444 {
445 return m_steps.model();
446 }
447
449 [[nodiscard]] Model_ &
451 {
452 return m_steps.model();
453 }
454
456 [[nodiscard]] Method_ const &
457 method() const
458 {
459 return m_steps.method();
460 }
461
474 template <std::uniform_random_bit_generator G, typename... Inputs>
475 requires(!isFixedStep<Model_>)
476 [[nodiscard]] auto
477 predict(G & rng, TimeStep<Scalar_> auto const & dt, Inputs const &... inputs)
478 {
479 return predict(execution::sequential, rng, dt, inputs...);
480 }
481
487 template <ExecutionPolicy P, std::uniform_random_bit_generator G, typename... Inputs>
488 requires(!isFixedStep<Model_>)
489 [[nodiscard]] auto
490 predict(P && policy, G & rng, TimeStep<Scalar_> auto const & dt, Inputs const &... inputs)
491 {
492 Scalar_ const step = internal::toScalarTime<Scalar_>(dt);
493 if constexpr (Switching)
494 {
495 assertParticlesRunUnder<P>();
496 return moveHybridParticles(rng, [&](auto const & model, auto const & method, auto const & predictBy) {
497 auto const call = internal::modelWith(std::cref(model), inputs...);
498 auto const resolved = internal::resolveProcess(call, m_state.space());
499 return std::apply(
500 [&](auto const &... uncertain) {
501 return predictBy(
502 method,
503 resolved.template noise<Scalar_>(step),
504 internal::ProcessStepValues<std::remove_cvref_t<decltype(resolved)>, Scalar_>{
505 resolved,
506 step
507 },
508 internal::inputOf(uncertain)...
509 );
510 },
511 resolved.inputs()
512 );
513 });
514 }
515 else
516 {
517 predictOne(std::forward<P>(policy), rng, step, inputs...);
518 }
519 }
520
530 template <std::uniform_random_bit_generator G, typename... Inputs>
531 requires(isFixedStep<Model_> && !internal::leadsWithSteps<Inputs...>)
532 [[nodiscard]] auto
533 predict(G & rng, Inputs const &... inputs)
534 {
535 internal::rejectTimeStep<Inputs...>();
536 return predict(execution::sequential, rng, inputs...);
537 }
538
540 template <ExecutionPolicy P, std::uniform_random_bit_generator G, typename... Inputs>
541 requires(isFixedStep<Model_> && !internal::leadsWithSteps<Inputs...>)
542 [[nodiscard]] auto
543 predict(P && policy, G & rng, Inputs const &... inputs)
544 {
545 internal::rejectTimeStep<Inputs...>();
546 if constexpr (Switching)
547 {
548 assertParticlesRunUnder<P>();
549 return moveHybridParticles(rng, [&](auto const & model, auto const & method, auto const & predictBy) {
550 auto const call = internal::modelWith(std::cref(model.model()), inputs...);
551 auto const resolved = internal::resolveProcess(call, m_state.space());
552 return std::apply(
553 [&](auto const &... uncertain) {
554 return predictBy(
555 method,
556 model.noise(),
557 internal::ProcessStepValues<
558 std::remove_cvref_t<decltype(resolved)>,
559 std::remove_cvref_t<decltype(model.timeStep())>>{resolved, model.timeStep()},
560 internal::inputOf(uncertain)...
561 );
562 },
563 resolved.inputs()
564 );
565 });
566 }
567 else
568 {
569 predictOneFixed(std::forward<P>(policy), rng, inputs...);
570 }
571 }
572
580 template <std::uniform_random_bit_generator G, typename... Inputs>
581 requires isFixedStep<Model_>
582 [[nodiscard]] auto
583 predict(G & rng, Steps steps, Inputs const &... inputs)
584 {
585 return predict(execution::sequential, rng, steps, inputs...);
586 }
587
591 template <ExecutionPolicy P, std::uniform_random_bit_generator G, typename... Inputs>
592 requires isFixedStep<Model_>
593 [[nodiscard]] auto
594 predict(P && policy, G & rng, Steps steps, Inputs const &... inputs)
595 {
596 EMRYS_FILTER_ASSERT(steps.count >= 0 && "the number of steps must be nonnegative");
597 if constexpr (Switching)
598 {
599 for (Eigen::Index i = 0; i < steps.count; ++i)
600 {
601 if (auto moved = predict(policy, rng, inputs...); !moved)
602 {
603 return moved;
604 }
605 }
606 return std::expected<void, NumericError>{};
607 }
608 else
609 {
610 for (Eigen::Index i = 0; i < steps.count; ++i)
611 {
612 predict(policy, rng, inputs...);
613 }
614 }
615 }
616
619
640 template <internal::WeighsPoints M>
641 [[nodiscard]] std::expected<UpdateType, NumericError>
642 update(M const & measurement)
643 {
644 return update(execution::sequential, measurement);
645 }
646
650 template <ExecutionPolicy P, internal::WeighsPoints M>
651 [[nodiscard]] std::expected<UpdateType, NumericError>
652 update(P && executionPolicy, M const & measurement)
653 {
654 auto result = UpdateType{get_allocator()};
655 auto const updated = update(std::forward<P>(executionPolicy), measurement, result);
656 if (!updated)
657 {
658 return std::unexpected{updated.error()};
659 }
660 return result;
661 }
662
668 template <internal::WeighsPoints M, typename ResultAllocator>
669 [[nodiscard]] std::expected<void, NumericError>
671 {
672 return update(execution::sequential, measurement, result);
673 }
674
678 template <ExecutionPolicy P, internal::WeighsPoints M, typename ResultAllocator>
679 [[nodiscard]] std::expected<void, NumericError>
680 update(P && executionPolicy, M const & measurement, RbParticleFilterUpdate<Scalar, ResultAllocator> & result)
681 {
682 if constexpr (!internal::hasOwnLikelihood<M> && !Shared)
683 {
684 return update(std::forward<P>(executionPolicy), measurement, reductions::collapse(), result);
685 }
686 else
687 {
688 if (auto const total = m_state.logTotalWeight(executionPolicy); !total)
689 {
690 return std::unexpected{total.error()};
691 }
692 auto & points = internal::PointLikelihoodsAccess::points(result);
693 if constexpr (internal::hasOwnLikelihood<M>)
694 {
695 internal::updateGivenLikelihood(
696 executionPolicy,
697 m_state.points(),
698 m_state.m_conditionals,
699 measurement,
700 points
701 );
702 finish<false>(std::forward<P>(executionPolicy), result);
703 }
704 else
705 {
706 auto const updated = internal::updateGivenShared(
707 executionPolicy,
708 m_state.points(),
709 m_state.m_conditionals,
710 m_state.mostProbable(),
711 measurement,
712 points,
713 m_posterior
714 );
715 if (!updated)
716 {
717 return std::unexpected{updated.error()};
718 }
719 finish(std::forward<P>(executionPolicy), result);
720 }
721 return {};
722 }
723 }
724
730 template <internal::WeighsPoints M, typename Policy>
731 [[nodiscard]] std::expected<UpdateType, NumericError>
732 update(M const & measurement, Policy const & policy)
733 requires(!Shared)
734 {
735 return update(execution::sequential, measurement, policy);
736 }
737
742 template <ExecutionPolicy P, internal::WeighsPoints M, typename Policy>
743 [[nodiscard]] std::expected<UpdateType, NumericError>
744 update(P && executionPolicy, M const & measurement, Policy const & policy)
745 requires(!Shared)
746 {
747 auto result = UpdateType{get_allocator()};
748 auto const updated = update(std::forward<P>(executionPolicy), measurement, policy, result);
749 if (!updated)
750 {
751 return std::unexpected{updated.error()};
752 }
753 return result;
754 }
755
761 template <internal::WeighsPoints M, typename Policy, typename ResultAllocator>
762 [[nodiscard]] std::expected<void, NumericError>
763 update(M const & measurement, Policy const & policy, RbParticleFilterUpdate<Scalar, ResultAllocator> & result)
764 requires(!Shared)
765 {
766 return update(execution::sequential, measurement, policy, result);
767 }
768
772 template <ExecutionPolicy P, internal::WeighsPoints M, typename Policy, typename ResultAllocator>
773 [[nodiscard]] std::expected<void, NumericError>
775 P && executionPolicy,
776 M const & measurement,
777 Policy const & policy,
779 )
780 requires(!Shared)
781 {
782 static_assert(
783 !internal::hasOwnLikelihood<M>,
784 "a measurement weighted by its own logLikelihood() has no branches to reduce, so it takes no policy"
785 );
786 // Weights the caller left undefined fail the update before any particle is conditioned.
787 if (auto const total = m_state.logTotalWeight(executionPolicy); !total)
788 {
789 return std::unexpected{total.error()};
790 }
791 if constexpr (Switching && internal::isByMode<M>)
792 {
793 // Each mode's measurement holds its own noise component, so the recursion resolves it.
794 return updateParticles(
795 std::forward<P>(executionPolicy),
796 m_steps.template emptyGivenUpdate<M, Space_, Path_>(get_allocator(), modeCount()),
797 [&](auto & hybrid, auto const & pinned, auto & one) {
798 using Value = std::remove_cvref_t<decltype(pinned)>;
799 return m_steps.update(
800 hybrid,
801 measurement,
802 GivenBlock<Space_, Path_, Value>{m_state.space(), pinned},
803 policy,
804 one
805 );
806 },
807 result
808 );
809 }
810 else if constexpr (Switching)
811 {
812 auto const f = internal::PinnedMeasurement<Space_, Path_, M>{m_state.space(), measurement};
813 return updateParticles(
814 std::forward<P>(executionPolicy),
815 m_steps.template emptyGivenUpdate<M, Space_, Path_>(get_allocator(), modeCount()),
816 [&](auto & hybrid, auto const & pinned, auto & one) {
817 return m_steps.updateGiven(hybrid, f, pinned, policy, one);
818 },
819 result
820 );
821 }
822 else
823 {
824 auto const updated = internal::updateGivenEach(
825 executionPolicy,
826 m_state.points(),
827 m_state.m_conditionals,
828 measurement,
829 policy,
830 internal::PointLikelihoodsAccess::points(result),
831 m_posterior
832 );
833 if (!updated)
834 {
835 return std::unexpected{updated.error()};
836 }
837 finish(std::forward<P>(executionPolicy), result);
838 return {};
839 }
840 }
841
842private:
843
844 using ScalarAllocator = internal::ReBound<Allocator_, Scalar_>;
845
849 void
850 assertModelPartitions(StateType const & state) const
851 {
852 internal::assertPartitions(
853 m_steps.model(),
854 state.space(),
855 Switching ? "every mode's process model must write every coordinate of the state space once"
856 : "the process model must write every coordinate of the state space once"
857 );
858 }
859
861 [[nodiscard]] Eigen::Index
862 modeCount() const
863 requires Switching
864 {
865 return m_steps.modeSpace().cardinality();
866 }
867
871 template <typename P, typename G, typename... Inputs>
872 void
873 predictOne(P && policy, G & rng, Scalar_ step, Inputs const &... inputs)
874 {
875 auto const call = internal::modelWith(std::cref(m_steps.model()), inputs...);
876 auto const resolved = internal::resolveProcess(call, m_state.space());
877 std::apply(
878 [&](auto const &... uncertain) {
879 moveParticles(
880 std::forward<P>(policy),
881 rng,
882 resolved.template noise<Scalar_>(step),
883 [&](auto const & x, auto const &... in) {
884 return internal::valueOf(resolved.propagate(x, step, in...));
885 },
886 internal::inputOf(uncertain)...
887 );
888 },
889 resolved.inputs()
890 );
891 }
892
896 template <typename P, typename G, typename... Inputs>
897 void
898 predictOneFixed(P && policy, G & rng, Inputs const &... inputs)
899 {
900 auto const & model = m_steps.model();
901 auto const call = internal::modelWith(std::cref(model.model()), inputs...);
902 auto const resolved = internal::resolveProcess(call, m_state.space());
903 std::apply(
904 [&](auto const &... uncertain) {
905 moveParticles(
906 std::forward<P>(policy),
907 rng,
908 model.noise(),
909 [&](auto const & x, auto const &... in) {
910 return internal::valueOf(resolved.propagate(x, model.timeStep(), in...));
911 },
912 internal::inputOf(uncertain)...
913 );
914 },
915 resolved.inputs()
916 );
917 }
918
923 template <typename M, typename NoiseDerived, typename Propagate, typename PinnedVector, typename... Inputs>
925 predictWhole(
926 M const & method,
927 Eigen::MatrixBase<NoiseDerived> const & noise,
928 Propagate const & propagate,
929 PinnedVector const & pinned,
930 GaussianParams<RestSpace, Scalar_> const & conditional,
931 Inputs const &... inputs
932 ) const
933 {
934 auto const & split = m_state.m_split;
935 auto const f = internal::PinnedStep<Propagate, std::remove_cvref_t<decltype(split)>, PinnedVector>{
936 propagate,
937 split,
938 pinned
939 };
940 auto predicted = internal::transformBy(method, f, m_state.space(), conditional, inputs...);
941 predicted.setCovariance(predicted.covarianceLower() + noise);
942 return predicted;
943 }
944
963 template <typename G, typename StepOf>
964 [[nodiscard]] std::expected<void, NumericError>
965 moveHybridParticles(G & rng, StepOf const & stepOf)
966 {
967 using PinnedSpace = typename StateType::PinnedSpace;
968 using PinnedSamples = SampleSet<PinnedSpace, Scalar_>;
969 using PinnedNormals = SampleSet<DifferenceSpace<PinnedSpace>, Scalar_>;
970 using PinnedVector = typename Moments<PinnedSpace, Scalar_>::VectorType;
971 using RestVector = typename Moments<RestSpace, Scalar_>::VectorType;
972 using ComponentType = typename ConditionalType::ComponentType;
973 using ModesType = typename ConditionalType::ModesType;
975 constexpr int PinnedValues = valueDimensionAtCompileTime<PinnedSpace>;
976 constexpr Scalar_ none = -Eigen::NumTraits<Scalar_>::infinity();
977 auto const & split = m_state.m_split;
978 BlockRows const rows = split.pinnedRows();
979 Eigen::Index const count = m_state.size();
980 Eigen::Index const normalSize = split.pinnedDifferenceRows().size;
981 m_normals.resize(std::size_t(normalSize * count));
982 auto z = Eigen::Map<PinnedNormals>{m_normals.data(), normalSize, count};
983 internal::fillStandardNormal(z, rng);
984 m_moved.resize(std::size_t(rows.size * count));
985 auto moved = Eigen::Map<PinnedSamples>{m_moved.data(), rows.size, count};
986 if (m_posterior.size() != count)
987 {
988 m_posterior = m_state.m_conditionals;
989 }
990 auto const & model = m_steps.model();
991 auto const & transitions = model.chain().matrix();
992 // Under the identity chain, each mode starts from its own component, as ImmRecursion's does.
993 bool const mixes = !transitions.isIdentity(Scalar_(0));
994 Eigen::Index const modes = modeCount();
995 auto logMixing = typename ModesType::ArrayType{};
996 logMixing.resize(modes);
997 auto logLikelihoods = typename ModesType::ArrayType{};
998 logLikelihoods.resize(modes);
999 for (Eigen::Index i = 0; i < count; ++i)
1000 {
1001 auto const & prior = m_state.m_conditionals.conditional(i);
1002 auto & posterior = m_posterior.conditional(i);
1003 PinnedVector const pinned = m_state.particle(i);
1004 ModesType predictedModes = mixes ? Emrys::predict(prior.modes(), model.chain()) : prior.modes();
1005 auto const logTotal = predictedModes.logTotalWeight();
1006 if (!logTotal)
1007 {
1008 return std::unexpected{logTotal.error()};
1009 }
1010 Eigen::Index const drawn = internal::drawIndex<Scalar_>(
1011 modes,
1012 [&](Eigen::Index m) {
1013 return Eigen::numext::exp(predictedModes.logWeight(m) - *logTotal);
1014 },
1015 rng
1016 );
1017 logLikelihoods.setConstant(none);
1018 for (Eigen::Index pass = 0; pass < modes; ++pass)
1019 {
1020 // The drawn mode first, since every mode is conditioned on its draw.
1021 Eigen::Index const j = pass == 0 ? drawn : (pass - 1 < drawn ? pass - 1 : pass);
1022 bool const reachable = predictedModes.logWeight(j) > none;
1023 auto const predictFrom = [&](ComponentType const & start) {
1024 auto predicted = std::optional<Whole>{};
1025 model.visit(j, m_steps.method(), [&](auto const & mode, auto const & modeMethod) {
1026 predicted.emplace(stepOf(
1027 mode,
1028 modeMethod,
1029 [&](auto const & method, auto const & noise, auto const & propagate, auto const &... in) {
1030 return predictWhole(method, noise, propagate, pinned, start, in...);
1031 }
1032 ));
1033 });
1034 return std::move(*predicted);
1035 };
1036 Whole const predicted = [&] {
1037 if (!mixes)
1038 {
1039 return predictFrom(prior.conditional(j));
1040 }
1041 // A mode the chain can't reach starts from its own component, which is never weighted.
1042 for (Eigen::Index m = 0; m < modes; ++m)
1043 {
1044 logMixing(m) = reachable ? Eigen::numext::log(transitions(m, j)) + prior.modes().logWeight(m)
1045 : (m == j ? Scalar_(0) : none);
1046 }
1047 ComponentType const mixed = ComponentTraits<ComponentType>::collapse(
1048 modes,
1049 [&](Eigen::Index m) -> ComponentType const & {
1050 return prior.conditional(m);
1051 },
1052 [&](Eigen::Index m) {
1053 return logMixing(m);
1054 }
1055 );
1056 return predictFrom(mixed);
1057 }();
1058 auto conditioning = internal::pinnedConditioning(split, predicted.covarianceLower());
1059 auto const pinnedMean = internal::middleRowsOf<PinnedValues>(predicted.mean(), rows.start, rows.size);
1060 auto mean = RestVector{};
1061 if (pass == 0)
1062 {
1063 internal::drawPinned(split, conditioning, predicted.mean(), z.col(i), moved.col(i), mean);
1064 }
1065 auto const offset = Emrys::minus(split.pinnedSpace(), moved.col(i), pinnedMean).eval();
1066 if (pass != 0)
1067 {
1068 mean = Emrys::plus(
1069 split.restSpace(),
1070 predicted.mean()(split.restIndices()),
1071 internal::multiply(conditioning.gain, offset)
1072 );
1073 }
1074 if (reachable)
1075 {
1076 auto const logDensity = internal::pinnedLogDensity(split, predicted.covarianceLower(), offset);
1077 if (!logDensity)
1078 {
1079 return std::unexpected{logDensity.error()};
1080 }
1081 logLikelihoods(j) = *logDensity;
1082 }
1083 posterior.setConditional(
1084 j,
1085 ComponentType{split.restSpace(), std::move(mean), std::move(conditioning.cov)}
1086 );
1087 }
1088 // The drawn mode has a finite density, so the posterior modes define a distribution.
1089 [[maybe_unused]] auto const evidence = predictedModes.update(logLikelihoods);
1090 EMRYS_FILTER_ASSERT(evidence && "the draw's mode has a density");
1091 posterior.setModes(std::move(predictedModes));
1092 }
1093 // The particles and their hybrids change here, so a density of the state is stale.
1094 m_state.m_stamp.touchValues();
1095 m_state.m_conditionals.swap(m_posterior);
1096 m_state.m_particles.setStates(moved);
1097 return {};
1098 }
1099
1107 template <typename P, typename OneResult, typename UpdateOne, typename ResultAllocator>
1108 [[nodiscard]] std::expected<void, NumericError>
1109 updateParticles(
1110 P && policy,
1111 OneResult oneResult,
1112 UpdateOne const & updateOne,
1113 RbParticleFilterUpdate<Scalar, ResultAllocator> & result
1114 )
1115 {
1116 assertParticlesRunUnder<P>();
1117 // Each particle's hybrid is updated in place below, so a density of the state is stale.
1118 m_state.m_stamp.touchValues();
1119 auto const updated = internal::updateHybridsGiven(
1120 m_state.points(),
1121 m_state.m_conditionals,
1122 internal::PointLikelihoodsAccess::points(result),
1123 oneResult,
1124 updateOne
1125 );
1126 if (!updated)
1127 {
1128 return updated;
1129 }
1130 finish<false>(std::forward<P>(policy), result);
1131 return {};
1132 }
1133
1135 void
1136 reserveScratch()
1137 {
1138 Eigen::Index const count = m_state.particles().capacity().value_or(m_state.size());
1139 m_posterior.reserve(count);
1140 m_normals.reserve(std::size_t(m_state.pinnedDifferenceRows().size * count));
1141 m_moved.reserve(std::size_t(m_state.pinnedRows().size * count));
1142 }
1143
1148 template <typename P, typename G, typename Derived, typename Propagate, typename... Inputs>
1149 void
1150 moveParticles(
1151 P && policy,
1152 G & rng,
1153 Eigen::MatrixBase<Derived> const & noise,
1154 Propagate const & propagate,
1155 Inputs const &... inputs
1156 )
1157 {
1158 using PinnedSamples = SampleSet<typename StateType::PinnedSpace, Scalar_>;
1159 using PinnedNormals = SampleSet<DifferenceSpace<typename StateType::PinnedSpace>, Scalar_>;
1160 using PinnedVector = typename Moments<typename StateType::PinnedSpace, Scalar_>::VectorType;
1161 using WholeMoments = Moments<Space_, Scalar_>;
1162 using RestMoments = Moments<typename StateType::RestSpace, Scalar_>;
1163 // The transition's columns for the rest, and their product with a rest covariance.
1164 using RestColumns = internal::MatrixOf<
1165 Scalar_,
1166 WholeMoments::DifferenceDimensionAtCompileTime,
1167 RestMoments::DifferenceDimensionAtCompileTime,
1168 WholeMoments::MaxDifferenceDimensionAtCompileTime,
1169 RestMoments::MaxDifferenceDimensionAtCompileTime>;
1170 using RestCross = internal::MatrixOf<
1171 Scalar_,
1172 RestMoments::DifferenceDimensionAtCompileTime,
1173 WholeMoments::DifferenceDimensionAtCompileTime,
1174 RestMoments::MaxDifferenceDimensionAtCompileTime,
1175 WholeMoments::MaxDifferenceDimensionAtCompileTime>;
1176 constexpr bool cached = internal::cachesTransition<Model_> && sizeof...(Inputs) == 0;
1177 auto const & space = m_state.space();
1178 auto const & split = m_state.m_split;
1179 Eigen::Index const count = m_state.size();
1180 Eigen::Index const pinnedSize = split.pinnedRows().size;
1181 Eigen::Index const normalSize = split.pinnedDifferenceRows().size;
1182 m_normals.resize(std::size_t(normalSize * count));
1183 auto z = Eigen::Map<PinnedNormals>{m_normals.data(), normalSize, count};
1184 internal::fillStandardNormal(z, rng);
1185 // The particles and their Gaussians change below, so a density of the state is stale.
1186 m_state.m_stamp.touchValues();
1187 auto & conditionals = m_state.m_conditionals;
1188 auto const restColumns = [&] {
1189 if constexpr (cached)
1190 {
1191 return RestColumns{
1192 m_steps.model().transition()(Eigen::placeholders::all, split.restDifferenceIndices())
1193 };
1194 }
1195 else
1196 {
1197 return internal::NoTransition{};
1198 }
1199 }();
1200 auto const predictGiven = [&](Eigen::Index i) -> GaussianParams<Space_, Scalar_> {
1201 PinnedVector const pinned = m_state.particle(i);
1202 auto const & conditional = conditionals.conditional(i);
1203 if constexpr (cached)
1204 {
1205 typename WholeMoments::VectorType mean = propagate(split.assemble(conditional.mean(), pinned));
1206 normalizeValues(space, mean);
1207 RestCross const cross = internal::multiply(conditional.covarianceLower(), restColumns.transpose());
1208 typename WholeMoments::MatrixType cov = internal::multiply(restColumns, cross);
1209 cov = (Scalar_(0.5) * (cov + cov.transpose()) + noise).eval();
1210 return {space, std::move(mean), std::move(cov)};
1211 }
1212 else
1213 {
1214 return predictWhole(m_steps.method(), noise, propagate, pinned, conditional, inputs...);
1215 }
1216 };
1217
1218 m_moved.resize(std::size_t(pinnedSize * count));
1219 auto moved = Eigen::Map<PinnedSamples>{m_moved.data(), pinnedSize, count};
1220 if constexpr (Shared)
1221 {
1222 auto const conditioning =
1223 internal::pinnedConditioning(split, predictGiven(m_state.mostProbable()).covarianceLower());
1224 auto means = conditionals.means();
1225 internal::forEachIndex(std::forward<P>(policy), count, [&](Eigen::Index i) {
1226 auto mean = propagate(split.assemble(means.col(i), m_state.particle(i)), internal::meanOf(inputs)...);
1227 normalizeValues(space, mean);
1228 internal::drawPinned(split, conditioning, mean, z.col(i), moved.col(i), means.col(i));
1229 });
1230 conditionals.sharedCovariance() = conditioning.cov;
1231 }
1232 else
1233 {
1234 internal::forEachIndex(std::forward<P>(policy), count, [&](Eigen::Index i) {
1235 auto mean = typename Moments<typename StateType::RestSpace, Scalar_>::VectorType{};
1236 auto const predicted = predictGiven(i);
1237 auto conditioning = internal::pinnedConditioning(split, predicted.covarianceLower());
1238 internal::drawPinned(split, conditioning, predicted.mean(), z.col(i), moved.col(i), mean);
1239 conditionals.conditional(i) =
1240 ConditionalType{split.restSpace(), std::move(mean), std::move(conditioning.cov)};
1241 });
1242 }
1243 m_state.m_particles.setStates(moved);
1244 }
1245
1251 template <bool Posterior = true, typename P, typename ResultAllocator>
1252 void
1253 finish(P && policy, RbParticleFilterUpdate<Scalar, ResultAllocator> & result)
1254 {
1255 auto & points = internal::PointLikelihoodsAccess::points(result);
1256 auto const [logLikelihood, accepted] = [&] {
1257 if constexpr (Posterior)
1258 {
1259 return m_state.reweight(std::forward<P>(policy), points.logLikelihoods(), m_posterior);
1260 }
1261 else
1262 {
1263 return m_state.reweight(std::forward<P>(policy), points.logLikelihoods());
1264 }
1265 }();
1266 internal::UpdateAccess::logLikelihood(result) = logLikelihood;
1267 result.accepted = accepted;
1268 }
1269
1273 using StepsType = std::conditional_t<
1274 Switching,
1275 ImmRecursion<Gaussian<RestSpace, Scalar_>, Model_, Method_, Allocator_>,
1276 internal::PointSteps<Model_, Method_>>;
1277
1278 StepsType m_steps;
1279 StateType m_state;
1280
1281 // Scratch, reused across steps.
1282
1283 typename StateType::ConditionalsType m_posterior;
1284 internal::Buffer<Scalar_, Allocator_> m_normals;
1285 internal::Buffer<Scalar_, Allocator_> m_moved;
1286};
1287
1289// One guide with a defaulted method rather than one per arity, its parameters in the class's
1290// order: otherwise MSVC deduces the scalar as the method. The state's component follows from the
1291// model, so it is not carried over.
1292template <
1293 typename Space,
1294 typename Path,
1295 typename Model,
1296 typename Covariance,
1297 typename Method = DefaultMethodFor<Model>,
1298 typename Scalar,
1299 typename Allocator,
1300 typename Component>
1301 requires(!internal::UpkeepArgument<Method>)
1302RbParticleFilter(Model, ParticleGaussianParams<Space, Path, Covariance, Scalar, Allocator, Component>, Method = {})
1303 -> RbParticleFilter<Space, Path, internal::BoundStep<Model, Space, Scalar>, Covariance, Method, Scalar, Allocator>;
1304
1305template <
1306 typename Space,
1307 typename Path,
1308 typename Model,
1309 typename Covariance,
1310 typename Scalar,
1311 typename Allocator,
1312 typename Component,
1313 typename U>
1314 requires internal::UpkeepArgument<U>
1315RbParticleFilter(Model, ParticleGaussianParams<Space, Path, Covariance, Scalar, Allocator, Component>, U)
1316 -> RbParticleFilter<
1317 Space,
1318 Path,
1319 internal::BoundStep<Model, Space, Scalar>,
1320 Covariance,
1321 DefaultMethodFor<Model>,
1322 Scalar,
1323 Allocator,
1324 U>;
1325
1326template <
1327 typename Space,
1328 typename Path,
1329 typename Model,
1330 typename Covariance,
1331 typename Method,
1332 typename Scalar,
1333 typename Allocator,
1334 typename Component,
1335 typename U>
1336RbParticleFilter(
1337 Model,
1338 ParticleGaussianParams<Space, Path, Covariance, Scalar, Allocator, Component>,
1339 Method,
1340 U
1341) -> RbParticleFilter<Space, Path, internal::BoundStep<Model, Space, Scalar>, Covariance, Method, Scalar, Allocator, U>;
1343
1344} // namespace Emrys
1345
1346#ifndef EMRYS_FILTER_NO_IO
1347
1348namespace Emrys
1349{
1350
1351namespace internal
1352{
1353
1355template <
1356 typename Space_,
1357 typename Path_,
1358 typename Model_,
1359 typename Covariance_,
1360 typename Method_,
1361 typename Scalar_,
1362 typename Allocator_,
1363 typename Upkeep_>
1364void
1365describe(
1366 std::ostream & stream,
1367 RbParticleFilter<Space_, Path_, Model_, Covariance_, Method_, Scalar_, Allocator_, Upkeep_> const & filter,
1368 Indent indent
1369)
1370{
1371 describeFilter(stream, "Rao-Blackwellized particle filter", filter.state(), indent);
1372}
1373
1374} // namespace internal
1375
1376} // namespace Emrys
1377
1378#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...
Execution policies (decisions.md [execution-policies]): execution::sequential, the ExecutionPolicy co...
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]):...
ImmRecursion and ImmFilter (decisions.md [switching-models], [imm], [filters-and-recursions]): a Hybr...
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]...
Moments: the mean and covariance that a Gaussian carries and that a mixture or a sample set is moment...
Products of fixed-size matrices by Eigen's fixed-size product at every size: multiply() is by Eigen'...
NumericError, the reasons a factorization or a normalization can fail, and its two names,...
The state of a Rao-Blackwellized particle filter (decisions.md [rb-particle-filter]): ParticleGaussia...
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...
The update that the Rao-Blackwellized filters share (decisions.md [rb-point-mass],...
The Recursion concept (decisions.md [filters-and-recursions], [user-extension-points]): what a Kalman...
Reducing an update's branches (decisions.md [weighted-branches], [reduction-policies],...
SampleSet: a matrix whose columns are samples in a space, which the samplers return and the unscented...
Drawing samples: SamplesWithLogDensity and SampleWithLogDensity, the Gaussian draws drawSamples(),...
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...
A Gaussian distribution: a mean and covariance in a space.
Definition Gaussian.h:259
DifferenceMatrix< Space_, Space_, Scalar > MatrixType
a matrix over differences, such as the covariance
Definition Moments.h:76
ValueVector< Space_, Scalar > VectorType
a value, such as the mean
Definition Moments.h:74
static constexpr int DifferenceDimensionAtCompileTime
the length of a difference and the rows of a covariance, or Eigen::Dynamic
Definition Moments.h:69
The parameters of a Rao-Blackwellized particle filter's state: weighted particles for one block of th...
Definition ParticleGaussian.h:192
Params< Component_ > ConditionalType
a particle's distribution over the rest: a Gaussian or a hybrid
Definition ParticleGaussian.h:226
What a Rao-Blackwellized particle filter's update did, per particle and in total.
Definition RbParticleFilter.h:162
Scalar logLikelihood() const
Definition RbParticleFilter.h:205
Eigen::Index innovationSize() const
Definition RbParticleFilter.h:191
RbParticleFilterUpdate(Allocator const &allocator={})
An empty result whose arrays come from allocator.
Definition RbParticleFilter.h:169
Allocator allocator_type
where the per-particle arrays come from
Definition RbParticleFilter.h:166
Eigen::Map< Eigen::Array< Scalar, Eigen::Dynamic, 1 > const > ArrayType
a per-particle array
Definition RbParticleFilter.h:165
ArrayType nis() const
Definition RbParticleFilter.h:184
bool accepted
Definition RbParticleFilter.h:210
allocator_type get_allocator() const
Definition RbParticleFilter.h:198
ArrayType logLikelihoods() const
Definition RbParticleFilter.h:175
typename StateType::ConditionalType ConditionalType
a particle's distribution over the rest
Definition RbParticleFilter.h:361
std::expected< void, NumericError > update(P &&executionPolicy, M const &measurement, RbParticleFilterUpdate< Scalar, ResultAllocator > &result)
update(measurement, result) under executionPolicy, as update(executionPolicy,measurement) runs.
Definition RbParticleFilter.h:680
std::expected< UpdateType, NumericError > update(P &&executionPolicy, M const &measurement)
update() under executionPolicy: each particle's update is a for-each and the evidence a reduction,...
Definition RbParticleFilter.h:652
Allocator_ allocator_type
the state's allocator
Definition RbParticleFilter.h:362
std::expected< UpdateType, NumericError > update(M const &measurement)
Conditions each particle's Gaussian on measurement given the particle, by the measurement's own metho...
Definition RbParticleFilter.h:642
auto predict(G &rng, Inputs const &... inputs)
Moves each particle and its Gaussian forward by the fixed-step model's step, as predict(rng,...
Definition RbParticleFilter.h:533
Model_ const & model() const
Definition RbParticleFilter.h:443
RbParticleFilter(std::type_identity_t< Model_ > model, StateType state, MethodFor< Model_, Method_ > method={}, std::type_identity_t< Upkeep_ > upkeep={})
A filter with process model model over the whole state, initial state state, prediction method method...
Definition RbParticleFilter.h:370
std::expected< void, NumericError > update(P &&executionPolicy, M const &measurement, Policy const &policy, RbParticleFilterUpdate< Scalar, ResultAllocator > &result)
update(measurement, policy, result) under executionPolicy, as update(executionPolicy,...
Definition RbParticleFilter.h:774
Method_ const & method() const
Definition RbParticleFilter.h:457
Space_ SpaceType
the state space
Definition RbParticleFilter.h:350
Model_ & model()
Definition RbParticleFilter.h:450
std::expected< void, NumericError > update(M const &measurement, Policy const &policy, RbParticleFilterUpdate< Scalar, ResultAllocator > &result)
As update(measurement, policy), but into result, whose storage is reused (see update(measurement,...
Definition RbParticleFilter.h:763
std::expected< UpdateType, NumericError > update(M const &measurement, Policy const &policy)
As update(measurement), with PerPointCovariance, but with each particle's branches reduced by policy ...
Definition RbParticleFilter.h:732
auto predict(P &&policy, G &rng, Inputs const &... inputs)
predict(rng, inputs...) under policy, as predict(policy, rng, dt, inputs...) runs.
Definition RbParticleFilter.h:543
RbParticleFilterUpdate< Scalar, Allocator_ > UpdateType
The result of update(): the per-particle arrays from the filter's allocator.
Definition RbParticleFilter.h:618
auto predict(P &&policy, G &rng, Steps steps, Inputs const &... inputs)
predict(rng, steps, inputs...) under policy, as that many calls of predict(policy,...
Definition RbParticleFilter.h:594
auto predict(P &&policy, G &rng, TimeStep< Scalar_ > auto const &dt, Inputs const &... inputs)
predict(rng, dt, inputs...) under policy: the draws are taken first, as the sequential prediction tak...
Definition RbParticleFilter.h:490
auto predict(G &rng, TimeStep< Scalar_ > auto const &dt, Inputs const &... inputs)
Moves each particle and its Gaussian forward by dt, any TimeStep (see Time steps): predicts the whole...
Definition RbParticleFilter.h:477
ParticleGaussianParams< Space_, Path_, Covariance_, Scalar_, Allocator_, typename internal::ParticleComponentOf< RestSpace, Scalar_, Allocator_, Model_ >::type > StateType
The state the filter owns: a Gaussian per particle, or a hybrid under a switching model.
Definition RbParticleFilter.h:354
Scalar_ Scalar
the scalar type
Definition RbParticleFilter.h:349
std::expected< UpdateType, NumericError > update(P &&executionPolicy, M const &measurement, Policy const &policy)
update(measurement, policy) under executionPolicy: each particle's Kalman update and reduction by pol...
Definition RbParticleFilter.h:744
StateType & state()
Definition RbParticleFilter.h:413
Model_ ModelType
the process model
Definition RbParticleFilter.h:351
auto predict(G &rng, Steps steps, Inputs const &... inputs)
Moves each particle and its Gaussian forward by steps of the fixed-step model, as that many calls of ...
Definition RbParticleFilter.h:583
allocator_type get_allocator() const
Definition RbParticleFilter.h:397
std::expected< void, NumericError > update(M const &measurement, RbParticleFilterUpdate< Scalar, ResultAllocator > &result)
As update(measurement), but into result, whose storage is reused: an update into a result kept across...
Definition RbParticleFilter.h:670
Method_ MethodType
the prediction method
Definition RbParticleFilter.h:352
void setState(StateType state)
Replaces the state, as when reinitializing, with one whose space the process model's writes partition...
Definition RbParticleFilter.h:423
RbParticleFilter(std::type_identity_t< Model_ > model, StateType state, std::type_identity_t< Upkeep_ > upkeep)
A filter with process model model over the whole state, initial state state, the default prediction m...
Definition RbParticleFilter.h:390
StateType const & state() const
Definition RbParticleFilter.h:404
An execution policy: execution::sequential, a policy of the standard library such as std::execution::...
Definition Execution.h:459
A type usable as the scalar of a distribution: a real number type such as float, double,...
Definition Math.h:80
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
@ NotPositiveDefinite
the covariance is not positive definite, so it has no Cholesky factor or inverse
Definition NumericError.h:31
Eigen::Matrix< Scalar, valueDimensionAtCompileTime< Space >, Eigen::Dynamic,(maxValueDimensionAtCompileTime< Space >==1 ? Eigen::RowMajor :Eigen::ColMajor), maxValueDimensionAtCompileTime< Space >, Eigen::Dynamic > SampleSet
A matrix whose columns are samples in Space (see Spaces), with the space's size and storage bound.
Definition SampleSet.h:33
CategoricalParams< Space, Scalar > predict(CategoricalParams< Space, Scalar > const &prior, MarkovChain< Space, Scalar > const &chain)
Definition Categorical.h:1049
constexpr Sequential sequential
The default execution policy: plain loops on the calling thread, in index order (Sequential).
Definition Execution.h:128
constexpr Collapse collapse()
Definition Reduction.h:291
Derived::Scalar logLikelihood(M const &measurement, Space const &space, Eigen::MatrixBase< Derived > const &x)
Definition Likelihood.h:433
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
constexpr bool isFixedStep
Whether Model is a FixedStepModel, whose predictions take no time step.
Definition ProcessModel.h:2776
Moments< Space_, typenameDerived::Scalar >::VectorType propagate(Space_ const &space, Eigen::MatrixBase< Derived > const &x, Model const &model, TimeStep< typename Derived::Scalar > auto const &dt, Args const &... args)
Definition ProcessModel.h:2754
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
auto plus(S const &space, Eigen::MatrixBase< XDerived > const &x, Eigen::MatrixBase< DDerived > const &d)
Definition Space.h:675
auto minus(S const &space, Eigen::MatrixBase< ADerived > const &a, Eigen::MatrixBase< BDerived > const &b)
Definition Space.h:715
constexpr int valueDimensionAtCompileTime
The value dimension of Space at compile time, or Eigen::Dynamic if it is resizable.
Definition Space.h:251
void normalizeValues(S const &space, Eigen::MatrixBase< Derived > const &block)
Brings the values in block (rows are space's coordinates, columns samples) into canonical form,...
Definition Space.h:331
constexpr None none()
Definition Upkeep.h:903
The upkeep operations, each made by its factory (decisions.md [policy-factories], [upkeep]): the acti...
Definition Upkeep.h:380
Rows of a contiguous block of a space.
Definition TagPath.h:825
Eigen::Index size
number of rows
Definition TagPath.h:827
What a container does with a component beyond the component's own members (decisions....
Definition Component.h:104
A block of a state given from outside the Gaussian being updated, as given() makes it,...
Definition Measurement.h:723
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
The upkeep a filter holds when it is given none: it takes any state, changes nothing,...
Definition Upkeep.h:889