blob: a5a243df5b148c9ce40f9ea4af023fb93b617ed5 [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 2023 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: keir@google.com (Keir Mierle)
30
31#include "ceres/program.h"
32
33#include <algorithm>
34#include <map>
35#include <memory>
Austin Schuh3de38b02024-06-25 18:25:10 -070036#include <string>
Austin Schuh70cc9552019-01-21 19:46:48 -080037#include <vector>
38
39#include "ceres/array_utils.h"
40#include "ceres/casts.h"
41#include "ceres/compressed_row_sparse_matrix.h"
42#include "ceres/cost_function.h"
43#include "ceres/evaluator.h"
Austin Schuh3de38b02024-06-25 18:25:10 -070044#include "ceres/internal/export.h"
Austin Schuh70cc9552019-01-21 19:46:48 -080045#include "ceres/loss_function.h"
Austin Schuh3de38b02024-06-25 18:25:10 -070046#include "ceres/manifold.h"
Austin Schuh70cc9552019-01-21 19:46:48 -080047#include "ceres/map_util.h"
Austin Schuh3de38b02024-06-25 18:25:10 -070048#include "ceres/parallel_for.h"
Austin Schuh70cc9552019-01-21 19:46:48 -080049#include "ceres/parameter_block.h"
50#include "ceres/problem.h"
51#include "ceres/residual_block.h"
52#include "ceres/stl_util.h"
53#include "ceres/triplet_sparse_matrix.h"
54
Austin Schuh3de38b02024-06-25 18:25:10 -070055namespace ceres::internal {
Austin Schuh70cc9552019-01-21 19:46:48 -080056
Austin Schuh3de38b02024-06-25 18:25:10 -070057const std::vector<ParameterBlock*>& Program::parameter_blocks() const {
Austin Schuh70cc9552019-01-21 19:46:48 -080058 return parameter_blocks_;
59}
60
Austin Schuh3de38b02024-06-25 18:25:10 -070061const std::vector<ResidualBlock*>& Program::residual_blocks() const {
Austin Schuh70cc9552019-01-21 19:46:48 -080062 return residual_blocks_;
63}
64
Austin Schuh3de38b02024-06-25 18:25:10 -070065std::vector<ParameterBlock*>* Program::mutable_parameter_blocks() {
Austin Schuh70cc9552019-01-21 19:46:48 -080066 return &parameter_blocks_;
67}
68
Austin Schuh3de38b02024-06-25 18:25:10 -070069std::vector<ResidualBlock*>* Program::mutable_residual_blocks() {
Austin Schuh70cc9552019-01-21 19:46:48 -080070 return &residual_blocks_;
71}
72
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080073EvaluationCallback* Program::mutable_evaluation_callback() {
74 return evaluation_callback_;
75}
76
77bool Program::StateVectorToParameterBlocks(const double* state) {
Austin Schuh3de38b02024-06-25 18:25:10 -070078 for (auto* parameter_block : parameter_blocks_) {
79 if (!parameter_block->IsConstant() && !parameter_block->SetState(state)) {
Austin Schuh70cc9552019-01-21 19:46:48 -080080 return false;
81 }
Austin Schuh3de38b02024-06-25 18:25:10 -070082 state += parameter_block->Size();
Austin Schuh70cc9552019-01-21 19:46:48 -080083 }
84 return true;
85}
86
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080087void Program::ParameterBlocksToStateVector(double* state) const {
Austin Schuh3de38b02024-06-25 18:25:10 -070088 for (auto* parameter_block : parameter_blocks_) {
89 parameter_block->GetState(state);
90 state += parameter_block->Size();
Austin Schuh70cc9552019-01-21 19:46:48 -080091 }
92}
93
94void Program::CopyParameterBlockStateToUserState() {
Austin Schuh3de38b02024-06-25 18:25:10 -070095 for (auto* parameter_block : parameter_blocks_) {
96 parameter_block->GetState(parameter_block->mutable_user_state());
Austin Schuh70cc9552019-01-21 19:46:48 -080097 }
98}
99
100bool Program::SetParameterBlockStatePtrsToUserStatePtrs() {
Austin Schuh3de38b02024-06-25 18:25:10 -0700101 for (auto* parameter_block : parameter_blocks_) {
102 if (!parameter_block->IsConstant() &&
103 !parameter_block->SetState(parameter_block->user_state())) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800104 return false;
105 }
106 }
107 return true;
108}
109
110bool Program::Plus(const double* state,
111 const double* delta,
Austin Schuh3de38b02024-06-25 18:25:10 -0700112 double* state_plus_delta,
113 ContextImpl* context,
114 int num_threads) const {
115 std::atomic<bool> abort(false);
116 auto* parameter_blocks = parameter_blocks_.data();
117 ParallelFor(
118 context,
119 0,
120 parameter_blocks_.size(),
121 num_threads,
122 [&abort, state, delta, state_plus_delta, parameter_blocks](int block_id) {
123 if (abort) {
124 return;
125 }
126 auto parameter_block = parameter_blocks[block_id];
127
128 auto block_state = state + parameter_block->state_offset();
129 auto block_delta = delta + parameter_block->delta_offset();
130 auto block_state_plus_delta =
131 state_plus_delta + parameter_block->state_offset();
132 if (!parameter_block->Plus(
133 block_state, block_delta, block_state_plus_delta)) {
134 abort = true;
135 }
136 });
137 return abort == false;
Austin Schuh70cc9552019-01-21 19:46:48 -0800138}
139
140void Program::SetParameterOffsetsAndIndex() {
141 // Set positions for all parameters appearing as arguments to residuals to one
142 // past the end of the parameter block array.
Austin Schuh3de38b02024-06-25 18:25:10 -0700143 for (auto* residual_block : residual_blocks_) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800144 for (int j = 0; j < residual_block->NumParameterBlocks(); ++j) {
145 residual_block->parameter_blocks()[j]->set_index(-1);
146 }
147 }
148 // For parameters that appear in the program, set their position and offset.
149 int state_offset = 0;
150 int delta_offset = 0;
151 for (int i = 0; i < parameter_blocks_.size(); ++i) {
152 parameter_blocks_[i]->set_index(i);
153 parameter_blocks_[i]->set_state_offset(state_offset);
154 parameter_blocks_[i]->set_delta_offset(delta_offset);
155 state_offset += parameter_blocks_[i]->Size();
Austin Schuh3de38b02024-06-25 18:25:10 -0700156 delta_offset += parameter_blocks_[i]->TangentSize();
Austin Schuh70cc9552019-01-21 19:46:48 -0800157 }
158}
159
160bool Program::IsValid() const {
161 for (int i = 0; i < residual_blocks_.size(); ++i) {
162 const ResidualBlock* residual_block = residual_blocks_[i];
163 if (residual_block->index() != i) {
164 LOG(WARNING) << "Residual block: " << i
165 << " has incorrect index: " << residual_block->index();
166 return false;
167 }
168 }
169
170 int state_offset = 0;
171 int delta_offset = 0;
172 for (int i = 0; i < parameter_blocks_.size(); ++i) {
173 const ParameterBlock* parameter_block = parameter_blocks_[i];
174 if (parameter_block->index() != i ||
175 parameter_block->state_offset() != state_offset ||
176 parameter_block->delta_offset() != delta_offset) {
177 LOG(WARNING) << "Parameter block: " << i
178 << "has incorrect indexing information: "
179 << parameter_block->ToString();
180 return false;
181 }
182
183 state_offset += parameter_blocks_[i]->Size();
Austin Schuh3de38b02024-06-25 18:25:10 -0700184 delta_offset += parameter_blocks_[i]->TangentSize();
Austin Schuh70cc9552019-01-21 19:46:48 -0800185 }
186
187 return true;
188}
189
Austin Schuh3de38b02024-06-25 18:25:10 -0700190bool Program::ParameterBlocksAreFinite(std::string* message) const {
Austin Schuh70cc9552019-01-21 19:46:48 -0800191 CHECK(message != nullptr);
Austin Schuh3de38b02024-06-25 18:25:10 -0700192 for (auto* parameter_block : parameter_blocks_) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800193 const double* array = parameter_block->user_state();
194 const int size = parameter_block->Size();
195 const int invalid_index = FindInvalidValue(size, array);
196 if (invalid_index != size) {
197 *message = StringPrintf(
198 "ParameterBlock: %p with size %d has at least one invalid value.\n"
199 "First invalid value is at index: %d.\n"
200 "Parameter block values: ",
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800201 array,
202 size,
203 invalid_index);
Austin Schuh70cc9552019-01-21 19:46:48 -0800204 AppendArrayToString(size, array, message);
205 return false;
206 }
207 }
208 return true;
209}
210
211bool Program::IsBoundsConstrained() const {
Austin Schuh3de38b02024-06-25 18:25:10 -0700212 for (auto* parameter_block : parameter_blocks_) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800213 if (parameter_block->IsConstant()) {
214 continue;
215 }
216 const int size = parameter_block->Size();
217 for (int j = 0; j < size; ++j) {
218 const double lower_bound = parameter_block->LowerBoundForParameter(j);
219 const double upper_bound = parameter_block->UpperBoundForParameter(j);
220 if (lower_bound > -std::numeric_limits<double>::max() ||
221 upper_bound < std::numeric_limits<double>::max()) {
222 return true;
223 }
224 }
225 }
226 return false;
227}
228
Austin Schuh3de38b02024-06-25 18:25:10 -0700229bool Program::IsFeasible(std::string* message) const {
Austin Schuh70cc9552019-01-21 19:46:48 -0800230 CHECK(message != nullptr);
Austin Schuh3de38b02024-06-25 18:25:10 -0700231 for (auto* parameter_block : parameter_blocks_) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800232 const double* parameters = parameter_block->user_state();
233 const int size = parameter_block->Size();
234 if (parameter_block->IsConstant()) {
235 // Constant parameter blocks must start in the feasible region
236 // to ultimately produce a feasible solution, since Ceres cannot
237 // change them.
238 for (int j = 0; j < size; ++j) {
239 const double lower_bound = parameter_block->LowerBoundForParameter(j);
240 const double upper_bound = parameter_block->UpperBoundForParameter(j);
241 if (parameters[j] < lower_bound || parameters[j] > upper_bound) {
242 *message = StringPrintf(
243 "ParameterBlock: %p with size %d has at least one infeasible "
244 "value."
245 "\nFirst infeasible value is at index: %d."
246 "\nLower bound: %e, value: %e, upper bound: %e"
247 "\nParameter block values: ",
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800248 parameters,
249 size,
250 j,
251 lower_bound,
252 parameters[j],
253 upper_bound);
Austin Schuh70cc9552019-01-21 19:46:48 -0800254 AppendArrayToString(size, parameters, message);
255 return false;
256 }
257 }
258 } else {
259 // Variable parameter blocks must have non-empty feasible
260 // regions, otherwise there is no way to produce a feasible
261 // solution.
262 for (int j = 0; j < size; ++j) {
263 const double lower_bound = parameter_block->LowerBoundForParameter(j);
264 const double upper_bound = parameter_block->UpperBoundForParameter(j);
265 if (lower_bound >= upper_bound) {
266 *message = StringPrintf(
267 "ParameterBlock: %p with size %d has at least one infeasible "
268 "bound."
269 "\nFirst infeasible bound is at index: %d."
270 "\nLower bound: %e, upper bound: %e"
271 "\nParameter block values: ",
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800272 parameters,
273 size,
274 j,
275 lower_bound,
276 upper_bound);
Austin Schuh70cc9552019-01-21 19:46:48 -0800277 AppendArrayToString(size, parameters, message);
278 return false;
279 }
280 }
281 }
282 }
283
284 return true;
285}
286
Austin Schuh3de38b02024-06-25 18:25:10 -0700287std::unique_ptr<Program> Program::CreateReducedProgram(
288 std::vector<double*>* removed_parameter_blocks,
Austin Schuh70cc9552019-01-21 19:46:48 -0800289 double* fixed_cost,
Austin Schuh3de38b02024-06-25 18:25:10 -0700290 std::string* error) const {
Austin Schuh70cc9552019-01-21 19:46:48 -0800291 CHECK(removed_parameter_blocks != nullptr);
292 CHECK(fixed_cost != nullptr);
293 CHECK(error != nullptr);
294
Austin Schuh3de38b02024-06-25 18:25:10 -0700295 std::unique_ptr<Program> reduced_program = std::make_unique<Program>(*this);
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800296 if (!reduced_program->RemoveFixedBlocks(
297 removed_parameter_blocks, fixed_cost, error)) {
298 return nullptr;
Austin Schuh70cc9552019-01-21 19:46:48 -0800299 }
300
301 reduced_program->SetParameterOffsetsAndIndex();
Austin Schuh3de38b02024-06-25 18:25:10 -0700302 return reduced_program;
Austin Schuh70cc9552019-01-21 19:46:48 -0800303}
304
Austin Schuh3de38b02024-06-25 18:25:10 -0700305bool Program::RemoveFixedBlocks(std::vector<double*>* removed_parameter_blocks,
Austin Schuh70cc9552019-01-21 19:46:48 -0800306 double* fixed_cost,
Austin Schuh3de38b02024-06-25 18:25:10 -0700307 std::string* error) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800308 CHECK(removed_parameter_blocks != nullptr);
309 CHECK(fixed_cost != nullptr);
310 CHECK(error != nullptr);
311
312 std::unique_ptr<double[]> residual_block_evaluate_scratch;
Austin Schuh3de38b02024-06-25 18:25:10 -0700313 residual_block_evaluate_scratch =
314 std::make_unique<double[]>(MaxScratchDoublesNeededForEvaluate());
Austin Schuh70cc9552019-01-21 19:46:48 -0800315 *fixed_cost = 0.0;
316
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800317 bool need_to_call_prepare_for_evaluation = evaluation_callback_ != nullptr;
318
Austin Schuh70cc9552019-01-21 19:46:48 -0800319 // Mark all the parameters as unused. Abuse the index member of the
320 // parameter blocks for the marking.
Austin Schuh3de38b02024-06-25 18:25:10 -0700321 for (auto* parameter_block : parameter_blocks_) {
322 parameter_block->set_index(-1);
Austin Schuh70cc9552019-01-21 19:46:48 -0800323 }
324
325 // Filter out residual that have all-constant parameters, and mark
326 // all the parameter blocks that appear in residuals.
327 int num_active_residual_blocks = 0;
328 for (int i = 0; i < residual_blocks_.size(); ++i) {
329 ResidualBlock* residual_block = residual_blocks_[i];
330 int num_parameter_blocks = residual_block->NumParameterBlocks();
331
332 // Determine if the residual block is fixed, and also mark varying
333 // parameters that appear in the residual block.
334 bool all_constant = true;
335 for (int k = 0; k < num_parameter_blocks; k++) {
336 ParameterBlock* parameter_block = residual_block->parameter_blocks()[k];
337 if (!parameter_block->IsConstant()) {
338 all_constant = false;
339 parameter_block->set_index(1);
340 }
341 }
342
343 if (!all_constant) {
344 residual_blocks_[num_active_residual_blocks++] = residual_block;
345 continue;
346 }
347
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800348 // This is an exceedingly rare case, where the user has residual
349 // blocks which are effectively constant but they are also
350 // performance sensitive enough to add an EvaluationCallback.
351 //
352 // In this case before we evaluate the cost of the constant
353 // residual blocks, we must call
354 // EvaluationCallback::PrepareForEvaluation(). Because this call
355 // can be costly, we only call this if we actually encounter a
356 // residual block with all constant parameter blocks.
357 //
358 // It is worth nothing that there is a minor inefficiency here,
359 // that the iteration 0 of TrustRegionMinimizer will also cause
360 // PrepareForEvaluation to be called on the same point, but with
361 // evaluate_jacobians = true. We could try and optimize this here,
362 // but given the rarity of this case, the additional complexity
363 // and long range dependency is not worth it.
364 if (need_to_call_prepare_for_evaluation) {
365 constexpr bool kNewPoint = true;
366 constexpr bool kDoNotEvaluateJacobians = false;
367 evaluation_callback_->PrepareForEvaluation(kDoNotEvaluateJacobians,
368 kNewPoint);
369 need_to_call_prepare_for_evaluation = false;
370 }
371
Austin Schuh70cc9552019-01-21 19:46:48 -0800372 // The residual is constant and will be removed, so its cost is
373 // added to the variable fixed_cost.
374 double cost = 0.0;
375 if (!residual_block->Evaluate(true,
376 &cost,
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800377 nullptr,
378 nullptr,
Austin Schuh70cc9552019-01-21 19:46:48 -0800379 residual_block_evaluate_scratch.get())) {
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800380 *error = StringPrintf(
381 "Evaluation of the residual %d failed during "
382 "removal of fixed residual blocks.",
383 i);
Austin Schuh70cc9552019-01-21 19:46:48 -0800384 return false;
385 }
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800386
Austin Schuh70cc9552019-01-21 19:46:48 -0800387 *fixed_cost += cost;
388 }
389 residual_blocks_.resize(num_active_residual_blocks);
390
391 // Filter out unused or fixed parameter blocks.
392 int num_active_parameter_blocks = 0;
393 removed_parameter_blocks->clear();
Austin Schuh3de38b02024-06-25 18:25:10 -0700394 for (auto* parameter_block : parameter_blocks_) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800395 if (parameter_block->index() == -1) {
396 removed_parameter_blocks->push_back(
397 parameter_block->mutable_user_state());
398 } else {
399 parameter_blocks_[num_active_parameter_blocks++] = parameter_block;
400 }
401 }
402 parameter_blocks_.resize(num_active_parameter_blocks);
403
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800404 if (!(((NumResidualBlocks() == 0) && (NumParameterBlocks() == 0)) ||
405 ((NumResidualBlocks() != 0) && (NumParameterBlocks() != 0)))) {
406 *error = "Congratulations, you found a bug in Ceres. Please report it.";
Austin Schuh70cc9552019-01-21 19:46:48 -0800407 return false;
408 }
409
410 return true;
411}
412
413bool Program::IsParameterBlockSetIndependent(
Austin Schuh3de38b02024-06-25 18:25:10 -0700414 const std::set<double*>& independent_set) const {
Austin Schuh70cc9552019-01-21 19:46:48 -0800415 // Loop over each residual block and ensure that no two parameter
416 // blocks in the same residual block are part of
417 // parameter_block_ptrs as that would violate the assumption that it
418 // is an independent set in the Hessian matrix.
419 for (const ResidualBlock* residual_block : residual_blocks_) {
420 ParameterBlock* const* parameter_blocks =
421 residual_block->parameter_blocks();
422 const int num_parameter_blocks = residual_block->NumParameterBlocks();
423 int count = 0;
424 for (int i = 0; i < num_parameter_blocks; ++i) {
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800425 count += independent_set.count(parameter_blocks[i]->mutable_user_state());
Austin Schuh70cc9552019-01-21 19:46:48 -0800426 }
427 if (count > 1) {
428 return false;
429 }
430 }
431 return true;
432}
433
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800434std::unique_ptr<TripletSparseMatrix>
435Program::CreateJacobianBlockSparsityTranspose(int start_residual_block) const {
Austin Schuh70cc9552019-01-21 19:46:48 -0800436 // Matrix to store the block sparsity structure of the Jacobian.
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800437 const int num_rows = NumParameterBlocks();
438 const int num_cols = NumResidualBlocks() - start_residual_block;
439
440 std::unique_ptr<TripletSparseMatrix> tsm(
441 new TripletSparseMatrix(num_rows, num_cols, 10 * num_cols));
Austin Schuh70cc9552019-01-21 19:46:48 -0800442 int num_nonzeros = 0;
443 int* rows = tsm->mutable_rows();
444 int* cols = tsm->mutable_cols();
445 double* values = tsm->mutable_values();
446
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800447 for (int c = start_residual_block; c < residual_blocks_.size(); ++c) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800448 const ResidualBlock* residual_block = residual_blocks_[c];
449 const int num_parameter_blocks = residual_block->NumParameterBlocks();
450 ParameterBlock* const* parameter_blocks =
451 residual_block->parameter_blocks();
452
453 for (int j = 0; j < num_parameter_blocks; ++j) {
454 if (parameter_blocks[j]->IsConstant()) {
455 continue;
456 }
457
458 // Re-size the matrix if needed.
459 if (num_nonzeros >= tsm->max_num_nonzeros()) {
460 tsm->set_num_nonzeros(num_nonzeros);
461 tsm->Reserve(2 * num_nonzeros);
462 rows = tsm->mutable_rows();
463 cols = tsm->mutable_cols();
464 values = tsm->mutable_values();
465 }
466
467 const int r = parameter_blocks[j]->index();
468 rows[num_nonzeros] = r;
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800469 cols[num_nonzeros] = c - start_residual_block;
Austin Schuh70cc9552019-01-21 19:46:48 -0800470 values[num_nonzeros] = 1.0;
471 ++num_nonzeros;
472 }
473 }
474
475 tsm->set_num_nonzeros(num_nonzeros);
476 return tsm;
477}
478
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800479int Program::NumResidualBlocks() const { return residual_blocks_.size(); }
Austin Schuh70cc9552019-01-21 19:46:48 -0800480
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800481int Program::NumParameterBlocks() const { return parameter_blocks_.size(); }
Austin Schuh70cc9552019-01-21 19:46:48 -0800482
483int Program::NumResiduals() const {
484 int num_residuals = 0;
Austin Schuh3de38b02024-06-25 18:25:10 -0700485 for (auto* residual_block : residual_blocks_) {
486 num_residuals += residual_block->NumResiduals();
Austin Schuh70cc9552019-01-21 19:46:48 -0800487 }
488 return num_residuals;
489}
490
491int Program::NumParameters() const {
492 int num_parameters = 0;
Austin Schuh3de38b02024-06-25 18:25:10 -0700493 for (auto* parameter_block : parameter_blocks_) {
494 num_parameters += parameter_block->Size();
Austin Schuh70cc9552019-01-21 19:46:48 -0800495 }
496 return num_parameters;
497}
498
499int Program::NumEffectiveParameters() const {
500 int num_parameters = 0;
Austin Schuh3de38b02024-06-25 18:25:10 -0700501 for (auto* parameter_block : parameter_blocks_) {
502 num_parameters += parameter_block->TangentSize();
Austin Schuh70cc9552019-01-21 19:46:48 -0800503 }
504 return num_parameters;
505}
506
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800507// TODO(sameeragarwal): The following methods should just be updated
508// incrementally and the values cached, rather than the linear
509// complexity we have right now on every call.
Austin Schuh70cc9552019-01-21 19:46:48 -0800510int Program::MaxScratchDoublesNeededForEvaluate() const {
511 // Compute the scratch space needed for evaluate.
512 int max_scratch_bytes_for_evaluate = 0;
Austin Schuh3de38b02024-06-25 18:25:10 -0700513 for (auto* residual_block : residual_blocks_) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800514 max_scratch_bytes_for_evaluate =
Austin Schuh3de38b02024-06-25 18:25:10 -0700515 std::max(max_scratch_bytes_for_evaluate,
516 residual_block->NumScratchDoublesForEvaluate());
Austin Schuh70cc9552019-01-21 19:46:48 -0800517 }
518 return max_scratch_bytes_for_evaluate;
519}
520
521int Program::MaxDerivativesPerResidualBlock() const {
522 int max_derivatives = 0;
Austin Schuh3de38b02024-06-25 18:25:10 -0700523 for (auto* residual_block : residual_blocks_) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800524 int derivatives = 0;
Austin Schuh70cc9552019-01-21 19:46:48 -0800525 int num_parameters = residual_block->NumParameterBlocks();
526 for (int j = 0; j < num_parameters; ++j) {
527 derivatives += residual_block->NumResiduals() *
Austin Schuh3de38b02024-06-25 18:25:10 -0700528 residual_block->parameter_blocks()[j]->TangentSize();
Austin Schuh70cc9552019-01-21 19:46:48 -0800529 }
Austin Schuh3de38b02024-06-25 18:25:10 -0700530 max_derivatives = std::max(max_derivatives, derivatives);
Austin Schuh70cc9552019-01-21 19:46:48 -0800531 }
532 return max_derivatives;
533}
534
535int Program::MaxParametersPerResidualBlock() const {
536 int max_parameters = 0;
Austin Schuh3de38b02024-06-25 18:25:10 -0700537 for (auto* residual_block : residual_blocks_) {
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800538 max_parameters =
Austin Schuh3de38b02024-06-25 18:25:10 -0700539 std::max(max_parameters, residual_block->NumParameterBlocks());
Austin Schuh70cc9552019-01-21 19:46:48 -0800540 }
541 return max_parameters;
542}
543
544int Program::MaxResidualsPerResidualBlock() const {
545 int max_residuals = 0;
Austin Schuh3de38b02024-06-25 18:25:10 -0700546 for (auto* residual_block : residual_blocks_) {
547 max_residuals = std::max(max_residuals, residual_block->NumResiduals());
Austin Schuh70cc9552019-01-21 19:46:48 -0800548 }
549 return max_residuals;
550}
551
Austin Schuh3de38b02024-06-25 18:25:10 -0700552std::string Program::ToString() const {
553 std::string ret = "Program dump\n";
Austin Schuh70cc9552019-01-21 19:46:48 -0800554 ret += StringPrintf("Number of parameter blocks: %d\n", NumParameterBlocks());
555 ret += StringPrintf("Number of parameters: %d\n", NumParameters());
556 ret += "Parameters:\n";
557 for (int i = 0; i < parameter_blocks_.size(); ++i) {
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800558 ret +=
559 StringPrintf("%d: %s\n", i, parameter_blocks_[i]->ToString().c_str());
Austin Schuh70cc9552019-01-21 19:46:48 -0800560 }
561 return ret;
562}
563
Austin Schuh3de38b02024-06-25 18:25:10 -0700564} // namespace ceres::internal