1use crate::ipopt_cq::IpoptCqHandle;
17use crate::ipopt_data::IpoptDataHandle;
18use crate::output::r#trait::IterationOutput;
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum PrintInfoString {
22 Yes,
23 No,
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum InfPrTag {
28 Internal,
29 Original,
30}
31
32pub struct OrigIterationOutput {
33 pub print_info_string: PrintInfoString,
34 pub inf_pr_output: InfPrTag,
35 pub print_frequency_iter: i32,
36 pub print_frequency_time: f64,
37 last_header_iter: i32,
40}
41
42impl Default for OrigIterationOutput {
43 fn default() -> Self {
44 Self {
45 print_info_string: PrintInfoString::No,
46 inf_pr_output: InfPrTag::Original,
47 print_frequency_iter: 1,
48 print_frequency_time: 0.0,
49 last_header_iter: -1,
50 }
51 }
52}
53
54impl OrigIterationOutput {
55 pub fn new() -> Self {
56 Self::default()
57 }
58
59 pub const HEADER: &'static str =
63 "iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls\n";
64}
65
66impl IterationOutput for OrigIterationOutput {
67 fn write_output(&mut self) {
68 self.last_header_iter = 0;
71 }
72
73 fn format_row(&mut self, data: &IpoptDataHandle, cq: &IpoptCqHandle) -> String {
77 let d = data.borrow();
78 let c = cq.borrow();
79
80 let iter = d.iter_count;
81 let (unscaled_f, inf_pr) = self.objective_inf_pr(&c);
82 let inf_du = c.curr_dual_infeasibility_max();
83 let mu = d.curr_mu;
84 let lg_mu = mu.log10();
85
86 let dnrm = match &d.delta {
88 Some(delta) => delta.x.amax().max(delta.s.amax()),
89 None => 0.0,
90 };
91
92 let regu_x = d.info_regu_x;
93 let regu_str: String = if regu_x == 0.0 {
94 " -".to_string()
95 } else {
96 format!("{:6.1}", regu_x.log10())
97 };
98
99 let alpha_dual = d.info_alpha_dual;
100 let alpha_primal = d.info_alpha_primal;
101 let alpha_char = d.info_alpha_primal_char;
102 let ls_count = d.info_ls_count;
103
104 let mut row = format!(
105 "{:>4} {:>14} {:>8} {:>8} {:6.1} {:>8} {:>6} {:>8} {:>8}{}{:>3}",
106 iter,
107 format_e(unscaled_f, 7),
108 format_e(inf_pr, 2),
109 format_e(inf_du, 2),
110 lg_mu,
111 format_e(dnrm, 2),
112 regu_str,
113 format_e(alpha_dual, 2),
114 format_e(alpha_primal, 2),
115 alpha_char,
116 ls_count,
117 );
118 if self.print_info_string == PrintInfoString::Yes && !d.info_string.is_empty() {
125 row.push(' ');
126 row.push_str(&d.info_string);
127 }
128 row
129 }
130
131 fn printed_objective_inf_pr(
132 &mut self,
133 _data: &IpoptDataHandle,
134 cq: &IpoptCqHandle,
135 ) -> Option<(f64, f64)> {
136 Some(self.objective_inf_pr(&cq.borrow()))
137 }
138}
139
140impl OrigIterationOutput {
141 fn objective_inf_pr(&self, c: &crate::ipopt_cq::IpoptCalculatedQuantities) -> (f64, f64) {
144 let inf_pr = match self.inf_pr_output {
145 InfPrTag::Internal => c.curr_primal_infeasibility_max(),
146 InfPrTag::Original => c.curr_unscaled_nlp_constraint_violation_max(),
147 };
148 (c.unscaled_curr_f(), inf_pr)
149 }
150}
151
152pub(crate) fn format_e(x: f64, precision: usize) -> String {
163 if !x.is_finite() {
164 return format!("{}", x);
165 }
166 let s = format!("{:.*e}", precision, x);
167 let (mantissa, exp) = match s.split_once('e') {
168 Some(pair) => pair,
169 None => return s,
170 };
171 let (sign, digits) = match exp.strip_prefix('-') {
172 Some(rest) => ('-', rest),
173 None => ('+', exp),
174 };
175 if digits.len() == 1 {
176 format!("{}e{}0{}", mantissa, sign, digits)
177 } else {
178 format!("{}e{}{}", mantissa, sign, digits)
179 }
180}
181
182#[cfg(test)]
183mod tests {
184 use super::*;
185
186 #[test]
187 fn header_layout_right_aligns_each_label() {
188 assert_eq!(OrigIterationOutput::HEADER.len(), 83); let h = OrigIterationOutput::HEADER.trim_end_matches('\n');
194 assert!(h.ends_with("ls"), "h = {h:?}");
195 assert_eq!(&h[10..19], "objective");
196 assert_eq!(&h[22..28], "inf_pr");
197 assert_eq!(&h[61..69], "alpha_du");
198 assert_eq!(&h[70..78], "alpha_pr");
199 }
200
201 #[test]
202 fn format_e_pads_short_exponents() {
203 assert_eq!(format_e(0.0, 2), "0.00e+00");
204 assert_eq!(format_e(1.0, 2), "1.00e+00");
205 assert_eq!(format_e(0.178, 2), "1.78e-01");
206 assert_eq!(format_e(8.83e-13, 2), "8.83e-13");
207 assert_eq!(format_e(7.74, 2), "7.74e+00");
208 assert_eq!(format_e(1.0e10, 2), "1.00e+10");
210 }
211
212 #[test]
213 fn format_e_passes_through_non_finite() {
214 assert_eq!(format_e(f64::NAN, 2), "NaN");
215 assert_eq!(format_e(f64::INFINITY, 2), "inf");
216 }
217}