EmrysFilter 0.1.0
State estimation utilities in C++23 using Eigen
Loading...
Searching...
No Matches
Composite.h
Go to the documentation of this file.
1#pragma once
2
9
10#include <array>
11#include <cstddef>
12#include <limits>
13#include <numeric>
14#include <optional>
15#include <tuple>
16#include <type_traits>
17#include <utility>
18
22
23namespace Emrys
24{
25
26namespace internal
27{
28
30template <typename Part>
31using StoredPart = std::conditional_t<std::is_empty_v<Part>, std::tuple<>, std::tuple<Part>>;
32
34template <typename Stored>
35struct StoredParts
36{
37 Stored parts;
38
40 friend bool
41 operator==(StoredParts const &, StoredParts const &) = default;
42};
43
45template <>
46struct StoredParts<std::tuple<>>
47{
48 StoredParts() = default;
49
51 constexpr explicit StoredParts(std::tuple<>) {}
52
54 friend bool
55 operator==(StoredParts const &, StoredParts const &) = default;
56};
57
64template <typename... Parts>
65class PartStorage : private StoredParts<decltype(std::tuple_cat(std::declval<StoredPart<Parts>>()...))>
66{
68 using Stored = decltype(std::tuple_cat(std::declval<StoredPart<Parts>>()...));
69
71 using Base = StoredParts<Stored>;
72
74 template <std::size_t I>
75 using Part = std::tuple_element_t<I, std::tuple<Parts...>>;
76
78 static consteval std::size_t
79 storedIndex(std::size_t i)
80 {
81 constexpr auto empty = std::array<bool, sizeof...(Parts)>{std::is_empty_v<Parts>...};
82 std::size_t result = 0;
83 for (std::size_t j = 0; j < i; ++j)
84 {
85 result += empty[j] ? 0 : 1;
86 }
87 return result;
88 }
89
91 template <typename P>
92 static constexpr StoredPart<P>
93 keep(P & part)
94 {
95 if constexpr (std::is_empty_v<P>)
96 {
97 return {};
98 }
99 else
100 {
101 return StoredPart<P>{std::move(part)};
102 }
103 }
104
106 template <std::size_t I>
107 static inline Part<I> instance{};
108
109public:
110
111 PartStorage() = default;
112
114 constexpr explicit PartStorage(Parts... parts)
115 : Base{std::tuple_cat(keep(parts)...)}
116 {}
117
119 template <std::size_t I>
120 [[nodiscard]] constexpr Part<I> const &
121 part() const
122 {
123 if constexpr (std::is_empty_v<Part<I>>)
124 {
125 return instance<I>;
126 }
127 else
128 {
129 return std::get<storedIndex(I)>(Base::parts);
130 }
131 }
132
134 template <std::size_t I>
135 [[nodiscard]] Part<I> &
136 part()
137 {
138 if constexpr (std::is_empty_v<Part<I>>)
139 {
140 return instance<I>;
141 }
142 else
143 {
144 return std::get<storedIndex(I)>(Base::parts);
145 }
146 }
147
149 friend bool
150 operator==(PartStorage const &, PartStorage const &) = default;
151};
152
153} // namespace internal
154
168template <Space... Blocks>
169class Composite : private internal::PartStorage<Blocks...>
170{
172 using Storage = internal::PartStorage<Blocks...>;
173
174 static_assert(sizeof...(Blocks) > 0, "a composite needs at least one block");
175 static constexpr std::size_t BlockCount = sizeof...(Blocks);
176 static constexpr std::array<bool, BlockCount> Resizable{
177 (Blocks::ValueDimensionStep != 0)...
178 };
179
183 static consteval int
184 strides()
185 {
186 int result = 0;
187 ((result = std::gcd(result, Blocks::ValueDimensionStep)), ...);
188 return result;
189 }
190
192 static consteval std::size_t
193 firstResizable()
194 {
195 for (std::size_t i = 0; i < BlockCount; ++i)
196 {
197 if (Resizable[i])
198 {
199 return i;
200 }
201 }
202 return BlockCount;
203 }
204
205public:
206
207 static constexpr int FixedValueDimension =
208 (Blocks::FixedValueDimension + ...);
209
213 static constexpr int ValueDimensionStep = strides();
214
216 static constexpr int MaxValueDimension =
217 ((Blocks::MaxValueDimension == Eigen::Dynamic) || ...) ? Eigen::Dynamic : (Blocks::MaxValueDimension + ...);
218
219 Composite() = default;
220
222 explicit Composite(Blocks... blocks)
223 : Storage{std::move(blocks)...}
224 {}
225
227 template <std::size_t I>
228 [[nodiscard]] auto const &
229 block() const
230 {
231 return Storage::template part<I>();
232 }
233
235 template <std::size_t I>
236 [[nodiscard]] auto &
238 {
239 return Storage::template part<I>();
240 }
241
243 [[nodiscard]] Eigen::Index
245 {
246 return offset<BlockCount>();
247 }
248
250 template <std::size_t I>
251 [[nodiscard]] Eigen::Index
252 offset() const
253 {
254 return [&]<std::size_t... J>(std::index_sequence<J...>) {
255 return (Eigen::Index{0} + ... + block<J>().valueDimension());
256 }(std::make_index_sequence<I>{});
257 }
258
262 void
263 resize(Eigen::Index n)
264 requires((internal::resizableBlockCount<Blocks> + ...) == 1)
265 {
266 auto & resizable = block<firstResizable()>();
267 Eigen::Index const rest = valueDimension() - resizable.valueDimension();
268 EMRYS_FILTER_ASSERT(n >= rest && "the dimension does not fit the space");
269 resizable.resize(n - rest);
270 }
271
273 template <typename Derived>
274 void
275 normalize(Eigen::MatrixBase<Derived> const & rows) const
276 requires((internal::HasNormalize<Blocks> || ...))
277 {
278 forEachBlock(rows, [](auto const & block, auto && sub) {
280 });
281 }
282
286 template <typename X, typename D, typename Out>
287 void
288 plus(Eigen::MatrixBase<X> const & x, Eigen::MatrixBase<D> const & d, Eigen::MatrixBase<Out> const & out) const
289 requires((internal::HasPlus<Blocks> || ...))
290 {
291 forEachBlockOf<false, true, false>(
292 x,
293 d,
294 out,
295 [](auto const & block, auto const & xs, auto const & ds, auto const & os) {
296 internal::plusInto(block, xs, ds, os);
297 }
298 );
299 }
300
304 template <typename A, typename B, typename Out>
305 void
306 minus(Eigen::MatrixBase<A> const & a, Eigen::MatrixBase<B> const & b, Eigen::MatrixBase<Out> const & out) const
307 requires((internal::HasMinus<Blocks> || ...))
308 {
309 forEachBlockOf<false, false, true>(
310 a,
311 b,
312 out,
313 [](auto const & block, auto const & as, auto const & bs, auto const & os) {
314 internal::minusInto(block, as, bs, os);
315 }
316 );
317 }
318
322 [[nodiscard]] Composite<DifferenceSpace<Blocks>...>
324 {
325 return [&]<std::size_t... I>(std::index_sequence<I...>) {
326 return Composite<DifferenceSpace<Blocks>...>{differenceSpace(block<I>())...};
327 }(std::make_index_sequence<BlockCount>{});
328 }
329
331 friend bool
332 operator==(Composite const &, Composite const &) = default;
333
334private:
335
337 template <typename Derived, typename F>
338 void
339 forEachBlock(Eigen::MatrixBase<Derived> const & rows, F f) const
340 {
341 auto & m = writable(rows);
342 EMRYS_FILTER_ASSERT(m.rows() == valueDimension() && "the rows do not match the space");
343 Eigen::Index offset = 0;
344 [&]<std::size_t... I>(std::index_sequence<I...>) {
345 ((f(block<I>(),
346 internal::middleRowsOf<valueDimensionAtCompileTime<std::tuple_element_t<I, std::tuple<Blocks...>>>>(
347 m,
348 offset,
350 )),
351 offset += block<I>().valueDimension()),
352 ...);
353 }(std::make_index_sequence<BlockCount>{});
354 }
355
359 template <
360 bool FirstDifference,
361 bool SecondDifference,
362 bool OutDifference,
363 typename First,
364 typename Second,
365 typename Out,
366 typename F>
367 void
368 forEachBlockOf(
369 Eigen::MatrixBase<First> const & first,
370 Eigen::MatrixBase<Second> const & second,
371 Eigen::MatrixBase<Out> const & out,
372 F f
373 ) const
374 {
375 auto & o = writable(out);
376 Eigen::Index firstOffset = 0;
377 Eigen::Index secondOffset = 0;
378 Eigen::Index outOffset = 0;
379 auto const step = [&]<typename B>(B const & b) {
380 Eigen::Index const firstRows = internal::rowsOf<FirstDifference>(b);
381 Eigen::Index const secondRows = internal::rowsOf<SecondDifference>(b);
382 Eigen::Index const outRows = internal::rowsOf<OutDifference>(b);
383 f(b,
384 internal::middleRowsOf<internal::rowsAtCompileTime<FirstDifference, B>>(first, firstOffset, firstRows),
385 internal::middleRowsOf<internal::rowsAtCompileTime<SecondDifference, B>>(
386 second,
387 secondOffset,
388 secondRows
389 ),
390 internal::middleRowsOf<internal::rowsAtCompileTime<OutDifference, B>>(o, outOffset, outRows));
391 firstOffset += firstRows;
392 secondOffset += secondRows;
393 outOffset += outRows;
394 };
395 [&]<std::size_t... I>(std::index_sequence<I...>) {
396 (step(block<I>()), ...);
397 }(std::make_index_sequence<BlockCount>{});
398 EMRYS_FILTER_ASSERT(
399 firstOffset == first.rows() && secondOffset == second.rows() && outOffset == o.rows()
400 && "the rows do not match the space"
401 );
402 }
403};
404
414template <Space Block, int MaxCount_ = Eigen::Dynamic>
415class Repeated
416{
417 static_assert(
418 Block::ValueDimensionStep == 0 && Block::FixedValueDimension > 0,
419 "a repeated block must have a fixed, nonzero size"
420 );
421 static_assert(MaxCount_ == Eigen::Dynamic || MaxCount_ >= 0);
422
423public:
424
426 using BlockType = Block;
427
429 static constexpr int MaxCount = MaxCount_;
430
431 static constexpr int FixedValueDimension = 0;
432 static constexpr int ValueDimensionStep = Block::FixedValueDimension;
433
435 static constexpr int MaxValueDimension =
436 MaxCount == Eigen::Dynamic ? Eigen::Dynamic : MaxCount * Block::FixedValueDimension;
437
438 Repeated() = default;
439
441 explicit Repeated(Block block, Eigen::Index count = 0)
442 : m_block{std::move(block)}
443 {
445 }
446
448 [[nodiscard]] Block const &
449 block() const
450 {
451 return m_block;
452 }
453
455 [[nodiscard]] Eigen::Index
456 count() const
457 {
458 return m_count;
459 }
460
462 [[nodiscard]] Eigen::Index
464 {
465 return m_count * ValueDimensionStep;
466 }
467
469 void
470 setCount(Eigen::Index count)
471 {
472 EMRYS_FILTER_ASSERT(
473 count >= 0 && (MaxCount == Eigen::Dynamic || count <= MaxCount) && "the dimension does not fit the space"
474 );
475 m_count = count;
476 }
477
479 void
480 resize(Eigen::Index n)
481 {
482 EMRYS_FILTER_ASSERT(n >= 0 && n % ValueDimensionStep == 0 && "the dimension does not fit the space");
484 }
485
487 template <typename Derived>
488 void
489 normalize(Eigen::MatrixBase<Derived> const & rows) const
490 requires(internal::HasNormalize<Block>)
491 {
492 forEachRepetition(rows, [this](auto && sub) {
493 Emrys::normalizeValues(m_block, sub);
494 });
495 }
496
498 template <typename X, typename D, typename Out>
499 void
500 plus(Eigen::MatrixBase<X> const & x, Eigen::MatrixBase<D> const & d, Eigen::MatrixBase<Out> const & out) const
501 requires(internal::HasPlus<Block>)
502 {
503 forEachRepetitionOf<false, true, false>(x, d, out, [this](auto const & xs, auto const & ds, auto const & os) {
504 internal::plusInto(m_block, xs, ds, os);
505 });
506 }
507
509 template <typename A, typename B, typename Out>
510 void
511 minus(Eigen::MatrixBase<A> const & a, Eigen::MatrixBase<B> const & b, Eigen::MatrixBase<Out> const & out) const
512 requires(internal::HasMinus<Block>)
513 {
514 forEachRepetitionOf<false, false, true>(a, b, out, [this](auto const & as, auto const & bs, auto const & os) {
515 internal::minusInto(m_block, as, bs, os);
516 });
517 }
518
520 [[nodiscard]] Repeated<DifferenceSpace<Block>, MaxCount_>
522 {
523 return Repeated<DifferenceSpace<Block>, MaxCount_>{differenceSpace(m_block), m_count};
524 }
525
527 friend bool
528 operator==(Repeated const &, Repeated const &) = default;
529
530private:
531
533 template <typename Derived, typename F>
534 void
535 forEachRepetition(Eigen::MatrixBase<Derived> const & rows, F f) const
536 {
537 auto & m = writable(rows);
538 EMRYS_FILTER_ASSERT(m.rows() == valueDimension() && "the rows do not match the space");
539 for (Eigen::Index k = 0; k < m_count; ++k)
540 {
541 f(internal::middleRowsOf<ValueDimensionStep>(m, k * ValueDimensionStep, ValueDimensionStep));
542 }
543 }
544
548 template <
549 bool FirstDifference,
550 bool SecondDifference,
551 bool OutDifference,
552 typename First,
553 typename Second,
554 typename Out,
555 typename F>
556 void
557 forEachRepetitionOf(
558 Eigen::MatrixBase<First> const & first,
559 Eigen::MatrixBase<Second> const & second,
560 Eigen::MatrixBase<Out> const & out,
561 F f
562 ) const
563 {
564 auto & o = writable(out);
565 Eigen::Index const firstRows = internal::rowsOf<FirstDifference>(m_block);
566 Eigen::Index const secondRows = internal::rowsOf<SecondDifference>(m_block);
567 Eigen::Index const outRows = internal::rowsOf<OutDifference>(m_block);
568 EMRYS_FILTER_ASSERT(
569 first.rows() == m_count * firstRows && second.rows() == m_count * secondRows
570 && o.rows() == m_count * outRows && "the rows do not match the space"
571 );
572 for (Eigen::Index k = 0; k < m_count; ++k)
573 {
574 f(internal::middleRowsOf<internal::rowsAtCompileTime<FirstDifference, Block>>(
575 first,
576 k * firstRows,
577 firstRows
578 ),
579 internal::middleRowsOf<internal::rowsAtCompileTime<SecondDifference, Block>>(
580 second,
581 k * secondRows,
582 secondRows
583 ),
584 internal::middleRowsOf<internal::rowsAtCompileTime<OutDifference, Block>>(o, k * outRows, outRows));
585 }
586 }
587
588 EMRYS_FILTER_NO_UNIQUE_ADDRESS Block m_block;
589 Eigen::Index m_count = 0;
590};
591
592namespace internal
593{
594
598template <typename... Blocks>
599std::tuple<Blocks...> *
600compositeBlocks(Composite<Blocks...> const *);
601
603template <typename T>
604concept IsComposite = requires(T const * t) { compositeBlocks(t); };
605
607template <typename T>
608using CompositeBlocks = std::remove_pointer_t<decltype(compositeBlocks(std::declval<T const *>()))>;
609
611template <typename Blocks>
612inline constexpr int resizableBlockCountIn = 0;
613
615template <typename... Blocks>
616inline constexpr int resizableBlockCountIn<std::tuple<Blocks...>> = (0 + ... + resizableBlockCount<Blocks>);
617
619template <IsComposite S>
620inline constexpr int resizableBlockCount<S> = resizableBlockCountIn<CompositeBlocks<S>>;
621
628template <typename S>
629concept SizedByLength = resizableBlockCount<S> <= 1;
630
632template <typename Block, int MaxCount>
633Block *
634repeatedBlock(Repeated<Block, MaxCount> const *);
635
637template <typename T>
638concept IsRepeated = requires(T const * t) { repeatedBlock(t); };
639
641template <typename T>
642using RepeatedBlock = std::remove_pointer_t<decltype(repeatedBlock(std::declval<T const *>()))>;
643
645template <int Dimension, int MaxDimension>
646std::true_type
647leafExtent(LeafExtent<Dimension, MaxDimension> const *);
648
650std::false_type
651leafExtent(void const *);
652
654template <typename T>
655concept IsUniformLeaf = decltype(leafExtent(std::declval<T const *>()))::value;
656
658template <PeriodicInterval Interval, int Dimension, int MaxDimension>
659std::true_type
660periodicLeaf(Periodic<Interval, Dimension, MaxDimension> const *);
661
663std::false_type
664periodicLeaf(void const *);
665
667template <typename T>
668concept IsPeriodic = decltype(periodicLeaf(std::declval<T const *>()))::value;
669
673struct LeafSupport
674{
675 double lower = -std::numeric_limits<double>::infinity();
676 double upper = std::numeric_limits<double>::infinity();
677 bool periodic = false;
678};
679
684// constexpr rather than consteval: MSVC 14.50 can't apply -> to a consteval call's optional result.
685template <typename Leaf>
686[[nodiscard]] constexpr std::optional<LeafSupport>
687leafSupport()
688{
689 if constexpr (IsPeriodic<Leaf>)
690 {
691 return LeafSupport{.lower = Leaf::lower(), .upper = Leaf::upper(), .periodic = true};
692 }
693 else if constexpr (isLinear<Leaf>)
694 {
695 return LeafSupport{};
696 }
697 else
698 {
699 return std::nullopt;
700 }
701}
702
707template <typename Space, typename F>
708void
709forEachLeaf(Space const & space, F && f, Eigen::Index offset = 0)
710{
711 if constexpr (IsComposite<Space>)
712 {
713 [&]<std::size_t... I>(std::index_sequence<I...>) {
714 ((forEachLeaf(space.template block<I>(), f, offset), offset += space.template block<I>().valueDimension()),
715 ...);
716 }(std::make_index_sequence<std::tuple_size_v<CompositeBlocks<Space>>>{});
717 }
718 else if constexpr (IsRepeated<Space>)
719 {
720 for (Eigen::Index k = 0; k < space.count(); ++k)
721 {
722 forEachLeaf(space.block(), f, offset + k * Space::ValueDimensionStep);
723 }
724 }
725 else
726 {
727 f(offset, space);
728 }
729}
730
731} // namespace internal
732
733} // namespace Emrys
The coordinate types (decisions.md [no-bounded-coordinates]): Linear, plain coordinates,...
The macros every header includes first.
The Space concept and what every continuous space shares (decisions.md [sizes-and-dimensions]): the t...
The concatenation of Blocks, any number of them resizable.
Definition Composite.h:170
friend bool operator==(Composite const &, Composite const &)=default
Equal when every stored parameter and size is; a fixed-size Composite is always equal.
auto const & block() const
Definition Composite.h:229
void minus(Eigen::MatrixBase< A > const &a, Eigen::MatrixBase< B > const &b, Eigen::MatrixBase< Out > const &out) const
block by block, for a composite with a block that has its own minus(); see Emrys::minus().
Definition Composite.h:306
void resize(Eigen::Index n)
Resizes the composite's only resizable block, at any depth, so the composite has n coordinates.
Definition Composite.h:263
static constexpr int MaxValueDimension
Definition Composite.h:216
Eigen::Index offset() const
Definition Composite.h:252
Composite< DifferenceSpace< Blocks >... > difference() const
The composite of the blocks' difference spaces, with their sizes.
Definition Composite.h:323
Composite(Blocks... blocks)
The concatenation of blocks, with their parameters and sizes.
Definition Composite.h:222
void normalize(Eigen::MatrixBase< Derived > const &rows) const
Normalizes each block's rows; a composite of blocks without normalize() has none.
Definition Composite.h:275
Eigen::Index valueDimension() const
Definition Composite.h:244
void plus(Eigen::MatrixBase< X > const &x, Eigen::MatrixBase< D > const &d, Eigen::MatrixBase< Out > const &out) const
block by block, for a composite with a block that has its own plus(); see Emrys::plus().
Definition Composite.h:288
static constexpr int ValueDimensionStep
Definition Composite.h:213
static constexpr int FixedValueDimension
Definition Composite.h:207
auto & block()
Definition Composite.h:237
A fixed-size Block repeated count() times, up to MaxCount_, such as one block per vehicle.
Definition Composite.h:416
void plus(Eigen::MatrixBase< X > const &x, Eigen::MatrixBase< D > const &d, Eigen::MatrixBase< Out > const &out) const
for each repetition, for a block with its own plus().
Definition Composite.h:500
void setCount(Eigen::Index count)
Sets the number of repetitions to count, at most MaxCount.
Definition Composite.h:470
Block const & block() const
Definition Composite.h:449
static constexpr int ValueDimensionStep
the coordinates of one repetition
Definition Composite.h:432
friend bool operator==(Repeated const &, Repeated const &)=default
Equal when every stored parameter and size is; a fixed-size Repeated is always equal.
static constexpr int MaxCount
Maximum number of repetitions, or Eigen::Dynamic if unbounded.
Definition Composite.h:429
Eigen::Index valueDimension() const
Definition Composite.h:463
void resize(Eigen::Index n)
Resizes to n coordinates, a whole number of repetitions.
Definition Composite.h:480
Block BlockType
The repeated block's type.
Definition Composite.h:426
static constexpr int MaxValueDimension
MaxCount repetitions' worth of coordinates, or Eigen::Dynamic if unbounded.
Definition Composite.h:435
Eigen::Index count() const
Definition Composite.h:456
static constexpr int FixedValueDimension
nothing is fixed: every coordinate belongs to a repetition
Definition Composite.h:431
Repeated(Block block, Eigen::Index count=0)
count repetitions of block.
Definition Composite.h:441
Repeated< DifferenceSpace< Block >, MaxCount_ > difference() const
The block's difference space, repeated as many times.
Definition Composite.h:521
void normalize(Eigen::MatrixBase< Derived > const &rows) const
Normalizes each repetition; a repeated block without normalize() has none.
Definition Composite.h:489
void minus(Eigen::MatrixBase< A > const &a, Eigen::MatrixBase< B > const &b, Eigen::MatrixBase< Out > const &out) const
for each repetition, for a block with its own minus().
Definition Composite.h:511
A type usable as the space of a distribution.
Definition Space.h:186
#define EMRYS_FILTER_NO_UNIQUE_ADDRESS
Assertion used for programmer errors (misuse of the API).
Definition Macros.h:38
Eigen::MatrixBase< Derived > & writable(Eigen::MatrixBase< Derived > const &block)
block, writable: Eigen's idiom for a function that writes through an expression passed as const&.
Definition Space.h:389
DifferenceSpace< S > differenceSpace(S const &space)
Definition Space.h:235
auto block(Space auto const &space, X &&x)
Definition TagPath.h:979
constexpr bool isLinear
Whether S is linear: its values and its differences are both plain vectors.
Definition Space.h:321
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