forked from sabbadino/container-optimizations
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathprint_utils.py
More file actions
134 lines (114 loc) · 6.07 KB
/
Copy pathprint_utils.py
File metadata and controls
134 lines (114 loc) · 6.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
"""
Utility functions for conditional printing.
"""
def create_print_if_verbose(verbose):
"""
Creates a print function that only prints if verbose is True.
Args:
verbose (bool): Whether to enable printing
Returns:
function: A print function that respects the verbose flag
Example:
print_if_verbose = create_print_if_verbose(True)
print_if_verbose("This will print")
print_if_verbose = create_print_if_verbose(False)
print_if_verbose("This will not print")
"""
def print_if_verbose(*args, **kwargs):
"""Helper function to print only if verbose is True"""
if verbose:
print(*args, **kwargs)
return print_if_verbose
def print_if_verbose_factory(verbose):
"""
Alternative factory function name for creating conditional print functions.
This is an alias for create_print_if_verbose.
"""
return create_print_if_verbose(verbose)
def dump_phase1_results(
solver, status, x, y, group_in_containers, group_ids, group_to_items,
items, container_size, container_weight, verbose=False):
"""
Print and write markdown summary of phase 1 assignment model results.
Args:
solver: OR-Tools CP-SAT solver instance
status: Solver status
x: Assignment variables (item -> container)
y: Container usage variables
group_in_containers: Group assignment variables
group_ids: List of group IDs
group_to_items: Mapping from group ID to item indices
items: List of item dictionaries
container_size: [L, W, H] container dimensions
container_weight: Maximum weight per container
verbose: Whether to print detailed output
"""
print_if_verbose = create_print_if_verbose(verbose)
num_items = len(items)
item_ids = [item.get('id', i+1) for i, item in enumerate(items)]
item_weights = [item['weight'] for item in items]
item_volumes = [item['size'][0] * item['size'][1] * item['size'][2] for item in items]
item_group_ids = [item.get('group_id') for item in items]
container_volume = container_size[0] * container_size[1] * container_size[2]
if container_volume <= 0:
raise ValueError(f"Invalid container volume: {container_volume}. Container dimensions: {container_size}")
if container_weight <= 0:
raise ValueError(f"Invalid container weight: {container_weight}")
max_containers = len(y)
# Import here to avoid circular dependencies
from ortools.sat.python import cp_model
status_dict = {
cp_model.OPTIMAL: 'OPTIMAL',
cp_model.FEASIBLE: 'FEASIBLE',
cp_model.INFEASIBLE: 'INFEASIBLE',
cp_model.MODEL_INVALID: 'MODEL_INVALID',
cp_model.UNKNOWN: 'UNKNOWN',
}
print('')
print(f'******** PHASE 1 OUTPUT ********')
print(f'Step 1 Solver status: {status_dict.get(status, status)}')
if status in (cp_model.OPTIMAL, cp_model.FEASIBLE):
min_containers = int(sum(solver.Value(y[j]) for j in range(max_containers)))
group_splits = {g: solver.Value(group_in_containers[g]) - 1 for g in group_ids}
total_group_splits = sum(group_splits.values())
print(f'Minimum containers used: {min_containers}')
print(f'Total group splits (penalized): {total_group_splits}')
used_container_indices = [j for j in range(max_containers) if solver.Value(y[j])]
container_rebase = {old_idx: new_idx+1 for new_idx, old_idx in enumerate(used_container_indices)}
total_boxes_weight_check = sum(item_weights[i] for j in used_container_indices for i in range(num_items) if solver.Value(x[i, j]))
total_boxes_volume_check = sum(item_volumes[i] for j in used_container_indices for i in range(num_items) if solver.Value(x[i, j]))
total_container_boxes = 0
for old_j in used_container_indices:
new_j = container_rebase[old_j]
items_in_container = [i for i in range(num_items) if solver.Value(x[i, old_j])]
total_weight = sum(item_weights[i] for i in items_in_container)
total_volume = sum(item_volumes[i] for i in items_in_container)
container_boxes = [items[i] for i in items_in_container]
total_container_boxes += len(container_boxes)
pct_weight = 100 * total_weight / container_weight if container_weight > 0 else 0
pct_volume = 100 * total_volume / container_volume if container_volume > 0 else 0
print_if_verbose('')
print_if_verbose(f'### Container {new_j}')
print_if_verbose('| Item id | Weight | Volume | Group id |')
for i in items_in_container:
print_if_verbose(f'| {item_ids[i]} | {item_weights[i]} | {item_volumes[i]} | {item_group_ids[i]} |')
print(f'**Total for container {new_j}: total items {len(items_in_container)} weight = {total_weight} ({pct_weight:.1f}% of max), volume = {total_volume} ({pct_volume:.1f}% of max)**')
print(f'Total boxes weight check: {total_boxes_weight_check}')
print(f'Total boxes volume check: {total_boxes_volume_check}')
print(f'Total container boxes: {total_container_boxes}')
if group_ids:
print_if_verbose('')
print_if_verbose('### Group Splits')
print_if_verbose('| Group id | Containers used | Splits (penalized) | Container numbers |')
print_if_verbose('|----------|----------------|--------------------|-------------------|')
for g in group_ids:
containers_for_group = []
for old_j in used_container_indices:
if any(solver.Value(x[i, old_j]) for i in group_to_items[g]):
containers_for_group.append(str(container_rebase[old_j]))
containers_str = ', '.join(containers_for_group)
print_if_verbose(f'| {g} | {solver.Value(group_in_containers[g])} | {group_splits[g]} | {containers_str} |')
print('')
else:
print('No solution found.')
print(f'********************************')