19#include <Eigen/Cholesky>
30namespace Emrys::internal
34template <
int Size,
int MaxSize>
35using IndexVector = Eigen::Matrix<Eigen::Index, Size, 1, Eigen::ColMajor, MaxSize, 1>;
38template <
typename Indices,
int SourceSize,
int SourceMaxSize>
44 using Probe = Eigen::Matrix<int, SourceSize, 1, Eigen::ColMajor, SourceMaxSize, 1>;
47 using Expr = std::remove_cvref_t<decltype(std::declval<Probe const &>()(std::declval<Indices const &>()))>;
49 static_assert(Expr::ColsAtCompileTime == 1,
"marginal and conditional indices must select a set, not one index");
53 static constexpr int size = Expr::RowsAtCompileTime;
57 static constexpr int maxSize = size != Eigen::Dynamic ? size
58 : Expr::MaxRowsAtCompileTime != Eigen::Dynamic ? int(Expr::MaxRowsAtCompileTime)
61 using type = IndexVector<size, maxSize>;
67template <
typename Indices,
int SourceSize,
int SourceMaxSize>
68using IndexedSize =
typename IndexedSizeOf<Indices, SourceSize, SourceMaxSize>::type;
74template <
int SourceSize,
int SourceMaxSize,
typename Indices>
75[[nodiscard]] IndexedSize<Indices, SourceSize, SourceMaxSize>
76resolveIndices(Eigen::Index n, Indices
const & indices)
78 using Identity = Eigen::Matrix<Eigen::Index, SourceSize, 1, Eigen::ColMajor, SourceMaxSize, 1>;
79 Identity
const identity = Identity::LinSpaced(n, 0, n - 1);
80 return identity(indices);
84template <
int Size,
int MaxSize>
85[[nodiscard]] IndexVector<Size, MaxSize>
86consecutiveIndices(Eigen::Index start, Eigen::Index count)
88 return IndexVector<Size, MaxSize>::LinSpaced(count, start, start + count - 1);
94template <
typename... Vectors>
96distinctIndices(Vectors
const &... indices)
99 std::size_t position = 0;
100 auto const seenBefore = [&](Eigen::Index index, std::size_t before) {
103 auto const scan = [&](
auto const & vector) {
104 for (Eigen::Index
const other : vector)
106 if (k++ < before && other == index)
112 (scan(indices), ...);
115 auto const check = [&](
auto const & vector) {
116 for (Eigen::Index
const index : vector)
118 if (seenBefore(index, position))
125 (check(indices), ...);
133template <
int SourceSize,
int SourceMaxSize,
int GivenSize,
int GivenMaxSize>
135complementIndices(Eigen::Index n, IndexVector<GivenSize, GivenMaxSize>
const &
given)
137 auto taken = Eigen::Matrix<bool, SourceSize, 1, Eigen::ColMajor, SourceMaxSize, 1>{};
138 taken.setConstant(n,
false);
139 for (Eigen::Index
const i :
given)
143 constexpr int RestSize =
144 SourceSize != Eigen::Dynamic && GivenSize != Eigen::Dynamic ? SourceSize - GivenSize : Eigen::Dynamic;
145 auto result = IndexVector<RestSize, RestSize == Eigen::Dynamic ? SourceMaxSize : RestSize>{};
146 result.resize(n -
given.size());
147 Eigen::Index out = 0;
148 for (Eigen::Index i = 0; i < n; ++i)
162template <
typename Values,
typename Differences>
166 Differences differences;
172template <
typename Space,
typename ValueIndices,
typename DifferenceIndices>
174resolveCoordinates(Space
const & space, ValueIndices
const & values, DifferenceIndices
const & differences)
188template <
typename Path,
typename Space>
190pathCoordinates(Space
const & space)
199template <
typename IndexVector_,
typename DifferenceIndexVector_,
typename SubSpace>
200struct ResolvedSelection
202 IndexVector_ indices;
203 DifferenceIndexVector_ differenceIndices;
208template <
typename Resolved>
209struct SharedSelection
211 Resolved
const * resolved;
215template <
typename Selection>
216inline constexpr bool isSharedSelection =
false;
219template <
typename Resolved>
220inline constexpr bool isSharedSelection<SharedSelection<Resolved>> =
true;
225template <
typename Source,
typename Selection,
typename SubSpace>
231 using ResolvedType = ResolvedSelection<
233 decltype(Selection::values),
234 Source::ValueDimensionAtCompileTime,
235 Source::MaxValueDimensionAtCompileTime>,
237 decltype(Selection::differences),
238 Source::DifferenceDimensionAtCompileTime,
239 Source::MaxDifferenceDimensionAtCompileTime>,
243 SelectionHolder(Source
const & source, Selection
const & selection, SubSpace space)
245 resolveIndices<Source::ValueDimensionAtCompileTime, Source::MaxValueDimensionAtCompileTime>(
249 resolveIndices<Source::DifferenceDimensionAtCompileTime, Source::MaxDifferenceDimensionAtCompileTime>(
251 selection.differences
257 distinctIndices(m_resolved.indices) && distinctIndices(m_resolved.differenceIndices)
258 &&
"marginal indices must be distinct"
263 [[nodiscard]] ResolvedType
const &
271 ResolvedType m_resolved;
275template <
typename Source,
typename Resolved,
typename SubSpace>
276class SelectionHolder<Source, SharedSelection<Resolved>, SubSpace>
280 using ResolvedType = Resolved;
283 explicit SelectionHolder(SharedSelection<Resolved> shared)
284 : m_resolved{
shared.resolved}
288 [[nodiscard]] ResolvedType
const &
296 Resolved
const * m_resolved;
309template <
typename GivenPath,
typename Space,
typename Condition>
311tagConditional(Space
const & space, Condition
condition)
319 auto const given = pathCoordinates<GivenPath>(space);
321 Coordinates{complementIndices<N, NMax>(n,
given.values), complementIndices<D, DMax>(d,
given.differences)};
322 return condition(target,
given, pathComplement<GivenPath>(space), pathSubspace<GivenPath>(space));
330template <
typename Path,
typename Space,
typename Make>
332marginalOverPath(Space
const & space, Make make)
334 return make(pathCoordinates<Path>(space), pathSubspace<Path>(space));
343template <
typename Path,
typename Space,
typename Make>
345marginalOverElement(Space
const & space, Eigen::Index element, Make make)
349 pathElementSubspace<Path>(space)
354template <
typename Matrix,
typename Derived>
356insertRows(Matrix & matrix, Eigen::Index
at, Eigen::MatrixBase<Derived>
const & rows)
358 Eigen::Index
const n = matrix.rows();
359 Eigen::Index
const k = rows.rows();
360 EMRYS_FILTER_ASSERT(
at >= 0 &&
at <= n && rows.cols() == matrix.cols());
361 auto result = Matrix{};
362 result.resize(n + k, matrix.cols());
363 result.topRows(
at) = matrix.topRows(
at);
364 result.middleRows(
at, k) = rows;
365 result.bottomRows(n -
at) = matrix.bottomRows(n -
at);
366 matrix = std::move(result);
370template <
typename Matrix>
372eraseRows(Matrix & matrix, Eigen::Index
at, Eigen::Index count)
374 Eigen::Index
const n = matrix.rows();
375 EMRYS_FILTER_ASSERT(
at >= 0 && count >= 0 &&
at + count <= n);
376 auto result = Matrix{};
377 result.resize(n - count, matrix.cols());
378 result.topRows(
at) = matrix.topRows(
at);
379 result.bottomRows(n -
at - count) = matrix.bottomRows(n -
at - count);
380 matrix = std::move(result);
386template <
typename Matrix,
typename BlockDerived,
typename CrossDerived>
391 Eigen::MatrixBase<BlockDerived>
const &
block,
392 Eigen::MatrixBase<CrossDerived>
const & cross
395 Eigen::Index
const n = matrix.rows();
396 Eigen::Index
const k =
block.rows();
397 Eigen::Index
const after = n -
at;
398 EMRYS_FILTER_ASSERT(
at >= 0 &&
at <= n &&
block.cols() == k && cross.rows() == k && cross.cols() == n);
399 auto result = Matrix{};
400 result.resize(n + k, n + k);
401 result.topLeftCorner(
at,
at) = matrix.topLeftCorner(
at,
at);
402 result.topRightCorner(
at, after) = matrix.topRightCorner(
at, after);
403 result.bottomLeftCorner(after,
at) = matrix.bottomLeftCorner(after,
at);
404 result.bottomRightCorner(after, after) = matrix.bottomRightCorner(after, after);
406 result.block(
at, 0, k,
at) = cross.leftCols(
at);
407 result.block(
at,
at + k, k, after) = cross.rightCols(after);
408 result.block(0,
at,
at, k) = cross.leftCols(
at).transpose();
409 result.block(
at + k,
at, after, k) = cross.rightCols(after).transpose();
410 matrix = std::move(result);
414template <
typename Matrix>
416eraseSymmetric(Matrix & matrix, Eigen::Index
at, Eigen::Index count)
418 Eigen::Index
const n = matrix.rows();
419 Eigen::Index
const after = n -
at - count;
420 EMRYS_FILTER_ASSERT(
at >= 0 && count >= 0 && after >= 0);
421 auto result = Matrix{};
422 result.resize(n - count, n - count);
423 result.topLeftCorner(
at,
at) = matrix.topLeftCorner(
at,
at);
424 result.topRightCorner(
at, after) = matrix.topRightCorner(
at, after);
425 result.bottomLeftCorner(after,
at) = matrix.bottomLeftCorner(after,
at);
426 result.bottomRightCorner(after, after) = matrix.bottomRightCorner(after, after);
427 matrix = std::move(result);
433template <
typename Result,
typename Derived>
435symmetricFromLower(Eigen::MatrixBase<Derived>
const & matrix)
437 if constexpr (std::is_base_of_v<Eigen::PlainObjectBase<Derived>, Derived>)
439 Result result = matrix.template selfadjointView<Eigen::Lower>();
444 Result result = matrix;
445 result.template triangularView<Eigen::StrictlyUpper>() = result.transpose();
455template <
int Rows,
int MaxRows,
int Cols,
int MaxCols,
typename Derived>
458 Eigen::MatrixBase<Derived>
const & matrix,
459 IndexVector<Rows, MaxRows>
const & rows,
460 IndexVector<Cols, MaxCols>
const & cols
463 using Scalar =
typename Derived::Scalar;
464 using Result = MatrixOf<Scalar, Rows, Cols, MaxRows, MaxCols>;
465 return Result::NullaryExpr(
468 [&matrix = matrix.derived(), &rows, &cols](Eigen::Index i, Eigen::Index j) {
469 Eigen::Index const r = rows(i);
470 Eigen::Index const c = cols(j);
471 return r >= c ? matrix(r, c) : matrix(c, r);
481template <
int Size,
int MaxSize,
typename Derived,
typename Indices>
483lowerSymmetricSelection(Eigen::MatrixBase<Derived>
const & matrix, Indices
const & indices)
485 if constexpr (std::is_integral_v<Indices>)
487 return matrix.derived().coeff(indices, indices);
491 using Scalar =
typename Derived::Scalar;
492 using Resolved = IndexedSize<Indices, Size, MaxSize>;
493 using Result = MatrixOf<
495 Resolved::RowsAtCompileTime,
496 Resolved::RowsAtCompileTime,
497 Resolved::MaxRowsAtCompileTime,
498 Resolved::MaxRowsAtCompileTime>;
499 Resolved resolved = resolveIndices<Size, MaxSize>(matrix.rows(), indices);
500 Eigen::Index
const n = resolved.size();
501 return Result::NullaryExpr(
504 [&matrix = matrix.derived(), resolved = std::move(resolved)](Eigen::Index i, Eigen::Index j) {
505 Eigen::Index const r = resolved(i);
506 Eigen::Index const c = resolved(j);
507 return r >= c ? matrix(r, c) : matrix(c, r);
517template <
int Size,
int MaxSize,
typename Derived,
typename Rows,
typename Cols>
519lowerSymmetricCross(Eigen::MatrixBase<Derived>
const & matrix, Rows
const & rows, Cols
const & cols)
521 if constexpr (std::is_integral_v<Rows> && std::is_integral_v<Cols>)
523 return rows >= cols ? matrix.derived().coeff(rows, cols) : matrix.derived().coeff(cols, rows);
527 auto const resolvedRows = resolveIndices<Size, MaxSize>(matrix.rows(), asIndexSet(rows));
528 auto const resolvedCols = resolveIndices<Size, MaxSize>(matrix.rows(), asIndexSet(cols));
529 using RowIndices = std::remove_cvref_t<
decltype(resolvedRows)>;
530 using ColIndices = std::remove_cvref_t<
decltype(resolvedCols)>;
531 using Result = MatrixOf<
532 typename Derived::Scalar,
533 RowIndices::RowsAtCompileTime,
534 ColIndices::RowsAtCompileTime,
535 RowIndices::MaxRowsAtCompileTime,
536 ColIndices::MaxRowsAtCompileTime>;
537 return Result{lowerSymmetricBlock(matrix, resolvedRows, resolvedCols)};
542template <
typename Scalar,
typename TargetSpace>
543struct ConditionedMoments
568template <
typename Scalar,
typename TargetSpace>
569struct ConditionedMomentsWithEvidence
571 ConditionedMoments<Scalar, TargetSpace> moments;
572 Scalar logLikelihood;
573 Scalar squaredDistance;
577template <
typename Scalar,
typename GivenSpace>
578using GivenMatrixOf = MatrixOf<
586template <
typename Scalar,
typename GivenSpace>
587using GivenVectorOf = Eigen::Matrix<
600 typename Factorization,
601 typename MeanDerived,
603 typename ResidualDerived,
606 typename TargetSpace>
607[[nodiscard]] ConditionedMoments<typename MeanDerived::Scalar, TargetSpace>
609 Factorization
const & factorization,
610 Eigen::MatrixBase<MeanDerived>
const & mean,
611 Eigen::MatrixBase<CovDerived>
const & cov,
612 Target
const & target,
614 Eigen::MatrixBase<ResidualDerived>
const & residual,
615 TargetSpace
const & targetSpace
618 using Scalar =
typename MeanDerived::Scalar;
619 constexpr int GivenSize = ResidualDerived::RowsAtCompileTime;
620 constexpr int GivenMaxSize = ResidualDerived::MaxRowsAtCompileTime;
623 using CrossMatrix = MatrixOf<Scalar, TargetSize, GivenSize, TargetMaxSize, GivenMaxSize>;
625 auto result = ConditionedMoments<Scalar, TargetSpace>{};
627 if constexpr (TargetSize != 0)
629 CrossMatrix
const cross = lowerSymmetricBlock(cov, target.differences,
given.differences);
630 CrossMatrix
const gain = transposedSolve<CrossMatrix>(factorization, cross.transpose());
631 result.cov = lowerSymmetricBlock(cov, target.differences, target.differences);
632 result.cov.noalias() -= multiply(gain, cross.transpose());
635 result.mean =
Emrys::plus(targetSpace, mean.derived()(target.values), multiply(gain, residual));
639 using TargetVector =
decltype(result.mean);
640 using TargetDifference = Eigen::Matrix<Scalar, TargetSize, 1, Eigen::ColMajor, TargetMaxSize, 1>;
641 TargetVector
const start = mean.derived()(target.values);
642 TargetDifference
const step = multiply(gain, residual);
643 result.mean =
Emrys::plus(targetSpace, start, step);
644 resetCovarianceAt(targetSpace, start, step, result.mean, result.cov);
673 typename MeanDerived,
675 typename ValuesDerived,
679 typename TargetSpace>
680[[nodiscard]] ConditionedMoments<typename MeanDerived::Scalar, TargetSpace>
682 Eigen::MatrixBase<MeanDerived>
const & mean,
683 Eigen::MatrixBase<CovDerived>
const & cov,
684 Target
const & target,
686 Eigen::MatrixBase<ValuesDerived>
const & values,
687 GivenSpace
const & givenSpace,
688 TargetSpace
const & targetSpace
691 using Scalar =
typename MeanDerived::Scalar;
692 EMRYS_FILTER_ASSERT(values.size() ==
given.values.size());
695 Eigen::LDLT<GivenMatrixOf<Scalar, GivenSpace>>{lowerSymmetricBlock(cov,
given.differences,
given.differences)};
696 return conditionMomentsBy(ldlt, mean, cov, target,
given, residual, targetSpace);
708 typename MeanDerived,
710 typename ValuesDerived,
714 typename TargetSpace>
715[[nodiscard]] std::optional<ConditionedMomentsWithEvidence<typename MeanDerived::Scalar, TargetSpace>>
716conditionMomentsWithEvidence(
717 Eigen::MatrixBase<MeanDerived>
const & mean,
718 Eigen::MatrixBase<CovDerived>
const & cov,
719 Target
const & target,
721 Eigen::MatrixBase<ValuesDerived>
const & values,
722 GivenSpace
const & givenSpace,
723 TargetSpace
const & targetSpace
726 using Scalar =
typename MeanDerived::Scalar;
727 using GivenVector = GivenVectorOf<Scalar, GivenSpace>;
728 EMRYS_FILTER_ASSERT(values.size() ==
given.values.size());
730 Eigen::LLT<GivenMatrixOf<Scalar, GivenSpace>>{lowerSymmetricBlock(cov,
given.differences,
given.differences)};
731 if (llt.info() != Eigen::Success)
737 llt.matrixL().solveInPlace(whitened);
740 Scalar
const logDeterminant = Scalar(2) * llt.matrixLLT().diagonal().array().log().sum();
741 Scalar
const squaredDistance = whitened.squaredNorm();
742 return ConditionedMomentsWithEvidence<Scalar, TargetSpace>{
743 conditionMomentsBy(llt, mean, cov, target,
given, residual, targetSpace),
744 Scalar(-0.5) * (Scalar(
given.differences.size()) *
log2Pi + logDeterminant + squaredDistance),
The macros every header includes first.
Mathematical constants and scalar functions that work in every scalar type the library accepts,...
Products of fixed-size matrices by Eigen's fixed-size product at every size: multiply() is by Eigen'...
The covariance reset (decisions.md [covariance-reset]): hasIdentityReset<>, resetJacobian(),...
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...
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...
Scalar log2Pi()
Definition Math.h:177
constexpr AxisIndex< Path, sizeof...(I)> at(I... indices)
Definition Discrete.h:1071
constexpr Shared shared()
Definition Batched.h:63
GivenBlock< Space, P, Value > given(Space const &space, Value const &value)
Definition Measurement.h:917
constexpr Residual< Inner > residual(Inner inner={})
Definition Resampling.h:544
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
constexpr auto diffIdx()
idx<S, Path>() of the differences of the subspace named by Path: its rows of a covariance.
Definition TagPath.h:669
auto block(Space auto const &space, X &&x)
Definition TagPath.h:979
auto minus(S const &space, Eigen::MatrixBase< ADerived > const &a, Eigen::MatrixBase< BDerived > const &b)
Definition Space.h:715
constexpr bool hasIdentityReset
Whether the reset Jacobian of S is the identity, so that a covariance needs no change when its mean m...
Definition Reset.h:100
Eigen::Index differenceDimension(S const &space)
Definition Space.h:284
typename internal::DifferenceSpaceOf< S >::type DifferenceSpace
The space that differences of values in S live in: the type of its difference(), or S itself if it ha...
Definition Space.h:227
constexpr int valueDimensionAtCompileTime
The value dimension of Space at compile time, or Eigen::Dynamic if it is resizable.
Definition Space.h:251
constexpr int maxValueDimensionAtCompileTime
The largest valid value dimension of Space, or Eigen::Dynamic if it is unbounded.
Definition Space.h:258
constexpr auto idx()
Definition TagPath.h:658