EmrysFilter 0.1.0
State estimation utilities in C++23 using Eigen
Loading...
Searching...
No Matches
ParticleSet.h
Go to the documentation of this file.
1#pragma once
2
10
11#include <concepts>
12#include <cstddef>
13#include <expected>
14#include <functional>
15#include <optional>
16#include <random>
17#include <span>
18#include <type_traits>
19#include <utility>
20#include <variant>
21#include <vector>
22
23#include <Eigen/Cholesky>
24
44
45namespace Emrys
46{
47
48template <Space Space_, RealScalar Scalar_, typename Allocator_>
49class ParticleSet;
50
51template <Space Space_, RealScalar Scalar_, typename Allocator_>
52class KernelDensity;
53
54template <typename Source, typename Selection, typename SubSpace>
56
57namespace internal
58{
59
63struct ParticleSetAccess
64{
66 template <typename Set, typename Derived>
67 static void
68 setNormalizedLogWeights(Set & set, Eigen::DenseBase<Derived> const & logWeights)
69 {
70 set.setLogWeights(logWeights);
71 set.m_logTotalWeight = typename Set::Scalar(0);
72 }
73
78 template <typename Set, typename Rule, typename OnKept>
79 [[nodiscard]] static std::expected<typename Set::Scalar, WeightError>
80 prune(Set & set, Rule const & rule, OnKept const & onKept)
81 {
82 return set.pruneParticles(rule, onKept);
83 }
84};
85
86} // namespace internal
87
88namespace internal
89{
90
95template <typename MomentsType, ExecutionPolicy P, typename Space, typename StatesDerived, typename WeightsDerived>
96[[nodiscard]] typename MomentsType::MatrixType
97silvermanBandwidth(
98 P && policy,
99 Space const & space,
100 Eigen::MatrixBase<StatesDerived> const & states,
101 Eigen::ArrayBase<WeightsDerived> const & logWeights
102)
103{
104 using Scalar = typename MomentsType::Scalar;
105 Scalar const logTotal = logSumExp<Scalar>(policy, logWeights.size(), [&](Eigen::Index i) {
106 return logWeights.derived()(i);
107 });
108 Scalar const d = Scalar(differenceDimension(space));
109 Scalar const factor = Eigen::numext::pow(
110 Scalar(4) / (ess(policy, logWeights, logTotal) * (d + Scalar(2))),
111 Scalar(2) / (d + Scalar(4))
112 );
113 return factor * weightedMomentsIn<MomentsType>(policy, space, states, logWeights, logTotal).covarianceLower();
114}
115
117template <typename MomentsType, typename Space, typename StatesDerived, typename WeightsDerived>
118[[nodiscard]] typename MomentsType::MatrixType
119silvermanBandwidth(
120 Space const & space,
121 Eigen::MatrixBase<StatesDerived> const & states,
122 Eigen::ArrayBase<WeightsDerived> const & logWeights
123)
124{
125 return silvermanBandwidth<MomentsType>(execution::sequential, space, states, logWeights);
126}
127
142template <
143 typename Source,
144 typename BandwidthDerived,
145 typename Target,
146 typename Given,
147 typename Derived,
148 typename TargetSpace,
149 typename GivenSpace>
150[[nodiscard]] auto
151conditionalParticlesIn(
152 Source const & source,
153 Eigen::MatrixBase<BandwidthDerived> const & bandwidth,
154 Target const & target,
155 Given const & given,
156 Eigen::MatrixBase<Derived> const & values,
157 TargetSpace const & targetSpace,
158 GivenSpace const & givenSpace
159)
160{
161 using Scalar = typename Source::Scalar;
162 EMRYS_FILTER_ASSERT(distinctIndices(target.values, given.values) && "target and given indices must be distinct");
163 EMRYS_FILTER_ASSERT(values.size() == given.values.size());
168 using GivenMatrix = MatrixOf<Scalar, GivenSize, GivenSize, GivenMaxSize, GivenMaxSize>;
169 using CrossMatrix = MatrixOf<Scalar, TargetSize, GivenSize, TargetMaxSize, GivenMaxSize>;
170 using GivenOffsets = SampleSet<DifferenceSpace<GivenSpace>, Scalar>;
171 using TargetStates = SampleSet<TargetSpace, Scalar>;
172
173 using Result = ParticleSet<TargetSpace, Scalar, typename Source::allocator_type>;
174
175 auto const llt =
176 Eigen::LLT<GivenMatrix>{GivenMatrix{lowerSymmetricBlock(bandwidth, given.differences, given.differences)}};
177 if (llt.info() != Eigen::Success)
178 {
179 return std::expected<Result, GaussianError>{std::unexpected{GaussianError::NotPositiveDefinite}};
180 }
181 CrossMatrix const cross = lowerSymmetricBlock(bandwidth, target.differences, given.differences);
182 CrossMatrix const gain = transposedSolve<CrossMatrix>(llt, cross.transpose());
183
184 GivenOffsets offsets = Emrys::minus(givenSpace, source.states()(given.values, Eigen::placeholders::all), values);
185 GivenOffsets solved = offsets;
186 solveInPlace(llt, solved);
187 Eigen::Array<Scalar, 1, Eigen::Dynamic> const squaredDistance = (offsets.array() * solved.array()).colwise().sum();
188 Scalar const log2Pi = Emrys::log2Pi<Scalar>();
189 Scalar const logNormalizer =
190 Scalar(-0.5)
191 * (Scalar(given.differences.size()) * log2Pi + Scalar(2) * llt.matrixLLT().diagonal().array().log().sum());
192
193 TargetStates const centers =
194 Emrys::plus(targetSpace, source.states()(target.values, Eigen::placeholders::all), -(gain * offsets));
195 auto result = Result{
196 targetSpace,
197 centers,
198 Eigen::Array<Scalar, Eigen::Dynamic, 1>{
199 source.logWeights() + logNormalizer - Scalar(0.5) * squaredDistance.transpose()
200 },
201 source.get_allocator()
202 };
203 result.setBandwidth(
204 lowerSymmetricBlock(bandwidth, target.differences, target.differences) - multiply(gain, cross.transpose())
205 );
206 return std::expected<Result, GaussianError>{std::move(result)};
207}
208
210template <typename GivenPath, typename Source, typename BandwidthDerived, typename Derived>
211[[nodiscard]] auto
212tagConditionalParticles(
213 Source const & source,
214 Eigen::MatrixBase<BandwidthDerived> const & bandwidth,
215 Eigen::MatrixBase<Derived> const & values
216)
217{
218 return tagConditional<GivenPath>(
219 source.space(),
220 [&](auto const & target, auto const & given, auto const & targetSpace, auto const & givenSpace) {
221 return conditionalParticlesIn(source, bandwidth, target, given, values, targetSpace, givenSpace);
222 }
223 );
224}
225
226} // namespace internal
227
277template <Space Space_, RealScalar Scalar_ = double, typename Allocator_ = DefaultAllocator>
278class ParticleSet
279{
280public:
281
283 using Scalar = typename MomentsType::Scalar;
284 using Index = typename MomentsType::Index;
288
291
294
296 using ConstStateType = Eigen::Block<StatesType const, SampleSetType::RowsAtCompileTime, 1, true>;
297
298 using WeightArrayType = Eigen::Array<Scalar, Eigen::Dynamic, 1>;
299
301 using LogWeightsType = Eigen::Map<WeightArrayType const>;
302
303 using SpaceType = Space_;
304 using allocator_type = Allocator_;
306
309
312
315
318
319 static constexpr bool IsParams = true;
320
324 template <bool ReturnRemovedWeight>
325 using RemovalResult = std::conditional_t<ReturnRemovedWeight, std::expected<Scalar, WeightError>, void>;
326
332 {
333 public:
334
336
339 operator=(MutableParticle const &) = delete;
340
342 [[nodiscard]] Index
343 index() const
344 {
345 return m_index;
346 }
347
349 [[nodiscard]] Scalar
350 logWeight() const
351 {
352 return m_set.logWeight(m_index);
353 }
354
356 [[nodiscard]] ConstStateType
357 state() const
358 {
359 return m_set.state(m_index);
360 }
361
363 template <typename Derived>
364 void
365 setState(Eigen::MatrixBase<Derived> const & state)
366 {
367 EMRYS_FILTER_ASSERT(state.size() == m_set.valueDimension());
368 m_set.statesMap().col(m_index) = state;
369 normalizeValues(m_set.m_space, m_set.statesMap().col(m_index));
370 }
371
372 private:
373
374 friend ParticleSet;
375
377 MutableParticle(ParticleSet & set, Index index)
378 : m_set{set}
379 , m_index{index}
380 {}
381
382 ParticleSet & m_set;
383 Index m_index;
384 };
385
386 ParticleSet() = default;
387
389 explicit ParticleSet(Allocator_ const & allocator)
390 : m_states(StateAllocator(allocator))
391 , m_logWeights(StateAllocator(allocator))
392 , m_stateScratch(StateAllocator(allocator))
393 , m_weightScratch(StateAllocator(allocator))
394 , m_resamplerScratch(StateAllocator(allocator))
395 , m_ancestors(IndexAllocator(allocator))
396 {
397 m_rows = Emrys::valueDimension(m_space);
398 }
399
403 explicit ParticleSet(Capacity capacity, Allocator_ const & allocator = {})
405 : ParticleSet{allocator}
406 {
407 setCapacity(capacity.count);
408 }
409
411 explicit ParticleSet(Index valueDimension, Allocator_ const & allocator = {})
412 requires internal::SizedByLength<Space_>
413 : ParticleSet{allocator}
414 {
415 m_rows = valueDimension;
416 fitSpace();
417 }
418
420 template <typename StatesDerived>
421 requires internal::SizedByLength<Space_>
422 explicit ParticleSet(Eigen::MatrixBase<StatesDerived> const & states, Allocator_ const & allocator = {})
423 : ParticleSet{allocator}
424 {
425 assign(states);
426 }
427
429 template <typename StatesDerived, typename WeightsDerived>
430 requires internal::SizedByLength<Space_>
432 Eigen::MatrixBase<StatesDerived> const & states,
433 Eigen::DenseBase<WeightsDerived> const & logWeights,
434 Allocator_ const & allocator = {}
435 )
436 : ParticleSet{allocator}
437 {
439 }
440
442 explicit ParticleSet(Space_ space, Allocator_ const & allocator = {})
443 : ParticleSet{allocator}
444 {
445 m_space = std::move(space);
446 m_rows = Emrys::valueDimension(m_space);
447 }
448
450 ParticleSet(Space_ space, Capacity capacity, Allocator_ const & allocator = {})
451 : ParticleSet{std::move(space), allocator}
452 {
453 setCapacity(capacity.count);
454 }
455
460 ParticleSet(Space_ space, Index valueDimension, Allocator_ const & allocator = {})
461 : ParticleSet{allocator}
462 {
463 m_space = std::move(space);
464 m_rows = valueDimension;
465 fitSpace();
466 }
467
469 template <typename StatesDerived>
470 ParticleSet(Space_ space, Eigen::MatrixBase<StatesDerived> const & states, Allocator_ const & allocator = {})
471 : ParticleSet{allocator}
472 {
473 m_space = std::move(space);
474 assign(states);
475 }
476
480 template <typename StatesDerived, typename WeightsDerived>
482 Space_ space,
483 Eigen::MatrixBase<StatesDerived> const & states,
484 Eigen::DenseBase<WeightsDerived> const & logWeights,
485 Allocator_ const & allocator = {}
486 )
487 : ParticleSet{allocator}
488 {
489 m_space = std::move(space);
491 }
492
497 ParticleSet(ParticleSet const & other)
498 : ParticleSet{other, other.get_allocator()}
499 {}
500
504 ParticleSet(ParticleSet const & other, Allocator_ const & allocator)
505 : ParticleSet{allocator}
506 {
507 m_capacity = other.m_capacity;
508 reserveRows(other.m_rows);
509 copyFrom(other);
510 }
511
513 ParticleSet(ParticleSet && other) noexcept
514 : m_rows{other.m_rows}
515 , m_states{std::move(other.m_states)}
516 , m_logWeights{std::move(other.m_logWeights)}
517 , m_space{std::move(other.m_space)}
518 , m_bandwidth{std::move(other.m_bandwidth)}
519 , m_capacity{other.m_capacity}
520 , m_stateScratch{std::move(other.m_stateScratch)}
521 , m_weightScratch{std::move(other.m_weightScratch)}
522 , m_resamplerScratch{std::move(other.m_resamplerScratch)}
523 , m_ancestors{std::move(other.m_ancestors)}
524 , m_resampleCount{other.m_resampleCount}
525 , m_logTotalWeight{other.m_logTotalWeight}
526 {
527 other.touchShape();
528 }
529
533 ParticleSet(ParticleSet && other, Allocator_ const & allocator)
534 : m_rows{other.m_rows}
535 , m_states{std::move(other.m_states), StateAllocator(allocator)}
536 , m_logWeights{std::move(other.m_logWeights), StateAllocator(allocator)}
537 , m_space{std::move(other.m_space)}
538 , m_bandwidth{std::move(other.m_bandwidth)}
539 , m_capacity{other.m_capacity}
540 , m_stateScratch{std::move(other.m_stateScratch), StateAllocator(allocator)}
541 , m_weightScratch{std::move(other.m_weightScratch), StateAllocator(allocator)}
542 , m_resamplerScratch{std::move(other.m_resamplerScratch), StateAllocator(allocator)}
543 , m_ancestors{std::move(other.m_ancestors), IndexAllocator(allocator)}
544 , m_resampleCount{other.m_resampleCount}
545 , m_logTotalWeight{other.m_logTotalWeight}
546 {
547 other.touchShape();
548 }
549
554 operator=(ParticleSet const & other)
555 {
556 touchShape();
557 if (this != &other)
558 {
559 copyFrom(other);
560 }
561 return *this;
562 }
563
569 operator=(ParticleSet && other) noexcept
570 {
571 touchShape();
572 other.touchShape();
573 if (this == &other)
574 {
575 return *this;
576 }
577 if constexpr (std::allocator_traits<Allocator_>::propagate_on_container_move_assignment::value)
578 {
579 takeStorage(other);
580 }
581 else
582 {
583 if (get_allocator() == other.get_allocator())
584 {
585 takeStorage(other);
586 }
587 else
588 {
589 copyFrom(other);
590 m_ancestors.assign(other.m_ancestors.begin(), other.m_ancestors.end());
591 m_resampleCount = other.m_resampleCount;
592 }
593 }
594 return *this;
595 }
596
598 [[nodiscard]] allocator_type
600 {
601 return allocator_type(m_states.get_allocator());
602 }
603
605 [[nodiscard]] Index
606 size() const
607 {
608 return Index(m_logWeights.size());
609 }
610
612 [[nodiscard]] bool
613 empty() const
614 {
615 return size() == 0;
616 }
617
619 [[nodiscard]] std::optional<Index>
620 capacity() const
621 {
622 return m_capacity;
623 }
624
628 void
630 {
631 EMRYS_FILTER_ASSERT(capacity >= size() && "the capacity is smaller than the set");
632 m_capacity = capacity;
633 reserveRows(m_rows);
634 }
635
637 [[nodiscard]] Index
639 {
640 return m_rows;
641 }
642
644 [[nodiscard]] Index
646 {
647 return Emrys::differenceDimension(m_space);
648 }
649
651 [[nodiscard]] Space_ const &
652 space() const
653 {
654 return m_space;
655 }
656
658 [[nodiscard]] StatesType
659 states() const
660 {
661 return StatesType{m_states.data(), m_rows, size()};
662 }
663
665 [[nodiscard]] LogWeightsType
667 {
668 return LogWeightsType{m_logWeights.data(), size()};
669 }
670
672 [[nodiscard]] ConstStateType
673 state(Index i) const
674 {
675 StatesType const all = states();
676 return ConstStateType{all, i};
677 }
678
680 [[nodiscard]] Scalar
682 {
683 assertIndex(i);
684 return m_logWeights[std::size_t(i)];
685 }
686
688 [[nodiscard]] std::optional<MatrixType> const &
689 bandwidth() const
690 {
691 return m_bandwidth;
692 }
693
698 [[nodiscard]] std::expected<MatrixType, WeightError>
700 {
702 }
703
707 template <ExecutionPolicy P>
708 [[nodiscard]] std::expected<MatrixType, WeightError>
709 kdeBandwidth(P && policy) const
710 {
711 if (m_bandwidth)
712 {
713 return *m_bandwidth;
714 }
715 return silvermanBandwidth(std::forward<P>(policy), *this);
716 }
717
719 template <MatrixExpression Bandwidth>
720 void
722 {
723 m_stamp.touchValues();
724 EMRYS_FILTER_ASSERT(bandwidth.rows() == differenceDimension() && bandwidth.cols() == differenceDimension());
725 m_bandwidth = MatrixType{std::forward<Bandwidth>(bandwidth)};
726 }
727
729 void
731 {
732 m_stamp.touchValues();
733 m_bandwidth.reset();
734 }
735
740 void
742 requires internal::SizedByLength<Space_>
743 {
744 touchShape();
745 internal::fitValueDimension(m_space, n);
746 m_rows = n;
747 reserveRows(m_rows);
748 m_states.resize(std::size_t(m_rows * size()));
749 m_bandwidth.reset();
750 }
751
757 template <typename Path, typename Derived>
758 void
759 insert(Index element, Eigen::MatrixBase<Derived> const & states)
760 {
761 touchShape();
762 EMRYS_FILTER_ASSERT(states.cols() == size() && "insert one column per particle");
763 auto const rows = internal::growBlock<Path>(m_space, element, states.rows());
764 Index const newRows = m_rows + states.rows();
765 reserveRows(newRows);
766 m_stateScratch.resize(std::size_t(newRows * size()));
767 auto grown = SampleSetMap<Space_, Scalar_>{m_stateScratch.data(), newRows, size()};
768 grown.topRows(rows.start) = this->states().topRows(rows.start);
769 grown.middleRows(rows.start, rows.count) = states;
770 grown.bottomRows(m_rows - rows.start) = this->states().bottomRows(m_rows - rows.start);
771 m_states.swap(m_stateScratch);
772 m_rows = newRows;
773 normalizeValues(m_space, statesMap());
774 m_bandwidth.reset();
775 }
776
781 template <typename Path>
782 void
783 erase(Index element, Index count = 1)
784 {
785 touchShape();
786 auto const rows = internal::shrinkBlock<Path>(m_space, element, count);
787 Index const newRows = m_rows - rows.count;
788 m_stateScratch.resize(std::size_t(newRows * size()));
789 auto shrunk = SampleSetMap<Space_, Scalar_>{m_stateScratch.data(), newRows, size()};
790 shrunk.topRows(rows.start) = states().topRows(rows.start);
791 shrunk.bottomRows(newRows - rows.start) = states().bottomRows(newRows - rows.start);
792 m_states.swap(m_stateScratch);
793 m_rows = newRows;
794 if (m_bandwidth)
795 {
796 internal::eraseSymmetric(*m_bandwidth, rows.differenceStart, rows.differenceCount);
797 }
798 }
799
801 void
803 {
804 touchShape();
805 m_states.clear();
806 m_logWeights.clear();
807 }
808
812 template <typename StatesDerived>
813 void
814 assign(Eigen::MatrixBase<StatesDerived> const & states)
815 {
816 touchShape();
817 copyStates(states);
818 logWeightsMap().setConstant(-Eigen::numext::log(Scalar(size())));
819 fitSpace();
820 }
821
825 template <typename StatesDerived, typename WeightsDerived>
826 void
827 assign(Eigen::MatrixBase<StatesDerived> const & states, Eigen::DenseBase<WeightsDerived> const & logWeights)
828 {
829 touchShape();
830 EMRYS_FILTER_ASSERT(logWeights.size() == states.cols());
831 copyStates(states);
832 logWeightsMap() = logWeights.derived().array();
833 fitSpace();
834 }
835
840 template <typename Derived>
841 Index
842 add(Scalar logWeight, Eigen::MatrixBase<Derived> const & state)
843 {
844 touchShape();
845 EMRYS_FILTER_ASSERT(state.size() == valueDimension());
846 // Copied first: growing the storage would move a state read from this set.
847 VectorType const value = state;
848 Index const i = size();
849 setCount(i + 1);
850 statesMap().col(i) = value;
851 normalizeValues(m_space, statesMap().col(i));
852 m_logWeights[std::size_t(i)] = logWeight;
853 return i;
854 }
855
857 template <typename OtherAllocator>
858 void
859 append(ParticleSet<Space_, Scalar_, OtherAllocator> const & other)
860 {
861 touchShape();
862 EMRYS_FILTER_ASSERT(other.space() == m_space && "cannot append a set in a different space");
863 Index const n = size();
864 Index const m = other.size();
865 // Read after growing, which may move this set's storage when other is this set.
866 auto const * source = &other;
867 setCount(n + m);
868 statesMap().rightCols(m) = source->states().leftCols(m);
869 logWeightsMap().tail(m) = source->logWeights().head(m);
870 }
871
873 void
875 {
876 touchShape();
877 assertIndex(i);
878 Index const last = size() - 1;
879 if (i != last)
880 {
881 statesMap().col(i) = states().col(last);
882 m_logWeights[std::size_t(i)] = m_logWeights[std::size_t(last)];
883 }
884 setCount(last);
885 }
886
888 template <typename Derived>
889 void
890 setState(Index i, Eigen::MatrixBase<Derived> const & state)
891 {
892 m_stamp.touchValues();
893 assertIndex(i);
894 EMRYS_FILTER_ASSERT(state.size() == valueDimension());
895 statesMap().col(i) = state;
896 normalizeValues(m_space, statesMap().col(i));
897 }
898
900 template <typename Derived>
901 void
902 setStates(Eigen::MatrixBase<Derived> const & states)
903 {
904 m_stamp.touchValues();
905 EMRYS_FILTER_ASSERT(states.rows() == valueDimension() && states.cols() == size());
906 statesMap() = states;
907 normalizeValues(m_space, statesMap());
908 }
909
911 void
913 {
914 touchWeights();
915 assertIndex(i);
916 m_logWeights[std::size_t(i)] = logWeight;
917 }
918
920 template <typename Derived>
921 void
922 setLogWeights(Eigen::DenseBase<Derived> const & logWeights)
923 {
924 touchWeights();
925 EMRYS_FILTER_ASSERT(logWeights.size() == size());
926 logWeightsMap() = logWeights.derived().array();
927 }
928
930 void
932 {
933 touchWeights();
934 assertIndex(i);
935 m_logWeights[std::size_t(i)] += logScale;
936 }
937
939 void
941 {
942 touchWeights();
943 logWeightsMap() += logScale;
944 }
945
947 template <typename Derived>
948 void
949 scaleWeights(Eigen::DenseBase<Derived> const & logScales)
950 {
951 touchWeights();
952 EMRYS_FILTER_ASSERT(logScales.size() == size());
953 logWeightsMap() += logScales.derived().array();
954 }
955
964 [[nodiscard]] std::expected<Scalar, WeightError>
966 {
968 }
969
971 template <ExecutionPolicy P>
972 [[nodiscard]] std::expected<Scalar, WeightError>
973 logTotalWeight(P && policy) const
974 {
975 if (m_logTotalWeight)
976 {
977 return *m_logTotalWeight;
978 }
979 return internal::logTotalWeight<Scalar>(
980 std::forward<P>(policy),
981 std::span<Scalar const>{m_logWeights.data(), m_logWeights.size()}
982 );
983 }
984
989 [[nodiscard]] std::expected<void, WeightError>
991 {
993 }
994
998 template <ExecutionPolicy P>
999 [[nodiscard]] std::expected<void, WeightError>
1000 normalize(P && policy)
1001 {
1002 touchWeights();
1003 auto const total = logTotalWeight(std::forward<P>(policy));
1004 if (!total)
1005 {
1006 return std::unexpected{total.error()};
1007 }
1008 scaleWeights(-*total);
1009 m_logTotalWeight = Scalar(0);
1010 return {};
1011 }
1012
1016 [[nodiscard]] std::expected<WeightArrayType, WeightError>
1018 {
1019 return logTotalWeight().transform([this](Scalar total) {
1020 return WeightArrayType{(logWeights() - total).exp()};
1021 });
1022 }
1023
1028 [[nodiscard]] std::expected<Scalar, WeightError>
1029 ess() const
1030 {
1031 return ess(execution::sequential);
1032 }
1033
1035 template <ExecutionPolicy P>
1036 [[nodiscard]] std::expected<Scalar, WeightError>
1037 ess(P && policy) const
1038 {
1039 return logTotalWeight(policy).transform([&](Scalar total) {
1040 return internal::ess(policy, logWeights(), total);
1041 });
1042 }
1043
1052 [[nodiscard]] std::expected<MomentsType, WeightError>
1053 moments() const
1054 {
1056 }
1057
1061 template <ExecutionPolicy P>
1062 [[nodiscard]] std::expected<MomentsType, WeightError>
1063 moments(P && policy) const
1064 {
1065 return logTotalWeight(policy).transform([&](Scalar total) {
1066 return internal::weightedMomentsIn<MomentsType>(policy, m_space, states(), logWeights(), total);
1067 });
1068 }
1069
1075 template <typename F>
1076 void
1077 forEach(F && f)
1078 {
1079 forEach(execution::sequential, std::forward<F>(f));
1080 }
1081
1086 template <ExecutionPolicy P, typename F>
1087 void
1088 forEach(P && policy, F && f)
1089 {
1090 static_assert(
1091 std::is_void_v<std::invoke_result_t<F &, MutableParticle &>>,
1092 "forEach() takes a callback that returns nothing; one that returns a log scale factor goes to reweight()"
1093 );
1094 visitParticles(std::forward<P>(policy), [&](MutableParticle & particle, Index) {
1095 std::invoke(f, particle);
1096 });
1097 }
1098
1102 template <typename F>
1103 void
1104 reweight(F && f)
1105 {
1106 reweight(execution::sequential, std::forward<F>(f));
1107 }
1108
1110 template <ExecutionPolicy P, typename F>
1111 void
1112 reweight(P && policy, F && f)
1113 {
1114 static_assert(
1115 std::floating_point<std::remove_cvref_t<std::invoke_result_t<F &, MutableParticle &>>>,
1116 "reweight() takes a callback that returns a log scale factor, a floating-point number"
1117 );
1118 visitParticles(std::forward<P>(policy), [&](MutableParticle & particle, Index i) {
1119 m_logWeights[std::size_t(i)] += Scalar(std::invoke(f, particle));
1120 });
1121 }
1122
1129 template <bool ReturnRemovedWeight = false, typename Pred>
1130 requires(!ReturnRemovedWeight && !ExecutionPolicy<Pred>)
1131 void
1132 removeIf(Pred pred)
1133 {
1134 removeParticlesIf<false>(execution::sequential, std::move(pred));
1135 }
1136
1140 template <bool ReturnRemovedWeight, typename Pred>
1141 requires(ReturnRemovedWeight && !ExecutionPolicy<Pred>)
1142 [[nodiscard]] std::expected<Scalar, WeightError>
1143 removeIf(Pred pred)
1144 {
1145 return removeParticlesIf<true>(execution::sequential, std::move(pred));
1146 }
1147
1153 template <bool ReturnRemovedWeight = false, ExecutionPolicy P, typename Pred>
1154 requires(!ReturnRemovedWeight)
1155 void
1156 removeIf(P && policy, Pred pred)
1157 {
1158 removeParticlesIf<false>(std::forward<P>(policy), std::move(pred));
1159 }
1160
1164 template <bool ReturnRemovedWeight, ExecutionPolicy P, typename Pred>
1165 requires ReturnRemovedWeight
1166 [[nodiscard]] std::expected<Scalar, WeightError>
1167 removeIf(P && policy, Pred pred)
1168 {
1169 return removeParticlesIf<true>(std::forward<P>(policy), std::move(pred));
1170 }
1171
1183 [[nodiscard]] std::expected<Scalar, WeightError>
1184 pruneBelow(Scalar logThreshold)
1185 {
1186 return pruneParticles(internal::PruneBelowRule<Scalar>{logThreshold}, [](Index) {});
1187 }
1188
1199 [[nodiscard]] std::expected<Scalar, WeightError>
1200 pruneTail(Scalar logFraction)
1201 {
1202 return pruneParticles(internal::PruneTailRule<Scalar>{logFraction}, [](Index) {});
1203 }
1204
1214 [[nodiscard]] std::expected<Scalar, WeightError>
1216 {
1217 return pruneParticles(internal::PruneToSizeRule{maxSize}, [](Index) {});
1218 }
1219
1233 template <std::uniform_random_bit_generator G, typename R = resamplers::Systematic>
1235 [[nodiscard]] std::expected<void, WeightError>
1236 resample(G & rng, Index newSize, R const & resampler = {})
1237 {
1238 return resample(execution::sequential, rng, newSize, resampler);
1239 }
1240
1242 template <std::uniform_random_bit_generator G, typename R = resamplers::Systematic>
1243 requires Resampler<R, Scalar, G>
1244 [[nodiscard]] std::expected<void, WeightError>
1245 resample(G & rng, R const & resampler = {})
1246 {
1247 return resample(execution::sequential, rng, size(), resampler);
1248 }
1249
1256 template <ExecutionPolicy P, std::uniform_random_bit_generator G, typename R = resamplers::Systematic>
1257 requires Resampler<R, Scalar, G>
1258 [[nodiscard]] std::expected<void, WeightError>
1259 resample(P && policy, G & rng, Index newSize, R const & resampler = {})
1260 {
1261 touchShape();
1262 EMRYS_FILTER_ASSERT(newSize > 0);
1263 assertCapacity(newSize);
1264 auto const total = logTotalWeight(policy);
1265 if (!total)
1266 {
1267 return std::unexpected{total.error()};
1268 }
1269
1270 m_weightScratch.resize(std::size_t(size()));
1271 auto linearWeights = Eigen::Map<WeightArrayType>{m_weightScratch.data(), size()};
1272 internal::forEachChunk(policy, size(), [&](Index begin, Index length) {
1273 linearWeights.segment(begin, length) = (logWeights().segment(begin, length) - *total).exp();
1274 });
1275 m_ancestors.resize(std::size_t(newSize));
1276 auto const weights = std::span<Scalar const>{m_weightScratch};
1277 auto const ancestors = std::span<Index>{m_ancestors};
1278 if constexpr (ResamplerUnder<R, P, Scalar, G> && !internal::isSequential<P>)
1279 {
1280 resampler(policy, weights, ancestors, rng);
1281 }
1282 else if constexpr (ResamplerWithScratch<R, Scalar, G>)
1283 {
1284 m_resamplerScratch.resize(std::size_t(size() + newSize));
1285 resampler(weights, ancestors, rng, std::span<Scalar>{m_resamplerScratch});
1286 }
1287 else
1288 {
1289 resampler(weights, ancestors, rng);
1290 }
1291
1292 m_stateScratch.resize(std::size_t(m_rows * newSize));
1293 internal::gather(
1294 policy,
1295 std::span<Index const>{ancestors},
1296 states(),
1297 SampleSetMap<Space_, Scalar_>{m_stateScratch.data(), m_rows, newSize}
1298 );
1299 m_states.swap(m_stateScratch);
1300 m_logWeights.resize(std::size_t(newSize));
1301 internal::fill(std::forward<P>(policy), std::span<Scalar>{m_logWeights}, -Eigen::numext::log(Scalar(newSize)));
1302 m_logTotalWeight = Scalar(0);
1303 m_bandwidth.reset();
1304 ++m_resampleCount;
1305 return {};
1306 }
1307
1309 template <ExecutionPolicy P, std::uniform_random_bit_generator G, typename R = resamplers::Systematic>
1310 requires Resampler<R, Scalar, G>
1311 [[nodiscard]] std::expected<void, WeightError>
1312 resample(P && policy, G & rng, R const & resampler = {})
1313 {
1314 return resample(std::forward<P>(policy), rng, size(), resampler);
1315 }
1316
1320 [[nodiscard]] std::span<Index const>
1322 {
1323 return {m_ancestors.data(), m_ancestors.size()};
1324 }
1325
1330 [[nodiscard]] Index
1332 {
1333 return m_resampleCount;
1334 }
1335
1339 template <typename Path>
1340 [[nodiscard]] auto
1341 marginal() const &
1342 {
1343 return internal::tagMarginal<ParticleSetMarginal, Path>(*this);
1344 }
1345
1347 template <typename Path>
1348 void
1349 marginal() const && = delete;
1350
1352 template <typename Path>
1353 [[nodiscard]] auto
1354 marginal(Index element) const &
1355 {
1356 return internal::elementMarginal<ParticleSetMarginal, Path>(*this, element);
1357 }
1358
1360 template <typename Path>
1361 void
1362 marginal(Index element) const && = delete;
1363
1380 template <typename GivenPath, typename Derived>
1381 [[nodiscard]] auto
1382 conditional(Eigen::MatrixBase<Derived> const & values) const
1383 {
1384 auto const bandwidth = kdeBandwidth();
1385 using Result = decltype(internal::tagConditionalParticles<GivenPath>(*this, *bandwidth, values));
1386 if (!bandwidth)
1387 {
1388 return Result{std::unexpected{GaussianError::NonFinite}};
1389 }
1390 return internal::tagConditionalParticles<GivenPath>(*this, *bandwidth, values);
1391 }
1392
1401 [[nodiscard]] std::expected<DensityType, NumericError>
1402 toDensity() const &
1403 {
1404 auto const total = logTotalWeight();
1405 if (!total)
1406 {
1407 return std::unexpected{total.error()};
1408 }
1409 // The weights are valid, so the bandwidth is found.
1410 return kernelDensity(*total, *kdeBandwidth());
1411 }
1412
1414 void
1415 toDensity() const && = delete;
1416
1423 template <MatrixExpression Bandwidth>
1424 [[nodiscard]] std::expected<DensityType, NumericError>
1425 toDensity(Bandwidth && bandwidth) const &
1426 {
1427 auto const total = logTotalWeight();
1428 if (!total)
1429 {
1430 return std::unexpected{total.error()};
1431 }
1432 return kernelDensity(*total, std::forward<Bandwidth>(bandwidth));
1433 }
1434
1436 template <MatrixExpression Bandwidth>
1437 void
1438 toDensity(Bandwidth && bandwidth) const && = delete;
1439
1440private:
1441
1443 template <typename>
1444 friend class internal::ShapeCheck;
1445
1447 template <typename>
1448 friend class internal::ValuesCheck;
1449
1451 friend struct internal::ParticleSetAccess;
1452
1454 template <bool ReturnRemovedWeight, ExecutionPolicy P, typename Pred>
1456 removeParticlesIf(P && policy, Pred pred)
1457 {
1458 touchShape();
1459 [[maybe_unused]] Scalar logTotal = 0;
1460 if constexpr (ReturnRemovedWeight)
1461 {
1462 auto const total = logTotalWeight(policy);
1463 if (!total)
1464 {
1465 return std::unexpected{total.error()};
1466 }
1467 logTotal = *total;
1468 }
1469 auto const guard = internal::CallbackGuard{m_stamp};
1470 auto const checked = [&](Scalar logWeight, ConstStateType const & state) {
1471 bool const remove = static_cast<bool>(std::invoke(pred, logWeight, state));
1472 guard.check();
1473 return remove;
1474 };
1475 [[maybe_unused]] Scalar removed = 0;
1476 if constexpr (internal::isSequential<P>)
1477 {
1478 removed = compact([&](Index i) {
1479 return checked(m_logWeights[std::size_t(i)], state(i));
1480 });
1481 }
1482 else
1483 {
1484 removed = compact(policy, checked, ReturnRemovedWeight);
1485 }
1486 if constexpr (ReturnRemovedWeight)
1487 {
1488 return removed - logTotal;
1489 }
1490 }
1491
1499 template <typename Rule, typename OnKept>
1500 [[nodiscard]] std::expected<Scalar, WeightError>
1501 pruneParticles(Rule const & rule, OnKept const & onKept)
1502 {
1503 auto const total = logTotalWeight();
1504 if (!total)
1505 {
1506 return std::unexpected{total.error()};
1507 }
1508 touchShape();
1509 auto cut = rule(
1510 size(),
1511 [&](Index i) {
1512 return m_logWeights[std::size_t(i)];
1513 },
1514 *total,
1515 m_weightScratch
1516 );
1517 Scalar const removed = compact([&](Index i) {
1518 bool const remove = cut.removes(m_logWeights[std::size_t(i)]);
1519 if (!remove)
1520 {
1521 onKept(i);
1522 }
1523 return remove;
1524 });
1525 // The kept particles include one of positive weight, so their total is finite.
1526 static_cast<void>(normalize());
1527 return removed - *total;
1528 }
1529
1533 template <ExecutionPolicy P, typename Visit>
1534 void
1535 visitParticles(P && policy, Visit const & visit)
1536 {
1537 touchWeights();
1538 auto const guard = internal::CallbackGuard{m_stamp};
1539 internal::forEachIndex(std::forward<P>(policy), size(), [&](Index i) {
1540 auto particle = MutableParticle{*this, i};
1541 visit(particle, i);
1542 guard.check();
1543 });
1544 }
1545
1547 void
1548 touchWeights()
1549 {
1550 m_stamp.touchValues();
1551 m_logTotalWeight.reset();
1552 }
1553
1555 void
1556 touchShape()
1557 {
1558 m_stamp.touchShape();
1559 m_logTotalWeight.reset();
1560 }
1561
1562 using StateAllocator = internal::ReBound<Allocator_, Scalar>;
1563 using IndexAllocator = internal::ReBound<Allocator_, Index>;
1564
1566 void
1567 assertCapacity([[maybe_unused]] Index count) const
1568 {
1569 EMRYS_FILTER_ASSERT(
1570 (!m_capacity || count <= *m_capacity) && "the set would hold more particles than its capacity"
1571 );
1572 }
1573
1575 [[nodiscard]] SampleSetMap<Space_, Scalar_>
1576 statesMap()
1577 {
1578 return {m_states.data(), m_rows, size()};
1579 }
1580
1582 [[nodiscard]] Eigen::Map<WeightArrayType>
1583 logWeightsMap()
1584 {
1585 return {m_logWeights.data(), size()};
1586 }
1587
1589 void
1590 setCount(Index count)
1591 {
1592 assertCapacity(count);
1593 m_states.resize(std::size_t(m_rows * count));
1594 m_logWeights.resize(std::size_t(count));
1595 }
1596
1600 void
1601 reserveRows(Index rows)
1602 {
1603 if (!m_capacity)
1604 {
1605 return;
1606 }
1607 Index const bound = maxValueDimensionAtCompileTime<Space_> == Eigen::Dynamic
1608 ? rows
1610 auto const count = std::size_t(*m_capacity);
1611 m_states.reserve(std::size_t(bound) * count);
1612 m_stateScratch.reserve(std::size_t(bound) * count);
1613 m_logWeights.reserve(count);
1614 m_weightScratch.reserve(count);
1615 m_resamplerScratch.reserve(2 * count);
1616 m_ancestors.reserve(count);
1617 }
1618
1622 template <typename StatesDerived>
1623 void
1624 copyStates(Eigen::MatrixBase<StatesDerived> const & states)
1625 {
1626 if (states.rows() != m_rows)
1627 {
1628 m_bandwidth.reset();
1629 }
1630 m_rows = states.rows();
1631 reserveRows(m_rows);
1632 setCount(states.cols());
1633 statesMap() = states;
1634 }
1635
1639 void
1640 copyFrom(ParticleSet const & other)
1641 {
1642 m_rows = other.m_rows;
1643 m_space = other.m_space;
1644 m_bandwidth = other.m_bandwidth;
1645 setCount(other.size());
1646 m_states.assign(other.m_states.begin(), other.m_states.end());
1647 m_logWeights.assign(other.m_logWeights.begin(), other.m_logWeights.end());
1648 m_logTotalWeight = other.m_logTotalWeight;
1649 m_ancestors.clear();
1650 m_resampleCount = 0;
1651 }
1652
1654 void
1655 takeStorage(ParticleSet & other)
1656 {
1657 m_rows = other.m_rows;
1658 m_states = std::move(other.m_states);
1659 m_logWeights = std::move(other.m_logWeights);
1660 m_logTotalWeight = other.m_logTotalWeight;
1661 m_space = std::move(other.m_space);
1662 m_bandwidth = std::move(other.m_bandwidth);
1663 m_capacity = other.m_capacity;
1664 m_stateScratch = std::move(other.m_stateScratch);
1665 m_weightScratch = std::move(other.m_weightScratch);
1666 m_resamplerScratch = std::move(other.m_resamplerScratch);
1667 m_ancestors = std::move(other.m_ancestors);
1668 m_resampleCount = other.m_resampleCount;
1669 }
1670
1674 template <MatrixExpression Bandwidth>
1675 [[nodiscard]] std::expected<DensityType, NumericError>
1676 kernelDensity(Scalar logTotalWeight, Bandwidth && bandwidth) const
1677 {
1678 auto kde = DensityType::create(*this, logTotalWeight, std::forward<Bandwidth>(bandwidth));
1679 if (!kde)
1680 {
1681 return std::unexpected{kde.error()};
1682 }
1683 return std::move(*kde);
1684 }
1685
1687 void
1688 assertIndex([[maybe_unused]] Index i) const
1689 {
1690 EMRYS_FILTER_ASSERT(i >= 0 && i < size() && "particle index out of range");
1691 }
1692
1696 void
1697 fitSpace()
1698 {
1699 internal::fitValueDimension(m_space, valueDimension());
1700 normalizeValues(m_space, statesMap());
1701 }
1702
1707 template <typename RemoveAtIndex>
1708 Scalar
1709 compact(RemoveAtIndex removeAtIndex)
1710 {
1711 auto removed = LogSumExp<Scalar>{};
1712 Index out = 0;
1713 for (Index i = 0; i < size(); ++i)
1714 {
1715 if (removeAtIndex(i))
1716 {
1717 removed.add(m_logWeights[std::size_t(i)]);
1718 continue;
1719 }
1720 if (out != i)
1721 {
1722 statesMap().col(out) = states().col(i);
1723 m_logWeights[std::size_t(out)] = m_logWeights[std::size_t(i)];
1724 }
1725 ++out;
1726 }
1727 setCount(out);
1728 return removed.value();
1729 }
1730
1738 template <ExecutionPolicy P, typename Pred>
1739 Scalar
1740 compact(P && policy, Pred const & pred, bool sumRemoved)
1741 {
1742 Index const count = size();
1743 auto remove = internal::scratchVector<char>(policy, static_cast<std::size_t>(count));
1744 internal::forEachIndex(policy, count, [&](Index i) {
1745 remove[std::size_t(i)] = static_cast<bool>(std::invoke(pred, m_logWeights[std::size_t(i)], state(i)));
1746 });
1747 Scalar const removed = sumRemoved ? logSumExp<Scalar>(
1748 policy,
1749 count,
1750 [&](Index i) {
1751 return remove[std::size_t(i)]
1752 ? m_logWeights[std::size_t(i)]
1753 : -Eigen::NumTraits<Scalar>::infinity();
1754 }
1755 )
1756 : -Eigen::NumTraits<Scalar>::infinity();
1757 m_stateScratch.resize(m_states.size());
1758 m_weightScratch.resize(m_logWeights.size());
1759 auto kept = SampleSetMap<Space_, Scalar_>{m_stateScratch.data(), m_rows, count};
1760 Index const keptCount = internal::copyIfIndex(
1761 std::forward<P>(policy),
1762 count,
1763 [&](Index i) {
1764 return remove[std::size_t(i)] == 0;
1765 },
1766 [&](Index i, Index j) {
1767 kept.col(j) = states().col(i);
1768 m_weightScratch[std::size_t(j)] = m_logWeights[std::size_t(i)];
1769 }
1770 );
1771 m_states.swap(m_stateScratch);
1772 m_logWeights.swap(m_weightScratch);
1773 setCount(keptCount);
1774 return removed;
1775 }
1776
1777 Index m_rows = valueDimensionAtCompileTime<Space_> == Eigen::Dynamic
1778 ? 0
1780 internal::Buffer<Scalar, Allocator_> m_states;
1781 internal::Buffer<Scalar, Allocator_> m_logWeights;
1782 EMRYS_FILTER_NO_UNIQUE_ADDRESS Space_ m_space;
1783 std::optional<MatrixType> m_bandwidth;
1784 std::optional<Index> m_capacity;
1785
1786 // Scratch, kept between calls so that steady-size resampling and resizing don't allocate.
1787 internal::Buffer<Scalar, Allocator_> m_stateScratch;
1788
1789 internal::Buffer<Scalar, Allocator_> m_weightScratch;
1790 internal::Buffer<Scalar, Allocator_> m_resamplerScratch;
1791 internal::Buffer<Index, Allocator_> m_ancestors;
1792 Index m_resampleCount = 0;
1793 std::optional<Scalar> m_logTotalWeight;
1794 internal::Stamp m_stamp;
1795};
1796
1823template <typename Source, typename Selection, typename SubSpace>
1825{
1826public:
1827
1828 using Scalar = typename Source::Scalar;
1829 using Index = Eigen::Index;
1830
1833
1836
1839
1843
1844 using SpaceType = SubSpace;
1845 using allocator_type = typename Source::allocator_type;
1849 using LogWeightsType = typename Source::LogWeightsType;
1850
1852 using IndexVectorType = internal::IndexedSize<
1853 decltype(Selection::values),
1854 Source::ValueDimensionAtCompileTime,
1855 Source::MaxValueDimensionAtCompileTime>;
1856
1858 using DifferenceIndexVectorType = internal::IndexedSize<
1859 decltype(Selection::differences),
1860 Source::DifferenceDimensionAtCompileTime,
1861 Source::MaxDifferenceDimensionAtCompileTime>;
1862
1864 using StateType = decltype(std::declval<Source const &>().state(Index{})(std::declval<IndexVectorType const &>()));
1865
1872
1874 using const_iterator = internal::ElementIterator<ParticleSetMarginal>;
1876
1878 ParticleSetMarginal(Source const & source, Selection const & selection, SubSpace space)
1879 : m_holder{source}
1880 , m_indices{internal::resolveIndices<
1882 Source::MaxValueDimensionAtCompileTime>(source.valueDimension(), selection.values)}
1883 , m_differenceIndices{internal::resolveIndices<
1885 Source::MaxDifferenceDimensionAtCompileTime>(source.differenceDimension(), selection.differences)}
1886 , m_space{std::move(space)}
1887 {
1888 EMRYS_FILTER_ASSERT(
1889 internal::distinctIndices(m_indices) && internal::distinctIndices(m_differenceIndices)
1890 && "marginal indices must be distinct"
1891 );
1892 }
1893
1895 [[nodiscard]] SubSpace const &
1896 space() const
1897 {
1898 return m_space;
1899 }
1900
1902 [[nodiscard]] allocator_type
1904 {
1905 return source().get_allocator();
1906 }
1907
1909 [[nodiscard]] Index
1910 size() const
1911 {
1912 return source().size();
1913 }
1914
1916 [[nodiscard]] Index
1918 {
1919 return m_indices.size();
1920 }
1921
1923 [[nodiscard]] Index
1925 {
1926 return m_differenceIndices.size();
1927 }
1928
1930 [[nodiscard]] IndexVectorType const &
1931 indices() const
1932 {
1933 return m_indices;
1934 }
1935
1937 [[nodiscard]] DifferenceIndexVectorType const &
1939 {
1940 return m_differenceIndices;
1941 }
1942
1944 [[nodiscard]] auto
1945 states() const
1946 {
1947 return source().states()(m_indices, Eigen::placeholders::all);
1948 }
1949
1951 [[nodiscard]] auto
1952 state(Index i) const
1953 {
1954 return source().state(i)(m_indices);
1955 }
1956
1958 [[nodiscard]] LogWeightsType
1960 {
1961 return source().logWeights();
1962 }
1963
1965 [[nodiscard]] Scalar
1967 {
1968 return source().logWeight(i);
1969 }
1970
1974 [[nodiscard]] Element
1976 {
1977 return Element{logWeight(i), state(i)};
1978 }
1979
1981 [[nodiscard]] const_iterator
1982 begin() const
1983 {
1984 return const_iterator{*this, 0};
1985 }
1986
1988 [[nodiscard]] const_iterator
1989 end() const
1990 {
1991 return const_iterator{*this, size()};
1992 }
1993
1995 [[nodiscard]] std::optional<MatrixType>
1997 {
1998 auto const & full = source().bandwidth();
1999 if (!full)
2000 {
2001 return std::nullopt;
2002 }
2003 return MatrixType{internal::lowerSymmetricBlock(*full, m_differenceIndices, m_differenceIndices)};
2004 }
2005
2009 [[nodiscard]] ParamsType
2010 copy() const
2011 {
2012 return copy(get_allocator());
2013 }
2014
2016 [[nodiscard]] ParamsType
2017 copy(allocator_type const & allocator) const
2018 {
2019 auto result = ParamsType{m_space, states(), logWeights(), allocator};
2020 if (auto const h = bandwidth())
2021 {
2022 result.setBandwidth(*h);
2023 }
2024 return result;
2025 }
2026
2027private:
2028
2030 [[nodiscard]] Source const &
2031 source() const
2032 {
2033 return m_holder.get();
2034 }
2035
2036 internal::HolderFor<Source> m_holder;
2037 IndexVectorType m_indices;
2038 DifferenceIndexVectorType m_differenceIndices;
2039 SubSpace m_space;
2040};
2041
2055template <typename Scalar, typename Space, typename Allocator>
2056[[nodiscard]] std::expected<typename ParticleSet<Space, Scalar, Allocator>::MatrixType, WeightError>
2061
2066template <ExecutionPolicy P, typename Scalar, typename Space, typename Allocator>
2067[[nodiscard]] std::expected<typename ParticleSet<Space, Scalar, Allocator>::MatrixType, WeightError>
2069{
2070 using MomentsType = typename ParticleSet<Space, Scalar, Allocator>::MomentsType;
2071 return particles.logTotalWeight(policy).transform([&](Scalar) {
2072 return internal::silvermanBandwidth<MomentsType>(
2073 policy,
2074 particles.space(),
2075 particles.states(),
2076 particles.logWeights()
2077 );
2078 });
2079}
2080
2081} // namespace Emrys
2082
2083// KernelDensity completes ParticleSet::toDensity(); it includes this header first.
2085
2086#ifndef EMRYS_FILTER_NO_IO
2087
2088namespace Emrys
2089{
2090
2091namespace internal
2092{
2093
2097template <typename Space_, typename Scalar_, typename Allocator_>
2098void
2099describe(std::ostream & stream, ParticleSet<Space_, Scalar_, Allocator_> const & particles, Indent indent)
2100{
2101 stream << indent << "particle set of ";
2102 printCount(stream, particles.size(), "particle");
2103 printDimension(stream, particles.valueDimension(), particles.differenceDimension());
2104 if (auto const ess = particles.ess())
2105 {
2106 stream << ", effective sample size " << *ess;
2107 }
2108 printMoments(stream, indent.deeper(), particles.moments());
2109}
2110
2111} // namespace internal
2112
2113} // namespace Emrys
2114
2115#endif // EMRYS_FILTER_NO_IO
The allocator conventions of decisions.md [allocation]: DefaultAllocator, Capacity,...
Spaces built from other spaces (decisions.md [sizes-and-dimensions], [several-resizable-blocks]): Com...
Execution policies (decisions.md [execution-policies]): execution::sequential, the ExecutionPolicy co...
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,...
What marginals and conditionals are built from: resolving Eigen index sets against a space; the share...
KernelDensity: the Gaussian kernel density estimate over a ParticleSet, the read-only view that Parti...
The macros every header includes first.
Mathematical constants and scalar functions that work in every scalar type the library accepts,...
Moments: the mean and covariance that a Gaussian carries and that a mixture or a sample set is moment...
Products of fixed-size matrices by Eigen's fixed-size product at every size: multiply() is by Eigen'...
Printing: operator<< and std::formatter for every distribution, every filter (which prints its name a...
The resamplers: function objects that draw ancestors in proportion to weights, which ParticleSet::res...
SampleSet: a matrix whose columns are samples in a space, which the samplers return and the unscented...
Solving by a factorization of a small matrix: transposedSolve() gives , the shape of a Kalman gain an...
The Space concept and what every continuous space shares (decisions.md [sizes-and-dimensions]): the t...
What parameters and their views share (decisions.md [view-stamp], [params-and-density]): Stamp,...
Weighted statistics over points, all in internal, which the containers of weighted elements (Gaussian...
Subspaces, and growing and shrinking blocks (decisions.md [several-resizable-blocks]).
Log-weights: the WeightError a normalization can fail with (NumericError.h), and the streaming log-su...
Gaussian kernel density estimate over a ParticleSet.
Definition KernelDensity.h:55
First and second moments (mean and covariance) of a distribution.
Definition Moments.h:55
Scalar_ Scalar
Definition Moments.h:58
Eigen::Index Index
Definition Moments.h:59
DifferenceMatrix< Space_, Space_, Scalar > MatrixType
Definition Moments.h:76
DifferenceVector< Space_, Scalar > DifferenceVectorType
Definition Moments.h:75
ValueVector< Space_, Scalar > VectorType
Definition Moments.h:74
static constexpr int MaxDifferenceDimensionAtCompileTime
Definition Moments.h:72
static constexpr int DifferenceDimensionAtCompileTime
Definition Moments.h:69
static constexpr int MaxValueDimensionAtCompileTime
Definition Moments.h:66
static constexpr int ValueDimensionAtCompileTime
Definition Moments.h:63
Read-only view of a subset of the state entries of a ParticleSet.
Definition ParticleSet.h:1825
LogWeightsType logWeights() const
Definition ParticleSet.h:1959
Index differenceDimension() const
Definition ParticleSet.h:1924
Element operator[](Index i) const
Definition ParticleSet.h:1975
Index size() const
Definition ParticleSet.h:1910
typename Source::allocator_type allocator_type
the source's allocator
Definition ParticleSet.h:1845
static constexpr int ValueDimensionAtCompileTime
the length of a marginal state, or Eigen::Dynamic
Definition ParticleSet.h:1832
typename ParamsType::WeightArrayType WeightArrayType
one entry per particle
Definition ParticleSet.h:1848
SubSpace SpaceType
the selected block's space
Definition ParticleSet.h:1844
typename Source::LogWeightsType LogWeightsType
the source's log-weights map
Definition ParticleSet.h:1849
internal::ElementIterator< ParticleSetMarginal > const_iterator
iterates the particles
Definition ParticleSet.h:1874
IndexVectorType const & indices() const
Definition ParticleSet.h:1931
static constexpr int MaxValueDimensionAtCompileTime
a bound on the length of a marginal state, or Eigen::Dynamic
Definition ParticleSet.h:1835
typename Source::Scalar Scalar
the scalar type
Definition ParticleSet.h:1828
std::optional< MatrixType > bandwidth() const
Definition ParticleSet.h:1996
ParamsType copy() const
Definition ParticleSet.h:2010
static constexpr int DifferenceDimensionAtCompileTime
the length of a difference and the rows of the marginal bandwidth, or Eigen::Dynamic
Definition ParticleSet.h:1838
Scalar logWeight(Index i) const
Definition ParticleSet.h:1966
const_iterator end() const
Definition ParticleSet.h:1989
typename ParamsType::MatrixType MatrixType
a matrix over the marginal's differences
Definition ParticleSet.h:1847
DifferenceIndexVectorType const & differenceIndices() const
Definition ParticleSet.h:1938
auto states() const
Definition ParticleSet.h:1945
ParticleSetMarginal(Source const &source, Selection const &selection, SubSpace space)
Selects selection's coordinates, which form space.
Definition ParticleSet.h:1878
internal::IndexedSize< decltype(Selection::differences), Source::DifferenceDimensionAtCompileTime, Source::MaxDifferenceDimensionAtCompileTime > DifferenceIndexVectorType
The selected bandwidth rows' indices.
Definition ParticleSet.h:1858
internal::IndexedSize< decltype(Selection::values), Source::ValueDimensionAtCompileTime, Source::MaxValueDimensionAtCompileTime > IndexVectorType
The selected state entries' indices.
Definition ParticleSet.h:1852
Element value_type
an element, made when read
Definition ParticleSet.h:1873
const_iterator begin() const
Definition ParticleSet.h:1982
decltype(std::declval< Source const & >().state(Index{})(std::declval< IndexVectorType const & >())) StateType
The selected entries of one particle's state, a lazy expression that reads the source.
Definition ParticleSet.h:1864
ParamsType copy(allocator_type const &allocator) const
copy() with storage from allocator.
Definition ParticleSet.h:2017
static constexpr int MaxDifferenceDimensionAtCompileTime
a bound on the length of a difference, or Eigen::Dynamic
Definition ParticleSet.h:1841
ParticleSet< SubSpace, Scalar, allocator_type > ParamsType
what copy() returns
Definition ParticleSet.h:1846
Eigen::Index Index
Eigen's index type.
Definition ParticleSet.h:1829
Index valueDimension() const
Definition ParticleSet.h:1917
const_iterator iterator
iterates the particles; they are read-only
Definition ParticleSet.h:1875
allocator_type get_allocator() const
Definition ParticleSet.h:1903
SubSpace const & space() const
Definition ParticleSet.h:1896
auto state(Index i) const
Definition ParticleSet.h:1952
Writable access to one particle during forEach().
Definition ParticleSet.h:332
Index index() const
Definition ParticleSet.h:343
ConstStateType state() const
Definition ParticleSet.h:357
MutableParticle & operator=(MutableParticle const &)=delete
Lives only inside the callback.
void setState(Eigen::MatrixBase< Derived > const &state)
Sets the particle's state, normalized by the space.
Definition ParticleSet.h:365
Scalar logWeight() const
Definition ParticleSet.h:350
MutableParticle(MutableParticle const &)=delete
lives only inside the callback
A set of weighted samples: the empirical distribution.
Definition ParticleSet.h:279
typename MomentsType::Index Index
Definition ParticleSet.h:284
void setLogWeight(Index i, Scalar logWeight)
Sets particle i's log-weight.
Definition ParticleSet.h:912
ParticleSet(Eigen::MatrixBase< StatesDerived > const &states, Eigen::DenseBase< WeightsDerived > const &logWeights, Allocator_ const &allocator={})
Constructs a set from the columns of states with the log-weights logWeights.
Definition ParticleSet.h:431
Eigen::Map< WeightArrayType const > LogWeightsType
Definition ParticleSet.h:301
void scaleWeights(Eigen::DenseBase< Derived > const &logScales)
Multiplies weight i by , e.g.
Definition ParticleSet.h:949
static constexpr int MaxDifferenceDimensionAtCompileTime
Definition ParticleSet.h:317
ParticleSet(Space_ space, Index valueDimension, Allocator_ const &allocator={})
Constructs an empty set in space whose particles will have valueDimension coordinates.
Definition ParticleSet.h:460
ParticleSet(ParticleSet const &other, Allocator_ const &allocator)
Copies the particles and any stored bandwidth into storage from allocator, with other's capacity and ...
Definition ParticleSet.h:504
std::expected< Scalar, WeightError > removeIf(Pred pred)
As above, returning the log of the removed fraction of the total weight, or the WeightError that left...
Definition ParticleSet.h:1143
void assign(Eigen::MatrixBase< StatesDerived > const &states)
Replaces the particles with the columns of states, with equal weights summing to one.
Definition ParticleSet.h:814
auto conditional(Eigen::MatrixBase< Derived > const &values) const
Definition ParticleSet.h:1382
Index resampleCount() const
Definition ParticleSet.h:1331
std::expected< MomentsType, WeightError > moments() const
Definition ParticleSet.h:1053
Index size() const
Definition ParticleSet.h:606
ConstSampleSetMap< Space_, Scalar_ > StatesType
Definition ParticleSet.h:293
void assign(Eigen::MatrixBase< StatesDerived > const &states, Eigen::DenseBase< WeightsDerived > const &logWeights)
Replaces the particles and weights.
Definition ParticleSet.h:827
allocator_type get_allocator() const
Definition ParticleSet.h:599
std::expected< void, WeightError > resample(G &rng, R const &resampler={})
Resamples to the current size().
Definition ParticleSet.h:1245
void reweight(P &&policy, F &&f)
reweight() under policy, as forEach() under it runs.
Definition ParticleSet.h:1112
void reweight(F &&f)
Calls f on each particle as a MutableParticle&, as forEach() does, and adds what it returns,...
Definition ParticleSet.h:1104
std::expected< Scalar, WeightError > pruneBelow(Scalar logThreshold)
Removes every particle whose normalized log-weight is below logThreshold, and normalizes the weights ...
Definition ParticleSet.h:1184
Space_ SpaceType
Definition ParticleSet.h:303
void remove(Index i)
Removes particle i by moving the last particle into its place.
Definition ParticleSet.h:874
std::expected< void, WeightError > resample(P &&policy, G &rng, R const &resampler={})
resample() to the current size() under policy.
Definition ParticleSet.h:1312
std::expected< Scalar, WeightError > ess(P &&policy) const
ess() under policy: two reductions over the particles (see Execution policies).
Definition ParticleSet.h:1037
void removeIf(Pred pred)
Removes every particle for which pred(logWeight, state) is true.
Definition ParticleSet.h:1132
auto marginal() const &
marginal() of the subspace named by the tag path Path (see idx()), whose space is that subspace with ...
Definition ParticleSet.h:1341
void toDensity() const &&=delete
A density of a temporary would outlive it.
LogWeightsType logWeights() const
Definition ParticleSet.h:666
std::expected< void, WeightError > resample(G &rng, Index newSize, R const &resampler={})
Replaces the particles with newSize draws chosen by resampler in proportion to their weights,...
Definition ParticleSet.h:1236
Allocator_ allocator_type
Definition ParticleSet.h:304
ParticleSet(Eigen::MatrixBase< StatesDerived > const &states, Allocator_ const &allocator={})
Constructs a set from the columns of states, with equal weights summing to one.
Definition ParticleSet.h:422
ParticleSet(Space_ space, Eigen::MatrixBase< StatesDerived > const &states, Eigen::DenseBase< WeightsDerived > const &logWeights, Allocator_ const &allocator={})
Constructs a set in space from the columns of states with the log-weights logWeights.
Definition ParticleSet.h:481
Eigen::Array< Scalar, Eigen::Dynamic, 1 > WeightArrayType
Definition ParticleSet.h:298
static constexpr int ValueDimensionAtCompileTime
Definition ParticleSet.h:308
bool empty() const
Definition ParticleSet.h:613
ParticleSet(Space_ space, Eigen::MatrixBase< StatesDerived > const &states, Allocator_ const &allocator={})
Constructs a set in space from the columns of states, with equal weights summing to one.
Definition ParticleSet.h:470
std::expected< void, WeightError > normalize()
Scales the weights so they sum to one.
Definition ParticleSet.h:990
typename MomentsType::Scalar Scalar
Definition ParticleSet.h:283
std::optional< Index > capacity() const
Definition ParticleSet.h:620
ParticleSet(ParticleSet &&other) noexcept
Takes other's particles and storage; a view of other may not be read afterward.
Definition ParticleSet.h:513
std::optional< MatrixType > const & bandwidth() const
Definition ParticleSet.h:689
std::expected< MatrixType, WeightError > kdeBandwidth() const
Definition ParticleSet.h:699
std::expected< Scalar, WeightError > logTotalWeight(P &&policy) const
logTotalWeight() under policy: a reduction over the particles (see Execution policies).
Definition ParticleSet.h:973
void scaleWeight(Index i, Scalar logScale)
Multiplies the weight of particle i by .
Definition ParticleSet.h:931
static constexpr int MaxValueDimensionAtCompileTime
Definition ParticleSet.h:311
void forEach(P &&policy, F &&f)
forEach() under policy, a for-each over the particles (see Execution policies).
Definition ParticleSet.h:1088
void clearBandwidth()
Forgets the stored bandwidth, so kdeBandwidth() returns to Silverman's rule.
Definition ParticleSet.h:730
void erase(Index element, Index count=1)
Removes count elements of the resizable block named by the tag path Path from every particle,...
Definition ParticleSet.h:783
KernelDensity< Space_, Scalar_, Allocator_ > DensityType
Definition ParticleSet.h:305
Index add(Scalar logWeight, Eigen::MatrixBase< Derived > const &state)
Appends a particle.
Definition ParticleSet.h:842
typename MomentsType::DifferenceVectorType DifferenceVectorType
Definition ParticleSet.h:286
std::expected< Scalar, WeightError > pruneTail(Scalar logFraction)
Removes the lightest particles, as many as can be while their combined normalized weight stays below ...
Definition ParticleSet.h:1200
ParticleSet(Space_ space, Capacity capacity, Allocator_ const &allocator={})
Constructs an empty set in space that reserves room for capacity particles.
Definition ParticleSet.h:450
Space_ const & space() const
Definition ParticleSet.h:652
ParticleSet(ParticleSet &&other, Allocator_ const &allocator)
Moves other's particles into storage from allocator, taking its storage when the allocators are equal...
Definition ParticleSet.h:533
Scalar logWeight(Index i) const
Definition ParticleSet.h:681
void removeIf(P &&policy, Pred pred)
removeIf() under policy: pred runs as a for-each over the particles, and the particles kept are compa...
Definition ParticleSet.h:1156
static constexpr bool IsParams
Definition ParticleSet.h:319
Index differenceDimension() const
Definition ParticleSet.h:645
ParticleSet(ParticleSet const &other)
Copies the particles and any stored bandwidth into storage from other's allocator,...
Definition ParticleSet.h:497
std::expected< Scalar, WeightError > pruneToSize(Index maxSize)
Removes the lightest particles until at most maxSize, at least one, remain, and normalizes the weight...
Definition ParticleSet.h:1215
std::conditional_t< ReturnRemovedWeight, std::expected< Scalar, WeightError >, void > RemovalResult
Definition ParticleSet.h:325
static constexpr int DifferenceDimensionAtCompileTime
Definition ParticleSet.h:314
std::expected< void, WeightError > normalize(P &&policy)
normalize() under policy: the total is a reduction over the particles, and scaling the weights a whol...
Definition ParticleSet.h:1000
ParticleSet(Capacity capacity, Allocator_ const &allocator={})
Constructs an empty set in a FixedSizeSpace that reserves room for capacity particles.
Definition ParticleSet.h:403
ParticleSet & operator=(ParticleSet &&other) noexcept
Takes other's storage when the allocators are equal or propagate, otherwise copies into this set's st...
Definition ParticleSet.h:569
void marginal() const &&=delete
A marginal of a temporary would outlive it.
void resizeParticles(Index n)
Resizes every particle state to n coordinates and clears the bandwidth.
Definition ParticleSet.h:741
Eigen::Block< StatesType const, SampleSetType::RowsAtCompileTime, 1, true > ConstStateType
Definition ParticleSet.h:296
ConstStateType state(Index i) const
Definition ParticleSet.h:673
std::expected< DensityType, NumericError > toDensity() const &
Validates the weights and builds a kernel density estimate with kdeBandwidth(): the stored bandwidth ...
Definition ParticleSet.h:1402
void append(ParticleSet< Space_, Scalar_, OtherAllocator > const &other)
Appends copies of other's particles, which must be in the same space.
Definition ParticleSet.h:859
typename MomentsType::VectorType VectorType
Definition ParticleSet.h:285
std::span< Index const > ancestors() const
Definition ParticleSet.h:1321
void toDensity(Bandwidth &&bandwidth) const &&=delete
A density of a temporary would outlive it.
StatesType states() const
Definition ParticleSet.h:659
std::expected< WeightArrayType, WeightError > normalizedWeights() const
Definition ParticleSet.h:1017
Moments< Space_, Scalar_ > MomentsType
Definition ParticleSet.h:282
Index valueDimension() const
Definition ParticleSet.h:638
void setLogWeights(Eigen::DenseBase< Derived > const &logWeights)
Sets every log-weight at once; logWeights has one entry per particle.
Definition ParticleSet.h:922
void insert(Index element, Eigen::MatrixBase< Derived > const &states)
Inserts the state entries states, one column per particle, into the resizable block named by the tag ...
Definition ParticleSet.h:759
ParticleSet(Space_ space, Allocator_ const &allocator={})
Constructs an empty set in space.
Definition ParticleSet.h:442
void scaleWeights(Scalar logScale)
Multiplies every weight by .
Definition ParticleSet.h:940
void forEach(F &&f)
Calls f on each particle as a MutableParticle&, returning nothing; reweight() takes one that returns ...
Definition ParticleSet.h:1077
ParticleSet(Index valueDimension, Allocator_ const &allocator={})
Constructs an empty set whose particles will have valueDimension coordinates.
Definition ParticleSet.h:411
void setCapacity(Index capacity)
Reserves room for capacity particles, and for the scratch resample() uses, and makes holding more a p...
Definition ParticleSet.h:629
void marginal(Index element) const &&=delete
A marginal of a temporary would outlive it.
std::expected< void, WeightError > resample(P &&policy, G &rng, Index newSize, R const &resampler={})
resample() under policy: the total weight is a reduction, the linear weights a whole-array expression...
Definition ParticleSet.h:1259
std::expected< Scalar, WeightError > removeIf(P &&policy, Pred pred)
As above, returning the log of the removed fraction of the total weight, or the WeightError that left...
Definition ParticleSet.h:1167
std::expected< Scalar, WeightError > logTotalWeight() const
Definition ParticleSet.h:965
void setState(Index i, Eigen::MatrixBase< Derived > const &state)
Sets particle i's state, normalized by the space.
Definition ParticleSet.h:890
ParticleSet(Allocator_ const &allocator)
Constructs an empty set that allocates through allocator.
Definition ParticleSet.h:389
ParticleSet & operator=(ParticleSet const &other)
Copies other's particles, weights, space, and bandwidth into this set's storage, which keeps its allo...
Definition ParticleSet.h:554
std::expected< MomentsType, WeightError > moments(P &&policy) const
moments() under policy: reductions over the particles, which match Sequential's to rounding (see Exec...
Definition ParticleSet.h:1063
void clear()
Removes every particle; the space, bandwidth, capacity, and scratch stay.
Definition ParticleSet.h:802
void setBandwidth(Bandwidth &&bandwidth)
Stores bandwidth as the kernel covariance.
Definition ParticleSet.h:721
typename MomentsType::MatrixType MatrixType
Definition ParticleSet.h:287
void setStates(Eigen::MatrixBase< Derived > const &states)
Sets every state at once.
Definition ParticleSet.h:902
std::expected< MatrixType, WeightError > kdeBandwidth(P &&policy) const
kdeBandwidth() under policy: Silverman's rule's total weight, effective sample size,...
Definition ParticleSet.h:709
SampleSet< Space_, Scalar_ > SampleSetType
Definition ParticleSet.h:290
std::expected< Scalar, WeightError > ess() const
Definition ParticleSet.h:1029
An execution policy: execution::sequential, a policy of the standard library such as std::execution::...
Definition Execution.h:459
A space whose instance holds nothing, so that S{} is the only one there is: a space of fixed size,...
Definition Space.h:217
A dense matrix or matrix expression, possibly cv- or reference-qualified: what a const Eigen::MatrixB...
Definition Forwarding.h:39
Requirements on a resampler for scalar type Scalar and random generator G.
Definition Resampling.h:61
Scalar logSumExp(P &&policy, Eigen::Index count, LogAt const &logAt)
Definition Weights.h:151
#define EMRYS_FILTER_NO_UNIQUE_ADDRESS
Assertion used for programmer errors (misuse of the API).
Definition Macros.h:38
NumericError WeightError
Reasons a collection of log-weights cannot be normalized: Empty, ZeroSum, or NonFinite of NumericErro...
Definition NumericError.h:53
Scalar log2Pi()
Definition Math.h:177
NumericError
Reasons a computation from parameters the caller gave can fail: a matrix that does not factorize or a...
Definition NumericError.h:30
@ NotPositiveDefinite
the covariance is not positive definite, so it has no Cholesky factor or inverse
Definition NumericError.h:31
@ NonFinite
a matrix, a factor, or a log-weight holds NaN or infinity (+inf, for a log-weight)
Definition NumericError.h:33
Eigen::Matrix< Scalar, valueDimensionAtCompileTime< Space >, Eigen::Dynamic,(maxValueDimensionAtCompileTime< Space >==1 ? Eigen::RowMajor :Eigen::ColMajor), maxValueDimensionAtCompileTime< Space >, Eigen::Dynamic > SampleSet
A matrix whose columns are samples in Space (see Spaces), with the space's size and storage bound.
Definition SampleSet.h:33
std::expected< typename ParticleSet< Space, Scalar, Allocator >::MatrixType, WeightError > silvermanBandwidth(ParticleSet< Space, Scalar, Allocator > const &particles)
Definition ParticleSet.h:2057
Eigen::Map< SampleSet< Space, Scalar > > SampleSetMap
A writable view of samples in Space, one per column, over memory it does not own, laid out as a Sampl...
Definition SampleSet.h:50
Eigen::Map< SampleSet< Space, Scalar > const > ConstSampleSetMap
A read-only view of samples in Space, one per column, over memory it does not own,...
Definition SampleSet.h:60
constexpr Sequential sequential
The default execution policy: plain loops on the calling thread, in index order (Sequential).
Definition Execution.h:128
GivenBlock< Space, P, Value > given(Space const &space, Value const &value)
Definition Measurement.h:917
Eigen::Index valueDimension(S const &space)
Definition Space.h:272
auto plus(S const &space, Eigen::MatrixBase< XDerived > const &x, Eigen::MatrixBase< DDerived > const &d)
Definition Space.h:675
auto minus(S const &space, Eigen::MatrixBase< ADerived > const &a, Eigen::MatrixBase< BDerived > const &b)
Definition Space.h:715
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
constexpr int maxValueDimensionAtCompileTime
The largest valid value dimension of Space, or Eigen::Dynamic if it is unbounded.
Definition Space.h:258
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
The most elements a container will hold, reserved once: a mixture's components or a particle set's pa...
Definition Allocation.h:54
One particle's log-weight, as in the source, and the selected entries of its state.
Definition ParticleSet.h:1868
StateType state
the selected entries of its state, read in place
Definition ParticleSet.h:1870
Scalar logWeight
the particle's log-weight
Definition ParticleSet.h:1869