def generate_row_model(row_matrix, b, v, c): """ rows_matrix: The matrix, represented as a list of rows b: List of variables in b v: The target vector c: The weights of each column """ # Initializes a model mdl = Model('persistenthomologylocalization') # Add matrix variables x = mdl.addVars(list(b), vtype=GRB.BINARY) # Add the dummy variable needed to do arithmetics mod 2 y = mdl.addVars(list(range(len(row_matrix))), vtype=GRB.INTEGER) # Set model to minimization mdl.modelSense = GRB.MINIMIZE # Set objective function to minimize mdl.setObjective(quicksum(x[j] * c[j] for j in b)) # Set the constrains for i in list(range(len(row_matrix))): mdl.addConstr(quicksum(x[j] for j in row_matrix[i]) + v[i] == y[i] * 2) return mdl, x, y
def create_model(self): model = Model() _A = [(i,t) for i in self.tasks for t in self.periods] x = model.addVars(_A, vtype = GRB.BINARY) # create decision variables b = model.addVars(_A, vtype = GRB.BINARY) # create decision variables # create constraints model.addConstrs(quicksum(self.p[i,t] * (1 - x[i,t]) for i in self.tasks) - \ self.d[t] >= 0 for t in self.periods) # 1 model.addConstrs(quicksum(b[i,t] for t in self.periods) == 1 \ for i in self.tasks) # 2 model.addConstrs(x[i,t] >= b[i,t] for i in self.tasks for t in self.periods) # 3 model.addConstrs(x[i,t] - x[i,t-1] <= b[i,t] for i in self.tasks \ for t in self.periods[1:]) # 3, t >= 2 _tmp = self.periods[1:] # model.addConstrs(x[i,t] <= b[i,t] for t in _tmp \ # for i in self.tasks) # 4 model.addConstrs(x[i,t] + x[i,t-1] + b[i,t] <= 1+self.LP[i] for t in self.periods[1:] \ for i in self.tasks) # 4 model.addConstrs(quicksum(x[i,t] for t in self.periods) == self.LP[i] \ for i in self.tasks) # 5 model.addConstrs(quicksum(x[i,t] for i in self.tasks) <= self.LT[t] \ for t in self.periods) # 6 model.addConstrs(quicksum(b[i,k] for k in self.periods) - b[j,t] >= 0 \ for t in self.periods for i in self.tasks for j in self.tasks if j!=i) # 7 model.addConstrs(x[i,t] + x[j,t] <= 1 for t in self.periods \ for i in self.tasks for j in self.tasks if j!=i) # 8 model.addConstrs(quicksum(b[i,t] for t in self.periods[:self.L[i]-self.LP[i]+2]) == 1 \ for i in self.tasks) # 9 # model.addConstrs(quicksum(x[i,t] == 0 for t in self.U) for i in self.tasks) # 10 model.addConstrs(x[i,t] == 0 for t in self.U for i in self.tasks) # 10 model.addConstrs(quicksum((self.MV[i] + self.MH[i] + self.ML[i]) * x[i,t] \ for i in self.tasks) <= self.AM[t] for t in self.periods) # 11 model.addConstrs(quicksum(self.V[i] * x[i,t] for i in self.tasks) \ <= self.AV[t] for t in self.periods) # 12 model.addConstrs(quicksum(self.H[i] * x[i,t] for i in self.tasks) <= \ self.AH[t] for t in self.periods) # 13 # create costs C_M, C_T = {}, {} for i in self.tasks: for t in self.periods: C_M[i,t] = self.C_MV[t] * self.MV[i] + self.C_MH[t] * self.MH[i] + \ self.C_ML[t] * self.ML[i] C_T[i,t] = (self.C_FV * self.V[i] + self.C_FH * self.H[i])/self.LP[i] + \ self.C_SV[i,t] * self.V[i] + self.C_SH[i,t] * self.H[i] model.modelSense = GRB.MINIMIZE # create ojective function model.setObjective(quicksum((C_M[i,t] + self.C_EQ[i,t] + self.C_I[i,t] + \ C_T[i,t]) * x[i,t] for t in self.periods for i in self.tasks)) # model.setParam('OutputFlag', 0) model.optimize() return
def generateInstance(self): def euc_dist(bor, sh): dx = bor["x_coord"] - sh["x_coord"] dy = bor["y_coord"] - sh["y_coord"] return math.sqrt(dx * dx + dy * dy) model = Model('FireSolver') # Generate variables x = {} for bor in self.boroughs: # New firehouses for fh in self.new_firehouses + self.old_firehouses: name = "x_" + fh["loc_id"] + "_" + bor["loc_id"] x[bor["loc_id"], fh["loc_id"]] = model.addVar(name=name, vtype ="b", obj=self.cost_coef * euc_dist(bor, fh)) # Open variables openfh = {} for fh in self.new_firehouses: openfh[fh["loc_id"]] = model.addVar(name = "open_" + fh["loc_id"], vtype ="b", obj=fh["construction_cost"]) # Close variables closefh = {} for fh in self.old_firehouses: closefh[fh["loc_id"]] = model.addVar(name = "close_" + fh["loc_id"], vtype ="b", obj=fh["destruction_cost"]) model.modelSense = GRB.MINIMIZE model.update() # Constraints: one firehouse / borough for bor in self.boroughs: model.addConstr(quicksum(x[key] for key in x if key[0] == bor["loc_id"]) == 1) # capacity of firehouses for fh in self.new_firehouses: model.addConstr(quicksum(x[key] for key in x if key[1] == fh["loc_id"]) <= self.capacity * openfh[fh["loc_id"]]) # If it is not removed, the initial assignment needs to be respected for fh in self.old_firehouses: for bor in self.boroughs: if bor["currently_protected_by"] == fh["loc_id"]: model.addConstr(x[bor["loc_id"], fh["loc_id"]] == 1 - closefh[fh["loc_id"]]) else: model.addConstr(x[bor["loc_id"], fh["loc_id"]] == 0) # solve it model.optimize() self.model = model
def _mmp_solve(w1_ij, x_ij_keys, n, w2_ij=None): """A helper function that solves a weighted maximum matching problem. """ m = Model("MBSA") if __debug__: log(DEBUG, "") log( DEBUG, "Solving a weighted maximum matching problem with " + "%d savings weights." % len(w1_ij)) # build model x_ij = m.addVars(x_ij_keys, obj=w1_ij, vtype=GRB.BINARY, name='x') _mmp_add_cnts_sum(m, x_ij, x_ij_keys, w1_ij, n) m._vars = x_ij m.modelSense = GRB.MAXIMIZE m.update() # disable output m.setParam('OutputFlag', 0) m.setParam('TimeLimit', MAX_MIP_SOLVER_RUNTIME) m.setParam('Threads', MIP_SOLVER_THREADS) #m.write("out.lp") m.optimize() # restore SIGINT callback handler which is changed by gurobipy signal(SIGINT, default_int_handler) if __debug__: log(DEBUG - 1, "Gurobi runtime = %.2f" % m.Runtime) if m.Status == GRB.OPTIMAL: if w2_ij == None: max_wt, max_merge = max( (w1_ij[k], x_ij_keys[k]) for k, v in enumerate(m.X) if v) else: max_wt, _, max_merge = max((w1_ij[k], w2_ij[k], x_ij_keys[k]) for k, v in enumerate(m.X) if v) return max_wt, max_merge[0], max_merge[1] elif m.Status == GRB.TIME_LIMIT: raise GurobiError( 10023, "Gurobi timeout reached when attempting to solve GAP") elif m.Status == GRB.INTERRUPTED: raise KeyboardInterrupt() return None
def vrproutes(n, xc, yc, cost_func): rnd = np.random rnd.seed(0) plt.plot(xc[0], yc[0], c='r', marker='s') plt.scatter(xc[1:], yc[1:], c='b') N = [i for i in range(1, n + 1)] V = [0] + N A = [(i, j) for i in V for j in V if i != j] # c = {(i, j): np.hypot(xc[i]-xc[j], yc[i]-yc[j]) for i, j in A} c = {(i, j): cost_func(i, j) for i, j in A} print(c) Q = 20 q = {i: rnd.randint(1, 10) for i in N} mdl = Model('CVRP') x = mdl.addVars(A, vtype=GRB.BINARY) u = mdl.addVars(N, vtype=GRB.CONTINUOUS) mdl.modelSense = GRB.MINIMIZE mdl.params.LogToConsole = False mdl.setObjective(quicksum(x[i, j] * c[i, j] for i, j in A)) mdl.addConstrs(quicksum(x[i, j] for j in V if j != i) == 1 for i in N) mdl.addConstrs(quicksum(x[i, j] for i in V if i != j) == 1 for j in N) mdl.addConstrs((x[i, j] == 1) >> (u[i] + q[j] == u[j]) for i, j in A if i != 0 and j != 0) mdl.addConstrs(u[i] >= q[i] for i in N) mdl.addConstrs(u[i] <= Q for i in N) mdl.Params.MIPGap = 0.001 mdl.Params.TimeLimit = 60 # seconds mdl.optimize() active_arcs = [a for a in A if x[a].x > 0.5] return active_arcs
def schedule_all(self, timelimit=0): if not self.reservation_list: return self.schedule_dict # populate all the windows, requests, etc self.build_data_structures() # weight the priorities in each timeslice by airmass self.weight_by_airmass() # Instantiate a Gurobi Model object m = Model("LCOGT Schedule") # Constraint: Decision variable (isScheduled) must be binary (eq 4) requestLocations = tuplelist() scheduled_vars = [] for r in self.Yik: # convert self.Yik to a tuplelist for optimized searches # [(reqID, window idx, priority, resource, isScheduled)] var = m.addVar(vtype=GRB.BINARY, name=str(r[0])) scheduled_vars.append(var) requestLocations.append((r[0], r[1], r[2], r[3], var)) # update the Gurobi model to use isScheduled variables in constraints m.update() for i, r in enumerate(self.Yik): scheduled_vars[i].start = r[4] m.update() # Constraint: One-of (eq 5) i = 0 for oneof in self.oneof_constraints: match = tuplelist() for r in oneof: reqid = r.get_ID() match += requestLocations.select(reqid, '*', '*', '*', '*') r.skip_constraint2 = True # does this do what I think it does? nscheduled = quicksum(isScheduled for reqid, winidx, priority, resource, isScheduled in match) m.addConstr(nscheduled <= 1, "oneof_constraint_" + str(i)) i = i + 1 # Constraint: And (all or nothing) (eq 6) i = 0 andtuple = tuplelist() for andconstraint in self.and_constraints: # add decision variable that must be equal to all "and"ed blocks andVar = m.addVar(vtype=GRB.BINARY, name="and_var_" + str(i)) m.update() j = 0 for r in andconstraint: reqid = r.get_ID() match = requestLocations.select(reqid, '*', '*', '*', '*') nscheduled = quicksum(isScheduled for reqid, winidx, priority, resource, isScheduled in match) m.addConstr(andVar == nscheduled, "and_constraint_" + str(i) + "_" + str(j)) j = j + 1 i = i + 1 # Constraint: No more than one request should be scheduled in each (timeslice, resource) (eq 3) # self.aikt.keys() indexes the requests that occupy each (timeslice, resource) # for s in self.aikt: # faster?? for s in self.aikt.keys(): match = tuplelist() for timeslice in self.aikt[s]: match.append(requestLocations[timeslice]) nscheduled = quicksum(isScheduled for reqid, winidx, priority, resource, isScheduled in match) m.addConstr(nscheduled <= 1, 'one_per_slice_constraint_' + s) # Constraint: No request should be scheduled more than once (eq 2) # skip if One-of (redundant) for r in self.reservation_list: if not hasattr(r, 'skip_constraint2'): reqid = r.get_ID() match = requestLocations.select(reqid, '*', '*', '*', '*') nscheduled = quicksum(isScheduled for reqid, winidx, priority, resource, isScheduled in match) m.addConstr(nscheduled <= 1, 'one_per_reqid_constraint_' + str(reqid)) # Objective: Maximize the merit functions of all scheduled requests (eq 1); objective = quicksum( [isScheduled * (priority + 0.1 / (winidx + 1.0)) for req, winidx, priority, resource, isScheduled in requestLocations]) # set the objective, and maximize it m.setObjective(objective) m.modelSense = GRB.MAXIMIZE # impose a time limit on the solve if timelimit > 0: m.params.timeLimit = timelimit # Set the tolerance for the model solution to be within 1% of what it thinks is the best solution m.params.MIPGap = 0.01 # Set the Method of solving the root relaxation of the MIPs model to concurrent (default is dual simplex) m.params.Method = 3 # add all the constraints to the model m.update() # Solve the model m.optimize() # Return the optimally-scheduled windows r = Result() r.xf = [] for request, winidx, priority, resource, isScheduled in requestLocations: r.xf.append(isScheduled.x) return self.unpack_result(r)
y = m.addVars(species, vtype=GRB.BINARY, name="select") D = m.addVar(name="max_diss", obj = 1) m.addConstrs( (x.sum(i,'*') <= 10 * y[i] for i in species), "group") m.addConstrs( (x.sum('*',j) == 1 for j in species), "assign") m.addConstr(y.sum() == 1) m.addConstrs(dissimilarity[i][j] * x[i,j] <= D for i in species for j in species) # The objective is to minimize the group dissimilarity within group and maximize the group dissimilarity between groups m.modelSense = GRB.MINIMIZE # Solve m.optimize() # Create an empty directed graph G = nx.DiGraph() for i in species: for j in species: if x[i,j].x > 0: G.add_edge(i, j, cost=dissimilarity[i][j]) print('SUB GRAPH NODE LIST: ') for i in species: subNodeList = [];
#!/usr/bin/env python # This is the GAP per Wolsey, pg 182, using Lagrangian Relaxation from gurobipy import GRB, Model model = Model('GAP per Wolsey with Lagrangian Relaxation') model.modelSense = GRB.MAXIMIZE model.setParam('OutputFlag', False) # turns off solver chatter b = [15, 15, 15] c = [ [ 6, 10, 1], [12, 12, 5], [15, 4, 3], [10, 3, 9], [ 8, 9, 5] ] a = [ [ 5, 7, 2], [14, 8, 7], [10, 6, 12], [ 8, 4, 15], [ 6, 12, 5] ] # x[i][j] = 1 if i is assigned to j x = [] for i in range(len(c)): x_i = [] for j in c[i]:
def _solve_gap(N, D_s, d, C, K, L=None, L_ctr_multipiler=1.0): """A helper function that Solves VRP as a Generalized Assignment Problem to assign customers to vehicles with a objective function that the delivery cost as described in (Fisher & Jaikumar 1981). D_s is the distance matrix complemented with distances to K seed points. That is: [D_0] D_s = [S ], where D_0 is the first row of the full distance matrix and S is the distances from seed points to node points d is the list of customer demands with d[0]=0 being the depot node C is the capacity of the K identical trucks also, additional (and optional) constraints can be given: L is the maximum tour cost/duration/length L_ctr_multipiler allows iteratively adjusting the max route cost approximation constraint in order to avoid producing assignments that are ruled infeasible by the feasibility checker. -- Fisher, M. L. and Jaikumar, R. (1981), A generalized assignment heuristic for vehicle routing. Networks, 11: 109-124. """ ## build the cost approximation matrix "insertion_cost" # it is ~ a insertion cost matrix, where each coefficient is the cost # of inserting customer i to the route consisting visit to seed k. # # we assume that distances are symmetric, but if asymmetric # distances are to be used, take min # d_{ik} = min(c_{0i}+c_{i{i_k}}+c_{i{i_k}}, # c_{0{i_k}}+c_[{i_k}i}+c_{i0}) # -(c_{0{i_k}}+c_{{i_k}0}) m = Model("GAPCVRP") # the order of the keys is important when we interpret the results Y_ik_keys = [(i, k) for k in range(K) for i in range(1, N)] # delivery cost approximation coefficients for the objective function insertion_cost = {(i,k): D_s[0,i]+D_s[k,i]-D_s[k,0] \ for i,k in Y_ik_keys} # variables and the objective Y_ik = m.addVars(Y_ik_keys, obj=insertion_cost, vtype=GRB.BINARY, name='y') ## constraints # c1, the capacity constraint and optional tour cost constraint cl approx_route_cost_constraints = [] if C: c1_coeffs = d[1:] for k in range(K): ck_vars = [Y_ik[i, k] for i in range(1, N)] if C: c1_lhs = LinExpr(c1_coeffs, ck_vars) #c1_lhs = Y_ik.prod(c1_coeffs, '*', k) m.addConstr(c1_lhs <= C, "c1_k%d" % k) # ct = optional tour cost constraints # it is a bit hidden, but the additional side constraint can be found # from Fisher & Jaikumar (1981) p121, 2. paragraph. # However, for whatever reason, this does not seem to produce the # same results as reported in their paper as the constraint easily # starts to make the problem infeasible and the exact mechanism to # recover that is not specified in the paper. if L: ct_coeffs = [ insertion_cost[(i, k)] * L_ctr_multipiler for i in range(1, N) ] ct_lhs = LinExpr(ct_coeffs, ck_vars) #ct_lhs = Y_ik.prod(ct_coeffs, '*', k) constr_l = m.addConstr(ct_lhs <= L, "cl_k%d" % k) approx_route_cost_constraints.append(constr_l) # c2, the assignment constraints for i in range(1, N): # c2_1..N every node assigned only to 1 route m.addConstr(Y_ik.sum(i, '*') == 1, "c1_i%d" % i) ## update the model and solve m._vars = Y_ik m.modelSense = GRB.MINIMIZE m.update() #m.write("gapvrp_model.lp") # disable output m.setParam('OutputFlag', 0) m.setParam('Threads', MIP_SOLVER_THREADS) # REMOVEME m.setParam('MIPFocus', 3) m.setParam('TimeLimit', MAX_MIP_SOLVER_RUNTIME) m.optimize() # restore SIGINT callback handler which is changed by gurobipy signal(SIGINT, default_int_handler) if __debug__: log(DEBUG - 1, "Gurobi runtime = %.2f" % m.Runtime) if m.Status == GRB.OPTIMAL: return _decision_variables_to_assignments(m, Y_ik, N, K) elif m.Status == GRB.INFEASIBLE and L: # relax the model and allow violating minimal number of the approximate # route length constraints pens = [1.0] * len(approx_route_cost_constraints) m.feasRelax(1, True, None, None, None, approx_route_cost_constraints, pens) # TODO: not sure if feasRelax can change Status, test it someday if m.Status == GRB.INTERRUPTED: raise KeyboardInterrupt() # pass it on m.optimize() # restore SIGINT callback handler which is changed by gurobipy signal(SIGINT, default_int_handler) status = m.Status if __debug__: log(DEBUG - 1, "Relaxed problem Gurobi runtime = %.2f" % m.Runtime) if status == GRB.OPTIMAL: return _decision_variables_to_assignments(m, Y_ik, N, K) elif status == GRB.TIME_LIMIT: raise GurobiError( 10023, "Gurobi timeout reached when attempting to solve relaxed SCPCVRP" ) elif m.Status == GRB.INTERRUPTED: raise KeyboardInterrupt() # pass it on return None elif m.Status == GRB.TIME_LIMIT: raise GurobiError( 10023, "Gurobi timeout reached when attempting to solve GAP") elif m.Status == GRB.INTERRUPTED: raise KeyboardInterrupt() # pass it on return None
def generateInstance(self): # Check that variables have been intialized correctly if (not self.planes) or (self.n == 0) or (self.T == 0): logging.error('Store is not initialized correctly. Check for completeness of input-file.') return None model = Model("PlaneSolver") """ r: Earliest landing possibility b: Best landing d: latest landing g: penalty for each time earlier from best h: penalty for each time later from best s_ij: if i lands before j: s needs to pass in time x_i: Landing time for i p_i: positive divertion from bi n_i: negative divertion from bi """ bigM = max([max(x["s"]) for x in self.planes]) logging.debug("Using bigM punishment: " + str(bigM)) # generate Variables x = {} # Landing time p = {} # Positive divertion from optimal landing time n = {} # Negative divertion from optimal landing time s = {} # 1 iff plane i lands before j + buffer is invalid s1 = {} # for these, see constraints s2 = {} h1 = {} h2 = {} for i in range(len(self.planes)): x[i] = model.addVar(name="x_%d" % (i+1), vtype="i", lb=self.planes[i]["r"], ub=self.planes[i]["d"]) p[i] = model.addVar(name="p_%d" % (i+1), vtype="i", obj=self.planes[i]["h"], lb=0, ub=self.planes[i]["d"] - self.planes[i]["b"]) n[i] = model.addVar(name="n_%d" % (i+1), vtype="i", obj=self.planes[i]["g"], lb=0, ub=self.planes[i]["b"] - self.planes[i]["r"]) for j in range(len(self.planes)): s[i, j] = model.addVar(name="s_%d_%d" % (i+1, j+1), vtype="b", obj=bigM, lb=0, ub=1) s1[i, j] = model.addVar(name="s1_%d_%d" % (i+1, j+1), vtype="b", obj=0, lb=0, ub=1) s2[i, j] = model.addVar(name="s2_%d_%d" % (i+1, j+1), vtype="b", obj=0, lb=0, ub=1) h1[i, j] = model.addVar(name="h1_%d_%d" % (i+1, j+1), vtype="i", obj=0, lb=0) h2[i, j] = model.addVar(name="h2_%d_%d" % (i+1, j+1), vtype="i", obj=0, lb=0) model.modelSense = GRB.MINIMIZE model.update() for y in model.getVars(): if y.ub < 10000: logging.debug("%10s\t[%5d:%5d]\tobj %4d", y.varName, y.lb, y.ub, y.obj) # Constraints for i in range(len(self.planes)): logging.debug("{} == {} + {} - {}".format(x[i].varName, self.planes[i]["b"], p[i].varName, n[i].varName)) model.addConstr(x[i] == self.planes[i]["b"] + p[i] - n[i]) for j in range(len(self.planes)): if j == i: continue # model.addConstr(x[j] - x[i] ) # Force s = s1 AND s2 model.addConstr(s[i, j] <= s1[i, j]) model.addConstr(s[i, j] <= s2[i, j]) model.addConstr(s[i, j] >= s1[i, j] + s2[i, j] - 1) # s2 == xj - xi > 0 logging.debug("{}\t <= {}\t - {}\t - {}\t".format(s2[i, j].varName, x[j].varName, x[i].varName, h1[i, j].varName)) model.addConstr(s2[i, j] <= x[j] - x[i] + h1[i, j]) logging.debug("{}\t >= 1\t".format(h1[i, j].varName)) model.addConstr(h1[i, j] >= 1) logging.debug("{}\t - {}\t + {}\t <= {}\t*{}\t".format(x[j].varName, x[i].varName, h1[i, j].varName, s2[i, j].varName, bigM)) model.addConstr(x[j] - x[i] + h1[i, j] <= s2[i, j]*bigM) # s1 == xi + sij - xj > 0 logging.debug("{}\t + {}\t - {}\t >= {}\t".format(x[i].varName, self.planes[i]["s"][j], x[j].varName, s1[i, j].varName)) model.addConstr(s1[i, j] <= x[i] + self.planes[i]["s"][j] - x[j] + h2[i, j]) logging.debug("{}\t + {}\t - {}\t <= {}\t*{}\t".format(x[i].varName, self.planes[i]["s"][j], x[j].varName, s1[i, j].varName, bigM)) model.addConstr(x[i] + self.planes[i]["s"][j] - x[j] <= s1[i, j]*bigM) # solve it model.optimize() # Debugging printouts dbgStrs = {} for y in model.getVars(): if y.varName[0:2] not in dbgStrs: dbgStrs[y.varName[0:2]] = [] dbgStrs[y.varName[0:2]].append("%8s = %4s [%4s]" % (y.varName, int(y.x), int(y.x*y.obj) if y.obj else "")) for x in dbgStrs: dbgStrs[x].sort() while dbgStrs: printed = False keys = [x for x in dbgStrs.keys()] keys.sort() logStr = "" for x in keys: if dbgStrs[x]: logStr += dbgStrs[x][0] dbgStrs[x] = dbgStrs[x][1::] printed = True else: logStr += " "*22 logging.debug(logStr) if not printed: break self.model = model
def generateInstance(self): # Check that variables have been intialized correctly if (not self.planes) or (self.n == 0) or (self.T == 0): logging.error( 'Store is not initialized correctly. Check for completeness of input-file.' ) return None model = Model("PlaneSolver") """ r: Earliest landing possibility b: Best landing d: latest landing g: penalty for each time earlier from best h: penalty for each time later from best s_ij: if i lands before j: s needs to pass in time x_i: Landing time for i p_i: positive divertion from bi n_i: negative divertion from bi """ bigM = max([max(x["s"]) for x in self.planes]) logging.debug("Using bigM punishment: " + str(bigM)) # generate Variables x = {} # Landing time p = {} # Positive divertion from optimal landing time n = {} # Negative divertion from optimal landing time s = {} # 1 iff plane i lands before j + buffer is invalid s1 = {} # for these, see constraints s2 = {} h1 = {} h2 = {} for i in range(len(self.planes)): x[i] = model.addVar(name="x_%d" % (i + 1), vtype="i", lb=self.planes[i]["r"], ub=self.planes[i]["d"]) p[i] = model.addVar(name="p_%d" % (i + 1), vtype="i", obj=self.planes[i]["h"], lb=0, ub=self.planes[i]["d"] - self.planes[i]["b"]) n[i] = model.addVar(name="n_%d" % (i + 1), vtype="i", obj=self.planes[i]["g"], lb=0, ub=self.planes[i]["b"] - self.planes[i]["r"]) for j in range(len(self.planes)): s[i, j] = model.addVar(name="s_%d_%d" % (i + 1, j + 1), vtype="b", obj=bigM, lb=0, ub=1) s1[i, j] = model.addVar(name="s1_%d_%d" % (i + 1, j + 1), vtype="b", obj=0, lb=0, ub=1) s2[i, j] = model.addVar(name="s2_%d_%d" % (i + 1, j + 1), vtype="b", obj=0, lb=0, ub=1) h1[i, j] = model.addVar(name="h1_%d_%d" % (i + 1, j + 1), vtype="i", obj=0, lb=0) h2[i, j] = model.addVar(name="h2_%d_%d" % (i + 1, j + 1), vtype="i", obj=0, lb=0) model.modelSense = GRB.MINIMIZE model.update() for y in model.getVars(): if y.ub < 10000: logging.debug("%10s\t[%5d:%5d]\tobj %4d", y.varName, y.lb, y.ub, y.obj) # Constraints for i in range(len(self.planes)): logging.debug("{} == {} + {} - {}".format(x[i].varName, self.planes[i]["b"], p[i].varName, n[i].varName)) model.addConstr(x[i] == self.planes[i]["b"] + p[i] - n[i]) for j in range(len(self.planes)): if j == i: continue # model.addConstr(x[j] - x[i] ) # Force s = s1 AND s2 model.addConstr(s[i, j] <= s1[i, j]) model.addConstr(s[i, j] <= s2[i, j]) model.addConstr(s[i, j] >= s1[i, j] + s2[i, j] - 1) # s2 == xj - xi > 0 logging.debug("{}\t <= {}\t - {}\t - {}\t".format( s2[i, j].varName, x[j].varName, x[i].varName, h1[i, j].varName)) model.addConstr(s2[i, j] <= x[j] - x[i] + h1[i, j]) logging.debug("{}\t >= 1\t".format(h1[i, j].varName)) model.addConstr(h1[i, j] >= 1) logging.debug("{}\t - {}\t + {}\t <= {}\t*{}\t".format( x[j].varName, x[i].varName, h1[i, j].varName, s2[i, j].varName, bigM)) model.addConstr(x[j] - x[i] + h1[i, j] <= s2[i, j] * bigM) # s1 == xi + sij - xj > 0 logging.debug("{}\t + {}\t - {}\t >= {}\t".format( x[i].varName, self.planes[i]["s"][j], x[j].varName, s1[i, j].varName)) model.addConstr(s1[i, j] <= x[i] + self.planes[i]["s"][j] - x[j] + h2[i, j]) logging.debug("{}\t + {}\t - {}\t <= {}\t*{}\t".format( x[i].varName, self.planes[i]["s"][j], x[j].varName, s1[i, j].varName, bigM)) model.addConstr( x[i] + self.planes[i]["s"][j] - x[j] <= s1[i, j] * bigM) # solve it model.optimize() # Debugging printouts dbgStrs = {} for y in model.getVars(): if y.varName[0:2] not in dbgStrs: dbgStrs[y.varName[0:2]] = [] dbgStrs[y.varName[0:2]].append( "%8s = %4s [%4s]" % (y.varName, int(y.x), int(y.x * y.obj) if y.obj else "")) for x in dbgStrs: dbgStrs[x].sort() while dbgStrs: printed = False keys = [x for x in dbgStrs.keys()] keys.sort() logStr = "" for x in keys: if dbgStrs[x]: logStr += dbgStrs[x][0] dbgStrs[x] = dbgStrs[x][1::] printed = True else: logStr += " " * 22 logging.debug(logStr) if not printed: break self.model = model
def populate_dual_subproblem(data, upper_cost=None, flow_cost=None): """ Function that populates the Benders Dual Subproblem, as suggested by the paper "Minimal Infeasible Subsystems and Bender's cuts" by Fischetti, Salvagnin and Zanette. :param data: Problem data structure :param upper_cost: Link setup decisions fixed in the master :param flow_cost: This is the cost of the continuous variables of the master problem, as explained in the paper :return: Numpy array of Gurobi model objects """ # Gurobi model objects subproblems = np.empty(shape=(data.periods, data.commodities), dtype=object) # Construct model for period/commodity 0. # Then, copy this and change the coefficients dual_subproblem = Model('dual_subproblem_(0,0)') # Ranges we are going to need arcs, periods, commodities = xrange(data.arcs.size), xrange( data.periods), xrange(data.commodities) # Origins and destinations of commodities origins, destinations = data.origins, data.destinations # We use arrays to store variable indexes and variable objects. Why use # both? Gurobi wont let us get the values of individual variables # within a callback.. We just get the values of a large array of # variables, in the order they were initially defined. To separate them # in variable categories, we will have to use index arrays flow_index = np.zeros(shape=data.nodes, dtype=int) flow_duals = np.empty_like(flow_index, dtype=object) ubounds_index = np.zeros(shape=len(arcs), dtype=int) ubounds_duals = np.empty_like(ubounds_index, dtype=object) # Makes sure we don't add variables more than once flow_duals_names = set() if upper_cost is None: upper_cost = np.zeros(shape=(len(periods), len(arcs)), dtype=float) if flow_cost is None: flow_cost = np.zeros(shape=(len(periods), len(commodities)), dtype=float) # Populate all variables in one loop, keep track of their indexes # Data for period = 0, com = 0 count = 0 for arc in arcs: ubounds_duals[arc] = dual_subproblem.addVar( obj=-upper_cost[0, arc], lb=0., name='ubound_dual_a{}'.format(arc)) ubounds_index[arc] = count count += 1 start_node, end_node = get_2d_index(data.arcs[arc], data.nodes) start_node, end_node = start_node - 1, end_node - 1 for node in (start_node, end_node): var_name = 'flow_dual_n{}'.format(node) if var_name not in flow_duals_names: flow_duals_names.add(var_name) obj, ub = 0., GRB.INFINITY if data.origins[0] == node: obj = 1. if data.destinations[0] == node: obj = -1. ub = 0. flow_duals[node] = \ dual_subproblem.addVar( obj=obj, lb=0., name=var_name) flow_index[node] = count count += 1 opt_var = dual_subproblem.addVar(obj=-flow_cost[0, 0], lb=0., name='optimality_var') dual_subproblem.params.threads = 2 dual_subproblem.params.LogFile = "" dual_subproblem.update() # Add constraints demand = data.demand[0, 0] for arc in arcs: start_node, end_node = get_2d_index(data.arcs[arc], data.nodes) start_node, end_node = start_node - 1, end_node - 1 lhs = flow_duals[start_node] - flow_duals[end_node] \ - ubounds_duals[arc] - \ opt_var * data.variable_cost[arc] * demand dual_subproblem.addConstr(lhs <= 0., name='flow_a{}'.format(arc)) # Original Fischetti model lhs = quicksum(ubounds_duals) + opt_var dual_subproblem.addConstr(lhs == 1, name='normalization_constraint') # Store variable indices dual_subproblem._ubounds_index = ubounds_index dual_subproblem._flow_index = flow_index dual_subproblem._all_variables = np.array(dual_subproblem.getVars()) dual_subproblem._flow_duals = np.take(dual_subproblem._all_variables, flow_index) dual_subproblem._ubound_duals = np.take(dual_subproblem._all_variables, ubounds_index) dual_subproblem.setParam('OutputFlag', 0) dual_subproblem.modelSense = GRB.MAXIMIZE dual_subproblem.update() subproblems[0, 0] = dual_subproblem for period, com in product(periods, commodities): if (period, com) != (0, 0): model = dual_subproblem.copy() optimality_var = model.getVarByName('optimality_var') optimality_var.Obj = -flow_cost[period, com] demand = data.demand[period, com] for node in xrange(data.nodes): variable = model.getVarByName('flow_dual_n{}'.format(node)) if origins[com] == node: obj = 1. elif destinations[com] == node: obj = -1. else: obj = 0. variable.obj = obj for arc in arcs: variable = model.getVarByName('ubound_dual_a{}'.format(arc)) variable.Obj = -np.sum(upper_cost[:period + 1, arc]) constraint = model.getConstrByName('flow_a{}'.format(arc)) model.chgCoeff(constraint, optimality_var, -demand * data.variable_cost[arc]) model._all_variables = np.array(model.getVars()) model.update() subproblems[period, com] = model return subproblems
def optimize(parameter, obj): global ft N = parameter[0] D = parameter[1] A = parameter[2] R = parameter[3] M = parameter[4] u = parameter[5] q = parameter[6] e = parameter[7] l = parameter[8] vehicles = parameter[9] m = parameter[10] clients = parameter[11] Q = parameter[12] V = parameter[13] c = parameter[14] xc = parameter[15] yc = parameter[16] k = parameter[17] n = clients #model in gurobipy mdl = Model("MDCCVRPTW") x = mdl.addVars(V, V, R, vtype=GRB.BINARY) #X variable t = mdl.addVars(V, R, vtype=GRB.CONTINUOUS) #t variable if obj == 1: mdl.modelSense = GRB.MINIMIZE mdl.setObjective(quicksum(t[i, k] for i in N for k in R)) #objective function 1 elif obj == 2: mdl.modelSense = GRB.MINIMIZE mdl.setObjective(quicksum(t[i, k] - e[i] for i in N for k in R)) #objective function 2 elif obj == 3: mdl.modelSense = GRB.MAXIMIZE mdl.setObjective(quicksum(l[i] - t[i, k] for i in N for k in R)) #objective function 3 elif obj == 4: mdl.modelSense = GRB.MINIMIZE mdl.setObjective((1 / n) * quicksum((t[i, k] - e[i]) / (l[i] - e[i]) for i in N for k in R)) #objective function 4 #restrictions mdl.addConstrs( quicksum(x[i, j, k] for i in V for k in R if i != j) == 1 for j in N) #(2) mdl.addConstrs( (quicksum(x[i, j, k] for i in V if i != j) - quicksum(x[j, i, k] for i in V if i != j)) == 0 for j in N for k in R) #(3) mdl.addConstrs(quicksum(x[i, j, k] for i in D for j in V) == 1 for k in R) #(4) mdl.addConstrs(quicksum(x[j, i, k] for i in D for j in V) == 1 for k in R) #(5) mdl.addConstrs( quicksum(q[j] * x[i, j, k] for i in V for j in V if i != j) <= Q for k in R) #(6) #mdl.addConstrs(t[i,k]+c[i,j]+u[i]-t[j,k]<=(1-x[i,j,k])*M for i in V for j in N for k in R if i!=j) #(7) NUEVA mdl.addConstrs((x[i, j, k] == 1) >> (t[i, k] + c[i, j] + u[i] - t[j, k] <= 0) for i in V for j in N for k in R if i != j) #(7) NUEVA mdl.addConstrs(e[i] <= t[i, k] for i in V for k in R) #(8) NUEVA mdl.addConstrs(t[i, k] + u[i] <= l[i] for i in V for k in R) #(9) NUEVA mdl.addConstrs(t[i, k] >= 0 for i in V for k in R) #mdl.Params.MIPGap= 0.1 mdl.Params.Timelimit = 3600 mdl.Params.SolFiles first_sol = 'Not Found' try: mdl.optimize(mycallback) if len(ft) > 0: first_sol = min(ft) #print(ft,first_sol) ft = [] obj = mdl.getObjective() sol = [] for i in V: for j in V: for k in R: if x[i, j, k].x > 0.7: s = "x[{},{},{}]={}, t[{},{}]={}".format( i, j, k, x[i, j, k].x, i, k, t[i, k].x) sol.append(s) for i in V: plt.annotate("Node_{}".format(i), (xc[i - 1], yc[i - 1])).set_fontsize(3) colors = itertools.cycle(["r", "b", "g", "c", "m", "y", "k"]) #plot active arcs #for i,j in active_arcs: # plt.plot([xc[i-1],xc[j-1]],[yc[i-1],yc[j-1]],c=next(colors), linewidth=.85) for k in R: prox_color = next(colors) for i in V: for j in V: if x[i, j, k].x > 0.9: plt.plot([xc[i - 1], xc[j - 1]], [yc[i - 1], yc[j - 1]], c=prox_color, linewidth=.45, zorder=1) #plt.annotate("k={}".format(k),((xc[j-1]+xc[i-1])/2,(yc[j-1]+yc[i-1])/2)).set_fontsize(4) plt.scatter(xc[0:clients], yc[0:clients], c='b', s=10, zorder=2) plt.scatter(xc[clients:], yc[clients:], c='r', s=10, zorder=2) #plt.figure(figsize=(10, 10), dpi=80) #plt.axis('auto') #fig.suptitle('test title', fontsize=20) plt.axis('off') #plt.savefig("test.png") plt.rcParams.update({'font.size': 4}) name = str(n) + 'x' + str(m) + '_' + str(k) + '.png' plt.savefig('Images\\' + name, dpi=400, bbox_inches=0) #plt.show() #print(D) return obj.getValue( ), mdl.Runtime, vehicles, clients, m, mdl.MIPGap, first_sol, sol except gp.GurobiError as e: print('Error code ' + str(e.errno) + ': ' + str(e)) except AttributeError: print('Encountered an attribute error') return [ 'Not Found', mdl.Runtime, vehicles, clients, m, 'Not Found', first_sol ] + [[None]]
def populate_dual_subproblem(data): """ Function that populates the Benders Dual Subproblem, as suggested by the paper "Minimal Infeasible Subsystems and Bender's cuts" by Fischetti, Salvagnin and Zanette. :param data: Problem data structure :param upper_cost: Link setup decisions fixed in the master :param flow_cost: This is the cost of the continuous variables of the master problem, as explained in the paper :return: Numpy array of Gurobi model objects """ # Gurobi model objects subproblems = np.empty(shape=(data.periods, data.commodities), dtype=object) # Construct model for period/commodity 0. # Then, copy this and change the coefficients subproblem = Model('subproblem_(0,0)') # Ranges we are going to need arcs, periods, commodities, nodes = xrange(data.arcs.size), xrange( data.periods), xrange(data.commodities), xrange(data.nodes) # Other data demand, var_cost = data.demand, data.variable_cost # Origins and destinations of commodities origins, destinations = data.origins, data.destinations # We use arrays to store variable indexes and variable objects. Why use # both? Gurobi wont let us get the values of individual variables # within a callback.. We just get the values of a large array of # variables, in the order they were initially defined. To separate them # in variable categories, we will have to use index arrays flow_vars = np.empty_like(arcs, dtype=object) # Populate all variables in one loop, keep track of their indexes # Data for period = 0, com = 0 for arc in arcs: flow_vars[arc] = subproblem.addVar(obj=demand[0, 0] * var_cost[arc], lb=0., ub=1., name='flow_a{}'.format(arc)) subproblem.update() # Add constraints for node in nodes: out_arcs = get_2d_index(data.arcs, data.nodes)[0] == node + 1 in_arcs = get_2d_index(data.arcs, data.nodes)[1] == node + 1 lhs = quicksum(flow_vars[out_arcs]) - quicksum(flow_vars[in_arcs]) subproblem.addConstr(lhs == 0., name='flow_bal{}'.format(node)) subproblem.update() # Store variables subproblem._all_variables = flow_vars.tolist() # Set parameters subproblem.setParam('OutputFlag', 0) subproblem.modelSense = GRB.MINIMIZE subproblem.params.threads = 2 subproblem.params.LogFile = "" subproblem.update() subproblems[0, 0] = subproblem for period, com in product(periods, commodities): if (period, com) != (0, 0): model = subproblem.copy() model.ModelName = 'subproblem_({},{})'.format(period, com) flow_cost = data.demand[period, com] * var_cost model.setObjective(LinExpr(flow_cost.tolist(), model.getVars())) model.setAttr('rhs', model.getConstrs(), [0.0] * data.nodes) model._all_variables = model.getVars() model.update() subproblems[period, com] = model return subproblems
def generateInstance(self): # Check that store has been intialized correctly if not all([x in self.store for x in ["Timehorizon", "h", "K", "a", "d", "s", "st"]]): logging.error('Store is not initialized correctly. Check for completeness of input-file.') return None model = Model("LotSolver") tHor = self.store["Timehorizon"] nPr = self.store["nProducts"] timeRange = range(1, tHor + 1) prodRange = range(1, nPr + 1) # Assume that production of products costs at least 1h, so a max of K products can be produced per period bigM = max(self.store["K"]) # generate Variables # boolean production bx = {} # lager l = {} # production x = {} for t in timeRange: for p in prodRange: x[p, t] = model.addVar(name="x_%d_%d" % (p, t), vtype="i") bx[p, t] = model.addVar(name="bx_%d_%d" % (p, t), vtype="b") for t in range(0, tHor + 1): for p in prodRange: l[p, t] = model.addVar(name="l_%d_%d" % (p, t), vtype="i", obj=float(self.store["h"][p-1])) # switch costs s = {} for t in range(0, tHor+1): for p1 in prodRange: for p2 in prodRange: # Switching to the same product does not cost anything - even if the file may say otherwise objective = float(self.store["s"][p1-1][p2-1]) if p1 != p2 else 0 s[p1, p2, t] = model.addVar(name="s_%d_%d_%d" % (p1, p2, t), vtype="b", obj=objective) model.modelSense = GRB.MINIMIZE model.update() for y in model.getVars(): logging.debug("%s obj %s", y.varName, y.obj) # Constraints # Initially only allow a single product logging.debug("%s == 1", [s[key].varName for key in s if key[2] == 0]) model.addConstr(quicksum(s[key] for key in s if key[2] == 0) == 1, name="single_switch_" + str(t)) # Only allow products in each period that actually has been switched to for t in timeRange: for p in prodRange: logging.debug("(%s) == %s", [s[key].varName for key in s if key[2] == t and (key[1] == p or key[0] == p)], bx[p, t].varName) model.addConstr(quicksum(s[key] for key in s if key[2] == t and (key[1] == p or key[0] == p)) >= bx[p,t], name="single_switch_" + str(t)) # Force bx = 1 iff x != 0 model.addConstr(x[p,t] >= bx[p,t]) model.addConstr(x[p,t] <= bigM * bx[p,t]) for t in timeRange: # Allow only a single switch each period logging.debug('Single switch constraint for ' + str([s[key].varName for key in s if key[2] == t])) model.addConstr(quicksum(s[key] for key in s if key[2] == t) == 1, name="single_switch_" + str(t)) # Only allow connected switches between t-1 and t for p in prodRange: logging.debug('valid_switch for ' + str([s[key].varName for key in s if key[2] == (t-1) and key[1] == p]) + " and " + str([s[key].varName for key in s if key[2] == t and key[0] == p])) model.addConstr(quicksum(s[key] for key in s if key[2] == (t-1) and key[1] == p) == quicksum(s[key] for key in s if key[2] == t and key[0] == p), name="valid_switch_%d_%d" % (p, t)) # Machine can't be occupied for more then K hours / period for t in timeRange: logging.debug("sum {} + sum {} <= {}".format([x[key].varName + "*" + str(self.store["a"][key[0]-1]) for key in x if key[1] == t], [s[key].varName + "*" + str(self.store["st"][key[0]-1][key[1]-1]) for key in s if key[2] == t], self.store["K"][t-1])) model.addConstr(quicksum(x[key]*self.store["a"][key[0]-1] for key in x if key[1] == t) + quicksum(s[key]*self.store["st"][key[0]-1][key[1]-1] for key in s if key[2] == t) <= self.store["K"][t-1]) # Initial warehouse stock for p in prodRange: logging.debug("%s == %s", l[p, 0].varName, self.store["l"][p-1]) model.addConstr(l[p, 0] == self.store["l"][p-1]) # Update warehouse stock inbetween periods + enforce demand to be met for t in range(1, tHor+1): for p in prodRange: logging.debug("{} = {} + {} - {}".format(l[p, t].varName, l[p, t-1].varName, x[p, t].varName, self.store["d"][p-1][t-1])) model.addConstr(l[p, t] == l[p, t-1] + x[p, t] - self.store["d"][p-1][t-1]) # solve it model.optimize() # Debugging printouts for y in model.getVars(): if y.x >= 0.001: logging.debug("%s = %s cost %d", y.varName, y.x, y.x*y.obj) for t in timeRange: logging.debug("%s + %s", (["{}[{}] * {}".format(x[key].x, x[key].varName, self.store["a"][key[0]-1]) for key in x if key[1] == t]), ([str(s[key].varName) + "*" + str(self.store["st"][key[0]-1][key[1]-1]) for key in s if key[2] == t and s[key].x >= 0.001])) self.model = model
def solvePerfectInformation(gp, I, K, W, l_init, q_init, c_init): GenAvg=0.0 UpAvg=0.0 SpAvg=0.0 RefAvg=0.0 FlrAvg=0.0 L_min= 50 L_max=1200 ub=np.zeros(K) M=1000000 T_R_down=2 T_F_down=4 # loop over all sample paths for k in range(K): # Model perfectInfo = Model("GenAndUpg") Gen=[] for x_G in range(I): Gen.append(perfectInfo.addVar(lb=0 , vtype=GRB.CONTINUOUS, obj=-W[x_G, k, 0]*gp[5]*np.power(gp[6], x_G), name="Gen[%d]" %x_G)) Up=[] for x_U in range(I): Up.append(perfectInfo.addVar(lb=0, vtype=GRB.CONTINUOUS, obj=gp[7]*np.power(gp[6], x_U), name="Up[%d]" %x_U)) Sp=[] for x_S in range(I): Sp.append(perfectInfo.addVar(lb=0, vtype=GRB.CONTINUOUS, obj=0, name="Sp[%d]" %x_S)) Ref=[] for x_R in range(I): Ref.append(perfectInfo.addVar(lb=0, vtype=GRB.BINARY, obj=gp[8]*np.power(gp[6],x_R), name="Ref[%d]" %x_R)) Gen_b=[] for xi_G in range(I): Gen_b.append(perfectInfo.addVar(lb=0, vtype=GRB.BINARY, obj=0, name="Gen_b[%d]" %xi_G)) Flr=[] for xi_F in range(I): Flr.append(perfectInfo.addVar(lb=0, vtype=GRB.BINARY, obj=(100)* np.power(gp[6], xi_F), name="Flr[%d]" %xi_F)) Cap=[] for q in range(I): Cap.append(perfectInfo.addVar(lb=0 , vtype=GRB.CONTINUOUS, obj=0, name="Cap[%d]" %q)) Res=[] for l in range(I): Res.append(perfectInfo.addVar(lb= L_min, ub=L_max, vtype=GRB.CONTINUOUS, obj=0, name="Res[%d]" %l)) Con=[] for c in range(I): Con.append(perfectInfo.addVar(lb=0 , ub=1, vtype=GRB.CONTINUOUS, obj=0, name="Con[%d]" %c)) ######################################################################### #robust part perfectInfo.modelSense = GRB.MINIMIZE perfectInfo.addConstr( (Res[0]- l_init == 0), "initial reservior") perfectInfo.addConstr( (Cap[0]- q_init == 0), "initial capacity") perfectInfo.addConstr( (Con[0]- c_init == 0), "initial condition") for i in range(I): perfectInfo.addConstr( (Gen[i]- Cap[i] <= 0), "Generation") for i in range(I): perfectInfo.addConstr( (Gen[i]- Res[i] <= 0)) for i in range(I): perfectInfo.addConstr( (Up[i]- gp[4] * Ref[i] <= 0)) for i in range(I): perfectInfo.addConstr( ( Gen_b[i] + Ref[i] <= 1)) for i in range(I): perfectInfo.addConstr( ( Gen[i] - M* Gen_b[i] <= 0)) for i in range(I): perfectInfo.addConstr( (Ref[i]- Flr[i] <= 0)) for i in range(I): perfectInfo.addConstr( (quicksum(Gen[i+j] for j in range(min(T_R_down, I-i )))- M* (1-Ref[i]) <=0)) for i in range(I): perfectInfo.addConstr( (quicksum(Gen[i+j] for j in range(min(T_F_down, I-i )))- M* (1-Flr[i]) <=0)) for i in range(I-1): perfectInfo.addConstr( (Res[i+1]- Res[i]+ Gen[i] - W[i , k, 1] + Sp [i]==0 )) for i in range(I-1): perfectInfo.addConstr( (Cap[i+1]- Cap[i] - Up[i] ==0 )) for i in range(I-1): perfectInfo.addConstr( (Con[i+1]- Con[i] - W[i, k, 2]* Gen_b[i] -(0- Con[i])* Ref[i] ==0 )) perfectInfo.write('GenAndUpg.lp') perfectInfo.optimize() print('Up[0]: {}, Sp[0]:{}, Ref[0]:{}, Flr[0]:{} \n'. format( Up[0].x, Sp[0].x, Ref[0].x, Flr[0].x )) print('Power: {}, Inflow: {}, Detereoration: {}\n'. format( W[i, 0, 0], W[i, 0, 1], W[i, 0, 2] )) print('Sample path: {:.4f}'.format( k )) if perfectInfo.status == GRB.Status.OPTIMAL: print('Optimal objective: %g' % perfectInfo.objVal) ub[k]= -perfectInfo.objVal # else: # ub[k]= M elif perfectInfo.status == GRB.Status.INF_OR_UNBD: print('Model is infeasible or unbounded') exit(0) elif perfectInfo.status == GRB.Status.INFEASIBLE: print('Model is infeasible') exit(0) elif perfectInfo.status == GRB.Status.UNBOUNDED: print('Model is unbounded') exit(0) else: print('Optimization ended with status %d' % perfectInfo.status) #refering to decisions at current period # pdb.set_trace() GenAvg+= Gen[0].x print('Optimal generation: {:.4f}'. format( Gen[0].x)) UpAvg+= Up[0].x SpAvg+= Sp[0].x RefAvg+= Ref[0].x FlrAvg+= Flr[0].x # average of actions in the sample paths GenAvg=GenAvg/K UpAvg=UpAvg/K SpAvg=SpAvg/K RefAvg= RefAvg/K FlrAvg=FlrAvg/K if RefAvg> 0.5: RefAvg=1 UpAvg= UpAvg GenAvg= 0 else: RefAvg=0 UpAvg=0 XAvg=[GenAvg, UpAvg, SpAvg, RefAvg, FlrAvg] return ub, XAvg
from gurobipy import GRB, Model from datetime import datetime startTime = datetime.now() model = Model('GAP per Wolsey') model.modelSense = GRB.MAXIMIZE model.setParam('OutputFlag', False) # turns off solver chatter b = [15, 15, 15] c = [[6, 10, 1], [12, 12, 5], [15, 4, 3], [10, 3, 9], [8, 9, 5]] a = [[5, 7, 2], [14, 8, 7], [10, 6, 12], [8, 4, 15], [6, 12, 5]] # x[i][j] = 1 if i is assigned to j x = [] for i in range(len(c)): x_i = [] for j in c[i]: x_i.append(model.addVar(vtype=GRB.BINARY)) x.append(x_i) # We have to update the model so it knows about new variables model.update() # sum j: x_ij <= 1 for all i for x_i in x: model.addConstr(sum(x_i) <= 1) # sum i: a_ij * x_ij <= b[j] for all j for j in range(len(b)): model.addConstr(sum(a[i][j] * x[i][j] for i in range(len(x))) <= b[j])
def _solve_set_covering(N, active_ptls, relaxed_ptls, forbidden_combos, allow_infeasible=True, D=None, K=None): """A helper function that solves a set covering problem. The inputs are a list of node sets and corresponding costs for the set. -- Fisher, M. L. and Jaikumar, R. (1981), A generalized assignment heuristic for vehicle routing. Networks, 11: 109-124. """ m = Model("SCPCVRP") nactive = len(active_ptls.routes) nrelaxed = len(relaxed_ptls.routes) nforbidden = len(forbidden_combos) # the order of the keys is important when we interpret the results X_j_keys = range(nactive + nrelaxed) # variables and the objective X_j_costs = active_ptls.costs + relaxed_ptls.costs X_j_node_sets = active_ptls.nodes + relaxed_ptls.nodes #update forbidden indices to match the current active petal set X_j_forbidden_combos = [] for fc in forbidden_combos: if all(i < nactive for i in fc): X_j_forbidden_combos.append( [i if i >= 0 else -i - 1 + nactive for i in fc]) #print("REMOVEME: Solving with K=%d, %d node sets, and %d solutions forbidden" % (K, nactive+nrelaxed,nforbidden)) if __debug__: log( DEBUG, "Solving over-constrained VRP as a set covering problem " + "with %d petals, where %d of the possible configurations are forbidden." % (nactive + nrelaxed, nforbidden)) if nforbidden > 0: log(DEBUG - 2, " and with following solutions forbidden:") log(DEBUG - 3, "(petal indices = %s)" % str(X_j_forbidden_combos)) for fc in X_j_forbidden_combos: fc_sol = [0] for i in fc: if i < nactive: fc_sol.extend(active_ptls.routes[i][1:]) else: fc_sol.extend(relaxed_ptls.routes[i - nactive][1:]) fc_sol = without_empty_routes(fc_sol) log(DEBUG - 2, "%s (%.2f)" % (fc_sol, objf(fc_sol, D))) X_j = m.addVars(X_j_keys, obj=X_j_costs, vtype=GRB.BINARY, name='x') ## constraints c1_constrs, c2_constrs, c3_constrs = _add_set_covering_constraints( m, N, K, X_j, X_j_keys, X_j_node_sets, X_j_forbidden_combos) ## update the model and solve m._vars = X_j m.modelSense = GRB.MINIMIZE m.update() # disable output m.setParam('OutputFlag', 0) m.setParam('TimeLimit', MAX_MIP_SOLVER_RUNTIME) m.setParam('Threads', MIP_SOLVER_THREADS) #m.write("petalout.lp") m.optimize() # restore SIGINT callback handler which is changed by gurobipy signal(SIGINT, default_int_handler) if __debug__: log(DEBUG - 2, "Gurobi runtime = %.2f" % m.Runtime) if m.Status == GRB.OPTIMAL: log(DEBUG - 3, "Gurobi objective = %.2f" % m.getObjective().getValue()) if m.Status == GRB.OPTIMAL: return _decision_variables_to_petals(m.X, active_ptls, relaxed_ptls), True elif m.Status == GRB.TIME_LIMIT: raise GurobiError( 10023, "Gurobi timeout reached when attempting to solve SCPCVRP") elif m.Status == GRB.INTERRUPTED: raise KeyboardInterrupt() # Sometimes the solution is infeasible, try to relax it a little. elif m.Status == GRB.INFEASIBLE and allow_infeasible: return _relax_customer_constraints_with_feasRelax( m, c1_constrs, active_ptls, relaxed_ptls) return None, False
def Mater_Problem(self): [R_turbine, routes, NumTechs] = self.Generating_Feasible_Routes() model = Model() # set parameters A = [] for v in range(self.num_V): for t in range(self.period): for b in range(self.num_B): for wf in range(self.num_WF): if R_turbine[v][t][b][wf] == []: # print (['(v,t,b,wf)[%d,%d,%d,%d]' % (v,t,b,wf)]) continue for r in range(np.size(R_turbine[v][t][b][wf][0], 1)): A.append((v, t, b, wf, r)) x = model.addVars(A, vtype=GRB.BINARY) # add constraints for v in range(self.num_V): for t in range(self.period): tmp1 = 0 for b in range(self.num_B): for wf in range(self.num_WF): if R_turbine[v][t][b][wf] == []: continue ntmp = np.size(R_turbine[v][t][b][wf][0], 1) tmp1 += sum(x[v, t, b, wf, r] for r in range(ntmp)) model.addConstr(tmp1 <= 1) # explore every turbines in the farm for b in range(self.num_B): for wf in range(self.num_WF): for j in range(len(self.WF_tech[wf])): tmp = 0 for v in range(self.num_V): for t in range(self.period): if R_turbine[v][t][b][wf] == []: continue ntmp = np.size(R_turbine[v][t][b][wf][0], 1) tmp += quicksum(x[v, t, b, wf, r] * R_turbine[v][t][b][wf][0][j, r] \ for r in range(ntmp)) model.addConstr(tmp == 1) for b in range(self.num_B): for t in range(self.period): for p in self.types: tmp = 0 for v in range(self.num_V): for wf in range(self.num_WF): if R_turbine[v][t][b][wf] == []: continue tmp += quicksum(x[v, t, b, wf, r] * NumTechs[v][t][b][wf][r][p] \ for r in range(np.size(R_turbine[v][t][b][wf][0], 1))) model.addConstr(tmp <= self.B_tech[b][p][t]) ctmp = 0 for v in range(self.num_V): for t in range(self.period): for b in range(self.num_B): for wf in range(self.num_WF): if R_turbine[v][t][b][wf] == []: continue ctmp += quicksum(R_turbine[v][t][b][wf][0][-1, r] * x[v, t, b, wf, r] \ for r in range(np.size(R_turbine[v][t][b][wf][0], 1))) model.modelSense = GRB.MINIMIZE model.setParam('OutputFlag', 0) model.setObjective(ctmp) model.optimize() _routes = [[] for i in range(self.num_V)] if model.status == GRB.OPTIMAL: cost_opt = model.objVal # print('Master Problem: OPTIMAL\n', 'total cost: ', round(cost_opt, 2)) # for v in range(self.num_V): # for t in range(self.period): # nums = 0 # for b in range(self.num_B): # for wf in range(self.num_WF): # if R_turbine[v][t][b][wf] == []: # continue # for r in range(np.size(R_turbine[v][t][b][wf][0],1)): # if x[v,t,b,wf,r].x == 1: # nums += 1 # # print ('(v,t)[%d,%d]' % (v,t), 'nums: ', nums) for b in range(self.num_B): for wf in range(self.num_WF): if R_turbine[v][t][b][wf] == []: continue turbines = [] for v in range(self.num_V): for t in range(self.period): # if R_turbine[v][t][b][wf] == []: # continue if sum(x[v, t, b, wf, r].x for r in range( np.size(R_turbine[v][t][b][wf][0], 1))) < 1: _routes[v].append([]) continue for r in range( np.size(R_turbine[v][t][b][wf][0], 1)): if x[v, t, b, wf, r].x == 1: print('(b,wf,v,t)[%d,%d,%d,%d]' % (b, wf, v, t), \ 'routes: ', routes[v][t][b][wf][r]) _routes[v].append(routes[v][t][b][wf][r]) print('_routes: \n', _routes) return [cost_opt, _routes] else: print('Master Problem: Infeasible') return [None, None]
def solve(n, width, height, startNodes, targetNodes, startTimes, endTimes): model = Model("BoatSolver") def toGrid(val, y=0): if isinstance(val, list): return val[0] + val[1] * width return val + y * width T = max(endTimes) x = {} wait = {} for i, j, t in itertools.product(range(n), range(width * height), range(T)): x[i, j, t] = model.addVar(name="x_%d_%d_%d" % (i+1, j+1, t+1), vtype="b", obj=1) wait[i, j, t] = model.addVar(name="wait_%d_%d_%d" % (i+1, j+1, t+1), vtype="i", obj=-1) model.modelSense = GRB.MINIMIZE model.update() # Force startpositions for i, (sN, sT) in enumerate(zip(startNodes, startTimes)): for t in range(sT): for j in range(width * height): if j == toGrid(sN - 1) and t == sT - 1: logging.debug("start: %s == 1", x[i, j, t].varName) model.addConstr(x[i, j, t] == 1) else: logging.debug("start: %s == 0", x[i, j, t].varName) model.addConstr(x[i, j, t] == 0) # Force endpositions for i, (eN, eT) in enumerate(zip(targetNodes, endTimes)): j = toGrid(eN - 1) logging.debug("end: %s == 1", x[i, j, eT - 1].varName) model.addConstr(x[i, j, eT - 1] == 1) # Container vanishes after endTime for t in range(eT, T): logging.debug("end: %s == 0", x[i, j, t].varName) model.addConstr(x[i, j, t] == 0) # single container per node for j, t in itertools.product(range(width * height), range(T)): logging.debug("%s <= 1", [x[i, j, t].varName for i in range(n)]) model.addConstr(quicksum(x[i, j, t] for i in range(n)) <= 1) # Force valid container movement for w, h in itertools.product(range(width), range(height)): vals = [toGrid(w, h)] if h >= 1: vals += [toGrid(w, h - 1)] if w >= 1: vals += [toGrid(w - 1, h)] if h+1 < height: vals += [toGrid(w, h + 1)] if w+1 < width: vals += [toGrid(w + 1, h)] for i, t in itertools.product(range(n), range(1, T)): if endTimes[i] > t and startTimes[i] <= t: logging.debug("sum(%s) >= %s", [x[i, j, t].varName for j in vals], x[i, toGrid(w, h), t - 1].varName) model.addConstr(quicksum(x[i, j, t] for j in vals) >= x[i, toGrid(w, h), t - 1]) else: logging.debug("skipped(%s) >= %s", [x[i, j, t].varName for j in vals], x[i, toGrid(w, h), t - 1].varName) for i, t in itertools.product(range(n), range(1, T)): logging.debug("sum(%s) <= 1", [x[i, j, t].varName for j in vals]) model.addConstr(quicksum(x[i, j, t] for j in vals) <= 1) # Force continous line through grid for i, t in itertools.product(range(n), range(0, T)): if endTimes[i] > t and startTimes[i] <= t + 1: logging.debug("sum(%s) == 1", [x[i, j, t].varName for j in range(width * height)]) model.addConstr(quicksum(x[i, j, t] for j in range(width * height)) == 1) else: logging.debug("sum(%s) == 0", [x[i, j, t].varName for j in range(width * height)]) model.addConstr(quicksum(x[i, j, t] for j in range(width * height)) == 0) # Prevent ships from passing over same link for t in range(1, T): for w, h in itertools.product(range(width - 1), range(height)): for i in range(n): for k in range(i+1, n): model.addConstr(x[i, toGrid(w, h), t - 1] + x[i, toGrid(w + 1, h), t] + x[k, toGrid(w, h), t] + x[k, toGrid(w + 1, h), t - 1] <= 3) model.addConstr(x[i, toGrid(w, h), t] + x[i, toGrid(w + 1, h), t - 1] + x[k, toGrid(w, h), t - 1] + x[k, toGrid(w + 1, h), t] <= 3) for w, h in itertools.product(range(width), range(height - 1)): for i in range(n): for k in range(i+1, n): model.addConstr(x[i, toGrid(w, h), t - 1] + x[i, toGrid(w, h + 1), t] + x[k, toGrid(w, h), t] + x[k, toGrid(w, h + 1), t - 1] <= 3) model.addConstr(x[i, toGrid(w, h), t] + x[i, toGrid(w, h + 1), t - 1] + x[k, toGrid(w, h), t - 1] + x[k, toGrid(w, h + 1), t] <= 3) # Allow free waiting for i, j, t in itertools.product(range(n), range(width * height), range(0, T)): if t < startTimes[i]: model.addConstr(x[i, j, t] == wait[i, j, t]) else: model.addConstr(x[i, j, t - 1] + x[i, j, t] - 1 <= wait[i, j, t]) model.addConstr(x[i, j, t - 1] >= wait[i, j, t]) model.addConstr(x[i, j, t] >= wait[i, j, t]) model.optimize() for y in model.getVars(): if y.x: logging.warning("%s = %d", y.varName, y.x) return model
def Solving_MILP(self, turbines, dist, travelTime, vessel, t, base, farm, \ R, routes, Techs, visited): for t_1 in range(1, t): for b_1 in range(self.num_B): if turbines not in visited[vessel][t_1][b_1][farm]: continue index = visited[vessel][t_1][b_1][farm].index(turbines) if self.time_window[vessel][t][farm] == self.time_window[ vessel][t_1][farm]: c_t = R[vessel][t_1][b_1][farm][0][-4, index] c_p = R[vessel][t_1][b_1][farm][0][-3, index] c_lr = sum(max(0, t - self.WF_deadline[farm][j]) \ * self.WF_penCost[farm][j] for j in turbines) c_total = c_t + c_p + c_lr path = routes[vessel][t_1][b_1][farm][index] NumTechs = Techs[vessel][t_1][b_1][farm][index] return [[c_t, c_p, c_lr, c_total], path, NumTechs] # total num of turbines in the wind farm n = len(self.WF_deadline[farm]) # set of drop off nodes [1,...,n] dropoff = list(map(lambda qq: qq + 1, turbines)) d1 = list(range(n + 1, 2 * n + 1)) # set of pick up nodes [n+1, n+2,...,2n] pickup = [d1[i - 1] for i in dropoff] # set of nodes need vessels to be present waiting = [i + 1 for i in turbines if self.WF_present[farm][i] == 1] nodes = [0] + dropoff + pickup + [2 * n + 1] model = Model() T = model.addVars(nodes, lb=0.0, vtype=GRB.CONTINUOUS) A = [(i, j) for i in nodes for j in nodes if i != j] B = [(p, i) for p in self.types for i in nodes] y = model.addVars(A, vtype=GRB.BINARY) Q = model.addVars(B, lb=0, vtype=GRB.INTEGER) model.addConstr(y[2 * n + 1, 0] == 1) model.addConstrs( quicksum(y[i, j] for j in nodes if i != j) == 1 for i in nodes) # 9 model.addConstr(quicksum(y[0, j] for j in dropoff) == 1) # 10 model.addConstr(quicksum(y[j, 2 * n + 1] for j in pickup) == 1) # 11 model.addConstrs(quicksum(y[i, j] for j in nodes if i != j) == \ quicksum(y[j, i] for j in nodes if i != j) for i in nodes) # 12 model.addConstrs(quicksum(y[i, j] for j in nodes if i != j) == \ quicksum(y[n + i, j] for j in nodes if n + i != j) for i in dropoff) # 13 model.addConstrs(y[i, n + i] == 1 for i in waiting) # 14 model.addConstrs(T[n + i] - T[i] >= self.WF_mainTime[farm][i - 1] \ + self.V_transTime[vessel] for i in dropoff) # 15 model.addConstr( T[2 * n + 1] <= self.time_window[vessel][t][farm]) # 16 model.addConstr(T[0] == 0) # 17 model.addConstrs(y[0, j] == 0 for j in pickup) model.addConstrs(y[j, 2 * n + 1] == 0 for j in dropoff) model.addConstr( quicksum(Q[p, 0] for p in self.types) <= self.V_tech[vessel]) # 18 model.addConstrs(Q[p, 0] <= self.B_tech[base][p][t] for p in self.types) model.addConstrs(Q[p, 0] >= Q[p, i] for p in self.types for i in nodes) for i in nodes[1:-1]: for j in dropoff: if i == j: continue model.addConstrs((y[i, j] == 1) >> \ (Q[p, i] - Q[p, j] == self.WF_tech[farm][j - 1][p]) for p in self.types) for j in pickup: if i == j: continue model.addConstrs((y[i, j] == 1) >> \ (Q[p, j] - Q[p, i] == self.WF_tech[farm][j - n - 1][p]) for p in self.types) model.addConstrs((y[0, j] == 1) >> \ (T[0] + travelTime[0, j] <= T[j]) for j in dropoff) model.addConstrs((y[j, 2 * n + 1] == 1) >> (T[j] + self.V_transTime[vessel] \ + travelTime[j - n, 0] <= T[2 * n + 1]) for j in pickup) for i in nodes[1:-1]: for j in nodes[1:-1]: if j == i + n or i == j: continue i1 = i if i <= n else i - n j1 = j if j <= n else j - n model.addConstr((y[i, j] == 1) >> \ (T[i] + self.V_transTime[vessel] + travelTime[i1, j1] <= T[j])) # 19 # cost for technicians c_qr = sum(Q[p, 0] * self.tech_cost[p] for p in self.types) # travel cost (fuel cost) c_tr = 0 for i in nodes: for j in nodes: if i != j: if i == 2 * n + 1: i1 = 0 elif i <= n: i1 = i else: i1 = i - n i1 = i if i <= n else i - n if j == 2 * n + 1: j1 = 0 elif j <= n: j1 = j else: j1 = j - n c_tr += self.V_cost[vessel] * travelTime[i1, j1] * y[i, j] # penalty cost c_lr = sum(max(0, t + 1 - self.WF_deadline[farm][j]) \ * self.WF_penCost[farm][j] for j in turbines) # ojective function model.modelSense = GRB.MINIMIZE model.setObjective(c_qr + c_tr + c_lr) model.setParam('OutputFlag', 0) model.optimize() if model.status == GRB.OPTIMAL: print('optimal') c_total = model.objVal # cost for technicians c_q = sum(Q[p, 0].x * self.tech_cost[p] for p in self.types) # travel cost (fuel cost) c_t = 0 for i in nodes: for j in nodes: if i != j: if i == 2 * n + 1: i1 = 0 elif i <= n: i1 = i else: i1 = i - n i1 = i if i <= n else i - n if j == 2 * n + 1: j1 = 0 elif j <= n: j1 = j else: j1 = j - n c_t += self.V_cost[vessel] * travelTime[i1, j1] * y[i, j].x path = [] # store the optimal route without base # path = [0] TT = [0] rr = 0 ii = 0 while rr < len(nodes) - 2: for k in nodes: if ii != k and round(y[ii, k].x) == 1: ii = k path.append(k - 1 if k <= n else k - n - 1) TT.append(round(T[k].x, 2)) # path.append(k) break rr += 1 TT.append(T[nodes[-1]].x) [c_t, c_q, c_lr, c_total] = [round(i, 2) for i in [c_t, c_q, c_lr, c_total]] num_techs = [Q[p, 0].x for p in self.types] # print('nodes: ', nodes) # print('path: ', ['vessel[%d]' % vessel] + ['period[%d]' % t] + \ # ['wf[%d]' % farm] + ['b[%d]' % base] + path) # print ('T: ', TT) # print ('###############\n \n') return [[c_t, c_q, c_lr, c_total], path, num_techs] else: print('infeasible') return [[None], None, None]
def Solver(self): model = Model() n = len(self.WF_mainTime[0]) dropoff = list(range(1, n+1)) pickup = list(range(n+1, 2*n+1)) nodes = list(range(2*n+2)) B = list(range(self.num_B)) WF = list(range(self.num_WF)) D = list(range(self.period)) V = list(range(self.num_V)) P = list(range(len(self.B_tech[0]))) waiting = [[i for i in dropoff if self.WF_present[wf][i-1] == 1] for wf in WF] # calculate distance dist = [[] for b in B] for b in B: for wf in WF: if self.lambda_bf[b][wf] == 1: dist[b].append(self.createDistanceMatrix(self.WF_coordinate[wf], \ self.B_coordinate[b])) else: dist[b].append([]) # print (dist) # calculate travel time travelTime = [[] for v in V] for v in V: for b in B: if self.B_vessel[b][v] == 1: for wf in WF: if self.lambda_bf[b][wf] == 1: travelTime[v].append(dist[b][wf]/self.V_speed[v]) else: travelTime[v].append([]) # set decision variables AA = [(v,t,wf,i,j) for i in nodes for j in nodes for wf in WF \ for t in D for v in V] BB = [(v,t,wf,i) for i in nodes for wf in WF for t in D for v in V] CC = [(v,t,wf,p,i) for i in nodes for p in P for wf in WF for t in D for v in V] DD = [(wf,i) for i in dropoff for wf in WF] y = model.addVars(AA, vtype = GRB.BINARY) T = model.addVars(BB, lb = 0, vtype = GRB.CONTINUOUS) Q = model.addVars(CC, lb = 0, vtype = GRB.INTEGER) x = model.addVars(DD, lb = 0, vtype = GRB.INTEGER) ################# Constraints #################### model.addConstrs(y[v,t,wf,i,i] == 0 for i in nodes for wf in WF \ for t in D for v in V) model.addConstrs(quicksum(y[v,t,wf,0,j] for j in dropoff) <= 1 for wf in WF \ for t in D for v in V) model.addConstrs(quicksum(y[v,t,wf,j,2*n+1] for j in pickup) <= 1 for wf in WF \ for t in D for v in V) model.addConstrs(quicksum(y[v,t,wf,i,j] for i in nodes for t in D \ for v in V) == 1 for j in pickup for wf in WF) model.addConstrs(quicksum(y[v,t,wf,i,j] for j in nodes) == \ quicksum(y[v,t,wf,j,i] for j in nodes) for i in nodes for wf in WF \ for t in D for v in V) model.addConstrs(quicksum(y[v,t,wf,i,j] for j in nodes) ==\ quicksum(y[v,t,wf,n+i,j] for j in nodes) for i in dropoff for wf in WF \ for t in D for v in V) model.addConstrs(quicksum(y[v,t,wf,i,n+i] for t in D for v in V) == 1 \ for wf in WF for i in waiting[wf]) model.addConstrs(T[v,t,wf,0] == 0 for wf in WF for t in D for v in V) model.addConstrs(T[v,t,wf,n+i] - T[v,t,wf,i] >= quicksum(y[v,t,wf,0,j] \ for j in dropoff) * (self.WF_mainTime[wf][i-1] + self.V_transTime[v]) \ for i in dropoff for v in V for t in D for wf in WF) model.addConstrs((y[v,t,wf,0,j]==1) >> (T[v,t,wf,j] >= travelTime[v][wf][0,j]) \ for j in dropoff for wf in WF for t in D for v in V) model.addConstrs(quicksum(Q[v,t,wf,p,0] for p in P) <= self.V_tech[v] \ for wf in WF for t in D for v in V) model.addConstrs(quicksum(Q[v,t,wf,p,0] * self.B_vessel[b][v] for v in V \ for wf in WF) <= self.B_tech[b][p][t] for p in P for t in D for b in B) model.addConstrs(Q[v,t,wf,p,i] <= Q[v,t,wf,p,0] for p in P for i in nodes \ for t in D for v in V for wf in WF) for i in nodes[1:-1]: for j in dropoff: model.addConstrs((y[v,t,wf,i,j] == 1) >> \ (Q[v,t,wf,p,i] - Q[v,t,wf,p,j] == self.WF_tech[wf][j-1][p]) \ for p in P for wf in WF for t in D for v in V) for j in pickup: model.addConstrs((y[v,t,wf,i,j] == 1) >> \ (Q[v,t,wf,p,j] - Q[v,t,wf,p,i] == self.WF_tech[wf][j-n-1][p]) \ for p in P for wf in WF for t in D for v in V) for j in nodes[1:]: if j == i+n: continue model.addConstrs((y[v,t,wf,i,j]==1) >> (T[v,t,wf,j] - T[v,t,wf,i] >=\ self.V_transTime[v] + travelTime[v][wf][i,j]) for wf in WF for t in D \ for v in V) model.addConstrs(quicksum(t * y[v,t,wf,i,j] - x[wf,j] for i in nodes for v in V \ for t in D) <= self.WF_deadline[wf][j-1] for wf in WF for j in dropoff) model.addConstrs(y[v,t,wf,j,2*n+1] == 0 for j in dropoff for wf in WF \ for t in D for v in V) model.addConstrs(y[v,t,wf,n+i,i] == 0 for i in dropoff for wf in WF \ for t in D for v in V) model.addConstrs(y[v,t,wf,0,j] == 0 for j in pickup for wf in WF \ for t in D for v in V) model.addConstrs(y[v,t,wf,i,j] <= self.lambda_bf[b][wf] for i in nodes \ for j in nodes for t in D for v in V for wf in WF for b in B) model.addConstrs(y[v,t,wf,i,j] <= self.B_vessel[b][v] for i in nodes \ for j in nodes for t in D for wf in WF for b in B for v in V) c_qr = quicksum(Q[v,t,wf,p,0] * self.tech_cost[p] for p in P \ for wf in WF for t in D for v in V) c_tr = quicksum(self.V_cost[v] * travelTime[v][wf][i,j] * y[v,t,wf,i,j] \ for i in nodes for j in nodes for wf in WF for t in D for v in V) c_lr = quicksum(x[wf,j] * self.WF_penCost[wf][j-1] for j in dropoff for wf in WF) model.modelSense = GRB.MINIMIZE model.setObjective(c_qr+c_tr+c_lr) model.optimize() if model.status == GRB.OPTIMAL: c_total = model.objVal print (c_total) else: print('infeasible') return