5#ifndef IDOL_IMPL_PSEUDOCOST_H
6#define IDOL_IMPL_PSEUDOCOST_H
9#include "VariableBranching.h"
10#include "NodeScoreFunction.h"
12namespace idol::BranchingRules {
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>
49typename idol::BranchingRules::PseudoCost<NodeInfoT>::PseudoCostData &
50idol::BranchingRules::PseudoCost<NodeInfoT>::PseudoCostData::operator+=(
51 const idol::BranchingRules::PseudoCost<NodeInfoT>::PseudoCostData &t_rhs) {
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>
62void idol::BranchingRules::PseudoCost<NodeInfoT>::on_node_solved(
const idol::Node<NodeInfoT> &t_node) {
64 BranchingRule<NodeInfoT>::on_node_solved(t_node);
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>
112idol::BranchingRules::PseudoCost<NodeInfoT>::PseudoCost(
113 const idol::Optimizers::BranchAndBound<NodeInfoT> &t_parent, std::list<Var> t_branching_candidates)
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>>
120idol::BranchingRules::PseudoCost<NodeInfoT>::scoring_function(
const std::list<idol::Var> &t_variables,
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>
146double idol::BranchingRules::PseudoCost<NodeInfoT>::compute_lower_bounding_score(
const idol::Var &t_var,
147 const PseudoCostData& t_data,
148 const idol::Node<NodeInfoT> &t_node)
const {
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>
158double idol::BranchingRules::PseudoCost<NodeInfoT>::compute_upper_bounding_score(
const idol::Var &t_var,
159 const PseudoCostData& t_data,
160 const idol::Node<NodeInfoT> &t_node)
const {
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>
170double idol::BranchingRules::PseudoCost<NodeInfoT>::compute_average_upper_bounding_score(
171 const idol::Node<NodeInfoT> &t_node)
const {
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>
194double idol::BranchingRules::PseudoCost<NodeInfoT>::compute_average_lower_bounding_score(
195 const idol::Node<NodeInfoT> &t_node)
const {
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;