Skip to main content

oximo_solver/
result.rs

1use std::time::Duration;
2
3use oximo_core::{ConstraintId, Expr, ExprNode, IndexKey, IndexedVar, VarId};
4use rustc_hash::FxHashMap;
5
6use crate::status::SolverStatus;
7
8/// A solver's final result on a model. `primal` and `dual` are sparse maps so a
9/// solver that does not return duals (e.g. MILP) can simply leave them empty.
10#[derive(Clone, Debug)]
11pub struct SolverResult {
12    pub status: SolverStatus,
13    pub objective: Option<f64>,
14    pub primal: FxHashMap<VarId, f64>,
15    pub dual: FxHashMap<ConstraintId, f64>,
16    pub reduced_costs: FxHashMap<VarId, f64>,
17    pub solve_time: Duration,
18    pub iterations: u64,
19    pub raw_log: Option<String>,
20}
21
22impl Default for SolverResult {
23    fn default() -> Self {
24        Self {
25            status: SolverStatus::NotSolved,
26            objective: None,
27            primal: FxHashMap::default(),
28            dual: FxHashMap::default(),
29            reduced_costs: FxHashMap::default(),
30            solve_time: Duration::ZERO,
31            iterations: 0,
32            raw_log: None,
33        }
34    }
35}
36
37impl SolverResult {
38    /// Look up a primal value by `VarId`.
39    pub fn value(&self, id: VarId) -> Option<f64> {
40        self.primal.get(&id).copied()
41    }
42
43    /// Look up the primal value for an `Expr` that points at a `Var` node.
44    /// Returns `None` for any expression that is not a single variable, so
45    /// callers that need the value of a derived expression should evaluate it
46    /// against the primal solution explicitly.
47    pub fn value_of(&self, expr: Expr<'_>) -> Option<f64> {
48        let arena = expr.arena.borrow();
49        match arena.get(expr.id) {
50            ExprNode::Var(v) => self.primal.get(v).copied(),
51            _ => None,
52        }
53    }
54
55    pub fn dual_of(&self, c: ConstraintId) -> Option<f64> {
56        self.dual.get(&c).copied()
57    }
58
59    /// Look up the primal value for a specific index of an [`IndexedVar`].
60    ///
61    /// Returns `None` if `key` is not in the variable's set or the solver did
62    /// not return a primal value for that scalar.
63    pub fn value_of_idx<K: Into<IndexKey>>(&self, var: &IndexedVar<'_>, key: K) -> Option<f64> {
64        var.get(key).and_then(|e| self.value_of(e))
65    }
66
67    /// Iterate over primal values for all entries of an [`IndexedVar`].
68    ///
69    /// Yields `(&IndexKey, f64)` for every index whose primal value is present
70    /// in the solution. To keep only nonzero values (common for sparse
71    /// solutions) chain `.filter(|(_, v)| *v != 0.0)`.
72    pub fn values_of<'iv, 'a>(
73        &'iv self,
74        var: &'iv IndexedVar<'a>,
75    ) -> impl Iterator<Item = (&'iv IndexKey, f64)> + 'iv {
76        var.iter().filter_map(|(k, e)| self.value_of(*e).map(|v| (k, v)))
77    }
78}