1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
use crate::logging::rip_log;
/// Status of the solve.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SolveStatus {
/// Converged to optimal solution within tolerance.
Optimal,
/// Converged to an acceptable solution: KKT residuals within the
/// relaxed acceptable-level tolerances (matching Ipopt's
/// `Solved_To_Acceptable_Level`). Counted as "solved" by the
/// benchmark reporter — the iterate is at a stationary point that
/// Ipopt's default user-facing settings would also return.
Acceptable,
/// Problem is infeasible.
Infeasible,
/// Local infeasibility detected: constraint violation is at a stationary
/// point (gradient of violation ≈ 0) but violation is still large.
/// For NE-to-LS reformulations, this means the system is inconsistent
/// and x* is the best least-squares solution.
LocalInfeasibility,
/// Reached maximum number of iterations.
MaxIterations,
/// Numerical difficulties (e.g., singular KKT system).
NumericalError,
/// Problem appears unbounded below.
Unbounded,
/// Restoration phase failed.
RestorationFailed,
/// User callback returned `false`, indicating evaluation failure at the
/// current iterate (not during line search, where failures cause step
/// rejection instead).
EvaluationError,
/// Intermediate callback returned `false`, requesting early termination.
UserRequestedStop,
/// Internal error.
InternalError,
}
/// Structured diagnostic summary from a solve.
///
/// Captures counts of key solver events (restoration entries, barrier parameter
/// mode switches, filter rejects, etc.) and final convergence measures.
/// Useful for automated analysis and solver tuning.
#[derive(Debug, Clone, Default)]
pub struct SolverDiagnostics {
/// Number of GN (Gauss-Newton) restoration entries.
pub restoration_count: usize,
/// Number of full NLP restoration entries.
pub nlp_restoration_count: usize,
/// Number of mu mode switches (Free↔Fixed).
pub mu_mode_switches: usize,
/// Number of filter rejects (line search exhausted backtracking).
pub filter_rejects: usize,
/// Number of watchdog activations.
pub watchdog_activations: usize,
/// Number of second-order corrections (SOC) applied.
pub soc_corrections: usize,
/// Final barrier parameter mu.
pub final_mu: f64,
/// Final primal infeasibility.
pub final_primal_inf: f64,
/// Final dual infeasibility (iterative z).
pub final_dual_inf: f64,
/// Final complementarity error (iterative z).
pub final_compl: f64,
/// Dual scaling factor s_d.
pub final_s_d: f64,
/// Total wall-clock time in seconds.
pub wall_time_secs: f64,
/// Fallback strategy used, if any.
pub fallback_used: Option<String>,
}
impl SolverDiagnostics {
/// Print a structured diagnostic summary to stderr.
pub fn print_summary(&self, status: SolveStatus, iterations: usize) {
rip_log!("\n--- ripopt diagnostics ---");
rip_log!("status: {:?}", status);
rip_log!("iterations: {}", iterations);
rip_log!("wall_time: {:.3}s", self.wall_time_secs);
rip_log!("final_mu: {:.2e}", self.final_mu);
rip_log!("final_primal_inf: {:.2e}", self.final_primal_inf);
rip_log!("final_dual_inf: {:.2e}", self.final_dual_inf);
rip_log!("final_compl: {:.2e}", self.final_compl);
rip_log!("restoration_count: {}", self.restoration_count);
rip_log!("nlp_restoration_count: {}", self.nlp_restoration_count);
rip_log!("mu_mode_switches: {}", self.mu_mode_switches);
rip_log!("filter_rejects: {}", self.filter_rejects);
rip_log!("watchdog_activations: {}", self.watchdog_activations);
rip_log!("soc_corrections: {}", self.soc_corrections);
if let Some(ref fb) = self.fallback_used {
rip_log!("fallback_used: {}", fb);
}
rip_log!("--- end diagnostics ---");
}
}
/// Result of solving an NLP.
#[derive(Debug, Clone)]
pub struct SolveResult {
/// Optimal primal variables x*.
pub x: Vec<f64>,
/// Optimal objective value f(x*).
pub objective: f64,
/// Constraint multipliers (lambda).
pub constraint_multipliers: Vec<f64>,
/// Lower bound multipliers (z_L).
pub bound_multipliers_lower: Vec<f64>,
/// Upper bound multipliers (z_U).
pub bound_multipliers_upper: Vec<f64>,
/// Constraint values g(x*).
pub constraint_values: Vec<f64>,
/// Solve status.
pub status: SolveStatus,
/// Number of iterations performed.
pub iterations: usize,
/// Structured solver diagnostics.
pub diagnostics: SolverDiagnostics,
}