blob: 4bf7e563c88ad6ee4fb4c2c5f5a180ea7794bfa0 [file] [log] [blame]
Austin Schuh3de38b02024-06-25 18:25:10 -07001// Ceres Solver - A fast non-linear least squares minimizer
2// Copyright 2023 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: sameeragarwal@google.com (Sameer Agarwal)
30
31#ifndef CERES_PUBLIC_AUTODIFF_MANIFOLD_H_
32#define CERES_PUBLIC_AUTODIFF_MANIFOLD_H_
33
34#include <memory>
35
36#include "ceres/internal/autodiff.h"
37#include "ceres/manifold.h"
38
39namespace ceres {
40
41// Create a Manifold with Jacobians computed via automatic differentiation. For
42// more information on manifolds, see include/ceres/manifold.h
43//
44// To get an auto differentiated manifold, you must define a class/struct with
45// templated Plus and Minus functions that compute
46//
47// x_plus_delta = Plus(x, delta);
48// y_minus_x = Minus(y, x);
49//
50// Where, x, y and x_plus_delta are vectors on the manifold in the ambient space
51// (so they are kAmbientSize vectors) and delta, y_minus_x are vectors in the
52// tangent space (so they are kTangentSize vectors).
53//
54// The Functor should have the signature:
55//
56// struct Functor {
57// template <typename T>
58// bool Plus(const T* x, const T* delta, T* x_plus_delta) const;
59//
60// template <typename T>
61// bool Minus(const T* y, const T* x, T* y_minus_x) const;
62// };
63//
64// Observe that the Plus and Minus operations are templated on the parameter T.
65// The autodiff framework substitutes appropriate "Jet" objects for T in order
66// to compute the derivative when necessary. This is the same mechanism that is
67// used to compute derivatives when using AutoDiffCostFunction.
68//
69// Plus and Minus should return true if the computation is successful and false
70// otherwise, in which case the result will not be used.
71//
72// Given this Functor, the corresponding Manifold can be constructed as:
73//
74// AutoDiffManifold<Functor, kAmbientSize, kTangentSize> manifold;
75//
76// As a concrete example consider the case of Quaternions. Quaternions form a
77// three dimensional manifold embedded in R^4, i.e. they have an ambient
78// dimension of 4 and their tangent space has dimension 3. The following Functor
79// (taken from autodiff_manifold_test.cc) defines the Plus and Minus operations
80// on the Quaternion manifold:
81//
82// NOTE: The following is only used for illustration purposes. Ceres Solver
83// ships with optimized production grade QuaternionManifold implementation. See
84// manifold.h.
85//
86// This functor assumes that the quaternions are laid out as [w,x,y,z] in
87// memory, i.e. the real or scalar part is the first coordinate.
88//
89// struct QuaternionFunctor {
90// template <typename T>
91// bool Plus(const T* x, const T* delta, T* x_plus_delta) const {
92// const T squared_norm_delta =
93// delta[0] * delta[0] + delta[1] * delta[1] + delta[2] * delta[2];
94//
95// T q_delta[4];
96// if (squared_norm_delta > T(0.0)) {
97// T norm_delta = sqrt(squared_norm_delta);
98// const T sin_delta_by_delta = sin(norm_delta) / norm_delta;
99// q_delta[0] = cos(norm_delta);
100// q_delta[1] = sin_delta_by_delta * delta[0];
101// q_delta[2] = sin_delta_by_delta * delta[1];
102// q_delta[3] = sin_delta_by_delta * delta[2];
103// } else {
104// // We do not just use q_delta = [1,0,0,0] here because that is a
105// // constant and when used for automatic differentiation will
106// // lead to a zero derivative. Instead we take a first order
107// // approximation and evaluate it at zero.
108// q_delta[0] = T(1.0);
109// q_delta[1] = delta[0];
110// q_delta[2] = delta[1];
111// q_delta[3] = delta[2];
112// }
113//
114// QuaternionProduct(q_delta, x, x_plus_delta);
115// return true;
116// }
117//
118// template <typename T>
119// bool Minus(const T* y, const T* x, T* y_minus_x) const {
120// T minus_x[4] = {x[0], -x[1], -x[2], -x[3]};
121// T ambient_y_minus_x[4];
122// QuaternionProduct(y, minus_x, ambient_y_minus_x);
123// T u_norm = sqrt(ambient_y_minus_x[1] * ambient_y_minus_x[1] +
124// ambient_y_minus_x[2] * ambient_y_minus_x[2] +
125// ambient_y_minus_x[3] * ambient_y_minus_x[3]);
126// if (u_norm > 0.0) {
127// T theta = atan2(u_norm, ambient_y_minus_x[0]);
128// y_minus_x[0] = theta * ambient_y_minus_x[1] / u_norm;
129// y_minus_x[1] = theta * ambient_y_minus_x[2] / u_norm;
130// y_minus_x[2] = theta * ambient_y_minus_x[3] / u_norm;
131// } else {
132// // We do not use [0,0,0] here because even though the value part is
133// // a constant, the derivative part is not.
134// y_minus_x[0] = ambient_y_minus_x[1];
135// y_minus_x[1] = ambient_y_minus_x[2];
136// y_minus_x[2] = ambient_y_minus_x[3];
137// }
138// return true;
139// }
140// };
141//
142// Then given this struct, the auto differentiated Quaternion Manifold can now
143// be constructed as
144//
145// Manifold* manifold = new AutoDiffManifold<QuaternionFunctor, 4, 3>;
146
147template <typename Functor, int kAmbientSize, int kTangentSize>
148class AutoDiffManifold final : public Manifold {
149 public:
150 AutoDiffManifold() : functor_(std::make_unique<Functor>()) {}
151
152 // Takes ownership of functor.
153 explicit AutoDiffManifold(Functor* functor) : functor_(functor) {}
154
155 int AmbientSize() const override { return kAmbientSize; }
156 int TangentSize() const override { return kTangentSize; }
157
158 bool Plus(const double* x,
159 const double* delta,
160 double* x_plus_delta) const override {
161 return functor_->Plus(x, delta, x_plus_delta);
162 }
163
164 bool PlusJacobian(const double* x, double* jacobian) const override;
165
166 bool Minus(const double* y,
167 const double* x,
168 double* y_minus_x) const override {
169 return functor_->Minus(y, x, y_minus_x);
170 }
171
172 bool MinusJacobian(const double* x, double* jacobian) const override;
173
174 const Functor& functor() const { return *functor_; }
175
176 private:
177 std::unique_ptr<Functor> functor_;
178};
179
180namespace internal {
181
182// The following two helper structs are needed to interface the Plus and Minus
183// methods of the ManifoldFunctor with the automatic differentiation which
184// expects a Functor with operator().
185template <typename Functor>
186struct PlusWrapper {
187 explicit PlusWrapper(const Functor& functor) : functor(functor) {}
188 template <typename T>
189 bool operator()(const T* x, const T* delta, T* x_plus_delta) const {
190 return functor.Plus(x, delta, x_plus_delta);
191 }
192 const Functor& functor;
193};
194
195template <typename Functor>
196struct MinusWrapper {
197 explicit MinusWrapper(const Functor& functor) : functor(functor) {}
198 template <typename T>
199 bool operator()(const T* y, const T* x, T* y_minus_x) const {
200 return functor.Minus(y, x, y_minus_x);
201 }
202 const Functor& functor;
203};
204} // namespace internal
205
206template <typename Functor, int kAmbientSize, int kTangentSize>
207bool AutoDiffManifold<Functor, kAmbientSize, kTangentSize>::PlusJacobian(
208 const double* x, double* jacobian) const {
209 double zero_delta[kTangentSize];
210 for (int i = 0; i < kTangentSize; ++i) {
211 zero_delta[i] = 0.0;
212 }
213
214 double x_plus_delta[kAmbientSize];
215 for (int i = 0; i < kAmbientSize; ++i) {
216 x_plus_delta[i] = 0.0;
217 }
218
219 const double* parameter_ptrs[2] = {x, zero_delta};
220
221 // PlusJacobian is D_2 Plus(x,0) so we only need to compute the Jacobian
222 // w.r.t. the second argument.
223 double* jacobian_ptrs[2] = {nullptr, jacobian};
224 return internal::AutoDifferentiate<
225 kAmbientSize,
226 internal::StaticParameterDims<kAmbientSize, kTangentSize>>(
227 internal::PlusWrapper<Functor>(*functor_),
228 parameter_ptrs,
229 kAmbientSize,
230 x_plus_delta,
231 jacobian_ptrs);
232}
233
234template <typename Functor, int kAmbientSize, int kTangentSize>
235bool AutoDiffManifold<Functor, kAmbientSize, kTangentSize>::MinusJacobian(
236 const double* x, double* jacobian) const {
237 double y_minus_x[kTangentSize];
238 for (int i = 0; i < kTangentSize; ++i) {
239 y_minus_x[i] = 0.0;
240 }
241
242 const double* parameter_ptrs[2] = {x, x};
243
244 // MinusJacobian is D_1 Minus(x,x), so we only need to compute the Jacobian
245 // w.r.t. the first argument.
246 double* jacobian_ptrs[2] = {jacobian, nullptr};
247 return internal::AutoDifferentiate<
248 kTangentSize,
249 internal::StaticParameterDims<kAmbientSize, kAmbientSize>>(
250 internal::MinusWrapper<Functor>(*functor_),
251 parameter_ptrs,
252 kTangentSize,
253 y_minus_x,
254 jacobian_ptrs);
255}
256
257} // namespace ceres
258
259#endif // CERES_PUBLIC_AUTODIFF_MANIFOLD_H_