idol
A C++ Framework for Optimization
Loading...
Searching...
No Matches
KnapsackCover.h
1//
2// Created by henri on 20.11.23.
3//
4
5#ifndef IDOL_KNAPSACKCOVER_H
6#define IDOL_KNAPSACKCOVER_H
7
8#include "idol/mixed-integer/optimizers/branch-and-bound/callbacks/BranchAndBoundCallbackFactory.h"
9#include "idol/mixed-integer/optimizers/branch-and-bound/callbacks/BranchAndBoundCallback.h"
10#include "idol/mixed-integer/optimizers/branch-and-bound/nodes/DefaultNodeInfo.h"
11
12namespace idol::Cuts {
13 template<class NodeInfoT> class KnapsackCover;
14}
15
16template<class NodeInfoT = idol::DefaultNodeInfo>
18 std::optional<bool> m_lifting;
19 std::optional<bool> m_apply_to_tree_nodes;
20 std::optional<unsigned int> m_max_pass_root_node;
21 std::optional<double> m_max_cuts_factor;
22public:
23 class Strategy;
24 BranchAndBoundCallback<NodeInfoT> *operator()() override;
25 BranchAndBoundCallbackFactory<NodeInfoT> *clone() const override;
26
27 KnapsackCover& with_lifting(bool t_value);
28
29 KnapsackCover& with_tree_node_cuts(bool t_value);
30
31 KnapsackCover& with_max_pass_root_node(unsigned int t_value);
32
33 KnapsackCover& with_max_cuts_factor(double t_value);
34};
35
36template<class NodeInfoT>
38
39 if (m_max_pass_root_node.has_value()) {
40 throw Exception("Maximum pass at root node has already been configured.");
41 }
42
43 m_max_pass_root_node = t_value;
44
45 return *this;
46}
47
48template<class NodeInfoT>
50
51 if (m_max_cuts_factor.has_value()) {
52 throw Exception("Maximum cuts factor has already been configured.");
53 }
54
55 m_max_cuts_factor = t_value;
56
57 return *this;
58}
59
60template<class NodeInfoT>
62
63 if (m_apply_to_tree_nodes.has_value()) {
64 throw Exception("Tree node cuts have already been configured.");
65 }
66
67 m_apply_to_tree_nodes = t_value;
68
69 return *this;
70}
71
72template<class NodeInfoT>
74
75 if (m_lifting.has_value()) {
76 throw Exception("Lifting has already been configured.");
77 }
78
79 m_lifting = t_value;
80
81 return *this;
82}
83
84template<class NodeInfoT>
85class idol::Cuts::KnapsackCover<NodeInfoT>::Strategy : public BranchAndBoundCallback<NodeInfoT> {
86
87 struct KnapsackStructure {
88
89 struct Item {
90 Var variable;
91 int weight;
92 double current_value = -1.;
93 std::optional<int> cut_coefficient;
94 Item(const Var& t_var, int t_weight);
95
96 Item(const Item&) = default;
97 Item(Item&&) = default;
98
99 Item& operator=(const Item&) = default;
100 Item& operator=(Item&&) = default;
101 };
102
103 std::vector<Item> items;
104 int capacity;
105 KnapsackStructure(std::vector<Item> t_items, int t_capacity);
106 };
107
108 using ItemIterator = typename std::vector<typename KnapsackStructure::Item>::iterator;
109
110 class SetOfItems {
111 ItemIterator m_begin;
112 ItemIterator m_end;
113 public:
114 SetOfItems(ItemIterator t_begin, ItemIterator t_end) : m_begin(t_begin), m_end(t_end) {}
115
116 ItemIterator begin() const { return m_begin; }
117 ItemIterator end() const { return m_end; }
118 unsigned int size() const { return std::distance(m_begin, m_end); }
119
120 static SetOfItems merge_consecutive(const SetOfItems& t_a, const SetOfItems& t_b);
121 };
122
123 std::list<KnapsackStructure> m_knapsack_structures;
124 const bool m_lifting;
125 const bool m_apply_to_tree_nodes;
126 const unsigned int m_max_pass_root_node;
127 const double m_max_cuts_factor;
128 unsigned int m_max_cuts = 0;
129 unsigned int m_n_cuts = 0;
130 unsigned int m_n_separations = 0;
131protected:
132 // Detect structure
133 void detect_knapsack_structure(const Ctr& t_ctr);
134 void add_knapsack_structure(const Row& t_row, CtrType t_type);
135 bool has_only_binary_variables(const Row& t_row);
136 bool has_only_single_signed_coefficients(const Row& t_row);
137
138 // Separation
139 void separate_cut(const KnapsackStructure& t_knapsack_structure);
140 std::tuple<SetOfItems, SetOfItems, SetOfItems> fix_variables_equal_to_one_or_zero(const SetOfItems& t_set_of_items);
141 void set_current_values(const SetOfItems& t_set_of_items);
142 int sum_of_weights(const SetOfItems& t_set_of_items);
143 std::pair<SetOfItems, SetOfItems> compute_initial_cover(const SetOfItems& t_N_1, const SetOfItems& t_N_free, const SetOfItems& t_N_0, int t_capacity);
144 std::pair<SetOfItems, SetOfItems> solve_knapsack_approximately(const SetOfItems& t_set_of_items, int t_capacity);
145 std::pair<SetOfItems, SetOfItems> swap_items(const SetOfItems& t_a, const SetOfItems& t_b);
146 std::pair<SetOfItems, SetOfItems> compute_minimal_cover(const SetOfItems& t_initial_cover, int t_capacity);
147 std::pair<SetOfItems, SetOfItems> partition_minimal_cover(const SetOfItems& t_minimal_cover);
148 std::pair<SetOfItems, SetOfItems> partition_remaining_items(const SetOfItems& t_remaining_items);
149 void sort_by_non_decreasing_weights(const SetOfItems& t_set_of_items);
150 void sort_by_non_increasing_weights(const SetOfItems& t_set_of_items);
151 int sequential_up_and_down_lifting(const SetOfItems& t_C1, const SetOfItems& t_C2, const SetOfItems &t_F, const SetOfItems& t_R, int t_capacity);
152 double compute_cut_activity(const SetOfItems& t_set_of_items, int t_right_hand_side);
153 TempCtr create_cut(const SetOfItems& t_set_of_items, int t_right_hand_side);
154
155 void debug(const std::string& t_name, const SetOfItems& t_set_of_items, bool t_with_values = false, bool t_with_cut = false);
156
157 void initialize() override;
158
159 void operator()(CallbackEvent t_event) override;
160
161 void log_after_termination() override;
162
163public:
164 Strategy(bool t_use_lifting,
165 bool t_apply_to_tree_nodes,
166 unsigned int t_max_pass_root_node,
167 double t_max_cuts_factor);
168};
169
170template<class NodeInfoT>
172
173 BranchAndBoundCallback<NodeInfoT>::log_after_termination();
174
175 std::cout << "\tKnapsack Cover Cuts: " << m_n_cuts << std::endl;
176}
177
178template<class NodeInfoT>
180 bool t_apply_to_tree_nodes,
181 unsigned int t_max_pass_root_node,
182 double t_max_cuts_factor)
183 : m_lifting(t_use_lifting),
184 m_apply_to_tree_nodes(t_apply_to_tree_nodes),
185 m_max_pass_root_node(t_max_pass_root_node),
186 m_max_cuts_factor(t_max_cuts_factor) {
187
188}
189
190template<class NodeInfoT>
191void idol::Cuts::KnapsackCover<NodeInfoT>::Strategy::debug(const std::string &t_name,
192 const SetOfItems &t_set_of_items,
193 bool t_with_values,
194 bool t_with_cut) {
195
196 std::cout << t_name << ": ";
197 for (const auto& item : t_set_of_items) {
198 if (t_with_cut) {
199 if (item.cut_coefficient.has_value()) {
200 std::cout << item.cut_coefficient.value();
201 } else {
202 std::cout << "?";
203 }
204 std::cout << " ";
205 }
206 std::cout << item.variable;
207 if (t_with_values) {
208 std::cout << "(" << item.current_value << ")";
209 }
210 std::cout << ", ";
211 }
212 std::cout << std::endl;
213
214}
215
216template<class NodeInfoT>
219 const SetOfItems &t_b) {
220
221 if (t_a.end() != t_b.begin()) {
222 throw Exception("Trying to merge non-consecutive sets of items while calling merge_consecutive.");
223 }
224
225 return {
226 t_a.begin(),
227 t_b.end()
228 };
229
230}
231
232template<class NodeInfoT>
234 : items(std::move(t_items)), capacity(t_capacity) {
235
236}
237
238template<class NodeInfoT>
240 : variable(t_var), weight(t_weight) {
241
242}
243
244template<class NodeInfoT>
247
248 m_knapsack_structures.clear();
249 m_n_cuts = 0;
250 m_n_separations = 0;
251
252 const auto& model = this->original_model();
253
254 for (const auto& ctr : model.ctrs()) {
255 detect_knapsack_structure(ctr);
256 }
257
258 m_max_cuts = m_max_cuts_factor * model.ctrs().size();
259
260 std::cout << "Lifted Minimal Cover Cuts 1, " << m_knapsack_structures.size() << " candidates found" << std::endl;
261}
262
263template<class NodeInfoT>
265
266 const auto& model = this->original_model();
267 const auto& row = model.get_ctr_row(t_ctr);
268
269 if (!row.quadratic().empty()) {
270 return;
271 }
272
273 if (!has_only_binary_variables(row)) {
274 return;
275 }
276
277 if (!has_only_single_signed_coefficients(row)) {
278 return;
279 }
280
281 const auto type = model.get_ctr_type(t_ctr);
282
283 if (type == Equal) {
284 return;
285 }
286
287 add_knapsack_structure(row, type);
288
289}
290
291template<class NodeInfoT>
292void idol::Cuts::KnapsackCover<NodeInfoT>::Strategy::add_knapsack_structure(const idol::Row &t_row, idol::CtrType t_type) {
293
294 const unsigned int n_items = t_row.linear().size();
295 const double factor = (t_type == LessOrEqual ? 1. : -1.);
296
297 auto copy = t_row;
298 copy.scale_to_integers(Tolerance::Digits);
299
300 std::vector<typename KnapsackStructure::Item> items;
301 items.reserve(n_items);
302
303 for (const auto& [var, coefficient] : copy.linear()) {
304 items.emplace_back(var, factor * coefficient.as_numerical());
305 }
306
307 m_knapsack_structures.emplace_back(std::move(items), factor * copy.rhs().as_numerical());
308
309}
310
311template<class NodeInfoT>
313
314 const auto& model = this->original_model();
315
316 for (const auto& [var, coefficient] : t_row.linear()) {
317
318 if (model.get_var_type(var) != Binary) {
319 return false;
320 }
321
322 }
323
324 return true;
325}
326
327template<class NodeInfoT>
329
330 std::optional<bool> are_all_non_negative;
331
332 for (const auto& [var, coefficient] : t_row.linear()) {
333
334 bool is_non_negative = coefficient.as_numerical() >= 0;
335
336 if (!are_all_non_negative.has_value()) [[unlikely]] {
337 are_all_non_negative = is_non_negative;
338 continue;
339 }
340
341 if (is_non_negative != are_all_non_negative) {
342 return false;
343 }
344
345 }
346
347 return true;
348}
349
350template<class NodeInfoT>
352
353 if (t_event != InvalidSolution) {
354 return;
355 }
356
357 if (m_n_cuts >= m_max_cuts) {
358 return;
359 }
360
361 if (this->node().level() == 0) {
362
363 if (m_n_separations > m_max_pass_root_node) {
364 return;
365 }
366
367 }
368
369 if (!m_apply_to_tree_nodes && this->node().level() != 0) {
370 return;
371 }
372
373 for (const auto& knapsack_structure : m_knapsack_structures) {
374 separate_cut(knapsack_structure);
375 ++m_n_separations;
376 }
377
378}
379
380template<class NodeInfoT>
382
383 if (m_n_cuts >= m_max_cuts) {
384 return;
385 }
386
387 auto knapsack_structure = t_knapsack_structure;
388 SetOfItems N(knapsack_structure.items.begin(), knapsack_structure.items.end());
389
390 set_current_values(N);
391
392 const auto [N_1, N_free, N_0] = fix_variables_equal_to_one_or_zero(N);
393 const int sum_weights_of_variables_not_fixed_to_zero = sum_of_weights(N_1) + sum_of_weights(N_free);
394
395 if (sum_weights_of_variables_not_fixed_to_zero < knapsack_structure.capacity + 1) {
396 return;
397 }
398
399 // Initial cover
400 auto [initial_cover, not_initial_cover] = compute_initial_cover(
401 N_1,
402 N_free,
403 N_0,
404 sum_weights_of_variables_not_fixed_to_zero - (knapsack_structure.capacity + 1)
405 );
406
407 // Minimal cover
408 auto [C, others] = compute_minimal_cover(initial_cover, t_knapsack_structure.capacity);
409
410 int rhs;
411
412 if (m_lifting) {
413
414 // Partition
415 auto not_in_C = SetOfItems::merge_consecutive(others, not_initial_cover);
416 auto [C1, C2] = partition_minimal_cover(C);
417
418 // Lifting sequence and computing the lifting coefficients
419 auto [F, R] = partition_remaining_items(not_in_C);
420 sort_by_non_decreasing_weights(C1);
421 sort_by_non_increasing_weights(C2);
422 sort_by_non_increasing_weights(F);
423 sort_by_non_increasing_weights(R);
424
425 rhs = sequential_up_and_down_lifting(C1, C2, F, R, knapsack_structure.capacity);
426
427 } else {
428
429 rhs = C.size() - 1;
430 for (auto& item : C) {
431 item.cut_coefficient = 1.;
432 }
433
434 }
435
436 const double activity = compute_cut_activity(N, rhs);
437
438 if (activity < Tolerance::Feasibility) {
439 return;
440 }
441
442 auto cut = create_cut(N, rhs);
443
444 this->add_user_cut(cut);
445 ++m_n_cuts;
446
447}
448
449template<class NodeInfoT>
450idol::TempCtr idol::Cuts::KnapsackCover<NodeInfoT>::Strategy::create_cut(const SetOfItems &t_set_of_items, int t_right_hand_side) {
451
452 AffExpr result;
453
454 for (const auto& item : t_set_of_items) {
455
456 if (!item.cut_coefficient.has_value()) {
457 continue;
458 }
459
460 result += item.cut_coefficient.value() * item.variable;
461
462 }
463
464 return result <= t_right_hand_side;
465}
466
467template<class NodeInfoT>
468double idol::Cuts::KnapsackCover<NodeInfoT>::Strategy::compute_cut_activity(const SetOfItems &t_set_of_items, int t_right_hand_side) {
469
470 double result = 0.;
471
472 for (const auto& item : t_set_of_items) {
473
474 if (!item.cut_coefficient.has_value()) {
475 continue;
476 }
477
478 result += item.cut_coefficient.value() * item.current_value;
479
480 }
481
482 return result - t_right_hand_side;
483}
484
485template<class NodeInfoT>
487 const SetOfItems &t_C2,
488 const SetOfItems &t_F,
489 const SetOfItems &t_R,
490 int t_capacity) {
491
492 // Set coefficients of variables in C1 to one
493 for (auto& item : t_C1) {
494 item.cut_coefficient = 1;
495 }
496
497 const auto initialize_min_weights = [](const SetOfItems& t_C1) {
498
499 const unsigned int n_C1 = t_C1.size();
500
501 std::vector<int> result;
502 result.reserve(n_C1 + 1);
503 result.emplace_back(0);
504
505 auto it = t_C1.begin();
506 for (unsigned int w = 0 ; w < n_C1 ; ++w) {
507 result.emplace_back(result[w] + it->weight);
508 ++it;
509 }
510
511 return result;
512 };
513
514 auto min_weights = initialize_min_weights(t_C1);
515
516 int alpha_0 = t_C1.size() - 1;
517 int lift_rhs = alpha_0;
518
519 int fixed_ones_weight = sum_of_weights(t_C2);
520
521 assert(fixed_ones_weight >= 0);
522
523 // Lifting items in F
524 for (auto& item : t_F) {
525
526 int z;
527 if (t_capacity - fixed_ones_weight - item.weight < 0) { // knapsack is infeasible
528 z = 0;
529 } else if (min_weights[alpha_0] <= t_capacity - fixed_ones_weight - item.weight) { // easy case
530 z = lift_rhs;
531 } else { // binary search for z
532
533 int lb = 0;
534 int ub = lift_rhs + 1;
535 while (lb < ub - 1) {
536 const int middle = (lb + ub) / 2;
537 if ( min_weights[middle] <= t_capacity - fixed_ones_weight - item.weight ) {
538 lb = middle;
539 } else {
540 ub = middle;
541 }
542 }
543
544 assert(lb == ub - 1);
545 assert(0 <= lb && lb < min_weights.size());
546 assert(min_weights[lb] <= t_capacity - fixed_ones_weight - item.weight);
547 assert(lb == min_weights.size() - 1 || min_weights[lb + 1] > t_capacity - fixed_ones_weight - item.weight);
548
549 z = lb;
550
551 assert(z <= lift_rhs);
552
553 }
554
555 const int alpha_j = alpha_0 - z;
556
557 item.cut_coefficient = alpha_j;
558
559 if (alpha_j == 0) {
560 continue;
561 }
562
563 min_weights.resize(min_weights.size() + alpha_j, std::numeric_limits<int>::infinity());
564
565 for (int w = min_weights.size() - 1 ; w >= 0 ; w--) {
566
567 if (w < alpha_j ) {
568 min_weights[w] = std::min(min_weights[w], item.weight);
569 } else {
570 min_weights[w] = std::min(min_weights[w], min_weights[w - alpha_j] + item.weight);
571 }
572
573 }
574
575 }
576
577 // Lifting in C2
578 for (auto& item : t_C2) {
579
580 int lb = 0;
581 int ub = min_weights.size();
582 while (lb < ub - 1) {
583 int middle = (lb + ub) / 2;
584 if ( min_weights[middle] <= t_capacity - fixed_ones_weight + item.weight ) {
585 lb = middle;
586 } else {
587 ub = middle;
588 }
589 }
590 int z = lb;
591
592 const int alpha_j = z - alpha_0;
593
594 item.cut_coefficient = alpha_j;
595
596 alpha_0 += alpha_j;
597
598 fixed_ones_weight -= item.weight;
599
600 if (alpha_j == 0) {
601 continue;
602 }
603
604 if (alpha_j > 0) {
605 min_weights.resize(min_weights.size() + alpha_j, std::numeric_limits<int>::infinity());
606 }
607
608 for (int w = min_weights.size() - 1 ; w >= 0 ; w--) {
609
610 if (w < alpha_j ) {
611 min_weights[w] = std::min(min_weights[w], item.weight);
612 } else {
613 min_weights[w] = std::min(min_weights[w], min_weights[w - alpha_j] + item.weight);
614 }
615
616 }
617
618
619 }
620
621 assert(fixed_ones_weight == 0);
622
623 // Lifting in R
624 for (auto& item : t_R) {
625
626 int z;
627 if (min_weights[alpha_0] <= t_capacity - item.weight) {
628 z = alpha_0;
629 } else { // binary search for z
630
631 int lb = 0;
632 int ub = alpha_0 + 1;
633 while (lb < ub - 1) {
634 int middle = (lb + ub) / 2;
635 if ( min_weights[middle] <= t_capacity - item.weight ) {
636 lb = middle;
637 } else {
638 ub = middle;
639 }
640 }
641 z = lb;
642
643 }
644
645 const int alpha_j = alpha_0 - z;
646
647 item.cut_coefficient = alpha_j;
648
649 if (alpha_j == 0) {
650 continue;
651 }
652
653 for (int w = min_weights.size() - 1 ; w >= 0 ; w--) {
654
655 if (w < alpha_j ) {
656 min_weights[w] = std::min(min_weights[w], item.weight);
657 } else {
658 min_weights[w] = std::min(min_weights[w], min_weights[w - alpha_j] + item.weight);
659 }
660
661 }
662
663
664 }
665
666 return alpha_0;
667
668}
669
670template<class NodeInfoT>
672
673 std::sort(t_set_of_items.begin(), t_set_of_items.end(), [](const auto& t_a, const auto& t_b) {
674 return t_a.weight >= t_b.weight;
675 });
676
677}
678
679template<class NodeInfoT>
681
682 std::sort(t_set_of_items.begin(), t_set_of_items.end(), [](const auto& t_a, const auto& t_b) {
683 return t_a.weight <= t_b.weight;
684 });
685
686}
687
688template<class NodeInfoT>
689void idol::Cuts::KnapsackCover<NodeInfoT>::Strategy::set_current_values(const SetOfItems &t_set_of_items) {
690
691 const auto& primal_solution = this->node().info().primal_solution();
692
693 for (auto& item : t_set_of_items) {
694 item.current_value = primal_solution.get(item.variable);
695 }
696
697}
698
699template<class NodeInfoT>
700std::tuple<
704 >
706
707 auto end_of_fixed_to_one = t_set_of_items.begin();
708 auto begin_of_fixed_to_zero = t_set_of_items.end();
709
710 for (auto it = end_of_fixed_to_one ; it != begin_of_fixed_to_zero && end_of_fixed_to_one != begin_of_fixed_to_zero ; ) {
711
712 if (equals(it->current_value, 0., Tolerance::Integer)) {
713 --begin_of_fixed_to_zero;
714 std::iter_swap(it, begin_of_fixed_to_zero);
715 continue;
716 }
717
718 if (equals(it->current_value, 1., Tolerance::Integer)) {
719 std::iter_swap(it, end_of_fixed_to_one);
720 ++end_of_fixed_to_one;
721 ++it;
722 continue;
723 }
724
725 ++it;
726
727 }
728
729 return {
730 { t_set_of_items.begin(), end_of_fixed_to_one }, // fixed to 1
731 { end_of_fixed_to_one, begin_of_fixed_to_zero }, // free
732 { begin_of_fixed_to_zero, t_set_of_items.end() }, // fixed to 0
733 };
734}
735
736template<class NodeInfoT>
737int idol::Cuts::KnapsackCover<NodeInfoT>::Strategy::sum_of_weights(const SetOfItems &t_set_of_items) {
738
739 int result = 0.;
740
741 for (auto it = t_set_of_items.begin(), end = t_set_of_items.end() ; it != end ; ++it) {
742 result += it->weight;
743 }
744
745 return result;
746}
747
748template<class NodeInfoT>
749std::pair<
752>
754 const SetOfItems &t_N_free,
755 const SetOfItems &t_N_0,
756 int t_capacity) {
757
758 auto [ones, zeroes] = solve_knapsack_approximately(t_N_free, t_capacity);
759
760 assert(ones.end() == zeroes.begin());
761
762 auto [flipped_zeroes, flipped_ones] = swap_items(ones, zeroes);
763
764 auto initial_cover = SetOfItems::merge_consecutive(t_N_1, flipped_zeroes);
765 auto not_initial_cover = SetOfItems::merge_consecutive(flipped_ones, t_N_0);
766
767 return { initial_cover, not_initial_cover };
768
769}
770
771template<class NodeInfoT>
772std::pair<
775>
777
778 auto end_of_C1 = t_minimal_cover.end();
779
780 for (auto it = t_minimal_cover.begin() ; it != end_of_C1 ; ) {
781
782 if (!equals(it->current_value, 1., Tolerance::Integer)) {
783 ++it;
784 continue;
785 }
786
787 --end_of_C1;
788 std::iter_swap(it, end_of_C1);
789
790 }
791
792 return {
793 { t_minimal_cover.begin(), end_of_C1 },
794 { end_of_C1, t_minimal_cover.end() }
795 };
796}
797
798template<class NodeInfoT>
799std::pair<
802>
804
805 auto end_of_F = t_remaining_items.end();
806
807 for (auto it = t_remaining_items.begin() ; it != end_of_F ; ) {
808
809 if (!equals(it->current_value, 0, Tolerance::Integer)) {
810 ++it;
811 continue;
812 }
813
814 --end_of_F;
815 std::iter_swap(it, end_of_F);
816
817 }
818
819 return {
820 { t_remaining_items.begin(), end_of_F },
821 { end_of_F, t_remaining_items.end() },
822 };
823}
824
825template<class NodeInfoT>
826std::pair<
829 >
831 int t_capacity) {
832
833 std::sort(t_set_of_items.begin(),
834 t_set_of_items.end(),
835 [&](const auto& t_a, const auto& t_b) {
836
837 const double score_a = (1. - t_a.current_value) / t_a.weight;
838 const double score_b = (1. - t_b.current_value) / t_b.weight;
839
840 return score_a >= score_b;
841
842 });
843
844 int weight_of_packed_items = 0.;
845 auto it = t_set_of_items.begin();
846 auto end = t_set_of_items.end();
847
848 auto begin_zero = it;
849
850 for ( ; it != end ; ++it) {
851
852 if (weight_of_packed_items + it->weight > t_capacity) {
853 // all remaining-s go to zero
854 break;
855 }
856
857 // set to 1
858 std::iter_swap(it, begin_zero);
859 ++begin_zero;
860
861 weight_of_packed_items += it->weight;
862
863 }
864
865 return {
866 { t_set_of_items.begin(), begin_zero }, // ones
867 { begin_zero, t_set_of_items.end() }, // zeroes
868 };
869
870}
871
872template<class NodeInfoT>
873std::pair<
876>
878 const SetOfItems &t_b) {
879
880 const auto min_size = std::min(t_a.size(), t_b.size());
881 const auto max_size = std::max(t_a.size(), t_b.size());
882
883 std::swap_ranges(t_a.begin(), t_a.begin() + min_size, t_a.begin() + max_size);
884
885 ItemIterator split = t_a.begin() + t_b.size();
886
887 return {
888 { t_a.begin(), split },
889 { split, t_b.end() }
890 };
891}
892
893template<class NodeInfoT>
894std::pair<
897>
899 int t_capacity) {
900
901 std::sort(t_initial_cover.begin(), t_initial_cover.end(), [](const auto& t_a, const auto& t_b) {
902
903 const double score_a = (1. - t_a.current_value) / t_a.weight;
904 const double score_b = (1. - t_b.current_value) / t_b.weight;
905
906 if (equals(score_a, score_b, Tolerance::Sparsity)) {
907 return t_a.weight <= t_b.weight;
908 }
909
910 return score_a >= score_b;
911 });
912
913 int sum_of_weights_in_C = sum_of_weights(t_initial_cover);
914 auto end = t_initial_cover.end();
915
916 for (auto it = t_initial_cover.begin() ; it != end ; ) {
917
918 if (sum_of_weights_in_C - it->weight <= t_capacity) {
919 ++it;
920 continue;
921 }
922
923 sum_of_weights_in_C -= it->weight;
924 --end;
925 std::iter_swap(it, end);
926
927 }
928
929 return {
930 { t_initial_cover.begin(), end },
931 { end, t_initial_cover.end() },
932 };
933
934}
935
936template<class NodeInfoT>
938 return new KnapsackCover<NodeInfoT>(*this);
939}
940
941template<class NodeInfoT>
943 return new Strategy(
944 m_lifting.has_value() ? m_lifting.value() : true,
945 m_apply_to_tree_nodes.has_value() ? m_apply_to_tree_nodes.value() : true,
946 m_max_pass_root_node.has_value() ? m_max_pass_root_node.value() : 200,
947 m_max_cuts_factor.has_value() ? m_max_cuts_factor.value() : 1.5
948 );
949}
950
951#endif //IDOL_KNAPSACKCOVER_H
void add_user_cut(const TempCtr &t_cut)
const Node< NodeInfoT > & node() const
void operator()(CallbackEvent t_event) override
static double Feasibility
Definition numericals.h:83
static double Integer
Definition numericals.h:73