21#include <gch/small_vector.hpp>
23#include "sleipnir/util/print.hpp"
24#include "sleipnir/util/time_tracer.hpp"
29enum class IterationType : uint8_t {
33 SECOND_ORDER_CORRECTION,
35 FEASIBILITY_RESTORATION
40template <
typename Rep,
typename Period = std::ratio<1>>
41constexpr double to_ms(
const std::chrono::duration<Rep, Period>& duration) {
42 using std::chrono::duration_cast;
43 using std::chrono::microseconds;
44 return duration_cast<microseconds>(duration).count() / 1e3;
51template <
typename Scalar>
52std::string power_of_10(Scalar value) {
53 if (value == Scalar(0)) {
57 int exponent =
static_cast<int>(log10(value));
61 }
else if (exponent == 1) {
65 int n = std::abs(exponent);
66 gch::small_vector<int> digits;
68 digits.emplace_back(n % 10);
72 std::string output =
"10";
78 constexpr std::array strs{
"⁰",
"¹",
"²",
"³",
"⁴",
79 "⁵",
"⁶",
"⁷",
"⁸",
"⁹"};
80 for (
const auto& digit : digits | std::views::reverse) {
81 output += strs[digit];
89#ifndef SLEIPNIR_DISABLE_DIAGNOSTICS
95template <
typename Scalar>
96void print_too_few_dofs_error(
const Eigen::VectorX<Scalar>& c_e) {
97 slp::println(
"The problem has too few degrees of freedom.");
98 slp::println(
"Violated constraints (cₑ(x) = 0) in order of declaration:");
99 for (
int row = 0; row < c_e.rows(); ++row) {
100 if (c_e[row] < Scalar(0)) {
101 slp::println(
" {}/{}: {} = 0", row + 1, c_e.rows(), c_e[row]);
106#define print_too_few_dofs_error(...)
109#ifndef SLEIPNIR_DISABLE_DIAGNOSTICS
117template <
typename Scalar>
118void print_c_e_local_infeasibility_error(
const Eigen::VectorX<Scalar>& c_e,
123 "The problem is locally infeasible due to violated equality "
125 slp::println(
"Violated constraints (cₑ(x) = 0) in order of declaration:");
126 for (
int row = 0; row < c_e.rows(); ++row) {
127 if (abs(c_e[row]) > tolerance) {
128 slp::println(
" {}/{}: {} = 0", row + 1, c_e.rows(), c_e[row]);
133#define print_c_e_local_infeasibility_error(...)
136#ifndef SLEIPNIR_DISABLE_DIAGNOSTICS
144template <
typename Scalar>
145void print_c_i_local_infeasibility_error(
const Eigen::VectorX<Scalar>& c_i,
148 "The problem is locally infeasible due to violated inequality "
150 slp::println(
"Violated constraints (cᵢ(x) ≥ 0) in order of declaration:");
151 for (
int row = 0; row < c_i.rows(); ++row) {
152 if (c_i[row] < -tolerance) {
153 slp::println(
" {}/{}: {} ≥ 0", row + 1, c_i.rows(), c_i[row]);
158#define print_c_i_local_infeasibility_error(...)
161#ifndef SLEIPNIR_DISABLE_DIAGNOSTICS
162inline void print_bound_constraint_global_infeasibility_error(
163 const std::span<
const std::pair<Eigen::Index, Eigen::Index>>
164 conflicting_lower_upper_bound_indices) {
166 "The problem is globally infeasible due to conflicting bound "
168 for (
const auto& [lower_bound_idx, upper_bound_idx] :
169 conflicting_lower_upper_bound_indices) {
171 " Inequality constraint {} gives a lower bound that is greater than "
172 "the upper bound given by inequality constraint {}",
173 lower_bound_idx, upper_bound_idx);
177#define print_bound_constraint_global_infeasibility_error(...)
180#ifndef SLEIPNIR_DISABLE_DIAGNOSTICS
189template <
typename Scalar>
190void print_initial_iterate_diagnostics(Scalar error, Scalar cost,
191 Scalar infeasibility,
192 Scalar complementarity, Scalar μ) {
193 slp::println(
"┏{:━^119}┓",
"");
195 "┃{:^4} {:^9} {:^10} {:^11} {:^10} {:^8} {:^8} {:^5} {:^5} {:^8} {:^8} "
196 "{:^8} {:^8} {:^2}┃",
197 "iter",
"duration",
"error",
"cost",
"infeas.",
"complem.",
"μ",
"δ",
"γ",
198 "|p_pr|",
"|p_du|",
"α",
"α_z",
"↩");
199 slp::println(
"┡{:━^119}┩",
"");
202 "│init {:^9} {:10.4e} {:11.4e} {:10.4e} {:8.2e} {:8.2e} {:^50}│",
"",
203 error, cost, infeasibility, complementarity, μ,
"");
206#define print_initial_iterate_diagnostics(...)
209#ifndef SLEIPNIR_DISABLE_DIAGNOSTICS
230template <
typename Scalar,
typename Rep,
typename Period = std::ratio<1>>
231void print_iterate_diagnostics(
int iterations, IterationType type,
232 const std::chrono::duration<Rep, Period>& time,
233 Scalar error, Scalar cost, Scalar infeasibility,
234 Scalar complementarity, Scalar μ, Scalar δ,
235 Scalar γ, Scalar full_primal_step_inf_norm,
236 Scalar full_dual_step_inf_norm, Scalar α,
237 Scalar α_max, Scalar α_reduction_factor,
239 if (iterations > 0 && iterations % 20 == 0) {
240 slp::println(
"┢{:━^119}┪",
"");
242 "┃{:^4} {:^9} {:^10} {:^11} {:^10} {:^8} {:^8} {:^5} {:^5} {:^8} "
243 "{:^8} {:^8} {:^8} {:^2}┃",
244 "iter",
"duration",
"error",
"cost",
"infeas.",
"complem.",
"μ",
"δ",
245 "γ",
"|p_pr|",
"|p_du|",
"α",
"α_z",
"↩");
246 slp::println(
"┡{:━^119}┩",
"");
260 int backtracks =
static_cast<int>(log(α / α_max) / log(α_reduction_factor));
262 constexpr std::array ITERATION_TYPES{
" ",
"s",
"r"};
264 "│{:4} {:1} {:9.3f} {:10.4e} {:11.4e} {:10.4e} {:8.2e} {:8.2e} {:<5} "
265 "{:<5} {:8.2e} {:8.2e} {:8.2e} {:8.2e} {:2d}│",
266 iterations, ITERATION_TYPES[std::to_underlying(type)], to_ms(time), error,
267 cost, infeasibility, complementarity, μ, power_of_10(δ), power_of_10(γ),
268 full_primal_step_inf_norm, full_dual_step_inf_norm, α, α_z, backtracks);
271#define print_iterate_diagnostics(...)
290 template <
typename Scalar>
292 [[maybe_unused]] Scalar cost,
293 [[maybe_unused]] Scalar infeasibility,
294 [[maybe_unused]] Scalar complementarity,
295 [[maybe_unused]] Scalar μ)
298 print_initial_iterate_diagnostics(error, cost, infeasibility,
306 slp::println(
"└{:─^119}┘",
"");
323std::string histogram(
double value) {
324 value = std::clamp(value, 0.0, 1.0);
327 int fpart =
static_cast<int>(std::modf(value * Width, &ipart) * 8);
329 constexpr std::array strs{
" ",
"▏",
"▎",
"▍",
"▌",
"▋",
"▊",
"▉",
"█"};
333 while (index < ipart) {
341 while (index < Width) {
349#ifndef SLEIPNIR_DISABLE_DIAGNOSTICS
355inline std::string time_trace_label(std::string_view name,
int depth) {
357 return std::string{name};
359 return std::format(
"{:{}}↳ {}",
"", 2 * (depth - 1), name);
363#ifndef SLEIPNIR_DISABLE_DIAGNOSTICS
368template <const auto& Entries>
369 requires(detail::is_valid_time_trace(Entries))
370void print_solver_diagnostics(
371 const TimeTracerTree<SolveTimeTracer, Entries>& solve_tracers) {
372 auto entries = solve_tracers.entries();
373 auto tracers = solve_tracers.tracers();
374 auto solve_duration = to_ms(tracers[0].total_duration());
376 slp::println(
"┏{:━^66}┓",
"");
377 slp::println(
"┃{:^21} {:^18} {:^10} {:^9} {:^4}┃",
"time trace",
"percentage",
378 "total",
"each",
"runs");
379 slp::println(
"┡{:━^66}┩",
"");
381 for (
size_t i = 0; i < tracers.size(); ++i) {
382 const auto& tracer = tracers[i];
383 double norm = solve_duration == 0.0
384 ? (i == 0 ? 1.0 : 0.0)
385 : to_ms(tracer.total_duration()) / solve_duration;
386 slp::println(
"│{:<21} {:>6.2f}%▕{}▏ {:>10.3f} {:>9.3f} {:>4}│",
387 time_trace_label(entries[i].name, entries[i].depth),
388 norm * 100.0, histogram<9>(norm),
389 to_ms(tracer.total_duration()),
390 to_ms(tracer.average_duration()), tracer.num_solves());
393 slp::println(
"└{:─^66}┘",
"");
396#define print_solver_diagnostics(...)
399#ifndef SLEIPNIR_DISABLE_DIAGNOSTICS
404template <const auto& Entries>
405 requires(detail::is_valid_time_trace(Entries))
406void print_setup_diagnostics(
407 const TimeTracerTree<SetupTimeTracer, Entries>& setup_tracers) {
408 auto entries = setup_tracers.entries();
409 auto tracers = setup_tracers.tracers();
410 auto setup_duration = to_ms(tracers[0].duration());
414 "See https://sleipnirgroup.github.io/Sleipnir/md_usage.html#output for "
415 "diagnostic output description.\n");
418 slp::println(
"┏{:━^50}┓",
"");
419 slp::println(
"┃{:^21} {:^18} {:^9}┃",
"time trace",
"percentage",
"duration");
420 slp::println(
"┡{:━^50}┩",
"");
423 for (
size_t i = 0; i < tracers.size(); ++i) {
424 const auto& tracer = tracers[i];
425 double norm = setup_duration == 0.0
426 ? (i == 0 ? 1.0 : 0.0)
427 : to_ms(tracer.duration()) / setup_duration;
428 slp::println(
"│{:<21} {:>6.2f}%▕{}▏ {:>9.3f}│",
429 time_trace_label(entries[i].name, entries[i].depth),
430 norm * 100.0, histogram<9>(norm), to_ms(tracer.duration()));
433 slp::println(
"└{:─^50}┘",
"");
436#define print_setup_diagnostics(...)
445template <const auto& Entries>
446 requires(detail::is_valid_time_trace(Entries))
455 : m_tracers{trace}, m_print{trace && print} {
456 m_tracers.root().start();
462 m_tracers.root().stop();
465 print_solver_diagnostics(m_tracers);
478 return m_tracers[index];
Definition print_diagnostics.hpp:279
~IterateDiagnosticsTable()
Prints the table bottom if requested.
Definition print_diagnostics.hpp:304
IterateDiagnosticsTable(bool print, Scalar error, Scalar cost, Scalar infeasibility, Scalar complementarity, Scalar μ)
Definition print_diagnostics.hpp:291
Definition print_diagnostics.hpp:447
SolveDiagnostics(bool trace, bool print)
Definition print_diagnostics.hpp:454
~SolveDiagnostics()
Definition print_diagnostics.hpp:461
SolveTimeTracer & operator[](TimeTracerIndex< Entries > index)
Definition print_diagnostics.hpp:477
Definition time_tracer.hpp:64
Definition time_tracer.hpp:321
Definition time_tracer.hpp:360