haileyhalimj@gmail.com
commited on
Commit
ยท
d8fcfd0
1
Parent(s):
951e634
clean code for optimizer
Browse files- src/models/optimizer_real.py +32 -28
src/models/optimizer_real.py
CHANGED
|
@@ -26,7 +26,6 @@ from src.config.optimization_config import (
|
|
| 26 |
get_max_hour_per_shift_per_person, # DYNAMIC: {1: hours, 2: hours, 3: hours}
|
| 27 |
get_per_product_speed, # DYNAMIC: {6: cap_units_per_hour, 7: cap_units_per_hour}
|
| 28 |
get_max_parallel_workers, # DYNAMIC: {6: max_workers, 7: max_workers}
|
| 29 |
-
DAILY_WEEKLY_SCHEDULE, # 'daily' or 'weekly'
|
| 30 |
FIXED_STAFF_CONSTRAINT_MODE, # not used in fixed-team model (๋์ ํฌ์
์ด๋ผ ๋ฌด์๋ฏธ)
|
| 31 |
get_team_requirements, # DYNAMIC: {emp_type: {product: team_size}} from Kits_Calculation.csv
|
| 32 |
get_payment_mode_config, # DYNAMIC: {shift: 'bulk'/'partial'} payment mode configuration
|
|
@@ -41,9 +40,6 @@ from src.config.optimization_config import (
|
|
| 41 |
get_fixed_min_unicef_per_day, # DYNAMIC: Minimum UNICEF employees required per day
|
| 42 |
)
|
| 43 |
|
| 44 |
-
# -----------------------------------------
|
| 45 |
-
# ์ถ๊ฐ ํ๋ผ๋ฏธํฐ ์ค์ (config์ ์๋ ๊ฒ๋ค) - TODO: ์ค์ ๊ฐ ์ฑ์ฐ์ธ์
|
| 46 |
-
# -----------------------------------------
|
| 47 |
|
| 48 |
|
| 49 |
# 2) kit_line_match
|
|
@@ -98,39 +94,51 @@ def sort_products_by_hierarchy(product_list):
|
|
| 98 |
in_degree[product] = 0
|
| 99 |
|
| 100 |
# Build edges based on actual dependencies
|
| 101 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 102 |
for product in products_with_hierarchy:
|
| 103 |
-
deps = KIT_DEPENDENCIES.get(product, [])
|
| 104 |
for dep in deps:
|
| 105 |
if dep in products_with_hierarchy: # Only if dependency is in our production list
|
| 106 |
-
|
|
|
|
|
|
|
|
|
|
| 107 |
in_degree[product] += 1
|
| 108 |
-
dependency_count += 1
|
| 109 |
-
|
| 110 |
-
print(f"[HIERARCHY] Found {dependency_count} dependency relationships in production list")
|
| 111 |
|
| 112 |
# Topological sort with hierarchy level priority
|
| 113 |
sorted_products = []
|
|
|
|
| 114 |
queue = deque()
|
| 115 |
|
| 116 |
-
# Start with products that have no dependencies
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
for _, product in no_deps:
|
| 121 |
-
queue.append(product)
|
| 122 |
|
| 123 |
while queue:
|
| 124 |
current = queue.popleft()
|
| 125 |
sorted_products.append(current)
|
| 126 |
|
| 127 |
-
# Process dependents
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
for _, dependent in dependents:
|
| 132 |
-
in_degree[dependent] -= 1
|
| 133 |
-
if in_degree[dependent] == 0:
|
| 134 |
queue.append(dependent)
|
| 135 |
|
| 136 |
# Check for cycles (shouldn't happen with proper hierarchy)
|
|
@@ -141,7 +149,7 @@ def sort_products_by_hierarchy(product_list):
|
|
| 141 |
remaining_sorted = sorted(remaining, key=lambda p: (KIT_LEVELS.get(p, 999), p))
|
| 142 |
sorted_products.extend(remaining_sorted)
|
| 143 |
|
| 144 |
-
# Add products without hierarchy at the end
|
| 145 |
sorted_products.extend(sorted(products_without_hierarchy))
|
| 146 |
|
| 147 |
print(f"[HIERARCHY] Dependency-aware production order: {len(sorted_products)} products")
|
|
@@ -249,10 +257,6 @@ def solve_fixed_team_weekly():
|
|
| 249 |
print(f" RECOMMENDATION: Change EVENING_SHIFT_MODE to 'activate_evening' to enable evening shift")
|
| 250 |
print(f" This will add shift 3 to increase capacity\n")
|
| 251 |
|
| 252 |
-
# 3) DAILY_WEEKLY_SCHEDULE warning (current model only enforces weekly demand)
|
| 253 |
-
if DAILY_WEEKLY_SCHEDULE.lower() == "daily":
|
| 254 |
-
print("[INFO] DAILY_WEEKLY_SCHEDULE='daily' but this model only enforces weekly demand. "
|
| 255 |
-
"If daily demand decomposition is needed, please let us know.")
|
| 256 |
|
| 257 |
# --- Solver ---
|
| 258 |
solver = pywraplp.Solver.CreateSolver('CBC')
|
|
|
|
| 26 |
get_max_hour_per_shift_per_person, # DYNAMIC: {1: hours, 2: hours, 3: hours}
|
| 27 |
get_per_product_speed, # DYNAMIC: {6: cap_units_per_hour, 7: cap_units_per_hour}
|
| 28 |
get_max_parallel_workers, # DYNAMIC: {6: max_workers, 7: max_workers}
|
|
|
|
| 29 |
FIXED_STAFF_CONSTRAINT_MODE, # not used in fixed-team model (๋์ ํฌ์
์ด๋ผ ๋ฌด์๋ฏธ)
|
| 30 |
get_team_requirements, # DYNAMIC: {emp_type: {product: team_size}} from Kits_Calculation.csv
|
| 31 |
get_payment_mode_config, # DYNAMIC: {shift: 'bulk'/'partial'} payment mode configuration
|
|
|
|
| 40 |
get_fixed_min_unicef_per_day, # DYNAMIC: Minimum UNICEF employees required per day
|
| 41 |
)
|
| 42 |
|
|
|
|
|
|
|
|
|
|
| 43 |
|
| 44 |
|
| 45 |
# 2) kit_line_match
|
|
|
|
| 94 |
in_degree[product] = 0
|
| 95 |
|
| 96 |
# Build edges based on actual dependencies
|
| 97 |
+
# KIT_DEPENDENCIES = {product: [dependencies]} - "What does THIS product need?"
|
| 98 |
+
# graph = {dependency: [products]} - "What depends on THIS dependency?"
|
| 99 |
+
#
|
| 100 |
+
# Example transformation:
|
| 101 |
+
# KIT_DEPENDENCIES = {'subkit_A': ['prepack_1'], 'master_B': ['subkit_A']}
|
| 102 |
+
# After building: graph = {'prepack_1': ['subkit_A'], 'subkit_A': ['master_B']}
|
| 103 |
+
# This means: prepack_1 is needed by subkit_A, subkit_A is needed by master_B
|
| 104 |
+
#
|
| 105 |
+
# Example:
|
| 106 |
+
# 1. product='subkit_A', deps=['prepack_1']
|
| 107 |
+
# โ graph['prepack_1'].append('subkit_A')
|
| 108 |
+
# โ graph = {'prepack_1': ['subkit_A']}
|
| 109 |
+
# 2. product='master_B', deps=['subkit_A']
|
| 110 |
+
# โ graph['subkit_A'].append('master_B')
|
| 111 |
+
# โ graph = {'prepack_1': ['subkit_A'], 'subkit_A': ['master_B']}
|
| 112 |
+
|
| 113 |
+
|
| 114 |
for product in products_with_hierarchy:
|
| 115 |
+
deps = KIT_DEPENDENCIES.get(product, []) #dependencies = products that has to be packed first
|
| 116 |
for dep in deps:
|
| 117 |
if dep in products_with_hierarchy: # Only if dependency is in our production list
|
| 118 |
+
# REVERSE THE RELATIONSHIP:
|
| 119 |
+
# KIT_DEPENDENCIES says: "product needs dep"
|
| 120 |
+
# graph says: "dep is needed by product"
|
| 121 |
+
graph[dep].append(product) # dep -> product (reverse the relationship!)
|
| 122 |
in_degree[product] += 1
|
|
|
|
|
|
|
|
|
|
| 123 |
|
| 124 |
# Topological sort with hierarchy level priority
|
| 125 |
sorted_products = []
|
| 126 |
+
#queue = able to remove from both sides
|
| 127 |
queue = deque()
|
| 128 |
|
| 129 |
+
# Start with products that have no dependencies
|
| 130 |
+
for product in products_with_hierarchy:
|
| 131 |
+
if in_degree[product] == 0:
|
| 132 |
+
queue.append(product)
|
|
|
|
|
|
|
| 133 |
|
| 134 |
while queue:
|
| 135 |
current = queue.popleft()
|
| 136 |
sorted_products.append(current)
|
| 137 |
|
| 138 |
+
# Process dependents - sort by hierarchy level first
|
| 139 |
+
for dependent in sorted(graph[current], key=lambda p: (KIT_LEVELS.get(p, 999), p)):
|
| 140 |
+
in_degree[dependent] -= 1 #decrement the in_degree of the dependent
|
| 141 |
+
if in_degree[dependent] == 0: #if the in_degree of the dependent is 0, add it to the queue so that it can be processed
|
|
|
|
|
|
|
|
|
|
| 142 |
queue.append(dependent)
|
| 143 |
|
| 144 |
# Check for cycles (shouldn't happen with proper hierarchy)
|
|
|
|
| 149 |
remaining_sorted = sorted(remaining, key=lambda p: (KIT_LEVELS.get(p, 999), p))
|
| 150 |
sorted_products.extend(remaining_sorted)
|
| 151 |
|
| 152 |
+
# Add products without hierarchy information at the end
|
| 153 |
sorted_products.extend(sorted(products_without_hierarchy))
|
| 154 |
|
| 155 |
print(f"[HIERARCHY] Dependency-aware production order: {len(sorted_products)} products")
|
|
|
|
| 257 |
print(f" RECOMMENDATION: Change EVENING_SHIFT_MODE to 'activate_evening' to enable evening shift")
|
| 258 |
print(f" This will add shift 3 to increase capacity\n")
|
| 259 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 260 |
|
| 261 |
# --- Solver ---
|
| 262 |
solver = pywraplp.Solver.CreateSolver('CBC')
|