1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
//! # Gomez
//!
//! A pure Rust framework and implementation of (derivative-free) methods for
//! solving nonlinear (bound-constrained) systems of equations.
//!
//! This library provides a variety of solvers of nonlinear equation systems
//! with *n* equations and *n* unknowns written entirely in Rust. Bound
//! constraints for variables are supported first-class, which is useful for
//! engineering applications. All solvers implement the same interface which is
//! designed to give full control over the process and allows to combine
//! different components to achieve the desired solution. The implemented
//! methods are historically-proven numerical methods or global optimization
//! algorithms.
//!
//! The convergence of the numerical methods is tested on several problems and
//! the implementation is benchmarked against with
//! [GSL](https://www.gnu.org/software/gsl/doc/html/multiroots.html) library.
//!
//! ## Algorithms
//!
//! * [Trust region](solver::trust_region) -- Recommended method to be used as a
//! default and it will just work in most of the cases.
//! * [Cuckoo search](solver::cuckoo) -- A global optimization algorithm useful
//! for initial guesses search in combination with a numerical solver.
//! * [Steffensen](solver::steffensen) -- Fast and lightweight method for
//! one-dimensional systems.
//! * [Nelder-Mead](solver::nelder_mead) -- Not generally recommended, but may
//! be useful for low-dimensionality problems with ill-defined Jacobian
//! matrix.
//!
//! ## Problem
//!
//! The problem of solving systems of nonlinear equations (multidimensional root
//! finding) is about finding values of *n* variables given *n* equations that
//! have to be satisfied. In our case, we consider a general algebraic form of
//! the equations (compare with specialized solvers for [systems of linear
//! equations](https://en.wikipedia.org/wiki/System_of_linear_equations)).
//!
//! Mathematically, the problem is formulated as
//!
//! ```text
//! F(x) = 0,
//!
//! where F(x) = { f1(x), ..., fn(x) }
//! and x = { x1, ..., xn }
//! ```
//!
//! Moreover, it is possible to add bound constraints to the variables. That is:
//!
//! ```text
//! Li <= xi <= Ui for some bounds [L, U] for every i
//! ```
//!
//! The bounds can be negative/positive infinity, effectively making the
//! variable unconstrained.
//!
//! More sophisticated constraints (such as (in)equalities consisting of
//! multiple variables) are currently out of the scope of this library. If you
//! are in need of those, feel free to contribute with the API design
//! incorporating them and the implementation of appropriate solvers.
//!
//! When it comes to code, the problem is any type that implements the
//! [`System`](core::System) and [`Problem`](core::Problem) traits.
//!
//! ```rust
//! // Gomez is based on `nalgebra` crate.
//! use gomez::nalgebra as na;
//! use gomez::prelude::*;
//! use na::{Dim, DimName, IsContiguous};
//!
//! // A problem is represented by a type.
//! struct Rosenbrock {
//! a: f64,
//! b: f64,
//! }
//!
//! impl Problem for Rosenbrock {
//! // The numeric type. Usually f64 or f32.
//! type Scalar = f64;
//! // The dimension of the problem. Can be either statically known or dynamic.
//! type Dim = na::U2;
//!
//! // Return the actual dimension of the system.
//! fn dim(&self) -> Self::Dim {
//! na::U2::name()
//! }
//! }
//!
//! impl System for Rosenbrock {
//! // Evaluate trial values of variables to the system.
//! fn eval<Sx, Sfx>(
//! &self,
//! x: &na::Vector<Self::Scalar, Self::Dim, Sx>,
//! fx: &mut na::Vector<Self::Scalar, Self::Dim, Sfx>,
//! ) -> Result<(), ProblemError>
//! where
//! Sx: na::storage::Storage<Self::Scalar, Self::Dim> + IsContiguous,
//! Sfx: na::storage::StorageMut<Self::Scalar, Self::Dim>,
//! {
//! // Compute the residuals of all equations.
//! fx[0] = (self.a - x[0]).powi(2);
//! fx[1] = self.b * (x[1] - x[0].powi(2)).powi(2);
//!
//! Ok(())
//! }
//! }
//! ```
//!
//! And that's it. There is no need for defining gradient vector, Hessian or
//! Jacobian matrices. The library uses [finite
//! difference](https://en.wikipedia.org/wiki/Finite_difference_method)
//! technique (usually sufficient in practice) or algorithms that are
//! derivative-free by definition.
//!
//! By default, the variables are considered unconstrained, but for constrained
//! problems it is just matter of overriding the default implementation of the
//! [`domain`](core::Problem::domain) method.
//!
//! ```rust
//! # use gomez::nalgebra as na;
//! # use gomez::prelude::*;
//! # use na::{Dim, DimName};
//! #
//! # struct Rosenbrock {
//! # a: f64,
//! # b: f64,
//! # }
//! #
//! impl Problem for Rosenbrock {
//! # type Scalar = f64;
//! # type Dim = na::U2;
//! #
//! # fn dim(&self) -> Self::Dim {
//! # na::U2::name()
//! # }
//! // ...
//!
//! fn domain(&self) -> Domain<Self::Scalar> {
//! vec![var!(-10.0, 10.0), var!(-10.0, 10.0)].into()
//! }
//! }
//! ```
//!
//! ## Solving
//!
//! When you have your system available, pick a solver of your choice and
//! iterate until the solution under certain convergence criteria is found or
//! termination limits are reached.
//!
//! **NOTE:** The following example uses low-level solver interface where you
//! are in full control of the iteration process and access to all intermediate
//! results. We plan to provide high-level drivers for convenience in the
//! future.
//!
//! ```rust
//! use gomez::nalgebra as na;
//! use gomez::prelude::*;
//! // Pick your solver.
//! use gomez::solver::TrustRegion;
//! # use na::{Dim, DimName, IsContiguous};
//! #
//! # struct Rosenbrock {
//! # a: f64,
//! # b: f64,
//! # }
//! #
//! # impl Problem for Rosenbrock {
//! # type Scalar = f64;
//! # type Dim = na::U2;
//! #
//! # fn dim(&self) -> Self::Dim {
//! # na::U2::name()
//! # }
//! # }
//! #
//! # impl System for Rosenbrock {
//! # fn eval<Sx, Sfx>(
//! # &self,
//! # x: &na::Vector<Self::Scalar, Self::Dim, Sx>,
//! # fx: &mut na::Vector<Self::Scalar, Self::Dim, Sfx>,
//! # ) -> Result<(), ProblemError>
//! # where
//! # Sx: na::storage::Storage<Self::Scalar, Self::Dim> + IsContiguous,
//! # Sfx: na::storage::StorageMut<Self::Scalar, Self::Dim>,
//! # {
//! # fx[0] = (self.a - x[0]).powi(2);
//! # fx[1] = self.b * (x[1] - x[0].powi(2)).powi(2);
//! #
//! # Ok(())
//! # }
//! # }
//!
//! let f = Rosenbrock { a: 1.0, b: 1.0 };
//! let dom = f.domain();
//!
//! let mut solver = TrustRegion::new(&f, &dom);
//!
//! // Initial guess. Good choice helps the convergence of numerical methods.
//! let mut x = na::vector![-10.0, -5.0];
//!
//! // Residuals vector.
//! let mut fx = na::vector![0.0, 0.0];
//!
//! for i in 1.. {
//! // Do one iteration in the solving process.
//! solver
//! .next(&f, &dom, &mut x, &mut fx)
//! .expect("solver encountered an error");
//!
//! println!(
//! "iter = {}\t|| fx || = {}\tx = {:?}",
//! i,
//! fx.norm(),
//! x.as_slice()
//! );
//!
//! // Check the termination criteria.
//! if fx.norm() < 1e-6 {
//! println!("solved");
//! break;
//! } else if i == 100 {
//! println!("maximum number of iteration exceeded");
//! break;
//! }
//! }
//! ```
//!
//! ## Roadmap
//!
//! Listed *not* in order of priority.
//!
//! * [Homotopy continuation
//! method](http://homepages.math.uic.edu/~jan/srvart/node4.html) to compare
//! the performance with Trust region method.
//! * Conjugate gradient method
//! * Experimentation with various global optimization techniques for initial
//! guesses search
//! * Evolutionary/nature-inspired algorithms
//! * Bayesian optimization
//! * Focus on initial guesses search and tools in general
//! * High-level drivers encapsulating the low-level API for users that do not
//! need the fine-grained control.
//!
//! ## License
//!
//! Licensed under MIT.
pub use nalgebra;
/// Gomez prelude.