oximo_pounce/
persistent.rs1use std::time::Instant;
5
6use oximo_core::{Model, ModelKind};
7use oximo_solver::{Solver, SolverError, SolverResult};
8
9use crate::options::PounceOptions;
10use crate::translate::{WarmStart, assemble, setup};
11
12#[cfg(feature = "enzyme")]
13use crate::exact as backend;
14#[cfg(not(feature = "enzyme"))]
15use crate::stable as backend;
16
17struct State {
18 oracle: backend::Oracle,
19 warm: Option<WarmStart>,
20}
21
22#[derive(Default)]
34pub struct PouncePersistent {
35 state: Option<State>,
36}
37
38impl std::fmt::Debug for PouncePersistent {
39 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40 f.debug_struct("PouncePersistent").field("resident", &self.state.is_some()).finish()
41 }
42}
43
44impl PouncePersistent {
45 #[must_use]
47 pub fn new() -> Self {
48 Self::default()
49 }
50
51 pub fn reset(&mut self) {
54 self.state = None;
55 }
56
57 fn solve_resident(
58 &mut self,
59 model: &Model,
60 opts: &PounceOptions,
61 ) -> Result<SolverResult, SolverError> {
62 let prep = setup(model, opts)?;
63 let started = Instant::now();
64
65 let state = match &mut self.state {
66 Some(state) if backend::try_reuse(&state.oracle, model) => state,
67 state => state.insert(State { oracle: backend::build(model)?, warm: None }),
68 };
69 let mut outcome = backend::run(&state.oracle, &prep, opts, state.warm.as_ref())?;
70 let elapsed = started.elapsed();
71
72 state.warm = outcome.warm.take();
73 Ok(assemble(prep.sign, outcome, elapsed))
74 }
75}
76
77impl Solver for PouncePersistent {
78 type Options = PounceOptions;
79
80 fn name(&self) -> &str {
81 "pounce"
82 }
83
84 fn supports(&self, kind: ModelKind) -> bool {
85 matches!(kind, ModelKind::LP | ModelKind::QP | ModelKind::QCP | ModelKind::NLP)
86 }
87
88 fn solve(&mut self, model: &Model, opts: &PounceOptions) -> Result<SolverResult, SolverError> {
89 match self.solve_resident(model, opts) {
90 Ok(result) => Ok(result),
91 Err(e) => {
92 self.state = None;
93 Err(e)
94 }
95 }
96 }
97}