EmrysFilter 0.1.0
State estimation utilities in C++23 using Eigen
Loading...
Searching...
No Matches
Reset.h
Go to the documentation of this file.
1#pragma once
2
12
13#include <cstddef>
14#include <tuple>
15#include <type_traits>
16#include <utility>
17
18#include <Eigen/LU>
19
26
27namespace Emrys
28{
29
30namespace internal
31{
32
36template <typename S>
37concept HasResetJacobian = requires(S const & space, Eigen::MatrixXd & block) {
38 space.resetJacobian(block.col(0), block.col(0), block.middleRows(0, 0));
39};
40
44template <typename S>
45concept DeclaresIdentityReset = requires { requires bool(S::identityReset); };
46
47template <typename S>
48consteval bool
49identityResetOf();
50
52template <typename Blocks>
53struct AllIdentityReset;
54
56template <typename... Blocks>
57struct AllIdentityReset<std::tuple<Blocks...>> : std::bool_constant<(identityResetOf<Blocks>() && ...)>
58{};
59
64template <typename S>
65consteval bool
66identityResetOf()
67{
68 if constexpr (DeclaresIdentityReset<S>)
69 {
70 return true;
71 }
72 else if constexpr (HasResetJacobian<S>)
73 {
74 return false;
75 }
76 else if constexpr (IsComposite<S>)
77 {
78 return AllIdentityReset<CompositeBlocks<S>>::value;
79 }
80 else if constexpr (IsRepeated<S>)
81 {
82 return identityResetOf<RepeatedBlock<S>>();
83 }
84 else
85 {
86 return !HasPlus<S>;
87 }
88}
89
90} // namespace internal
91
99template <typename S>
100inline constexpr bool hasIdentityReset = internal::identityResetOf<S>();
101
102namespace internal
103{
104
111template <typename Block, typename X, typename D, typename Moved, typename Out>
112void
113blockResetJacobian(
114 Block const & block,
115 Eigen::MatrixBase<X> const & x,
116 Eigen::MatrixBase<D> const & d,
117 [[maybe_unused]] Eigen::MatrixBase<Moved> const & moved,
118 Eigen::MatrixBase<Out> const & out
119)
120{
121 if constexpr (HasResetJacobian<Block>)
122 {
123 block.resetJacobian(x, d, out);
124 }
125 else
126 {
127 using Scalar = typename Out::Scalar;
130 constexpr int V = valueDimensionAtCompileTime<Block>;
131 constexpr int VMax = maxValueDimensionAtCompileTime<Block>;
132 using Dual = Eigen::AutoDiffScalar<Eigen::Matrix<Scalar, K, 1, Eigen::ColMajor, KMax, 1>>;
133 using DualValue = Eigen::Matrix<Dual, V, 1, Eigen::ColMajor, VMax, 1>;
134 using DualDifference = Eigen::Matrix<Dual, K, 1, Eigen::ColMajor, KMax, 1>;
135 Eigen::Index const k = differenceDimension(block);
136 auto step = DualDifference{};
137 step.resize(k);
138 for (Eigen::Index i = 0; i < k; ++i)
139 {
140 step(i) = Dual{Scalar(d(i)), int(k), int(i)};
141 }
142 DualValue const start = x.template cast<Dual>();
143 DualValue const end = moved.template cast<Dual>();
144 DualValue perturbed = DualValue::Zero(start.size());
145 plusInto(block, start, step, perturbed);
146 DualDifference error = DualDifference::Zero(k);
147 minusInto(block, perturbed, end, error);
148 auto & o = writable(out);
149 for (Eigen::Index r = 0; r < k; ++r)
150 {
151 // Of a run-time size, the derivatives of a coordinate that does not depend on the step
152 // would be empty; the Jacobian of a space whose minus() undoes its plus() is invertible.
153 EMRYS_FILTER_ASSERT(
154 error(r).derivatives().size() == k && "each coordinate of the difference must depend on the step"
155 );
156 o.row(r) = error(r).derivatives().transpose();
157 }
158 }
159}
160
167template <typename S, typename F>
168void
169forEachResetBlock(S const & space, F && f, Eigen::Index valueOffset = 0, Eigen::Index differenceOffset = 0)
170{
171 if constexpr (hasIdentityReset<S>)
172 {}
173 else if constexpr (IsComposite<S> && !HasResetJacobian<S>)
174 {
175 [&]<std::size_t... I>(std::index_sequence<I...>) {
176 ((forEachResetBlock(space.template block<I>(), f, valueOffset, differenceOffset),
177 valueOffset += valueDimension(space.template block<I>()),
178 differenceOffset += differenceDimension(space.template block<I>())),
179 ...);
180 }(std::make_index_sequence<std::tuple_size_v<CompositeBlocks<S>>>{});
181 }
182 else if constexpr (IsRepeated<S> && !HasResetJacobian<S>)
183 {
184 auto const & block = space.block();
185 Eigen::Index const values = valueDimension(block);
186 Eigen::Index const differences = differenceDimension(block);
187 for (Eigen::Index k = 0; k < space.count(); ++k)
188 {
189 forEachResetBlock(block, f, valueOffset + k * values, differenceOffset + k * differences);
190 }
191 }
192 else
193 {
194 f(space, valueOffset, differenceOffset);
195 }
196}
197
203template <typename Cov, typename G>
204void
205congruence(Eigen::MatrixBase<Cov> const & cov, Eigen::Index offset, G const & g)
206{
207 using Scalar = typename Cov::Scalar;
208 constexpr int K = G::RowsAtCompileTime;
209 constexpr int KMax = G::MaxRowsAtCompileTime;
210 constexpr int N = Cov::ColsAtCompileTime;
211 constexpr int NMax = Cov::MaxColsAtCompileTime;
212 auto & p = writable(cov);
213 Eigen::Index const k = g.rows();
214 auto strip = MatrixOf<Scalar, K, N, KMax, NMax>{};
215 strip.resize(k, p.cols());
216 strip.noalias() = multiply(g, middleRowsOf<K>(p, offset, k));
217 middleRowsOf<K>(p, offset, k) = strip;
218 middleColsOf<K>(p, offset, k) = strip.transpose();
219 auto diagonal = MatrixOf<Scalar, K, K, KMax, KMax>{};
220 diagonal.resize(k, k);
221 diagonal.noalias() = multiply(middleColsOf<K>(strip, offset, k), g.transpose());
222 blockOf<K, K>(p, offset, offset, k, k) = Scalar(0.5) * (diagonal + diagonal.transpose());
223}
224
231template <bool Inverse = false, typename S, typename X, typename D, typename Moved, typename Cov>
232void
233resetCovarianceAt(
234 S const & space,
235 Eigen::MatrixBase<X> const & x,
236 Eigen::MatrixBase<D> const & d,
237 Eigen::MatrixBase<Moved> const & moved,
238 Eigen::MatrixBase<Cov> const & cov
239)
240{
241 if constexpr (!hasIdentityReset<S>)
242 {
243 EMRYS_FILTER_ASSERT(
244 x.size() == valueDimension(space) && moved.size() == valueDimension(space)
245 && d.size() == differenceDimension(space) && cov.rows() == differenceDimension(space)
246 && cov.cols() == differenceDimension(space) && "the value, step, and covariance must fit the space"
247 );
248 forEachResetBlock(space, [&](auto const & block, Eigen::Index values, Eigen::Index differences) {
249 using Block = std::remove_cvref_t<decltype(block)>;
250 using Scalar = typename Cov::Scalar;
252 constexpr int V = valueDimensionAtCompileTime<Block>;
254 Eigen::Index const v = valueDimension(block);
255 Eigen::Index const k = differenceDimension(block);
256 MatrixOf<Scalar, K, K, KMax, KMax> g = MatrixOf<Scalar, K, K, KMax, KMax>::Zero(k, k);
257 blockResetJacobian(
258 block,
259 middleRowsOf<V>(x, values, v),
260 middleRowsOf<K>(d, differences, k),
261 middleRowsOf<V>(moved, values, v),
262 g
263 );
264 if constexpr (Inverse)
265 {
266 using Matrix = MatrixOf<Scalar, K, K, KMax, KMax>;
267 g = inverseFrom<Matrix>(Eigen::PartialPivLU<Matrix>{g});
268 }
269 congruence(cov, differences, g);
270 });
271 }
272}
273
280template <typename S, typename X, typename D, typename Moved, typename F>
281void
282forEachInverseResetJacobian(
283 S const & space,
284 Eigen::MatrixBase<X> const & x,
285 Eigen::MatrixBase<D> const & d,
286 Eigen::MatrixBase<Moved> const & moved,
287 F && f
288)
289{
290 if constexpr (!hasIdentityReset<S>)
291 {
292 EMRYS_FILTER_ASSERT(
293 x.size() == valueDimension(space) && moved.size() == valueDimension(space)
294 && d.size() == differenceDimension(space) && "the value and step must fit the space"
295 );
296 forEachResetBlock(space, [&](auto const & block, Eigen::Index values, Eigen::Index differences) {
297 using Block = std::remove_cvref_t<decltype(block)>;
298 using Scalar = typename X::Scalar;
300 constexpr int V = valueDimensionAtCompileTime<Block>;
302 Eigen::Index const v = valueDimension(block);
303 Eigen::Index const k = differenceDimension(block);
304 MatrixOf<Scalar, K, K, KMax, KMax> g = MatrixOf<Scalar, K, K, KMax, KMax>::Zero(k, k);
305 blockResetJacobian(
306 block,
307 middleRowsOf<V>(x, values, v),
308 middleRowsOf<K>(d, differences, k),
309 middleRowsOf<V>(moved, values, v),
310 g
311 );
312 using Matrix = MatrixOf<Scalar, K, K, KMax, KMax>;
313 g = inverseFrom<Matrix>(Eigen::PartialPivLU<Matrix>{g});
314 f(differences, g);
315 });
316 }
317}
318
329template <typename S, typename X, typename D, typename Moved, typename Info, typename Vector>
330void
331resetInfoAt(
332 S const & space,
333 Eigen::MatrixBase<X> const & x,
334 Eigen::MatrixBase<D> const & d,
335 Eigen::MatrixBase<Moved> const & moved,
336 Eigen::MatrixBase<Info> const & info,
337 Eigen::MatrixBase<Vector> const & vector
338)
339{
340 forEachInverseResetJacobian(space, x, d, moved, [&](Eigen::Index differences, auto const & gInverse) {
341 using GInverse = std::remove_cvref_t<decltype(gInverse)>;
342 GInverse const gInverseTransposed = gInverse.transpose();
343 congruence(info, differences, gInverseTransposed);
344 auto & eta = writable(vector);
345 Eigen::Index const k = gInverse.rows();
346 middleRowsOf<GInverse::RowsAtCompileTime>(eta, differences, k) =
347 gInverseTransposed * middleRowsOf<GInverse::RowsAtCompileTime>(eta, differences, k);
348 });
349}
350
351} // namespace internal
352
375template <Space S, typename XDerived, typename DDerived>
376[[nodiscard]] auto
377resetJacobian(S const & space, Eigen::MatrixBase<XDerived> const & x, Eigen::MatrixBase<DDerived> const & d)
378{
379 using Scalar = typename XDerived::Scalar;
382 Eigen::Index const n = differenceDimension(space);
383 auto result = internal::MatrixOf<Scalar, N, N, NMax, NMax>{};
384 result.setIdentity(n, n);
385 if constexpr (!hasIdentityReset<S>)
386 {
387 auto const moved = Emrys::plus(space, x, d);
388 internal::forEachResetBlock(space, [&](auto const & block, Eigen::Index values, Eigen::Index differences) {
389 using Block = std::remove_cvref_t<decltype(block)>;
391 constexpr int V = valueDimensionAtCompileTime<Block>;
392 Eigen::Index const v = valueDimension(block);
393 Eigen::Index const k = differenceDimension(block);
394 internal::blockResetJacobian(
395 block,
396 internal::middleRowsOf<V>(x, values, v),
397 internal::middleRowsOf<K>(d, differences, k),
398 internal::middleRowsOf<V>(moved, values, v),
399 internal::blockOf<K, K>(result, differences, differences, k, k)
400 );
401 });
402 }
403 return result;
404}
405
424template <Space S, typename XDerived, typename DDerived, typename CovDerived>
425void
427 S const & space,
428 Eigen::MatrixBase<XDerived> const & x,
429 Eigen::MatrixBase<DDerived> const & d,
430 Eigen::MatrixBase<CovDerived> const & cov
431)
432{
433 if constexpr (!hasIdentityReset<S>)
434 {
435 auto const moved = Emrys::plus(space, x, d);
436 internal::resetCovarianceAt(space, x, d, moved, cov);
437 }
438}
439
440} // namespace Emrys
Spaces built from other spaces (decisions.md [sizes-and-dimensions], [several-resizable-blocks]): Com...
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'...
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...
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
Eigen::MatrixBase< Derived > & writable(Eigen::MatrixBase< Derived > const &block)
block, writable: Eigen's idiom for a function that writes through an expression passed as const&.
Definition Space.h:389
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
Eigen::Index differenceDimension(S const &space)
Definition Space.h:284
void resetCovariance(S const &space, Eigen::MatrixBase< XDerived > const &x, Eigen::MatrixBase< DDerived > const &d, Eigen::MatrixBase< CovDerived > const &cov)
Carries the covariance cov from the tangent space at x to the one at , in place: ,...
Definition Reset.h:426
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