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, and

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
    /// Reached target cost function value
    /// Algorithm manually interrupted with Ctrl+C
    /// Converged
    /// Solver exit with given reason

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 {

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 {

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.


  • Implementing the Solver trait for a solver does not require anymore that the solver implements serde::Serialize when the serde1 feature is enabled. This was a remnant of an earlier design of the Solver 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.

Star Watch Fork Sponsor