idol
A C++ Framework for Optimization
Loading...
Searching...
No Matches
Optimizers_BranchAndBound.h
1//
2// Created by henri on 13/03/23.
3//
4
5#ifndef IDOL_OPTIMIZERS_BRANCHANDBOUND_H
6#define IDOL_OPTIMIZERS_BRANCHANDBOUND_H
7
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"
20
21#include <memory>
22#include <cassert>
23#include <fstream>
24
25namespace idol::Optimizers {
26 template<class NodeInfoT> class BranchAndBound;
27}
28
29template<class NodeInfoT>
33
34 std::unique_ptr<OptimizerFactory> m_relaxation_optimizer_factory;
35
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;
39
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
44 std::unique_ptr<AbstractBranchAndBoundCallbackI<NodeInfoT>> m_callback;
45
46 bool m_has_integer_objective = false;
47 std::vector<unsigned int> m_steps = { std::numeric_limits<unsigned int>::max(), 0, 0 };
48 unsigned int m_n_created_nodes = 0;
49 unsigned int m_n_solved_nodes = 0;
50 unsigned int m_n_active_nodes = 0;
51 double m_root_node_best_bound = -Inf;
52 double m_root_node_best_obj = +Inf;
53
54 std::optional<TreeNode> m_incumbent;
55protected:
56 void build() override;
57 void hook_before_optimize() override;
58 void hook_optimize() override;
59 void hook_after_optimize() override;
60 void add(const Var &t_var) override;
61 void add(const Ctr &t_ctr) override;
62 void add(const QCtr &t_ctr) override;
63 void remove(const Var &t_var) override;
64 void remove(const Ctr &t_ctr) override;
65 void remove(const QCtr &t_ctr) override;
66 void update() override;
67 void write(const std::string &t_name) override;
68
69 void detect_integer_objective();
70 void create_relaxations();
71 Node<NodeInfoT> create_root_node();
72 void explore(TreeNode& t_node, unsigned int t_relaxation_id, SetOfActiveNodes& t_active_nodes, unsigned int t_step);
73 void analyze(const TreeNode& t_node, unsigned int t_relaxation_id, bool* t_explore_children_flag, bool* t_reoptimize_flag);
74 Node<NodeInfoT> select_node_for_branching(SetOfActiveNodes& t_active_nodes);
75 std::vector<TreeNode> create_child_nodes(const TreeNode& t_node);
76 void backtrack(SetOfActiveNodes& t_actives_nodes, SetOfActiveNodes& t_sub_active_nodes);
77 void set_as_incumbent(const TreeNode& t_node);
78 [[nodiscard]] bool gap_is_closed() const;
79 void prune_nodes_by_bound(SetOfActiveNodes& t_active_nodes);
80 void update_lower_bound(const SetOfActiveNodes& t_active_nodes);
81 bool is_valid(const TreeNode& t_node) const;
82
83 void log_node_after_solve(const Node<NodeInfoT>& t_node);
84 void log_after_termination();
85
86 SideEffectRegistry call_callbacks(CallbackEvent t_event, const TreeNode& t_node, unsigned int t_relaxation_id);
87 void update_obj_sense() override;
88 void update_obj() override;
89 void update_rhs() override;
90 void update_obj_constant() override;
91 void update_mat_coeff(const Ctr &t_ctr, const Var &t_var) override;
92 void update_ctr_type(const Ctr &t_ctr) override;
93 void update_ctr_rhs(const Ctr &t_ctr) override;
94 void update_var_type(const Var &t_var) override;
95 void update_var_lb(const Var &t_var) override;
96 void update_var_ub(const Var &t_var) override;
97
98 void update_var_obj(const Var &t_var) override;
99 void set_status(SolutionStatus t_status) override;
100 void set_reason(SolutionReason t_reason) override;
101 void set_best_bound(double t_value) override;
102 void set_best_obj(double t_value) override;
103public:
104 explicit BranchAndBound(const Model& t_model,
105 const OptimizerFactory& t_node_optimizer,
106 const BranchingRuleFactory<NodeInfoT>& t_branching_rule_factory,
107 const NodeSelectionRuleFactory<NodeInfoT>& t_node_selection_rule_factory,
109 const Logs::BranchAndBound::Factory<NodeInfoT>& t_logger_factory);
110
111 [[nodiscard]] std::string name() const override { return "Branch-and-Bound"; }
112
113 void solve(TreeNode& t_node, unsigned int t_relaxation_id) const;
114
115 virtual void set_subtree_depth(unsigned int t_depth) { m_steps.at(1) = t_depth; }
116
117 [[nodiscard]] unsigned int subtree_depth() const { return m_steps.at(1); }
118
119 virtual void add_callback(BranchAndBoundCallback<NodeInfoT>* t_cb);
120
121 void submit_heuristic_solution(NodeInfoT* t_info);
122
123 void submit_lower_bound(double t_lower_bound);
124
125 [[nodiscard]] unsigned int n_created_nodes() const { return m_n_created_nodes; }
126
127 [[nodiscard]] unsigned int n_solved_nodes() const { return m_n_solved_nodes; }
128
129 [[nodiscard]] unsigned int n_active_nodes() const { return m_n_active_nodes; }
130
131 [[nodiscard]] const Model& relaxation() const { return *m_relaxations.front(); }
132
133 [[nodiscard]] Model& relaxation() { return *m_relaxations.front(); }
134
135 [[nodiscard]] double root_node_best_bound() const { return m_root_node_best_bound; }
136
137 [[nodiscard]] double root_node_best_obj() const { return m_root_node_best_obj; }
138
139 BranchingRule<NodeInfoT>& branching_rule() { return *m_branching_rule; }
140
141 const BranchingRule<NodeInfoT>& branching_rule() const { return *m_branching_rule; }
142
143 [[nodiscard]] bool has_incumbent() const { return m_incumbent.has_value(); }
144
145 const TreeNode& incumbent() const { return m_incumbent.value(); }
146
147 [[nodiscard]] double get_var_primal(const Var &t_var) const override;
148
149 [[nodiscard]] double get_var_reduced_cost(const Var &t_var) const override;
150
151 [[nodiscard]] double get_var_ray(const Var &t_var) const override;
152
153 [[nodiscard]] double get_ctr_dual(const Ctr &t_ctr) const override;
154
155 [[nodiscard]] double get_ctr_farkas(const Ctr &t_ctr) const override;
156
157 [[nodiscard]] unsigned int get_n_solutions() const override;
158
159 [[nodiscard]] unsigned int get_solution_index() const override;
160
161 void set_solution_index(unsigned int t_index) override;
162
163 void terminate() override;
164
165 void set_relaxation_optimizer_factory(const OptimizerFactory& t_factory);
166
167 const OptimizerFactory& get_relaxation_optimizer_factory() const;
168};
169
170template<class NodeInfoT>
172 for (auto& relaxation : m_relaxations) {
173 relaxation->remove(t_ctr);
174 }
175}
176
177template<class NodeInfoT>
179 for (auto& relaxation : m_relaxations) {
180 relaxation->add(t_ctr);
181 }
182}
183
184template<class NodeInfoT>
185bool idol::Optimizers::BranchAndBound<NodeInfoT>::is_valid(const TreeNode &t_node) const {
186 bool result;
187#pragma omp critical
188 result = m_branching_rule->is_valid(t_node);
189 return result;
190}
191
192template<class NodeInfoT>
194#pragma omp critical
195 Optimizer::terminate();
196}
197
199 const OptimizerFactory& t_factory) {
200
201 m_relaxation_optimizer_factory.reset(t_factory.clone());
202
203}
204
207 return *m_relaxation_optimizer_factory;
208}
209
210template<class NodeInfoT>
212 const auto& src_model = parent();
213 const auto& objective = src_model.get_obj_expr();
214
215 if (!is_integer(objective.affine().constant(), Tolerance::Integer)) {
216 m_has_integer_objective = false;
217 return;
218 }
219
220 for (const auto& [var, val] : objective.affine().linear()) {
221 if (src_model.get_var_type(var) == Continuous || !is_integer(val, Tolerance::Integer)) {
222 m_has_integer_objective = false;
223 return;
224 }
225 }
226
227 m_has_integer_objective = true;
228}
229
230template<class NodeInfoT>
232 if (m_n_solved_nodes > 1) {
233 throw Exception("Reduced cost not available.");
234 }
235 return m_relaxations.front()->get_var_reduced_cost(t_var);
236}
237
238template<class NodeInfoT>
240
241 if (!get_param_logs()) {
242 return;
243 }
244
245 if (m_callback) {
246 m_callback->log_after_termination();
247 }
248
249 m_logger->log_after_termination();
250
251}
252
253template<class NodeInfoT>
255 m_logger->log_node_after_solve(t_node);
256}
257
258template<class NodeInfoT>
260 if (t_index != 0) {
261 throw Exception("Solution index out of bounds.");
262 }
263}
264
265template<class NodeInfoT>
267 return 0;
268}
269
270template<class NodeInfoT>
272 return m_incumbent.has_value();
273}
274
275template<class NodeInfoT>
277 for (auto& relaxation : m_relaxations) {
278 relaxation->set_obj_expr(parent().get_obj_expr());
279 }
280}
281
282template<class NodeInfoT>
284 for (auto& relaxation : m_relaxations) {
285 relaxation->set_rhs_expr(parent().get_rhs_expr());
286 }
287}
288
289template<class NodeInfoT>
291 for (auto& relaxation : m_relaxations) {
292 relaxation->set_obj_const(parent().get_obj_expr().affine().constant());
293 }
294}
295
296template<class NodeInfoT>
297void idol::Optimizers::BranchAndBound<NodeInfoT>::update_mat_coeff(const Ctr &t_ctr, const Var &t_var) {
298 for (auto& relaxation : m_relaxations) {
299 relaxation->set_mat_coeff(t_ctr, t_var, parent().get_mat_coeff(t_ctr, t_var));
300 }
301}
302
303template<class NodeInfoT>
305 for (auto& relaxation : m_relaxations) {
306 relaxation->set_ctr_type(t_ctr, parent().get_ctr_type(t_ctr));
307 }
308}
309
310template<class NodeInfoT>
312 for (auto& relaxation : m_relaxations) {
313 relaxation->set_ctr_rhs(t_ctr, parent().get_ctr_rhs(t_ctr));
314 }
315}
316
317template<class NodeInfoT>
319 for (auto& relaxation : m_relaxations) {
320 relaxation->set_var_type(t_var, parent().get_var_type(t_var));
321 }
322}
323
324template<class NodeInfoT>
326 for (auto& relaxation : m_relaxations) {
327 relaxation->set_var_lb(t_var, parent().get_var_lb(t_var));
328 }
329}
330
331template<class NodeInfoT>
333 for (auto& relaxation : m_relaxations) {
334 relaxation->set_var_ub(t_var, parent().get_var_ub(t_var));
335 }
336}
337
338template<class NodeInfoT>
340 for (auto& relaxation : m_relaxations) {
341 relaxation->set_var_obj(t_var, parent().get_var_obj(t_var));
342 }
343}
344
345template<class NodeInfoT>
347 throw Exception("Not implemented");
348}
349
350template<class NodeInfoT>
352 if (m_n_solved_nodes > 1) {
353 throw Exception("Accessing Farkas certificate for MIP is not available after the root node.");
354 }
355 return m_relaxations.front()->get_ctr_farkas(t_ctr);
356}
357
358template<class NodeInfoT>
360 if (m_n_solved_nodes > 1) {
361 throw Exception("Accessing dual values for MIP is not available after the root node.");
362 }
363 return m_relaxations.front()->get_ctr_dual(t_ctr);
364}
365
366template<class NodeInfoT>
368 if (m_n_solved_nodes > 1) {
369 throw Exception("Ray not available.");
370 }
371 return m_relaxations.front()->get_var_ray(t_var);
372}
373
374template<class NodeInfoT>
376
377 if (!m_incumbent.has_value()){
378 throw Exception("Trying to access primal values while no incumbent was found.");
379 }
380
381 return m_incumbent->info().primal_solution().get(t_var);
382}
383
384template<class NodeInfoT>
386 if (t_lower_bound > get_best_obj()) {
387 set_status(Fail);
388 set_reason(NotSpecified);
389 terminate();
390 return;
391 }
392 if (t_lower_bound > get_best_bound()) {
393 set_best_bound(t_lower_bound);
394 // New best LB
395 }
396}
397
398template<class NodeInfoT>
400
401 auto t_node = Node<NodeInfoT>::create_detached_node(t_info);
402
403 if (t_node.info().objective_value() < get_best_obj()) {
404
405 //if (m_branching_rule->is_valid(t_node)) {
406 set_as_incumbent(t_node);
407 // New incumbent by submission
408 //} else {
409 // idol_Log(Trace, "Ignoring submitted heuristic solution, solution is not valid.");
410 //}
411
412 } else {
413 // idol_Log(Trace, "Ignoring submitted heuristic solution, objective value is " << t_node.info().objective_value() << " while best obj is " << get_best_obj() << '.');
414 }
415
416}
417
418template<class NodeInfoT>
420idol::Optimizers::BranchAndBound<NodeInfoT>::call_callbacks(CallbackEvent t_event, const Optimizers::BranchAndBound<NodeInfoT>::TreeNode &t_node, unsigned int t_relaxation_id) {
421
422 if (!m_callback) {
423 return {};
424 }
425
426 return m_callback->operator()(this, t_event, t_node, m_relaxations[t_relaxation_id].get());
427}
428
429template<class NodeInfoT>
430void idol::Optimizers::BranchAndBound<NodeInfoT>::add_callback(BranchAndBoundCallback<NodeInfoT> *t_cb) {
431 m_callback->add_callback(t_cb);
432}
433
434template<class NodeInfoT>
436
437}
438
439template<class NodeInfoT>
441 const OptimizerFactory& t_node_optimizer,
442 const BranchingRuleFactory<NodeInfoT>& t_branching_rule_factory,
443 const NodeSelectionRuleFactory<NodeInfoT>& t_node_selection_rule_factory,
444 AbstractBranchAndBoundCallbackI<NodeInfoT>* t_callback,
445 const Logs::BranchAndBound::Factory<NodeInfoT>& t_logger_factory)
446 : Algorithm(t_model),
447 m_relaxation_optimizer_factory(t_node_optimizer.clone()),
448 m_branching_rule(t_branching_rule_factory(*this)),
449 m_node_selection_rule(t_node_selection_rule_factory(*this)),
450 m_callback(t_callback),
451 m_logger(t_logger_factory.operator()(*this)) {
452
453 create_relaxations();
454
455}
456
457template<class NodeInfoT>
459
460 const auto &original_model = parent();
461
462 m_relaxations.clear();
463 m_relaxations.reserve(m_n_threads);
464
465 m_node_updators.clear();
466 m_node_updators.reserve(m_n_threads);
467
468 for (unsigned int i = 0 ; i < m_n_threads ; ++i) {
469 auto* relaxation = original_model.clone();
470 relaxation->use(*m_relaxation_optimizer_factory);
471 m_relaxations.emplace_back(relaxation);
472 m_node_updators.emplace_back(dynamic_cast<NodeUpdator<NodeInfoT>*>(NodeInfoT::create_updator(parent(), *relaxation)));
473 }
474
475}
476
477template<class NodeInfoT>
479
480 auto root_node = Node<NodeInfoT>::create_root_node();
481 assert(root_node.id() == 0);
482 ++m_n_created_nodes;
483
484 return root_node;
485
486}
487
488template<class NodeInfoT>
490 Algorithm::hook_before_optimize();
491
492 // Reset solution
493 set_best_bound(std::max(-Inf, get_param_best_obj_stop()));
494 set_best_obj(std::min(+Inf, get_param_best_bound_stop()));
495 m_incumbent.reset();
496
497 detect_integer_objective();
498
499 m_n_created_nodes = 0;
500 m_n_solved_nodes = 0;
501 m_n_active_nodes = 0;
502
503 m_root_node_best_bound = -Inf;
504 m_root_node_best_obj = Inf;
505
506 m_branching_rule->initialize();
507 for (auto& node_updator : m_node_updators) {
508 node_updator->initialize();
509 }
510 m_callback->initialize(this);
511
512 m_logger->initialize();
513}
514
515template<class NodeInfoT>
517
518 auto root_node = create_root_node();
519
520 SetOfActiveNodes active_nodes;
521
522 explore(root_node, 0, active_nodes, 0);
523
524 if (active_nodes.empty()) {
525 set_best_bound(get_best_obj());
526 }
527
528 m_node_updators[0]->clear();
529
530 if (get_status() == Fail) {
531 return;
532 }
533
534 if (!m_incumbent.has_value()) {
535
536 if (is_pos_inf(get_best_obj())) {
537 set_status(Infeasible);
538 return;
539 }
540
541 if (is_neg_inf(get_best_obj())) {
542 set_status(Unbounded);
543 return;
544 }
545
546 }
547
548 set_status(Feasible);
549
550 if (gap_is_closed()) {
551 set_status(Optimal);
552 set_reason(Proved);
553 return;
554 }
555
556}
557
558template<class NodeInfoT>
560
561 Optimizer::hook_after_optimize();
562
563 log_after_termination();
564
565}
566
567template<class NodeInfoT>
569 TreeNode &t_node,
570 unsigned int t_relaxation_id,
571 SetOfActiveNodes & t_active_nodes,
572 unsigned int t_step) {
573
574 if (is_terminated() || gap_is_closed()) { return; }
575
576 bool reoptimize_flag;
577 bool explore_children_flag;
578
579 do {
580
581 solve(t_node, t_relaxation_id);
582
583 analyze(t_node, t_relaxation_id, &explore_children_flag, &reoptimize_flag);
584
585 log_node_after_solve(t_node);
586
587 } while (reoptimize_flag);
588
589#pragma omp atomic
590 ++m_n_solved_nodes;
591
592 if (is_terminated() || gap_is_closed()) { return; }
593
594 if (!explore_children_flag) { return; }
595
596 t_active_nodes.emplace(t_node);
597
598 const unsigned int max_levels = m_steps.at(t_step);
599
600 for (unsigned int level = 0 ; level < max_levels ; ++level) {
601
602 prune_nodes_by_bound(t_active_nodes);
603
604 if (t_step == 0) {
605 update_lower_bound(t_active_nodes);
606 m_n_active_nodes = t_active_nodes.size();
607 }
608
609 if (t_active_nodes.empty()) { break; }
610
611 if (is_terminated() || gap_is_closed()) { break; }
612
613 auto selected_node = select_node_for_branching(t_active_nodes);
614
615 auto children = create_child_nodes(selected_node);
616
617 const unsigned int n_children = children.size();
618
619 std::vector<SetOfActiveNodes> active_nodes(n_children);
620
621 if (m_n_threads > 1 && t_step == 0) {
622
623 std::vector<std::vector<unsigned int>> to_explore(m_n_threads);
624
625 for (unsigned int q = 0 ; q < n_children ; ++q) {
626 const unsigned int relaxation_id = q % m_n_threads;
627 to_explore[relaxation_id].emplace_back(q);
628 }
629
630#pragma omp parallel for num_threads(m_n_threads)
631 for (unsigned int k = 0 ; k < m_n_threads ; ++k) {
632 for (unsigned int j = 0, n_tasks = to_explore[k].size() ; j < n_tasks ; ++j) {
633 const unsigned int q = to_explore[k][j];
634 explore(children[q], k, active_nodes[q], t_step + 1);
635 }
636 }
637
638 } else {
639
640 for (unsigned int q = 0 ; q < n_children ; ++q) {
641 explore(children[q], t_relaxation_id, active_nodes[q], t_step + 1);
642 }
643
644 }
645
646 for (unsigned int q = 0 ; q < n_children ; ++q) {
647 backtrack(t_active_nodes, active_nodes[q]);
648 }
649
650 }
651
652}
653
654template<class NodeInfoT>
656 unsigned int t_relaxation_id) const {
657
658 auto& node_updator = *m_node_updators[t_relaxation_id];
659 auto& relaxation = *m_relaxations[t_relaxation_id];
660
661 relaxation.optimizer().set_param_best_bound_stop(std::min(get_best_obj(), get_param_best_bound_stop()));
662 relaxation.optimizer().set_param_time_limit(get_remaining_time());
663
664 node_updator.prepare(t_node);
665
666 /*
667 for (const auto& var : parent().vars()) {
668 const double lb = relaxation.get_var_lb(var);
669 const double ub = relaxation.get_var_ub(var);
670 if (lb > ub + Tolerance::Integer) {
671 std::cerr << "Inconsistent bounds for variable " << var << ": " << lb << " > " << ub << std::endl;
672 }
673 }
674 */
675
676 relaxation.optimize();
677
678 t_node.info().save(parent(), relaxation);
679
680 m_branching_rule->on_node_solved(t_node);
681
682}
683
684template<class NodeInfoT>
685void idol::Optimizers::BranchAndBound<NodeInfoT>::analyze(const BranchAndBound::TreeNode &t_node, unsigned int t_relaxation_id, bool* t_explore_children_flag, bool* t_reoptimize_flag) {
686
687 *t_explore_children_flag = false;
688 *t_reoptimize_flag = false;
689
690 if (get_best_bound() > get_best_obj()) {
691 set_status(Fail);
692 set_reason(NotSpecified);
693 terminate();
694 return;
695 }
696
697 const auto& status = t_node.info().status();
698 const auto& reason = t_node.info().reason();
699
700 if (get_remaining_time() == 0 || reason == TimeLimit) {
701 set_reason(TimeLimit);
702 terminate();
703 return;
704 }
705
706 if (status == Unbounded) {
707 set_reason(Proved);
708 set_best_obj(-Inf);
709 terminate();
710 return;
711 }
712
713 if (status == Infeasible || status == InfOrUnbnd) {
714
715 if (t_node.level() == 0) {
716 set_status(status);
717 }
718
719 call_callbacks(PrunedSolution, t_node, t_relaxation_id);
720 return;
721 }
722
723 if (status == Feasible && reason == ObjLimit) {
724 call_callbacks(PrunedSolution, t_node, t_relaxation_id);
725 return;
726 }
727
728 if (status == Fail || status == Loaded) {
729
730 set_status(Fail);
731 set_reason(NotSpecified);
732 terminate();
733 call_callbacks(PrunedSolution, t_node, t_relaxation_id);
734 return;
735 }
736
737 if (t_node.level() == 0) {
738 m_root_node_best_bound = t_node.info().objective_value();
739 }
740
741 if (t_node.info().objective_value() < get_best_obj()) {
742
743 if (is_valid(t_node)) {
744
745 auto side_effects = call_callbacks(IncumbentSolution, t_node, t_relaxation_id);
746
747 if (side_effects.n_added_user_cuts > 0 || side_effects.n_added_lazy_cuts > 0) {
748 *t_reoptimize_flag = true;
749 return;
750 }
751
752 set_as_incumbent(t_node);
753 return;
754
755 }
756
757 } else {
758
759 call_callbacks(PrunedSolution, t_node, t_relaxation_id);
760 return;
761
762 }
763
764 auto side_effects = call_callbacks(InvalidSolution, t_node, t_relaxation_id);
765
766 if (t_node.level() == 0) {
767 m_root_node_best_obj = get_best_obj();
768 }
769
770 if (side_effects.n_added_lazy_cuts > 0 || side_effects.n_added_user_cuts > 0 || side_effects.n_added_local_variable_branching > 0) {
771 *t_reoptimize_flag = true;
772 return;
773 }
774
775 *t_explore_children_flag = true;
776
777}
778
779template<class NodeInfoT>
780void idol::Optimizers::BranchAndBound<NodeInfoT>::set_as_incumbent(const BranchAndBound::TreeNode &t_node) {
781#pragma omp critical
782 m_incumbent = t_node;
783 set_best_obj(t_node.info().objective_value());
784 set_status(Feasible);
785}
786
787template<class NodeInfoT>
788void idol::Optimizers::BranchAndBound<NodeInfoT>::update_lower_bound(const BranchAndBound::SetOfActiveNodes &t_active_nodes) {
789
790 if (t_active_nodes.empty()) { return; }
791
792 auto& lowest_node = *t_active_nodes.by_objective_value().begin();
793 const double raw_lower_bound = lowest_node.info().objective_value();
794 const double lower_bound = m_has_integer_objective && !is_integer(raw_lower_bound, Tolerance::Integer) ? std::ceil(raw_lower_bound) : raw_lower_bound;
795 if (lower_bound > get_best_bound()) {
796 set_best_bound(lower_bound);
797 }
798
799}
800
801template<class NodeInfoT>
802void idol::Optimizers::BranchAndBound<NodeInfoT>::prune_nodes_by_bound(BranchAndBound::SetOfActiveNodes& t_active_nodes) {
803
804 const double upper_bound = get_best_obj();
805
806 auto it = t_active_nodes.by_objective_value().begin();
807 auto end = t_active_nodes.by_objective_value().end();
808
809 while (it != end) {
810
811 const auto& node = *it;
812 const double raw_lower_bound = 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
815 if (lower_bound >= upper_bound - get_tol_mip_absolute_gap()) {
816 it = t_active_nodes.erase(it);
817 end = t_active_nodes.by_objective_value().end();
818 } else {
819 break;
820 }
821
822 }
823
824}
825
826template<class NodeInfoT>
828idol::Optimizers::BranchAndBound<NodeInfoT>::select_node_for_branching(BranchAndBound::SetOfActiveNodes &t_active_nodes) {
829 auto iterator = m_node_selection_rule->operator()(t_active_nodes);
830 auto result = *iterator;
831 t_active_nodes.erase(iterator);
832 return result;
833}
834
835template<class NodeInfoT>
836void idol::Optimizers::BranchAndBound<NodeInfoT>::write(const std::string &t_name) {
837 m_relaxations.front()->write(t_name);
838}
839
840template<class NodeInfoT>
841std::vector<idol::Node<NodeInfoT>> idol::Optimizers::BranchAndBound<NodeInfoT>::create_child_nodes(const BranchAndBound::TreeNode &t_node) {
842
843 auto children_info = m_branching_rule->create_child_nodes(t_node);
844
845 std::vector<Node<NodeInfoT>> result;
846 result.reserve(children_info.size());
847
848 for (auto* ptr_to_info : children_info) {
849 result.emplace_back(ptr_to_info, m_n_created_nodes++, t_node);
850 }
851
852 m_branching_rule->on_nodes_have_been_created();
853
854 return result;
855}
856
857template<class NodeInfoT>
859 return get_relative_gap() <= get_tol_mip_relative_gap()
860 || get_absolute_gap() <= get_tol_mip_absolute_gap();
861}
862
863template<class NodeInfoT>
864void idol::Optimizers::BranchAndBound<NodeInfoT>::backtrack(BranchAndBound::SetOfActiveNodes &t_actives_nodes,
865 SetOfActiveNodes &t_sub_active_nodes) {
866
867 t_actives_nodes.merge(std::move(t_sub_active_nodes));
868
869}
870
871template<class NodeInfoT>
873 for (auto& relaxation : m_relaxations) {
874 relaxation->update();
875 }
876}
877
878template<class NodeInfoT>
880 for (auto& relaxation : m_relaxations) {
881 relaxation->remove(t_ctr);
882 }
883}
884
885template<class NodeInfoT>
887 for (auto& relaxation : m_relaxations) {
888 relaxation->remove(t_var);
889 }
890}
891
892template<class NodeInfoT>
894 for (auto& relaxation : m_relaxations) {
895 relaxation->add(t_ctr);
896 }
897}
898
899template<class NodeInfoT>
901 for (auto& relaxation : m_relaxations) {
902 relaxation->add(t_var);
903 }
904}
905
906template<class NodeInfoT>
908#pragma omp critical
909 Algorithm::set_best_obj(t_value);
910}
911
912template<class NodeInfoT>
914#pragma omp critical
915 Algorithm::set_best_bound(t_value);
916}
917
918template<class NodeInfoT>
919void idol::Optimizers::BranchAndBound<NodeInfoT>::set_reason(idol::SolutionReason t_reason) {
920#pragma omp critical
921 Algorithm::set_reason(t_reason);
922}
923
924template<class NodeInfoT>
925void idol::Optimizers::BranchAndBound<NodeInfoT>::set_status(idol::SolutionStatus t_status) {
926#pragma omp critical
927 Algorithm::set_status(t_status);
928}
929
930#endif //IDOL_OPTIMIZERS_BRANCHANDBOUND_H
static double Integer
Definition numericals.h:73