def solve(active: list, centers: list, sets: list, M: int) -> list: N, K = len(active), len(sets) ### model and variables m = Model(sense=MAXIMIZE, solver_name=CBC) # whether the ith set is picked x = [m.add_var(name=f"x{i}", var_type=BINARY) for i in range(K)] # whether the ith point is covered y = [m.add_var(name=f"y{i}", var_type=BINARY) for i in range(N)] ### constraints m += xsum(x) == M, "number_circles" for i in range(N): # if yi is covered, at least one set needs to have it included = [x[k] for k in range(K) if active[i] in sets[k]] m += xsum(included) >= y[i], f"inclusion{i}" ### objective: maximize number of circles covered m.objective = xsum(y[i] for i in range(N)) m.emphasis = 2 # emphasize optimality m.verbose = 1 status = m.optimize() circles = [centers[i] for i in range(K) if x[i].x >= 0.99] covered = {active[i] for i in range(N) if y[i].x >= 0.99} return circles, covered
def __init__(self, model: mip.Model): if model.status == mip.OptimizationStatus.LOADED: logger.debug("model not runned yet, checking if feasible or not") model.emphasis = 1 # feasibility model.preprocess = 1 # -1 automatic, 0 off, 1 on. model.optimize() assert (model.status == mip.OptimizationStatus.INFEASIBLE ), "model is not linear infeasible" self.model = model
def __init__(self, model: mip.Model): if model.status == mip.OptimizationStatus.LOADED: print("model not runned yet, checking if feasible or not") model.emphasis = 1 # feasibility model.preprocess = 1 # -1 automatic, 0 off, 1 on. model.optimize() assert ( model.status == mip.OptimizationStatus.INFEASIBLE ), "model is not linear infeasible" self.model = model self.iis_num_iterations = 0 self.iis_iterations = [] self.relax_slack_iterations = []
def solve(self): """Try to solve the problem and identify possible matches.""" self._check_linear_dependency() A_eq = self.A3D.reshape(-1, self.n_1 * self.n_2) n = self.n_1 * self.n_2 # PYTHON MIP: model = Model() x = [model.add_var(var_type=BINARY) for i in range(n)] model.objective = minimize(xsum(x[i] for i in range(n))) for i, row in enumerate(A_eq): model += xsum(int(row[j]) * x[j] for j in range(n)) == int(self.b[i]) model.emphasis = 2 model.verbose = 0 model.optimize(max_seconds=2) self.X_binary = np.asarray([x[i].x for i in range(n) ]).reshape(self.n_1, self.n_2)
def model(antidisable: bool=True, disable_series: bool=True, force_disable: bool=True) -> tuple: """ Generates a model. """ ### model and variables m = Model(sense=MAXIMIZE, solver_name=CBC) global x, y A = M if disable_series else 0 # whether the ith bundle/series is disabled x = [m.add_var(name=f"x{i}", var_type=BINARY) for i in range(N + A)] # whether the ith series is included or not y = [m.add_var(name=f"y{i}", var_type=BINARY) for i in range(M)] if antidisable: global z # whether the ith series is antidisabled or not z = [m.add_var(name=f"z{i}", var_type=BINARY) for i in range(M)] ### constraints # can only disable up to K = 10 bundles, exactly K is faster but inaccurate # change to == K if it doesn't affect the solution and is faster m += xsum(x) <= NUM_DISABLE, "number_disable" # total sum of bundle sizes less than C = 20,000 m += xsum(s[i]*x[i] for i in range(len(x))) <= OVERLAP, "capacity_limit" if antidisable: # can only antidisable up to A = 500 series m += xsum(z) <= NUM_ANTIDISABLE, "number_antidisable" for i in range(M): yi, name = y[i], series_list[i] bundles = [x[j] for j in range(N) if name in bundle_dict[bundle_list[j]]] if disable_series: # the psuedo-bundle containing just the series bundles.append(x[i + N]) # if yi is included, at least one bundle needs to have it m += xsum(bundles) >= yi, f"inclusion{i}" # forcing term, comment out if the objective incentivizes forcing if force_disable: m += xsum(bundles) <= len(bundles)*yi, f"forcing{i}" # shouldn't antidisable a series if it isn't disabled if antidisable: m += z[i] <= yi, f"antidisable{i}" m.emphasis = 2 # emphasize optimality return (m, x, y, z) if antidisable else (m, x, y)
def build_infeasible_cont_model(num_constraints: int = 10, num_infeasible_sets: int = 20) -> Model: # build an infeasible model, based on many redundant constraints mdl = Model(name='infeasible_model_continuous') var = mdl.add_var(name='var', var_type=CONTINUOUS, lb=-1000, ub=1000) for idx, rand_constraint in enumerate(np.linspace(1, 1000, num_constraints)): crt = mdl.add_constr(var >= rand_constraint, name='lower_bound_{}'.format(idx)) logger.debug('added {} to the model'.format(crt)) num_constraint_inf = int(num_infeasible_sets / num_constraints) for idx, rand_constraint in enumerate( np.linspace(-1000, -1, num_constraint_inf)): crt = mdl.add_constr(var <= rand_constraint, name='upper_bound_{}'.format(idx)) logger.debug('added {} to the model'.format(crt)) mdl.emphasis = 1 # feasibility mdl.preprocess = 1 # -1 automatic, 0 off, 1 on. # mdl.pump_passes TODO configure to feasibility emphasis return mdl
def top(self, v, mustvisits, weights, max_length, n_agents): """ weights: [n,] """ N = self.n M = n_agents L = max_length w = list(weights) MAX_VISIT = int(L / self.min_edge_length) l = list(np.array(self.edges).reshape(-1)) MD = Model("plan") MD.verbose = 0 x = [MD.add_var(var_type=BINARY) for i in range(N * N * M)] y = [MD.add_var(var_type=BINARY) for i in range(N * M)] u = [MD.add_var(var_type=INTEGER) for i in range(N * M)] z = [MD.add_var(var_type=BINARY) for i in range(N)] MD.objective = maximize(xsum(z[i] * w[i] for i in range(N))) MD.emphasis = 1 for i in mustvisits: MD += (z[i] == 1) for m in range(M): MD += xsum(x[j * M + m] for j in range(1, N)) == 1 for m in range(M): MD += xsum(x[i * M * N + m] for i in range(1, N)) == 1 for i in range(1, N): MD += sum(y[i * M + m] for m in range(M)) >= z[i] MD += sum(y[i * M + m] for m in range(M)) <= z[i] * M for m in range(M): for k in range(N): MD += xsum(x[i * M * N + k * M + m] for i in range(N)) == y[k * M + m] MD += xsum(x[k * M * N + i * M + m] for i in range(N)) == y[k * M + m] for m in range(M): c5 = [] for i in range(N): for j in range(N): c5.append(l[i * N + j] * x[i * M * N + j * M + m]) MD += xsum(iter(c5)) <= L for m in range(M): for i in range(1, N): for j in range(1, N): MD += (u[i * M + m] - u[j * M + m] + 1) <= (N - 1) * (1 - x[i * M * N + j * M + m]) MD += 2 <= u[i * M + m] MD += u[i * M + m] <= N MD.optimize(max_seconds=1000) X = np.zeros((N, N, M)) for m in range(M): for i in range(N): for j in range(N): X[i, j, m] = x[i * N * M + j * M + m].x return X
def solve_integer_programing( reactant_species: List[str], product_species: List[str], reactant_bonds: List[Bond], product_bonds: List[Bond], msg: bool = True, threads: Optional[int] = None, ) -> Tuple[int, List[Union[int, None]], List[Union[int, None]]]: """ Solve an integer programming problem to get atom mapping between reactants and products. This is a utility function for `get_reaction_atom_mapping()`. Args: reactant_species: species string of reactant atoms product_species: species string of product atoms reactant_bonds: bonds in reactant product_bonds: bonds in product msg: whether to show the solver running message to stdout. threads: number of threads for the solver. `None` to use default. Returns: objective: minimized objective value. This corresponds to the number of changed bonds (both broken and formed) in the reaction. r2p_mapping: mapping of reactant atom to product atom, e.g. r2p_mapping[0] giving 3 means that reactant atom 0 maps to product atom 3. A value of `None` means a mapping cannot be found for the reactant atom. p2r_mapping: mapping of product atom to reactant atom, e.g. p2r_mapping[3] giving 0 means that product atom 3 maps to reactant atom 0. A value of `None` means a mapping cannot be found for the product atom. Reference: `Stereochemically Consistent Reaction Mapping and Identification of Multiple Reaction Mechanisms through Integer Linear Optimization`, J. Chem. Inf. Model. 2012, 52, 84โ92, https://doi.org/10.1021/ci200351b """ atoms = list(range(len(reactant_species))) # init model and variables model = Model(name="Reaction_Atom_Mapping", sense=MINIMIZE, solver_name=CBC) model.emphasis = 1 if threads is not None: model.threads = threads if msg: model.verbose = 1 else: model.verbose = 0 y_vars = {(i, k): model.add_var(var_type=BINARY, name=f"y_{i}_{k}") for i in atoms for k in atoms} alpha_vars = {(i, j, k, l): model.add_var(var_type=BINARY, name=f"alpha_{i}_{j}_{k}_{l}") for (i, j) in reactant_bonds for (k, l) in product_bonds} # add constraints # constraint 2: each atom in the reactants maps to exactly one atom in the products # constraint 3: each atom in the products maps to exactly one atom in the reactants for i in atoms: model += xsum([y_vars[(i, k)] for k in atoms]) == 1 for k in atoms: model += xsum([y_vars[(i, k)] for i in atoms]) == 1 # constraint 4: allows only atoms of the same type to map to one another for i in atoms: for k in atoms: if reactant_species[i] != product_species[k]: model += y_vars[(i, k)] == 0 # constraints 5 and 6: define each alpha_ijkl variable, permitting it to take the # value of one only if the reactant bond (i,j) maps to the product bond (k,l) for (i, j) in reactant_bonds: for (k, l) in product_bonds: model += alpha_vars[(i, j, k, l)] <= y_vars[(i, k)] + y_vars[(i, l)] model += alpha_vars[(i, j, k, l)] <= y_vars[(j, k)] + y_vars[(j, l)] # create objective obj1 = xsum(1 - xsum(alpha_vars[(i, j, k, l)] for (k, l) in product_bonds) for (i, j) in reactant_bonds) obj2 = xsum(1 - xsum(alpha_vars[(i, j, k, l)] for (i, j) in reactant_bonds) for (k, l) in product_bonds) obj = obj1 + obj2 # solve the problem try: model.objective = obj model.optimize() except Exception: raise ReactionMappingError("Failed solving integer programming.") if not model.num_solutions: raise ReactionMappingError("Failed solving integer programming.") # get atom mapping between reactant and product r2p_mapping = [None for _ in atoms] # type: List[Union[int, None]] p2r_mapping = [None for _ in atoms] # type: List[Union[int, None]] for (i, k), v in y_vars.items(): if v.x == 1: r2p_mapping[i] = k p2r_mapping[k] = i objective = model.objective_value # type: int return objective, r2p_mapping, p2r_mapping
def solve(self, list_of_locations, list_of_homes, starting_car_location, adjacency_matrix, input_file, params=[]): """ Solve the problem using an MST/DFS approach. Input: list_of_locations: A list of locations such that node i of the graph corresponds to name at index i of the list list_of_homes: A list of homes starting_car_location: The name of the starting location for the car adjacency_matrix: The adjacency matrix from the input file Output: A cost of how expensive the current solution is A list of locations representing the car path A dictionary mapping drop-off location to a list of homes of TAs that got off at that particular location NOTE: all outputs should be in terms of indices not the names of the locations themselves """ conn = sqlite3.connect('models.sqlite') c = conn.cursor() seen = c.execute( 'SELECT best_objective_bound FROM models WHERE input_file = (?)', (input_file, )).fetchone() self.log_new_entry(input_file) home_indices = convert_locations_to_indices(list_of_homes, list_of_locations) location_indices = convert_locations_to_indices( list_of_locations, list_of_locations) edge_scale = 1.0 if "--approx" in params: edge_scale = 1 / 10000 G, message = adjacency_matrix_to_graph(adjacency_matrix, edge_scale) E = list(G.to_directed().edges(data='weight')) starting_car_index = list_of_locations.index(starting_car_location) start_paths = [ convert_locations_to_indices([starting_car_location], list_of_locations) ] num_random_paths = 5 if "-r" in params: num_random_paths = int(params[params.index("-r") + 1]) for i in range(num_random_paths): start_paths.append(self.generate_random(G, starting_car_index)) if seen: output_file = 'submissions/submission_final/{}.out'.format( input_file.split('.')[0]) print(output_file) if not "--no-prev" in params and os.path.isfile(output_file): start_paths.append( convert_locations_to_indices( utils.read_file(output_file)[0], list_of_locations)) best_start_path_cost = float('inf') best_start_path_index = -1 for i, path in enumerate(start_paths): walk_cost, dropoffs = self.find_best_dropoffs( G, home_indices, path) cost, msg = cost_of_solution(G, path, dropoffs) if cost < best_start_path_cost: best_start_path_cost = cost best_start_path_index = i start_path = start_paths[best_start_path_index] print("Starting path:") if best_start_path_index == num_random_paths: print("SAVED PATH:", start_path) elif best_start_path_index >= 0: print("RANDOM PATH:", start_path) else: print("No start path found") print("Starting cost:", best_start_path_cost) # number of nodes and list of vertices, not including source or sink n, V, H = len(list_of_locations), location_indices, home_indices bigNum = (2 * n) model = Model() # does the car drive from i to j? x = [model.add_var(var_type=BINARY) for e in E] # does the kth TA walk from i to j? over all num_homes TAs t = [[model.add_var(var_type=BINARY) for e in E] for k in H] # car flow from vertex u to vertex v f = [model.add_var(var_type=INTEGER) for e in E] \ + [model.add_var(var_type=INTEGER) for v in V] \ + [model.add_var(var_type=INTEGER)] # kth TA flow from vertex u to vertex v f_t = [[model.add_var(var_type=BINARY) for e in E] + [model.add_var(var_type=BINARY) for v in V] for k in H] for i in range(len(f)): model += f[i] >= 0 # For each vertex v where v != source and v != sink, Sum{x_(u, v)} = Sum{x_(v, w)} for v in V: model += xsum([x[i] for i in range(len(E)) if E[i][1] == v]) == xsum( [x[i] for i in range(len(E)) if E[i][0] == v]) # For each vertex v where v != sink, Sum{f_(u, v)} = Sum{f_(v, w)} for j in range(len(V)): model += xsum([f[i] for i in range(len(E)) if E[i][1] == V[j]]) + (f[-1] if V[j] == starting_car_index else 0) \ == xsum([f[i] for i in range(len(E)) if E[i][0] == V[j]]) + f[len(E) + j] # For each edge (u, v) where u != source and v != sink, f_(u, v) <= (big number) * x_(u, v) for i in range(len(E)): model += f[i] <= bigNum * x[i] # For edge (source, start vertex), f_(source, start vertex) <= (big number) model += f[-1] <= bigNum # For each edge (u, sink), f_(u, sink) <= Sum{x_(w, u)} for j in range(len(V)): model += f[j + len(E)] \ <= xsum([x[i] for i in range(len(E)) if E[i][1] == V[j]]) # For just the source vertex, f_(source,start vertex)} = Sum{x_(a, b)} model += f[-1] == xsum(x) # For every TA for every edge, can't flow unless edge is walked along for i in range(len(t)): for j in range(len(E)): model += f_t[i][j] <= t[i][j] # For every TA for every non-home vertex, flow in equals flow out for i in range(len(H)): for j in range(len(V)): if V[j] != H[i]: model += xsum(f_t[i][k] for k in range(len(E)) if E[k][1] == V[j]) + f_t[i][len(E) + j] \ == xsum(f_t[i][k] for k in range(len(E)) if E[k][0] == V[j]) # For every TA, flow out of the source vertex is exactly 1 for k in f_t: model += xsum(k[len(E) + i] for i in range(len(V))) == 1 # For every TA for every edge out of source, can't flow unless car visits vertex for k in f_t: for i in range(len(V)): model += k[len(E) + i] <= xsum( x[j] for j in range(len(E)) if E[j][1] == V[i]) # For every TA, flow into the home vertex is exactly 1 for i in range(len(H)): model += xsum(f_t[i][j] for j in range(len(E)) if E[j][1] == H[i]) + f_t[i][len(E) + H[i]] == 1 # objective function: minimize the distance model.objective = minimize(2.0/3.0 * xsum([x[i] * E[i][2] for i in range(len(E))]) \ + xsum([xsum([t[i][j] * E[j][2] for j in range(len(E))]) for i in range(len(t))])) # WINNING ONLINE model.max_gap = 0.00001 model.emphasis = 2 model.symmetry = 2 if "--no-model-start" not in params: model.start = self.construct_starter(x, t, G, home_indices, start_path) timeout = 300 if "-t" in params: timeout = int(params[params.index("-t") + 1]) if timeout != -1: status = model.optimize(max_seconds=timeout) else: status = model.optimize() objective_value = model.objective_value / edge_scale objective_bound = model.objective_bound / edge_scale if status == OptimizationStatus.OPTIMAL: print('optimal solution cost {} found'.format(objective_value)) self.log_update_entry(Fore.GREEN + "Optimal cost={}.".format(objective_value) + Style.RESET_ALL) else: print("!!!! TIMEOUT !!!!") self.log_update_entry(Fore.RED + "Timeout!" + Style.RESET_ALL) if status == OptimizationStatus.FEASIBLE: print('sol.cost {} found, best possible: {}'.format( objective_value, objective_bound)) self.log_update_entry("Feasible cost={}, bound={}.".format( objective_value, objective_bound)) elif status == OptimizationStatus.NO_SOLUTION_FOUND: print('no feasible solution found, lower bound is: {}'.format( objective_bound)) self.log_update_entry( "Failed, bound={}.".format(objective_bound)) # if no solution found, return inf cost if model.num_solutions == 0: conn.close() return float('inf'), [], {} # printing the solution if found out.write('Route with total cost %g found. \n' % (objective_value)) if "-v" in params: out.write('\nEdges (In, Out, Weight):\n') for i in E: out.write(str(i) + '\t') out.write('\n\nCar - Chosen Edges:\n') for i in x: out.write(str(i.x) + '\t') out.write('\n\nCar - Flow Capacities:\n') for i in f: out.write(str(i.x) + '\t') out.write('\n\nTAs - Home Indices:\n') for i in H: out.write(str(i) + '\n') out.write('\nTAs - Chosen Edges:\n') for i in t: for j in range(len(i)): out.write(str(i[j].x) + '\t') out.write('\n') out.write('\nTAs - Flow Capacities:\n') for i in f_t: for j in range(len(i)): out.write(str(i[j].x) + '\t') out.write('\n') out.write('\nActive Edges:\n') for i in range(len(x)): if (x[i].x >= 1.0): out.write('Edge from %i to %i with weight %f \n' % (E[i][0], E[i][1], E[i][2])) out.write('\n') list_of_edges = [E[i] for i in range(len(x)) if x[i].x >= 1.0] car_path_indices = self.construct_path(starting_car_index, list_of_edges, input_file) walk_cost, dropoffs_dict = self.find_best_dropoffs( G, home_indices, car_path_indices) updated = False if not seen: print("SAVING", input_file) c.execute('INSERT INTO models (input_file, best_objective_bound, optimal) VALUES (?, ?, ?)', \ (input_file, objective_value, status == OptimizationStatus.OPTIMAL)) conn.commit() elif objective_value <= seen[0]: updated = True print("UPDATING", input_file) c.execute('UPDATE models SET best_objective_bound = ?, optimal = ? WHERE input_file = ?', \ (objective_value, status == OptimizationStatus.OPTIMAL, input_file)) conn.commit() if not "-s" in params: print("Walk cost =", walk_cost, "\n") if updated: self.log_update_entry(Fore.GREEN + "Updated" + Style.RESET_ALL) else: self.log_update_entry(Fore.RED + "Not Updated" + Style.RESET_ALL) conn.close() return objective_value, car_path_indices, dropoffs_dict
def tsp_mip_solver(input_data): # parse the input lines = input_data.split('\n') nodeCount = int(lines[0]) points = [] for i in range(1, nodeCount + 1): line = lines[i] parts = line.split() points.append(Point(float(parts[0]), float(parts[1]))) print('Points parsed!') # calculate distance matrix d_m = [[length(q, p) for q in points] for p in points] print('Distance matrix ready!') # declare MIP model m = Model(solver_name='GRB') print('-Model instatiated!', datetime.datetime.now()) # states search emphasis # - '0' (default) balanced approach # - '1' (feasibility) aggressively searches for feasible solutions # - '2' (optimality) explores search space to tighten dual gap m.emphasis = 0 # whenever the distance of the lower and upper bounds is less or # equal max_gap*100%, the search can be finished m.max_gap = 0.05 # specifies number of used threads # 0 uses solver default configuration, # -1 uses the number of available processing cores # โฅ1 uses the specified number of threads. # An increased number of threads may improve the solution time but also increases # the memory consumption. Each thread needs to store a different model instance! m.threads = 0 # controls the generation of cutting planes # cutting planes usually improve the LP relaxation bound but also make the solution time of the LP relaxation larger # -1 means automatic # 0 disables completely # 1 (default) generates cutting planes in a moderate way # 2 generates cutting planes aggressively # 3 generates even more cutting planes m.cuts = -1 m.preprocess = 1 m.pump_passes = 20 m.sol_pool_size = 1 nodes = set(range(nodeCount)) # instantiate "entering and leaving" variables x = [[m.add_var(name="x{}_{}".format(p, q), var_type='B') for q in nodes] for p in nodes] # instantiate subtour elimination variables y = [m.add_var(name="y{}".format(i)) for i in nodes] print('-Variables instantiated', datetime.datetime.now()) # declare objective function m.objective = minimize( xsum(d_m[i][j] * x[i][j] for i in nodes for j in nodes)) print('-Objective declared!', datetime.datetime.now()) # declare constraints # leave each city only once for i in tqdm(nodes): m.add_constr(xsum(x[i][j] for j in nodes - {i}) == 1) # enter each city only once for i in tqdm(nodes): m.add_constr(xsum(x[j][i] for j in nodes - {i}) == 1) # subtour elimination constraints for (i, j) in tqdm(product(nodes - {0}, nodes - {0})): if i != j: m.add_constr(y[i] - (nodeCount + 1) * x[i][j] >= y[j] - nodeCount) print('-Constraints declared!', datetime.datetime.now()) #Maximum time in seconds that the search can go on if a feasible solution #is available and it is not being improved mssi = 1000 #default = inf # specifies maximum number of nodes to be explored in the search tree (default = inf) mn = 1000000 #default = 1073741824 # optimize model m within a processing time limit of 'ms' seconds ms = 3000 #default = inf # executes the optimization print('-Optimizer start.', datetime.datetime.now()) #status = m.optimize(max_seconds = ms,max_seconds_same_incumbent = mssi,max_nodes = mn) status = m.optimize(max_seconds=ms, max_seconds_same_incumbent=mssi) print('Opt. Status:', status) print('MIP Sol. Obj.:', m.objective_value) print('Dual Bound:', m.objective_bound) print('Dual gap:', m.gap) sol = [0] c_node = 0 for j in range(nodeCount - 1): for i in range(nodeCount): if round(m.var_by_name("x{}_{}".format(c_node, i)).x) != 0: sol.append(i) c_node = i break obj = m.objective_value # prepare the solution in the specified output format if status == OptimizationStatus.OPTIMAL: output_data = '%.2f' % obj + ' ' + str(1) + '\n' output_data += ' '.join(map(str, sol)) elif status == OptimizationStatus.FEASIBLE: output_data = '%.2f' % obj + ' ' + str(0) + '\n' output_data += ' '.join(map(str, sol)) return output_data
def fl_mip_solver(input_data): # Modify this code to run your optimization algorithm # parse the input lines = input_data.split('\n') parts = lines[0].split() facility_count = int(parts[0]) customer_count = int(parts[1]) facilities = [] for i in range(1, facility_count+1): parts = lines[i].split() facilities.append(Facility(i-1, float(parts[0]), int(parts[1]), Point(float(parts[2]), float(parts[3])) )) customers = [] for i in range(facility_count+1, facility_count+1+customer_count): parts = lines[i].split() customers.append(Customer(i-1-facility_count, int(parts[0]), Point(float(parts[1]), float(parts[2])))) print('F count:',facility_count) print('C count:',customer_count) # instantiates setup cost vector set_cost = [f.setup_cost for f in facilities] # instantiates proportional capacity matrix such that Cap[i,j] represents the demand of # customer 'j' as a fraction of the total capacity of facility 'i' Cap = [[c.demand/f.capacity for c in customers] for f in facilities] # instantiates distance matrix in such a way that "sum_{j \in M} D[i,j]*at[i,j]" is the total # distance cost for facility 'i' D = [[length(c.location, f.location) for c in customers] for f in facilities] # declares MIP model #m = Model(solver_name=CBC) m = Model(solver_name='GRB') print('-Model instatiated!',datetime.datetime.now()) # states search emphasis # - '0' (default) balanced approach # - '1' (feasibility) aggressively searches for feasible solutions # - '2' (optimality) explores search space to tighten dual gap m.emphasis = 2 # whenever the distance of the lower and upper bounds is less or # equal max_gap*100%, the search can be finished m.max_gap = 0.05 # specifies number of used threads # 0 uses solver default configuration, # -1 uses the number of available processing cores # โฅ1 uses the specified number of threads. # An increased number of threads may improve the solution time but also increases # the memory consumption. Each thread needs to store a different model instance! m.threads = -1 # controls the generation of cutting planes # cutting planes usually improve the LP relaxation bound but also make the solution time of the LP relaxation larger # -1 means automatic # 0 disables completely # 1 (default) generates cutting planes in a moderate way # 2 generates cutting planes aggressively # 3 generates even more cutting planes m.cuts=-1 m.preprocess=1 m.pump_passes=10 m.sol_pool_size=1 # instantiates open facilities variables op = [m.add_var(name="op{}".format(i),var_type='B') for i in range(facility_count)] # instantiates matrix of atribution variables # (line 'i' is an atribution vector for facility 'i') # e.g.: At[3][4] returns whether customer 4 is assigned to facility 3 At = [[m.add_var(name="At{},{}".format(i,j) ,var_type='B') for j in range(customer_count)] for i in range(facility_count)] print('-Variables declared!',datetime.datetime.now()) # instantiates objective function # m.objective = minimize("setup costs" + "distance costs") # Form example: # m.objective = minimize( # xsum(dist[i, j] * x[i, j] for (i, j) in product(F, C)) + xsum(y[i] for i in F) ) m.objective = minimize( xsum(set_cost[i]*op[i] for i in range(facility_count)) + xsum(sum(D[i][j]*At[i][j] for j in range(customer_count)) for i in range(facility_count)) ) print('-Objective declared!',datetime.datetime.now()) # instatiates capacity constraints # -can be expressed as "sum_{j \in M} Cap[i,j]*At[i,j] <= op[i]" for all facilities 'i' # -if a facility is closed (op[i]=0), its effective capacity is 0 for i in range(facility_count): m.add_lazy_constr( xsum(Cap[i][j]*At[i][j] for j in range(customer_count)) <= op[i] ) # instantiates assignment constraints (maximum of 1 facility per customer) # -can be expressed as: "sum of elements over of each column of 'At' == 1" for i in range(customer_count): m += xsum(At[j][i] for j in range(facility_count)) == 1 print('-Contraints processed!',datetime.datetime.now()) #Maximum time in seconds that the search can go on if a feasible solution #is available and it is not being improved mssi = 1000 #default = inf # specifies maximum number of nodes to be explored in the search tree (default = inf) mn = 40000 #default = 1073741824 # optimize model m within a processing time limit of 'ms' seconds ms = 3000 #default = inf print('-Optimizer start.',datetime.datetime.now()) # executes the optimization #status = m.optimize(max_seconds = ms,max_seconds_same_incumbent = mssi,max_nodes = mn) status = m.optimize(max_seconds = ms , max_seconds_same_incumbent = mssi) final_obj = m.objective_value print('Opt. Status:',status) print('MIP Sol. Obj.:',final_obj) print('Dual Bound:',m.objective_bound) print('Dual gap:',m.gap) used=[i for i in range(facility_count) if m.vars[i].x==1] solution=[None] * customer_count for i in range(facility_count): for j in range(customer_count): if round(m.vars[i*customer_count + j + facility_count].x) == 1: solution[j]=i # parse output varibles and convert to conventional solution output format # calculate the cost of the solution obj = sum([facilities[f].setup_cost for f in used]) for customer in customers: obj += length(customer.location, facilities[solution[customer.index]].location) if status == OptimizationStatus.OPTIMAL: # prepare the solution in the specified output format output_data = '%.2f' % obj + ' ' + str(1) + '\n' output_data += ' '.join(map(str, solution)) elif status == OptimizationStatus.FEASIBLE: output_data = '%.2f' % obj + ' ' + str(0) + '\n' output_data += ' '.join(map(str, solution)) return output_data
def mip_solver(input_data): # Modify this code to run your optimization algorithm # parse the input lines = input_data.split('\n') firstLine = lines[0].split() item_count = int(firstLine[0]) capacity = int(firstLine[1]) items = [] for i in range(1, item_count+1): line = lines[i] parts = line.split() items.append(Item(i-1, int(parts[0]), int(parts[1]))) # a trivial algorithm for filling the knapsack # it takes items in-order until the knapsack is full value = 0 weight = 0 taken = [0]*len(items) # declare value vector val_vec = [item.value for item in items] # declare weight vector scaled by capacity wgt_vec = [item.weight/capacity for item in items] # start MIP solver m = Model(solver_name='GRB') print('-Model instatiated!',datetime.datetime.now()) # states search emphasis # - '0' (default) balanced approach # - '1' (feasibility) aggressively searches for feasible solutions # - '2' (optimality) explores search space to tighten dual gap m.emphasis = 2 # whenever the distance of the lower and upper bounds is less or # equal max_gap*100%, the search can be finished m.max_gap = 0.05 # specifies number of used threads # 0 uses solver default configuration, # -1 uses the number of available processing cores # โฅ1 uses the specified number of threads. # An increased number of threads may improve the solution time but also increases # the memory consumption. Each thread needs to store a different model instance! m.threads = -1 # controls the generation of cutting planes # cutting planes usually improve the LP relaxation bound but also make the solution time of the LP relaxation larger # -1 means automatic # 0 disables completely # 1 (default) generates cutting planes in a moderate way # 2 generates cutting planes aggressively # 3 generates even more cutting planes m.cuts=-1 m.preprocess=1 m.pump_passes=10 m.sol_pool_size=1 # instantiates taken items variable vector taken = [m.add_var(name="it{}".format(i),var_type='B') for i in range(item_count)] print('-Variables declared!',datetime.datetime.now()) # instantiates objective function m.objective = maximize( xsum( val_vec[i]*taken[i] for i in range(item_count) ) ) print('-Objective declared!',datetime.datetime.now()) m.add_constr( xsum( wgt_vec[i]*taken[i] for i in range(item_count)) <=1 ) print('-Contraints processed!',datetime.datetime.now()) #Maximum time in seconds that the search can go on if a feasible solution #is available and it is not being improved mssi = 1000 #default = inf # specifies maximum number of nodes to be explored in the search tree (default = inf) mn = 40000 #default = 1073741824 # optimize model m within a processing time limit of 'ms' seconds ms = 3000 #default = inf print('-Optimizer start.',datetime.datetime.now()) # executes the optimization #status = m.optimize(max_seconds = ms,max_seconds_same_incumbent = mssi,max_nodes = mn) status = m.optimize(max_seconds = ms , max_seconds_same_incumbent = mssi) final_obj = m.objective_value print('Opt. Status:',status) print('MIP Sol. Obj.:',final_obj) print('Dual Bound:',m.objective_bound) print('Dual gap:',m.gap) sol = [round(it.x) for it in taken] value = int(final_obj) taken = sol # prepare the solution in the specified output format if status == OptimizationStatus.OPTIMAL: output_data = str(value) + ' ' + str(1) + '\n' output_data += ' '.join(map(str, taken)) elif status == OptimizationStatus.FEASIBLE: output_data = str(value) + ' ' + str(0) + '\n' output_data += ' '.join(map(str, taken)) if item_count == 10000: output_data=greedy(input_data) return output_data
# denominator of the expected value m += xsum(w[i]*(d - (y[1][i] - z[1][i])) for i in range(M)) == 1, "denominator" # if not antidisabling, turn each antidisable off if not ANTIDISABLE: for i in range(len(z[0])): m += z[0][i] == 0, f"{zi.name}0" m += z[1][i] == 0, f"{zi.name}1" ### objective: maximize expected value of the remaining characters # numerator of the expected value, denominator has been accounted for m.objective = xsum(t[i]*(d - (y[1][i] - z[1][i])) for i in range(M)) if __name__ == "__main__": m.emphasis = 2 # emphasize optimality m.preprocess = 1 # don't preprocess if it introduces error status = m.optimize() disable_list = [bundle_list[i] for i in range(N) if x[i].x >= 0.99] + \ [series_list[i - N] for i in range(N, N + A) if x[i].x >= 0.99] antidisable_list = [series_list[i] for i in range(M) if z[0][i].x >= 0.99] total = get_size(disable_list) count, count_anti = get_wa(disable_list), get_wa(antidisable_list) X, p = random_variable(character_values(disable_list, antidisable_list)) print(f"expected value: {E(p, X):.3f}") print(f"disablelist ({len(disable_list)}/{NUM_DISABLE})") print(f"{server_disabled + total} disabled ({server_wa + count} $wa)") print(f"Overlap limit: {total} / {OVERLAP} characters")