Skip to content

Commit da44659

Browse files
committed
Implement dual feasible functions for rectangle bin packing
1 parent 5b53ae8 commit da44659

File tree

4 files changed

+380
-1
lines changed

4 files changed

+380
-1
lines changed

src/rectangle/CMakeLists.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ target_sources(PackingSolver_rectangle PRIVATE
88
algorithm_formatter.cpp
99
optimize.cpp
1010
branching_scheme.cpp
11-
benders_decomposition.cpp)
11+
benders_decomposition.cpp
12+
dual_feasible_functions.cpp)
1213
target_include_directories(PackingSolver_rectangle PUBLIC
1314
${PROJECT_SOURCE_DIR}/include)
1415
target_include_directories(PackingSolver_rectangle PRIVATE
+308
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
#include "rectangle/dual_feasible_functions.hpp"
2+
3+
#include "packingsolver/rectangle/algorithm_formatter.hpp"
4+
5+
using namespace packingsolver;
6+
using namespace packingsolver::rectangle;
7+
8+
namespace
9+
{
10+
11+
Length f_ccm_0(
12+
Length capacity,
13+
Length k,
14+
Length length)
15+
{
16+
if (length > capacity - k) {
17+
return capacity;
18+
} else if (length < k) {
19+
return 0;
20+
} else {
21+
return length;
22+
}
23+
}
24+
25+
Length f_ccm_1(
26+
Length capacity,
27+
Length k,
28+
Length length,
29+
ItemPos value)
30+
{
31+
if (length > capacity / 2) {
32+
// MC(C, S) − MC(C − x, S)
33+
if (value < 0)
34+
throw std::invalid_argument(
35+
"packingsolver::rectange::f_ccm_1; "
36+
"capacity: " + std::to_string(capacity) + "; "
37+
"k: " + std::to_string(k) + "; "
38+
"length: " + std::to_string(length) + "; "
39+
"value: " + std::to_string(value) + "; ");
40+
return value;
41+
} else if (length < k) {
42+
return 0;
43+
} else {
44+
return 1;
45+
}
46+
}
47+
48+
Length f_ccm_2(
49+
Length capacity,
50+
Length k,
51+
Length length)
52+
{
53+
if (length > capacity / 2) {
54+
return 2 * (capacity / k - (capacity - length) / k);
55+
} else if (length == capacity / 2 && capacity % 2 == 0) {
56+
return capacity / k;
57+
} else {
58+
return 2 * (length / k);
59+
}
60+
}
61+
62+
}
63+
64+
DualFeasibleFunctionsOutput packingsolver::rectangle::dual_feasible_functions(
65+
const Instance& instance,
66+
const DualFeasibleFunctionsParameters& parameters)
67+
{
68+
const BinType& bin_type = instance.bin_type(0);
69+
70+
if (instance.objective() != Objective::BinPacking) {
71+
throw std::invalid_argument(
72+
"packingsolver::rectangle::dual_feasible_functions.");
73+
}
74+
if (instance.number_of_bin_types() != 1) {
75+
throw std::invalid_argument(
76+
"packingsolver::rectangle::dual_feasible_functions.");
77+
}
78+
79+
DualFeasibleFunctionsOutput output(instance);
80+
AlgorithmFormatter algorithm_formatter(instance, parameters, output);
81+
algorithm_formatter.start();
82+
algorithm_formatter.print_header();
83+
84+
// These bounds are only valid if all items are oriented.
85+
bool all_items_oriented = true;
86+
for (ItemTypeId item_type_id = 0;
87+
item_type_id < instance.number_of_item_types();
88+
++item_type_id) {
89+
const ItemType& item_type = instance.item_type(item_type_id);
90+
if (!item_type.oriented) {
91+
all_items_oriented = false;
92+
break;
93+
}
94+
}
95+
if (!all_items_oriented)
96+
return output;
97+
98+
// Compute all distinct widths and heights.
99+
std::vector<Length> widths;
100+
std::vector<Length> heights;
101+
for (ItemTypeId item_type_id = 0;
102+
item_type_id < instance.number_of_item_types();
103+
++item_type_id) {
104+
const ItemType& item_type = instance.item_type(item_type_id);
105+
if (item_type.rect.x == bin_type.rect.x) {
106+
} else if (item_type.rect.x <= bin_type.rect.x / 2) {
107+
widths.push_back(item_type.rect.x);
108+
} else {
109+
widths.push_back(bin_type.rect.x - item_type.rect.x);
110+
}
111+
if (item_type.rect.y == bin_type.rect.y) {
112+
} else if (item_type.rect.y <= bin_type.rect.y / 2) {
113+
heights.push_back(item_type.rect.y);
114+
} else {
115+
heights.push_back(bin_type.rect.y - item_type.rect.y);
116+
}
117+
}
118+
sort(widths.begin(), widths.end());
119+
sort(heights.begin(), heights.end());
120+
widths.erase(unique(widths.begin(), widths.end()), widths.end());
121+
heights.erase(unique(heights.begin(), heights.end()), heights.end());
122+
123+
// Compute maximum cardinalities.
124+
std::vector<std::vector<ItemPos>> maximum_cardinality_w(widths.size());
125+
for (ItemTypeId k_pos = 0; k_pos < (ItemTypeId)widths.size(); ++k_pos) {
126+
Length k = widths[k_pos];
127+
std::vector<ItemTypeId> sorted_item_type_ids_w;
128+
for (ItemTypeId item_type_id = 0;
129+
item_type_id < instance.number_of_item_types();
130+
++item_type_id) {
131+
const ItemType& item_type = instance.item_type(item_type_id);
132+
if (k <= item_type.rect.x && item_type.rect.x <= bin_type.rect.x / 2)
133+
sorted_item_type_ids_w.push_back(item_type_id);
134+
}
135+
std::sort(
136+
sorted_item_type_ids_w.begin(),
137+
sorted_item_type_ids_w.end(),
138+
[&instance](
139+
ItemTypeId item_type_id_1,
140+
ItemTypeId item_type_id_2)
141+
{
142+
const ItemType& item_type_1 = instance.item_type(item_type_id_1);
143+
const ItemType& item_type_2 = instance.item_type(item_type_id_2);
144+
return item_type_1.rect.x < item_type_2.rect.x;
145+
});
146+
maximum_cardinality_w[k_pos] = std::vector<ItemPos>(instance.number_of_item_types() + 1, -10000);
147+
for (ItemTypeId item_type_id = 0;
148+
item_type_id <= instance.number_of_item_types();
149+
++item_type_id) {
150+
Length capacity_w = bin_type.rect.x;
151+
if (item_type_id < instance.number_of_item_types()) {
152+
const ItemType& item_type = instance.item_type(item_type_id);
153+
if (item_type.rect.x <= bin_type.rect.x / 2)
154+
continue;
155+
capacity_w -= item_type.rect.x;
156+
}
157+
Length width_cur = 0;
158+
maximum_cardinality_w[k_pos][item_type_id] = 0;
159+
for (ItemTypeId item_type_id_cur: sorted_item_type_ids_w) {
160+
const ItemType& item_type_cur = instance.item_type(item_type_id_cur);
161+
if (width_cur + item_type_cur.copies * item_type_cur.rect.x < capacity_w) {
162+
width_cur += item_type_cur.copies * item_type_cur.rect.x;
163+
maximum_cardinality_w[k_pos][item_type_id] += item_type_cur.copies;
164+
} else {
165+
ItemPos copies = (capacity_w - width_cur) / item_type_cur.rect.x;
166+
maximum_cardinality_w[k_pos][item_type_id] += copies;
167+
break;
168+
}
169+
}
170+
}
171+
}
172+
std::vector<std::vector<ItemPos>> maximum_cardinality_h(heights.size());
173+
for (ItemTypeId l_pos = 0; l_pos < (ItemTypeId)heights.size(); ++l_pos) {
174+
Length l = heights[l_pos];
175+
std::vector<ItemTypeId> sorted_item_type_ids_h;
176+
for (ItemTypeId item_type_id = 0;
177+
item_type_id < instance.number_of_item_types();
178+
++item_type_id) {
179+
const ItemType& item_type = instance.item_type(item_type_id);
180+
if (l <= item_type.rect.y && item_type.rect.y <= bin_type.rect.y / 2)
181+
sorted_item_type_ids_h.push_back(item_type_id);
182+
}
183+
std::sort(
184+
sorted_item_type_ids_h.begin(),
185+
sorted_item_type_ids_h.end(),
186+
[&instance](
187+
ItemTypeId item_type_id_1,
188+
ItemTypeId item_type_id_2)
189+
{
190+
const ItemType& item_type_1 = instance.item_type(item_type_id_1);
191+
const ItemType& item_type_2 = instance.item_type(item_type_id_2);
192+
return item_type_1.rect.y < item_type_2.rect.y;
193+
});
194+
maximum_cardinality_h[l_pos] = std::vector<ItemPos>(instance.number_of_item_types() + 1, -10000);
195+
for (ItemTypeId item_type_id = 0;
196+
item_type_id <= instance.number_of_item_types();
197+
++item_type_id) {
198+
Length capacity_h = bin_type.rect.y;
199+
if (item_type_id < instance.number_of_item_types()) {
200+
const ItemType& item_type = instance.item_type(item_type_id);
201+
if (item_type.rect.y <= bin_type.rect.y / 2)
202+
continue;
203+
capacity_h -= item_type.rect.y;
204+
}
205+
Length height_cur = 0;
206+
maximum_cardinality_h[l_pos][item_type_id] = 0;
207+
for (ItemTypeId item_type_id_cur: sorted_item_type_ids_h) {
208+
const ItemType& item_type_cur = instance.item_type(item_type_id_cur);
209+
if (height_cur + item_type_cur.copies * item_type_cur.rect.y < capacity_h) {
210+
height_cur += item_type_cur.copies * item_type_cur.rect.y;
211+
maximum_cardinality_h[l_pos][item_type_id] += item_type_cur.copies;
212+
} else {
213+
ItemPos copies = (capacity_h - height_cur) / item_type_cur.rect.y;
214+
maximum_cardinality_h[l_pos][item_type_id] += copies;
215+
break;
216+
}
217+
}
218+
}
219+
}
220+
221+
BinPos bound = 0;
222+
223+
for (ItemTypeId k_pos = 0; k_pos < (ItemTypeId)widths.size(); ++k_pos) {
224+
Length k = widths[k_pos];
225+
226+
for (ItemTypeId l_pos = 0; l_pos < (ItemTypeId)heights.size(); ++l_pos) {
227+
Length l = heights[l_pos];
228+
229+
Length f_ccm_0_w_bin = f_ccm_0(bin_type.rect.x, k, bin_type.rect.x);
230+
Length f_ccm_0_h_bin = f_ccm_0(bin_type.rect.y, l, bin_type.rect.y);
231+
Length f_ccm_1_w_bin = f_ccm_1(bin_type.rect.x, k, bin_type.rect.x, maximum_cardinality_w[k_pos][instance.number_of_item_types()]);
232+
Length f_ccm_1_h_bin = f_ccm_1(bin_type.rect.y, l, bin_type.rect.y, maximum_cardinality_h[l_pos][instance.number_of_item_types()]);
233+
Length f_ccm_2_w_bin = f_ccm_2(bin_type.rect.x, k, bin_type.rect.x);
234+
Length f_ccm_2_h_bin = f_ccm_2(bin_type.rect.y, l, bin_type.rect.y);
235+
236+
Length f_ccm_0_w_0_h_sum = 0;
237+
Length f_ccm_0_w_1_h_sum = 0;
238+
Length f_ccm_0_w_2_h_sum = 0;
239+
Length f_ccm_1_w_0_h_sum = 0;
240+
Length f_ccm_1_w_1_h_sum = 0;
241+
Length f_ccm_1_w_2_h_sum = 0;
242+
Length f_ccm_2_w_0_h_sum = 0;
243+
Length f_ccm_2_w_1_h_sum = 0;
244+
Length f_ccm_2_w_2_h_sum = 0;
245+
for (ItemTypeId item_type_id = 0;
246+
item_type_id < instance.number_of_item_types();
247+
++item_type_id) {
248+
const ItemType& item_type = instance.item_type(item_type_id);
249+
250+
Length f_ccm_0_w = f_ccm_0(bin_type.rect.x, k, item_type.rect.x);
251+
Length f_ccm_0_h = f_ccm_0(bin_type.rect.y, l, item_type.rect.y);
252+
Length f_ccm_1_w = f_ccm_1(bin_type.rect.x, k, item_type.rect.x, maximum_cardinality_w[k_pos][instance.number_of_item_types()] - maximum_cardinality_w[k_pos][item_type_id]);
253+
Length f_ccm_1_h = f_ccm_1(bin_type.rect.y, l, item_type.rect.y, maximum_cardinality_h[l_pos][instance.number_of_item_types()] - maximum_cardinality_h[l_pos][item_type_id]);
254+
Length f_ccm_2_w = f_ccm_2(bin_type.rect.x, k, item_type.rect.x);
255+
Length f_ccm_2_h = f_ccm_2(bin_type.rect.y, l, item_type.rect.y);
256+
257+
f_ccm_0_w_0_h_sum += item_type.copies * f_ccm_0_w * f_ccm_0_h;
258+
f_ccm_0_w_1_h_sum += item_type.copies * f_ccm_0_w * f_ccm_1_h;
259+
f_ccm_0_w_2_h_sum += item_type.copies * f_ccm_0_w * f_ccm_2_h;
260+
f_ccm_1_w_0_h_sum += item_type.copies * f_ccm_1_w * f_ccm_0_h;
261+
f_ccm_1_w_1_h_sum += item_type.copies * f_ccm_1_w * f_ccm_1_h;
262+
f_ccm_1_w_2_h_sum += item_type.copies * f_ccm_1_w * f_ccm_2_h;
263+
f_ccm_2_w_0_h_sum += item_type.copies * f_ccm_2_w * f_ccm_0_h;
264+
f_ccm_2_w_1_h_sum += item_type.copies * f_ccm_2_w * f_ccm_1_h;
265+
f_ccm_2_w_2_h_sum += item_type.copies * f_ccm_2_w * f_ccm_2_h;
266+
}
267+
268+
BinPos bound_0_w_0_h = std::ceil((double)f_ccm_0_w_0_h_sum / (f_ccm_0_w_bin * f_ccm_0_h_bin));
269+
BinPos bound_0_w_1_h = std::ceil((double)f_ccm_0_w_1_h_sum / (f_ccm_0_w_bin * f_ccm_1_h_bin));
270+
BinPos bound_0_w_2_h = std::ceil((double)f_ccm_0_w_2_h_sum / (f_ccm_0_w_bin * f_ccm_2_h_bin));
271+
BinPos bound_1_w_0_h = std::ceil((double)f_ccm_1_w_0_h_sum / (f_ccm_1_w_bin * f_ccm_0_h_bin));
272+
BinPos bound_1_w_1_h = std::ceil((double)f_ccm_1_w_1_h_sum / (f_ccm_1_w_bin * f_ccm_1_h_bin));
273+
BinPos bound_1_w_2_h = std::ceil((double)f_ccm_1_w_2_h_sum / (f_ccm_1_w_bin * f_ccm_2_h_bin));
274+
BinPos bound_2_w_0_h = std::ceil((double)f_ccm_2_w_0_h_sum / (f_ccm_2_w_bin * f_ccm_0_h_bin));
275+
BinPos bound_2_w_1_h = std::ceil((double)f_ccm_2_w_1_h_sum / (f_ccm_2_w_bin * f_ccm_1_h_bin));
276+
BinPos bound_2_w_2_h = std::ceil((double)f_ccm_2_w_2_h_sum / (f_ccm_2_w_bin * f_ccm_2_h_bin));
277+
278+
//std::cout << "k " << k << " l " << l << " bound_0_w_0_h " << bound_0_w_0_h << std::endl;
279+
//std::cout << "k " << k << " l " << l << " bound_0_w_1_h " << bound_0_w_1_h << std::endl;
280+
//std::cout << "k " << k << " l " << l << " bound_0_w_2_h " << bound_0_w_2_h << std::endl;
281+
//std::cout << "k " << k << " l " << l << " bound_1_w_0_h " << bound_1_w_0_h << std::endl;
282+
//std::cout << "k " << k << " l " << l << " bound_1_w_1_h " << bound_1_w_1_h << std::endl;
283+
//std::cout << " f_ccm_1_w_1_h_sum " << f_ccm_1_w_1_h_sum << std::endl;
284+
//std::cout << " f_ccm_1_w_bin " << f_ccm_1_w_bin << std::endl;
285+
//std::cout << " f_ccm_1_h_bin " << f_ccm_1_h_bin << std::endl;
286+
//std::cout << "k " << k << " l " << l << " bound_1_w_2_h " << bound_1_w_2_h << std::endl;
287+
//std::cout << "k " << k << " l " << l << " bound_2_w_0_h " << bound_2_w_0_h << std::endl;
288+
//std::cout << "k " << k << " l " << l << " bound_2_w_1_h " << bound_2_w_1_h << std::endl;
289+
//std::cout << "k " << k << " l " << l << " bound_2_w_2_h " << bound_2_w_2_h << std::endl;
290+
291+
bound = (std::max)(bound, bound_0_w_0_h);
292+
bound = (std::max)(bound, bound_0_w_1_h);
293+
bound = (std::max)(bound, bound_0_w_2_h);
294+
bound = (std::max)(bound, bound_1_w_0_h);
295+
bound = (std::max)(bound, bound_1_w_1_h);
296+
bound = (std::max)(bound, bound_1_w_2_h);
297+
bound = (std::max)(bound, bound_2_w_0_h);
298+
bound = (std::max)(bound, bound_2_w_1_h);
299+
bound = (std::max)(bound, bound_2_w_2_h);
300+
}
301+
}
302+
303+
//std::cout << "bound " << bound << std::endl;
304+
algorithm_formatter.update_bin_packing_bound(bound);
305+
306+
algorithm_formatter.end();
307+
return output;
308+
}
+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/**
2+
* Dual feasible functions
3+
*
4+
* Formulas to get bounds on bin packing problems (with a single bin type and
5+
* without item rotation).
6+
*
7+
* References:
8+
* - "New reduction procedures and lower bounds for the two-dimensional bin
9+
* packing problem with fixed orientation" (Carlier et al., 2007)
10+
* https://doi.org/10.1016/j.cor.2005.08.012
11+
* - "A theoretical and experimental study of fast lower bounds for the
12+
* two-dimensional bin packing problem" (Serairi1 et Haouari, 2018)
13+
* https://doi.org/10.1051/ro/2017019
14+
*/
15+
16+
#pragma once
17+
18+
#include "packingsolver/rectangle/solution.hpp"
19+
20+
namespace packingsolver
21+
{
22+
namespace rectangle
23+
{
24+
25+
struct DualFeasibleFunctionsOutput: packingsolver::Output<Instance, Solution>
26+
{
27+
/** Constructor. */
28+
DualFeasibleFunctionsOutput(const Instance& instance):
29+
packingsolver::Output<Instance, Solution>(instance) { }
30+
};
31+
32+
struct DualFeasibleFunctionsParameters: packingsolver::Parameters<Instance, Solution>
33+
{
34+
};
35+
36+
DualFeasibleFunctionsOutput dual_feasible_functions(
37+
const Instance& instance,
38+
const DualFeasibleFunctionsParameters& parameters);
39+
40+
}
41+
}

0 commit comments

Comments
 (0)