forked from RobotLocomotion/drake
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathautodiff.h
236 lines (200 loc) · 10.3 KB
/
autodiff.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
/** @file
Utilities for arithmetic on AutoDiffScalar. */
#pragma once
#include <array>
#include <cmath>
#include <optional>
#include <tuple>
#include <utility>
#include <Eigen/Dense>
#include <unsupported/Eigen/AutoDiff>
#include "drake/common/autodiff.h"
#include "drake/common/eigen_types.h"
#include "drake/common/unused.h"
namespace drake {
namespace math {
/** Extracts the `value()` portion from a matrix of AutoDiffScalar entries.
(Each entry contains a value and some derivatives.)
@param auto_diff_matrix An object whose Eigen type represents a matrix of
AutoDiffScalar entries.
@retval value An Eigen::Matrix of the same dimensions as the input
matrix, but containing only the value portion of each entry, without the
derivatives.
@tparam Derived An Eigen type representing a matrix with AutoDiffScalar
entries. The type will be inferred from the type of the `auto_diff_matrix`
parameter at the call site.
<!-- Don't remove this note without fixing cross references to it in
Discard[Zero]Gradient(). -->
@note Drake provides several similar functions: DiscardGradient() is specialized
so that it can be applied to a `Matrix<T>` where T could be an AutoDiffScalar
or an ordinary double, in which case it returns the original matrix at no cost.
DiscardZeroGradient() is similar but requires that the discarded gradient was
zero. drake::ExtractDoubleOrThrow() has many specializations, including one for
`Matrix<AutoDiffScalar>` that behaves identically to ExtractValue().
@see DiscardGradient(), drake::ExtractDoubleOrThrow() */
template <typename Derived>
MatrixLikewise<typename Derived::Scalar::Scalar, Derived> ExtractValue(
const Eigen::MatrixBase<Derived>& auto_diff_matrix) {
MatrixLikewise<typename Derived::Scalar::Scalar, Derived> value(
auto_diff_matrix.rows(), auto_diff_matrix.cols());
for (int i = 0; i < auto_diff_matrix.rows(); ++i) {
for (int j = 0; j < auto_diff_matrix.cols(); ++j) {
value(i, j) = auto_diff_matrix(i, j).value();
}
}
return value;
}
/** `B = DiscardGradient(A)` enables casting from a matrix of AutoDiffScalars
to AutoDiffScalar::Scalar type, explicitly throwing away any gradient
information. For a matrix of type, e.g. `MatrixX<AutoDiffXd> A`, the
comparable operation
`B = A.cast<double>()`
should (and does) fail to compile. Use `DiscardGradient(A)` if you want to
force the cast (and explicitly declare that information is lost).
When called with a matrix that is already of type `double`, this function
returns a _reference_ to the argument without any copying. This efficiently
avoids extra copying, but be careful about reference lifetimes!
See ExtractValue() for a note on similar Drake functions.
@see ExtractValue(), DiscardZeroGradient() */
template <typename Derived>
decltype(auto) DiscardGradient(const Eigen::MatrixBase<Derived>& matrix) {
if constexpr (std::is_same_v<typename Derived::Scalar, double>) {
return matrix;
} else {
return ExtractValue(matrix);
}
}
/** Initializes a single AutoDiff matrix given the corresponding value matrix.
Sets the values of `auto_diff_matrix` (after resizing if necessary) to be equal
to `value`, and for each element i of `auto_diff_matrix`, resizes the
derivatives vector to `num_derivatives` and sets derivative number
`deriv_num_start` + i to one (all other elements of the derivative vector set
to zero).
When `value` and `auto_diff_matrix` are matrices (rather than just vectors)
note in particular that the derivative numbers count up using the _storage
order_ of `value(i)` and `autodiff_matrix(i)` and so the ColMajor vs RowMajor
storage order of the two must match.
@param[in] value a 'regular' matrix of values
@param[in] num_derivatives (Optional) size of the derivatives vector
@default total size of the value matrix
@param[in] deriv_num_start (Optional) starting index into derivative vector
(i.e. element deriv_num_start in derivative vector corresponds to
value(0, 0)).
@default 0
@param[out] auto_diff_matrix AutoDiff matrix set as described above
@exclude_from_pydrake_mkdoc{Not bound in pydrake.} */
template <typename Derived, typename DerivedAutoDiff>
void InitializeAutoDiff(const Eigen::MatrixBase<Derived>& value,
std::optional<int> num_derivatives,
std::optional<int> deriv_num_start,
Eigen::MatrixBase<DerivedAutoDiff>* auto_diff_matrix) {
// Any fixed-size dimension of auto_diff_matrix must match the
// corresponding fixed-size dimension of value. Any dynamic-size
// dimension must be dynamic in both matrices.
static_assert(static_cast<int>(Derived::RowsAtCompileTime) ==
static_cast<int>(DerivedAutoDiff::RowsAtCompileTime),
"auto diff matrix has wrong number of rows at compile time");
static_assert(static_cast<int>(Derived::ColsAtCompileTime) ==
static_cast<int>(DerivedAutoDiff::ColsAtCompileTime),
"auto diff matrix has wrong number of columns at compile time");
static_assert(static_cast<int>(Derived::IsRowMajor) ==
static_cast<int>(DerivedAutoDiff::IsRowMajor),
"auto diff matrix has wrong storage order at compile time");
DRAKE_DEMAND(auto_diff_matrix != nullptr);
if (!num_derivatives.has_value()) num_derivatives = value.size();
using ADScalar = typename DerivedAutoDiff::Scalar;
auto_diff_matrix->resize(value.rows(), value.cols());
int deriv_num = deriv_num_start.value_or(0);
for (int i = 0; i < value.size(); ++i) {
(*auto_diff_matrix)(i) = ADScalar(value(i), *num_derivatives, deriv_num++);
}
}
/** Alternate signature provides default values for the number of derivatives
(dynamic, determined at run time) and the starting index (0).
@exclude_from_pydrake_mkdoc{Not bound in pydrake.} */
template <typename Derived, typename DerivedAutoDiff>
void InitializeAutoDiff(const Eigen::MatrixBase<Derived>& value,
Eigen::MatrixBase<DerivedAutoDiff>* auto_diff_matrix) {
InitializeAutoDiff(value, {}, {}, auto_diff_matrix);
}
/** The appropriate AutoDiffScalar matrix type given the value type and the
number of derivatives at compile time. */
template <typename Derived, int nq>
using AutoDiffMatrixType =
MatrixLikewise<Eigen::AutoDiffScalar<Vector<typename Derived::Scalar, nq>>,
Derived>;
/** Initializes a single AutoDiff matrix given the corresponding value matrix.
Creates an AutoDiff matrix that matches `value` in size with derivative
of compile time size `nq` and runtime size `num_derivatives`. Sets its values to
be equal to `value`, and for each element i of `auto_diff_matrix`, sets
derivative number `deriv_num_start` + i to one (all other derivatives set to
zero).
When `value` is a matrix (rather than just a vector) note in particular that
the return value will use the same storage order (ColMajor vs RowMajor) and
that the derivative numbers count up using the _storage order_ of `value(i)`.
@param[in] value 'regular' matrix of values
@param[in] num_derivatives (Optional) size of the derivatives vector
@default total size of the value matrix
@param[in] deriv_num_start (Optional) starting index into derivative vector
(i.e. element deriv_num_start in derivative vector corresponds to
matrix(0, 0)).
@default 0
@retval auto_diff_matrix The result as described above.
@pydrake_mkdoc_identifier{just_value} */
template <int nq = Eigen::Dynamic, typename Derived>
AutoDiffMatrixType<Derived, nq> InitializeAutoDiff(
const Eigen::MatrixBase<Derived>& value,
std::optional<int> num_derivatives = {},
std::optional<int> deriv_num_start = {}) {
AutoDiffMatrixType<Derived, nq> auto_diff_matrix(value.rows(), value.cols());
InitializeAutoDiff(value, num_derivatives.value_or(value.size()),
deriv_num_start.value_or(0), &auto_diff_matrix);
return auto_diff_matrix;
}
/* Given a series of Eigen matrices, creates a tuple of corresponding
AutoDiff matrices with values equal to the input matrices and properly
initialized derivative vectors.
The size of the derivative vector of each element of the matrices in the
output tuple will be the same, and will equal the sum of the number of
elements of the matrices in `args`. If all of the matrices in `args` have fixed
size, then the derivative vectors will also have fixed size (being the sum of
the sizes at compile time of all of the input arguments), otherwise the
derivative vectors will have dynamic size. The 0th element of the derivative
vectors will correspond to the derivative with respect to the 0th element of the
first argument. Subsequent derivative vector elements correspond first to
subsequent elements of the first input argument (traversed in the same order as
InitializeAutoDiff() for that matrix), and so on for subsequent arguments.
@param args a series of Eigen matrices
@returns a tuple of properly initialized AutoDiff matrices corresponding to
`args` */
template <typename... Deriveds>
auto InitializeAutoDiffTuple(const Eigen::MatrixBase<Deriveds>&... args) {
// Compute the total compile-time size of all args (or Dynamic, if unknown).
// Refer to https://en.cppreference.com/w/cpp/language/fold for the syntax.
constexpr int nq = ((Deriveds::SizeAtCompileTime != Eigen::Dynamic) && ...)
? (static_cast<int>(Deriveds::SizeAtCompileTime) + ...)
: Eigen::Dynamic;
// Compute each deriv_num_start value and then the total runtime size.
constexpr size_t N = sizeof...(args);
const std::array<int, N> sizes{static_cast<int>(args.size())...};
std::array<int, N + 1> deriv_num_starts = {0};
for (size_t i = 1; i <= N; ++i) {
deriv_num_starts[i] = deriv_num_starts[i - 1] + sizes[i - 1];
}
const int num_derivatives = deriv_num_starts.back();
// Allocate the result.
std::tuple<AutoDiffMatrixType<Deriveds, nq>...> result(
AutoDiffMatrixType<Deriveds, nq>(args.rows(), args.cols())...);
// Set the values and gradients of the result using InitializeAutoDiff from
// each Matrix in 'args...'. This is a "constexpr for" loop for 0 <= I < N.
auto args_tuple = std::forward_as_tuple(args...);
[&]<size_t... I>(std::integer_sequence<size_t, I...> &&) {
(InitializeAutoDiff(std::get<I>(args_tuple), num_derivatives,
std::get<I>(deriv_num_starts), &std::get<I>(result)),
...);
}
(std::make_index_sequence<N>{});
return result;
}
} // namespace math
} // namespace drake