Skip to main content

ripopt/
intermediate.rs

1//! Intermediate callback infrastructure for ripopt.
2//!
3//! Provides a per-iteration callback mechanism matching IPOPT's
4//! `SetIntermediateCallback`. The callback is stored in a thread-local
5//! (same pattern as logging) and invoked after each iteration in the IPM loop.
6//!
7//! Also provides thread-local storage for the current iterate, so that
8//! `ripopt_get_current_iterate` and `ripopt_get_current_violations` can
9//! retrieve solver state during the intermediate callback (matching Ipopt's
10//! `GetIpoptCurrentIterate` and `GetIpoptCurrentViolations`).
11
12use std::cell::Cell;
13use std::cell::RefCell;
14use std::os::raw::c_void;
15
16use crate::c_api::IntermediateCb;
17
18thread_local! {
19    static INTERMEDIATE_CALLBACK: Cell<Option<(IntermediateCb, *mut c_void)>> = Cell::new(None);
20    static CURRENT_ITERATE: RefCell<Option<IterateSnapshot>> = RefCell::new(None);
21}
22
23/// Snapshot of solver state, stored during the intermediate callback so that
24/// GetCurrentIterate/GetCurrentViolations can read it.
25pub struct IterateSnapshot {
26    pub x: Vec<f64>,
27    pub z_l: Vec<f64>,
28    pub z_u: Vec<f64>,
29    pub g: Vec<f64>,
30    pub lambda: Vec<f64>,
31    // Violations
32    pub x_l_violation: Vec<f64>,
33    pub x_u_violation: Vec<f64>,
34    pub compl_x_l: Vec<f64>,
35    pub compl_x_u: Vec<f64>,
36    pub grad_lag_x: Vec<f64>,
37    pub constraint_violation: Vec<f64>,
38    pub compl_g: Vec<f64>,
39}
40
41/// Install an intermediate callback for the current thread.
42/// Pass `None` to clear.
43pub fn set_intermediate_callback(cb: Option<(IntermediateCb, *mut c_void)>) {
44    INTERMEDIATE_CALLBACK.with(|cell| cell.set(cb));
45}
46
47/// Store a snapshot of the current iterate for GetCurrentIterate/Violations access.
48pub fn set_current_iterate(snapshot: Option<IterateSnapshot>) {
49    CURRENT_ITERATE.with(|cell| {
50        *cell.borrow_mut() = snapshot;
51    });
52}
53
54/// Access the current iterate snapshot (only valid during intermediate callback).
55pub fn with_current_iterate<F, R>(f: F) -> Option<R>
56where
57    F: FnOnce(&IterateSnapshot) -> R,
58{
59    CURRENT_ITERATE.with(|cell| {
60        cell.borrow().as_ref().map(f)
61    })
62}
63
64/// Invoke the intermediate callback with current iteration data.
65/// Signature matches Ipopt's Intermediate_CB parameters.
66/// Returns `true` to continue, `false` to stop (user requested termination).
67pub fn invoke_intermediate(
68    alg_mod: i32,
69    iter: usize,
70    obj_value: f64,
71    inf_pr: f64,
72    inf_du: f64,
73    mu: f64,
74    d_norm: f64,
75    regularization_size: f64,
76    alpha_du: f64,
77    alpha_pr: f64,
78    ls_trials: usize,
79) -> bool {
80    INTERMEDIATE_CALLBACK.with(|cell| {
81        if let Some((cb, user_data)) = cell.get() {
82            let result = unsafe {
83                cb(
84                    alg_mod,
85                    iter as i32,
86                    obj_value,
87                    inf_pr,
88                    inf_du,
89                    mu,
90                    d_norm,
91                    regularization_size,
92                    alpha_du,
93                    alpha_pr,
94                    ls_trials as i32,
95                    user_data,
96                )
97            };
98            result != 0
99        } else {
100            true // no callback, continue
101        }
102    })
103}