blob: 2b8724dbb0d699ef6ea50f6eb8512bc450ea7764 [file] [log] [blame]
Austin Schuh70cc9552019-01-21 19:46:48 -08001// Ceres Solver - A fast non-linear least squares minimizer
Austin Schuh3de38b02024-06-25 18:25:10 -07002// Copyright 2024 Google Inc. All rights reserved.
Austin Schuh70cc9552019-01-21 19:46:48 -08003// 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// mierle@gmail.com (Keir Mierle)
31
32#ifndef CERES_PUBLIC_DYNAMIC_AUTODIFF_COST_FUNCTION_H_
33#define CERES_PUBLIC_DYNAMIC_AUTODIFF_COST_FUNCTION_H_
34
35#include <cmath>
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080036#include <memory>
Austin Schuh70cc9552019-01-21 19:46:48 -080037#include <numeric>
Austin Schuh3de38b02024-06-25 18:25:10 -070038#include <type_traits>
Austin Schuh70cc9552019-01-21 19:46:48 -080039#include <vector>
40
Austin Schuh70cc9552019-01-21 19:46:48 -080041#include "ceres/dynamic_cost_function.h"
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080042#include "ceres/internal/fixed_array.h"
Austin Schuh70cc9552019-01-21 19:46:48 -080043#include "ceres/jet.h"
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080044#include "ceres/types.h"
Austin Schuh70cc9552019-01-21 19:46:48 -080045#include "glog/logging.h"
46
47namespace ceres {
48
49// This autodiff implementation differs from the one found in
50// autodiff_cost_function.h by supporting autodiff on cost functions
51// with variable numbers of parameters with variable sizes. With the
52// other implementation, all the sizes (both the number of parameter
53// blocks and the size of each block) must be fixed at compile time.
54//
55// The functor API differs slightly from the API for fixed size
56// autodiff; the expected interface for the cost functors is:
57//
58// struct MyCostFunctor {
59// template<typename T>
60// bool operator()(T const* const* parameters, T* residuals) const {
61// // Use parameters[i] to access the i'th parameter block.
62// }
63// };
64//
65// Since the sizing of the parameters is done at runtime, you must
66// also specify the sizes after creating the dynamic autodiff cost
67// function. For example:
68//
Austin Schuh3de38b02024-06-25 18:25:10 -070069// DynamicAutoDiffCostFunction<MyCostFunctor, 3> cost_function;
Austin Schuh70cc9552019-01-21 19:46:48 -080070// cost_function.AddParameterBlock(5);
71// cost_function.AddParameterBlock(10);
72// cost_function.SetNumResiduals(21);
73//
74// Under the hood, the implementation evaluates the cost function
75// multiple times, computing a small set of the derivatives (four by
76// default, controlled by the Stride template parameter) with each
77// pass. There is a tradeoff with the size of the passes; you may want
78// to experiment with the stride.
79template <typename CostFunctor, int Stride = 4>
Austin Schuh3de38b02024-06-25 18:25:10 -070080class DynamicAutoDiffCostFunction final : public DynamicCostFunction {
Austin Schuh70cc9552019-01-21 19:46:48 -080081 public:
Austin Schuh3de38b02024-06-25 18:25:10 -070082 // Constructs the CostFunctor on the heap and takes the ownership.
83 template <class... Args,
84 std::enable_if_t<std::is_constructible_v<CostFunctor, Args&&...>>* =
85 nullptr>
86 explicit DynamicAutoDiffCostFunction(Args&&... args)
87 // NOTE We explicitly use direct initialization using parentheses instead
88 // of uniform initialization using braces to avoid narrowing conversion
89 // warnings.
90 : DynamicAutoDiffCostFunction{
91 std::make_unique<CostFunctor>(std::forward<Args>(args)...)} {}
92
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080093 // Takes ownership by default.
Austin Schuh3de38b02024-06-25 18:25:10 -070094 explicit DynamicAutoDiffCostFunction(CostFunctor* functor,
95 Ownership ownership = TAKE_OWNERSHIP)
96 : DynamicAutoDiffCostFunction{std::unique_ptr<CostFunctor>{functor},
97 ownership} {}
Austin Schuh70cc9552019-01-21 19:46:48 -080098
Austin Schuh3de38b02024-06-25 18:25:10 -070099 explicit DynamicAutoDiffCostFunction(std::unique_ptr<CostFunctor> functor)
100 : DynamicAutoDiffCostFunction{std::move(functor), TAKE_OWNERSHIP} {}
Austin Schuh70cc9552019-01-21 19:46:48 -0800101
Austin Schuh3de38b02024-06-25 18:25:10 -0700102 DynamicAutoDiffCostFunction(const DynamicAutoDiffCostFunction& other) =
103 delete;
104 DynamicAutoDiffCostFunction& operator=(
105 const DynamicAutoDiffCostFunction& other) = delete;
106 DynamicAutoDiffCostFunction(DynamicAutoDiffCostFunction&& other) noexcept =
107 default;
108 DynamicAutoDiffCostFunction& operator=(
109 DynamicAutoDiffCostFunction&& other) noexcept = default;
110
111 ~DynamicAutoDiffCostFunction() override {
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800112 // Manually release pointer if configured to not take ownership
113 // rather than deleting only if ownership is taken. This is to
114 // stay maximally compatible to old user code which may have
115 // forgotten to implement a virtual destructor, from when the
116 // AutoDiffCostFunction always took ownership.
117 if (ownership_ == DO_NOT_TAKE_OWNERSHIP) {
118 functor_.release();
119 }
120 }
121
122 bool Evaluate(double const* const* parameters,
123 double* residuals,
124 double** jacobians) const override {
Austin Schuh70cc9552019-01-21 19:46:48 -0800125 CHECK_GT(num_residuals(), 0)
126 << "You must call DynamicAutoDiffCostFunction::SetNumResiduals() "
127 << "before DynamicAutoDiffCostFunction::Evaluate().";
128
Austin Schuh3de38b02024-06-25 18:25:10 -0700129 if (jacobians == nullptr) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800130 return (*functor_)(parameters, residuals);
131 }
132
133 // The difficulty with Jets, as implemented in Ceres, is that they were
134 // originally designed for strictly compile-sized use. At this point, there
135 // is a large body of code that assumes inside a cost functor it is
136 // acceptable to do e.g. T(1.5) and get an appropriately sized jet back.
137 //
138 // Unfortunately, it is impossible to communicate the expected size of a
139 // dynamically sized jet to the static instantiations that existing code
140 // depends on.
141 //
142 // To work around this issue, the solution here is to evaluate the
143 // jacobians in a series of passes, each one computing Stride *
144 // num_residuals() derivatives. This is done with small, fixed-size jets.
145 const int num_parameter_blocks =
146 static_cast<int>(parameter_block_sizes().size());
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800147 const int num_parameters = std::accumulate(
148 parameter_block_sizes().begin(), parameter_block_sizes().end(), 0);
Austin Schuh70cc9552019-01-21 19:46:48 -0800149
150 // Allocate scratch space for the strided evaluation.
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800151 using JetT = Jet<double, Stride>;
152 internal::FixedArray<JetT, (256 * 7) / sizeof(JetT)> input_jets(
153 num_parameters);
154 internal::FixedArray<JetT, (256 * 7) / sizeof(JetT)> output_jets(
155 num_residuals());
Austin Schuh70cc9552019-01-21 19:46:48 -0800156
157 // Make the parameter pack that is sent to the functor (reused).
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800158 internal::FixedArray<Jet<double, Stride>*> jet_parameters(
159 num_parameter_blocks, nullptr);
Austin Schuh70cc9552019-01-21 19:46:48 -0800160 int num_active_parameters = 0;
161
162 // To handle constant parameters between non-constant parameter blocks, the
163 // start position --- a raw parameter index --- of each contiguous block of
164 // non-constant parameters is recorded in start_derivative_section.
165 std::vector<int> start_derivative_section;
166 bool in_derivative_section = false;
167 int parameter_cursor = 0;
168
169 // Discover the derivative sections and set the parameter values.
170 for (int i = 0; i < num_parameter_blocks; ++i) {
171 jet_parameters[i] = &input_jets[parameter_cursor];
172
173 const int parameter_block_size = parameter_block_sizes()[i];
Austin Schuh3de38b02024-06-25 18:25:10 -0700174 if (jacobians[i] != nullptr) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800175 if (!in_derivative_section) {
176 start_derivative_section.push_back(parameter_cursor);
177 in_derivative_section = true;
178 }
179
180 num_active_parameters += parameter_block_size;
181 } else {
182 in_derivative_section = false;
183 }
184
185 for (int j = 0; j < parameter_block_size; ++j, parameter_cursor++) {
186 input_jets[parameter_cursor].a = parameters[i][j];
187 }
188 }
189
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800190 if (num_active_parameters == 0) {
191 return (*functor_)(parameters, residuals);
192 }
Austin Schuh70cc9552019-01-21 19:46:48 -0800193 // When `num_active_parameters % Stride != 0` then it can be the case
194 // that `active_parameter_count < Stride` while parameter_cursor is less
195 // than the total number of parameters and with no remaining non-constant
196 // parameter blocks. Pushing parameter_cursor (the total number of
197 // parameters) as a final entry to start_derivative_section is required
198 // because if a constant parameter block is encountered after the
199 // last non-constant block then current_derivative_section is incremented
200 // and would otherwise index an invalid position in
201 // start_derivative_section. Setting the final element to the total number
202 // of parameters means that this can only happen at most once in the loop
203 // below.
204 start_derivative_section.push_back(parameter_cursor);
205
206 // Evaluate all of the strides. Each stride is a chunk of the derivative to
207 // evaluate, typically some size proportional to the size of the SIMD
208 // registers of the CPU.
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800209 int num_strides = static_cast<int>(
210 ceil(num_active_parameters / static_cast<float>(Stride)));
Austin Schuh70cc9552019-01-21 19:46:48 -0800211
212 int current_derivative_section = 0;
213 int current_derivative_section_cursor = 0;
214
215 for (int pass = 0; pass < num_strides; ++pass) {
216 // Set most of the jet components to zero, except for
217 // non-constant #Stride parameters.
218 const int initial_derivative_section = current_derivative_section;
219 const int initial_derivative_section_cursor =
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800220 current_derivative_section_cursor;
Austin Schuh70cc9552019-01-21 19:46:48 -0800221
222 int active_parameter_count = 0;
223 parameter_cursor = 0;
224
225 for (int i = 0; i < num_parameter_blocks; ++i) {
226 for (int j = 0; j < parameter_block_sizes()[i];
227 ++j, parameter_cursor++) {
228 input_jets[parameter_cursor].v.setZero();
229 if (active_parameter_count < Stride &&
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800230 parameter_cursor >=
231 (start_derivative_section[current_derivative_section] +
232 current_derivative_section_cursor)) {
Austin Schuh3de38b02024-06-25 18:25:10 -0700233 if (jacobians[i] != nullptr) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800234 input_jets[parameter_cursor].v[active_parameter_count] = 1.0;
235 ++active_parameter_count;
236 ++current_derivative_section_cursor;
237 } else {
238 ++current_derivative_section;
239 current_derivative_section_cursor = 0;
240 }
241 }
242 }
243 }
244
245 if (!(*functor_)(&jet_parameters[0], &output_jets[0])) {
246 return false;
247 }
248
249 // Copy the pieces of the jacobians into their final place.
250 active_parameter_count = 0;
251
252 current_derivative_section = initial_derivative_section;
253 current_derivative_section_cursor = initial_derivative_section_cursor;
254
255 for (int i = 0, parameter_cursor = 0; i < num_parameter_blocks; ++i) {
256 for (int j = 0; j < parameter_block_sizes()[i];
257 ++j, parameter_cursor++) {
258 if (active_parameter_count < Stride &&
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800259 parameter_cursor >=
260 (start_derivative_section[current_derivative_section] +
261 current_derivative_section_cursor)) {
Austin Schuh3de38b02024-06-25 18:25:10 -0700262 if (jacobians[i] != nullptr) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800263 for (int k = 0; k < num_residuals(); ++k) {
264 jacobians[i][k * parameter_block_sizes()[i] + j] =
265 output_jets[k].v[active_parameter_count];
266 }
267 ++active_parameter_count;
268 ++current_derivative_section_cursor;
269 } else {
270 ++current_derivative_section;
271 current_derivative_section_cursor = 0;
272 }
273 }
274 }
275 }
276
277 // Only copy the residuals over once (even though we compute them on
278 // every loop).
279 if (pass == num_strides - 1) {
280 for (int k = 0; k < num_residuals(); ++k) {
281 residuals[k] = output_jets[k].a;
282 }
283 }
284 }
285 return true;
286 }
287
Austin Schuh3de38b02024-06-25 18:25:10 -0700288 const CostFunctor& functor() const { return *functor_; }
289
Austin Schuh70cc9552019-01-21 19:46:48 -0800290 private:
Austin Schuh3de38b02024-06-25 18:25:10 -0700291 explicit DynamicAutoDiffCostFunction(std::unique_ptr<CostFunctor> functor,
292 Ownership ownership)
293 : functor_(std::move(functor)), ownership_(ownership) {}
294
Austin Schuh70cc9552019-01-21 19:46:48 -0800295 std::unique_ptr<CostFunctor> functor_;
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800296 Ownership ownership_;
Austin Schuh70cc9552019-01-21 19:46:48 -0800297};
298
Austin Schuh3de38b02024-06-25 18:25:10 -0700299// Deduction guide that allows the user to avoid explicitly specifying the
300// template parameter of DynamicAutoDiffCostFunction. The class can instead be
301// instantiated as follows:
302//
303// new DynamicAutoDiffCostFunction{new MyCostFunctor{}};
304// new DynamicAutoDiffCostFunction{std::make_unique<MyCostFunctor>()};
305//
306template <typename CostFunctor>
307DynamicAutoDiffCostFunction(CostFunctor* functor)
308 -> DynamicAutoDiffCostFunction<CostFunctor>;
309template <typename CostFunctor>
310DynamicAutoDiffCostFunction(CostFunctor* functor, Ownership ownership)
311 -> DynamicAutoDiffCostFunction<CostFunctor>;
312template <typename CostFunctor>
313DynamicAutoDiffCostFunction(std::unique_ptr<CostFunctor> functor)
314 -> DynamicAutoDiffCostFunction<CostFunctor>;
315
Austin Schuh70cc9552019-01-21 19:46:48 -0800316} // namespace ceres
317
318#endif // CERES_PUBLIC_DYNAMIC_AUTODIFF_COST_FUNCTION_H_