| | |
| | |
| | |
| | |
| | from ortools.linear_solver import pywraplp |
| | import pandas as pd |
| | import sys |
| | import os |
| |
|
| | sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) |
| |
|
| | from src.config import optimization_config |
| | import importlib |
| |
|
| | importlib.reload(optimization_config) |
| |
|
| |
|
| | class OptimizerReal: |
| | def __init__(self): |
| | self.config = optimization_config |
| |
|
| | def solve_option_A_multi_day_generalized(self): |
| | |
| | |
| | |
| | |
| | days = self.config.DATE_SPAN |
| |
|
| | |
| | |
| | product_list = self.config.PRODUCT_LIST |
| |
|
| | |
| | employee_types = self.config.EMPLOYEE_TYPE_LIST |
| |
|
| | |
| | shift_list = self.config.SHIFT_LIST |
| |
|
| | |
| | line_list = self.config.LINE_LIST |
| | line_cnt_per_type = self.config.LINE_LIST_PER_TYPE |
| | line_type_cnt_tuple = [ |
| | (t, i) for t in line_list for i in range(1, line_cnt_per_type[t] + 1) |
| | ] |
| |
|
| | |
| | |
| | |
| | |
| | weekly_demand = self.config.DEMAND_LIST |
| |
|
| | |
| | |
| | active = { |
| | t: {p: 1 for p in product_list} for t in days |
| | } |
| |
|
| | |
| | wage_types = self.config.COST_LIST_PER_EMP_SHIFT |
| |
|
| | |
| | |
| | productivities = self.config.PRODUCTIVITY_LIST_PER_EMP_PRODUCT |
| | |
| |
|
| | |
| | |
| | N_day = self.config.MAX_EMPLOYEE_PER_TYPE_ON_DAY |
| |
|
| | |
| | Hmax_daily_per_person = ( |
| | self.config.MAX_HOUR_PER_PERSON_PER_DAY |
| | ) |
| | Hmax_shift = self.config.MAX_HOUR_PER_SHIFT_PER_PERSON |
| | |
| | Cap = self.config.CAP_PER_LINE_PER_HOUR |
| |
|
| | |
| | |
| | |
| | |
| | first_shift_hour = Hmax_shift[1] |
| | daily_weekly_type = self.config.DAILY_WEEKLY_SCHEDULE |
| | print(first_shift_hour) |
| | F_x1_day = { |
| | t: first_shift_hour * N_day["Fixed"][t] + 1 for t in days |
| | } |
| | print(F_x1_day) |
| | F_x1_week = None |
| | cap_per_line_per_hour = self.config.CAP_PER_LINE_PER_HOUR |
| | |
| | allow = {} |
| | for e in employee_types: |
| | for p in product_list: |
| | for ell in line_type_cnt_tuple: |
| | allow[(e, p, ell)] = 1 |
| |
|
| | |
| | |
| | |
| | solver = pywraplp.Solver.CreateSolver("CBC") |
| | if not solver: |
| | raise RuntimeError("Failed to create solver. Check OR-Tools installation.") |
| | INF = solver.infinity() |
| |
|
| | |
| | |
| | |
| | |
| | h = {} |
| | for e in employee_types: |
| | for s in shift_list: |
| | for p in product_list: |
| | for ell in line_type_cnt_tuple: |
| | for t in days: |
| | |
| | ub = Hmax_shift[s] * N_day[e][t] |
| | h[e, s, p, ell, t] = solver.IntVar( |
| | 0, ub, f"h_{e}_{s}_{p}_{ell[0]}{ell[1]}_d{t}" |
| | ) |
| |
|
| | |
| | u = {} |
| | for p in product_list: |
| | for ell in line_type_cnt_tuple: |
| | for s in shift_list: |
| | for t in days: |
| | u[p, ell, s, t] = solver.NumVar( |
| | 0, INF, f"u_{p}_{ell[0]}{ell[1]}_{s}_d{t}" |
| | ) |
| |
|
| | |
| | tline = {} |
| | for ell in line_type_cnt_tuple: |
| | for s in shift_list: |
| | for t in days: |
| | tline[ell, s, t] = solver.NumVar( |
| | 0, Hmax_shift[s], f"t_{ell[0]}{ell[1]}_{s}_d{t}" |
| | ) |
| |
|
| | |
| | ybin = {} |
| | for e in employee_types: |
| | for s in shift_list: |
| | for t in days: |
| | ybin[e, s, t] = solver.BoolVar(f"y_{e}_{s}_d{t}") |
| |
|
| | |
| | |
| | |
| | solver.Minimize( |
| | solver.Sum( |
| | wage_types[e][s] * h[e, s, p, ell, t] |
| | for e in employee_types |
| | for s in shift_list |
| | for p in product_list |
| | for ell in line_type_cnt_tuple |
| | for t in days |
| | ) |
| | ) |
| |
|
| | |
| | |
| | |
| |
|
| | |
| | for p in product_list: |
| | solver.Add( |
| | solver.Sum(u[p, ell, s, t] for ell in line_type_cnt_tuple for s in shift_list for t in days) |
| | >= weekly_demand.get(p, 0) |
| | ) |
| |
|
| | |
| | |
| | BIG_H = max(Hmax_shift.values()) * sum(N_day[e][t] for e in employee_types for t in days) |
| | for p in product_list: |
| | for t in days: |
| | if active[t][p] == 0: |
| | for ell in line_type_cnt_tuple: |
| | for s in shift_list: |
| | solver.Add(u[p, ell, s, t] == 0) |
| | for e in employee_types: |
| | solver.Add(h[e, s, p, ell, t] == 0) |
| |
|
| | |
| | |
| | for p in product_list: |
| | for ell in line_type_cnt_tuple: |
| | for s in shift_list: |
| | for t in days: |
| | |
| | solver.Add( |
| | u[p, ell, s, t] |
| | <= solver.Sum(productivities[e][s][p] * h[e, s, p, ell, t] for e in employee_types) |
| | ) |
| |
|
| | |
| | for ell in line_type_cnt_tuple: |
| | for s in shift_list: |
| | for t in days: |
| | line_type = ell[0] |
| | solver.Add( |
| | solver.Sum(u[p, ell, s, t] for p in product_list) |
| | <= cap_per_line_per_hour[line_type] * tline[ell, s, t] |
| | ) |
| |
|
| | |
| | for ell in line_type_cnt_tuple: |
| | for s in shift_list: |
| | for t in days: |
| | solver.Add( |
| | tline[ell, s, t] |
| | == solver.Sum(h[e, s, p, ell, t] for e in employee_types for p in product_list) |
| | ) |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | |
| | if F_x1_day is not None: |
| | |
| | for t in days: |
| | solver.Add( |
| | solver.Sum(h["Fixed", 1, p, ell, t] for p in product_list for ell in line_type_cnt_tuple) |
| | == F_x1_day[t] |
| | ) |
| | elif F_x1_week is not None: |
| | |
| | solver.Add( |
| | solver.Sum( |
| | h["Fixed", 1, p, ell, t] for p in product_list for ell in line_type_cnt_tuple for t in days |
| | ) |
| | == F_x1_week |
| | ) |
| | else: |
| | raise ValueError( |
| | "Specify either F_x1_day (dict by day) or F_x1_week (scalar)." |
| | ) |
| |
|
| | |
| | for e in employee_types: |
| | for t in days: |
| | solver.Add( |
| | solver.Sum( |
| | h[e, s, p, ell, t] for s in shift_list for p in product_list for ell in line_type_cnt_tuple |
| | ) |
| | <= Hmax_daily_per_person * N_day[e][t] |
| | ) |
| |
|
| | |
| | |
| | for e in employee_types: |
| | for s in shift_list: |
| | for t in days: |
| | M_e_s_t = Hmax_shift[s] * N_day[e][t] |
| | solver.Add( |
| | solver.Sum(h[e, s, p, ell, t] for p in product_list for ell in line_type_cnt_tuple) |
| | <= M_e_s_t * ybin[e, s, t] |
| | ) |
| |
|
| | |
| | for e in employee_types: |
| | for t in days: |
| | solver.Add(ybin[e, 2, t] <= ybin[e, 1, t]) |
| | solver.Add( |
| | solver.Sum(h[e, 2, p, ell, t] for p in product_list for ell in line_type_cnt_tuple) |
| | <= solver.Sum(h[e, 1, p, ell, t] for p in product_list for ell in line_type_cnt_tuple) |
| | ) |
| | |
| | |
| | |
| | |
| |
|
| | |
| | for e in employee_types: |
| | for p in product_list: |
| | for ell in line_type_cnt_tuple: |
| | if allow[(e, p, ell)] == 0: |
| | for s in shift_list: |
| | for t in days: |
| | solver.Add(h[e, s, p, ell, t] == 0) |
| |
|
| | |
| | |
| | |
| | status = solver.Solve() |
| | if status != pywraplp.Solver.OPTIMAL: |
| | print("No optimal solution. Status:", status) |
| | return |
| |
|
| | |
| | |
| | |
| | print("Objective (min cost):", solver.Objective().Value()) |
| |
|
| | print("\n--- Weekly production by product ---") |
| | for p in product_list: |
| | produced = sum( |
| | u[p, ell, s, t].solution_value() for ell in line_type_cnt_tuple for s in shift_list for t in days |
| | ) |
| | print(f"{p}: {produced:.1f} (weekly demand {weekly_demand.get(p,0)})") |
| |
|
| | print("\n--- Line operating hours by shift/day ---") |
| | for ell in line_type_cnt_tuple: |
| | for s in shift_list: |
| | hours = [tline[ell, s, t].solution_value() for t in days] |
| | if sum(hours) > 1e-6: |
| | print( |
| | f"Line {ell} Shift {s}: " |
| | + ", ".join([f"days{t}={hours[t-1]:.2f}h" for t in days]) |
| | ) |
| |
|
| | print("\n--- Hours by employee type / shift / day ---") |
| | for e in employee_types: |
| | for s in shift_list: |
| | day_hours = [ |
| | sum(h[e, s, p, ell, t].solution_value() for p in product_list for ell in line_type_cnt_tuple) |
| | for t in days |
| | ] |
| | if sum(day_hours) > 1e-6: |
| | print( |
| | f"e={e}, s={s}: " |
| | + ", ".join([f"days{t}={day_hours[t-1]:.2f}h" for t in days]) |
| | ) |
| |
|
| | print("\n--- Implied headcount by type / shift / day ---") |
| | for e in employee_types: |
| | print(e) |
| | for s in shift_list: |
| | row = [] |
| | for t in days: |
| | hours = sum( |
| | h[e, s, p, ell, t].solution_value() for p in product_list for ell in line_type_cnt_tuple |
| | ) |
| | need = int((hours + Hmax_shift[s] - 1) // Hmax_shift[s]) |
| | row.append(f"days{t}={need}") |
| |
|
| | if any("=0" not in Fixed for Fixed in row): |
| | print(f"e={e}, s={s}: " + ", ".join(row)) |
| |
|
| |
|
| | if __name__ == "__main__": |
| | optimizer = OptimizerReal() |
| | optimizer.solve_option_A_multi_day_generalized() |
| |
|