Skip to main content

pounce_cli/
seeded_tnlp.rs

1//! TNLP wrapper that overrides the starting point with a seed captured
2//! by the interactive debugger, so `resolve` can re-run the solve from
3//! the current iterate with new options (a primal warm start).
4//!
5//! Every method forwards to the inner TNLP exactly like
6//! [`crate::counting_tnlp::CountingTnlp`]; only [`TNLP::get_starting_point`]
7//! is overridden. The seed is applied **only when its length matches the
8//! starting-point buffer** — if presolve or fixed-variable elimination
9//! changed the coordinate count, we fall back to the problem's own start
10//! rather than seed into the wrong space.
11
12use pounce_common::types::{Index, Number};
13use pounce_nlp::tnlp::{
14    BoundsInfo, IpoptCq, IpoptData, IterStats, MetaData, NlpInfo, ScalingRequest, Solution,
15    SparsityRequest, StartingPoint, TNLP,
16};
17use std::cell::RefCell;
18use std::rc::Rc;
19
20pub struct SeededTnlp {
21    inner: Rc<RefCell<dyn TNLP>>,
22    seed_x: Vec<Number>,
23}
24
25impl SeededTnlp {
26    pub fn new(inner: Rc<RefCell<dyn TNLP>>, seed_x: Vec<Number>) -> Self {
27        Self { inner, seed_x }
28    }
29}
30
31impl TNLP for SeededTnlp {
32    fn get_nlp_info(&mut self) -> Option<NlpInfo> {
33        self.inner.borrow_mut().get_nlp_info()
34    }
35    fn get_bounds_info(&mut self, b: BoundsInfo<'_>) -> bool {
36        self.inner.borrow_mut().get_bounds_info(b)
37    }
38    fn get_starting_point(&mut self, sp: StartingPoint<'_>) -> bool {
39        if self.seed_x.len() == sp.x.len() {
40            // `init_x` / `init_z` / `init_lambda` are inputs (which
41            // buffers the solver wants); for the initial point the solver
42            // always reads `x`, so writing the primal seed here suffices.
43            // We don't have warm duals, so we leave z/lambda untouched.
44            sp.x.copy_from_slice(&self.seed_x);
45            true
46        } else {
47            // Coordinate count changed (presolve / fixed-variable
48            // elimination); fall back to the problem's own start.
49            self.inner.borrow_mut().get_starting_point(sp)
50        }
51    }
52    fn eval_f(&mut self, x: &[Number], new_x: bool) -> Option<Number> {
53        self.inner.borrow_mut().eval_f(x, new_x)
54    }
55    fn eval_grad_f(&mut self, x: &[Number], new_x: bool, grad_f: &mut [Number]) -> bool {
56        self.inner.borrow_mut().eval_grad_f(x, new_x, grad_f)
57    }
58    fn eval_g(&mut self, x: &[Number], new_x: bool, g: &mut [Number]) -> bool {
59        self.inner.borrow_mut().eval_g(x, new_x, g)
60    }
61    fn eval_jac_g(&mut self, x: Option<&[Number]>, new_x: bool, mode: SparsityRequest<'_>) -> bool {
62        self.inner.borrow_mut().eval_jac_g(x, new_x, mode)
63    }
64    fn eval_h(
65        &mut self,
66        x: Option<&[Number]>,
67        new_x: bool,
68        obj_factor: Number,
69        lambda: Option<&[Number]>,
70        new_lambda: bool,
71        mode: SparsityRequest<'_>,
72    ) -> bool {
73        self.inner
74            .borrow_mut()
75            .eval_h(x, new_x, obj_factor, lambda, new_lambda, mode)
76    }
77    fn finalize_solution(&mut self, sol: Solution<'_>, ip_data: &IpoptData, ip_cq: &IpoptCq) {
78        self.inner
79            .borrow_mut()
80            .finalize_solution(sol, ip_data, ip_cq)
81    }
82    fn get_var_con_metadata(&mut self, var: &mut MetaData, con: &mut MetaData) -> bool {
83        self.inner.borrow_mut().get_var_con_metadata(var, con)
84    }
85    fn get_scaling_parameters(&mut self, req: ScalingRequest<'_>) -> bool {
86        self.inner.borrow_mut().get_scaling_parameters(req)
87    }
88    fn get_number_of_nonlinear_variables(&mut self) -> Index {
89        self.inner.borrow_mut().get_number_of_nonlinear_variables()
90    }
91    fn get_list_of_nonlinear_variables(&mut self, pos: &mut [Index]) -> bool {
92        self.inner.borrow_mut().get_list_of_nonlinear_variables(pos)
93    }
94    fn intermediate_callback(
95        &mut self,
96        stats: IterStats,
97        ip_data: &IpoptData,
98        ip_cq: &IpoptCq,
99    ) -> bool {
100        self.inner
101            .borrow_mut()
102            .intermediate_callback(stats, ip_data, ip_cq)
103    }
104    fn finalize_metadata(&mut self, var: &MetaData, con: &MetaData) {
105        self.inner.borrow_mut().finalize_metadata(var, con)
106    }
107}