simple_optimization/
lib.rs

1// TODO Avoid stupid long repeated trait requirements for `T`.
2
3//! Optimization algorithms.
4//!
5//! ## Crate status
6//!
7//! I made this since alternatives in the Rust ecosystem seemed awkward. I'm just some random dude, don't expect too much.
8//!
9//! Currently looking into adding macros `random_search!`, `grid_search!`, etc.
10//!
11//! These will allow you to optimize a tuple of ranges instead of an array, therefore allowing varying types.
12//! This is likely to come before gaussian optimization.
13//!
14//! ## Basic guide
15//!
16//! All the functions have the approximate form:
17//! ```ignore
18//! # use std::sync::Arc;
19//! fn function<A,T,const N: usize>(
20//!     // The values you want to optimise and there respective ranges.
21//!     // E.g. Optimizing 2 `f32`s between 0..1 and 2..3 `[0f32..1f32, 2f32..3f32]`).
22//!     ranges: [Range<T>; N],
23//!     // The function you want to optimize (loss/cost/whatever).
24//!     // `&[T; N]` are the input parameters which the algorithm will adjust to
25//!     //  minimize the function.
26//!     // `Option<Arc<A>>` is how you can pass additional data you might want to use.
27//!     f: fn(&[T; N], Option<Arc<A>>) -> f64,
28//!     // The additional data for the evaluation function.
29//!     evaluation_data: Option<Arc<A>>,
30//!     // Polling data, e.g. how often (if at all) you want to print progress, see `Polling`
31//!     //  struct docs for more info.
32//!     polling: Option<Polling>,
33//!     // The number of threads to use, leaving this as `None` uses all available (recommended).
34//!     //  If set it must be >=2.
35//!     threads: Option<usize>,
36//!     // ...
37//! ) -> [T;N] { /* ... */}
38//! ```
39//! The typical use case will be run with `--nocapture` (without this progress logging will not print), e.g.:
40//! - `cargo run --nocapture`
41//! - `cargo run --release -- --nocapture`
42//! - `cargo test your_test --release -- --nocapture`
43//!
44//! The most thorough output of progress might look like:
45//! ```ignore
46//!  2300
47//!   565 (24.57%) 00:00:11 / 00:00:47 [25.600657363049734] { [563.0ns, 561.3ms, 125.0ns, 110.0ns] [2.0µs, 361.8ms, 374.0ns, 405.0ns] [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] }
48//! ```
49//! This output describes:
50//!
51//! - The total number of iterations `2300`.
52//! - The total number of completed iterations `565`.
53//! - The percent of iterations completed `(24.57%)` (`565/2300=0.2457...`).
54//! - The time running `00:00:11` (`hh:mm:ss`).
55//! - The estimated time remaining `00:00:47` (`hh:mm:ss`).
56//! - The current best value `[25.600657363049734]`.
57//! - The most recently measured times between execution positions (effectively time taken for thread to go from some line, to another line (defined specifically with `update_execution_position` in the code) `[563.0ns, 561.3ms, 125.0ns, 110.0ns]`.
58//! - The averages times between execution positions (this is average across entire runtime rather than since last measured) `[2.0µs, 361.8ms, 374.0ns, 405.0ns]`.
59//! - The execution positions of threads (`0` is when a thread is completed, rest represent a thread having hit some line, which triggered this setting, but yet to hit next line which changes it, effectively being between 2 positions) (`[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]`). What these specifically refer to in code varies between functions.
60//!
61//! The last 3 of these I wouldn't expect you would ever use. But I use them for debugging this library and I think they could possibly in some rare circumstance be useful to you (so no harm having them as an option, well, only a few microseconds of harm).
62
63mod grid_search;
64mod random_search;
65mod simulated_annealing;
66mod util;
67
68#[macro_export]
69#[doc(hidden)]
70macro_rules! cpus {
71    ($threads:expr) => {{
72        // If `Some(n)` return `n` else `num_cpus::get()`.
73        let cpus = $threads.unwrap_or_else(num_cpus::get) as u64;
74        // Either way it must be at least 2.
75        assert!(
76            cpus >= 2,
77            "Due to the fundamentally multi-threaded design, we need at least 2 threads"
78        );
79        cpus
80    }};
81}
82
83pub use grid_search::*;
84pub use random_search::*;
85pub use simulated_annealing::*;
86pub use util::Polling;