1use 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 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}