gtsam
Loading...
Searching...
No Matches
SemidirectLieGroup-inl.h
Go to the documentation of this file.
1/* ----------------------------------------------------------------------------
2
3 * GTSAM Copyright 2010, Georgia Tech Research Corporation,
4 * Atlanta, Georgia 30332-0415
5 * All Rights Reserved
6 * Authors: Frank Dellaert, et al. (see THANKS for the full author list)
7
8 * See LICENSE for the license information
9
10 * -------------------------------------------------------------------------- */
11
20
21#pragma once
22
23#include <unsupported/Eigen/MatrixFunctions>
24
25namespace {
26
27template <int D, int Blocks, int Extra = 0>
28inline constexpr int semidirectAugmentedDimension =
29 D == Eigen::Dynamic ? Eigen::Dynamic : Blocks * D + Extra;
30
31template <int D, int Blocks, int Extra = 0>
32using SemidirectAugmentedMatrix =
33 Eigen::Matrix<double, semidirectAugmentedDimension<D, Blocks, Extra>,
34 semidirectAugmentedDimension<D, Blocks, Extra>>;
35
36template <typename Dst, typename Src>
37void semidirectAssignBlock(const Src& source, size_t row, size_t column,
38 Dst* destination) {
39 constexpr int rows = Src::RowsAtCompileTime;
40 constexpr int columns = Src::ColsAtCompileTime;
41 if constexpr (rows != Eigen::Dynamic && columns != Eigen::Dynamic) {
42 destination->template block<rows, columns>(row, column) = source;
43 } else {
44 destination->block(row, column, source.rows(), source.cols()) = source;
45 }
46}
47
48} // namespace
49
50namespace gtsam {
51
52template <typename G, typename H, typename Action>
53typename SemidirectLieGroup<G, H, Action>::Phi1KernelResult
54SemidirectLieGroup<G, H, Action>::phi1Kernel(const Jacobian2& A) {
55 const int r = A.rows();
56 using Augmented = SemidirectAugmentedMatrix<m, 2>;
57 Augmented M = Augmented::Zero(2 * r, 2 * r);
58 M.topLeftCorner(r, r) = A;
59 M.topRightCorner(r, r).setIdentity();
60 const Augmented expM = M.exp();
61 return {expM.topLeftCorner(r, r), expM.topRightCorner(r, r)};
62}
63
64template <typename G, typename H, typename Action>
66SemidirectLieGroup<G, H, Action>::phi1FrechetAction(
67 const Jacobian2& A, const Jacobian2& B,
68 const typename traits<H>::TangentVector& v) {
69 const int r = A.rows();
70 using Augmented = SemidirectAugmentedMatrix<m, 2, 1>;
71 Augmented M = Augmented::Zero(2 * r + 1, 2 * r + 1);
72 M.block(0, 0, r, r) = A;
73 M.block(0, r, r, r) = B;
74 M.block(r, r, r, r) = A;
75 M.block(r, 2 * r, r, 1) = v;
76 return M.exp().block(0, 2 * r, r, 1);
77}
78
79template <typename G, typename H, typename Action>
80typename SemidirectLieGroup<G, H, Action>::Jacobian
81SemidirectLieGroup<G, H, Action>::rightJacobian(const TangentVector& xi) {
82 const int d = xi.size();
83 using Augmented = SemidirectAugmentedMatrix<dimension, 2>;
84 Augmented M = Augmented::Zero(2 * d, 2 * d);
85 M.topLeftCorner(d, d) = -adjointMap(xi);
86 M.topRightCorner(d, d).setIdentity();
87 return M.exp().topRightCorner(d, d);
88}
89
90template <typename G, typename H, typename Action>
91SemidirectLieGroup<G, H, Action> SemidirectLieGroup<G, H, Action>::operator*(
92 const SemidirectLieGroup& other) const {
93 checkMatchingDimensions(other, "operator*");
94 const H acted = Action{}(this->first, other.second);
95 return {traits<G>::Compose(this->first, other.first),
96 traits<H>::Compose(this->second, acted)};
97}
98
99template <typename G, typename H, typename Action>
100SemidirectLieGroup<G, H, Action> SemidirectLieGroup<G, H, Action>::inverse()
101 const {
102 const G gInverse = traits<G>::Inverse(this->first);
103 return {gInverse, Action{}(gInverse, traits<H>::Inverse(this->second))};
104}
105
106template <typename G, typename H, typename Action>
107SemidirectLieGroup<G, H, Action> SemidirectLieGroup<G, H, Action>::retract(
108 const TangentVector& xi, ChartJacobian H1, ChartJacobian H2) const {
109 const size_t d1 = firstDim(), d2 = secondDim(), d = d1 + d2;
110 if (static_cast<size_t>(xi.size()) != d) {
111 throw std::invalid_argument(
112 "SemidirectLieGroup::retract tangent dimension mismatch");
113 }
114 Matrix D1, D2;
116 const SemidirectLieGroup delta =
117 Expmap(tangentSegment<G>(xi, 0, d1), tangentSegment<H>(xi, d1, d2),
118 H2 ? DynamicJacobian(D1) : DynamicJacobian(),
119 H2 ? DynamicJacobian(D2) : DynamicJacobian());
120 const SemidirectLieGroup result = compose(delta);
121 if (H1) *H1 = delta.inverse().AdjointMap();
122 if (H2) {
123 *H2 = zeroJacobian(d);
124 H2->leftCols(d1) = D1;
125 H2->rightCols(d2) = D2;
126 }
127 return result;
128}
129
130template <typename G, typename H, typename Action>
131typename SemidirectLieGroup<G, H, Action>::TangentVector
132SemidirectLieGroup<G, H, Action>::localCoordinates(
133 const SemidirectLieGroup& other, ChartJacobian H1, ChartJacobian H2) const {
134 checkMatchingDimensions(other, "localCoordinates");
135 const SemidirectLieGroup relative = between(other);
136 Jacobian Dlog;
137 const TangentVector xi =
138 Logmap(relative, H1 || H2 ? ChartJacobian(&Dlog) : ChartJacobian());
139 if (H1) *H1 = -Dlog * relative.inverse().AdjointMap();
140 if (H2) *H2 = Dlog;
141 return xi;
142}
143
144template <typename G, typename H, typename Action>
145SemidirectLieGroup<G, H, Action> SemidirectLieGroup<G, H, Action>::Expmap(
146 const TangentVector& xi, ChartJacobian D) {
147 size_t d1;
148 if constexpr (firstDynamic) {
149 if (xi.size() < m) {
150 throw std::invalid_argument(
151 "SemidirectLieGroup::Expmap tangent dimension is too small");
152 }
153 d1 = static_cast<size_t>(xi.size() - m);
154 } else {
155 d1 = n;
156 }
157 constexpr size_t d2 = m;
158 if (static_cast<size_t>(xi.size()) != d1 + d2) {
159 throw std::invalid_argument(
160 "SemidirectLieGroup::Expmap tangent dimension mismatch");
161 }
162 Matrix D1, D2;
164 const SemidirectLieGroup result =
165 Expmap(tangentSegment<G>(xi, 0, d1), tangentSegment<H>(xi, d1, d2),
166 D ? DynamicJacobian(D1) : DynamicJacobian(),
167 D ? DynamicJacobian(D2) : DynamicJacobian());
168 if (D) {
169 *D = zeroJacobian(d1 + d2);
170 D->leftCols(d1) = D1;
171 D->rightCols(d2) = D2;
172 }
173 return result;
174}
175
176template <typename G, typename H, typename Action>
177SemidirectLieGroup<G, H, Action> SemidirectLieGroup<G, H, Action>::Expmap(
178 const Eigen::Ref<const typename traits<G>::TangentVector>& u,
179 const Eigen::Ref<const typename traits<H>::TangentVector>& v,
182 const size_t d1 = u.size(), d2 = v.size(), d = d1 + d2;
183 constexpr bool hasAdjoint =
185 Jacobian1 Dg;
186 const G g = traits<G>::Expmap(u, H1 && !hasAdjoint ? &Dg : nullptr);
187
188 if (H1) {
189 if constexpr (hasAdjoint) {
190 const TangentVector xi = makeTangentVector(u, v, d1, d2);
191 const Jacobian derivative = rightJacobian(xi);
192 const Jacobian2 actionJacobian = derivative.bottomRightCorner(d2, d2);
193 const H h = Action{}(g, actionJacobian * v);
194 *H1 = derivative.leftCols(d1);
195 if (H2) *H2 = derivative.rightCols(d2);
196 return {g, h};
197 } else {
198 const Jacobian2 A = Action::generator(u);
199 const Phi1KernelResult kernels = phi1Kernel(A);
200 const auto phi0Solver = kernels.phi0.lu();
201 const H h = kernels.phi1 * v;
202 *H1 = Matrix::Zero(d, d1);
203 if constexpr (firstDynamic) {
204 H1->topRows(d1) = Dg;
205 } else {
206 H1->template topLeftCorner<n, n>() = Dg;
207 }
208 typename traits<G>::TangentVector ej;
209 if constexpr (firstDynamic) ej.resize(d1);
210 ej.setZero();
211 for (Eigen::Index j = 0; j < static_cast<Eigen::Index>(d1); ++j) {
212 ej(j) = 1.0;
213 H1->col(j).tail(d2) =
214 phi0Solver.solve(phi1FrechetAction(A, Action::generator(ej), v));
215 ej(j) = 0.0;
216 }
217 if (H2) {
218 *H2 = Matrix::Zero(d, d2);
219 H2->bottomRows(d2) = phi0Solver.solve(kernels.phi1);
220 }
221 return {g, h};
222 }
223 }
224
225 const Jacobian2 A = Action::generator(u);
226 if (H2) {
227 const Jacobian2 actionJacobian = phi1Kernel(-A).phi1;
228 const H h = Action{}(g, actionJacobian * v);
229 *H2 = Matrix::Zero(d, d2);
230 H2->bottomRows(d2) = actionJacobian;
231 return {g, h};
232 }
233 return {g, phi1Kernel(A).phi1 * v};
234}
235
236template <typename G, typename H, typename Action>
237typename SemidirectLieGroup<G, H, Action>::TangentVector
238SemidirectLieGroup<G, H, Action>::Logmap(const SemidirectLieGroup& p,
239 ChartJacobian D) {
240 const size_t d1 = p.firstDim(), d2 = p.secondDim(), d = d1 + d2;
241 constexpr bool hasAdjoint =
243 Jacobian1 Dg;
244 const auto u = traits<G>::Logmap(p.first, D && !hasAdjoint ? &Dg : nullptr);
245 const Jacobian2 A = Action::generator(u);
246
247 if (D) {
248 if constexpr (hasAdjoint) {
249 const auto solver = phi1Kernel(A).phi1.lu();
250 const typename traits<H>::TangentVector v = solver.solve(p.second);
251 const TangentVector xi = makeTangentVector(u, v, d1, d2);
252 *D = rightJacobian(xi).partialPivLu().solve(identityJacobian(d));
253 return xi;
254 } else {
255 const Phi1KernelResult kernels = phi1Kernel(A);
256 const auto solver = kernels.phi1.lu();
257 const typename traits<H>::TangentVector v = solver.solve(p.second);
258 *D = zeroJacobian(d);
259 if constexpr (firstDynamic) {
260 D->topLeftCorner(d1, d1) = Dg;
261 } else {
262 D->template topLeftCorner<n, n>() = Dg;
263 }
264 D->bottomRightCorner(d2, d2) = solver.solve(kernels.phi0);
265 for (Eigen::Index j = 0; j < static_cast<Eigen::Index>(d1); ++j) {
266 const Jacobian2 B = Action::generator(Dg.col(j));
267 D->col(j).tail(d2) = -solver.solve(phi1FrechetAction(A, B, v));
268 }
269 return makeTangentVector(u, v, d1, d2);
270 }
271 }
272
273 const Jacobian2 phi1 = phi1Kernel(A).phi1;
274 const typename traits<H>::TangentVector v = phi1.lu().solve(p.second);
275 return makeTangentVector(u, v, d1, d2);
276}
277
278template <typename G, typename H, typename Action>
279typename SemidirectLieGroup<G, H, Action>::Jacobian
280SemidirectLieGroup<G, H, Action>::AdjointMap() const {
281 const size_t d1 = firstDim(), d2 = secondDim(), d = d1 + d2;
282 ActionJacobianG Jg;
283 Jacobian2 Jh;
284 Action{}(defaultIdentity<G>(), this->second, &Jg, {});
285 Action{}(this->first, traits<H>::Identity(), {}, &Jh);
286 const Jacobian1 AdG = traits<G>::AdjointMap(this->first);
287 Jacobian result = zeroJacobian(d);
288 semidirectAssignBlock(AdG, 0, 0, &result);
289 semidirectAssignBlock(Jh, d1, d1, &result);
290 result.block(d1, 0, d2, d1) = -Jg * AdG;
291 return result;
292}
293
294template <typename G, typename H, typename Action>
295typename SemidirectLieGroup<G, H, Action>::Jacobian
296SemidirectLieGroup<G, H, Action>::adjointMap(const TangentVector& xi) {
297 const size_t d = xi.size();
298 const size_t d2 = m;
299 if (d < d2) {
300 throw std::invalid_argument(
301 "SemidirectLieGroup::adjointMap tangent dimension is too small");
302 }
303 const size_t d1 = d - d2;
304 const auto u = tangentSegment<G>(xi, 0, d1);
305 const auto v = tangentSegment<H>(xi, d1, d2);
306 Jacobian result = zeroJacobian(d);
307 semidirectAssignBlock(G::adjointMap(u), 0, 0, &result);
308
309 typename traits<G>::TangentVector ei;
310 if constexpr (firstDynamic) ei.resize(d1);
311 ei.setZero();
312 for (Eigen::Index i = 0; i < static_cast<Eigen::Index>(d1); ++i) {
313 ei(i) = 1.0;
314 result.block(d1, i, d2, 1) = -(Action::generator(ei) * v);
315 ei(i) = 0.0;
316 }
317 semidirectAssignBlock(Action::generator(u), d1, d1, &result);
318 return result;
319}
320
321template <typename G, typename H, typename Action>
322template <typename T>
323T SemidirectLieGroup<G, H, Action>::defaultIdentity() {
324 if constexpr (traits<T>::dimension == Eigen::Dynamic) {
325 return T();
326 } else {
327 return traits<T>::Identity();
328 }
329}
330
331template <typename G, typename H, typename Action>
332template <typename T, int D>
334SemidirectLieGroup<G, H, Action>::tangentSegment(const TangentVector& xi,
335 size_t start, size_t d) {
336 if constexpr (D == Eigen::Dynamic) {
337 return xi.segment(start, d);
338 } else {
339 return xi.template segment<D>(start);
340 }
341}
342
343template <typename G, typename H, typename Action>
344typename SemidirectLieGroup<G, H, Action>::TangentVector
345SemidirectLieGroup<G, H, Action>::makeTangentVector(
346 const typename traits<G>::TangentVector& u,
347 const typename traits<H>::TangentVector& v, size_t d1, size_t d2) {
348 if constexpr (dimension == Eigen::Dynamic) {
349 TangentVector xi(d1 + d2);
350 xi << u, v;
351 return xi;
352 } else {
353 TangentVector xi;
354 xi << u, v;
355 return xi;
356 }
357}
358
359template <typename G, typename H, typename Action>
360typename SemidirectLieGroup<G, H, Action>::Jacobian
361SemidirectLieGroup<G, H, Action>::zeroJacobian(size_t d) {
362 if constexpr (dimension == Eigen::Dynamic) return Jacobian::Zero(d, d);
363 return Jacobian::Zero();
364}
365
366template <typename G, typename H, typename Action>
367typename SemidirectLieGroup<G, H, Action>::Jacobian
368SemidirectLieGroup<G, H, Action>::identityJacobian(size_t d) {
369 if constexpr (dimension == Eigen::Dynamic) return Jacobian::Identity(d, d);
370 return Jacobian::Identity();
371}
372
373template <typename G, typename H, typename Action>
374void SemidirectLieGroup<G, H, Action>::checkMatchingDimensions(
375 const SemidirectLieGroup& other, const char* operation) const {
376 if (firstDim() != other.firstDim() || secondDim() != other.secondDim()) {
377 throw std::invalid_argument(std::string("SemidirectLieGroup::") +
378 operation + " dimension mismatch");
379 }
380}
381
382template <typename G, typename H, typename Action>
383void SemidirectLieGroup<G, H, Action>::print(const std::string& s) const {
384 std::cout << s << "SemidirectLieGroup" << std::endl;
385 traits<G>::Print(this->first, " first: ");
386 traits<H>::Print(this->second, " second: ");
387}
388
389} // namespace gtsam
Global functions in a separate testing namespace.
Definition chartTesting.h:28
@ Logmap
Use the SE_2(3) NavState Logmap for every backend.
Definition PreintegrationParams.h:32
A manifold defines a space in which there is a notion of a linear tangent space that can be centered ...
Definition Group.h:37
OptionalJacobian is an Eigen::Ref like class that can take be constructed using either a fixed size o...
Definition OptionalJacobian.h:40
Detects the optional static algebra adjoint on the base group.
Definition SemidirectLieGroup.h:54
Left semidirect product G ⋉ H induced by Action.
Definition SemidirectLieGroup.h:96