| |
| import time |
| import numpy as np |
| import gurobipy as grb |
| from envs import * |
|
|
|
|
| class PerfectInfoOptimization(): |
| def __init__(self, instance: SimplePlant): |
| self.name = "perfectInfoOptimization" |
| self.instance = instance |
| |
| self.items = range(instance.n_items) |
| self.machines = range(instance.n_machines) |
| self.time_steps = range(instance.T) |
| |
| self.model = grb.Model(self.name) |
|
|
| |
| X = self.model.addVars( |
| instance.n_items, instance.n_machines, instance.T, |
| vtype=grb.GRB.BINARY, |
| name='X' |
| ) |
|
|
| |
| D = self.model.addVars( |
| instance.n_items, instance.n_machines, instance.T, |
| vtype=grb.GRB.BINARY, |
| name='D' |
| ) |
|
|
| |
| I = self.model.addVars( |
| instance.n_items, instance.T + 1, |
| vtype=grb.GRB.CONTINUOUS, |
| lb=0.0, |
| name='I' |
| ) |
| |
| |
| Z = self.model.addVars( |
| instance.n_items, instance.T + 1, |
| vtype=grb.GRB.CONTINUOUS, |
| lb=0.0, |
| name='Z' |
| ) |
| |
| obj_func = grb.quicksum( |
| (instance.lost_sales_costs[i] * Z[i, t] + instance.holding_costs[i] * I[i, t]) |
| for i in self.items |
| for t in range(1, instance.T + 1) |
| ) |
| obj_func += grb.quicksum( |
| instance.setup_costs[m][i] * D[i, m, t] |
| for i in self.items |
| for m in self.machines |
| for t in self.time_steps |
| ) |
|
|
| self.model.setObjective(obj_func, grb.GRB.MINIMIZE) |
|
|
| self.model.addConstrs( |
| (I[i, t] <= instance.max_inventory_level[i] for i in self.items for t in self.time_steps), |
| name='max_inventory' |
| ) |
|
|
| |
| for m in self.machines: |
| for i in self.items: |
| |
| if i == instance.machine_initial_setup[m] - 1: |
| self.model.addConstr( |
| (D[i, m, 0] >= X[i, m, 0] - 1), |
| name=f'initial_state_machine_{m}' |
| ) |
| self.model.addConstr( |
| 1 - X[i, m, 0] + 0.01 <= (1 + 0.01)*(1 - D[i, m, 0]) |
| ) |
| else: |
| |
| self.model.addConstr( |
| (D[i, m, 0] >= X[i, m, 0]), |
| name=f'initial_state_machine_{m}' |
| ) |
| self.model.addConstr( |
| 0 - X[i, m, 0] + 0.01 <= (1 + 0.01)*(1 - D[i, m, 0]) |
| ) |
|
|
| |
| self.model.addConstrs( |
| ( I[i, 0] == instance.initial_inventory[i] for i in self.items), |
| name=f'initial_condition' |
| ) |
| for t in range(1, self.instance.T + 1): |
| |
| self.model.addConstrs( |
| (I[i, t] - Z[i, t] == I[i, t-1] + grb.quicksum( instance.machine_production_matrix[m][i] * X[i, m, t-1] - instance.setup_loss[m][i] * D[i, m, t-1] for m in self.machines ) - self.instance.scenario_demand[i, t-1] for i in self.items), |
| name=f'item_flow_{t}' |
| ) |
| |
| |
| self.model.addConstrs( |
| (grb.quicksum(X[i, m, t] for i in self.items) <= 1 for m in self.machines for t in self.time_steps ), |
| name=f"no_more_setting_machine_node" |
| ) |
|
|
| |
| for i in self.items: |
| for m in self.machines: |
| if instance.machine_production_matrix[m][i] == 0: |
| self.model.addConstrs( |
| (D[i, m, t] == 0 for t in self.time_steps), |
| name=f'no_change_in_forbidden_state' |
| ) |
|
|
| |
| for t in range(1, self.instance.T): |
| self.model.addConstrs( |
| D[i, m, t] >= X[i, m, t] - X[i, m, t-1] |
| for i in self.items for m in self.machines |
| ) |
| self.model.addConstrs( |
| X[i, m, t-1] - X[i, m, t] + 0.01 <= (1 + 0.01)*(1 - D[i, m, t]) |
| for i in self.items for m in self.machines |
| ) |
| self.model.update() |
| self.X = X |
| self.D = D |
| self.I = I |
| self.Z = Z |
| self.obj_func = obj_func |
|
|
| def solve( |
| self, time_limit=None, |
| gap=None, verbose=False, debug_model=False |
| ): |
|
|
| if gap: |
| self.model.setParam('MIPgap', gap) |
| if time_limit: |
| self.model.setParam(grb.GRB.Param.TimeLimit, time_limit) |
| if verbose: |
| self.model.setParam('OutputFlag', 1) |
| else: |
| self.model.setParam('OutputFlag', 0) |
| |
| self.model.setParam('MIPgap', 0.05) |
| |
| self.model.setParam('LogFile', './logs/gurobi.log') |
| if debug_model: |
| self.model.write(f"./logs/{self.name}.lp") |
| |
|
|
| start = time.time() |
| self.model.optimize() |
| end = time.time() |
| comp_time = end - start |
|
|
| if self.model.status == grb.GRB.Status.OPTIMAL: |
| sol = np.zeros((self.instance.n_machines, self.instance.T)) |
| for t in self.time_steps: |
| for m in self.machines: |
| sol[m,t] = sum([ (i+1) * self.X[i, m, t].X for i in self.items]) |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| return self.model.getObjective().getValue(), sol, comp_time |
| else: |
| print("MODEL INFEASIBLE OR UNBOUNDED") |
| return -1, [], comp_time |
|
|