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;
 
   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>
 
  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>
 
  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>
 
  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) {
 
  151        log_root_node(t_node);
 
  155    if (total_time - m_last_log_timestamp < m_frequency_in_seconds) {
 
  161    m_last_log_timestamp = total_time;
 
  164    std::cout << std::setw(log_width_explored) << parent.n_solved_nodes();
 
  165    std::cout << std::setw(log_width_open) << parent.n_active_nodes();
 
  166    std::cout << std::setw(log_width_total_time) << parent.time().count();
 
  170    std::cout << std::setw(log_width_problem_best_bound) << pretty_double(parent.get_best_bound(), double_precision);
 
  171    std::cout << std::setw(log_width_problem_best_obj) << pretty_double(parent.get_best_obj(), double_precision);
 
  172    std::cout << std::setw(log_width_problem_rel_gap) << pretty_double(parent.get_relative_gap() * 100, double_precision);
 
  173    std::cout << std::setw(log_width_problem_abs_gap) << pretty_double(parent.get_absolute_gap(), double_precision);
 
  177    std::cout << std::setw(log_width_current_node_depth) << t_node.level();
 
  178    std::cout << std::setw(log_width_current_node_status) << t_node.info().status();
 
  179    std::cout << std::setw(log_width_current_node_reason) << t_node.info().reason();
 
  180    std::cout << std::setw(log_width_current_node_obj) << pretty_double(t_node.info().objective_value(), double_precision);
 
  183    std::cout << std::endl;
 
  187template<
class NodeInfoT>
 
  190    if (!this->parent().get_param_logs()) {
 
  194    auto& branch_and_bound = this->parent();
 
  195    const double total_time = branch_and_bound.time().count();
 
  198            << 
"Root relaxation: objective " << t_node.info().objective_value()
 
  199            << 
", " << total_time << 
" seconds\n" 
  203        branch_and_bound.relaxation().optimizer().set_param_logs(
false);
 
  206    m_root_node_has_been_printed = 
true;
 
  210template<
class NodeInfoT>
 
  213    if (m_header_has_been_printed) {
 
  217    std::cout << 
' ';center(std::cout, 
"*", table_space, 
'*') << 
'\n';
 
  218    std::cout << 
' ';center(std::cout, 
" Branch-and-Bound Algorithm ", table_space) << 
'\n';
 
  219    std::cout << 
' ';center(std::cout, 
"*", table_space, 
'*') << 
'\n';
 
  222    center(std::cout, 
"Algorithm", log_section_width_algorithm);std::cout << 
" | ";
 
  223    center(std::cout, 
"Problem", log_section_width_problem);std::cout << 
" | ";
 
  224    center(std::cout, 
"Current Node", current_node_space);std::cout << 
" | ";
 
  228    std::cout << std::setw(log_width_explored) << 
"Expl.";
 
  229    std::cout << std::setw(log_width_open) << 
"Open";
 
  230    std::cout << std::setw(log_width_total_time) << 
"Time";
 
  234    std::cout << std::setw(log_width_problem_best_bound) << 
"Bound";
 
  235    std::cout << std::setw(log_width_problem_best_obj) << 
"Obj";
 
  236    std::cout << std::setw(log_width_problem_rel_gap) << 
"Gap (%)";
 
  237    std::cout << std::setw(log_width_problem_abs_gap) << 
"Abs.";
 
  241    std::cout << std::setw(log_width_current_node_depth) << 
"Depth";
 
  242    std::cout << std::setw(log_width_current_node_status) << 
"Status";
 
  243    std::cout << std::setw(log_width_current_node_reason) << 
"Reason";
 
  244    std::cout << std::setw(log_width_current_node_obj) << 
"Value";
 
  247    std::cout << std::endl;
 
  249    m_header_has_been_printed = 
true;
 
  252template<
class NodeInfoT>
 
  255    if (!this->parent().get_param_logs()) {
 
  259    Factory<NodeInfoT>::Strategy::Strategy::log_after_termination();
 
  261    auto& parent = this->parent();
 
  263    std::cout << 
"Explored " << parent.n_solved_nodes() << 
" nodes in " << parent.time().count() << 
" seconds\n" << std::endl;
 
  265    const unsigned int n_solutions = parent.get_n_solutions();
 
  267    std::cout << 
"Solution count " << n_solutions << 
":";
 
  269    for (
unsigned int i = 0 ; i < n_solutions ; ++i) {
 
  270        parent.set_solution_index(i);
 
  271        std::cout << 
'\t' << parent.get_best_obj();
 
  274    parent.set_solution_index(0);
 
  276    std::cout << std::endl;
 
  278    const auto status = parent.get_status();
 
  279    const auto reason = parent.get_reason();
 
  283            std::cout << 
"Time limit was reached." << std::endl;
 
  286            std::cout << 
"Iteration limit was reached." << std::endl;
 
  289            std::cout << 
"Objective limit was reached." << std::endl;
 
  292            std::cout << 
"There was unrecoverable numerical troubles during the execution of the algorithm." << std::endl;
 
  295            std::cout << 
"The reason for termination is not specified." << std::endl;
 
  302            std::cout << 
"Optimal solution found (tolerance " << parent.get_tol_mip_relative_gap() << 
")" << std::endl;
 
  305            std::cout << 
"Feasible solution found" << std::endl;
 
  308            std::cout << 
"No feasible solution found" << std::endl;
 
  311            std::cout << 
"Problem is infeasible or unbounded" << std::endl;
 
  314            std::cout << 
"Problem is unbounded" << std::endl;
 
  317            std::cout << 
"There was unrecoverable exceptions." << std::endl;
 
  320            std::cout << 
"Sub-optimal solution found" << std::endl;
 
  326            << 
"Best objective " << pretty_double(parent.get_best_obj(), double_precision)
 
  327            << 
", best bound " << pretty_double(parent.get_best_bound(), double_precision)
 
  328            << 
", gap " << pretty_double(parent.get_relative_gap() * 100, double_precision) << 
" %"