ceres_solver/lib.rs
1//! # ceres-solver-rs
2//! ## Safe Rust bindings for [Ceres Solver](http://ceres-solver.org)
3//!
4//! Solve large and small non-linear optimization problems in Rust.
5//! See [NllsProblem] for general non-linear least squares problem and
6//! [CurveFitProblem1D] for a multiparametric 1-D curve fitting.
7//!
8//! # Examples
9//!
10//! Let's solve min[(x - 2)^2] problem
11//!
12//! ```rust
13//! use ceres_solver::{CostFunctionType, NllsProblem, SolverOptions};
14//!
15//! // parameters vector consists of vector parameters, here we have a single 1-D parameter.
16//! let true_parameters = vec![vec![2.0]];
17//! let initial_parameters = vec![vec![0.0]];
18//!
19//! // You can skip type annotations in the closure definition, we use them for verbosity only.
20//! let cost: CostFunctionType = Box::new(
21//! move |parameters: &[&[f64]],
22//! residuals: &mut [f64],
23//! mut jacobians: Option<&mut [Option<&mut [&mut [f64]]>]>| {
24//! // residuals have the size of your data set, in our case it is just 1
25//! residuals[0] = parameters[0][0] - 2.0;
26//! // jacobians can be None, then you don't need to provide them
27//! if let Some(jacobians) = jacobians {
28//! // The size of the jacobians array is equal to the number of parameters,
29//! // each element is Option<&mut [&mut [f64]]>
30//! if let Some(d_dx) = &mut jacobians[0] {
31//! // Each element in the jacobians array is slice of slices:
32//! // the first index is for different residuals components,
33//! // the second index is for different components of the parameter vector
34//! d_dx[0][0] = 1.0;
35//! }
36//! }
37//! true
38//! },
39//! );
40//!
41//! let solution = NllsProblem::new()
42//! .residual_block_builder() // create a builder for residual block
43//! .set_cost(cost, 1) // 1 is the number of residuals
44//! .set_parameters(initial_parameters)
45//! .build_into_problem()
46//! .unwrap()
47//! .0 // build_into_problem returns a tuple (NllsProblem, ResidualBlockId)
48//! // You can repeat .residual_block_builder() and .build_into_problem() calls to add more
49//! // residual blocks
50//! .solve(&SolverOptions::default()) // SolverOptions can be customized
51//! .unwrap(); // Err should happen only if we added no residual blocks
52//!
53//! // Print the full solver report
54//! println!("{}", solution.summary.full_report());
55//!
56//! // The solution is a vector of parameter vectors, here we have a single 1-D parameter.
57//! assert!(f64::abs(solution.parameters[0][0] - true_parameters[0][0]) < 1e-8);
58//! ```
59//!
60//! See more details and examples in [nlls_problem] module documentation.
61//!
62//! We also provide a lighter interface for 1-D multiparameter curve fit problems via
63//! [CurveFitProblem1D]. Let's generate data points and fit them for a quadratic function.
64//!
65//! ```rust
66//! use ceres_solver::{CurveFitProblem1D, CurveFunctionType, SolverOptions};
67//!
68//! // A model to use for the cost function.
69//! fn model(
70//! x: f64,
71//! parameters: &[f64],
72//! y: &mut f64,
73//! jacobians: Option<&mut [Option<f64>]>,
74//! ) -> bool {
75//! let &[a, b, c]: &[f64; 3] = parameters.try_into().unwrap();
76//! *y = a * x.powi(2) + b * x + c;
77//! if let Some(jacobians) = jacobians {
78//! let [d_da, d_db, d_dc]: &mut [Option<f64>; 3] = jacobians.try_into().unwrap();
79//! if let Some(d_da) = d_da {
80//! *d_da = x.powi(2);
81//! }
82//! if let Some(d_db) = d_db {
83//! *d_db = x;
84//! }
85//! if let Some(d_dc) = d_dc {
86//! *d_dc = 1.0;
87//! }
88//! }
89//! true
90//! }
91//!
92//! let true_parameters = [1.0, 2.0, 3.0];
93//!
94//! // Generate data points.
95//! let x: Vec<_> = (0..100).map(|i| i as f64 * 0.01).collect();
96//! let y: Vec<_> = x
97//! .iter()
98//! .map(|&x| {
99//! let mut y = 0.0;
100//! model(x, &true_parameters, &mut y, None);
101//! // True value + "noise"
102//! y + 0.001 + f64::sin(1e6 * x)
103//! })
104//! .collect();
105//!
106//! // Wrap the model to be a cost function.
107//! let cost: CurveFunctionType = Box::new(model);
108//!
109//! // Create and solve the problem.
110//! let initial_guess = [0.0, 0.0, 0.0];
111//! let solution =
112//! CurveFitProblem1D::new(cost, &x, &y, &initial_guess).solve(&SolverOptions::default());
113//!
114//! // Print the brief report
115//! print!("{:?}", solution.summary);
116//!
117//! // Check the results.
118//! for (true_value, actual_value) in true_parameters
119//! .into_iter()
120//! .zip(solution.parameters.into_iter())
121//! {
122//! assert!(f64::abs(true_value - actual_value) < 0.1);
123//! }
124//! ```
125//!
126//! See more examples in [curve_fit::CurveFitProblem1DBuilder]'s documentation.
127
128pub use cost::CostFunctionType;
129pub use curve_fit::{CurveFitProblem1D, CurveFunctionType};
130pub use loss::{LossFunction, LossFunctionType};
131pub use nlls_problem::NllsProblem;
132pub use parameter_block::{ParameterBlock, ParameterBlockOrIndex};
133pub use solver::SolverOptions;
134
135pub mod cost;
136pub mod curve_fit;
137pub mod error;
138pub mod loss;
139pub mod nlls_problem;
140pub mod parameter_block;
141pub mod residual_block;
142pub mod solver;
143pub mod types;