idol
A C++ Framework for Optimization
Loading...
Searching...
No Matches
PseudoCost.h
1//
2// Created by henri on 18.10.23.
3//
4
5#ifndef IDOL_IMPL_PSEUDOCOST_H
6#define IDOL_IMPL_PSEUDOCOST_H
7
8#include <cmath>
9#include "VariableBranching.h"
10#include "NodeScoreFunction.h"
11
12namespace idol::BranchingRules {
13 template<class NodeInfoT> class PseudoCost;
14}
15
16template<class NodeInfoT>
18
19 std::unique_ptr<NodeScoreFunction> m_scoring_function;
20
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;
26
27 PseudoCostData& operator+=(const PseudoCostData& t_rhs);
28 };
29
30 Map<Var, PseudoCostData> m_pseudo_cost_data;
31
32 double compute_upper_bounding_score(const Var& t_var, const PseudoCostData& t_data, const Node<NodeInfoT>& t_node) const;
33
34 double compute_lower_bounding_score(const Var& t_var, const PseudoCostData& t_data, const Node<NodeInfoT>& t_node) const;
35
36 double compute_average_lower_bounding_score(const Node<NodeInfoT>& t_node) const;
37
38 double compute_average_upper_bounding_score(const Node<NodeInfoT>& t_node) const;
39public:
40 explicit PseudoCost(const Optimizers::BranchAndBound<NodeInfoT>& t_parent, std::list<Var> t_branching_candidates);
41
42 std::list<std::pair<Var, double>> scoring_function(const std::list<Var> &t_var, const Node<NodeInfoT> &t_node) override;
43
44 void on_node_solved(const Node<NodeInfoT> &t_node) override;
45
46};
47
48template<class NodeInfoT>
52
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;
57
58 return *this;
59}
60
61template<class NodeInfoT>
63
65
66 if (t_node.level() == 0) {
67 return;
68 }
69
70 // Parent
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();
74
75 // Current node
76 for (const auto &branching_decision : t_node.info().variable_branching_decisions()) {
77
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();
82
83 const double parent_var_primal_value = parent_solution.get(var);
84
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);
89 }
90
91 PseudoCostData data;
92
93 if (is_upper_bound) {
94 data.n_entries_for_upper_bounds = 1;
95 data.objective_gains_by_upper_boundings = objective_gain_per_unit_change;
96 } else {
97 data.n_entries_for_lower_bounds = 1;
98 data.objective_gains_by_lower_boundings = objective_gain_per_unit_change;
99 }
100
101 auto [it, success] = m_pseudo_cost_data.emplace(var, data);
102
103 if (!success) {
104 it->second += data;
105 }
106
107 }
108
109}
110
111template<class NodeInfoT>
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())
116{}
117
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) {
122
123 std::list<std::pair<idol::Var, double>> result;
124
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();
128
129 for (const auto& var : t_variables) {
130
131 const auto it = m_pseudo_cost_data.find(var);
132
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;
135
136 const double pseudo_cost = (*m_scoring_function)(upper_bounding_score,lower_bounding_score);
137
138 result.emplace_back(var, pseudo_cost);
139
140 }
141
142 return result;
143}
144
145template<class NodeInfoT>
147 const PseudoCostData& t_data,
148 const idol::Node<NodeInfoT> &t_node) const {
149
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);
153
154 return (std::ceil(var_value) - var_value) * sum_of_objective_gains_per_unit_change / (double) n_entries;
155}
156
157template<class NodeInfoT>
159 const PseudoCostData& t_data,
160 const idol::Node<NodeInfoT> &t_node) const {
161
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);
165
166 return (var_value - std::floor(var_value)) * sum_of_objective_gains_per_unit_change / (double) n_entries;
167}
168
169template<class NodeInfoT>
171 const idol::Node<NodeInfoT> &t_node) const {
172
173 double sum = 0.;
174 unsigned int n = 0;
175
176 for (const auto& [var, data] : m_pseudo_cost_data) {
177
178 if (data.n_entries_for_upper_bounds == 0) {
179 continue;
180 }
181
182 sum += compute_upper_bounding_score(var, data, t_node);
183 n += 1;
184 }
185
186 if (n == 0) {
187 return 0.;
188 }
189
190 return sum / (double) n;
191}
192
193template<class NodeInfoT>
195 const idol::Node<NodeInfoT> &t_node) const {
196
197 double sum = 0.;
198 unsigned int n = 0;
199
200 for (const auto& [var, data] : m_pseudo_cost_data) {
201
202 if (data.n_entries_for_lower_bounds == 0) {
203 continue;
204 }
205
206 sum += compute_lower_bounding_score(var, data, t_node);
207 n += 1;
208 }
209
210 if (n == 0) {
211 return 0.;
212 }
213
214 return sum / (double) n;
215}
216
217#endif //IDOL_IMPL_PSEUDOCOST_H