argmin 0.8.0 and argmin-math 0.3.0 released
Posted January 28, 2023 by Stefan Kroboth ‐ 6 min read
argmin is a Rust library which offers a range of numerical optimization methods and a framework for developing optimization algorithms. argmin-math is a trait-based abstraction layer for mathematical operations, which makes argmin compatible with various math backends such as ndarray and nalgebra (or your own backend). For details about the design of argmin and its features I suggest having a look at the website, the book, Github, crates.io and lib.rs.
This is a short summary of the changes in argmin 0.8.0 and argmin-math 0.3.0. Both releases include breaking API changes; however upgrading from the previous versions should hopefully be fairly smooth. Don't hesitate to get in touch in case you run into problems during upgrading.
argmin 0.8.0
Improved termination handling
The solver state contains a TerminationReason
enum which indicates why the solver terminated. The enum variants offered in previous versions of argmin were either applicable to all solvers or were solver-specific. @relf rightfully pointed out that these variants weren't ideal: For instance, it included an awkward variant NotTerminated
and a couple of variants could be summarized as Converged
. Whenever a Ctrl+C
was intercepted, the reason would be Aborted
, which is suboptimal because a solver could also abort due to other reasons.
In the discussion we decided to change the TerminationReason
enum to the following:
pub enum TerminationReason {
/// Reached maximum number of iterations
MaxItersReached,
/// Reached target cost function value
TargetCostReached,
/// Algorithm manually interrupted with Ctrl+C
KeyboardInterrupt,
/// Converged
SolverConverged,
/// Solver exit with given reason
SolverExit(String),
}
The first two are potential outcomes of two checks performed by the Executor
for every solver. KeyboardInterrupt
replaces Aborted
and is only used for Ctrl+C
. SolverConverged
indicates a successful optimization run and SolverExit(String)
is used for cases where the solver stopped for a solver-specific reason specified by a string.
The awkward NotTerminated
was dropped and instead a new enum TerminationStatus
is introduced:
enum TerminationStatus {
NotTerminated,
Terminated(TerminationReason)
}
This enum is stored in the state instead of TerminationReason
. It can be obtained from an OptimizationResult
via the solver state:
// of type &TerminationStatus
let status = result.state().get_termination_status();
// ... or ...
// of type Option<&TerminationReason>
let reason = result.state().get_termination_reason();
Both methods are part of the State
trait and as such available for all available states.
Note that this is a breaking change and as such you may have to adapt your code, in particular if you use the returned termination reason.
Huge thanks to @relf for the fruitful discussion and for doing all the heavy lifting!
Changes to the observer interface
In the past, values sent to the observers were only dyn Display
, which means they could effectively only be turned into strings. To get the actual value one had to parse the strings into a given type, which isn't great (to put it mildly).
Therefore I decided to make the values sent to the observers typed and added support for 64bit floats, signed and unsigned 64bit integer, Booleans and Strings via the KvValue
enum:
pub enum KvValue {
Float(f64),
Int(i64),
Uint(u64),
Bool(bool),
Str(String),
}
The actual values can be retrieved via the getters get_float
, get_int
, get_uint
, get_bool
and get_string
, which return Some(<inner_value>)
if the KvValue
is of the appropriate type and None
otherwise.
The make_kv!
macro used in solvers to construct the Key-Value store was renamed to kv!
.
These changes will only affect you if you wrote an observer or solver yourself. All observers shipped with argmin should continue to work as before.
Other
- Implementing the
Solver
trait for a solver does not require anymore that the solver implementsserde::Serialize
when theserde1
feature is enabled. This was a remnant of an earlier design of theSolver
trait found by @relf. Note that checkpointing requires solvers to be serializable! Therefore, despite lifting this requirement, it is still recommended that solvers are (de)serializable if possible. - The check whether an optional target cost is reached is now based on the best cost function value so far rather than the current cost function value (@relf)
- Added a
full
feature which activates all features. - Added a particle swarm optimization and L-BFGS example using the nalgebra backend.
- Elapsed time is now computed using
as_secs_f64
(@TheIronBorn) - Internally uses argmin-math 0.3.0, so make sure to update both!
argmin-math 0.3.0
With this release all backends finally implement all math-related traits, meaning that every backend now works with every solver. The only exception to this is the vec
backend, which does not implement ArgminInv
and as such does not work with (Gauss-)Newton methods. I've spent multiple days tediously implementing all missing traits and adding tests for every implementation (eventually reaching 100% test coverage). @hypotrochoid implemented the ArgminRandom
trait for the ndarray backend and with that kicked off this entire endeavour. Thanks!
Apart from that support for nalgebra 0.32 was added and ndarray-linalg was updated from version 0.14 to 0.16 for the ndarray v0.15 backend. Therefore you may have to update ndarray-linalg as well.
Upgrading to version 0.3.0 of argmin-math should be smooth for most cases.
Other news
argmin is not only a collection of optimization algorithms but also aims to be a framework which facilitates the development of optimization algorithms. Solvers implemented using argmin get features such as checkpointing, observers and support for various math backends for free.
However, I have not seen any use of this feature outside of argmin itself up until recently, when @relf made his egobox-ego solver compatible with argmin. This is very exciting for me as this led to valuable feedback on the design of argmin and I hope that others will follow that example and make their solvers compatible with argmin.