1 # Copyright (c) 2020 Janos Piddubnij
2 # Copyright (c) 2020 Moritz Dederichs
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
12 def __init__(self, input_file: str, boxes_file: str):
13 """ Creates an instance of the SplitModel approach.
18 The CSV file created by the INFORM-Generator to be processed.
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()}
27 def process(self, order: str) -> Dict[str, dict]:
28 """ Solves the optimisation problem for the given order.
33 The order to be processed.
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.
43 for item in self.orders[order]:
44 for index in range(item[1]):
45 items.append((item[0], index + 1))
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)
52 # Place items optimally in each box
54 for box in list(distribution.keys()):
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],
60 print('Ended Place_Items_In_Box')
61 packed_boxes[box] = item_placements
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
66 # Keep restarting until all items are packed
67 distribution, packed_boxes, check_needed = self.restart(distribution, packed_boxes)
70 distribution, packed_boxes, check_needed = self.restart(distribution, packed_boxes)
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
80 The dictionary containing the original distribution
83 The dictionary containing the packed boxes (including the empty ones)
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
92 unpacked_boxes = list()
93 unpacked_items = list()
95 for packed_box, values in packed_boxes.items():
97 unpacked_boxes.append(packed_box)
98 unpacked_items.extend(distribution[packed_box])
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)
106 # Calculate the exact placements
107 for box in list(distribution.keys()):
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
116 # Remove the empty boxes
117 for b in unpacked_boxes:
118 packed_boxes.pop(b, None)
120 # Add the boxes packed after restart
121 packed_boxes.update(boxes_to_add)
123 return distribution, packed_boxes, check_needed