EmrysFilter 0.1.0
State estimation utilities in C++23 using Eigen
Loading...
Searching...
No Matches
Placement.h
Go to the documentation of this file.
1#pragma once
2
10
11#include <array>
12#include <cstddef>
13#include <optional>
14#include <tuple>
15#include <type_traits>
16#include <utility>
17
30
31namespace Emrys
32{
33
34namespace internal
35{
36
41template <typename Prefix, typename Name>
42struct PlacedName : Name
43{
44 using Name::Name;
45};
46
48template <typename Prefix, typename Path>
49struct JoinPathsOf;
50
52template <typename... Prefix, typename... Tags>
53struct JoinPathsOf<TagPath<Prefix...>, TagPath<Tags...>>
54{
55 using type = TagPath<Prefix..., Tags...>;
56};
57
59
61template <typename Prefix, typename Path>
62using JoinPaths = typename JoinPathsOf<Prefix, Path>::type;
63
65template <typename Path>
66struct FirstTagOf;
67
69template <typename First, typename... Rest>
70struct FirstTagOf<TagPath<First, Rest...>>
71{
72 using type = First;
73};
74
76
78template <typename Path, typename S>
79inline constexpr bool startsIn = countTag<typename FirstTagOf<Path>::type, S>() > 0;
80
82template <typename T>
83inline constexpr bool isRename = false;
84
86template <PathValue X, typename Name>
87inline constexpr bool isRename<OnPath<X, Name>> = isTagPath<PathOf<X>> || PathValue<X>;
88
90
94template <typename Name, typename... Renames>
95struct RenamedOf
96{
97 using type = Name;
98};
99
101template <typename Name, typename X, typename New, typename... Rest>
102struct RenamedOf<Name, OnPath<X, New>, Rest...>
103{
104 using type =
105 std::conditional_t<std::is_same_v<PathOf<X>, TagPath<Name>>, New, typename RenamedOf<Name, Rest...>::type>;
106};
107
109
111template <typename Name, typename... Renames>
112using Renamed = typename RenamedOf<Name, Renames...>::type;
113
118template <typename Rename, typename S, typename Items>
119struct RenamesAnInput
120{
121 static constexpr bool value = false;
122};
123
125template <typename X, typename New, typename S, typename... Items>
126struct RenamesAnInput<OnPath<X, New>, S, std::tuple<Items...>>
127{
128 static constexpr bool value =
129 ((std::is_same_v<PathOf<X>, TagPath<typename Items::NameType>> && !startsIn<typename Items::PathType, S>) || ...
130 || false);
131};
132
134
136template <typename A>
137struct AffineNamesOf
138{
139 using type = std::tuple<>;
140};
141
143template <typename... Names>
144struct AffineNamesOf<AffineIn<Names...>>
145{
146 using type = std::tuple<Names...>;
147};
148
150
152template <typename Model>
153using AffineNamesOfModel = typename AffineNamesOf<typename Model::template Policy<PolicyKind::affineIn>>::type;
154
156template <typename Model, typename Name>
157inline constexpr bool declaresAffine = []<typename... Names>(std::tuple<Names...> *) {
158 return (std::is_same_v<Names, Name> || ...);
159}(static_cast<AffineNamesOfModel<Model> *>(nullptr));
160
162template <typename Name, typename... Names>
163[[nodiscard]] consteval std::size_t
164nameIndex()
165{
166 constexpr std::array<bool, sizeof...(Names) + 1> same{std::is_same_v<Names, Name>..., true};
167 std::size_t i = 0;
168 while (!same[i])
169 {
170 ++i;
171 }
172 return i;
173}
174
180template <typename Prefix, typename Model, typename... Renames>
181struct Placement
182{
183 using PrefixSpace = PathTag<Prefix>;
184 using InnerReads = typename Model::template Policy<PolicyKind::reads>;
185 using InnerItems = typename ItemsOf<InnerReads>::type;
186
187 static_assert(
188 (isRename<Renames> && ...),
189 "on<Path>(model, renames...) renames an input of the model with on<Name>(path<Tag>)"
190 );
191 static_assert(
192 (RenamesAnInput<Renames, PrefixSpace, InnerItems>::value && ... && true),
193 "a rename in on<Path>(model, on<Name>(path<Tag>)) names an input the model reads, a tag that is not a block of "
194 "the block Path names"
195 );
196
198 template <typename Item>
199 struct OuterItemOf
200 {
201 static constexpr bool State = startsIn<typename Item::PathType, PrefixSpace>;
202
203 using New = Renamed<typename Item::NameType, Renames...>;
204
205 static_assert(
206 State || sameLeaf<New, typename Item::NameType>,
207 "a renamed input has the same leaf beneath it as the name the model reads it under"
208 );
209
211 using type = std::conditional_t<
212 State,
213 ReadItem<JoinPaths<Prefix, typename Item::PathType>, PlacedName<Prefix, typename Item::NameType>>,
214 ReadItem<TagPath<New>, New>>;
215 };
216
218 using ReadsPolicy = typename decltype([]<typename... Items>(std::tuple<Items...> *) {
219 return std::type_identity<Reads<typename OuterItemOf<Items>::type...>>{};
220 }(static_cast<InnerItems *>(nullptr)))::type;
221
223 template <typename Name>
224 using OuterName = typename decltype([]<typename... Items>(std::tuple<Items...> *) {
225 return std::type_identity<std::tuple_element_t<
226 nameIndex<Name, typename Items::NameType...>(),
227 std::tuple<typename OuterItemOf<Items>::type::NameType...>>>{};
228 }(static_cast<InnerItems *>(nullptr)))::type;
229
231 using AffinePolicy = typename decltype([]<typename... Names>(std::tuple<Names...> *) {
232 return std::type_identity<AffineIn<OuterName<Names>...>>{};
233 }(static_cast<AffineNamesOfModel<Model> *>(nullptr)))::type;
234};
235
243template <typename Model>
244class PlacedStep
245{
246public:
247
248 using InnerIn = typename Model::template Policy<PolicyKind::reads>::InputSpace;
249 using InnerOut = OutputSpaceOf<Model>;
250
252 explicit PlacedStep(Model model)
253 : m_model{std::move(model)}
254 {}
255
257 [[nodiscard]] Model const &
258 model() const
259 {
260 return m_model;
261 }
262
264 [[nodiscard]] Model &
265 model()
266 {
267 return m_model;
268 }
269
273 template <typename In, typename Out, typename X, typename TimeScalar>
274 [[nodiscard]] decltype(auto)
275 operator()(In const & in, Out const & out, X const & x, TimeScalar dt) const
276 requires MatrixExpression<X>
277 {
278 return callForm(m_model.function(), renamedInstance<InnerIn>(in), renamedInstance<InnerOut>(out), x, dt);
279 }
280
282 using Inner = std::remove_cvref_t<decltype(std::declval<Model const &>().function())>;
283
285 static constexpr bool TakesBatches = takesBatches<Inner>;
286
288 using MarkType = BatchMarkOrNone<Inner>;
289
294 template <typename In, typename Out, typename X, typename TimeScalar, typename Y>
295 void
296 operator()(In const & in, Out const & out, X const & x, TimeScalar dt, Y & y) const
297 requires TakesBatches
298 {
299 auto const innerIn = renamedInstance<InnerIn>(in);
300 auto const innerOut = renamedInstance<InnerOut>(out);
301 auto inner = y.template renamed<InnerOut>(innerIn);
302 callForm(batchBody(m_model.function()), innerIn, innerOut, x, dt, inner);
303 }
304
306 template <typename Scalar, typename Out>
307 [[nodiscard]] auto
308 noiseOver(Out const & out, Scalar dt) const
309 {
310 return m_model.template noiseOver<Scalar>(renamedInstance<InnerOut>(out), dt);
311 }
312
313private:
314
315 Model m_model;
316};
317
321template <typename Prefix, typename Model>
322using PlacedWrites = typename decltype([]<std::size_t... K>(std::index_sequence<K...>) {
323 static_assert(
324 (startsIn<WritePathAt<Model, K>, PathTag<Prefix>> && ...),
325 "on<Path>(model) places a model whose writes() are blocks of the block Path names"
326 );
327 return std::type_identity<WritesAs<
328 TagPaths<JoinPaths<Prefix, WritePathAt<Model, K>>...>,
329 Composite<PlacedName<Prefix, WriteNameAt<Model, K>>...>>>{};
330}(std::make_index_sequence<writeCount<Model>>{}))::type;
331
333enum class EachKind
334{
335 state,
336 shared,
337 repeated,
338};
339
346template <typename Prefix, typename Model, typename... Renames>
347struct EachPlacement
348{
349 using RepeatedTag = PathTag<Prefix>;
350
351 static_assert(IsRepeated<RepeatedTag>, "onEach<Path>(model) places a model on a repeated block of the state");
352
353 using Block = RepeatedBlock<RepeatedTag>;
354 using InnerReads = typename Model::template Policy<PolicyKind::reads>;
355 using InnerItems = typename ItemsOf<InnerReads>::type;
356 using InnerIn = typename InnerReads::InputSpace;
357 using InnerOut = OutputSpaceOf<Model>;
358
359 static constexpr std::size_t ItemCount = std::tuple_size_v<InnerItems>;
360
361 static_assert(
362 (isRename<Renames> && ...),
363 "onEach<Path>(model, renames...) renames an input of the model with on<Name>(path<Tag>)"
364 );
365 static_assert(
366 (RenamesAnInput<Renames, Block, InnerItems>::value && ... && true),
367 "a rename in onEach<Path>(model, on<Name>(path<Tag>)) names an input the model reads, a tag that is not a "
368 "block of one repetition of the block Path names"
369 );
370 static_assert(
371 FixedSizeSpace<InnerIn> && FixedSizeSpace<InnerOut>,
372 "onEach<Path>(model) repeats a model whose input and output are fixed-size"
373 );
374 static_assert(
375 pathsPartition<Block, WritePathsOf<Model>>(),
376 "onEach<Path>(model) places a model that writes every coordinate of one repetition of the block exactly once"
377 );
378
380 template <std::size_t K>
381 using InnerItem = std::tuple_element_t<K, InnerItems>;
382
384 template <std::size_t K>
385 using NewName = Renamed<typename InnerItem<K>::NameType, Renames...>;
386
388 template <std::size_t K>
389 [[nodiscard]] static consteval EachKind
390 kind()
391 {
392 if constexpr (startsIn<typename InnerItem<K>::PathType, Block>)
393 {
394 return EachKind::state;
395 }
396 else if constexpr (IsRepeated<NewName<K>>)
397 {
398 static_assert(
399 sameLeaf<RepeatedBlock<NewName<K>>, typename InnerItem<K>::NameType>,
400 "an input renamed to a Repeated tag repeats the leaf the model reads it under"
401 );
402 return EachKind::repeated;
403 }
404 else
405 {
406 static_assert(
407 sameLeaf<NewName<K>, typename InnerItem<K>::NameType>,
408 "a renamed input has the same leaf beneath it as the name the model reads it under"
409 );
410 return EachKind::shared;
411 }
412 }
413
415 using ReadsPolicy = typename decltype([]<std::size_t... K>(std::index_sequence<K...>) {
416 return std::type_identity<decltype(std::tuple_cat(
417 std::tuple<ReadItem<Prefix, RepeatedTag>>{},
418 std::conditional_t<
419 kind<K>() == EachKind::state,
420 std::tuple<>,
421 std::tuple<ReadItem<TagPath<NewName<K>>, NewName<K>>>>{}...
422 ))>{};
423 }(std::make_index_sequence<ItemCount>{}))::type;
424
426 using ReadsOfEach = typename decltype([]<typename... Items>(std::tuple<Items...> *) {
427 return std::type_identity<Reads<Items...>>{};
428 }(static_cast<ReadsPolicy *>(nullptr)))::type;
429
433 using AffinePolicy = typename decltype([]<std::size_t... K>(std::index_sequence<K...>) {
434 constexpr bool blockAffine =
435 ((kind<K>() != EachKind::state || declaresAffine<Model, typename InnerItem<K>::NameType>) && ...);
436 return std::type_identity<decltype(std::tuple_cat(
437 std::conditional_t<blockAffine, std::tuple<RepeatedTag>, std::tuple<>>{},
438 std::conditional_t < kind<K>() != EachKind::state && declaresAffine<Model, typename InnerItem<K>::NameType>,
439 std::tuple<NewName<K>>,
440 std::tuple < >> {}...
441 ))>{};
442 }(std::make_index_sequence<ItemCount>{}))::type;
443
445 using AffineOfEach = typename decltype([]<typename... Names>(std::tuple<Names...> *) {
446 return std::type_identity<AffineIn<Names...>>{};
447 }(static_cast<AffinePolicy *>(nullptr)))::type;
448};
449
460template <typename Prefix, typename Model, typename... Renames>
461class EachStep
462{
464 using P = EachPlacement<Prefix, Model, Renames...>;
466 using RepeatedTag = typename P::RepeatedTag;
468 using Block = typename P::Block;
470 using InnerIn = typename P::InnerIn;
472 using InnerOut = typename P::InnerOut;
473
474 static constexpr int BlockSize = valueDimensionAtCompileTime<Block>;
475 static constexpr int BlockDifferenceSize = valueDimensionAtCompileTime<DifferenceSpace<Block>>;
476
477public:
478
480 explicit EachStep(Model model)
481 : m_model{std::move(model)}
482 {}
483
485 [[nodiscard]] Model const &
486 model() const
487 {
488 return m_model;
489 }
490
492 [[nodiscard]] Model &
493 model()
494 {
495 return m_model;
496 }
497
501 template <typename In, typename Out, typename X, typename TimeScalar>
502 [[nodiscard]] auto
503 operator()(In const & in, Out const & out, X const & x, TimeScalar dt) const
504 requires MatrixExpression<X>
505 {
506 using T = ScalarOf<X>;
507 using InnerVector = typename Moments<InnerIn, T>::VectorType;
508 constexpr bool ReturnsJacobian = returnsJacobianWith<Model, TimeScalar, TimeScalar>;
509 Eigen::Index const count = pathBlock<RepeatedTag>(in).count();
510 BlockRows const inRows = blockRows<RepeatedTag>(in);
511 BlockRows const outRows = blockRows<RepeatedTag>(out);
512 auto y = zeros<T>(out);
513 auto jacobian = [&] {
514 if constexpr (ReturnsJacobian)
515 {
516 using Matrix = MatrixOf<
517 TimeScalar,
522 return Matrix{Matrix::Zero(differenceDimension(out), differenceDimension(in))};
523 }
524 else
525 {
526 return NoPolicy{};
527 }
528 }();
529 InnerIn const innerIn{};
530 InnerOut const innerOut{};
531 for (Eigen::Index i = 0; i < count; ++i)
532 {
533 InnerVector xi{InnerVector::Zero(valueDimension(innerIn))};
534 [&]<std::size_t... K>(std::index_sequence<K...>) {
535 (gather<K>(in, x, i, inRows, innerIn, xi), ...);
536 }(std::make_index_sequence<P::ItemCount>{});
537 auto result = vectorReturned(callForm(m_model.function(), innerIn, innerOut, xi, dt));
538 auto const & value = valueOf(result);
539 [&]<std::size_t... W>(std::index_sequence<W...>) {
540 ((y.segment(
541 outRows.start + i * BlockSize + blockRows<WritePathAt<Model, W>>(Block{}).start,
542 blockRows<WritePathAt<Model, W>>(Block{}).size
543 ) =
544 value.segment(
545 blockRows<WriteNameAt<Model, W>>(innerOut).start,
546 blockRows<WriteNameAt<Model, W>>(innerOut).size
547 )),
548 ...);
549 }(std::make_index_sequence<writeCount<Model>>{});
550 if constexpr (ReturnsJacobian)
551 {
552 [&]<std::size_t... W>(std::index_sequence<W...>) {
553 (jacobianRows<W>(in, out, i, innerIn, innerOut, result.jacobian, jacobian), ...);
554 }(std::make_index_sequence<writeCount<Model>>{});
555 }
556 }
557 if constexpr (ReturnsJacobian)
558 {
559 return withJacobian(std::move(y), std::move(jacobian));
560 }
561 else
562 {
563 return y;
564 }
565 }
566
568 using Inner = std::remove_cvref_t<decltype(std::declval<Model const &>().function())>;
569
571 static constexpr bool TakesBatches = takesBatches<Inner>;
572
574 using MarkType = NoJacobians;
575
580 template <typename In, typename Out, typename X, typename TimeScalar, typename Y>
581 void
582 operator()(In const & in, Out const & out, X const & x, TimeScalar dt, Y & y) const
583 requires TakesBatches
584 {
585 using T = typename X::Scalar;
586 using InnerVector = typename Moments<InnerIn, T>::VectorType;
587 using XI = MatrixOf<
588 T,
590 X::ColsAtCompileTime,
592 X::MaxColsAtCompileTime>;
593 using YI = MatrixOf<
594 T,
596 X::ColsAtCompileTime,
598 X::MaxColsAtCompileTime>;
599 Eigen::Index const repetitions = pathBlock<RepeatedTag>(in).count();
600 Eigen::Index const points = x.cols();
601 BlockRows const inRows = blockRows<RepeatedTag>(in);
602 BlockRows const outRows = blockRows<RepeatedTag>(out);
603 auto & values = y.values();
604 values.setZero();
605 InnerIn const innerIn{};
606 InnerOut const innerOut{};
607 auto xi = XI{};
608 xi.resize(valueDimension(innerIn), points);
609 auto yi = YI{};
610 yi.resize(valueDimension(innerOut), points);
611 auto column = InnerVector{};
612 column.resize(valueDimension(innerIn));
613 for (Eigen::Index i = 0; i < repetitions; ++i)
614 {
615 for (Eigen::Index j = 0; j < points; ++j)
616 {
617 column.setZero();
618 [&]<std::size_t... K>(std::index_sequence<K...>) {
619 (gather<K>(in, x.col(j), i, inRows, innerIn, column), ...);
620 }(std::make_index_sequence<P::ItemCount>{});
621 xi.col(j) = column;
622 }
623 callBatched(m_model.function(), innerIn, innerOut, xi, yi, dt);
624 [&]<std::size_t... W>(std::index_sequence<W...>) {
625 ((values.middleRows(
626 outRows.start + i * BlockSize + blockRows<WritePathAt<Model, W>>(Block{}).start,
627 blockRows<WritePathAt<Model, W>>(Block{}).size
628 ) =
629 yi.middleRows(
630 blockRows<WriteNameAt<Model, W>>(innerOut).start,
631 blockRows<WriteNameAt<Model, W>>(innerOut).size
632 )),
633 ...);
634 }(std::make_index_sequence<writeCount<Model>>{});
635 }
636 }
637
641 template <typename Scalar, typename Out>
642 [[nodiscard]] typename Moments<Out, Scalar>::MatrixType
643 noiseOver(Out const & out, Scalar dt) const
644 {
645 using Matrix = typename Moments<Out, Scalar>::MatrixType;
646 Eigen::Index const n = differenceDimension(out);
647 Matrix q = Matrix::Zero(n, n);
648 InnerOut const innerOut{};
649 auto const inner = m_model.template noiseOver<Scalar>(innerOut, dt);
650 Eigen::Index const count = pathBlock<RepeatedTag>(out).count();
651 Eigen::Index const start = differenceBlockRows<RepeatedTag>(out).start;
652 for (Eigen::Index i = 0; i < count; ++i)
653 {
654 [&]<std::size_t... W>(std::index_sequence<W...>) {
655 (noiseRows<W>(q, start + i * BlockDifferenceSize, innerOut, inner), ...);
656 }(std::make_index_sequence<writeCount<Model>>{});
657 }
658 return q;
659 }
660
661private:
662
664 template <std::size_t K, typename In, typename X, typename XI>
665 static void
666 gather(In const & in, X const & x, Eigen::Index i, BlockRows const & inRows, InnerIn const & innerIn, XI & xi)
667 {
668 using Item = typename P::template InnerItem<K>;
669 BlockRows const rows = blockRows<typename Item::NameType>(innerIn);
670 if constexpr (P::template kind<K>() == EachKind::state)
671 {
672 BlockRows const within = blockRows<typename Item::PathType>(Block{});
673 xi.segment(rows.start, rows.size) = x.segment(inRows.start + i * BlockSize + within.start, within.size);
674 }
675 else if constexpr (P::template kind<K>() == EachKind::repeated)
676 {
677 xi.segment(rows.start, rows.size) =
678 block<typename P::template NewName<K>>(in, x).segment(i * rows.size, rows.size);
679 }
680 else
681 {
682 xi.segment(rows.start, rows.size) = block<typename P::template NewName<K>>(in, x);
683 }
684 }
685
689 template <std::size_t W, typename In, typename Out, typename J, typename Whole>
690 static void
691 jacobianRows(
692 In const & in,
693 Out const & out,
694 Eigen::Index i,
695 InnerIn const & innerIn,
696 InnerOut const & innerOut,
697 J const & inner,
698 Whole & jacobian
699 )
700 {
701 BlockRows const innerRows = differenceBlockRows<WriteNameAt<Model, W>>(innerOut);
702 Eigen::Index const row = differenceBlockRows<RepeatedTag>(out).start + i * BlockDifferenceSize
703 + differenceBlockRows<WritePathAt<Model, W>>(Block{}).start;
704 [&]<std::size_t... K>(std::index_sequence<K...>) {
705 (
706 [&] {
707 using Item = typename P::template InnerItem<K>;
708 BlockRows const innerColumns = differenceBlockRows<typename Item::NameType>(innerIn);
709 Eigen::Index column = 0;
710 if constexpr (P::template kind<K>() == EachKind::state)
711 {
712 column = differenceBlockRows<RepeatedTag>(in).start + i * BlockDifferenceSize
713 + differenceBlockRows<typename Item::PathType>(Block{}).start;
714 }
715 else if constexpr (P::template kind<K>() == EachKind::repeated)
716 {
717 column = differenceBlockRows<typename P::template NewName<K>>(in).start + i * innerColumns.size;
718 }
719 else
720 {
721 column = differenceBlockRows<typename P::template NewName<K>>(in).start;
722 }
723 jacobian.block(row, column, innerRows.size, innerColumns.size) =
724 inner.block(innerRows.start, innerColumns.start, innerRows.size, innerColumns.size);
725 }(),
726 ...
727 );
728 }(std::make_index_sequence<P::ItemCount>{});
729 }
730
734 template <std::size_t W, typename Q, typename Inner>
735 static void
736 noiseRows(Q & q, Eigen::Index start, InnerOut const & innerOut, Inner const & inner)
737 {
738 BlockRows const innerRows = differenceBlockRows<WriteNameAt<Model, W>>(innerOut);
739 Eigen::Index const row = start + differenceBlockRows<WritePathAt<Model, W>>(Block{}).start;
740 [&]<std::size_t... V>(std::index_sequence<V...>) {
741 (
742 [&] {
743 BlockRows const innerColumns = differenceBlockRows<WriteNameAt<Model, V>>(innerOut);
744 Eigen::Index const column = start + differenceBlockRows<WritePathAt<Model, V>>(Block{}).start;
745 q.block(row, column, innerRows.size, innerColumns.size) =
746 inner.block(innerRows.start, innerColumns.start, innerRows.size, innerColumns.size);
747 }(),
748 ...
749 );
750 }(std::make_index_sequence<writeCount<Model>>{});
751 }
752
753 Model m_model;
754};
755
763template <typename F, typename InnerIn>
764class PlacedMeasure
765{
766public:
767
769 explicit PlacedMeasure(F function)
770 : m_function{std::move(function)}
771 {}
772
774 template <typename In, typename Out, typename X>
775 [[nodiscard]] decltype(auto)
776 operator()(In const & in, Out const & out, X const & x) const
777 requires MatrixExpression<X>
778 {
779 return callForm(m_function, renamedInstance<InnerIn>(in), out, x);
780 }
781
783 static constexpr bool TakesBatches = takesBatches<F>;
784
786 using MarkType = BatchMarkOrNone<F>;
787
791 template <typename In, typename Out, typename X, typename Y>
792 void
793 operator()(In const & in, Out const & out, X const & x, Y & y) const
794 requires TakesBatches
795 {
796 auto const innerIn = renamedInstance<InnerIn>(in);
797 auto inner = y.template renamed<Out>(innerIn);
798 callForm(batchBody(m_function), innerIn, out, x, inner);
799 }
800
802 [[nodiscard]] Eigen::Index
803 outputRows() const
804 requires requires(F const & f) { f.outputRows(); }
805 {
806 return m_function.outputRows();
807 }
808
809private:
810
811 EMRYS_FILTER_NO_UNIQUE_ADDRESS F m_function;
812};
813
820template <typename G, typename InnerIn>
821struct PlacedLikelihood
822{
823 G function;
824
826 template <typename In, typename X, typename Z>
827 [[nodiscard]] auto
828 operator()(In const & in, X const & x, Z const & z) const
829 {
830 return function(renamedInstance<InnerIn>(in), x, z);
831 }
832};
833
840template <typename G, typename InnerIn>
841struct PlacedSample
842{
843 G function;
844
846 template <typename In, typename X, typename R>
847 [[nodiscard]] auto
848 operator()(In const & in, X const & x, R & rng) const
849 {
850 return function(renamedInstance<InnerIn>(in), x, rng);
851 }
852};
853
857template <typename Prefix, typename G>
858struct PlacedOutputSpace
859{
860 G function;
861
863 template <typename StateSpace>
864 [[nodiscard]] auto
865 operator()(StateSpace const & space) const
866 {
867 return function(pathSubspace<Prefix>(space));
868 }
869};
870
876template <typename Prefix, typename InnerIn, typename ReadsPolicy, typename AffinePolicy, typename Policy>
877[[nodiscard]] auto
878placedPolicy(Policy const & policy)
879{
880 constexpr std::optional<PolicyKind> kind = policyKind<Policy>;
881 if constexpr (kind == PolicyKind::reads)
882 {
883 return ReadsPolicy{};
884 }
885 else if constexpr (kind == PolicyKind::affineIn)
886 {
887 return AffinePolicy{};
888 }
889 else if constexpr (kind == PolicyKind::logLikelihood)
890 {
891 using G = std::remove_cvref_t<decltype(policy.function)>;
892 return LogLikelihood<PlacedLikelihood<G, InnerIn>>{PlacedLikelihood<G, InnerIn>{policy.function}};
893 }
894 else if constexpr (kind == PolicyKind::sample)
895 {
896 using G = std::remove_cvref_t<decltype(policy.function)>;
897 return Sample<PlacedSample<G, InnerIn>>{PlacedSample<G, InnerIn>{policy.function}};
898 }
899 else if constexpr (kind == PolicyKind::outputSpace)
900 {
901 if constexpr (Policy::Computed)
902 {
903 using G = std::remove_cvref_t<decltype(policy.value)>;
904 return OutputSpace<PlacedOutputSpace<Prefix, G>>{PlacedOutputSpace<Prefix, G>{policy.value}};
905 }
906 else
907 {
908 return policy;
909 }
910 }
911 else
912 {
913 return policy;
914 }
915}
916
923template <typename... Models>
924class MeasurementParts
925{
926public:
927
929 explicit MeasurementParts(Models... models)
930 : m_parts{std::move(models)...}
931 {}
932
934 [[nodiscard]] std::tuple<Models...> const &
935 parts() const
936 {
937 return m_parts;
938 }
939
941 template <typename In, typename Out, typename X>
942 [[nodiscard]] auto
943 operator()(In const & in, Out const & out, X const & x) const
944 requires MatrixExpression<X>
945 {
946 return stepParts<ScalarOf<X>>(m_parts, in, out, x);
947 }
948
950 static constexpr bool TakesBatches = anyTakesBatches<Models...>;
951
953 using MarkType = NoJacobians;
954
959 template <typename In, typename Out, typename X, typename Y>
960 void
961 operator()(In const & in, Out const & out, X const & x, Y & y) const
962 requires TakesBatches
963 {
964 stepPartsBatch(m_parts, in, out, x, y);
965 }
966
967private:
968
969 std::tuple<Models...> m_parts;
970};
971
978template <typename Out, typename... Models>
979struct SumOfLikelihoods
980{
981 std::tuple<Models...> parts;
982
984 template <typename In, typename X, typename Z>
985 [[nodiscard]] auto
986 operator()(In const & in, X const & x, Z const & z) const
987 {
988 return [&]<std::size_t... I>(std::index_sequence<I...>) {
989 return (likelihoodOf<I>(in, x, z) + ...);
990 }(std::index_sequence_for<Models...>{});
991 }
992
993private:
994
996 template <std::size_t I, typename In, typename X, typename Z>
997 [[nodiscard]] auto
998 likelihoodOf(In const & in, X const & x, Z const & z) const
999 {
1000 using Part = std::tuple_element_t<I, std::tuple<Models...>>;
1001 using PartOut = OutputSpaceOf<Part>;
1002 auto const pin = partIn<Part>(in);
1003 auto const px = partVector<Part>(in, pin, x);
1004 Out const out{};
1005 PartOut const pout{};
1006 auto pz = zeros<typename Z::Scalar>(pout);
1007 [&]<typename... Names>(std::tuple<Names...> *) {
1008 ((block<Names>(pout, pz) = block<Names>(out, z)), ...);
1009 }(static_cast<CompositeBlocks<PartOut> *>(nullptr));
1010 return callWithScalar<typename X::Scalar>(
1011 std::get<I>(parts).template policy<PolicyKind::logLikelihood>().function,
1012 pin,
1013 px,
1014 pz
1015 );
1016 }
1017};
1018
1023template <typename Model>
1024[[nodiscard]] auto
1025placedNoiseParts(Model const & model)
1026{
1027 if constexpr (!Model::template has<PolicyKind::additiveNoise>)
1028 {
1029 return std::tuple<>{};
1030 }
1031 else
1032 {
1033 using Noise = typename Model::template Policy<PolicyKind::additiveNoise>;
1034 if constexpr (Noise::Placed)
1035 {
1036 return model.template policy<PolicyKind::additiveNoise>().parts();
1037 }
1038 else
1039 {
1040 static_assert(
1041 writeCount<Model> == 1,
1042 "a part of a composition whose additiveNoise() covers its whole output writes one block; place each "
1043 "part of its noise with on<Path>() to write several"
1044 );
1045 return std::tuple{on<WritePathAt<Model, 0>>(
1046 typename Noise::Distribution{model.template policy<PolicyKind::additiveNoise>().distribution()}
1047 )};
1048 }
1049 }
1050}
1051
1052} // namespace internal
1053
1065template <typename Path, ProcessModel Model, typename... Renames>
1066[[nodiscard]] auto
1067on(Model const & model, Renames const &...)
1068{
1069 static_assert(internal::wholeStateRefused<Model>);
1070 using Prefix = internal::AsTagPath<Path>;
1071 using Placement = internal::Placement<Prefix, Model, Renames...>;
1072 using Step = internal::PlacedStep<Model>;
1073 return ProcessFunction<
1074 Step,
1075 typename Placement::ReadsPolicy,
1076 internal::PlacedWrites<Prefix, Model>,
1077 typename Placement::AffinePolicy>{
1078 Step{model},
1079 typename Placement::ReadsPolicy{},
1080 internal::PlacedWrites<Prefix, Model>{},
1081 typename Placement::AffinePolicy{}
1082 };
1083}
1084
1091template <typename Path, MeasurementModel Model, typename... Renames>
1092[[nodiscard]] auto
1093on(Model const & model, Renames const &...)
1094{
1095 static_assert(internal::wholeStateRefused<Model>);
1096 using Prefix = internal::AsTagPath<Path>;
1097 using Placement = internal::Placement<Prefix, Model, Renames...>;
1098 using InnerIn = typename Model::template Policy<internal::PolicyKind::reads>::InputSpace;
1099 using ReadsPolicy = typename Placement::ReadsPolicy;
1100 using AffinePolicy = typename Placement::AffinePolicy;
1101 auto const function = [&] {
1102 if constexpr (Model::HasFunction)
1103 {
1104 return internal::PlacedMeasure<typename Model::FunctionType, InnerIn>{model.function()};
1105 }
1106 else
1107 {
1108 return internal::NoFunction{};
1109 }
1110 }();
1111 return std::apply(
1112 [&](auto const &... policies) {
1113 return MeasurementFunction<
1114 std::remove_cvref_t<decltype(function)>,
1115 decltype(internal::placedPolicy<Prefix, InnerIn, ReadsPolicy, AffinePolicy>(policies))...>{
1116 function,
1117 internal::placedPolicy<Prefix, InnerIn, ReadsPolicy, AffinePolicy>(policies)...
1118 };
1119 },
1120 model.policies()
1121 );
1122}
1123
1136template <typename Path, ProcessModel Model, typename... Renames>
1137[[nodiscard]] auto
1138onEach(Model const & model, Renames const &...)
1139{
1140 static_assert(internal::wholeStateRefused<Model>);
1141 using Prefix = internal::AsTagPath<Path>;
1142 using Each = internal::EachPlacement<Prefix, Model, Renames...>;
1143 using Step = internal::EachStep<Prefix, Model, Renames...>;
1144 return ProcessFunction<Step, typename Each::ReadsOfEach, Writes<Prefix>, typename Each::AffineOfEach>{
1145 Step{model},
1146 typename Each::ReadsOfEach{},
1148 typename Each::AffineOfEach{}
1149 };
1150}
1151
1152namespace internal
1153{
1154
1156template <typename... Models>
1157[[nodiscard]] auto
1158composeMeasurements(Models... models)
1159{
1160 static_assert((wholeStateRefused<Models> && ...));
1161 // A whole-state model has no writes() either, which its own refusal says.
1162 static_assert(
1163 ((Models::template has<PolicyKind::writes> || readsWholeState<Models>) && ...),
1164 "compose() takes measurement models that name their output with writes()"
1165 );
1166 static_assert(
1167 (Models::HasFunction && ...),
1168 "compose() takes measurement models with a function; one known by its likelihood alone has no output"
1169 );
1170 static_assert(
1171 (!Models::template has<PolicyKind::outputSpace> && ...),
1172 "compose() takes measurement models whose output writes() fixes, without outputSpace()"
1173 );
1174 constexpr std::size_t likelihoods = (std::size_t{Models::template has<PolicyKind::logLikelihood>} + ...);
1175 static_assert(
1176 likelihoods == 0 || likelihoods == sizeof...(Models),
1177 "in a composition of measurement models, every model gives logLikelihood() or none does"
1178 );
1179 using AllItems = decltype(std::tuple_cat(std::declval<ReadItemsOf<Models>>()...));
1180 using ReadsPolicy = typename ReadUnionOf<AllItems>::type;
1181 using WritesPolicy = typename WritesOfTuple<
1182 decltype(std::tuple_cat(std::declval<typename PathsOf<WritePathsOf<Models>>::type>()...)),
1183 decltype(std::tuple_cat(std::declval<CompositeBlocks<OutputSpaceOf<Models>>>()...))>::type;
1184 using AffinePolicy = typename AffineOf<ReadsPolicy, std::tuple<Models...>>::type;
1185 using Function = MeasurementParts<Models...>;
1186 auto const noise = std::tuple_cat(placedNoiseParts(models)...);
1187 auto const base = std::tuple{ReadsPolicy{}, WritesPolicy{}, AffinePolicy{}};
1188 auto const withNoise = [&] {
1189 if constexpr (std::tuple_size_v<std::remove_cvref_t<decltype(noise)>> > 0)
1190 {
1191 return std::tuple_cat(
1192 base,
1193 std::tuple{std::apply(
1194 [](auto const &... parts) {
1195 return AdditiveNoise<std::remove_cvref_t<decltype(parts)>...>{parts...};
1196 },
1197 noise
1198 )}
1199 );
1200 }
1201 else
1202 {
1203 return base;
1204 }
1205 }();
1206 auto const policies = [&] {
1207 if constexpr (likelihoods > 0)
1208 {
1209 using Sum = SumOfLikelihoods<typename WritesPolicy::OutputSpace, Models...>;
1210 return std::tuple_cat(withNoise, std::tuple{LogLikelihood<Sum>{Sum{std::tuple{models...}}}});
1211 }
1212 else
1213 {
1214 return withNoise;
1215 }
1216 }();
1217 return std::apply(
1218 [&](auto const &... each) {
1219 return MeasurementFunction<Function, std::remove_cvref_t<decltype(each)>...>{
1220 Function{std::move(models)...},
1221 each...
1222 };
1223 },
1224 policies
1225 );
1226}
1227
1228} // namespace internal
1229
1240template <MeasurementModel... Models>
1241 requires(sizeof...(Models) > 0)
1242[[nodiscard]] auto
1243compose(Models &&... models)
1244{
1245 return internal::composeMeasurements(std::remove_cvref_t<Models>{std::forward<Models>(models)}...);
1246}
1247
1248} // namespace Emrys
Spaces built from other spaces (decisions.md [sizes-and-dimensions], [several-resizable-blocks]): Com...
What marginals and conditionals are built from: resolving Eigen index sets against a space; the share...
The macros every header includes first.
Measurement models and their observations (decisions.md [model-policies], [sensors],...
The transform methods, as policies made by their factories (decisions.md [policy-factories],...
Moments: the mean and covariance that a Gaussian carries and that a mixture or a sample set is moment...
The policies a model is built from, and the marks on a step's arguments (decisions....
Process models as functions with reads and writes (decisions.md [model-policies], [continuous-time-no...
How a model's reads() meet a state and a call, all internal (decisions.md [model-policies],...
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...
A measurement model: a function of its input, with the policies that say what it reads,...
Definition MeasurementModel.h:156
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
A process model: a function of its input and the step, with the policies that say what it reads,...
Definition ProcessModel.h:518
A measurement model: what measurementModel() returns, or a composition of such models (decisions....
Definition MeasurementModel.h:231
A process model: what processModel() returns, a built-in model, or a composition of process models (d...
Definition ProcessModel.h:2010
#define EMRYS_FILTER_NO_UNIQUE_ADDRESS
Assertion used for programmer errors (misuse of the API).
Definition Macros.h:38
typename std::remove_cvref_t< X >::Scalar ScalarOf
The scalar type of the Eigen object or distribution X, for writing functions generic in it.
Definition Moments.h:26
auto on(Model const &model, Renames const &...)
Definition Placement.h:1067
auto onEach(Model const &model, Renames const &...)
Definition Placement.h:1138
auto compose(Models &&... models)
Definition Placement.h:1243
Eigen::Index valueDimension(S const &space)
Definition Space.h:272
auto block(Space auto const &space, X &&x)
Definition TagPath.h:979
Eigen::Index differenceDimension(S const &space)
Definition Space.h:284
constexpr int valueDimensionAtCompileTime
The value dimension of Space at compile time, or Eigen::Dynamic if it is resizable.
Definition Space.h:251
ValueVector< S, Scalar > zeros(S const &space)
Definition TagPath.h:1092
constexpr int maxValueDimensionAtCompileTime
The largest valid value dimension of Space, or Eigen::Dynamic if it is unbounded.
Definition Space.h:258
WithJacobian< internal::PlainResult< std::remove_cvref_t< Value > >, internal::PlainResult< std::remove_cvref_t< Jacobian > > > withJacobian(Value &&value, Jacobian &&jacobian)
Definition Methods.h:222
What a model writes, as writes() declares it: the blocks its function's result fills (decisions....
Definition Policies.h:223