g2o
Toggle main menu visibility
Loading...
Searching...
No Matches
g2o
autodiff
autodiff.h
Go to the documentation of this file.
1
// Ceres Solver - A fast non-linear least squares minimizer
2
// Copyright 2019 Google Inc. All rights reserved.
3
// http://ceres-solver.org/
4
//
5
// Redistribution and use in source and binary forms, with or without
6
// modification, are permitted provided that the following conditions are met:
7
//
8
// * Redistributions of source code must retain the above copyright notice,
9
// this list of conditions and the following disclaimer.
10
// * Redistributions in binary form must reproduce the above copyright notice,
11
// this list of conditions and the following disclaimer in the documentation
12
// and/or other materials provided with the distribution.
13
// * Neither the name of Google Inc. nor the names of its contributors may be
14
// used to endorse or promote products derived from this software without
15
// specific prior written permission.
16
//
17
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
// POSSIBILITY OF SUCH DAMAGE.
28
//
29
// Author: keir@google.com (Keir Mierle)
30
//
31
// Computation of the Jacobian matrix for vector-valued functions of multiple
32
// variables, using automatic differentiation based on the implementation of
33
// dual numbers in jet.h. Before reading the rest of this file, it is advisable
34
// to read jet.h's header comment in detail.
35
//
36
// The helper wrapper AutoDifferentiate() computes the jacobian of
37
// functors with templated operator() taking this form:
38
//
39
// struct F {
40
// template<typename T>
41
// bool operator()(const T *x, const T *y, ..., T *z) {
42
// // Compute z[] based on x[], y[], ...
43
// // return true if computation succeeded, false otherwise.
44
// }
45
// };
46
//
47
// All inputs and outputs may be vector-valued.
48
//
49
// To understand how jets are used to compute the jacobian, a
50
// picture may help. Consider a vector-valued function, F, returning 3
51
// dimensions and taking a vector-valued parameter of 4 dimensions:
52
//
53
// y x
54
// [ * ] F [ * ]
55
// [ * ] <--- [ * ]
56
// [ * ] [ * ]
57
// [ * ]
58
//
59
// Similar to the 2-parameter example for f described in jet.h, computing the
60
// jacobian dy/dx is done by substituting a suitable jet object for x and all
61
// intermediate steps of the computation of F. Since x is has 4 dimensions, use
62
// a Jet<double, 4>.
63
//
64
// Before substituting a jet object for x, the dual components are set
65
// appropriately for each dimension of x:
66
//
67
// y x
68
// [ * | * * * * ] f [ * | 1 0 0 0 ] x0
69
// [ * | * * * * ] <--- [ * | 0 1 0 0 ] x1
70
// [ * | * * * * ] [ * | 0 0 1 0 ] x2
71
// ---+--- [ * | 0 0 0 1 ] x3
72
// | ^ ^ ^ ^
73
// dy/dx | | | +----- infinitesimal for x3
74
// | | +------- infinitesimal for x2
75
// | +--------- infinitesimal for x1
76
// +----------- infinitesimal for x0
77
//
78
// The reason to set the internal 4x4 submatrix to the identity is that we wish
79
// to take the derivative of y separately with respect to each dimension of x.
80
// Each column of the 4x4 identity is therefore for a single component of the
81
// independent variable x.
82
//
83
// Then the jacobian of the mapping, dy/dx, is the 3x4 sub-matrix of the
84
// extended y vector, indicated in the above diagram.
85
//
86
// Functors with multiple parameters
87
// ---------------------------------
88
// In practice, it is often convenient to use a function f of two or more
89
// vector-valued parameters, for example, x[3] and z[6]. Unfortunately, the jet
90
// framework is designed for a single-parameter vector-valued input. The wrapper
91
// in this file addresses this issue adding support for functions with one or
92
// more parameter vectors.
93
//
94
// To support multiple parameters, all the parameter vectors are concatenated
95
// into one and treated as a single parameter vector, except that since the
96
// functor expects different inputs, we need to construct the jets as if they
97
// were part of a single parameter vector. The extended jets are passed
98
// separately for each parameter.
99
//
100
// For example, consider a functor F taking two vector parameters, p[2] and
101
// q[3], and producing an output y[4]:
102
//
103
// struct F {
104
// template<typename T>
105
// bool operator()(const T *p, const T *q, T *z) {
106
// // ...
107
// }
108
// };
109
//
110
// In this case, the necessary jet type is Jet<double, 5>. Here is a
111
// visualization of the jet objects in this case:
112
//
113
// Dual components for p ----+
114
// |
115
// -+-
116
// y [ * | 1 0 | 0 0 0 ] --- p[0]
117
// [ * | 0 1 | 0 0 0 ] --- p[1]
118
// [ * | . . | + + + ] |
119
// [ * | . . | + + + ] v
120
// [ * | . . | + + + ] <--- F(p, q)
121
// [ * | . . | + + + ] ^
122
// ^^^ ^^^^^ |
123
// dy/dp dy/dq [ * | 0 0 | 1 0 0 ] --- q[0]
124
// [ * | 0 0 | 0 1 0 ] --- q[1]
125
// [ * | 0 0 | 0 0 1 ] --- q[2]
126
// --+--
127
// |
128
// Dual components for q --------------+
129
//
130
// where the 4x2 submatrix (marked with ".") and 4x3 submatrix (marked with "+"
131
// of y in the above diagram are the derivatives of y with respect to p and q
132
// respectively. This is how autodiff works for functors taking multiple vector
133
// valued arguments (up to 6).
134
//
135
// Jacobian NULL pointers
136
// ----------------------
137
// In general, the functions below will accept NULL pointers for all or some of
138
// the Jacobian parameters, meaning that those Jacobians will not be computed.
139
140
#ifndef G2O_CERES_PUBLIC_INTERNAL_AUTODIFF_H_
141
#define G2O_CERES_PUBLIC_INTERNAL_AUTODIFF_H_
142
143
#include <stddef.h>
144
145
#include <array>
146
#include <utility>
147
148
#include "
array_selector.h
"
149
#include "
eigen.h
"
150
#include "
fixed_array.h
"
151
#include "
jet.h
"
152
#include "
parameter_dims.h
"
153
#include "
types.h
"
154
#include "
variadic_evaluate.h
"
155
156
// If the number of parameters exceeds this values, the corresponding jets are
157
// placed on the heap. This will reduce performance by a factor of 2-5 on
158
// current compilers.
159
#ifndef G2O_CERES_AUTODIFF_MAX_PARAMETERS_ON_STACK
160
#define G2O_CERES_AUTODIFF_MAX_PARAMETERS_ON_STACK 50
161
#endif
162
163
#ifndef G2O_CERES_AUTODIFF_MAX_RESIDUALS_ON_STACK
164
#define G2O_CERES_AUTODIFF_MAX_RESIDUALS_ON_STACK 20
165
#endif
166
167
namespace
g2o
{
168
namespace
ceres {
169
namespace
internal
{
170
171
// Extends src by a 1st order perturbation for every dimension and puts it in
172
// dst. The size of src is N. Since this is also used for perturbations in
173
// blocked arrays, offset is used to shift which part of the jet the
174
// perturbation occurs. This is used to set up the extended x augmented by an
175
// identity matrix. The JetT type should be a Jet type, and T should be a
176
// numeric type (e.g. double). For example,
177
//
178
// 0 1 2 3 4 5 6 7 8
179
// dst[0] [ * | . . | 1 0 0 | . . . ]
180
// dst[1] [ * | . . | 0 1 0 | . . . ]
181
// dst[2] [ * | . . | 0 0 1 | . . . ]
182
//
183
// is what would get put in dst if N was 3, offset was 3, and the jet type JetT
184
// was 8-dimensional.
185
template
<
int
j,
int
N,
int
Offset,
typename
T,
typename
JetT>
186
struct
Make1stOrderPerturbation
{
187
public
:
188
inline
static
void
Apply
(
const
T* src, JetT* dst) {
189
dst[j] = JetT(src[j], j + Offset);
190
Make1stOrderPerturbation<j + 1, N, Offset, T, JetT>::Apply
(src, dst);
191
}
192
};
193
194
template
<
int
N,
int
Offset,
typename
T,
typename
JetT>
195
struct
Make1stOrderPerturbation
<N, N, Offset, T, JetT> {
196
public
:
197
static
void
Apply
(
const
T*
/*src*/
, JetT*
/*dst*/
) {}
198
};
199
200
// Calls Make1stOrderPerturbation for every parameter block.
201
//
202
// Example:
203
// If one having three parameter blocks with dimensions (3, 2, 4), the call
204
// Make1stOrderPerturbations<integer_sequence<3, 2, 4>::Apply(params, x);
205
// will result in the following calls to Make1stOrderPerturbation:
206
// Make1stOrderPerturbation<0, 3, 0>::Apply(params[0], x + 0);
207
// Make1stOrderPerturbation<0, 2, 3>::Apply(params[1], x + 3);
208
// Make1stOrderPerturbation<0, 4, 5>::Apply(params[2], x + 5);
209
template
<
typename
Seq,
int
ParameterIdx = 0,
int
Offset = 0>
210
struct
Make1stOrderPerturbations
;
211
212
template
<
int
N,
int
... Ns,
int
ParameterIdx,
int
Offset>
213
struct
Make1stOrderPerturbations
<
std
::integer_sequence<int, N, Ns...>,
214
ParameterIdx, Offset> {
215
template
<
typename
T,
typename
JetT>
216
inline
static
void
Apply
(T
const
*
const
* parameters, JetT* x) {
217
Make1stOrderPerturbation<0, N, Offset, T, JetT>::Apply
(
218
parameters[ParameterIdx], x + Offset);
219
Make1stOrderPerturbations
<std::integer_sequence<int, Ns...>,
220
ParameterIdx + 1, Offset + N>
::Apply
(parameters,
221
x);
222
}
223
};
224
225
// End of 'recursion'. Nothing more to do.
226
template
<
int
ParameterIdx,
int
Total>
227
struct
Make1stOrderPerturbations
<
std
::integer_sequence<int>, ParameterIdx,
228
Total> {
229
template
<
typename
T,
typename
JetT>
230
static
void
Apply
(T
const
*
const
*
/* NOT USED */
, JetT*
/* NOT USED */
) {}
231
};
232
233
// Takes the 0th order part of src, assumed to be a Jet type, and puts it in
234
// dst. This is used to pick out the "vector" part of the extended y.
235
template
<
typename
JetT,
typename
T>
236
inline
void
Take0thOrderPart
(
int
M,
const
JetT* src, T dst) {
237
for
(
int
i = 0; i < M; ++i) {
238
dst[i] = src[i].a;
239
}
240
}
241
242
// Takes N 1st order parts, starting at index N0, and puts them in the M x N
243
// matrix 'dst'. This is used to pick out the "matrix" parts of the extended y.
244
template
<
int
N0,
int
N,
typename
JetT,
typename
T>
245
inline
void
Take1stOrderPart
(
const
int
M,
const
JetT* src, T* dst) {
246
for
(
int
i = 0; i < M; ++i) {
247
Eigen::Map<Eigen::Matrix<T, N, 1>>(dst + N * i, N) =
248
src[i].v.template segment<N>(N0);
249
}
250
}
251
252
// Calls Take1stOrderPart for every parameter block.
253
//
254
// Example:
255
// If one having three parameter blocks with dimensions (3, 2, 4), the call
256
// Take1stOrderParts<integer_sequence<3, 2, 4>::Apply(num_outputs,
257
// output,
258
// jacobians);
259
// will result in the following calls to Take1stOrderPart:
260
// if (jacobians[0]) {
261
// Take1stOrderPart<0, 3>(num_outputs, output, jacobians[0]);
262
// }
263
// if (jacobians[1]) {
264
// Take1stOrderPart<3, 2>(num_outputs, output, jacobians[1]);
265
// }
266
// if (jacobians[2]) {
267
// Take1stOrderPart<5, 4>(num_outputs, output, jacobians[2]);
268
// }
269
template
<
typename
Seq,
int
ParameterIdx = 0,
int
Offset = 0>
270
struct
Take1stOrderParts
;
271
272
template
<
int
N,
int
... Ns,
int
ParameterIdx,
int
Offset>
273
struct
Take1stOrderParts
<
std
::integer_sequence<int, N, Ns...>, ParameterIdx,
274
Offset> {
275
template
<
typename
JetT,
typename
T>
276
inline
static
void
Apply
(
int
num_outputs, JetT* output, T** jacobians) {
277
if
(jacobians[ParameterIdx]) {
278
Take1stOrderPart<Offset, N>
(num_outputs, output, jacobians[ParameterIdx]);
279
}
280
Take1stOrderParts
<std::integer_sequence<int, Ns...>, ParameterIdx + 1,
281
Offset + N>
::Apply
(num_outputs, output, jacobians);
282
}
283
};
284
285
// End of 'recursion'. Nothing more to do.
286
template
<
int
ParameterIdx,
int
Offset>
287
struct
Take1stOrderParts
<
std
::integer_sequence<int>, ParameterIdx, Offset> {
288
template
<
typename
T,
typename
JetT>
289
static
void
Apply
(
int
/* NOT USED*/
, JetT*
/* NOT USED*/
,
290
T**
/* NOT USED */
) {}
291
};
292
293
template
<
int
kNumResiduals,
typename
ParameterDims,
typename
Functor,
294
typename
T>
295
inline
bool
AutoDifferentiate
(
const
Functor& functor,
296
T
const
*
const
* parameters,
297
int
dynamic_num_outputs, T* function_value,
298
T** jacobians) {
299
typedef
Jet<T, ParameterDims::kNumParameters>
JetT;
300
using
Parameters =
typename
ParameterDims::Parameters
;
301
302
ArraySelector
<JetT,
ParameterDims::kNumParameters
,
303
G2O_CERES_AUTODIFF_MAX_PARAMETERS_ON_STACK
>
304
parameters_as_jets(
ParameterDims::kNumParameters
);
305
306
// Pointers to the beginning of each parameter block
307
std::array<JetT*, ParameterDims::kNumParameterBlocks> unpacked_parameters =
308
ParameterDims::GetUnpackedParameters
(parameters_as_jets.data());
309
310
// If the number of residuals is fixed, we use the template argument as the
311
// number of outputs. Otherwise we use the num_outputs parameter. Note: The
312
// ?-operator here is compile-time evaluated, therefore num_outputs is also
313
// a compile-time constant for functors with fixed residuals.
314
const
int
num_outputs =
315
kNumResiduals ==
kDynamic
? dynamic_num_outputs : kNumResiduals;
316
317
ArraySelector<JetT, kNumResiduals, G2O_CERES_AUTODIFF_MAX_RESIDUALS_ON_STACK>
318
residuals_as_jets(num_outputs);
319
320
// Invalidate the output Jets, so that we can detect if the user
321
// did not assign values to all of them.
322
for
(
int
i = 0; i < num_outputs; ++i) {
323
residuals_as_jets[i].a =
kImpossibleValue
;
324
residuals_as_jets[i].v.setConstant(
kImpossibleValue
);
325
}
326
327
Make1stOrderPerturbations<Parameters>::Apply
(parameters,
328
parameters_as_jets.data());
329
330
if
(!
VariadicEvaluate<ParameterDims>
(functor, unpacked_parameters.data(),
331
residuals_as_jets.data())) {
332
return
false
;
333
}
334
335
Take0thOrderPart
(num_outputs, residuals_as_jets.data(), function_value);
336
Take1stOrderParts<Parameters>::Apply
(num_outputs, residuals_as_jets.data(),
337
jacobians);
338
339
return
true
;
340
}
341
342
}
// namespace internal
343
}
// namespace ceres
344
}
// namespace g2o
345
346
#endif
// G2O_CERES_PUBLIC_INTERNAL_AUTODIFF_H_
array_selector.h
G2O_CERES_AUTODIFF_MAX_PARAMETERS_ON_STACK
#define G2O_CERES_AUTODIFF_MAX_PARAMETERS_ON_STACK
Definition
autodiff.h:160
g2o::ceres::internal::ParameterDims::kNumParameters
static constexpr int kNumParameters
Definition
parameter_dims.h:85
g2o::ceres::internal::ParameterDims::Parameters
std::integer_sequence< int, Ns... > Parameters
Definition
parameter_dims.h:70
g2o::ceres::internal::ParameterDims::GetUnpackedParameters
static std::array< T *, kNumParameterBlocks > GetUnpackedParameters(T *ptr)
Definition
parameter_dims.h:93
eigen.h
fixed_array.h
jet.h
g2o::ceres::internal::Take1stOrderPart
void Take1stOrderPart(const int M, const JetT *src, T *dst)
Definition
autodiff.h:245
g2o::ceres::internal::AutoDifferentiate
bool AutoDifferentiate(const Functor &functor, T const *const *parameters, int dynamic_num_outputs, T *function_value, T **jacobians)
Definition
autodiff.h:295
g2o::ceres::internal::Take0thOrderPart
void Take0thOrderPart(int M, const JetT *src, T dst)
Definition
autodiff.h:236
g2o::ceres::internal::VariadicEvaluate
bool VariadicEvaluate(const Functor &functor, T const *const *input, T *output)
Definition
variadic_evaluate.h:99
g2o::ceres::kDynamic
@ kDynamic
Definition
types.h:51
g2o::ceres::kImpossibleValue
const double kImpossibleValue
Definition
types.h:59
g2o
Definition
dl_wrapper.cpp:55
internal
Definition
base_variable_sized_edge.hpp:29
std
Definition
jet.h:876
parameter_dims.h
g2o::ceres::Jet
Definition
jet.h:172
g2o::ceres::internal::ArraySelector
Definition
array_selector.h:63
g2o::ceres::internal::Make1stOrderPerturbation< N, N, Offset, T, JetT >::Apply
static void Apply(const T *, JetT *)
Definition
autodiff.h:197
g2o::ceres::internal::Make1stOrderPerturbation
Definition
autodiff.h:186
g2o::ceres::internal::Make1stOrderPerturbation::Apply
static void Apply(const T *src, JetT *dst)
Definition
autodiff.h:188
g2o::ceres::internal::Make1stOrderPerturbations< std::integer_sequence< int, N, Ns... >, ParameterIdx, Offset >::Apply
static void Apply(T const *const *parameters, JetT *x)
Definition
autodiff.h:216
g2o::ceres::internal::Make1stOrderPerturbations< std::integer_sequence< int >, ParameterIdx, Total >::Apply
static void Apply(T const *const *, JetT *)
Definition
autodiff.h:230
g2o::ceres::internal::Make1stOrderPerturbations
Definition
autodiff.h:210
g2o::ceres::internal::Take1stOrderParts< std::integer_sequence< int, N, Ns... >, ParameterIdx, Offset >::Apply
static void Apply(int num_outputs, JetT *output, T **jacobians)
Definition
autodiff.h:276
g2o::ceres::internal::Take1stOrderParts< std::integer_sequence< int >, ParameterIdx, Offset >::Apply
static void Apply(int, JetT *, T **)
Definition
autodiff.h:289
g2o::ceres::internal::Take1stOrderParts
Definition
autodiff.h:270
types.h
variadic_evaluate.h
Generated on
for g2o by
1.17.0