ceres_solver/
solver.rs

1//! Structures for solver configuration and report.
2
3use crate::error::SolverOptionsBuildingError;
4use crate::residual_block::ResidualBlockId;
5
6use ceres_solver_sys::cxx::{UniquePtr, let_cxx_string};
7use ceres_solver_sys::ffi;
8pub use ceres_solver_sys::ffi::{
9    DenseLinearAlgebraLibraryType, DoglegType, DumpFormatType, LineSearchDirectionType,
10    LineSearchInterpolationType, LineSearchType, LinearSolverType, LoggingType, MinimizerType,
11    NonlinearConjugateGradientType, PreconditionerType, SparseLinearAlgebraLibraryType,
12    TrustRegionStrategyType, VisibilityClusteringType,
13};
14use std::borrow::Cow;
15use std::ffi::OsStr;
16use std::path::Path;
17use std::pin::Pin;
18
19pub struct SolverOptions(pub(crate) UniquePtr<ffi::SolverOptions>);
20
21impl SolverOptions {
22    pub fn builder() -> SolverOptionsBuilder {
23        SolverOptionsBuilder::new()
24    }
25}
26
27impl Default for SolverOptions {
28    fn default() -> Self {
29        Self::builder().build().unwrap()
30    }
31}
32
33pub struct SolverOptionsBuilder(pub(crate) UniquePtr<ffi::SolverOptions>);
34
35impl SolverOptionsBuilder {
36    pub fn new() -> Self {
37        let slf = Self(ffi::new_solver_options());
38        // Remove annoying output from ceres
39        slf.logging_type(LoggingType::SILENT)
40    }
41
42    pub fn build(self) -> Result<SolverOptions, SolverOptionsBuildingError> {
43        self.validate()?;
44        Ok(SolverOptions(self.0))
45    }
46
47    pub fn validate(&self) -> Result<(), SolverOptionsBuildingError> {
48        let_cxx_string!(msg = "");
49        if self.0.is_valid(msg.as_mut()) {
50            Ok(())
51        } else {
52            Err(SolverOptionsBuildingError::Invalid(
53                msg.to_string_lossy().into_owned(),
54            ))
55        }
56    }
57
58    pub fn is_valid(&self) -> bool {
59        self.validate().is_ok()
60    }
61
62    fn inner_mut(&mut self) -> Pin<&mut ffi::SolverOptions> {
63        self.0
64            .as_mut()
65            .expect("Underlying C++ unique_ptr<SolverOptions> must not hold nullptr")
66    }
67
68    #[inline]
69    pub fn minimizer_type(mut self, minimizer_type: MinimizerType) -> Self {
70        self.inner_mut().set_minimizer_type(minimizer_type);
71        self
72    }
73
74    #[inline]
75    pub fn line_search_direction_type(
76        mut self,
77        line_search_direction_type: LineSearchDirectionType,
78    ) -> Self {
79        self.inner_mut()
80            .set_line_search_direction_type(line_search_direction_type);
81        self
82    }
83
84    #[inline]
85    pub fn line_search_type(mut self, line_search_type: LineSearchType) -> Self {
86        self.inner_mut().set_line_search_type(line_search_type);
87        self
88    }
89
90    #[inline]
91    pub fn nonlinear_conjugate_gradient_type(
92        mut self,
93        nonlinear_conjugate_gradient_type: NonlinearConjugateGradientType,
94    ) -> Self {
95        self.inner_mut()
96            .set_nonlinear_conjugate_gradient_type(nonlinear_conjugate_gradient_type);
97        self
98    }
99
100    #[inline]
101    pub fn max_lbfgs_rank(mut self, max_rank: i32) -> Self {
102        self.inner_mut().set_max_lbfgs_rank(max_rank);
103        self
104    }
105
106    #[inline]
107    pub fn use_approximate_eigenvalue_bfgs_scaling(mut self, yes: bool) -> Self {
108        self.inner_mut()
109            .set_use_approximate_eigenvalue_bfgs_scaling(yes);
110        self
111    }
112
113    #[inline]
114    pub fn line_search_interpolation_type(
115        mut self,
116        line_search_interpolation_type: LineSearchInterpolationType,
117    ) -> Self {
118        self.inner_mut()
119            .set_line_search_interpolation_type(line_search_interpolation_type);
120        self
121    }
122
123    #[inline]
124    pub fn min_line_search_step_size(mut self, step_size: f64) -> Self {
125        self.inner_mut().set_min_line_search_step_size(step_size);
126        self
127    }
128
129    #[inline]
130    pub fn line_search_sufficient_function_decrease(mut self, sufficient_decrease: f64) -> Self {
131        self.inner_mut()
132            .set_line_search_sufficient_function_decrease(sufficient_decrease);
133        self
134    }
135
136    #[inline]
137    pub fn max_line_search_step_contraction(mut self, max_step_contraction: f64) -> Self {
138        self.inner_mut()
139            .set_max_line_search_step_contraction(max_step_contraction);
140        self
141    }
142
143    #[inline]
144    pub fn min_line_search_step_contraction(mut self, min_step_contraction: f64) -> Self {
145        self.inner_mut()
146            .set_min_line_search_step_contraction(min_step_contraction);
147        self
148    }
149
150    #[inline]
151    pub fn max_num_line_search_direction_restarts(mut self, max_num_restarts: i32) -> Self {
152        self.inner_mut()
153            .set_max_num_line_search_direction_restarts(max_num_restarts);
154        self
155    }
156
157    #[inline]
158    pub fn line_search_sufficient_curvature_decrease(
159        mut self,
160        sufficient_curvature_decrease: f64,
161    ) -> Self {
162        self.inner_mut()
163            .set_line_search_sufficient_curvature_decrease(sufficient_curvature_decrease);
164        self
165    }
166
167    #[inline]
168    pub fn max_line_search_step_expansion(mut self, max_step_expansion: f64) -> Self {
169        self.inner_mut()
170            .set_max_line_search_step_expansion(max_step_expansion);
171        self
172    }
173
174    #[inline]
175    pub fn trust_region_strategy_type(
176        mut self,
177        trust_region_strategy_type: TrustRegionStrategyType,
178    ) -> Self {
179        self.inner_mut()
180            .set_trust_region_strategy_type(trust_region_strategy_type);
181        self
182    }
183
184    #[inline]
185    pub fn dogleg_type(mut self, dogleg_type: DoglegType) -> Self {
186        self.inner_mut().set_dogleg_type(dogleg_type);
187        self
188    }
189
190    #[inline]
191    pub fn use_nonmonotonic_steps(mut self, yes: bool) -> Self {
192        self.inner_mut().set_use_nonmonotonic_steps(yes);
193        self
194    }
195
196    #[inline]
197    pub fn max_consecutive_nonmonotonic_steps(
198        mut self,
199        max_consecutive_nonmonotonic_steps: i32,
200    ) -> Self {
201        self.inner_mut()
202            .set_max_consecutive_nonmonotonic_steps(max_consecutive_nonmonotonic_steps);
203        self
204    }
205
206    #[inline]
207    pub fn max_num_iterations(mut self, max_num_iterations: i32) -> Self {
208        self.inner_mut().set_max_num_iterations(max_num_iterations);
209        self
210    }
211
212    #[inline]
213    pub fn max_solver_time_in_seconds(mut self, max_solver_time_in_seconds: f64) -> Self {
214        self.inner_mut()
215            .set_max_solver_time_in_seconds(max_solver_time_in_seconds);
216        self
217    }
218
219    #[inline]
220    pub fn num_threads(mut self, num_threads: i32) -> Self {
221        self.inner_mut().set_num_threads(num_threads);
222        self
223    }
224
225    #[inline]
226    pub fn initial_trust_region_radius(mut self, initial_trust_region_radius: f64) -> Self {
227        self.inner_mut()
228            .set_initial_trust_region_radius(initial_trust_region_radius);
229        self
230    }
231
232    #[inline]
233    pub fn max_trust_region_radius(mut self, max_trust_region_radius: f64) -> Self {
234        self.inner_mut()
235            .set_max_trust_region_radius(max_trust_region_radius);
236        self
237    }
238
239    #[inline]
240    pub fn min_trust_region_radius(mut self, min_trust_region_radius: f64) -> Self {
241        self.inner_mut()
242            .set_min_trust_region_radius(min_trust_region_radius);
243        self
244    }
245
246    #[inline]
247    pub fn min_relative_decrease(mut self, min_relative_decrease: f64) -> Self {
248        self.inner_mut()
249            .set_min_relative_decrease(min_relative_decrease);
250        self
251    }
252
253    #[inline]
254    pub fn min_lm_diagonal(mut self, min_lm_diagonal: f64) -> Self {
255        self.inner_mut().set_min_lm_diagonal(min_lm_diagonal);
256        self
257    }
258
259    #[inline]
260    pub fn max_lm_diagonal(mut self, max_lm_diagonal: f64) -> Self {
261        self.inner_mut().set_max_lm_diagonal(max_lm_diagonal);
262        self
263    }
264
265    #[inline]
266    pub fn max_num_consecutive_invalid_steps(
267        mut self,
268        max_num_consecutive_invalid_steps: i32,
269    ) -> Self {
270        self.inner_mut()
271            .set_max_num_consecutive_invalid_steps(max_num_consecutive_invalid_steps);
272        self
273    }
274
275    #[inline]
276    pub fn function_tolerance(mut self, function_tolerance: f64) -> Self {
277        self.inner_mut().set_function_tolerance(function_tolerance);
278        self
279    }
280
281    #[inline]
282    pub fn gradient_tolerance(mut self, gradient_tolerance: f64) -> Self {
283        self.inner_mut().set_gradient_tolerance(gradient_tolerance);
284        self
285    }
286
287    #[inline]
288    pub fn parameter_tolerance(mut self, parameter_tolerance: f64) -> Self {
289        self.inner_mut()
290            .set_parameter_tolerance(parameter_tolerance);
291        self
292    }
293
294    #[inline]
295    pub fn linear_solver_type(mut self, linear_solver_type: LinearSolverType) -> Self {
296        self.inner_mut().set_linear_solver_type(linear_solver_type);
297        self
298    }
299
300    #[inline]
301    pub fn preconditioner_type(mut self, preconditioner_type: PreconditionerType) -> Self {
302        self.inner_mut()
303            .set_preconditioner_type(preconditioner_type);
304        self
305    }
306
307    #[inline]
308    pub fn visibility_clustering_type(
309        mut self,
310        visibility_clustering_type: VisibilityClusteringType,
311    ) -> Self {
312        self.inner_mut()
313            .set_visibility_clustering_type(visibility_clustering_type);
314        self
315    }
316
317    #[inline]
318    pub fn residual_blocks_for_subset_preconditioner(
319        mut self,
320        residual_blocks: &[ResidualBlockId],
321    ) -> Self {
322        self.inner_mut()
323            .set_residual_blocks_for_subset_preconditioner(residual_blocks);
324        self
325    }
326
327    #[inline]
328    pub fn dense_linear_algebra_library_type(
329        mut self,
330        dense_linear_algebra_library_type: DenseLinearAlgebraLibraryType,
331    ) -> Self {
332        self.inner_mut()
333            .set_dense_linear_algebra_library_type(dense_linear_algebra_library_type);
334        self
335    }
336
337    #[inline]
338    pub fn sparse_linear_algebra_library_type(
339        mut self,
340        sparse_linear_algebra_library_type: SparseLinearAlgebraLibraryType,
341    ) -> Self {
342        self.inner_mut()
343            .set_sparse_linear_algebra_library_type(sparse_linear_algebra_library_type);
344        self
345    }
346
347    #[inline]
348    pub fn logging_type(mut self, logging_type: LoggingType) -> Self {
349        self.inner_mut().set_logging_type(logging_type);
350        self
351    }
352
353    #[inline]
354    pub fn minimizer_progress_to_stdout(mut self, yes: bool) -> Self {
355        self.inner_mut().set_minimizer_progress_to_stdout(yes);
356        self
357    }
358
359    #[inline]
360    pub fn trust_region_minimizer_iterations_to_dump(mut self, iterations_to_dump: &[i32]) -> Self {
361        self.inner_mut()
362            .set_trust_region_minimizer_iterations_to_dump(iterations_to_dump);
363        self
364    }
365
366    #[inline]
367    pub fn trust_region_problem_dump_directory(mut self, directory: impl AsRef<Path>) -> Self {
368        let os_string: &OsStr = directory.as_ref().as_ref();
369        let bytes: Cow<[u8]>;
370        #[cfg(unix)]
371        {
372            use std::os::unix::ffi::OsStrExt;
373            bytes = os_string.as_bytes().into();
374        }
375        #[cfg(target_family = "wasm")]
376        {
377            use std::os::wasi::ffi::OsStrExt;
378            bytes = os_string.as_bytes().into();
379        }
380        #[cfg(windows)]
381        {
382            use std::os::windows::ffi::OsStrExt;
383            bytes = os_string
384                .encode_wide()
385                .flat_map(|c| c.to_le_bytes().into_iter())
386                .collect::<Vec<_>>()
387                .into();
388        }
389        let_cxx_string!(cxx_string = bytes);
390        self.inner_mut()
391            .set_trust_region_problem_dump_directory(cxx_string.into_ref());
392        self
393    }
394
395    #[inline]
396    pub fn trust_region_problem_dump_format_type(
397        mut self,
398        trust_region_problem_dump_format_type: DumpFormatType,
399    ) -> Self {
400        self.inner_mut()
401            .set_trust_region_problem_dump_format_type(trust_region_problem_dump_format_type);
402        self
403    }
404
405    #[inline]
406    pub fn check_gradients(mut self, yes: bool) -> Self {
407        self.inner_mut().set_check_gradients(yes);
408        self
409    }
410
411    #[inline]
412    pub fn gradient_check_relative_precision(
413        mut self,
414        gradient_check_relative_precision: f64,
415    ) -> Self {
416        self.inner_mut()
417            .set_gradient_check_relative_precision(gradient_check_relative_precision);
418        self
419    }
420
421    #[inline]
422    pub fn gradient_check_numeric_derivative_relative_step_size(
423        mut self,
424        gradient_check_numeric_derivative_relative_step_size: f64,
425    ) -> Self {
426        self.inner_mut()
427            .set_gradient_check_numeric_derivative_relative_step_size(
428                gradient_check_numeric_derivative_relative_step_size,
429            );
430        self
431    }
432
433    #[inline]
434    pub fn update_state_every_iteration(mut self, yes: bool) -> Self {
435        self.inner_mut().set_update_state_every_iteration(yes);
436        self
437    }
438}
439
440impl Default for SolverOptionsBuilder {
441    fn default() -> Self {
442        Self::new()
443    }
444}
445
446pub struct SolverSummary(pub(crate) UniquePtr<ffi::SolverSummary>);
447
448impl SolverSummary {
449    pub fn new() -> Self {
450        Self(ffi::new_solver_summary())
451    }
452
453    fn inner(&self) -> &ffi::SolverSummary {
454        self.0
455            .as_ref()
456            .expect("Underlying C++ unique_ptr<SolverSummary> must not hold nullptr")
457    }
458
459    pub fn brief_report(&self) -> String {
460        self.inner().brief_report().to_string_lossy().into()
461    }
462
463    pub fn full_report(&self) -> String {
464        self.inner().full_report().to_string_lossy().into()
465    }
466
467    #[inline]
468    pub fn is_solution_usable(&self) -> bool {
469        self.inner().is_solution_usable()
470    }
471
472    #[inline]
473    pub fn initial_cost(&self) -> f64 {
474        self.inner().initial_cost()
475    }
476
477    #[inline]
478    pub fn final_cost(&self) -> f64 {
479        self.inner().final_cost()
480    }
481
482    #[inline]
483    pub fn fixed_cost(&self) -> f64 {
484        self.inner().fixed_cost()
485    }
486
487    #[inline]
488    pub fn num_successful_steps(&self) -> i32 {
489        self.inner().num_successful_steps()
490    }
491
492    #[inline]
493    pub fn num_unsuccessful_steps(&self) -> i32 {
494        self.inner().num_unsuccessful_steps()
495    }
496
497    #[inline]
498    pub fn num_inner_iteration_steps(&self) -> i32 {
499        self.inner().num_inner_iteration_steps()
500    }
501
502    #[inline]
503    pub fn num_line_search_steps(&self) -> i32 {
504        self.inner().num_line_search_steps()
505    }
506}
507
508impl std::fmt::Debug for SolverSummary {
509    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
510        write!(
511            f,
512            "SolverSummary {{ brief_report: {:?} }}",
513            self.brief_report()
514        )
515    }
516}
517
518impl Default for SolverSummary {
519    fn default() -> Self {
520        Self::new()
521    }
522}