5#ifndef IDOL_BRANCHANDBOUND_INFO_H
6#define IDOL_BRANCHANDBOUND_INFO_H
9#include "idol/general/optimizers/logs.h"
11namespace idol::Logs::BranchAndBound {
12 template<
class NodeInfoT>
class Info;
15template<
class NodeInfoT =
idol::DefaultNodeInfo>
18 static constexpr auto double_precision = 5;
19 static constexpr auto n_sections = 3;
21 static constexpr auto log_width_explored = 6;
22 static constexpr auto log_width_open = 6;
23 static constexpr auto log_width_total_time = 12;
25 static constexpr auto log_width_current_node_depth = 6;
26 static constexpr auto log_width_problem_best_bound = 10;
27 static constexpr auto log_width_problem_best_obj = 10;
28 static constexpr auto log_width_problem_rel_gap = 10;
29 static constexpr auto log_width_problem_abs_gap = 10;
31 static constexpr auto log_width_current_node_status = 12;
32 static constexpr auto log_width_current_node_reason = 12;
33 static constexpr auto log_width_current_node_obj = 10;
35 static constexpr auto log_section_width_algorithm = (log_width_explored + log_width_open + log_width_total_time);
36 static constexpr auto log_section_width_problem = (log_width_problem_best_bound + log_width_problem_best_obj + log_width_problem_rel_gap + log_width_problem_abs_gap);
37 static constexpr auto current_node_space = (log_width_current_node_status + log_width_current_node_reason + log_width_current_node_obj + log_width_current_node_depth);
38 static constexpr auto table_space = log_section_width_algorithm + log_section_width_problem + current_node_space + n_sections * 3 + 1;
41 std::optional<double> m_log_frequency_in_seconds;
42 std::optional<double> m_node_logs;
44 class Strategy :
public Factory<NodeInfoT>::Strategy {
45 double m_last_log_timestamp = 0;
46 double m_frequency_in_seconds;
48 bool m_header_has_been_printed =
false;
49 bool m_root_node_has_been_printed =
false;
56 void initialize()
override;
58 void log_node_after_solve(
const Node <NodeInfoT> &t_node)
override;
62 void log_after_termination()
override;
69 Info& with_frequency_in_seconds(
double t_frequency);
71 Info& with_node_logs(
bool t_value);
74template<
class NodeInfoT>
77 if (m_node_logs.has_value()) {
78 throw Exception(
"Node logs have already been configured.");
81 m_node_logs = t_value;
86template<
class NodeInfoT>
89 if (m_log_frequency_in_seconds.has_value()) {
90 throw Exception(
"Log frequency has already been configured.");
93 m_log_frequency_in_seconds = t_frequency;
98template<
class NodeInfoT>
100 return new Strategy(t_parent,
101 m_log_frequency_in_seconds.has_value() ? m_log_frequency_in_seconds.value() : 5,
102 m_node_logs.has_value() ? m_node_logs.value() :
false);
105template<
class NodeInfoT>
107 return new Info<NodeInfoT>(*
this);
110template<
class NodeInfoT>
111idol::Logs::BranchAndBound::Info<NodeInfoT>::Strategy::Strategy(Optimizers::BranchAndBound<NodeInfoT> &t_parent,
double t_frequency_in_seconds,
bool t_node_logs)
112 : Factory<NodeInfoT>::Strategy(t_parent),
113 m_frequency_in_seconds(t_frequency_in_seconds),
114 m_node_logs(t_node_logs) {
118template<
class NodeInfoT>
119void idol::Logs::BranchAndBound::Info<NodeInfoT>::Strategy::initialize() {
121 m_last_log_timestamp = 0;
122 m_header_has_been_printed =
false;
123 m_root_node_has_been_printed =
false;
125 if (!this->parent().get_param_logs()) {
129 Factory<NodeInfoT>::Strategy::initialize();
131 std::cout <<
"Solving root node with " << this->parent().relaxation().optimizer().name() <<
"...\n" << std::endl;
133 auto& branch_and_bound = this->parent();
134 branch_and_bound.relaxation().optimizer().set_param_logs(
true);
137template<
class NodeInfoT>
138void idol::Logs::BranchAndBound::Info<NodeInfoT>::Strategy::log_node_after_solve(
const Node <NodeInfoT> &t_node) {
140 if (!this->parent().get_param_logs()) {
144 Factory<NodeInfoT>::Strategy::Strategy::log_node_after_solve(t_node);
146 const auto& parent = this->parent();
148 const double total_time = parent.time().count();
150 if (!m_root_node_has_been_printed && t_node.id() == 0) {
151 if (t_node.id() == 0) {
152 log_root_node(t_node);
157 if (t_node.id() > 0 && total_time - m_last_log_timestamp < m_frequency_in_seconds) {
163 m_last_log_timestamp = total_time;
166 std::cout << std::setw(log_width_explored) << parent.n_solved_nodes();
167 std::cout << std::setw(log_width_open) << parent.n_active_nodes();
168 std::cout << std::setw(log_width_total_time) << parent.time().count();
172 std::cout << std::setw(log_width_problem_best_bound) << pretty_double(parent.get_best_bound(), double_precision);
173 std::cout << std::setw(log_width_problem_best_obj) << pretty_double(parent.get_best_obj(), double_precision);
174 std::cout << std::setw(log_width_problem_rel_gap) << pretty_double(parent.get_relative_gap() * 100, double_precision);
175 std::cout << std::setw(log_width_problem_abs_gap) << pretty_double(parent.get_absolute_gap(), double_precision);
178 const auto value = t_node.id() == -1 ? t_node.info().best_obj() : t_node.info().best_bound();
180 std::cout << std::setw(log_width_current_node_depth) << t_node.level();
181 std::cout << std::setw(log_width_current_node_status) << t_node.info().status();
182 std::cout << std::setw(log_width_current_node_reason);
183 if (t_node.id() == -1) {
186 std::cout << t_node.info().reason();
188 std::cout << std::setw(log_width_current_node_obj) << pretty_double(value, double_precision);
191 std::cout << std::endl;
195template<
class NodeInfoT>
196void idol::Logs::BranchAndBound::Info<NodeInfoT>::Strategy::log_root_node(
const Node<NodeInfoT> &t_node) {
198 if (!this->parent().get_param_logs()) {
202 auto& branch_and_bound = this->parent();
203 const double total_time = branch_and_bound.time().count();
206 <<
"Root relaxation: objective " << t_node.info().best_bound()
207 <<
", " << total_time <<
" seconds\n"
211 branch_and_bound.relaxation().optimizer().set_param_logs(
false);
214 m_root_node_has_been_printed =
true;
218template<
class NodeInfoT>
219void idol::Logs::BranchAndBound::Info<NodeInfoT>::Strategy::log_header() {
221 if (m_header_has_been_printed) {
225 std::cout <<
' ';center(std::cout,
"*", table_space,
'*') <<
'\n';
226 std::cout <<
' ';center(std::cout,
" Branch-and-Bound Algorithm ", table_space) <<
'\n';
227 std::cout <<
' ';center(std::cout,
"*", table_space,
'*') <<
'\n';
230 center(std::cout,
"Algorithm", log_section_width_algorithm);std::cout <<
" | ";
231 center(std::cout,
"Problem", log_section_width_problem);std::cout <<
" | ";
232 center(std::cout,
"Current Node", current_node_space);std::cout <<
" | ";
236 std::cout << std::setw(log_width_explored) <<
"Expl.";
237 std::cout << std::setw(log_width_open) <<
"Open";
238 std::cout << std::setw(log_width_total_time) <<
"Time";
242 std::cout << std::setw(log_width_problem_best_bound) <<
"Bound";
243 std::cout << std::setw(log_width_problem_best_obj) <<
"Obj";
244 std::cout << std::setw(log_width_problem_rel_gap) <<
"Gap (%)";
245 std::cout << std::setw(log_width_problem_abs_gap) <<
"Abs.";
249 std::cout << std::setw(log_width_current_node_depth) <<
"Depth";
250 std::cout << std::setw(log_width_current_node_status) <<
"Status";
251 std::cout << std::setw(log_width_current_node_reason) <<
"Reason";
252 std::cout << std::setw(log_width_current_node_obj) <<
"Value";
255 std::cout << std::endl;
257 m_header_has_been_printed =
true;
260template<
class NodeInfoT>
261void idol::Logs::BranchAndBound::Info<NodeInfoT>::Strategy::log_after_termination() {
263 if (!this->parent().get_param_logs()) {
267 Factory<NodeInfoT>::Strategy::Strategy::log_after_termination();
269 auto& parent = this->parent();
271 std::cout <<
"Explored " << parent.n_solved_nodes() <<
" nodes in " << parent.time().count() <<
" seconds\n" << std::endl;
273 const unsigned int n_solutions = parent.get_n_solutions();
275 std::cout <<
"Solution count " << n_solutions <<
":";
277 for (
unsigned int i = 0 ; i < n_solutions ; ++i) {
278 parent.set_solution_index(i);
279 std::cout <<
'\t' << parent.get_best_obj();
282 parent.set_solution_index(0);
284 std::cout << std::endl;
286 const auto status = parent.get_status();
287 const auto reason = parent.get_reason();
291 std::cout <<
"Time limit was reached." << std::endl;
294 std::cout <<
"Iteration limit was reached." << std::endl;
297 std::cout <<
"Objective limit was reached." << std::endl;
300 std::cout <<
"There was unrecoverable numerical troubles during the execution of the algorithm." << std::endl;
303 std::cout <<
"The reason for termination is not specified." << std::endl;
310 std::cout <<
"Optimal solution found (tolerance " << parent.get_tol_mip_relative_gap() <<
")" << std::endl;
313 std::cout <<
"Feasible solution found" << std::endl;
316 std::cout <<
"No feasible solution found" << std::endl;
319 std::cout <<
"Problem is infeasible or unbounded" << std::endl;
322 std::cout <<
"Problem is unbounded" << std::endl;
325 std::cout <<
"There was unrecoverable exceptions." << std::endl;
328 std::cout <<
"Sub-optimal solution found" << std::endl;
334 <<
"Best objective " << pretty_double(parent.get_best_obj(), double_precision)
335 <<
", best bound " << pretty_double(parent.get_best_bound(), double_precision)
336 <<
", gap " << pretty_double(parent.get_relative_gap() * 100, double_precision) <<
" %"