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}