Skip to content

Commit b0f7158

Browse files
committed
Reimplement AutoDiff from the ground up
1 parent e307bdf commit b0f7158

28 files changed

Lines changed: 2698 additions & 445 deletions

common/BUILD.bazel

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,13 +134,12 @@ drake_cc_library(
134134
hdrs = [
135135
"autodiff.h",
136136
"autodiff_overloads.h",
137-
"autodiffxd.h",
138-
"eigen_autodiff_types.h",
139137
],
140138
deps = [
141139
":cond",
142140
":dummy_value",
143141
":essential",
142+
"//common/autodiff:auto_diff",
144143
],
145144
)
146145

@@ -655,6 +654,7 @@ drake_cc_googletest(
655654
"test/autodiffxd_tanh_test.cc",
656655
"test/autodiffxd_test.h",
657656
],
657+
tags = ["manual"],
658658
deps = [
659659
":autodiff",
660660
"//common/test_utilities:eigen_matrix_compare",
@@ -663,6 +663,7 @@ drake_cc_googletest(
663663

664664
drake_cc_googletest(
665665
name = "autodiffxd_heap_test",
666+
tags = ["manual"],
666667
deps = [
667668
":autodiff",
668669
"//common/test_utilities:limit_malloc",
@@ -671,6 +672,7 @@ drake_cc_googletest(
671672

672673
drake_cc_googletest(
673674
name = "autodiff_overloads_test",
675+
tags = ["manual"],
674676
deps = [
675677
":autodiff",
676678
":essential",
@@ -1283,6 +1285,7 @@ drake_cc_googletest(
12831285

12841286
drake_cc_googletest(
12851287
name = "eigen_autodiff_types_test",
1288+
tags = ["manual"],
12861289
deps = [":autodiff"],
12871290
)
12881291

common/autodiff.h

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,20 @@
11
#pragma once
2-
/// @file This header provides a single inclusion point for autodiff-related
3-
/// header files in the `drake/common` directory. Users should include this
4-
/// file. Including other individual headers such as `drake/common/autodiffxd.h`
5-
/// will generate a compile-time warning.
6-
7-
// In each header included below, it asserts that this macro
8-
// `DRAKE_COMMON_AUTODIFF_HEADER` is defined. If the macro is not defined, it
9-
// generates diagnostic warning messages.
10-
#define DRAKE_COMMON_AUTODIFF_HEADER
112

123
#include <Eigen/Core>
13-
#include <unsupported/Eigen/AutoDiff>
144

15-
// Do not alpha-sort the following block of hard-coded #includes, which is
16-
// protected by `clang-format on/off`.
17-
//
18-
// Rationale: We want to maximize the use of this header, `autodiff.h`, even
19-
// inside of the autodiff-related files to avoid any mistakes which might not be
20-
// detected. By centralizing the list here, we make sure that everyone will see
21-
// the correct order which respects the inter-dependencies of the autodiff
22-
// headers. This shields us from triggering undefined behaviors due to
23-
// order-of-specialization-includes-changed mistakes.
24-
//
25-
// clang-format off
26-
#include "drake/common/eigen_autodiff_types.h"
27-
#include "drake/common/autodiffxd.h"
5+
#include "drake/common/autodiff/auto_diff.h"
6+
7+
namespace drake {
8+
9+
/// An autodiff variable with a dynamic number of partials.
10+
using AutoDiffXd = autodiff::AutoDiff;
11+
12+
/// A dynamic-sized vector of autodiff variables, each with a dynamic-sized
13+
/// vector of partials.
14+
using AutoDiffVecXd = Eigen::Matrix<AutoDiffXd, Eigen::Dynamic, 1>;
15+
16+
} // namespace drake
17+
18+
#define DRAKE_COMMON_AUTODIFF_HEADER
2819
#include "drake/common/autodiff_overloads.h"
29-
// clang-format on
3020
#undef DRAKE_COMMON_AUTODIFF_HEADER

common/autodiff/BUILD.bazel

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# -*- python -*-
2+
3+
load(
4+
"@drake//tools/skylark:drake_cc.bzl",
5+
"drake_cc_googletest",
6+
"drake_cc_library",
7+
"drake_cc_package_library",
8+
)
9+
load("//tools/lint:lint.bzl", "add_lint_tests")
10+
11+
package(default_visibility = ["//visibility:public"])
12+
13+
drake_cc_library(
14+
name = "auto_diff",
15+
srcs = [
16+
"auto_diff_derivatives_xpr.cc",
17+
"internal/partials.cc",
18+
"internal/standard_operations.cc",
19+
"internal/static_data.cc",
20+
],
21+
hdrs = [
22+
"auto_diff.h",
23+
"auto_diff_derivatives_xpr.h",
24+
"internal/eigen_specializations.h",
25+
"internal/partials.h",
26+
"internal/standard_operations.h",
27+
"internal/static_data.h",
28+
],
29+
deps = [
30+
"//common:essential",
31+
"//common:reset_after_move",
32+
],
33+
)
34+
35+
drake_cc_googletest(
36+
name = "matrix_test",
37+
deps = [
38+
"//common:autodiff",
39+
],
40+
)
41+
42+
drake_cc_googletest(
43+
name = "partials_test",
44+
deps = [
45+
"//common:autodiff",
46+
"//common:random",
47+
"//common/test_utilities:eigen_matrix_compare",
48+
"//common/test_utilities:expect_throws_message",
49+
],
50+
)
51+
52+
drake_cc_googletest(
53+
name = "partials_heap_test",
54+
deps = [
55+
"//common:autodiff",
56+
"//common/test_utilities:limit_malloc",
57+
],
58+
)
59+
60+
drake_cc_googletest(
61+
name = "standard_operations_heap_test",
62+
deps = [
63+
"//common:autodiff",
64+
"//common/test_utilities:limit_malloc",
65+
],
66+
)
67+
68+
add_lint_tests()

common/autodiff/_zzz_notes.txt

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
2+
namespace internal {
3+
4+
/* ... See Eigen/src/Core/DenseStorage.h. */
5+
class DenseStorage {
6+
public:
7+
DenseStorage() = default;
8+
explicit DenseStorage(Eigen::internal::constructor_without_unaligned_array_assert) {}
9+
DenseStorage(Eigen::Index size, Eigen::Index rows, Eigen::Index cols) {
10+
resize(size, rows, cols);
11+
}
12+
13+
DenseStorage(const DenseStorage& other);
14+
DenseStorage& operator=(const DenseStorage& other);
15+
DenseStorage(DenseStorage&& other) noexcept;
16+
DenseStorage& operator=(DenseStorage&& other);
17+
18+
~DenseStorage() = default;
19+
20+
Eigen::Index rows() const { return rows_; }
21+
Eigen::Index cols() const { return cols_; }
22+
const AutoDiffScalar* data() const { return data_.data(); }
23+
AutoDiffScalar* data() { return data_.data(); }
24+
25+
void conservativeResize(Eigen::Index size, Eigen::Index rows, Eigen::Index cols) {
26+
DRAKE_ASSERT(size == rows * cols);
27+
data_.resize(size);
28+
rows_ = rows;
29+
cols_ = cols;
30+
}
31+
void resize(Eigen::Index size, Eigen::Index rows, Eigen::Index cols) {
32+
conservativeResize(size, rows, cols);
33+
}
34+
void swap(DenseStorage& other);
35+
36+
private:
37+
std::vector<AutoDiffScalar> data_;
38+
Eigen::Index rows_{0};
39+
Eigen::Index cols_{0};
40+
};
41+
} // namespace internal
42+
43+
namespace Eigen {
44+
45+
/** A specialization of DenseStorage for use by Drake's AutoDiffScalar.
46+
This is for fully-dynamic matrices. */
47+
template <>
48+
class DenseStorage<
49+
/* T = */ drake::autodiff::AutoDiffScalar,
50+
/* Size = */ Dynamic,
51+
/* _Rows = */ Dynamic,
52+
/* _Cols = */ Dynamic,
53+
/* _Options = */ 0>
54+
: public drake::autodiff::internal::DenseStorage {};
55+
56+
} // namespace Eigen
57+
#endif
58+
59+
60+
61+
/// Overloads copysign from <cmath>.
62+
template <typename DerType, typename T>
63+
Eigen::AutoDiffScalar<DerType> copysign(const Eigen::AutoDiffScalar<DerType>& x,
64+
const T& y) {
65+
using std::isnan;
66+
if (isnan(x)) return (y >= 0) ? NAN : -NAN;
67+
if ((x < 0 && y >= 0) || (x >= 0 && y < 0))
68+
return -x;
69+
else
70+
return x;
71+
}
72+
73+
/// Overloads copysign from <cmath>.
74+
template <typename DerType>
75+
double copysign(double x, const Eigen::AutoDiffScalar<DerType>& y) {
76+
using std::isnan;
77+
if (isnan(x)) return (y >= 0) ? NAN : -NAN;
78+
if ((x < 0 && y >= 0) || (x >= 0 && y < 0))
79+
return -x;
80+
else
81+
return x;
82+
}

common/autodiff/auto_diff.h

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
#pragma once
2+
3+
#include "drake/common/autodiff/auto_diff_derivatives_xpr.h"
4+
#include "drake/common/autodiff/internal/partials.h"
5+
#include "drake/common/drake_copyable.h"
6+
#include "drake/common/eigen_types.h"
7+
8+
namespace drake {
9+
namespace autodiff {
10+
11+
/** A scalar type that performs automatic differentiation, similar to
12+
Eigen::AutoDiffScalar<Eigen::VectorXd>. Unlike Eigen::AutoDiffScalar,
13+
Drake's AutoDiff is not templated; it only supports dynamically-sized
14+
derivatives using floating-point doubles.
15+
16+
However, using modern C++ implementation tricks (reference-counted,
17+
copy-on-write derivatives storage) and a more careful representation
18+
(maintaining the derivatives scale separately, and using inline storage
19+
in case only one partial is non-zero) it runs much faster than
20+
Eigen::AutoDiffScalar<Eigen::VectorXd>. */
21+
class AutoDiff {
22+
public:
23+
DRAKE_DEFAULT_COPY_AND_MOVE_AND_ASSIGN(AutoDiff);
24+
25+
/** Compatibility alias to mimic Eigen::AutoDiffScalar. */
26+
using DerType = Eigen::VectorXd;
27+
28+
/** Compatibility alias to mimic Eigen::AutoDiffScalar. */
29+
using Scalar = double;
30+
31+
/** Constructs zero. */
32+
AutoDiff() = default;
33+
34+
/** Constructs a value with empty derivatives. */
35+
// NOLINTNEXTLINE(runtime/explicit): This conversion is desirable.
36+
AutoDiff(double value) : value_{value} {}
37+
38+
/** Constructs a value with a single partial derivative of 1.0 at the given
39+
`offset` in a vector of `size` otherwise-zero derivatives. */
40+
AutoDiff(double value, Eigen::Index size, Eigen::Index offset)
41+
: value_{value},
42+
partials_{size, offset} {}
43+
44+
/** Constructs a value with the given derivatives. */
45+
AutoDiff(
46+
double value,
47+
const Eigen::Ref<const Eigen::VectorXd>& derivatives)
48+
: value_{value},
49+
partials_{derivatives} {}
50+
51+
/** Assigns a value and clears the derivatives. */
52+
AutoDiff& operator=(double value) {
53+
value_ = value;
54+
partials_.SetZero();
55+
return *this;
56+
}
57+
58+
~AutoDiff() = default;
59+
60+
/** Returns the value part of this AutoDiff (readonly). */
61+
double value() const { return value_; }
62+
63+
/** (Advanced) Returns the value part of this AutoDiff (mutable).
64+
Operations on this value will NOT alter the dervatives. */
65+
double& value() { return value_; }
66+
67+
/** Returns a view of the dervatives part of this AutoDiff (readonly). */
68+
AutoDiffDerivativesConstXpr derivatives() const {
69+
return partials_.make_const_xpr();
70+
}
71+
72+
/** (Advanced) Returns a mutable view of the dervatives part of this AutoDiff.
73+
74+
Instead of mutating the derivatives after construction, it's generally
75+
preferable to set them directly in the constructor if possible.
76+
77+
@note this function name is kept for compatibility with Eigen::AutoDiffScalar
78+
but it does NOT run in constant-time even though its name is lowercase.
79+
Calling this function often needs to copy the derivatives prior to returning
80+
the reference, so is O(N) in the size() of the derivatives. */
81+
AutoDiffDerivativesMutableXpr derivatives() {
82+
return partials_.MakeMutableXpr();
83+
}
84+
85+
/** (Internal use only)
86+
Users should call derivatives() instead. */
87+
const internal::Partials& partials() const { return partials_; }
88+
89+
/** (Internal use only)
90+
Users should call derivatives() instead. */
91+
internal::Partials& partials() { return partials_; }
92+
93+
private:
94+
double value_{0.0};
95+
internal::Partials partials_;
96+
};
97+
98+
} // namespace autodiff
99+
} // namespace drake
100+
101+
/* clang-format off to disable clang-format-includes */
102+
// These futher refine our AutoDiff type and must appear in exactly this order.
103+
#include "drake/common/autodiff/internal/standard_operations.h"
104+
#include "drake/common/autodiff/internal/eigen_specializations.h"

0 commit comments

Comments
 (0)