def build_infeasible_cont_model(num_constraints: int = 10, num_infeasible_sets: int = 20) -> mip.Model: # build an infeasible model, based on many redundant constraints mdl = mip.Model(name="infeasible_model_continuous") var = mdl.add_var(name="x", var_type=mip.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_{0}_l{1}".format(idx, random.randint(1, 6)), ) 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_{0}_l{1}".format(idx, random.randint(1, 7)), ) 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 upperbounding_hyperplane(A, b): A = np.array(A) b = np.array(b) N = A.shape[1] M = A.shape[0] assert (b.shape[0] == M) # print(N) # print(M) model = mip.Model(solver_name=mip.CBC) model.verbose = 0 e = [model.add_var(name='e({})'.format(i + 1)) for i in range(M)] x = [model.add_var(name='x({})'.format(i + 1)) for i in range(N)] # e = A*x - b for i in range(M): e[i] = -1 * b[i] for j in range(N): e[i] = e[i] + A[i, j] * x[j] model += e[i] >= 0 total_error = mip.xsum(e[i] for i in range(M)) model.objective = total_error model.optimize() # x_found = np.zeros(N) x_found = np.array([x[i].x for i in range(N)]) # print(x_found) return x_found, model.objective_value
def find_optimal_pairs(N, weights) -> list[tuple[int, int]]: """ find_optimal_pairs finds an optimal set of pairs of integers between 0 and N-1 (incl) that minimize the sum of the weights specified for each pair. """ pairs = [(i, j) for i in range(N - 1) for j in range(i + 1, N)] def pairs_containing(k): return chain(((i, k) for i in range(k)), ((k, i) for i in range(k + 1, N))) m = mip.Model() p = {(i, j): m.add_var(var_type=mip.BINARY) for i, j in pairs} # Constraint: a person can only be in one pair, so sum of all pairs with person k must be 1 for k in range(N): m += mip.xsum(p[i, j] for i, j in pairs_containing(k)) == 1 m.objective = mip.minimize( mip.xsum(weights(i, j) * p[i, j] for i, j in pairs)) m.verbose = False status = m.optimize() if status != mip.OptimizationStatus.OPTIMAL: raise Exception("not optimal" + status) print("Objective value =", m.objective_value) return [(i, j) for i, j in pairs if p[i, j].x > 0.5]
def het_qp(t, B): t = np.array(t) B = np.array(B) n = t.shape[0] m = B.shape[0] assert (B.shape[1] == n) model = mip.Model(solver_name=mip.CBC) model.verbose = 0 e = [[ model.add_var(name='e({},{})'.format(i + 1, j + 1)) for j in range(n) ] for i in range(m)] h_1 = model.add_var(name='h_1') h_2 = [model.add_var(name='h_2({})'.format(i + 1)) for i in range(m)] # total_error = model.add_var(name='e_sum') total_error = 0 for i in range(m): for j in range(n): if B[i][j] <= t[-1]: e[i][j] = t[j] * h_1 + h_2[i] - B[i][j] model += e[i][j] >= 0 total_error = total_error + e[i][j] model.objective = total_error model.optimize() h1_found = h_1.x h2_found = np.array([h_2[i].x for i in range(m)]) return h1_found, h2_found
def generate_model(): model = mip.Model(solver_name=solver_id) # `in_committee` is a binary variable indicating whether `cand` is in the committee in_committee = [ model.add_var(var_type=mip.BINARY, name=f"cand{cand}_in_committee") for cand in profile.candidates ] # find a new committee that has not been found yet by excluding previously found committees for committee in committees: model += mip.xsum(in_committee[cand] for cand in committee) <= committeesize - 1 # note: verbose = 1 causes issues with unittests, seems as if output is printed too late # and anyway the output does not seem to be very helpful model.verbose = 0 set_opt_model_func( model, profile, in_committee, committeesize, ) # emphasis is optimality: # activates procedures that produce improved lower bounds, focusing in pruning the search # tree even if the production of the first feasible solutions is delayed. model.emphasis = 2 model.opt_tol = ACCURACY model.max_mip_gap = ACCURACY model.integer_tol = ACCURACY return model, in_committee
def estimate_cp(bal_res, de_ads): df_normal = bal_res.loc[lambda row: row.dp_phased == False] df_abnormal = bal_res.loc[lambda row: row.dp_phased == True] ads_abnormal = de_ads[df_abnormal.index] M_, m_ = np.full(len(bal_res), np.nan), np.full(len(bal_res), np.nan) hap_dp = df_normal.avg_depths.mean() / 2 for row in df_abnormal.itertuples(): ads = de_ads[row.Index] mod = mip.Model() M, m = mod.add_var(var_type=mip.INTEGER, lb=1), mod.add_var(var_type=mip.INTEGER, lb=1) eM = [mod.add_var() for i in range(len(ads))] em = [mod.add_var() for i in range(len(ads))] for i in range(len(ads)): mod += ads[i].max() - M * hap_dp >= -eM[i] mod += ads[i].max() - M * hap_dp <= eM[i] mod += ads[i].min() - m * hap_dp >= -em[i] mod += ads[i].min() - m * hap_dp <= em[i] mod += M >= m mod.objective = mip.xsum(eM[i] for i in range(len(ads))) + mip.xsum( em[i] for i in range(len(ads))) mod.optimize() M_[row.Index] = M.x m_[row.Index] = m.x res = pd.concat( [bal_res, pd.DataFrame({ 'cp': M_ + m_, 'M': M_, 'm': m_ })], axis=1) return res
def find_optimal_pairs(N, weights) -> (float, list[tuple[int, int]]): """ find_optimal_pairs finds an optimal set of pairs of integers between 0 and N-1 (incl) that minimize the sum of the weights specified for each pair. Returns the objective value and list of pairs. """ pairs = [(i, j) for i in range(N) for j in range(i, N)] # note: people are excluded from the round by pairing with themselves def pairs_containing(k): return chain(((i, k) for i in range(k)), ((k, i) for i in range(k, N))) m = mip.Model() p = {(i, j): m.add_var(var_type=mip.BINARY) for i, j in pairs} # Constraint: a person can only be in one pair, so sum of all pairs with person k must be 1 for k in range(N): m += mip.xsum(p[i, j] for i, j in pairs_containing(k)) == 1 m.objective = mip.minimize( mip.xsum(weights[i, j] * p[i, j] for i, j in pairs)) m.verbose = False status = m.optimize() if status != mip.OptimizationStatus.OPTIMAL: raise Exception("not optimal") return m.objective_value, [(i, j) for i, j in pairs if p[i, j].x > 0.5]
def _compute_integer_image_sizes(image_sizes: List[Size], layout: Layout) -> List[Size]: import mip constraints = layout.get_constraints(image_sizes) aspect_ratios = [h / w for w, h in image_sizes] # set up a mixed-integer program, and solve it n_images = len(image_sizes) m = mip.Model() var_widths = [m.add_var(var_type=mip.INTEGER) for _ in range(n_images)] var_heights = [m.add_var(var_type=mip.INTEGER) for _ in range(n_images)] for c in constraints: if c.is_height: vars = ([var_heights[i] for i in c.positive_ids] + [-var_heights[i] for i in c.negative_ids]) else: vars = ([var_widths[i] for i in c.positive_ids] + [-var_widths[i] for i in c.negative_ids]) m.add_constr(mip.xsum(vars) == c.result) # the errors come from a deviation in aspect ratio var_errs = [m.add_var(var_type=mip.CONTINUOUS) for _ in range(n_images)] for err, w, h, ar in zip(var_errs, var_widths, var_heights, aspect_ratios): m.add_constr(err == h - w * ar) # To minimise error, we need to create a convex cost function. Common # options are either abs(err) or err ** 2. However, both these functions are # non-linear, so cannot be directly computed in MIP. We can represent abs # exactly with a type-1 SOS, and approximate ** 2 with a type-2 SOS. Here we # use abs. var_errs_pos = [ m.add_var(var_type=mip.CONTINUOUS) for _ in range(n_images) ] var_errs_neg = [ m.add_var(var_type=mip.CONTINUOUS) for _ in range(n_images) ] var_abs_errs = [ m.add_var(var_type=mip.CONTINUOUS) for _ in range(n_images) ] for abs_err, err, err_pos, err_neg in zip(var_abs_errs, var_errs, var_errs_pos, var_errs_neg): # err_pos and err_neg are both positive representing each side of the # abs function. Only one will be non-zero (SOS Type-1). m.add_constr(err == err_pos - err_neg) m.add_constr(abs_err == err_pos + err_neg) m.add_sos([(err_pos, 1), (err_neg, -1)], sos_type=1) m.objective = mip.minimize(mip.xsum(var_abs_errs)) m.optimize(max_seconds=30) new_sizes = [ Size(int(w.x), int(h.x)) for w, h in zip(var_widths, var_heights) ] return new_sizes
def test_conflict_finder_feasible(): mdl = mip.Model(name="feasible_model") var = mdl.add_var(name="x", var_type=mip.CONTINUOUS, lb=-mip.INF, ub=mip.INF) mdl.add_constr(var >= 1, "lower_bound") with pytest.raises(AssertionError, match="model is not linear infeasible"): cf = ConflictFinder(model=mdl)
def create_model(sudoku: SudokuMatrix, verbose: int) -> (mip.Model, SudokuVariableTensor): model = mip.Model() model.verbose = verbose variable_tensor = init_model_variables(model) add_general_sudoku_constraints(model, variable_tensor) add_predefined_digits_constraints(model, variable_tensor, sudoku) return model, variable_tensor
def init_model(self, **kwargs): nb_facilities = self.facility_problem.facility_count nb_customers = self.facility_problem.customer_count use_matrix_indicator_heuristic = kwargs.get("use_matrix_indicator_heuristic", True) if use_matrix_indicator_heuristic: n_shortest = kwargs.get("n_shortest", 10) n_cheapest = kwargs.get("n_cheapest", 10) matrix_fc_indicator, matrix_length = prune_search_space(self.facility_problem, n_cheapest=n_cheapest, n_shortest=n_shortest) else: matrix_fc_indicator, matrix_length = prune_search_space(self.facility_problem, n_cheapest=nb_facilities, n_shortest=nb_facilities) s = mip.Model(name="facilities", sense=mip.MINIMIZE, solver_name=self.solver_name) x = {} for f in range(nb_facilities): for c in range(nb_customers): if matrix_fc_indicator[f, c] == 0: x[f, c] = 0 elif matrix_fc_indicator[f, c] == 1: x[f, c] = 1 elif matrix_fc_indicator[f, c] == 2: x[f, c] = s.add_var(var_type=mip.BINARY, obj=0, name="x_" + str((f, c))) facilities = self.facility_problem.facilities customers = self.facility_problem.customers used = s.add_var_tensor((nb_facilities, 1), var_type=GRB.BINARY, name="y") constraints_customer = {} for c in range(nb_customers): constraints_customer[c] = s.add_constr(mip.xsum([x[f, c] for f in range(nb_facilities)]) == 1) # one facility constraint_capacity = {} for f in range(nb_facilities): for c in range(nb_customers): s.add_constr(used[f, 0] >= x[f, c]) constraint_capacity[f] = s.add_constr(mip.xsum([x[f, c] * customers[c].demand for c in range(nb_customers)]) <= facilities[f].capacity) new_obj_f = mip.LinExpr(const=0.) new_obj_f.add_expr(mip.xsum([facilities[f].setup_cost * used[f, 0] for f in range(nb_facilities)])) new_obj_f.add_expr(mip.xsum([matrix_length[f, c] * x[f, c] for f in range(nb_facilities) for c in range(nb_customers)])) s.objective = new_obj_f self.model = s self.variable_decision = {"x": x} self.constraints_dict = {"constraint_customer": constraints_customer, "constraint_capacity": constraint_capacity} self.description_variable_description = {"x": {"shape": (nb_facilities, nb_customers), "type": bool, "descr": "for each facility/customer indicate" " if the pair is active, meaning " "that the customer c is dealt with facility f"}} self.description_constraint = {"Im lazy."} print("Initialized")
def mip_model(self, representation, labeled_idx, budget, delta, outlier_count, greedy_indices=None): model = mip.Model("Core Set Selection") # set up the variables: points = {} outliers = {} feasible_start = [] for i in range(representation.shape[0]): if i in labeled_idx: points[i] = model.add_var(ub=1.0, lb=1.0, var_type=BINARY, name="points_{}".format(i)) else: points[i] = model.add_var(var_type=BINARY, name="points_{}".format(i)) for i in range(representation.shape[0]): name = "outliers_{}".format(i) outliers[i] = model.add_var(var_type=BINARY, name=name) # outliers[i].start = 0 feasible_start.append((outliers[i], 0.0)) # initialize the solution to be the greedy solution: if greedy_indices is not None: for i in greedy_indices: # points[i].start = 1.0 # gurobi feasible_start.append((points[i], 1)) model.start = feasible_start # set the outlier budget: model.add_constr(xsum(outliers[i] for i in outliers) <= outlier_count, "budget") # build the graph and set the constraints: model.add_constr(xsum(points[i] for i in range(representation.shape[0])) == budget, "budget") neighbors = {} graph = {} print("Updating Neighborhoods In MIP Model...") for i in range(0, representation.shape[0], 1000): print("At Point " + str(i)) if i + 1000 > representation.shape[0]: distances = self.get_distance_matrix(representation[i:], representation) amount = representation.shape[0] - i else: distances = self.get_distance_matrix(representation[i:i + 1000], representation) amount = 1000 distances = np.reshape(distances, (amount, -1)) for j in range(i, i + amount): graph[j] = [(idx, distances[j - i, idx]) for idx in np.reshape(np.where(distances[j - i, :] <= delta), (-1))] neighbors[j] = [points[idx] for idx in np.reshape(np.where(distances[j - i, :] <= delta), (-1))] neighbors[j].append(outliers[j]) model.add_constr(xsum(neighbors[j]) >= 1, "coverage+outliers") model.__data = points, outliers model.emphasis = 1 model.threads = -1 # model.max_seconds = self.max_seconds return model, graph
def test_conflict_finder(): mdl = mip.Model(name="infeasible_model_continuous") var = mdl.add_var(name="x", var_type=mip.CONTINUOUS, lb=-mip.INF, ub=mip.INF) mdl.add_constr(var >= 1, "lower_bound") mdl.add_constr(var <= 0, "upper_bound") cf = ConflictFinder(model=mdl) iis = cf.find_iis() iis_names = set([crt.name for crt in iis]) assert set(["lower_bound", "upper_bound"]) == iis_names
def test_conflict_finder_iis_additive_method_two_options(): mdl = mip.Model(name="infeasible_model_continuous") var_x = mdl.add_var(name="x", var_type=mip.CONTINUOUS, lb=-mip.INF, ub=mip.INF) mdl.add_constr(var_x >= 1, "lower_bound") mdl.add_constr(var_x <= 0, "upper_bound") mdl.add_constr(var_x <= -3, "upper_bound_2") cf = ConflictFinder(model=mdl) iis = cf.find_iis(method = IISFinderAlgorithm.ADDITIVE_ALGORITHM) iis_names = set([crt.name for crt in iis]) assert set(["lower_bound", "upper_bound"]) == iis_names or set(["lower_bound", "upper_bound_2"]) == iis_names
def additive_algorithm(self) -> mip.ConstrList: """Additive algorithm to find an IIS Returns: mip.ConstrList: IIS """ # Create some aux models to test feasibility of the set of constraints aux_model_testing = mip.Model() for var in self.model.vars: aux_model_testing.add_var( name=var.name, lb=var.lb, ub=var.ub, var_type=var.var_type, # obj= var.obj, # column=var.column #!! libc++abi.dylib: terminating with uncaught exception of type CoinError ) aux_model_testing.objective = 1 aux_model_testing.emphasis = 1 # feasibility aux_model_testing.preprocess = 1 # -1 automatic, 0 off, 1 on. aux_model_iis = ( aux_model_testing.copy() ) # a second aux model to test feasibility of the incumbent iis # algorithm start all_constraints = self.model.constrs testing_crt_set = mip.ConstrList(model=aux_model_testing) # T iis = mip.ConstrList(model=aux_model_iis) # I while True: for crt in all_constraints: testing_crt_set.add(crt.expr, name=crt.name) aux_model_testing.constrs = testing_crt_set aux_model_testing.optimize() if aux_model_testing.status == mip.OptimizationStatus.INFEASIBLE: iis.add(crt.expr, name=crt.name) aux_model_iis.constrs = iis aux_model_iis.optimize() if aux_model_iis.status == mip.OptimizationStatus.INFEASIBLE: return iis elif aux_model_iis.status in [ mip.OptimizationStatus.FEASIBLE, mip.OptimizationStatus.OPTIMAL, ]: testing_crt_set = mip.ConstrList( model=aux_model_testing) for (crt) in ( iis ): # basically this loop is for set T=I // aux_model_iis = iis.copy() testing_crt_set.add(crt.expr, name=crt.name) break
def equivalent_tol(self): """Return list of components which in series/parallel are within tolerance of the target value, minimising the number of components in use. Args: components: float values of the resistors/capacitors to choose from. A component value can be used as many times as it occurs in this list. target: The target value. series: True for series, false for parallel. tol: Solved resistance will be in range [(1-tol)*target, (1+tol)*target, % resistor: True for resistors and inductors, False for capacitors Returns: Optimal component values, or empty list if no solution. """ tol = self.tolerance/100 # This is what sets the different parallel and series combination # depending on the component type. Reasoning = Boolean algebra. condition = (not self.resistor and not self.series) or (self.series and self.resistor) _target = self.target if condition else 1/self.target _components = self.components if condition else [1/x for x in self.components] lower = (1-tol) * self.target if condition else 1/((1+tol) * self.target) upper = (1+tol) * self.target if condition else 1/((1-tol) * self.target) m = mip.Model() # Create new mixed integer/linear model. r_in_use = [m.add_var(var_type=mip.BINARY) for _ in _components] opt_r = sum([b * r for b, r in zip(r_in_use, _components)]) m += opt_r >= lower m += opt_r <= upper m.objective = mip.minimize(mip.xsum(r_in_use)) m.verbose = False sol_status = m.optimize() if sol_status != mip.OptimizationStatus.OPTIMAL: print('No solution found') return [] r_in_use_sol = [float(v) for v in r_in_use] r_to_use = [r for r, i in zip(self.components, r_in_use_sol) if i > 0] solved_values = sum(x for x in r_to_use) if condition else 1/sum(1/x for x in r_to_use) solved_error = 100 * (solved_values - self.target) / self.target print(f'{"Resistors/Inductors" if self.resistor else "Capacitors"}; {r_to_use} in {"series" if self.series else "parallel"} ' f'will produce {"resistance/inductance" if self.resistor else "capacitance"} = {solved_values:.3f}'\ f' {"R/I" if self.resistor else "C"}. Aiming for {"R/I" if self.resistor else "C"} = {self.target:.3f}, ' f'error of {solved_error:.2f}%') return r_to_use
def __init__(self, obj): self.obj = obj self.model = mip.Model() self.getIndex = make_getIndex(obj) self.objlen = obj['size'] self.constraints = np.zeros(self.objlen) self.constraints[self.getIndex('Transform', 'T')] = 1 self.direction = ['=='] self.rhs = [1] self.instructions = [] self.state_order = [['Transform', 'Normal']] self.boost_on = None self.boost_num = 0 self.rules = {}
def optimize_player_strategy(player_cards: List[int], opponent_cards: List[int], payoff_matrix: Matrix) -> Strategy: """ Get the optimal strategy for the player, by solving a simple linear program based on payoff matrix. """ lp = mip.Model("player_strategy", solver_name=mip.CBC) lp.verbose = False # the problems are simple and we don't need to see the output x = [ lp.add_var(f"x_{card}", var_type=mip.CONTINUOUS) for card in player_cards ] v = lp.add_var("v", var_type=mip.CONTINUOUS, lb=-mip.INF) for opponent_card in opponent_cards: transposed_row = [ payoff_matrix[(player_card, opponent_card)] for player_card in player_cards ] constraint = (mip.xsum(transposed_row[i] * x_i for i, x_i in enumerate(x)) - v >= 0) lp += constraint, f"strategy_against_{opponent_card}" logging.debug(f"constraint={constraint}") lp += mip.xsum(x) == 1, "probability_distribution" lp.objective = mip.maximize(v) # all variables are continuous so we only need to solve relaxed problem lp.optimize(max_seconds=30, relax=True) if lp.status is not mip.OptimizationStatus.OPTIMAL: logging.error(f"lp.status={lp.status}") raise RuntimeError( f"Solver couldn't optimize the problem and returned status {lp.status}" ) strategy = Strategy( card_probabilities={ card: lp.var_by_name(f"x_{card}").x for card in player_cards }, expected_value=lp.var_by_name("v").x, ) logging.debug(f"strategy.expected_value={strategy.expected_value}") logging.debug("\n") return strategy
def mip_model_subsample(self, data, subsample_num, budget, dist, delta, outlier_count, greedy_indices=None): model = mip.Model("Core Set Selection") # calculate neighborhoods: data_1, data_2 = np.where(dist <= delta) feasible_start = {} # set up the variables: points = {} outliers = {} for i in range(data.shape[0]): if i >= subsample_num: points[i] = model.add_var(ub=1.0, lb=1.0, var_type=BINARY, name="points_{}".format(i)) else: points[i] = model.add_var(var_type=BINARY, name="points_{}".format(i)) for i in range(data.shape[0]): name = "outliers_{}".format(i) outliers[i] = model.add_var(var_type=BINARY, name=name) # outliers[i].start = 0 # feasible_start[name] = 0 # initialize the solution to be the greedy solution: if greedy_indices is not None: for i in greedy_indices: # points[i].start = 1.0 feasible_start[points[i].name] = 1 # set up the constraints: model.add_constr(xsum(points[i] for i in range(data.shape[0])) == budget, "budget") neighbors = {} for i in range(data.shape[0]): neighbors[i] = [] neighbors[i].append(outliers[i]) for i in range(len(data_1)): neighbors[data_1[i]].append(points[data_2[i]]) for i in range(data.shape[0]): model.add_constr(xsum(neighbors[i]) >= 1, "coverage+outliers") model.add_constr(xsum(outliers[i] for i in outliers) <= outlier_count, "budget") model.objective = xsum(outliers[i] for i in outliers) model.__data = points, outliers model.emphasis = 1 # model.max_seconds = self.max_seconds return model
def get_mip_model_silent(): """Create a mip model but filter out the Gurobi prints""" try: f = io.BytesIO() with stdout_redirector(f): m = mip.Model() m.verbose = 0 return m except: logger = get_cli_logger() logger.error( '*** ERROR: Failed to start Gurobi. Check if GUROBI_HOME and GRB_LICENSE_FILE are correctly set' ) logger.error('GUROBI_HOME is currently set to %s', os.environ.get('GUROBI_HOME', '')) logger.error('GRB_LICENSE_FILE is currently set to %s', os.environ.get('GRB_LICENSE_FILE', '')) exit(1)
def optimizepy(): import mip from mip import OptimizationStatus import sys m = mip.Model() m.read('ifrum.lp') m.max_gap = 0.05 status = m.optimize(max_seconds=300) if status == OptimizationStatus.OPTIMAL: print('optimal solution cost {} found'.format(m.objective_value)) elif status == OptimizationStatus.FEASIBLE: print('sol.cost {} found, best possible: {}'.format(m.objective_value, m.objective_bound)) elif status == OptimizationStatus.NO_SOLUTION_FOUND: print('no feasible solution found, lower bound is: {}'.format(m.objective_bound)) if status == OptimizationStatus.OPTIMAL or status == OptimizationStatus.FEASIBLE: print('solution:') for v in m.vars: if abs(v.x) > 1e-3: # only printing non-zeros print('{} : {}'.format(v.name, v.x))
def mip_from_std_form(A, b, c): # initialize n = len(c) m = len(b) ilp = mip.Model() # variables x = [ ilp.add_var(name='x({})'.format(i), var_type=mip.INTEGER) for i in range(n) ] # constraints for j in range(m): a, bi = A[j, :], b[j] cons = mip.xsum(a[i] * x[i] for i in range(n) if a[i] != 0) <= 0 cons.add_const(-bi) ilp.add_constr(cons) # objective ilp.objective = mip.minimize(mip.xsum(c[i] * x[i] for i in range(n))) return ilp
def pl_tft(n): m = mip.Model() m.verbose = 1 E = range(n) colors = range(1, 2) x = {s: m.add_var(var_type=mip.BINARY) for s in combinations(E, 4)} c = {(k, s): m.add_var(var_type=mip.BINARY) for k in colors for s in combinations(E, 4)} # c[k, s] == True iff s has color k for p in combinations(E, 2): S_i = [] for s in combinations(E, 4): if p[0] in s and p[1] in s: S_i.append(x[s]) m += mip.xsum(S_i) == 2 for s in combinations(E, 4): m += x[s] <= mip.xsum(c[(k, s)] for k in colors) <= 1 for k in colors: for s1 in combinations(E, 4): for s2 in combinations(E, 4): set_s1, set_s2 = set(s1), set(s2) if len(set_s1 & set_s2) != 0 and set_s1 != set_s2: m += c[k, s1] + c[k, s2] <= 1 # m.objective = mip.minimize(mip.xsum(c)) print(f"{m.num_cols} variables") print(f"{m.num_rows} constraints") m.optimize() for k, s in c: if c[k, s] == 1: print(k, s)
def optimal_comb(api: GBD, query, runtimes, timeout, k): result = api.query_search(query, [], runtimes) result = [[ int(float(val)) if is_number(val) and float(val) < float(timeout) else int(2 * timeout) for val in row[1:] ] for row in result] dataset = pd.DataFrame(result, columns=runtimes) dataset = dataset[(dataset != 2 * timeout).any(axis='columns')] model = mip.Model() instance_solver_vars = [[ model.add_var(f'x_{i}_{j}', var_type=mip.BINARY) for j in range(dataset.shape[1]) ] for i in range(dataset.shape[0])] solver_vars = [ model.add_var(f's_{j}', var_type=mip.BINARY) for j in range(dataset.shape[1]) ] for var_list in instance_solver_vars: # per-instance constraints model.add_constr(mip.xsum(var_list) == 1) for j in range(dataset.shape[1]): # per-solver-constraints model.add_constr( mip.xsum(instance_solver_vars[i][j] for i in range(dataset.shape[0])) <= dataset.shape[0] * solver_vars[j]) # "Implies" in Z3 model.add_constr(mip.xsum(solver_vars) <= k) model.objective = mip.minimize( mip.xsum(instance_solver_vars[i][j] * int(dataset.iloc[i, j]) for i in range(dataset.shape[0]) for j in range(dataset.shape[1]))) print(dataset.sum().min()) print(model.optimize()) print(model.objective_value) for index, item in enumerate([var.x for var in solver_vars]): if item > 0: print(runtimes[index])
def _build_model(self, structure: Atoms, solver_name: str, verbose: bool) -> mip.Model: """ Build a Python-MIP model based on the provided structure Parameters ---------- structure atomic configuration solver_name 'gurobi', alternatively 'grb', or 'cbc', searches for available solvers if not informed verbose whether to display solver messages on the screen """ # Create cluster maps self._create_cluster_maps(structure) # Initiate MIP model model = mip.Model('CE', solver_name=solver_name) model.solver.set_mip_gap(0) # avoid stopping prematurely model.solver.set_emphasis(2) # focus on finding optimal solution model.preprocess = 2 # maximum preprocessing # Set verbosity model.verbose = int(verbose) # Spin variables (remapped) for all atoms in the structure xs = { i: model.add_var(name='atom_{}'.format(i), var_type=BINARY) for subl in self._active_sublattices for i in subl.indices } ys = [ model.add_var(name='cluster_{}'.format(i), var_type=BINARY) for i in range(len(self._cluster_to_orbit_map)) ] # The objective function is added to 'model' first model.objective = mip.minimize(mip.xsum(self._get_total_energy(ys))) # Connect cluster variables to spin variables with cluster constraints # TODO: don't create cluster constraints for singlets constraint_count = 0 for i, cluster in enumerate(self._cluster_to_sites_map): orbit = self._cluster_to_orbit_map[i] parameter = self._transformed_parameters[orbit + 1] assert parameter != 0 if len(cluster) < 2 or parameter < 0: # no "downwards" pressure for atom in cluster: model.add_constr( ys[i] <= xs[atom], 'Decoration -> cluster {}'.format(constraint_count)) constraint_count = constraint_count + 1 if len(cluster) < 2 or parameter > 0: # no "upwards" pressure model.add_constr( ys[i] >= 1 - len(cluster) + mip.xsum(xs[atom] for atom in cluster), 'Decoration -> cluster {}'.format(constraint_count)) constraint_count = constraint_count + 1 for sym, subl in zip(self._count_symbols, self._active_sublattices): # Create slack variable slack = model.add_var(name='slackvar_{}'.format(sym), var_type=INTEGER, lb=0, ub=len(subl.indices)) # Add slack constraint model.add_constr(slack <= -1, name='{} slack'.format(sym)) # Set species constraint model.add_constr(mip.xsum([xs[i] for i in subl.indices]) + slack == -1, name='{} count'.format(sym)) # Update the model so that variables and constraints can be queried if model.solver_name.upper() in ['GRB', 'GUROBI']: model.solver.update() return model
def get_mip_model_silent(): f = io.BytesIO() with stdout_redirector(f): m = mip.Model() m.verbose = 0 return m
for p in conf.participants: for s in sessions.keys(): for talk in s: if talk in p.preferences: W[p, s] += 1/3 for s, times in sessions.items(): for t in times: if t<0 or t>=conf.num_hours: continue all_times.add(t) A[s, t] = 1 print(f'Finished setting up input matrices. {int(time.time()-start_time)}s') print(f' A has {len(A)} entries.') print(f' W has {len(W)} entries.') # create the model model = mip.Model() # Add decision variables S = {} V = {} for (s, t) in A.keys(): S[s, t] = model.add_var(var_type=mip.BINARY) for p in conf.participants: if (p, s) in W: V[p, s, t] = model.add_var(var_type=mip.BINARY) print(f"Finished setting up decision variables. {int(time.time()-start_time)}s") print(f" S: {len(S)}, V: {len(V)}, total {len(S)+len(V)}") # Add constraints for s in sessions.keys(): model += mip.xsum(S[s, t] for t in all_times if (s, t) in S)==1 # only assign talk to 1 time for t in all_times: model += mip.xsum(S[s, t] for s in sessions.keys() if (s, t) in S)<=max_track_vector[t] # only use at most max tracks
def init_model(self, **kwargs): greedy_start = kwargs.get("greedy_start", True) verbose = kwargs.get("verbose", False) use_cliques = kwargs.get("use_cliques", False) if greedy_start: if verbose: print("Computing greedy solution") greedy_solver = GreedyColoring(self.coloring_problem, params_objective_function=self.params_objective_function) result_store = greedy_solver.solve(strategy=NXGreedyColoringMethod.best, verbose=verbose) self.start_solution = result_store.get_best_solution_fit()[0] else: if verbose: print("Get dummy solution") solution = self.coloring_problem.get_dummy_solution() self.start_solution = solution nb_colors = self.start_solution.nb_color color_model = mip.Model("color", sense=mip.MINIMIZE, solver_name=self.solver_name) colors_var = {} range_node = range(self.number_of_nodes) range_color = range(nb_colors) for node in self.nodes_name: for color in range_color: colors_var[node, color] = color_model.add_var(var_type=BINARY, obj=0, name="x_" + str((node, color))) one_color_constraints = {} for n in range_node: one_color_constraints[n] = color_model.add_constr(xsum([colors_var[n, c] for c in range_color]) == 1) cliques = [] g = self.graph.to_networkx() if use_cliques: for c in nx.algorithms.clique.find_cliques(g): cliques += [c] cliques = sorted(cliques, key=lambda x: len(x), reverse=True) else: cliques = [[e[0], e[1]] for e in g.edges()] cliques_constraint = {} index_c = 0 opt = color_model.add_var(var_type=INTEGER, lb=0, ub=nb_colors, obj=1) if use_cliques: for c in cliques[:100]: cliques_constraint[index_c] = color_model.add_constr(xsum([(color_i + 1) * colors_var[node, color_i] for node in c for color_i in range_color]) >= sum([i + 1 for i in range(len(c))])) cliques_constraint[(index_c, 1)] = color_model.add_constr(xsum([colors_var[node, color_i] for node in c for color_i in range_color]) <= opt) index_c += 1 edges = g.edges() constraints_neighbors = {} for e in edges: for c in range_color: constraints_neighbors[(e[0], e[1], c)] = \ color_model.add_constr(colors_var[e[0], c] + colors_var[e[1], c] <= 1) for n in range_node: color_model.add_constr(xsum([(color_i + 1) * colors_var[n, color_i] for color_i in range_color]) <= opt) self.model = color_model self.variable_decision = {"colors_var": colors_var} self.constraints_dict = {"one_color_constraints": one_color_constraints, "constraints_neighbors": constraints_neighbors} self.description_variable_description = {"colors_var": {"shape": (self.number_of_nodes, nb_colors), "type": bool, "descr": "for each node and each color," " a binary indicator"}} self.description_constraint["one_color_constraints"] = {"descr": "one and only one color " "should be assignated to a node"} self.description_constraint["constraints_neighbors"] = {"descr": "no neighbors can have same color"}
def multiple_pairs(dist_matrices, voi_ind_to_S_sets, threshold=None, api='py-mip', solver='pulp', variable_num_tol=0.001, max_seconds=np.inf): """ Doc string to be written. """ voi_indices = list(voi_ind_to_S_sets.keys()) n_voi = len(voi_indices) S_sets = list(voi_ind_to_S_sets.values()) M = np.max(np.max(abs(dist_matrices), axis=0)) a, b, c = dist_matrices.shape if b == c: J = a n = b dist_matrices = dist_matrices.transpose((1, 2, 0))[voi_indices] else: n = b J = c voi_to_Q_indices = {voi: np.array([int(i) for i in np.concatenate((range(0, voi), range(voi+1, n))) if i not in voi_ind_to_S_sets[voi]]) for voi in voi_indices} if variable_num_tol is not None: up_bound = int(np.math.ceil(1 / variable_num_tol)) variable_num_tol = 1 / up_bound cat='Integer' else: up_bound=1 cat='Continuous' if threshold is not None: if b == c: raise ValueError('dist_matrices not shaped correctly') temp_dist_matrices = np.zeros((n_voi, n, J)) for i, voi in enumerate(voi_indices): temp_ranks = evaluate_best_vertices(dist_matrices[i], np.arange(J), S_sets[i]) temp_dist_matrices[i] = edit_dist_matrices(dist_matrices[i], S_sets[i], temp_ranks, threshold) if api == 'pulp': model=pulp.LpProblem(sense=pulp.LpMinimize) inds = pulp.LpVariable.dicts("indicators for elements not in S", ('voi' + str(voi) + str(q) for voi in voi_indices for q in voi_to_Q_indices[voi]), cat='Integer', upBound=1, lowBound=0 ) weights = pulp.LpVariable.dicts("weights for representations", (j for j in range(J)), cat=cat, upBound=up_bound, lowBound=0 ) model += ( pulp.lpSum( [inds[('voi' + str(voi) + str(q))] for voi in voi_indices for q in voi_to_Q_indices[voi]] ) ) model += ( pulp.lpSum( [weights[(j)] for j in range(J)] ) == up_bound ) for i, voi in enumerate(voi_indices): dist_matrix = dist_matrices[i, :, :] for s in voi_ind_to_S_sets[voi]: for q in voi_to_Q_indices[voi]: model += ( pulp.lpSum( [weights[(j)] * dist_matrix[s, j] for j in range(J)] ) <= pulp.lpSum( [weights[(j)] * dist_matrix[q, j] for j in range(J)] ) + pulp.lpSum(inds[('voi' + str(voi) + str(q))] * M * up_bound) ) try: if solver == 'pulp': model.solve() elif solver == 'coin_cmd': model.solve(solver=pulp.COIN_CMD()) alpha_hat = np.array([w.varValue for w in weights.values()]) except Exception as e: print(e) return None elif api=='py-mip': model = mip.Model(sense=mip.MINIMIZE) # Need to fix inds inds = [[model.add_var(name='inds', var_type=mip.BINARY) for q in voi_to_Q_indices[voi]] for voi in voi_indices] # if variable_num_tol is None: weights = [model.add_var(name='weights', lb=0.0, ub=up_bound, var_type=cat) for j in range(J)] model += mip.xsum(w for w in weights) == up_bound # else: # weights = [model.add_var(name='weights', lb=0, ub=up_bound, var_type='I') for j in range(J)] # model += mip.xsum(w for w in weights) == up_bound for i, voi in enumerate(voi_indices): dist_matrix = dist_matrices[i, :, :] for s in voi_ind_to_S_sets[voi]: for k, q in enumerate(voi_to_Q_indices[voi]): model += mip.xsum(weights[j] * dist_matrix[s, j] for j in range(J)) <= mip.xsum(weights[j] * dist_matrix[q, j] for j in range(J)) + inds[i][k]*M*up_bound model.objective = mip.xsum(mip.xsum(i for i in ind) for ind in inds) model.optimize(max_seconds=max_seconds) alpha_hat = np.array([w.x for w in weights]) if alpha_hat[0] is None: return None else: return alpha_hat / np.sum(alpha_hat)
def combine_representations(dist_matrix, voi_index, S_indices, return_new_dists=True, threshold=None, solver='coin_cmd', api='py-mip', variable_num_tol=0.001, max_seconds=np.inf): """ A function to find the weights of optimal linear combination of representations. Input dist_matrix - np.array (shape=(n, J)) Array containing the distances between the vertex of interest and the other n - 1 vertices. voi_index - int Index of vertex of interest. S_indices - array-like Indices of the vertices that should be at the top of the nomination list for the vertex of interest. return_new_dists - bool If true, returns both the weights and the corresponding distance matrix. threshold - maximum value of the rank of an element of S_indices. solver - solver to use. in {'coin_cmd'} api - api to use. in {'gurobi', 'py-mip', 'pulp'} variable_num_tol - float in (0, 1]. resolution of the approximation of the continuous weights. If None, continuous weights are found. max_seconds - float in (0, inf) Return weights - np.array (length=J) Array containing the coefficients for the optimal distance function. -- optional -- new_dists - np.array (length=n) Array containing the distances after applying the learned weight vector. """ # Grab the shape of the data that was passed int n, J = dist_matrix.shape # Pre-process the data so that there are no elements of S_indices that are above threshold if threshold is not None: ranks = evaluate_best_vertices(dist_matrix, vertices=np.arange(J), s_star=S_indices) dist_matrix = edit_dist_matrices(dist_matrix, S_indices, ranks, threshold) # Grab the maximum value of dist_matrix (to be used as an upper bound later) M = np.max(abs(dist_matrix)) # Grab the number of elements known to be similar to voi_index S = len(S_indices) # Define an array of integers corresponding to elements not in S_indices Q_indices = np.array([int(i) for i in np.concatenate((range(0, voi_index), range(voi_index+1, n))) if i not in S_indices]) # Grab the number of elements not known to be similar to voi_index Q = len(Q_indices) # We can either use continuous weights for the representations or use discrete approximation if variable_num_tol is not None: # Here, we are using a discrete approximation # variable_num_tol is in the interval (0, 1]. If variable_num_tol is close to 0 that means # we want our approximation to be of a high resolution. To achieve this, we define 1 / variable_num_tol # to be the maximum value that a particular weight can take on. # i.e. with variable_num_tol = 0.1, the weights can take on values 0.0, 0.1, 0.2, 0.3, .., 1. # We normalize later. up_bound = int(np.math.ceil(1 / variable_num_tol)) variable_num_tol = 1 / up_bound cat='Integer' else: # Here, we let the weights be continuous up_bound=1 cat='Continuous' # We've implemented the ILP in 3 different APIs: pulp, py-mip and gurobi. # Gurobi seems to be the industry standard but licensing is a bit prohibitive. # Each API is relatively similar. First, you define a set of variables. # Then, using these variables, you define an objective function and a set of constraints that you assign to a model object. if api == 'pulp': # Define a model. model=pulp.LpProblem(sense=pulp.LpMinimize) # Define indicator variables (as defined in section 1.2 of https://arxiv.org/pdf/2005.10700.pdf) for every vertex # That is not in S_indices. # NB: i am more than happy to walk through the paper if that would be helpful! inds = pulp.LpVariable.dicts("indicators for elements not in S", (q for q in Q_indices), cat='Integer', upBound=1, lowBound=0 ) # Define non-negative weight variables for each of the representations. weights = pulp.LpVariable.dicts("weights for representations", (j for j in range(J)), cat=cat, upBound=up_bound, lowBound=0 ) # Set the objective function. model += ( pulp.lpSum( [inds[(q)] for q in Q_indices] ) ) # Add constraint that the weights must sum to the upper bound defined by variable_num_tol. model += ( pulp.lpSum( [weights[(j)] for j in range(J)] ) == up_bound ) # Add constraint that elements of S_indices should be closer than elements not in S_indices (or, the Q_indices) for s in S_indices: for q in Q_indices: model += ( pulp.lpSum( [weights[(j)] * dist_matrix[s, j] for j in range(J)] ) <= pulp.lpSum( [weights[(j)] * dist_matrix[q, j] for j in range(J)] ) + pulp.lpSum(inds[(q)] * M * up_bound) ) # Different solvers for api == 'pulp' try: if solver == 'pulp': model.solve() elif solver == 'coin_cmd': model.solve(solver=pulp.COIN_CMD()) alpha_hat = np.array([w.varValue for w in weights.values()]) except Exception as e: print(e) return None alpha_hat = np.array([w.varValue for w in weights.values()]) elif api=='gurobi': model= gp.Model() model.setParam('OutputFlag', 0) model.setParam('TimeLimit', max_seconds) ind = model.addVars(Q, vtype=GRB.BINARY, name='ind') model.setObjective(gp.quicksum(ind), GRB.MINIMIZE) w = model.addVars(J, lb=0, ub=1, vtype=GRB.CONTINUOUS, name='w') model.addConstr(w.sum() == 1) for s in S_indices: temp_s = gp.tupledict([((i), dist_matrix[s, i]) for i in range(J)]) for i, q in enumerate(Q_indices): temp_q = gp.tupledict([((i), dist_matrix[q, i]) for i in range(J)]) model.addConstr(w.prod(temp_s) <= w.prod(temp_q) + ind[i]*M) model.optimize() alpha_hat = np.array([i.X for i in list(w.values())]) elif api=='py-mip': model = mip.Model(sense=mip.MINIMIZE) inds = [model.add_var(name='inds', var_type=mip.BINARY) for q in range(Q)] # if variable_num_tol is None: weights = [model.add_var(name='weights', lb=0.0, ub=up_bound, var_type=cat) for j in range(J)] model += mip.xsum(w for w in weights) == 1 # else: # weights = [model.add_var(name='weights', lb=0, ub=up_bound, var_type='I') for j in range(J)] # model += mip.xsum(w for w in weights) == up_bound for s in S_indices: for i, q in enumerate(Q_indices): model += mip.xsum(weights[j] * dist_matrix[s, j] for j in range(J)) <= mip.xsum(weights[j] * dist_matrix[q, j] for j in range(J)) + inds[i]*M*up_bound model.objective = mip.xsum(ind for ind in inds) model.optimize(max_seconds=max_seconds) alpha_hat = np.array([w.x for w in weights]) else: raise ValueError("api %s not implemented"%(api)) # Return the new distances if told to do so. if return_new_dists: if np.sum(alpha_hat == 0) == J: return alpha_hat, dist_matrix else: return alpha_hat, np.average(dist_matrix, axis=1, weights=alpha_hat) if alpha_hat[0] is None: return None else: # Normalize return alpha_hat / np.sum(alpha_hat)