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
// TODO Avoid stupid long repeated trait requirements for `T`.

//! Optimization algorithms.
//!
//! ## Crate status
//!
//! I made this since alternatives in the Rust ecosystem seemed awkward. I'm just some random dude, don't expect too much.
//!
//! Currently looking into adding macros `random_search!`, `grid_search!`, etc.
//!
//! These will allow you to optimize a tuple of ranges instead of an array, therefore allowing varying types.
//! This is likely to come before gaussian optimization.
//!
//! ## Basic guide
//!
//! All the functions have the approximate form:
//! ```ignore
//! # use std::sync::Arc;
//! fn function<A,T,const N: usize>(
//!     // The values you want to optimise and there respective ranges.
//!     // E.g. Optimizing 2 `f32`s between 0..1 and 2..3 `[0f32..1f32, 2f32..3f32]`).
//!     ranges: [Range<T>; N],
//!     // The function you want to optimize (loss/cost/whatever).
//!     // `&[T; N]` are the input parameters which the algorithm will adjust to
//!     //  minimize the function.
//!     // `Option<Arc<A>>` is how you can pass additional data you might want to use.
//!     f: fn(&[T; N], Option<Arc<A>>) -> f64,
//!     // The additional data for the evaluation function.
//!     evaluation_data: Option<Arc<A>>,
//!     // Polling data, e.g. how often (if at all) you want to print progress, see `Polling`
//!     //  struct docs for more info.
//!     polling: Option<Polling>,
//!     // The number of threads to use, leaving this as `None` uses all available (recommended).
//!     //  If set it must be >=2.
//!     threads: Option<usize>,
//!     // ...
//! ) -> [T;N] { /* ... */}
//! ```
//! The typical use case will be run with `--nocapture` (without this progress logging will not print), e.g.:
//! - `cargo run --nocapture`
//! - `cargo run --release -- --nocapture`
//! - `cargo test your_test --release -- --nocapture`
//!
//! The most thorough output of progress might look like:
//! ```ignore
//!  2300
//!   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] }
//! ```
//! This output describes:
//!
//! - The total number of iterations `2300`.
//! - The total number of completed iterations `565`.
//! - The percent of iterations completed `(24.57%)` (`565/2300=0.2457...`).
//! - The time running `00:00:11` (`hh:mm:ss`).
//! - The estimated time remaining `00:00:47` (`hh:mm:ss`).
//! - The current best value `[25.600657363049734]`.
//! - 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]`.
//! - 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]`.
//! - 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.
//!
//! 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).

mod grid_search;
mod random_search;
mod simulated_annealing;
mod util;

#[macro_export]
#[doc(hidden)]
macro_rules! cpus {
    ($threads:expr) => {{
        // If `Some(n)` return `n` else `num_cpus::get()`.
        let cpus = $threads.unwrap_or_else(num_cpus::get) as u64;
        // Either way it must be at least 2.
        assert!(
            cpus >= 2,
            "Due to the fundamentally multi-threaded design, we need at least 2 threads"
        );
        cpus
    }};
}

pub use grid_search::*;
pub use random_search::*;
pub use simulated_annealing::*;
pub use util::Polling;