5#ifndef IDOL_IMPL_PSEUDOCOST_H 
    6#define IDOL_IMPL_PSEUDOCOST_H 
    9#include "VariableBranching.h" 
   10#include "NodeScoreFunction.h" 
   12namespace idol::BranchingRules {
 
   13    template<
class NodeInfoT> 
class PseudoCost;
 
   16template<
class NodeInfoT>
 
   19    std::unique_ptr<NodeScoreFunction> m_scoring_function;
 
   21    struct PseudoCostData {
 
   22        double objective_gains_by_upper_boundings = 0;
 
   23        double objective_gains_by_lower_boundings = 0;
 
   24        unsigned int n_entries_for_upper_bounds = 0;
 
   25        unsigned int n_entries_for_lower_bounds = 0;
 
   27        PseudoCostData& operator+=(
const PseudoCostData& t_rhs);
 
   30    Map<Var, PseudoCostData> m_pseudo_cost_data;
 
   32    double compute_upper_bounding_score(
const Var& t_var, 
const PseudoCostData& t_data, 
const Node<NodeInfoT>& t_node) 
const;
 
   34    double compute_lower_bounding_score(
const Var& t_var, 
const PseudoCostData& t_data, 
const Node<NodeInfoT>& t_node) 
const;
 
   36    double compute_average_lower_bounding_score(
const Node<NodeInfoT>& t_node) 
const;
 
   38    double compute_average_upper_bounding_score(
const Node<NodeInfoT>& t_node) 
const;
 
   42    std::list<std::pair<Var, double>> scoring_function(
const std::list<Var> &t_var, 
const Node<NodeInfoT> &t_node) 
override;
 
 
   48template<
class NodeInfoT>
 
   53    objective_gains_by_upper_boundings += t_rhs.objective_gains_by_upper_boundings;
 
   54    objective_gains_by_lower_boundings += t_rhs.objective_gains_by_lower_boundings;
 
   55    n_entries_for_upper_bounds += t_rhs.n_entries_for_upper_bounds;
 
   56    n_entries_for_lower_bounds += t_rhs.n_entries_for_lower_bounds;
 
   61template<
class NodeInfoT>
 
   66    if (t_node.level() == 0) {
 
   71    const auto& parent = t_node.parent();
 
   72    const auto& parent_solution = parent.info().primal_solution();
 
   73    const double parent_objective_value = parent_solution.objective_value();
 
   76    for (
const auto &branching_decision : t_node.info().variable_branching_decisions()) {
 
   78        const auto &var = branching_decision.variable;
 
   79        const double bound = branching_decision.bound;
 
   80        const bool is_upper_bound = branching_decision.type == LessOrEqual;
 
   81        const double node_objective_value = t_node.info().objective_value();
 
   83        const double parent_var_primal_value = parent_solution.get(var);
 
   85        double objective_gain_per_unit_change = 0;
 
   86        if (!equals(bound, parent_var_primal_value, 1e-5)) {
 
   87            objective_gain_per_unit_change =
 
   88                    (node_objective_value - parent_objective_value) / std::abs(bound - parent_var_primal_value);
 
   94            data.n_entries_for_upper_bounds = 1;
 
   95            data.objective_gains_by_upper_boundings = objective_gain_per_unit_change;
 
   97            data.n_entries_for_lower_bounds = 1;
 
   98            data.objective_gains_by_lower_boundings = objective_gain_per_unit_change;
 
  101        auto [it, success] = m_pseudo_cost_data.emplace(var, data);
 
  111template<
class NodeInfoT>
 
  114        : VariableBranching<NodeInfoT>(t_parent, std::move(t_branching_candidates)),
 
  115          m_scoring_function(new NodeScoreFunctions::Linear())
 
  118template<
class NodeInfoT>
 
  119std::list<std::pair<idol::Var, double>>
 
  121                                                                  const Node<NodeInfoT> &t_node) {
 
  123    std::list<std::pair<idol::Var, double>> result;
 
  125    const double average_upper_bound_score = compute_average_upper_bounding_score(t_node);
 
  126    const double average_lower_bound_score = compute_average_lower_bounding_score(t_node);
 
  127    const auto end = m_pseudo_cost_data.end();
 
  129    for (
const auto& var : t_variables) {
 
  131        const auto it = m_pseudo_cost_data.find(var);
 
  133        const auto upper_bounding_score = it != end && it->second.n_entries_for_upper_bounds > 0 ? compute_upper_bounding_score(it->first, it->second, t_node) : average_upper_bound_score;
 
  134        const auto lower_bounding_score = it != end && it->second.n_entries_for_lower_bounds > 0 ? compute_lower_bounding_score(it->first, it->second, t_node) : average_lower_bound_score;
 
  136        const double pseudo_cost = (*m_scoring_function)(upper_bounding_score,lower_bounding_score);
 
  138        result.emplace_back(var, pseudo_cost);
 
  145template<
class NodeInfoT>
 
  147                                                                              const PseudoCostData& t_data,
 
  150    const double sum_of_objective_gains_per_unit_change = t_data.objective_gains_by_lower_boundings;
 
  151    const unsigned int n_entries = t_data.n_entries_for_lower_bounds;
 
  152    const double var_value = t_node.info().primal_solution().get(t_var);
 
  154    return (std::ceil(var_value) - var_value) * sum_of_objective_gains_per_unit_change / (double) n_entries;
 
  157template<
class NodeInfoT>
 
  159                                                                              const PseudoCostData& t_data,
 
  162    const double sum_of_objective_gains_per_unit_change = t_data.objective_gains_by_upper_boundings;
 
  163    const unsigned int n_entries = t_data.n_entries_for_upper_bounds;
 
  164    const double var_value = t_node.info().primal_solution().get(t_var);
 
  166    return (var_value - std::floor(var_value)) * sum_of_objective_gains_per_unit_change / (double) n_entries;
 
  169template<
class NodeInfoT>
 
  176    for (
const auto& [var, data] : m_pseudo_cost_data) {
 
  178        if (data.n_entries_for_upper_bounds == 0) {
 
  182        sum += compute_upper_bounding_score(var, data, t_node);
 
  190    return sum / (double) n;
 
  193template<
class NodeInfoT>
 
  200    for (
const auto& [var, data] : m_pseudo_cost_data) {
 
  202        if (data.n_entries_for_lower_bounds == 0) {
 
  206        sum += compute_lower_bounding_score(var, data, t_node);
 
  214    return sum / (double) n;