22#include <Eigen/Cholesky>
53template <
typename Input>
58 using Scalar =
typename Input::Scalar;
59 using SpaceType =
typename Input::SpaceType;
62 static constexpr int ValueDimensionAtCompileTime = Input::ValueDimensionAtCompileTime;
65 static constexpr int MaxValueDimensionAtCompileTime = Input::MaxValueDimensionAtCompileTime;
68 static constexpr int DifferenceDimensionAtCompileTime = Input::DifferenceDimensionAtCompileTime;
71 static constexpr int MaxDifferenceDimensionAtCompileTime = Input::MaxDifferenceDimensionAtCompileTime;
75 Eigen::Matrix<Scalar, ValueDimensionAtCompileTime, 1, Eigen::ColMajor, MaxValueDimensionAtCompileTime, 1>;
78 MovedInput(Input
const & input, VectorType mean)
80 , m_mean{std::move(mean)}
84 [[nodiscard]]
decltype(
auto)
87 return m_input.space();
91 [[nodiscard]] VectorType
const &
98 [[nodiscard]]
decltype(
auto)
99 covarianceLower()
const
101 return m_input.covarianceLower();
105 [[nodiscard]] Eigen::Index
106 valueDimension()
const
108 return m_input.valueDimension();
112 [[nodiscard]] Eigen::Index
113 differenceDimension()
const
115 return m_input.differenceDimension();
120 Input
const & m_input;
128template <
typename Input,
typename Cov>
129class RespreadInput :
public MovedInput<Input>
134 RespreadInput(Input
const & input,
typename MovedInput<Input>::VectorType mean, Cov
const & cov)
135 : MovedInput<Input>{input, std::move(mean)}
140 [[nodiscard]] Cov
const &
141 covarianceLower()
const
157template <
typename M,
typename Component>
158class WithNoiseComponent
162 using MethodType =
typename M::MethodType;
163 using ModelType =
typename M::ModelType;
164 using ArgumentsType =
typename M::ArgumentsType;
165 using FormType =
typename M::FormType;
166 using InnovationRepair =
typename M::InnovationRepair;
167 using ConsiderType =
typename M::ConsiderType;
168 using NoiseType = AdditiveNoise<Component const &>;
170 static constexpr bool HasMethod = M::HasMethod;
171 static constexpr bool HasObservation = M::HasObservation;
172 static constexpr bool OwnNoise =
true;
175 WithNoiseComponent(M
const & measurement, Component
const & component)
176 : m_measurement{measurement}
181 [[nodiscard]] MethodType
const &
184 return m_measurement.method();
188 [[nodiscard]] ModelType
const &
191 return m_measurement.model();
195 [[nodiscard]] NoiseType
const &
202 [[nodiscard]]
decltype(
auto)
205 return m_measurement.observation();
209 [[nodiscard]] ArgumentsType
const &
212 return m_measurement.arguments();
216 [[nodiscard]]
decltype(
auto)
217 covarianceUpdate()
const
219 return m_measurement.covarianceUpdate();
224 M
const & m_measurement;
229template <
typename M,
typename Component>
230inline constexpr bool isObserved<WithNoiseComponent<M, Component>>{
true};
238template <
typename Cov>
240iterationScale(Eigen::MatrixBase<Cov>
const & cov,
typename Cov::Scalar tolerance)
242 using Scalar =
typename Cov::Scalar;
243 EMRYS_FILTER_ASSERT(tolerance >= Scalar(0) &&
"an iteration's tolerance must not be negative");
244 using Result = Eigen::Matrix<Scalar, Cov::RowsAtCompileTime, 1, Eigen::ColMajor, Cov::MaxRowsAtCompileTime, 1>;
245 Result result = cov.diagonal().cwiseSqrt() * tolerance;
252template <
typename Next,
typename Delta,
typename Scale>
255 Eigen::MatrixBase<Next>
const & next,
256 Eigen::MatrixBase<Delta>
const & delta,
257 Eigen::MatrixBase<Scale>
const & scale
260 return ((next - delta).cwiseAbs().array() <= scale.array()).all();
269template <
typename Reads,
typename Jacobian,
typename ReadMean,
typename ReadStep>
273 Eigen::MatrixBase<Jacobian>
const & jacobian,
274 ReadMean
const & readMean,
275 ReadStep
const & readStep
280 using Scalar =
typename Jacobian::Scalar;
281 using Result = MatrixOf<
283 Jacobian::RowsAtCompileTime,
284 Reads::DifferenceSize,
285 Jacobian::MaxRowsAtCompileTime,
286 Reads::DifferenceMaxSize>;
287 Result result(jacobian.rows(), jacobian.cols());
288 result.noalias() = multiply(jacobian,
resetJacobian(reads.space(), readMean, readStep));
289 jacobian.const_cast_derived() = result;
322[[nodiscard]]
decltype(
auto)
325 Method
const & method,
329 StateRead
const & stateRead,
330 ReadMean
const & readMean,
332 ReadStep
const & readStep,
333 SpreadCov
const & spreadCov,
337 using ReadVector =
typename MovedInput<Read>::VectorType;
338 if constexpr (!isLinearMethod<Method>)
340 using ReadMatrix = SpreadCov;
341 auto const input = RespreadInput<Read, ReadMatrix>{
343 moved ? ReadVector{
Emrys::plus(reads.space(), readMean, readStep)} : readMean,
349 auto const predicted = transformMeasurement(resolved, method, f, input);
350 using InnovationGaussian = std::remove_cvref_t<
decltype(predicted.output())>;
351 using InnovationMatrix =
typename InnovationGaussian::MatrixType;
352 using Predicted = OutputWithCross<Space, typename InnovationGaussian::SpaceType, JacobianForm, Scalar>;
353 using ReadJacobianMatrix = MatrixOf<
355 InnovationGaussian::DifferenceDimensionAtCompileTime,
356 Reads::DifferenceSize,
357 InnovationGaussian::MaxDifferenceDimensionAtCompileTime,
358 Reads::DifferenceMaxSize>;
359 ReadJacobianMatrix jacobian = predicted.jacobian();
360 InnovationMatrix cov = predicted.inputsCovariance() + predicted.unexplainedCovariance();
361 InnovationMatrix
const error = cov;
362 typename InnovationGaussian::VectorType mean = predicted.output().mean();
365 toPriorChart(reads, jacobian, readMean, readStep);
366 auto back =
typename InnovationGaussian::DifferenceVectorType{};
367 back.noalias() = -multiply(jacobian, readStep);
368 mean =
Emrys::plus(predicted.output().space(), predicted.output().mean(), back);
370 auto cross =
typename Predicted::CrossMatrix{};
371 cross.noalias() = multiply(stateRead, jacobian.transpose());
372 cov.noalias() += multiply(jacobian, std::as_const(cross)(reads.differenceIndices(), Eigen::placeholders::all));
373 auto const y = InnovationGaussian{predicted.output().space(), std::move(mean), std::move(cov)};
374 return use(y, cross, ReadLinearization{jacobian, reads.differenceIndices(), [&] {
381 MovedInput<Read>{read, moved ? ReadVector{
Emrys::plus(reads.space(), readMean, readStep)} : readMean};
382 return linearizeMeasurement(
387 [&](
auto const & problem,
auto const & linearized) ->
decltype(
auto) {
388 using Problem = std::remove_cvref_t<
decltype(problem)>;
389 using Predicted = OutputWithCross<Space, typename Problem::OutputSpaceType, JacobianForm, Scalar>;
390 Eigen::Index
const readSize = reads.differenceIndices().size();
391 Eigen::Index
const offset = problem.differenceOffset(0);
392 typename Problem::JacobianMatrix jacobian =
linearized.jacobian;
393 typename Problem::template OutputVector<Scalar> mean =
linearized.mean;
394 auto readJacobian = jacobian.template middleCols<Reads::DifferenceSize>(offset, readSize);
397 toPriorChart(reads, readJacobian, readMean, readStep);
398 auto back =
typename Problem::template OutputDifferenceVector<Scalar>{};
399 back.noalias() = -multiply(readJacobian, readStep);
402 auto cross =
typename Predicted::CrossMatrix{};
403 cross.noalias() = multiply(stateRead, readJacobian.transpose());
404 auto const linearization =
405 ReadLinearization{readJacobian, reads.differenceIndices(), [&] {
406 using Error =
typename Problem::OutputMatrix;
407 return inputsCovariance<Error>(resolved, problem, jacobian);
409 if constexpr (R::InputCount > 0)
411 typename Problem::CrossMatrix
const joint =
412 multiply(problem.covarianceLower(), jacobian.transpose());
413 auto const y = PredictionMoments{problem.outputSpace(), mean, jacobian, joint};
414 return use(y, cross, linearization);
418 auto const joint = std::as_const(cross)(reads.differenceIndices(), Eigen::placeholders::all);
419 auto const y = PredictionMoments{problem.outputSpace(), mean, jacobian, joint};
420 return use(y, cross, linearization);
445[[nodiscard]]
decltype(
auto)
452 StateRead
const & stateRead,
456 using Scalar =
typename Prior::Scalar;
457 using Space =
typename Prior::SpaceType;
459 using ReadVector =
typename MovedInput<Read>::VectorType;
461 auto const & method = resolved.method();
462 auto const scale = iterationScale(covarianceLowerOf(prior), Scalar(method.tolerance));
463 ReadVector
const readMean = read.mean();
464 Difference delta = Difference::Zero(prior.differenceDimension());
465 ReadDifference readStep = ReadDifference::Zero(reads.differenceIndices().size());
466 std::size_t iteration = 0;
467 bool converged =
false;
468 auto const considered = consideredRows<R>(prior.space());
469 constexpr bool Considers = R::ConsiderType::Count > 0;
474 using Inner = std::remove_cvref_t<
decltype(method.method)>;
475 constexpr bool Spreads = !isLinearMethod<Inner>;
476 using ReadMatrix = MatrixOf<
478 Reads::DifferenceSize,
479 Reads::DifferenceSize,
480 Reads::DifferenceMaxSize,
481 Reads::DifferenceMaxSize>;
482 using SpreadMatrix = std::conditional_t<Spreads, ReadMatrix, std::monostate>;
483 auto priorReadCov = SpreadMatrix{};
484 auto spreadCov = SpreadMatrix{};
485 if constexpr (Spreads)
487 priorReadCov = read.covarianceLower();
488 spreadCov = priorReadCov;
494 auto const atIterate = [&](
auto const & use) ->
decltype(
auto) {
495 return linearizeReadsAt<Space, Scalar>(
517 [&](
auto const & y,
auto const & cross,
auto const &) -> std::expected<Difference, GaussianError> {
518 using MeasurementSpace = std::remove_cvref_t<
decltype(y.space())>;
522 if constexpr (!R::HasAdditiveNoise)
525 observedResidual<Scalar>(resolved, y.space(), y.mean()),
526 InnovationMatrix{y.covarianceLower()}
531 auto const & noise = resolved.noise().distribution();
534 observedResidual<Scalar>(resolved, y.space(),
Emrys::plus(y.space(), y.mean(), noise.mean()))
536 innovationCovariancePlus<InnovationMatrix>(y, noise.covarianceLower())
540 auto const factored = innovationCholesky<typename R::InnovationRepair>(cov);
543 return std::unexpected{factored.error()};
545 auto const & llt = *factored;
546 auto next = Difference{};
547 next.resize(delta.size());
548 next.noalias() = multiply(cross, llt.solve(residual));
549 if constexpr (Considers)
551 zeroConsideredRows(next, considered);
553 if constexpr (Spreads)
555 auto const readCross = std::as_const(cross)(reads.differenceIndices(), Eigen::placeholders::all);
556 spreadCov = priorReadCov;
557 using ReadCross = internal::MatrixOf<
559 Reads::DifferenceSize,
560 InnovationMatrix::RowsAtCompileTime,
561 Reads::DifferenceMaxSize,
562 InnovationMatrix::MaxRowsAtCompileTime>;
563 spreadCov.noalias() -=
564 multiply(internal::transposedSolve<ReadCross>(llt, readCross.transpose()), readCross.transpose());
565 spreadCov = (Scalar(0.5) * (spreadCov + spreadCov.transpose())).eval();
566 if constexpr (Considers)
569 auto const & rows = reads.differenceIndices();
570 auto const isConsidered = [&](Eigen::Index row) {
571 for (BlockRows
const block : considered)
580 for (Eigen::Index i = 0; i < rows.size(); ++i)
582 for (Eigen::Index j = 0; j <= i; ++j)
584 if (isConsidered(rows(i)) && isConsidered(rows(j)))
586 spreadCov(i, j) = priorReadCov(i, j);
587 spreadCov(j, i) = priorReadCov(i, j);
594 ReadDifference
const nextReadStep = next(reads.differenceIndices());
595 auto const reset =
resetJacobian(reads.space(), readMean, nextReadStep);
596 spreadCov = multiply(multiply(reset, spreadCov), reset.transpose()).eval();
605 auto failure = std::optional<GaussianError>{};
606 auto const finish = [&](
auto const & y,
auto const & cross,
auto const & readJacobian) {
607 using Result =
decltype(conditionOnPrediction<Statistics>(prior, resolved, y, cross, readJacobian, sink));
610 return Result{std::unexpected{*failure}};
612 auto result = conditionOnPrediction<Statistics>(prior, resolved, y, cross, readJacobian, sink);
615 for (
auto & branch : result->branches)
617 branch.iterations = iteration + 1;
618 branch.converged = converged;
625 for (; iteration + 1 < method.maxIterations && !converged; ++iteration)
627 auto const next = atIterate(step);
630 failure = next.error();
633 converged = withinScale(*next, delta, scale);
635 readStep = delta(reads.differenceIndices());
637 return atIterate(finish);
The allocator conventions of decisions.md [allocation]: DefaultAllocator, Capacity,...
Spaces built from other spaces (decisions.md [sizes-and-dimensions], [several-resizable-blocks]): Com...
The coordinate types (decisions.md [no-bounded-coordinates]): Linear, plain coordinates,...
Execution policies (decisions.md [execution-policies]): execution::sequential, the ExecutionPolicy co...
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...
The macros every header includes first.
Measurement models and their observations (decisions.md [model-policies], [sensors],...
How an update reads a measurement, all internal (decisions.md [model-policies], [measurement-space-fu...
The mixture: the parameters MixtureParams of weighted components of one type (decisions....
Products of fixed-size matrices by Eigen's fixed-size product at every size: multiply() is by Eigen'...
Product: the product of independent densities, each on its own block of a space, built with on<Path>(...
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...
Log-weights: the WeightError a normalization can fail with (NumericError.h), and the streaming log-su...
typename MomentsType::MatrixType MatrixType
a matrix over differences
Definition Gaussian.h:267
typename MomentsType::DifferenceVectorType DifferenceVectorType
a difference, such as a residual
Definition Gaussian.h:266
constexpr Residual< Inner > residual(Inner inner={})
Definition Resampling.h:544
auto plus(S const &space, Eigen::MatrixBase< XDerived > const &x, Eigen::MatrixBase< DDerived > const &d)
Definition Space.h:675
auto block(Space auto const &space, X &&x)
Definition TagPath.h:979
auto resetJacobian(S const &space, Eigen::MatrixBase< XDerived > const &x, Eigen::MatrixBase< DDerived > const &d)
Definition Reset.h:377
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