5#ifndef IDOL_OPTIMIZERS_BRANCHANDBOUND_H
6#define IDOL_OPTIMIZERS_BRANCHANDBOUND_H
8#include "idol/mixed-integer/optimizers/branch-and-bound/nodes/NodeSet.h"
9#include "idol/general/optimizers/Algorithm.h"
10#include "idol/mixed-integer/optimizers/branch-and-bound/branching-rules/factories/BranchingRuleFactory.h"
11#include "idol/mixed-integer/optimizers/branch-and-bound/node-selection-rules/factories/NodeSelectionRuleFactory.h"
12#include "idol/mixed-integer/optimizers/branch-and-bound/branching-rules/impls/BranchingRule.h"
13#include "idol/mixed-integer/optimizers/branch-and-bound/node-selection-rules/impls/NodeSelectionRule.h"
14#include "idol/mixed-integer/optimizers/branch-and-bound/nodes/Node.h"
15#include "idol/mixed-integer/optimizers/branch-and-bound/nodes/NodeUpdator.h"
16#include "idol/mixed-integer/optimizers/branch-and-bound/callbacks/AbstractBranchAndBoundCallbackI.h"
17#include "idol/general/optimizers/OptimizerFactory.h"
18#include "idol/mixed-integer/modeling/models/Model.h"
19#include "idol/mixed-integer/optimizers/branch-and-bound/logs/Factory.h"
25namespace idol::Optimizers {
29template<
class NodeInfoT>
34 std::unique_ptr<OptimizerFactory> m_relaxation_optimizer_factory;
36 unsigned int m_n_threads = 1;
37 std::vector<std::unique_ptr<Model>> m_relaxations;
38 std::vector<std::unique_ptr<NodeUpdator<NodeInfoT>>> m_node_updators;
40 std::unique_ptr<BranchingRule<NodeInfoT>> m_branching_rule;
41 std::unique_ptr<NodeSelectionRule<NodeInfoT>> m_node_selection_rule;
42 std::unique_ptr<typename Logs::BranchAndBound::Factory<NodeInfoT>::Strategy> m_logger;
43 std::unique_ptr<NodeInfoT> m_root_node_info;
45 std::unique_ptr<AbstractBranchAndBoundCallbackI<NodeInfoT>> m_callback;
47 bool m_has_integer_objective =
false;
48 std::vector<unsigned int> m_steps = { std::numeric_limits<unsigned int>::max(), 0, 0 };
49 unsigned int m_n_created_nodes = 0;
50 unsigned int m_n_solved_nodes = 0;
51 unsigned int m_n_active_nodes = 0;
52 double m_root_node_best_bound = -Inf;
53 double m_root_node_best_obj = +Inf;
55 std::optional<TreeNode> m_incumbent;
57 void build()
override;
58 void hook_before_optimize()
override;
59 void hook_optimize()
override;
60 void hook_after_optimize()
override;
61 void add(
const Var &t_var)
override;
62 void add(
const Ctr &t_ctr)
override;
63 void add(
const QCtr &t_ctr)
override;
64 void remove(
const Var &t_var)
override;
65 void remove(
const Ctr &t_ctr)
override;
66 void remove(
const QCtr &t_ctr)
override;
67 void update()
override;
68 void write(
const std::string &t_name)
override;
70 void detect_integer_objective();
71 void create_relaxations();
73 void explore(TreeNode& t_node,
unsigned int t_relaxation_id, SetOfActiveNodes& t_active_nodes,
unsigned int t_step);
74 void analyze(
const TreeNode& t_node,
unsigned int t_relaxation_id,
bool* t_explore_children_flag,
bool* t_reoptimize_flag);
75 Node<NodeInfoT> select_node_for_branching(SetOfActiveNodes& t_active_nodes);
76 std::vector<TreeNode> create_child_nodes(
const TreeNode& t_node);
77 void backtrack(SetOfActiveNodes& t_actives_nodes, SetOfActiveNodes& t_sub_active_nodes);
78 void set_as_incumbent(
const TreeNode& t_node);
79 [[nodiscard]]
bool gap_is_closed()
const;
80 void prune_nodes_by_bound(SetOfActiveNodes& t_active_nodes);
81 void update_lower_bound(
const SetOfActiveNodes& t_active_nodes);
82 bool is_valid(
const TreeNode& t_node)
const;
85 void log_after_termination();
87 SideEffectRegistry call_callbacks(CallbackEvent t_event,
const TreeNode& t_node,
unsigned int t_relaxation_id);
88 void update_obj_sense()
override;
89 void update_obj()
override;
90 void update_rhs()
override;
91 void update_obj_constant()
override;
92 void update_mat_coeff(
const Ctr &t_ctr,
const Var &t_var)
override;
93 void update_ctr_type(
const Ctr &t_ctr)
override;
94 void update_ctr_rhs(
const Ctr &t_ctr)
override;
95 void update_var_type(
const Var &t_var)
override;
96 void update_var_lb(
const Var &t_var)
override;
97 void update_var_ub(
const Var &t_var)
override;
99 void update_var_obj(
const Var &t_var)
override;
100 void set_status(SolutionStatus t_status)
override;
101 void set_reason(SolutionReason t_reason)
override;
102 void set_best_bound(
double t_value)
override;
103 void set_best_obj(
double t_value)
override;
105 explicit BranchAndBound(
const Model& t_model,
112 [[nodiscard]] std::string name()
const override {
return "Branch-and-Bound"; }
114 void solve(TreeNode& t_node,
unsigned int t_relaxation_id)
const;
116 virtual void set_subtree_depth(
unsigned int t_depth) { m_steps.at(1) = t_depth; }
118 [[nodiscard]]
unsigned int subtree_depth()
const {
return m_steps.at(1); }
122 void submit_heuristic_solution(NodeInfoT* t_info);
124 void submit_lower_bound(
double t_lower_bound);
126 [[nodiscard]]
unsigned int n_created_nodes()
const {
return m_n_created_nodes; }
128 [[nodiscard]]
unsigned int n_solved_nodes()
const {
return m_n_solved_nodes; }
130 [[nodiscard]]
unsigned int n_active_nodes()
const {
return m_n_active_nodes; }
132 [[nodiscard]]
const Model& relaxation()
const {
return *m_relaxations.front(); }
134 [[nodiscard]] Model& relaxation() {
return *m_relaxations.front(); }
136 [[nodiscard]]
double root_node_best_bound()
const {
return m_root_node_best_bound; }
138 [[nodiscard]]
double root_node_best_obj()
const {
return m_root_node_best_obj; }
144 [[nodiscard]]
bool has_incumbent()
const {
return m_incumbent.has_value(); }
146 const TreeNode& incumbent()
const {
return m_incumbent.value(); }
148 [[nodiscard]]
double get_var_primal(
const Var &t_var)
const override;
150 [[nodiscard]]
double get_var_reduced_cost(
const Var &t_var)
const override;
152 [[nodiscard]]
double get_var_ray(
const Var &t_var)
const override;
154 [[nodiscard]]
double get_ctr_dual(
const Ctr &t_ctr)
const override;
156 [[nodiscard]]
double get_ctr_farkas(
const Ctr &t_ctr)
const override;
158 [[nodiscard]]
unsigned int get_n_solutions()
const override;
160 [[nodiscard]]
unsigned int get_solution_index()
const override;
162 void set_solution_index(
unsigned int t_index)
override;
164 void terminate()
override;
168 void set_root_node_info(
const NodeInfoT& t_node_info);
173template<
class NodeInfoT>
174void idol::Optimizers::BranchAndBound<NodeInfoT>::remove(
const idol::QCtr &t_ctr) {
175 for (
auto& relaxation : m_relaxations) {
176 relaxation->remove(t_ctr);
180template<
class NodeInfoT>
181void idol::Optimizers::BranchAndBound<NodeInfoT>::add(
const idol::QCtr &t_ctr) {
182 for (
auto& relaxation : m_relaxations) {
183 relaxation->add(t_ctr);
187template<
class NodeInfoT>
188bool idol::Optimizers::BranchAndBound<NodeInfoT>::is_valid(
const TreeNode &t_node)
const {
191 result = m_branching_rule->is_valid(t_node);
195template<
class NodeInfoT>
196void idol::Optimizers::BranchAndBound<NodeInfoT>::terminate() {
198 Optimizer::terminate();
201template <
class NodeInfoT>
void idol::Optimizers::BranchAndBound<NodeInfoT>::set_relaxation_optimizer_factory(
202 const OptimizerFactory& t_factory) {
204 m_relaxation_optimizer_factory.reset(t_factory.clone());
208template <
class NodeInfoT>
209void idol::Optimizers::BranchAndBound<NodeInfoT>::set_root_node_info(
const NodeInfoT& t_node_info) {
210 m_root_node_info.reset(t_node_info.clone());
215 return *m_relaxation_optimizer_factory;
218template<
class NodeInfoT>
219void idol::Optimizers::BranchAndBound<NodeInfoT>::detect_integer_objective() {
220 const auto& src_model = parent();
221 const auto& objective = src_model.get_obj_expr();
223 if (!is_integer(objective.affine().constant(), Tolerance::Integer)) {
224 m_has_integer_objective =
false;
228 for (
const auto& [var, val] : objective.affine().linear()) {
229 if (src_model.get_var_type(var) == Continuous || !is_integer(val, Tolerance::Integer)) {
230 m_has_integer_objective =
false;
235 m_has_integer_objective =
true;
238template<
class NodeInfoT>
239double idol::Optimizers::BranchAndBound<NodeInfoT>::get_var_reduced_cost(
const idol::Var &t_var)
const {
240 if (m_n_solved_nodes > 1) {
241 throw Exception(
"Reduced cost not available.");
243 return m_relaxations.front()->get_var_reduced_cost(t_var);
246template<
class NodeInfoT>
247void idol::Optimizers::BranchAndBound<NodeInfoT>::log_after_termination() {
249 if (!get_param_logs()) {
254 m_callback->log_after_termination();
257 m_logger->log_after_termination();
261template<
class NodeInfoT>
262void idol::Optimizers::BranchAndBound<NodeInfoT>::log_node_after_solve(
const idol::Node<NodeInfoT> &t_node) {
263 m_logger->log_node_after_solve(t_node);
266template<
class NodeInfoT>
267void idol::Optimizers::BranchAndBound<NodeInfoT>::set_solution_index(
unsigned int t_index) {
269 throw Exception(
"Solution index out of bounds.");
273template<
class NodeInfoT>
274unsigned int idol::Optimizers::BranchAndBound<NodeInfoT>::get_solution_index()
const {
278template<
class NodeInfoT>
279unsigned int idol::Optimizers::BranchAndBound<NodeInfoT>::get_n_solutions()
const {
280 return m_incumbent.has_value();
283template<
class NodeInfoT>
284void idol::Optimizers::BranchAndBound<NodeInfoT>::update_obj() {
285 for (
auto& relaxation : m_relaxations) {
286 relaxation->set_obj_expr(parent().get_obj_expr());
290template<
class NodeInfoT>
291void idol::Optimizers::BranchAndBound<NodeInfoT>::update_rhs() {
292 for (
auto& relaxation : m_relaxations) {
293 relaxation->set_rhs_expr(parent().get_rhs_expr());
297template<
class NodeInfoT>
298void idol::Optimizers::BranchAndBound<NodeInfoT>::update_obj_constant() {
299 for (
auto& relaxation : m_relaxations) {
300 relaxation->set_obj_const(parent().get_obj_expr().affine().constant());
304template<
class NodeInfoT>
305void idol::Optimizers::BranchAndBound<NodeInfoT>::update_mat_coeff(
const Ctr &t_ctr,
const Var &t_var) {
306 for (
auto& relaxation : m_relaxations) {
307 relaxation->set_mat_coeff(t_ctr, t_var, parent().get_mat_coeff(t_ctr, t_var));
311template<
class NodeInfoT>
312void idol::Optimizers::BranchAndBound<NodeInfoT>::update_ctr_type(
const Ctr &t_ctr) {
313 for (
auto& relaxation : m_relaxations) {
314 relaxation->set_ctr_type(t_ctr, parent().get_ctr_type(t_ctr));
318template<
class NodeInfoT>
319void idol::Optimizers::BranchAndBound<NodeInfoT>::update_ctr_rhs(
const Ctr &t_ctr) {
320 for (
auto& relaxation : m_relaxations) {
321 relaxation->set_ctr_rhs(t_ctr, parent().get_ctr_rhs(t_ctr));
325template<
class NodeInfoT>
326void idol::Optimizers::BranchAndBound<NodeInfoT>::update_var_type(
const Var &t_var) {
327 for (
auto& relaxation : m_relaxations) {
328 relaxation->set_var_type(t_var, parent().get_var_type(t_var));
332template<
class NodeInfoT>
333void idol::Optimizers::BranchAndBound<NodeInfoT>::update_var_lb(
const Var &t_var) {
334 for (
auto& relaxation : m_relaxations) {
335 relaxation->set_var_lb(t_var, parent().get_var_lb(t_var));
339template<
class NodeInfoT>
340void idol::Optimizers::BranchAndBound<NodeInfoT>::update_var_ub(
const Var &t_var) {
341 for (
auto& relaxation : m_relaxations) {
342 relaxation->set_var_ub(t_var, parent().get_var_ub(t_var));
346template<
class NodeInfoT>
347void idol::Optimizers::BranchAndBound<NodeInfoT>::update_var_obj(
const Var &t_var) {
348 for (
auto& relaxation : m_relaxations) {
349 relaxation->set_var_obj(t_var, parent().get_var_obj(t_var));
353template<
class NodeInfoT>
354void idol::Optimizers::BranchAndBound<NodeInfoT>::update_obj_sense() {
355 throw Exception(
"Not implemented");
358template<
class NodeInfoT>
359double idol::Optimizers::BranchAndBound<NodeInfoT>::get_ctr_farkas(
const Ctr &t_ctr)
const {
360 if (m_n_solved_nodes > 1) {
361 throw Exception(
"Accessing Farkas certificate for MIP is not available after the root node.");
363 return m_relaxations.front()->get_ctr_farkas(t_ctr);
366template<
class NodeInfoT>
367double idol::Optimizers::BranchAndBound<NodeInfoT>::get_ctr_dual(
const Ctr &t_ctr)
const {
368 if (m_n_solved_nodes > 1) {
369 throw Exception(
"Accessing dual values for MIP is not available after the root node.");
371 return m_relaxations.front()->get_ctr_dual(t_ctr);
374template<
class NodeInfoT>
375double idol::Optimizers::BranchAndBound<NodeInfoT>::get_var_ray(
const Var &t_var)
const {
376 if (m_n_solved_nodes > 1) {
377 throw Exception(
"Ray not available.");
379 return m_relaxations.front()->get_var_ray(t_var);
382template<
class NodeInfoT>
383double idol::Optimizers::BranchAndBound<NodeInfoT>::get_var_primal(
const Var &t_var)
const {
385 if (!m_incumbent.has_value()){
386 throw Exception(
"Trying to access primal values while no incumbent was found.");
389 return m_incumbent->info().primal_solution().get(t_var);
392template<
class NodeInfoT>
393void idol::Optimizers::BranchAndBound<NodeInfoT>::submit_lower_bound(
double t_lower_bound) {
394 if (t_lower_bound > get_best_obj()) {
396 set_reason(NotSpecified);
400 if (t_lower_bound > get_best_bound()) {
401 set_best_bound(t_lower_bound);
406template<
class NodeInfoT>
407void idol::Optimizers::BranchAndBound<NodeInfoT>::submit_heuristic_solution(NodeInfoT* t_info) {
409 auto t_node = Node<NodeInfoT>::create_detached_node(t_info);
411 if (t_node.info().objective_value() < get_best_obj()) {
414 set_as_incumbent(t_node);
426template<
class NodeInfoT>
428idol::Optimizers::BranchAndBound<NodeInfoT>::call_callbacks(CallbackEvent t_event,
const Optimizers::BranchAndBound<NodeInfoT>::TreeNode &t_node,
unsigned int t_relaxation_id) {
434 return m_callback->operator()(
this, t_event, t_node, m_relaxations[t_relaxation_id].get());
437template<
class NodeInfoT>
438void idol::Optimizers::BranchAndBound<NodeInfoT>::add_callback(BranchAndBoundCallback<NodeInfoT> *t_cb) {
439 m_callback->add_callback(t_cb);
442template<
class NodeInfoT>
443void idol::Optimizers::BranchAndBound<NodeInfoT>::build() {
447template<
class NodeInfoT>
448idol::Optimizers::BranchAndBound<NodeInfoT>::BranchAndBound(
const Model &t_model,
449 const OptimizerFactory& t_node_optimizer,
450 const BranchingRuleFactory<NodeInfoT>& t_branching_rule_factory,
451 const NodeSelectionRuleFactory<NodeInfoT>& t_node_selection_rule_factory,
452 AbstractBranchAndBoundCallbackI<NodeInfoT>* t_callback,
453 const Logs::BranchAndBound::Factory<NodeInfoT>& t_logger_factory)
454 : Algorithm(t_model),
455 m_relaxation_optimizer_factory(t_node_optimizer.clone()),
456 m_branching_rule(t_branching_rule_factory(*this)),
457 m_node_selection_rule(t_node_selection_rule_factory(*this)),
458 m_callback(t_callback),
459 m_logger(t_logger_factory.operator()(*this)) {
461 create_relaxations();
465template<
class NodeInfoT>
466void idol::Optimizers::BranchAndBound<NodeInfoT>::create_relaxations() {
468 const auto &original_model = parent();
470 m_relaxations.clear();
471 m_relaxations.reserve(m_n_threads);
473 m_node_updators.clear();
474 m_node_updators.reserve(m_n_threads);
476 for (
unsigned int i = 0 ; i < m_n_threads ; ++i) {
477 auto* relaxation = original_model.clone();
478 relaxation->use(*m_relaxation_optimizer_factory);
479 m_relaxations.emplace_back(relaxation);
480 m_node_updators.emplace_back(
dynamic_cast<NodeUpdator<NodeInfoT>*
>(NodeInfoT::create_updator(parent(), *relaxation)));
485template<
class NodeInfoT>
486idol::Node<NodeInfoT> idol::Optimizers::BranchAndBound<NodeInfoT>::create_root_node() {
488 NodeInfoT* node_info =
nullptr;
489 if (m_root_node_info) {
490 node_info = m_root_node_info->clone();
491 }
else if constexpr (std::is_default_constructible_v<NodeInfoT>) {
492 node_info =
new NodeInfoT();
494 throw Exception(
"The given node info class has no default constructor and no root node info has been given.");
496 auto root_node = Node<NodeInfoT>::create_root_node(node_info);
497 assert(root_node.id() == 0);
504template<
class NodeInfoT>
505void idol::Optimizers::BranchAndBound<NodeInfoT>::hook_before_optimize() {
506 Algorithm::hook_before_optimize();
509 set_best_bound(std::max(-Inf, get_param_best_obj_stop()));
510 set_best_obj(std::min(+Inf, get_param_best_bound_stop()));
513 detect_integer_objective();
515 m_n_created_nodes = 0;
516 m_n_solved_nodes = 0;
517 m_n_active_nodes = 0;
519 m_root_node_best_bound = -Inf;
520 m_root_node_best_obj = Inf;
522 m_branching_rule->initialize();
523 for (
auto& node_updator : m_node_updators) {
524 node_updator->initialize();
526 m_callback->initialize(
this);
528 m_logger->initialize();
531template<
class NodeInfoT>
532void idol::Optimizers::BranchAndBound<NodeInfoT>::hook_optimize() {
534 auto root_node = create_root_node();
536 SetOfActiveNodes active_nodes;
538 explore(root_node, 0, active_nodes, 0);
540 if (active_nodes.empty()) {
541 set_best_bound(get_best_obj());
544 m_node_updators[0]->clear();
546 if (get_status() == Fail) {
550 if (!m_incumbent.has_value()) {
552 if (is_pos_inf(get_best_obj())) {
553 set_status(Infeasible);
557 if (is_neg_inf(get_best_obj())) {
558 set_status(Unbounded);
564 set_status(Feasible);
566 if (gap_is_closed()) {
574template<
class NodeInfoT>
575void idol::Optimizers::BranchAndBound<NodeInfoT>::hook_after_optimize() {
577 Optimizer::hook_after_optimize();
579 log_after_termination();
583template<
class NodeInfoT>
584void idol::Optimizers::BranchAndBound<NodeInfoT>::explore(
586 unsigned int t_relaxation_id,
587 SetOfActiveNodes & t_active_nodes,
588 unsigned int t_step) {
590 if (is_terminated() || gap_is_closed()) {
return; }
592 bool reoptimize_flag;
593 bool explore_children_flag;
597 solve(t_node, t_relaxation_id);
599 analyze(t_node, t_relaxation_id, &explore_children_flag, &reoptimize_flag);
601 log_node_after_solve(t_node);
603 }
while (reoptimize_flag);
608 if (is_terminated() || gap_is_closed()) {
return; }
610 if (!explore_children_flag) {
return; }
612 t_active_nodes.emplace(t_node);
614 const unsigned int max_levels = m_steps.at(t_step);
616 for (
unsigned int level = 0 ; level < max_levels ; ++level) {
618 prune_nodes_by_bound(t_active_nodes);
621 update_lower_bound(t_active_nodes);
622 m_n_active_nodes = t_active_nodes.size();
625 if (t_active_nodes.empty()) {
break; }
627 if (is_terminated() || gap_is_closed()) {
break; }
629 auto selected_node = select_node_for_branching(t_active_nodes);
631 auto children = create_child_nodes(selected_node);
633 const unsigned int n_children = children.size();
635 std::vector<SetOfActiveNodes> active_nodes(n_children);
637 if (m_n_threads > 1 && t_step == 0) {
639 std::vector<std::vector<unsigned int>> to_explore(m_n_threads);
641 for (
unsigned int q = 0 ; q < n_children ; ++q) {
642 const unsigned int relaxation_id = q % m_n_threads;
643 to_explore[relaxation_id].emplace_back(q);
646#pragma omp parallel for num_threads(m_n_threads)
647 for (
unsigned int k = 0 ; k < m_n_threads ; ++k) {
648 for (
unsigned int j = 0, n_tasks = to_explore[k].size() ; j < n_tasks ; ++j) {
649 const unsigned int q = to_explore[k][j];
650 explore(children[q], k, active_nodes[q], t_step + 1);
656 for (
unsigned int q = 0 ; q < n_children ; ++q) {
657 explore(children[q], t_relaxation_id, active_nodes[q], t_step + 1);
662 for (
unsigned int q = 0 ; q < n_children ; ++q) {
663 backtrack(t_active_nodes, active_nodes[q]);
670template<
class NodeInfoT>
671void idol::Optimizers::BranchAndBound<NodeInfoT>::solve(TreeNode& t_node,
672 unsigned int t_relaxation_id)
const {
674 auto& node_updator = *m_node_updators[t_relaxation_id];
675 auto& relaxation = *m_relaxations[t_relaxation_id];
677 relaxation.optimizer().set_param_best_bound_stop(std::min(get_best_obj(), get_param_best_bound_stop()));
678 relaxation.optimizer().set_param_time_limit(get_remaining_time());
680 node_updator.prepare(t_node);
694 relaxation.optimize();
696 t_node.info().save(parent(), relaxation);
698 m_branching_rule->on_node_solved(t_node);
702template<
class NodeInfoT>
703void idol::Optimizers::BranchAndBound<NodeInfoT>::analyze(
const BranchAndBound::TreeNode &t_node,
unsigned int t_relaxation_id,
bool* t_explore_children_flag,
bool* t_reoptimize_flag) {
705 *t_explore_children_flag =
false;
706 *t_reoptimize_flag =
false;
708 if (get_best_bound() > get_best_obj()) {
710 set_reason(NotSpecified);
715 const auto& status = t_node.info().status();
716 const auto& reason = t_node.info().reason();
718 if (get_remaining_time() == 0 || reason == TimeLimit) {
719 set_reason(TimeLimit);
724 if (status == Unbounded) {
731 if (status == Infeasible || status == InfOrUnbnd) {
733 if (t_node.level() == 0) {
737 call_callbacks(PrunedSolution, t_node, t_relaxation_id);
741 if (status == Feasible && reason == ObjLimit) {
742 call_callbacks(PrunedSolution, t_node, t_relaxation_id);
746 if (status == Fail || status == Loaded) {
749 set_reason(NotSpecified);
751 call_callbacks(PrunedSolution, t_node, t_relaxation_id);
755 if (t_node.level() == 0) {
756 m_root_node_best_bound = t_node.info().objective_value();
757 set_best_bound(std::max(m_root_node_best_bound, get_best_bound()));
760 if (t_node.info().objective_value() < get_best_obj()) {
762 if (is_valid(t_node)) {
764 auto side_effects = call_callbacks(IncumbentSolution, t_node, t_relaxation_id);
766 if (side_effects.n_added_user_cuts > 0 || side_effects.n_added_lazy_cuts > 0) {
767 *t_reoptimize_flag =
true;
771 set_as_incumbent(t_node);
778 call_callbacks(PrunedSolution, t_node, t_relaxation_id);
783 auto side_effects = call_callbacks(InvalidSolution, t_node, t_relaxation_id);
785 if (t_node.level() == 0) {
786 m_root_node_best_obj = get_best_obj();
789 if (side_effects.n_added_lazy_cuts > 0 || side_effects.n_added_user_cuts > 0 || side_effects.n_added_local_variable_branching > 0) {
790 *t_reoptimize_flag =
true;
794 *t_explore_children_flag =
true;
798template<
class NodeInfoT>
799void idol::Optimizers::BranchAndBound<NodeInfoT>::set_as_incumbent(
const BranchAndBound::TreeNode &t_node) {
801 m_incumbent = t_node;
802 set_best_obj(t_node.info().objective_value());
803 set_status(Feasible);
806template<
class NodeInfoT>
807void idol::Optimizers::BranchAndBound<NodeInfoT>::update_lower_bound(
const BranchAndBound::SetOfActiveNodes &t_active_nodes) {
809 if (t_active_nodes.empty()) {
return; }
811 auto& lowest_node = *t_active_nodes.by_objective_value().begin();
812 const double raw_lower_bound = lowest_node.info().objective_value();
813 const double lower_bound = m_has_integer_objective && !is_integer(raw_lower_bound,
Tolerance::Integer) ? std::ceil(raw_lower_bound) : raw_lower_bound;
814 if (lower_bound > get_best_bound()) {
815 set_best_bound(lower_bound);
820template<
class NodeInfoT>
821void idol::Optimizers::BranchAndBound<NodeInfoT>::prune_nodes_by_bound(BranchAndBound::SetOfActiveNodes& t_active_nodes) {
823 const double upper_bound = get_best_obj();
825 auto it = t_active_nodes.by_objective_value().begin();
826 auto end = t_active_nodes.by_objective_value().end();
830 const auto& node = *it;
831 const double raw_lower_bound = node.info().objective_value();
832 const double lower_bound = m_has_integer_objective && !is_integer(raw_lower_bound,
Tolerance::Integer) ? std::ceil(raw_lower_bound) : raw_lower_bound;
834 if (lower_bound >= upper_bound - get_tol_mip_absolute_gap()) {
835 it = t_active_nodes.erase(it);
836 end = t_active_nodes.by_objective_value().end();
845template<
class NodeInfoT>
847idol::Optimizers::BranchAndBound<NodeInfoT>::select_node_for_branching(BranchAndBound::SetOfActiveNodes &t_active_nodes) {
848 auto iterator = m_node_selection_rule->operator()(t_active_nodes);
849 auto result = *iterator;
850 t_active_nodes.erase(iterator);
854template<
class NodeInfoT>
855void idol::Optimizers::BranchAndBound<NodeInfoT>::write(
const std::string &t_name) {
856 m_relaxations.front()->write(t_name);
859template<
class NodeInfoT>
860std::vector<idol::Node<NodeInfoT>> idol::Optimizers::BranchAndBound<NodeInfoT>::create_child_nodes(
const BranchAndBound::TreeNode &t_node) {
862 auto children_info = m_branching_rule->create_child_nodes(t_node);
864 std::vector<Node<NodeInfoT>> result;
865 result.reserve(children_info.size());
867 for (
auto* ptr_to_info : children_info) {
868 result.emplace_back(ptr_to_info, m_n_created_nodes++, t_node);
871 m_branching_rule->on_nodes_have_been_created();
876template<
class NodeInfoT>
877bool idol::Optimizers::BranchAndBound<NodeInfoT>::gap_is_closed()
const {
878 return get_relative_gap() <= get_tol_mip_relative_gap()
879 || get_absolute_gap() <= get_tol_mip_absolute_gap();
882template<
class NodeInfoT>
883void idol::Optimizers::BranchAndBound<NodeInfoT>::backtrack(BranchAndBound::SetOfActiveNodes &t_actives_nodes,
884 SetOfActiveNodes &t_sub_active_nodes) {
886 t_actives_nodes.merge(std::move(t_sub_active_nodes));
890template<
class NodeInfoT>
891void idol::Optimizers::BranchAndBound<NodeInfoT>::update() {
892 for (
auto& relaxation : m_relaxations) {
893 relaxation->update();
897template<
class NodeInfoT>
898void idol::Optimizers::BranchAndBound<NodeInfoT>::remove(
const Ctr &t_ctr) {
899 for (
auto& relaxation : m_relaxations) {
900 relaxation->remove(t_ctr);
904template<
class NodeInfoT>
905void idol::Optimizers::BranchAndBound<NodeInfoT>::remove(
const Var &t_var) {
906 for (
auto& relaxation : m_relaxations) {
907 relaxation->remove(t_var);
911template<
class NodeInfoT>
912void idol::Optimizers::BranchAndBound<NodeInfoT>::add(
const Ctr &t_ctr) {
913 for (
auto& relaxation : m_relaxations) {
914 relaxation->add(t_ctr);
918template<
class NodeInfoT>
919void idol::Optimizers::BranchAndBound<NodeInfoT>::add(
const Var &t_var) {
920 for (
auto& relaxation : m_relaxations) {
921 relaxation->add(t_var);
925template<
class NodeInfoT>
926void idol::Optimizers::BranchAndBound<NodeInfoT>::set_best_obj(
double t_value) {
928 Algorithm::set_best_obj(t_value);
929 assert(get_best_bound() <= get_best_obj() + get_tol_mip_absolute_gap());
932template<
class NodeInfoT>
933void idol::Optimizers::BranchAndBound<NodeInfoT>::set_best_bound(
double t_value) {
935 Algorithm::set_best_bound(t_value);
936 assert(get_best_bound() <= get_best_obj() + get_tol_mip_absolute_gap());
939template<
class NodeInfoT>
940void idol::Optimizers::BranchAndBound<NodeInfoT>::set_reason(idol::SolutionReason t_reason) {
942 Algorithm::set_reason(t_reason);
945template<
class NodeInfoT>
946void idol::Optimizers::BranchAndBound<NodeInfoT>::set_status(idol::SolutionStatus t_status) {
948 Algorithm::set_status(t_status);