Beispiel #1
0
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
Beispiel #2
0
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
Beispiel #3
0
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]
Beispiel #4
0
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
Beispiel #5
0
    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
Beispiel #6
0
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
Beispiel #7
0
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]
Beispiel #8
0
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
Beispiel #9
0
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)
Beispiel #10
0
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")
Beispiel #12
0
    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
Beispiel #13
0
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
Beispiel #14
0
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
Beispiel #15
0
    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
Beispiel #17
0
 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 = {}
Beispiel #18
0
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
Beispiel #19
0
    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
Beispiel #20
0
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)
Beispiel #21
0
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))
Beispiel #22
0
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
Beispiel #23
0
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)
Beispiel #24
0
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])
Beispiel #25
0
    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
Beispiel #26
0
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)