HaLim commited on
Commit
42b5ea5
·
1 Parent(s): ef24926

Finalize optimizer with shift type / partial&bulk work type etc

Browse files
src/config/optimization_config.py CHANGED
@@ -220,8 +220,16 @@ def get_cost_list_per_emp_shift():
220
  print(f"using default value for cost list per emp shift")
221
  shift_cost_df = extract.read_shift_cost_data()
222
  #question - Important : there is multiple type of employment type in terms of the cost
223
- #1 -. unicef 2 - humanizer
224
- return {"UNICEF Fixed term":{1:43,2:43,3:64},"Humanizer":{1:27,2:27,3:41}}
 
 
 
 
 
 
 
 
225
 
226
  COST_LIST_PER_EMP_SHIFT = get_cost_list_per_emp_shift()
227
  # print("cost list per emp shift",COST_LIST_PER_EMP_SHIFT)
@@ -285,16 +293,29 @@ TEAM_REQ_PER_PRODUCT = get_team_requirements(PRODUCT_LIST)
285
  print("team requirements per product:", TEAM_REQ_PER_PRODUCT)
286
 
287
 
288
- MAX_EMPLOYEE_PER_TYPE_ON_DAY = { # Not available information
289
- "UNICEF Fixed term": {
290
- t: 8 for t in DATE_SPAN
291
- }, # EDIT: e.g., {'x': {1:5,2:5,...}, 'y':{1:6,...}}
292
- "Humanizer": {t: 6 for t in DATE_SPAN},
293
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
294
  # available employee but for fixed in shift 1, it is mandatory employment
295
 
296
  MAX_HOUR_PER_PERSON_PER_DAY = 14 # legal standard
297
- MAX_HOUR_PER_SHIFT_PER_PERSON = {1: 7, 2: 7, 3: 3} # work_shifts_timing.csv #Need to be fixed
298
  def get_per_product_speed():
299
  try:
300
  streamlit_per_product_speed = dashboard.per_product_speed
@@ -344,3 +365,34 @@ DAILY_WEEKLY_SCHEDULE = "daily" # daily or weekly ,this needs to be implemented
344
  # "none" - Purely demand-driven scheduling (cost-efficient)
345
  FIXED_STAFF_CONSTRAINT_MODE = "priority" # Recommended: "priority" for realistic business model
346
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
220
  print(f"using default value for cost list per emp shift")
221
  shift_cost_df = extract.read_shift_cost_data()
222
  #question - Important : there is multiple type of employment type in terms of the cost
223
+ #Shift 1 = normal, 2 = evening, 3 = overtime
224
+ return {"UNICEF Fixed term":{1:43.27,2:43.27,3:64.91},"Humanizer":{1:27.94,2:27.94,3:41.91}}
225
+
226
+ def shift_code_to_name():
227
+ shift_code_to_name_dict = {
228
+ 1: "normal",
229
+ 2: "evening",
230
+ 3: "overtime"
231
+ }
232
+ return shift_code_to_name_dict
233
 
234
  COST_LIST_PER_EMP_SHIFT = get_cost_list_per_emp_shift()
235
  # print("cost list per emp shift",COST_LIST_PER_EMP_SHIFT)
 
293
  print("team requirements per product:", TEAM_REQ_PER_PRODUCT)
294
 
295
 
296
+ def get_max_employee_per_type_on_day():
297
+ try:
298
+ max_employee_per_type_on_day = dashboard.max_employee_per_type_on_day
299
+ return max_employee_per_type_on_day
300
+ except Exception as e:
301
+ print(f"using default value for max employee per type on day")
302
+ max_employee_per_type_on_day = {
303
+ "UNICEF Fixed term": {
304
+ t: 8 for t in DATE_SPAN
305
+ },
306
+ "Humanizer": {
307
+ t: 10 for t in DATE_SPAN
308
+ }
309
+ }
310
+ return max_employee_per_type_on_day
311
+
312
+ MAX_EMPLOYEE_PER_TYPE_ON_DAY = get_max_employee_per_type_on_day()
313
+ print("max employee per type on day",MAX_EMPLOYEE_PER_TYPE_ON_DAY)
314
+
315
  # available employee but for fixed in shift 1, it is mandatory employment
316
 
317
  MAX_HOUR_PER_PERSON_PER_DAY = 14 # legal standard
318
+ MAX_HOUR_PER_SHIFT_PER_PERSON = {1: 7.5, 2: 7.5, 3: 5} #1 = normal, 2 = evening, 3 = overtime
319
  def get_per_product_speed():
320
  try:
321
  streamlit_per_product_speed = dashboard.per_product_speed
 
365
  # "none" - Purely demand-driven scheduling (cost-efficient)
366
  FIXED_STAFF_CONSTRAINT_MODE = "priority" # Recommended: "priority" for realistic business model
367
 
368
+
369
+ def get_payment_mode_config():
370
+ """
371
+ Get payment mode configuration - try from streamlit session state first, then default values
372
+ Payment modes:
373
+ - "bulk": If employee works any hours in shift, pay for full shift hours
374
+ - "partial": Pay only for actual hours worked
375
+ """
376
+ try:
377
+ # Try to get from streamlit session state (from Dataset Metadata page)
378
+ import streamlit as st
379
+ if hasattr(st, 'session_state') and 'payment_mode_config' in st.session_state:
380
+ print(f"Using payment mode config from streamlit session: {st.session_state.payment_mode_config}")
381
+ return st.session_state.payment_mode_config
382
+ except Exception as e:
383
+ print(f"Could not get payment mode config from streamlit session: {e}")
384
+
385
+ # Default payment mode configuration
386
+ # Shift 1: bulk, Shift 2: bulk (evening), Shift 3: partial (overtime)
387
+ print(f"Loading default payment mode configuration")
388
+ payment_mode_config = {
389
+ 1: "bulk", # Regular shift - bulk payment
390
+ 2: "bulk", # Evening shift - bulk payment
391
+ 3: "partial" # Overtime shift - partial payment
392
+ }
393
+
394
+ return payment_mode_config
395
+
396
+ PAYMENT_MODE_CONFIG = get_payment_mode_config()
397
+ print("Payment mode configuration:", PAYMENT_MODE_CONFIG)
398
+
src/models/optimizer_new_aug14.py CHANGED
@@ -28,6 +28,7 @@ from src.config.optimization_config import (
28
  DAILY_WEEKLY_SCHEDULE, # 'daily' or 'weekly' (여기선 weekly로 모델링)
29
  FIXED_STAFF_CONSTRAINT_MODE, # not used in fixed-team model (동시 투입이라 무의미)
30
  TEAM_REQ_PER_PRODUCT, # {emp_type: {product: team_size}} from Kits_Calculation.csv
 
31
  KIT_LINE_MATCH_DICT,
32
  EVENING_SHIFT_MODE,
33
  EVENING_SHIFT_DEMAND_THRESHOLD,
@@ -197,12 +198,47 @@ def solve_fixed_team_weekly():
197
  T[p, ell, s, t] = solver.NumVar(0, Hmax_s[s], f"T_{p}_{ell[0]}_{ell[1]}_s{s}_d{t}")
198
  U[p, ell, s, t] = solver.NumVar(0, INF, f"U_{p}_{ell[0]}_{ell[1]}_s{s}_d{t}")
199
 
200
- # --- Objective: total labor cost + hierarchy timing penalty ---
201
- # Primary objective: minimize labor cost
202
- total_cost = solver.Sum(
203
- cost[e][s] * TEAM_REQ_PER_PRODUCT[e][p] * T[p, ell, s, t]
204
- for e in E for s in S for p in P for ell in L for t in D
205
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
 
207
  # Secondary objective: encourage earlier production of dependencies (soft constraint)
208
  # Small weight (0.01) to prioritize hierarchy without overwhelming cost optimization
@@ -290,6 +326,8 @@ def solve_fixed_team_weekly():
290
  )
291
  # (if needed, evening(3) after usual(1): sum(...)_s=3 ≤ sum(...)_s=1)
292
 
 
 
293
  # 8) *** HIERARCHY DEPENDENCY CONSTRAINTS ***
294
  # For subkits with prepack dependencies: dependencies should be produced before or same time
295
  print("\n[HIERARCHY] Adding dependency constraints...")
 
28
  DAILY_WEEKLY_SCHEDULE, # 'daily' or 'weekly' (여기선 weekly로 모델링)
29
  FIXED_STAFF_CONSTRAINT_MODE, # not used in fixed-team model (동시 투입이라 무의미)
30
  TEAM_REQ_PER_PRODUCT, # {emp_type: {product: team_size}} from Kits_Calculation.csv
31
+ PAYMENT_MODE_CONFIG, # {shift: 'bulk'/'partial'} payment mode configuration
32
  KIT_LINE_MATCH_DICT,
33
  EVENING_SHIFT_MODE,
34
  EVENING_SHIFT_DEMAND_THRESHOLD,
 
198
  T[p, ell, s, t] = solver.NumVar(0, Hmax_s[s], f"T_{p}_{ell[0]}_{ell[1]}_s{s}_d{t}")
199
  U[p, ell, s, t] = solver.NumVar(0, INF, f"U_{p}_{ell[0]}_{ell[1]}_s{s}_d{t}")
200
 
201
+ # Note: Binary variables for bulk payment are now created inline in the cost calculation
202
+
203
+ # --- Objective: total labor cost with payment modes + hierarchy timing penalty ---
204
+ print(f"Payment mode configuration: {PAYMENT_MODE_CONFIG}")
205
+
206
+ # Build cost terms based on payment mode
207
+ cost_terms = []
208
+
209
+ for e in E:
210
+ for s in S:
211
+ payment_mode = PAYMENT_MODE_CONFIG.get(s, "partial") # Default to partial if not specified
212
+
213
+ if payment_mode == "partial":
214
+ # Partial payment: pay for actual hours worked
215
+ for p in P:
216
+ for ell in L:
217
+ for t in D:
218
+ cost_terms.append(cost[e][s] * TEAM_REQ_PER_PRODUCT[e][p] * T[p, ell, s, t])
219
+
220
+ elif payment_mode == "bulk":
221
+ # Bulk payment: if employees work ANY hours in a shift, pay them for FULL shift hours
222
+ # BUT only pay the employees who actually work, not all employees of that type
223
+ for p in P:
224
+ for ell in L:
225
+ for t in D:
226
+ # Calculate actual employees working: TEAM_REQ_PER_PRODUCT[e][p] employees work T[p,ell,s,t] hours
227
+ # For bulk payment: if T[p,ell,s,t] > 0, pay TEAM_REQ_PER_PRODUCT[e][p] employees for full shift
228
+ # We need a binary variable for each (e,s,p,ell,t) combination
229
+ # But we can use the existing logic: if T > 0, then those specific employees get bulk pay
230
+
231
+ # Create binary variable for this specific work assignment
232
+ work_binary = solver.BoolVar(f"work_{e}_s{s}_{p}_{ell[0]}{ell[1]}_d{t}")
233
+
234
+ # Link work_binary to T[p,ell,s,t]: work_binary = 1 if T > 0
235
+ solver.Add(T[p, ell, s, t] <= Hmax_s[s] * work_binary)
236
+ solver.Add(work_binary * 0.001 <= T[p, ell, s, t])
237
+
238
+ # Cost: pay the specific working employees for full shift hours
239
+ cost_terms.append(cost[e][s] * Hmax_s[s] * TEAM_REQ_PER_PRODUCT[e][p] * work_binary)
240
+
241
+ total_cost = solver.Sum(cost_terms)
242
 
243
  # Secondary objective: encourage earlier production of dependencies (soft constraint)
244
  # Small weight (0.01) to prioritize hierarchy without overwhelming cost optimization
 
326
  )
327
  # (if needed, evening(3) after usual(1): sum(...)_s=3 ≤ sum(...)_s=1)
328
 
329
+ # 7.5) Bulk payment linking constraints are now handled inline in the cost calculation
330
+
331
  # 8) *** HIERARCHY DEPENDENCY CONSTRAINTS ***
332
  # For subkits with prepack dependencies: dependencies should be produced before or same time
333
  print("\n[HIERARCHY] Adding dependency constraints...")