Skip to main content

forge_core/
solution.rs

1//! Optimization results shared by every algorithm.
2
3/// The best point an optimizer found, with its (minimized) objective value.
4#[derive(Debug, Clone, PartialEq)]
5pub struct Solution {
6    /// Best decision vector.
7    pub x: Vec<f64>,
8    /// Objective value at `x`, in the **minimizing** sense forge uses
9    /// internally. If the problem was wrapped in [`Maximize`], negate this to
10    /// recover the original value (or use [`Report::best_value_maximized`]).
11    ///
12    /// [`Maximize`]: crate::problem::Maximize
13    pub value: f64,
14}
15
16/// A point in a multi-objective search, with its full objective vector (all in
17/// the minimizing sense).
18#[derive(Debug, Clone, PartialEq)]
19pub struct MultiSolution {
20    /// Decision vector.
21    pub x: Vec<f64>,
22    /// Objective values at `x`, length `n_objectives`.
23    pub objectives: Vec<f64>,
24}
25
26/// The outcome of a multi-objective run: the approximated Pareto front (a set
27/// of mutually non-dominated solutions) and the evaluations spent.
28#[derive(Debug, Clone, PartialEq)]
29pub struct ParetoFront {
30    /// Non-dominated solutions found.
31    pub solutions: Vec<MultiSolution>,
32    /// Objective evaluations performed.
33    pub evaluations: usize,
34}
35
36impl ParetoFront {
37    /// Number of solutions on the front.
38    pub fn len(&self) -> usize {
39        self.solutions.len()
40    }
41
42    /// Whether the front is empty.
43    pub fn is_empty(&self) -> bool {
44        self.solutions.is_empty()
45    }
46
47    /// The objective vectors of every solution on the front.
48    pub fn objective_vectors(&self) -> impl Iterator<Item = &[f64]> {
49        self.solutions.iter().map(|s| s.objectives.as_slice())
50    }
51}
52
53/// Why an optimizer stopped.
54#[derive(Debug, Clone, Copy, PartialEq, Eq)]
55pub enum StopReason {
56    /// The evaluation budget was exhausted.
57    BudgetExhausted,
58    /// The target objective value was reached.
59    TargetReached,
60}
61
62/// Full outcome of a run: the best [`Solution`], how it stopped, and the number
63/// of objective evaluations actually spent.
64#[derive(Debug, Clone, PartialEq)]
65pub struct Report {
66    /// Best solution found.
67    pub solution: Solution,
68    /// Why the run terminated.
69    pub stop: StopReason,
70    /// Objective evaluations performed.
71    pub evaluations: usize,
72}
73
74impl Report {
75    /// Best decision vector.
76    pub fn best(&self) -> &[f64] {
77        &self.solution.x
78    }
79
80    /// Best objective in the minimizing sense (as stored).
81    pub fn best_value(&self) -> f64 {
82        self.solution.value
83    }
84
85    /// Best objective in the *maximizing* sense — the negation of
86    /// [`Report::best_value`]. Use this when the problem was wrapped in
87    /// [`Maximize`].
88    ///
89    /// [`Maximize`]: crate::problem::Maximize
90    pub fn best_value_maximized(&self) -> f64 {
91        -self.solution.value
92    }
93}