EmrysFilter 0.1.0
State estimation utilities in C++23 using Eigen
Loading...
Searching...
No Matches
MeasurementModel.h
Go to the documentation of this file.
1#pragma once
2
11
12#include <array>
13#include <concepts>
14#include <cstddef>
15#include <functional>
16#include <tuple>
17#include <type_traits>
18#include <utility>
19
33
34namespace Emrys
35{
36
37namespace internal
38{
39
41struct NoFunction
42{};
43
48struct NoOutputSpace
49{};
50
54template <typename S>
55inline constexpr int maxCountOf =
56 S::ValueDimensionStep == 0 ? 0
57 : maxValueDimensionAtCompileTime<S> == Eigen::Dynamic
58 ? Eigen::Dynamic
59 : (maxValueDimensionAtCompileTime<S> - S::FixedValueDimension) / S::ValueDimensionStep;
60
65template <typename In>
66inline constexpr bool blocksAddValues = [] {
67 if constexpr (IsComposite<In>)
68 {
69 return []<typename... Blocks>(std::tuple<Blocks...> *) {
70 return ((!HasPlus<Blocks> && hasEqualSizes<Blocks>) && ...);
71 }(static_cast<CompositeBlocks<In> *>(nullptr));
72 }
73 else
74 {
75 return !HasPlus<In> && hasEqualSizes<In>;
76 }
77}();
78
79} // namespace internal
80
102template <typename Path, Space MeasurementSpace>
104{
105 static_assert(
106 internal::resizableBlockCount<MeasurementSpace> == 1,
107 "CountFrom's measurement space must have exactly one resizable block"
108 );
109
111 template <Space StateSpace>
112 [[nodiscard]] MeasurementSpace
113 operator()(StateSpace const & space) const
114 {
115 using Block = internal::PathSubspaceType<StateSpace, Path>;
116 static_assert(
117 internal::resizableBlockCount<Block> == 1,
118 "CountFrom's block of the state must have exactly one resizable block"
119 );
120 static_assert(
121 internal::maxCountOf<MeasurementSpace> == Eigen::Dynamic
122 || (internal::maxCountOf<Block> != Eigen::Dynamic
123 && internal::maxCountOf<MeasurementSpace> >= internal::maxCountOf<Block>),
124 "CountFrom's measurement space must hold a block per repetition of the state's block at its largest"
125 );
126 Eigen::Index const count = (valueDimension(internal::pathSubspace<Path>(space)) - Block::FixedValueDimension)
127 / Block::ValueDimensionStep;
128 auto result = MeasurementSpace{};
129 result.resize(MeasurementSpace::FixedValueDimension + count * MeasurementSpace::ValueDimensionStep);
130 return result;
131 }
132};
133
154template <typename F, typename... Policies>
156{
158 using Set = internal::PolicySet<Policies...>;
159
160public:
161
162 using FunctionType = F;
163
164 static constexpr bool IsModel = true;
165
167 static constexpr bool HasFunction = !std::is_same_v<F, internal::NoFunction>;
168
170 template <internal::PolicyKind K>
171 static constexpr bool has = Set::template has<K>;
172
174 template <internal::PolicyKind K>
175 using Policy = typename Set::template Type<K>;
176
178 explicit MeasurementFunction(F function, Policies... policies)
179 : m_function{std::move(function)}
180 , m_policies{std::move(policies)...}
181 {}
182
184 [[nodiscard]] F const &
185 function() const
186 {
187 return m_function;
188 }
189
191 template <internal::PolicyKind K>
192 [[nodiscard]] Policy<K> const &
193 policy() const
194 {
195 return Set::template get<K>(m_policies);
196 }
197
199 [[nodiscard]] std::tuple<Policies...> const &
200 policies() const
201 {
202 return m_policies;
203 }
204
205private:
206
207 EMRYS_FILTER_NO_UNIQUE_ADDRESS F m_function;
208 std::tuple<Policies...> m_policies;
209};
210
211namespace internal
212{
213
215template <typename T>
216inline constexpr bool isMeasurementFunction = false;
217
219template <typename F, typename... Policies>
220inline constexpr bool isMeasurementFunction<MeasurementFunction<F, Policies...>>{true};
222
223} // namespace internal
224
230template <typename M>
231concept MeasurementModel = internal::isMeasurementFunction<std::remove_cvref_t<M>>;
232
233namespace internal
234{
235
240template <typename Matrix>
241class LinearFunction
242{
243public:
244
247
249 explicit LinearFunction(Matrix matrix)
250 : m_matrix{std::move(matrix)}
251 {}
252
254 [[nodiscard]] Matrix const &
255 matrix() const
256 {
257 return m_matrix;
258 }
259
261 [[nodiscard]] Eigen::Index
262 outputRows() const
263 {
264 return m_matrix.rows();
265 }
266
270 template <typename In, typename Out, typename X>
271 [[nodiscard]] auto
272 operator()([[maybe_unused]] In const & in, Out const &, X const & x) const
273 {
274 static_assert(
275 blocksAddValues<In>,
276 "a linear measurement model reads blocks with no plus() of their own; a block such as a unit quaternion "
277 "has no linear measurement, so give measurementModel() a function"
278 );
279 static_assert(
280 Matrix::ColsAtCompileTime == Eigen::Dynamic || valueDimensionAtCompileTime<In> == Eigen::Dynamic
281 || Matrix::ColsAtCompileTime == valueDimensionAtCompileTime<In>,
282 "a linear measurement model's matrix must have a column per coordinate of the blocks it reads"
283 );
284 EMRYS_FILTER_ASSERT(
285 m_matrix.cols() == x.size()
286 && "a linear measurement model's matrix must have a column per coordinate of the blocks it reads"
287 );
288 using T = typename X::Scalar;
289 using Value = Eigen::Matrix<T, Matrix::RowsAtCompileTime, 1, Eigen::ColMajor, Matrix::MaxRowsAtCompileTime, 1>;
290 Value value = m_matrix.template cast<T>() * x;
291 return WithJacobian<Value, Matrix const &>{std::move(value), m_matrix};
292 }
293
294private:
295
296 Matrix m_matrix;
297};
298
302template <typename T>
303struct ColumnsOf
304{
305 static constexpr bool IsPlaced = false;
306 static constexpr bool Each = false;
307 static constexpr int Rows = Eigen::Dynamic;
308 using Path = void;
309};
310
312template <MatrixExpression M, typename P>
313struct ColumnsOf<OnPath<M, P>>
314{
315 static constexpr bool IsPlaced = true;
316 static constexpr bool Each = false;
317 static constexpr int Rows = M::RowsAtCompileTime;
318 using Path = P;
319};
320
322template <MatrixExpression M, typename P>
323 requires(!std::is_same_v<P, WholeSpace>)
324struct ColumnsOf<OnEach<M, P>>
325{
326 static constexpr bool IsPlaced = true;
327 static constexpr bool Each = true;
328 static constexpr int Rows = M::RowsAtCompileTime;
329 using Path = P;
330};
331
333template <int... Rows>
334inline constexpr int agreedRows = [] {
335 int result = Eigen::Dynamic;
336 for (int const rows : std::array<int, sizeof...(Rows)>{Rows...})
337 {
338 if (rows == Eigen::Dynamic)
339 {
340 continue;
341 }
342 if (result != Eigen::Dynamic && rows != result)
343 {
344 return 0;
345 }
346 result = rows;
347 }
348 return result;
349}();
350
352template <typename... Placed>
353struct FirstEachPathOf
354{
356 using type = void;
357};
358
360template <typename First, typename... Rest>
361struct FirstEachPathOf<First, Rest...>
362{
364 using type = std::
365 conditional_t<ColumnsOf<First>::Each, typename ColumnsOf<First>::Path, typename FirstEachPathOf<Rest...>::type>;
366};
367
368} // namespace internal
369
394template <typename... Placed>
396{
397 static_assert(sizeof...(Placed) > 0, "a model's matrix needs at least one block of columns");
398 static_assert(
399 (internal::ColumnsOf<Placed>::IsPlaced && ...),
400 "each block of a model's columns is a matrix placed on a block of the state, with on<Path>() or "
401 "onEach<Path>()"
402 );
403 static_assert(
404 !(internal::ColumnsOf<Placed>::IsPlaced && ...)
405 || internal::distinctTypes<internal::AsTagPath<typename internal::ColumnsOf<Placed>::Path>...>(),
406 "a model's columns name each block of the state once"
407 );
408 static_assert(
409 internal::agreedRows<internal::ColumnsOf<Placed>::Rows...> != 0,
410 "every block of a model's columns has the rows of one repetition of the measurement"
411 );
412
413public:
414
416 static constexpr bool Repeats = (internal::ColumnsOf<Placed>::Each || ...);
417
419 static constexpr int Rows = internal::agreedRows<internal::ColumnsOf<Placed>::Rows...>;
420
423
425 using FirstEachPath = typename internal::FirstEachPathOf<Placed...>::type;
426
428 explicit Columns(Placed... placed)
429 : m_placed{std::move(placed)...}
430 {
431 EMRYS_FILTER_ASSERT(
432 std::apply(
433 [&](auto const &... each) {
434 return ((each.value.rows() == rows()) && ...);
435 },
436 m_placed
437 )
438 && "every block of a model's columns has the rows of one repetition of the measurement"
439 );
440 }
441
443 [[nodiscard]] std::tuple<Placed...> const &
444 placed() const
445 {
446 return m_placed;
447 }
448
450 [[nodiscard]] Eigen::Index
451 rows() const
452 {
453 return std::get<0>(m_placed).value.rows();
454 }
455
456private:
457
458 std::tuple<Placed...> m_placed;
459};
460
461namespace internal
462{
463
465template <typename T>
466inline constexpr bool isColumns = false;
467
469template <typename... Placed>
470inline constexpr bool isColumns<Columns<Placed...>>{true};
471
473
478template <typename Path, int Rows>
479struct LinearPerRepetition
480{
482 template <typename StateSpace>
483 [[nodiscard]] auto
484 operator()(StateSpace const & space) const
485 {
486 using Block = PathSubspaceType<StateSpace, Path>;
487 static_assert(
488 Block::FixedValueDimension == 0 && Block::ValueDimensionStep > 0,
489 "a model's onEach<Path>() repeats over a resizable block of the state, and the block named is fixed-size"
490 );
491 return CountFrom<Path, Repeated<Linear<Rows>, maxCountOf<Block>>>{}(space);
492 }
493};
494
501template <typename... Placed>
502class ColumnsFunction
503{
505 using Scalar = typename std::remove_cvref_t<
506 decltype(std::get<0>(std::declval<std::tuple<Placed...> const &>()).value)>::Scalar;
507
508public:
509
513 using OutputSpaceType = Linear<Columns<Placed...>::Rows>;
514
516 explicit ColumnsFunction(Columns<Placed...> columns)
517 : m_columns{std::move(columns)}
518 {}
519
521 [[nodiscard]] Columns<Placed...> const &
522 columns() const
523 {
524 return m_columns;
525 }
526
530 [[nodiscard]] Eigen::Index
531 outputRows() const
532 requires(!Columns<Placed...>::Repeats)
533 {
534 return m_columns.rows();
535 }
536
540 template <typename In, typename Out, typename X>
541 [[nodiscard]] auto
542 operator()(In const & in, Out const & out, X const & x) const
543 {
544 static_assert(
545 blocksAddValues<In>,
546 "a linear measurement model reads blocks with no plus() of their own; a block such as a unit quaternion "
547 "has no linear measurement, so give measurementModel() a function"
548 );
549 using T = typename X::Scalar;
550 constexpr int OutRows = Columns<Placed...>::Repeats ? Eigen::Dynamic : Columns<Placed...>::Rows;
551 constexpr int OutMaxRows =
552 Columns<Placed...>::Repeats ? maxValueDimensionAtCompileTime<Out> : Columns<Placed...>::Rows;
553 using Value = Eigen::Matrix<T, OutRows, 1, Eigen::ColMajor, OutMaxRows, 1>;
554 using Jacobian =
555 MatrixOf<Scalar, OutRows, valueDimensionAtCompileTime<In>, OutMaxRows, maxValueDimensionAtCompileTime<In>>;
556 Eigen::Index const rows = m_columns.rows();
557 Eigen::Index const count = [&] {
558 if constexpr (Columns<Placed...>::Repeats)
559 {
560 Eigen::Index const repetitions = valueDimension(out) / rows;
561 EMRYS_FILTER_ASSERT(
562 repetitions * rows == valueDimension(out)
563 && "a linear model's matrices have a row per coordinate of one repetition of the measurement"
564 );
565 return repetitions;
566 }
567 else
568 {
569 return Eigen::Index{1};
570 }
571 }();
572 Jacobian jacobian = Jacobian::Zero(count * rows, valueDimension(in));
573 Eigen::Index column = 0;
574 std::apply(
575 [&](auto const &... placed) {
576 (place(placed, in, count, column, jacobian), ...);
577 },
578 m_columns.placed()
579 );
580 EMRYS_FILTER_ASSERT(
581 column == valueDimension(in)
582 && "a linear model's matrices must have a column per coordinate of the blocks they read"
583 );
584 Value value = jacobian.template cast<T>() * x;
585 return WithJacobian<Value, Jacobian>{std::move(value), std::move(jacobian)};
586 }
587
588private:
589
594 template <typename P, typename In, typename Jacobian>
595 static void
596 place(P const & placed, In const & in, Eigen::Index count, Eigen::Index & column, Jacobian & jacobian)
597 {
598 using C = ColumnsOf<P>;
599 using Name = PathTag<typename C::Path>;
600 auto const & matrix = placed.value;
601 Eigen::Index const rows = matrix.rows();
602 Eigen::Index const width = valueDimension(pathSubspace<Name>(in));
603 if constexpr (C::Each)
604 {
605 using Block = PathSubspaceType<In, Name>;
606 static_assert(
607 Block::FixedValueDimension == 0 && Block::ValueDimensionStep > 0,
608 "a model's onEach<Path>() names a repeated block of the state"
609 );
610 constexpr int Cols = std::remove_cvref_t<decltype(matrix)>::ColsAtCompileTime;
611 static_assert(
612 Cols == Eigen::Dynamic || Cols == Block::ValueDimensionStep,
613 "a model's onEach<Path>() matrix has a column per coordinate of one repetition of the block"
614 );
615 EMRYS_FILTER_ASSERT(
616 matrix.cols() * count == width
617 && "a model's onEach<Path>() blocks have the measurement's count of repetitions"
618 );
619 for (Eigen::Index i = 0; i < count; ++i)
620 {
621 jacobian.block(i * rows, column + i * matrix.cols(), rows, matrix.cols()) = matrix;
622 }
623 }
624 else
625 {
626 EMRYS_FILTER_ASSERT(
627 matrix.cols() == width
628 && "a linear model's matrix must have a column per coordinate of the block it reads"
629 );
630 for (Eigen::Index i = 0; i < count; ++i)
631 {
632 jacobian.block(i * rows, column, rows, matrix.cols()) = matrix;
633 }
634 }
635 column += width;
636 }
637
638 Columns<Placed...> m_columns;
639};
640
642template <PolicyKind K, typename... Policies>
643inline constexpr bool hasPolicy = ((policyKind<std::remove_cvref_t<Policies>> == K) || ...);
644
646template <typename... Paths>
647[[nodiscard]] auto
648readsOf(TagPaths<Paths...> *)
649{
650 return reads(path<Paths>...);
651}
652
659template <typename Set>
660[[nodiscard]] consteval bool
661checkMeasurementPolicies()
662{
663 static_assert(
664 !Set::template has<PolicyKind::continuousNoise>,
665 "continuousNoise() is a process model's; a measurement model's noise is additiveNoise()"
666 );
667 static_assert(
668 !Set::template has<PolicyKind::affineIn> || Set::template has<PolicyKind::reads>,
669 "affineIn() names items of reads(), and a model without reads() reads the whole state under no names"
670 );
671 return true;
672}
673
679template <typename F>
680struct CallArityOf
681{
682 static constexpr int value = -1;
683};
684
686template <typename F>
687 requires requires { &F::operator(); }
688struct CallArityOf<F> : CallArityOf<decltype(&F::operator())>
689{};
690
691// Each signature is spelled with and without noexcept, since MSVC does not deduce a noexcept(B)
692// in a partial specialization (C2764).
693template <typename... A>
694struct ArityOfParameters
695{
696 static constexpr int value = sizeof...(A);
697};
698
699template <typename R, typename... A>
700struct CallArityOf<R (*)(A...)> : ArityOfParameters<A...>
701{};
702
703template <typename R, typename... A>
704struct CallArityOf<R (*)(A...) noexcept> : ArityOfParameters<A...>
705{};
706
707template <typename R, typename C, typename... A>
708struct CallArityOf<R (C::*)(A...)> : ArityOfParameters<A...>
709{};
710
711template <typename R, typename C, typename... A>
712struct CallArityOf<R (C::*)(A...) noexcept> : ArityOfParameters<A...>
713{};
714
715template <typename R, typename C, typename... A>
716struct CallArityOf<R (C::*)(A...) const> : ArityOfParameters<A...>
717{};
718
719template <typename R, typename C, typename... A>
720struct CallArityOf<R (C::*)(A...) const noexcept> : ArityOfParameters<A...>
721{};
722
723template <typename R, typename C, typename... A>
724struct CallArityOf<R (C::*)(A...) const &> : ArityOfParameters<A...>
725{};
726
727template <typename R, typename C, typename... A>
728struct CallArityOf<R (C::*)(A...) const & noexcept> : ArityOfParameters<A...>
729{};
730
732
737template <typename F, typename Set>
738[[nodiscard]] consteval auto
739knownOutputType()
740{
741 if constexpr (Set::template has<PolicyKind::outputSpace>)
742 {
743 return std::type_identity<void>{};
744 }
745 else if constexpr (Set::template has<PolicyKind::writes>)
746 {
747 return std::type_identity<typename Set::template Type<PolicyKind::writes>::OutputSpace>{};
748 }
749 else if constexpr (requires { typename F::OutputSpaceType; })
750 {
751 return std::type_identity<typename F::OutputSpaceType>{};
752 }
753 else
754 {
755 return std::type_identity<void>{};
756 }
757}
758
760template <typename S>
761inline constexpr int differencesAtCompileTime = valueDimensionAtCompileTime<DifferenceSpace<S>>;
762
766template <typename X>
767[[nodiscard]] consteval int
768noiseRowsAtCompileTime()
769{
770 if constexpr (MatrixExpression<X>)
771 {
772 return X::RowsAtCompileTime;
773 }
774 else if constexpr (requires { typename X::SpaceType; })
775 {
776 return differencesAtCompileTime<typename X::SpaceType>;
777 }
778 else
779 {
780 return Eigen::Dynamic;
781 }
782}
783
787[[nodiscard]] consteval bool
788noiseRowsFit(int rows, int coordinates)
789{
790 return rows == Eigen::Dynamic || coordinates == Eigen::Dynamic || rows == coordinates;
791}
792
794template <typename Out, typename X>
795[[nodiscard]] consteval bool
796noisePartFits(X const *)
797{
798 return noiseRowsFit(noiseRowsAtCompileTime<X>(), differencesAtCompileTime<Out>);
799}
800
802template <typename Out, typename X, typename Path>
803[[nodiscard]] consteval bool
804noisePartFits(OnPath<X, Path> const *)
805{
806 using Block = typename EachBlockOf<Out, Path>::type;
807 return noiseRowsFit(noiseRowsAtCompileTime<std::remove_cvref_t<X>>(), differencesAtCompileTime<Block>);
808}
809
813template <typename Out, typename X, typename Path>
814[[nodiscard]] consteval bool
815noisePartFits(OnEach<X, Path> const *)
816{
817 using Block = typename EachBlockOf<Out, Path>::type;
818 if constexpr (requires { typename Block::BlockType; })
819 {
820 return noiseRowsFit(
821 noiseRowsAtCompileTime<std::remove_cvref_t<X>>(),
822 differencesAtCompileTime<typename Block::BlockType>
823 );
824 }
825 else
826 {
827 return true;
828 }
829}
830
834template <typename Out, typename... Parts>
835[[nodiscard]] consteval bool
836noiseFits(AdditiveNoise<Parts...> const *)
837{
838 return (noisePartFits<Out>(static_cast<std::remove_cvref_t<Parts> const *>(nullptr)) && ...);
839}
840
853template <typename F, typename Set>
854[[nodiscard]] consteval bool
855checkMeasurementFunction()
856{
857 constexpr int Arity = CallArityOf<F>::value;
858 static_assert(
859 Arity < 0 || (Arity >= 1 && Arity <= 3),
860 "a measurement model's function takes one to three arguments, h(x), h(in, x), or h(in, out, x), with in the "
861 "input space, out the output space, and x a vector in the input space"
862 );
863 static_assert(
864 !Set::template has<PolicyKind::sample>,
865 "a measurement model with a function draws its observation through the function and its noise, so sample(g) "
866 "is for a model known by its likelihood alone"
867 );
868 using Known = typename decltype(knownOutputType<F, Set>())::type;
869 if constexpr (!std::is_void_v<Known> && Set::template has<PolicyKind::additiveNoise>)
870 {
871 static_assert(
872 noiseFits<Known>(static_cast<typename Set::template Type<PolicyKind::additiveNoise> const *>(nullptr)),
873 "a measurement model's additive noise has a row for each difference coordinate of the output space, from "
874 "writes() or the matrix's rows, or of the block it is placed on"
875 );
876 }
877 return true;
878}
879
880} // namespace internal
881
910template <typename F, typename... Policies>
911 requires(!MatrixExpression<F> && !internal::ModelPolicy<F> && !internal::isColumns<std::remove_cvref_t<F>>)
912[[nodiscard]] auto
913measurementModel(F && function, Policies &&... policies)
914{
915 using Set = internal::PolicySet<std::remove_cvref_t<Policies>...>;
916 static_assert(internal::checkMeasurementPolicies<Set>());
917 static_assert(internal::checkMeasurementFunction<std::decay_t<F>, Set>());
918 return MeasurementFunction<std::decay_t<F>, std::remove_cvref_t<Policies>...>{
919 std::forward<F>(function),
920 std::forward<Policies>(policies)...
921 };
922}
923
937template <MatrixExpression Matrix, typename... Policies>
938[[nodiscard]] auto
939measurementModel(Matrix && matrix, Policies &&... policies)
940{
941 using Plain = internal::PlainResult<std::remove_cvref_t<Matrix>>;
942 using Set = internal::PolicySet<std::remove_cvref_t<Policies>...>;
943 static_assert(internal::checkMeasurementPolicies<Set>());
944 static_assert(internal::checkMeasurementFunction<internal::LinearFunction<Plain>, Set>());
945 return MeasurementFunction<internal::LinearFunction<Plain>, std::remove_cvref_t<Policies>...>{
946 internal::LinearFunction<Plain>{Plain{std::forward<Matrix>(matrix)}},
947 std::forward<Policies>(policies)...
948 };
949}
950
957template <typename... Placed, typename... Policies>
958[[nodiscard]] auto
959measurementModel(Columns<Placed...> columns, Policies &&... policies)
960{
961 using ColumnsType = Columns<Placed...>;
962 static_assert(
963 !internal::hasPolicy<internal::PolicyKind::reads, Policies...>,
964 "a model given Columns reads the blocks its columns are placed on, so it takes no reads()"
965 );
966 auto const read = internal::readsOf(static_cast<typename ColumnsType::Paths *>(nullptr));
967 using Function = internal::ColumnsFunction<Placed...>;
968 if constexpr (ColumnsType::Repeats && !internal::hasPolicy<internal::PolicyKind::outputSpace, Policies...>)
969 {
970 static_assert(
971 ColumnsType::Rows != Eigen::Dynamic,
972 "a model whose columns repeat infers its measurement space from its matrices' rows, so fix them at "
973 "compile time, or give it outputSpace() with a function such as CountFrom"
974 );
975 auto const space =
976 outputSpace(internal::LinearPerRepetition<typename ColumnsType::FirstEachPath, ColumnsType::Rows>{});
977 using Set = internal::PolicySet<decltype(read), decltype(space), std::remove_cvref_t<Policies>...>;
978 static_assert(internal::checkMeasurementFunction<Function, Set>());
979 return MeasurementFunction<Function, decltype(read), decltype(space), std::remove_cvref_t<Policies>...>{
980 Function{std::move(columns)},
981 read,
982 space,
983 std::forward<Policies>(policies)...
984 };
985 }
986 else
987 {
988 using Set = internal::PolicySet<decltype(read), std::remove_cvref_t<Policies>...>;
989 static_assert(internal::checkMeasurementFunction<Function, Set>());
990 return MeasurementFunction<Function, decltype(read), std::remove_cvref_t<Policies>...>{
991 Function{std::move(columns)},
992 read,
993 std::forward<Policies>(policies)...
994 };
995 }
996}
997
1005template <internal::ModelPolicy... Policies>
1006[[nodiscard]] auto
1007measurementModel(Policies &&... policies)
1008{
1009 using Set = internal::PolicySet<std::remove_cvref_t<Policies>...>;
1010 static_assert(internal::checkMeasurementPolicies<Set>());
1011 static_assert(
1012 Set::template has<internal::PolicyKind::logLikelihood>,
1013 "a measurement model without a function is known by its likelihood alone, so it takes logLikelihood(g)"
1014 );
1015 static_assert(
1016 !Set::template has<internal::PolicyKind::additiveNoise>,
1017 "a measurement model known by its likelihood alone has no function to add noise to"
1018 );
1020 internal::NoFunction{},
1021 std::forward<Policies>(policies)...
1022 };
1023}
1024
1025namespace internal
1026{
1027
1031struct NoMethod
1032{};
1033
1035struct NoObservation
1036{};
1037
1039enum class ArgumentKind
1040{
1041 uncertain,
1042 sure,
1043 form,
1044 repair,
1045 consider,
1046 reference,
1047 other
1048};
1049
1051template <typename T>
1052[[nodiscard]] T const &
1053inputOf(T const & argument)
1054{
1055 return argument;
1056}
1057
1059template <typename T>
1060inline constexpr ArgumentKind argumentKind = [] {
1061 using Plain = std::remove_cvref_t<T>;
1062 if constexpr (isSure<Plain>)
1063 {
1064 return ArgumentKind::sure;
1065 }
1066 else if constexpr (UncertainInput<Plain>)
1067 {
1068 return ArgumentKind::uncertain;
1069 }
1070 else if constexpr (isLinearizedAbout<Plain>)
1071 {
1072 return ArgumentKind::reference;
1073 }
1074 else if constexpr (CovarianceUpdate<Plain>)
1075 {
1076 return ArgumentKind::form;
1077 }
1078 else if constexpr (Repair<Plain>)
1079 {
1080 return ArgumentKind::repair;
1081 }
1082 else if constexpr (isConsider<Plain>)
1083 {
1084 return ArgumentKind::consider;
1085 }
1086 else
1087 {
1088 return ArgumentKind::other;
1089 }
1090}();
1091
1095template <typename T>
1096struct InputTagOf
1097{
1098 using type = void;
1099};
1100
1102template <typename T>
1103 requires(argumentKind<T> == ArgumentKind::uncertain || argumentKind<T> == ArgumentKind::sure)
1104struct InputTagOf<T>
1105{
1106 using type =
1107 typename std::remove_cvref_t<decltype(inputOf(std::declval<std::remove_cvref_t<T> const &>()))>::SpaceType;
1108};
1109
1111
1113template <typename T>
1114using InputTag = typename InputTagOf<T>::type;
1115
1123template <typename... Args>
1124[[nodiscard]] consteval bool
1125checkArguments()
1126{
1127 static_assert(
1128 ((argumentKind<Args> != ArgumentKind::other) && ...),
1129 "an argument after the observation is an uncertain input over a tagged space, sure<Tag>(value), a covariance "
1130 "update form such as updates::joseph(), a repair of the innovation covariance such as roots::ridge(), or the "
1131 "blocks to consider, consider(path<Bias>); after a prediction's step, an uncertain or a sure input, or a "
1132 "linearizedAbout() reference first"
1133 );
1134 static_assert(
1135 (std::size_t{argumentKind<Args> == ArgumentKind::reference} + ... + 0) <= 1,
1136 "give the linearizedAbout() reference once"
1137 );
1138 static_assert(
1139 ((argumentKind<Args> != ArgumentKind::uncertain || !IsUntagged<InputTag<Args>>) && ...),
1140 "an uncertain input is named by the tag of its space, so give it over a tag such as struct SpecificForce : "
1141 "Linear<3> {}"
1142 );
1143 static_assert(
1144 (std::size_t{argumentKind<Args> == ArgumentKind::form} + ... + 0) <= 1,
1145 "give the covariance update form once"
1146 );
1147 static_assert(
1148 (std::size_t{argumentKind<Args> == ArgumentKind::repair} + ... + 0) <= 1,
1149 "give the repair of the innovation covariance once"
1150 );
1151 static_assert(
1152 (std::size_t{argumentKind<Args> == ArgumentKind::consider} + ... + 0) <= 1,
1153 "give the blocks to consider in one consider()"
1154 );
1155 static_assert(
1156 distinctTypes<
1157 std::conditional_t<std::is_void_v<InputTag<Args>>, std::type_identity<Args>, InputTag<Args>>...>(),
1158 "two inputs of the call have one tag; an input's tag names it once"
1159 );
1160 return true;
1161}
1162
1164template <ArgumentKind K, typename Default, typename... Args>
1165struct ArgumentOfKindOf
1166{
1167 using type = Default;
1168};
1169
1171template <ArgumentKind K, typename Default, typename First, typename... Rest>
1172struct ArgumentOfKindOf<K, Default, First, Rest...>
1173{
1174 using type = std::conditional_t<
1175 argumentKind<First> == K,
1176 std::remove_cvref_t<First>,
1177 typename ArgumentOfKindOf<K, Default, Rest...>::type>;
1178};
1179
1181
1183template <ArgumentKind K, typename Default, typename... Args>
1184using ArgumentOfKind = typename ArgumentOfKindOf<K, Default, Args...>::type;
1185
1187template <ArgumentKind K, typename Default, typename... Args>
1188[[nodiscard]] auto const &
1189argumentOfKind(std::tuple<Args...> const & args, Default const & fallback)
1190{
1191 using Result = ArgumentOfKind<K, Default, Args...>;
1192 constexpr std::size_t position = [] {
1193 constexpr auto kinds = std::array<ArgumentKind, sizeof...(Args)>{argumentKind<Args>...};
1194 for (std::size_t i = 0; i < kinds.size(); ++i)
1195 {
1196 if (kinds[i] == K)
1197 {
1198 return i;
1199 }
1200 }
1201 return sizeof...(Args);
1202 }();
1203 if constexpr (position < sizeof...(Args))
1204 {
1205 return static_cast<Result const &>(std::get<position>(args));
1206 }
1207 else
1208 {
1209 return fallback;
1210 }
1211}
1212
1213} // namespace internal
1214
1230template <typename Method, typename Model, typename Observation, typename... Args>
1232{
1233public:
1234
1235 using MethodType = Method;
1236 using ModelType = std::remove_cvref_t<Model>;
1237 using ObservationType = Observation;
1238 using ArgumentsType = std::tuple<Args...>;
1239 static constexpr bool HasMethod = !std::is_same_v<Method, internal::NoMethod>;
1240
1242 static constexpr bool HasObservation = !std::is_same_v<Observation, internal::NoObservation>;
1243
1245 using FormType = internal::ArgumentOfKind<internal::ArgumentKind::form, updates::Standard, Args...>;
1246
1250 using InnovationRepair = internal::ArgumentOfKind<internal::ArgumentKind::repair, internal::NoRepair, Args...>;
1251
1253 using ConsiderType = internal::ArgumentOfKind<internal::ArgumentKind::consider, Consider<>, Args...>;
1254
1255 static_assert(internal::isModel<ModelType>, "an observation is of a model");
1256 static_assert(internal::checkArguments<Args...>());
1257
1259 template <typename M, typename Z>
1260 Observed(Method method, M && model, Z && observation, Args... args)
1261 : m_method{std::move(method)}
1262 , m_model(std::forward<M>(model))
1263 , m_observation{std::forward<Z>(observation)}
1264 , m_arguments{std::move(args)...}
1265 {
1266 if constexpr (HasObservation)
1267 {
1268 EMRYS_FILTER_ASSERT(m_observation.cols() == 1 && "an observation is a column vector");
1269 }
1270 }
1271
1273 [[nodiscard]] Method const &
1274 method() const
1275 {
1276 return m_method;
1277 }
1278
1280 [[nodiscard]] ModelType const &
1281 model() const
1282 {
1283 return m_model;
1284 }
1285
1287 [[nodiscard]] Observation const &
1289 {
1290 return m_observation;
1291 }
1292
1294 [[nodiscard]] ArgumentsType const &
1296 {
1297 return m_arguments;
1298 }
1299
1301 [[nodiscard]] FormType const &
1303 {
1304 return internal::argumentOfKind<internal::ArgumentKind::form>(m_arguments, m_standard);
1305 }
1306
1307private:
1308
1309 EMRYS_FILTER_NO_UNIQUE_ADDRESS Method m_method;
1310 Model m_model;
1311 EMRYS_FILTER_NO_UNIQUE_ADDRESS Observation m_observation;
1312 ArgumentsType m_arguments;
1314};
1315
1316namespace internal
1317{
1318
1320template <typename T>
1321inline constexpr bool isObserved = false;
1322
1324template <typename Method, typename Model, typename Observation, typename... Args>
1325inline constexpr bool isObserved<Observed<Method, Model, Observation, Args...>>{true};
1327
1328} // namespace internal
1329
1336template <typename M>
1337concept Measurement = internal::isObserved<std::remove_cvref_t<M>> && std::remove_cvref_t<M>::HasObservation;
1338
1339namespace internal
1340{
1341
1343template <typename M>
1344concept WeighsPoints = Measurement<M>;
1345
1349template <typename M>
1350inline constexpr bool hasOwnLikelihood = std::remove_cvref_t<M>::ModelType::template has<PolicyKind::logLikelihood>;
1351
1353template <typename M>
1354inline constexpr bool hasInnovationRepair =
1355 !std::is_same_v<typename std::remove_cvref_t<M>::InnovationRepair, NoRepair>;
1356
1358template <typename M>
1359inline constexpr bool hasConsider = std::remove_cvref_t<M>::ConsiderType::Count > 0;
1360
1362template <typename F>
1363inline constexpr bool isLinearFunction = false;
1364
1366template <typename Matrix>
1367inline constexpr bool isLinearFunction<LinearFunction<Matrix>>{true};
1368
1369template <typename... Placed>
1370inline constexpr bool isLinearFunction<ColumnsFunction<Placed...>>{true};
1372
1376template <typename Model>
1377using MethodWithoutOne =
1378 std::conditional_t<isLinearFunction<typename Model::FunctionType>, methods::Linearized, NoMethod>;
1379
1380} // namespace internal
1381
1396template <TransformMethod Method, typename M, MatrixExpression Z, typename... Args>
1398[[nodiscard]] auto
1399observe(Method method, M && model, Z && observation, Args &&... args)
1400{
1401 using Stored = std::unwrap_ref_decay_t<M>;
1402 using Observation = typename internal::CrtpDerived<Z>::PlainObject;
1404 std::move(method),
1405 std::forward<M>(model),
1406 std::forward<Z>(observation),
1407 std::forward<Args>(args)...
1408 };
1409}
1410
1425template <typename M, MatrixExpression Z, typename... Args>
1426 requires MeasurementModel<std::unwrap_ref_decay_t<M>>
1427[[nodiscard]] auto
1428observe(M && model, Z && observation, Args &&... args)
1429{
1430 using Stored = std::unwrap_ref_decay_t<M>;
1431 using Observation = typename internal::CrtpDerived<Z>::PlainObject;
1432 using Method = internal::MethodWithoutOne<Stored>;
1434 Method{},
1435 std::forward<M>(model),
1436 std::forward<Z>(observation),
1437 std::forward<Args>(args)...
1438 };
1439}
1440
1441namespace internal
1442{
1443
1448template <typename M, typename... Args>
1449 requires isModel<std::remove_cvref_t<std::unwrap_ref_decay_t<M>>>
1450[[nodiscard]] auto
1451modelWith(M && model, Args &&... args)
1452{
1453 using Stored = std::unwrap_ref_decay_t<M>;
1454 return Observed<NoMethod, Stored, NoObservation, std::unwrap_ref_decay_t<Args>...>{
1455 NoMethod{},
1456 std::forward<M>(model),
1457 NoObservation{},
1458 std::forward<Args>(args)...
1459 };
1460}
1461
1462} // namespace internal
1463
1464} // namespace Emrys
Spaces built from other spaces (decisions.md [sizes-and-dimensions], [several-resizable-blocks]): Com...
The Kalman conditioning of a marked input on an observation of a transform's output (decisions....
The coordinate types (decisions.md [no-bounded-coordinates]): Linear, plain coordinates,...
How a function that stores a matrix argument takes it (decisions.md [forwarded-matrices]): the concep...
The Gaussian: its parameters GaussianParams, the Gaussian that evaluates the density,...
The macros every header includes first.
The transform methods, as policies made by their factories (decisions.md [policy-factories],...
The policies a model is built from, and the marks on a step's arguments (decisions....
Square roots of a covariance (decisions.md [sigma-point-axes]): the Axes and Repair concepts that a r...
The Space concept and what every continuous space shares (decisions.md [sizes-and-dimensions]): the t...
Subspaces, and growing and shrinking blocks (decisions.md [several-resizable-blocks]).
Tag paths and the index utilities (decisions.md [tag-addressing], [index-names]): TagPath and TagPath...
The arguments of transform() and the joint input they form (decisions.md [prediction-inputs],...
The matrix of a linear measurement model by its columns: a matrix per block of the state read,...
Definition MeasurementModel.h:396
static constexpr bool Repeats
whether the measurement repeats, with a block of the state read per repetition by onEach()
Definition MeasurementModel.h:416
TagPaths< typename internal::ColumnsOf< Placed >::Path... > Paths
The paths read, in order, as a TagPaths.
Definition MeasurementModel.h:422
Eigen::Index rows() const
Definition MeasurementModel.h:451
typename internal::FirstEachPathOf< Placed... >::type FirstEachPath
The path of the first block read per repetition, or void.
Definition MeasurementModel.h:425
Columns(Placed... placed)
The matrices placed.
Definition MeasurementModel.h:428
static constexpr int Rows
the rows of one repetition of the measurement, or Eigen::Dynamic
Definition MeasurementModel.h:419
std::tuple< Placed... > const & placed() const
Definition MeasurementModel.h:444
A measurement model: a function of its input, with the policies that say what it reads,...
Definition MeasurementModel.h:156
static constexpr bool IsModel
a model, for internal::isModel
Definition MeasurementModel.h:164
Policy< K > const & policy() const
Definition MeasurementModel.h:193
typename Set::template Type< K > Policy
The policy of kind K, or internal::NoPolicy.
Definition MeasurementModel.h:175
std::tuple< Policies... > const & policies() const
Definition MeasurementModel.h:200
static constexpr bool HasFunction
whether the model has a function; a model known by its likelihood alone has none
Definition MeasurementModel.h:167
F const & function() const
Definition MeasurementModel.h:185
MeasurementFunction(F function, Policies... policies)
The model of function with policies.
Definition MeasurementModel.h:178
F FunctionType
the function
Definition MeasurementModel.h:162
static constexpr bool has
Whether the policy of kind K was given.
Definition MeasurementModel.h:171
An observation of a measurement model: the observed value, the model, the method that pushes the stat...
Definition MeasurementModel.h:1232
ModelType const & model() const
Definition MeasurementModel.h:1281
static constexpr bool HasObservation
whether an observed value was given
Definition MeasurementModel.h:1242
FormType const & covarianceUpdate() const
Definition MeasurementModel.h:1302
Observed(Method method, M &&model, Z &&observation, Args... args)
The observation observation of model by method, with the call's args.
Definition MeasurementModel.h:1260
static constexpr bool HasMethod
whether a method was given
Definition MeasurementModel.h:1239
ArgumentsType const & arguments() const
Definition MeasurementModel.h:1295
internal::ArgumentOfKind< internal::ArgumentKind::consider, Consider<>, Args... > ConsiderType
The blocks the update considers rather than estimates, or Consider<> (decisions.md [consider-states])...
Definition MeasurementModel.h:1253
std::tuple< Args... > ArgumentsType
the arguments, as held
Definition MeasurementModel.h:1238
std::remove_cvref_t< Model > ModelType
the model's type
Definition MeasurementModel.h:1236
internal::ArgumentOfKind< internal::ArgumentKind::form, updates::Standard, Args... > FormType
The covariance update form given, or updates::Standard.
Definition MeasurementModel.h:1245
Method const & method() const
Definition MeasurementModel.h:1274
Method MethodType
the method
Definition MeasurementModel.h:1235
Observation const & observation() const
Definition MeasurementModel.h:1288
Observation ObservationType
the observed value's type
Definition MeasurementModel.h:1237
internal::ArgumentOfKind< internal::ArgumentKind::repair, internal::NoRepair, Args... > InnovationRepair
The repair of an innovation covariance that is not positive definite, or internal::NoRepair (decision...
Definition MeasurementModel.h:1250
A dense matrix or matrix expression, possibly cv- or reference-qualified: what a const Eigen::MatrixB...
Definition Forwarding.h:39
A measurement model: what measurementModel() returns, or a composition of such models (decisions....
Definition MeasurementModel.h:231
A measurement: an observed value with its model and the call's inputs, and the method a Kalman update...
Definition MeasurementModel.h:1337
A method observe() can take: Linearized, an UnscentedRule (Unscented, FourthMomentUnscented,...
Definition Methods.h:1019
#define EMRYS_FILTER_NO_UNIQUE_ADDRESS
Assertion used for programmer errors (misuse of the API).
Definition Macros.h:38
OutputSpace< std::remove_cvref_t< X > > outputSpace(X &&x)
Definition Policies.h:718
auto observe(Method method, M &&model, Z &&observation, Args &&... args)
Definition MeasurementModel.h:1399
auto measurementModel(F &&function, Policies &&... policies)
Definition MeasurementModel.h:913
Eigen::Index valueDimension(S const &space)
Definition Space.h:272
constexpr int valueDimensionAtCompileTime
The value dimension of Space at compile time, or Eigen::Dynamic if it is resizable.
Definition Space.h:251
constexpr int maxValueDimensionAtCompileTime
The largest valid value dimension of Space, or Eigen::Dynamic if it is unbounded.
Definition Space.h:258
constexpr TagPath< Tags... > path
The path TagPath<Tags...> as a value, for a call that takes its paths as arguments rather than as tem...
Definition TagPath.h:372
The measurement space function of a model whose measurement has a block of MeasurementSpace per repet...
Definition MeasurementModel.h:104
MeasurementSpace operator()(StateSpace const &space) const
Definition MeasurementModel.h:113
Dimension_ plain coordinates, or a resizable run of at most MaxDimension_ of them if Dimension_ is Ei...
Definition Coordinates.h:87
A list of tag paths, each a tag or a TagPath, such as the blocks a measurement reads (TagPaths<Positi...
Definition TagPath.h:363
What a function returns when it knows its Jacobian: its value, and the Jacobian of its output differe...
Definition Methods.h:46
Linearized method: and , with the Jacobians of f at the input means.
Definition Methods.h:327
The standard covariance update of condition(): , which is written without .
Definition Condition.h:75