OSDN Git Service

Correct my name in all copyrights (Dederichs) and added Janos as Copyright to all...
[bin-packing-3d/or_project_inform.git] / src / inform-or / inform_or_experiments / split_model.py
1 # Copyright (c) 2020 Janos Piddubnij 
2 # Copyright (c) 2020 Moritz Dederichs
3
4 from inform_or_models.assign_items_by_volume import assign_items_by_volume
5 from inform_or_parser.parse_generated_csv import parse_generated_csv
6 from inform_or_models.place_items_in_box import place_items_in_box
7 from inform_or_parser.parse_boxes import parse_boxes
8 from typing import Dict, Tuple
9
10
11 class SplitModel:
12     def __init__(self, input_file: str, boxes_file: str):
13         """ Creates an instance of the SplitModel approach.
14
15         Parameters
16         ----------
17         input_file: str
18             The CSV file created by the INFORM-Generator to be processed.
19         """
20         self.orders, self.length_items, self.width_items, self.height_items, self.weight_items = parse_generated_csv(input_file)
21         boxes_dict = parse_boxes(boxes_file)
22         self.boxes = list(boxes_dict.keys())
23         self.length_boxes = {k: v[0] for k, v in boxes_dict.items()}
24         self.width_boxes = {k: v[1] for k, v in boxes_dict.items()}
25         self.height_boxes = {k: v[2] for k, v in boxes_dict.items()}
26
27     def process(self, order: str) -> Dict[str, dict]:
28         """ Solves the optimisation problem for the given order.
29
30         Parameters
31         ----------
32         order: str
33             The order to be processed.
34
35         Returns
36         -------
37         Dict[str, dict]
38             A dictionary stating for each item in which item it has to be packed as well as the exact
39             placing coordinates and the item orientation.
40         """
41
42         items = list()
43         for item in self.orders[order]:
44             for index in range(item[1]):
45                 items.append((item[0], index + 1))
46
47         # Find an acceptable distribution of items into boxes
48         distribution = assign_items_by_volume(self.boxes, self.width_boxes, self.height_boxes,
49                                               self.length_boxes, items, self.width_items, self.height_items,
50                                               self.length_items, 0.8)
51
52         # Place items optimally in each box
53         packed_boxes = dict()
54         for box in list(distribution.keys()):
55             b = box[0]
56             print('Starting Place_Items_In_Box')
57             item_placements = place_items_in_box(distribution[box], self.width_items, self.height_items,
58                                                  self.length_items, self.width_boxes[b], self.height_boxes[b],
59                                                  self.length_boxes[b])
60             print('Ended Place_Items_In_Box')
61             packed_boxes[box] = item_placements
62
63         # If there are empty boxes (the assigned items did not fit), restart the procedure with the requirement to pack
64         # these items into at least 2 boxes
65
66         # Keep restarting until all items are packed
67         distribution, packed_boxes, check_needed = self.restart(distribution, packed_boxes)
68
69         while check_needed:
70             distribution, packed_boxes, check_needed = self.restart(distribution, packed_boxes)
71
72         return packed_boxes
73
74     def restart(self, distribution: dict, packed_boxes: dict) -> Tuple[dict, dict, bool]:
75         """ Restarts the procedure with the requirement to repack the items that did not fit in the previously assigned boxes
76
77         Parameters
78         ----------
79         distribution: dict
80             The dictionary containing the original distribution
81
82         packed_boxes: dict
83             The dictionary containing the packed boxes (including the empty ones)
84
85         Returns
86         -------
87         Tuple[dict, dict, bool]
88             A tuple containing the updated distribution, dictionary of packed boxes and a boolean determining whether a
89             new check for empty boxes is needed
90         """
91         boxes_to_add = dict()
92         unpacked_boxes = list()
93         unpacked_items = list()
94         check_needed = False
95         for packed_box, values in packed_boxes.items():
96             if not values:
97                 unpacked_boxes.append(packed_box)
98                 unpacked_items.extend(distribution[packed_box])
99         
100         # Determine the new distribution of previously unpacked items
101         if len(unpacked_boxes) > 0:
102             distribution = assign_items_by_volume(self.boxes, self.width_boxes, self.height_boxes, self.length_boxes,
103                                                   unpacked_items, self.width_items, self.height_items,
104                                                   self.length_items, 0.8, len(unpacked_boxes) + 1)
105                 
106             # Calculate the exact placements
107             for box in list(distribution.keys()):
108                 b = box[0]
109                 item_placements = place_items_in_box(distribution[box], self.width_items, self.height_items,
110                                                      self.length_items, self.width_boxes[b], self.height_boxes[b],
111                                                      self.length_boxes[b])
112                 boxes_to_add[box] = item_placements
113
114             check_needed = True
115
116         # Remove the empty boxes
117         for b in unpacked_boxes:
118             packed_boxes.pop(b, None)
119
120         # Add the boxes packed after restart
121         packed_boxes.update(boxes_to_add)
122
123         return distribution, packed_boxes, check_needed