Example #1
0
# constraint : leave each city only once
for i in V:
    model += xsum(x[i][j] for j in V - {i}) == 1

# constraint : enter each city only once
for i in V:
    model += xsum(x[j][i] for j in V - {i}) == 1

# subtour elimination
for (i, j) in product(V - {0}, V - {0}):
    if i != j:
        model += y[i] - (n + 1) * x[i][j] >= y[j] - n

# optimizing
model.threads = 4
model.optimize(max_nodes=75)

# checking if a solution was found
if model.num_solutions:
    out.write("route with total distance %g found: %s" %
              (model.objective_value, 0))
    nc = 0
    while True:
        nc = [i for i in V if x[nc][i].x >= 0.99][0]
        out.write(" -> %s" % nc)
        if nc == 0:
            break
    out.write("\n")

# sanity tests
Example #2
0
    def solve(self, max_runtime = 120):

        n, V = len(self.adj_mat), set(range(len(self.adj_mat)))
        H = set(range(len(self.house_names)))
        model = Model()
        model.threads = -1
        model.max_mip_gap = 0.05

        """
        Define Parameters
        d: {0, 1} whether or not an edge is selected
        y: for connectivity constraint
        s: whether or not a node is visited
        """
        d = {}
        for i in V:
            for j in V:
                #if self.adj_mat[i][j] > 0:
                if self.full_mat[i][j] > 0:
                    d[(i, j)] = model.add_var(var_type=BINARY)

        z = [model.add_var() for i in V]
        y = [model.add_var(var_type=BINARY) for i in V]

        # i is node, j is house/pedestrian
        c = []
        s = []
        for i in V:
            c.append(self.closest_nodes(10, self.node_names[i]))
            temp_row = []
            for j in H:
                temp_row.append(model.add_var(var_type=BINARY))
            s.append(temp_row)

        """
        Define optimization function
        """
        def cost_func():
            total = 0
            for i in V:
                for j in V:
                    if self.full_mat[i][j] > 0:
                        total += self.full_mat[i][j] * d[(i,j)] * 2.0 / 3.0
            for i in V:
                for j in H:
                    total += c[i][j] * s[i][j]
            return total
        model.objective = minimize(cost_func())

        """
        Add constraints
        """
        model += y[self.node_names.index(self.start)] == 1

        # Will need if skimp on distance calulation with closest_nodes
        #for i in H:
        #    model += xsum(s[j][i] * c[j][i] for j in V) >= 0.2

        for i in H:
            model += xsum(s[j][i] for j in V) == 1

        for i in V:
            for j in H:
                model += s[i][j] <= y[i]

        for i in V:
            model += xsum(d[(i,j)] for j in V -{i} if self.full_mat[i][j] > 0) == 1 * y[i]

        for i in V:
            model+= xsum(d[(j,i)] for j in V - {i} if self.full_mat[i][j] > 0) == 1 * y[i]

        for (i, j) in product(V - {0}, V - {0}):
            if i != j and self.full_mat[i][j] > 0:
                model += z[i] - (n+1)*d[(i,j)] >= z[j] - n

        status = model.optimize(max_seconds = max_runtime)

        n = 0
        for i in V:
            for j in V:
                if i != j and d[(i, j)].x > 0.99:
                    n += 1
        print('oofus doofus', n)

        if model.num_solutions:
            nc = self.node_names.index(self.start)
            solution = []
            # visited is to make sure we don't terminate the path too quickly
            visited = {}
            for _ in range(500):
                solution.append(self.node_names[nc])
                visited[nc] = len(visited)
                all_visited = True
                
                #nc_ls = []
                print('abcdefgh')
                for i in V:
                    if (nc, i) in d and d[(nc, i)].x > 0.99:
                        nc = i
                        if nc not in visited:
                            print('BROKE')
                            all_visited = False
                            break
                        #nc_ls.append(i)
                        #break

                if all_visited:
                    first_visit = len(visited)
                    for k in visited:
                        if visited[k] < first_visit:
                            first_visit = visited[k]
                            nc = k

                if nc == self.node_names.index(self.start):
                    solution.append(self.start)
                    break
            solution = self.make_valid_path(solution)
            print(solution)
            return solution, status, model.gap
        else:
            return None, status, model.gap
Example #3
0
def solve_integer_programing(
    reactant_species: List[str],
    product_species: List[str],
    reactant_bonds: List[Bond],
    product_bonds: List[Bond],
    msg: bool = True,
    threads: Optional[int] = None,
) -> Tuple[int, List[Union[int, None]], List[Union[int, None]]]:
    """
    Solve an integer programming problem to get atom mapping between reactants and
    products.

    This is a utility function for `get_reaction_atom_mapping()`.

    Args:
        reactant_species: species string of reactant atoms
        product_species: species string of product atoms
        reactant_bonds: bonds in reactant
        product_bonds: bonds in product
        msg: whether to show the solver running message to stdout.
        threads: number of threads for the solver. `None` to use default.

    Returns:
        objective: minimized objective value. This corresponds to the number of changed
            bonds (both broken and formed) in the reaction.
        r2p_mapping: mapping of reactant atom to product atom, e.g. r2p_mapping[0]
            giving 3 means that reactant atom 0 maps to product atom 3. A value of
            `None` means a mapping cannot be found for the reactant atom.
        p2r_mapping: mapping of product atom to reactant atom, e.g. p2r_mapping[3]
            giving 0 means that product atom 3 maps to reactant atom 0. A value of
            `None` means a mapping cannot be found for the product atom.

    Reference:
        `Stereochemically Consistent Reaction Mapping and Identification of Multiple
        Reaction Mechanisms through Integer Linear Optimization`,
        J. Chem. Inf. Model. 2012, 52, 84–92, https://doi.org/10.1021/ci200351b
    """

    atoms = list(range(len(reactant_species)))

    # init model and variables
    model = Model(name="Reaction_Atom_Mapping",
                  sense=MINIMIZE,
                  solver_name=CBC)
    model.emphasis = 1

    if threads is not None:
        model.threads = threads

    if msg:
        model.verbose = 1
    else:
        model.verbose = 0

    y_vars = {(i, k): model.add_var(var_type=BINARY, name=f"y_{i}_{k}")
              for i in atoms for k in atoms}

    alpha_vars = {(i, j, k, l): model.add_var(var_type=BINARY,
                                              name=f"alpha_{i}_{j}_{k}_{l}")
                  for (i, j) in reactant_bonds for (k, l) in product_bonds}

    # add constraints

    # constraint 2: each atom in the reactants maps to exactly one atom in the products
    # constraint 3: each atom in the products maps to exactly one atom in the reactants
    for i in atoms:
        model += xsum([y_vars[(i, k)] for k in atoms]) == 1
    for k in atoms:
        model += xsum([y_vars[(i, k)] for i in atoms]) == 1

    # constraint 4: allows only atoms of the same type to map to one another
    for i in atoms:
        for k in atoms:
            if reactant_species[i] != product_species[k]:
                model += y_vars[(i, k)] == 0

    # constraints 5 and 6: define each alpha_ijkl variable, permitting it to take the
    # value of one only if the reactant bond (i,j) maps to the product bond (k,l)
    for (i, j) in reactant_bonds:
        for (k, l) in product_bonds:
            model += alpha_vars[(i, j, k,
                                 l)] <= y_vars[(i, k)] + y_vars[(i, l)]
            model += alpha_vars[(i, j, k,
                                 l)] <= y_vars[(j, k)] + y_vars[(j, l)]

    # create objective
    obj1 = xsum(1 - xsum(alpha_vars[(i, j, k, l)] for (k, l) in product_bonds)
                for (i, j) in reactant_bonds)
    obj2 = xsum(1 - xsum(alpha_vars[(i, j, k, l)] for (i, j) in reactant_bonds)
                for (k, l) in product_bonds)
    obj = obj1 + obj2

    # solve the problem
    try:
        model.objective = obj
        model.optimize()
    except Exception:
        raise ReactionMappingError("Failed solving integer programming.")

    if not model.num_solutions:
        raise ReactionMappingError("Failed solving integer programming.")

    # get atom mapping between reactant and product
    r2p_mapping = [None for _ in atoms]  # type: List[Union[int, None]]
    p2r_mapping = [None for _ in atoms]  # type: List[Union[int, None]]
    for (i, k), v in y_vars.items():
        if v.x == 1:
            r2p_mapping[i] = k
            p2r_mapping[k] = i

    objective = model.objective_value  # type: int

    return objective, r2p_mapping, p2r_mapping
Example #4
0
def solve(G: nx.Graph, max_c, max_k, timeout, existing_solution, target_distance):
    """
    Args:
        G: networkx.Graph
    Returns:
        c: list of cities to remove
        k: list of edges to remove
    """
    model = Model()
    model.threads = int(os.getenv("THREAD_COUNT", "24"))
    model.max_mip_gap = 1e-12

    skipped_nodes = [model.add_var(var_type=BINARY) for node in G]

    flow_over_edge = [[model.add_var(var_type=CONTINUOUS) for j in G] for i in G]

    # no flow over nonexistent edges
    for i in G:
        for j in G:
            if not G.has_edge(i, j):
                model += flow_over_edge[i][j] == 0

    model += xsum(flow_over_edge[0][other_node] for other_node in G) == (len(G) - 1) - xsum(skipped_nodes)
    for other_node in G:
        model += flow_over_edge[other_node][0] == 0

    distance_to_node = [model.add_var(var_type=CONTINUOUS) for node in G]
    model += distance_to_node[0] == 0

    # in any connected subset of G, there will always be a shortest path w/ length <= sum of all edges
    # so a fake_infinity will never be better than any other option
    fake_infinity = sum([weight for _, _, weight in G.edges().data("weight")])
    skipped_edges = []
    skipped_edge_map = {}
    model += skipped_nodes[0] == 0
    model += skipped_nodes[len(G) - 1] == 0

    for node_a, node_b, weight in G.edges().data("weight"):
        edge_skip_var = model.add_var(var_type=BINARY)
        skipped_edges.append((edge_skip_var, (node_a, node_b)))
        model += edge_skip_var >= skipped_nodes[node_a]
        model += edge_skip_var >= skipped_nodes[node_b]

        model += distance_to_node[node_a] <= distance_to_node[node_b] + weight + edge_skip_var * fake_infinity
        model += distance_to_node[node_b] <= distance_to_node[node_a] + weight + edge_skip_var * fake_infinity

        # no flow if edge or node removed
        model += flow_over_edge[node_a][node_b] <= (len(G) - 1) - edge_skip_var * (len(G) - 1)
        model += flow_over_edge[node_b][node_a] <= (len(G) - 1) - edge_skip_var * (len(G) - 1)

    # results in binary variable leakage
    for node in G:
        # immediately force the distance to infinity if we skip the node
        model += distance_to_node[node] >= skipped_nodes[node] * fake_infinity
        model += distance_to_node[node] <= fake_infinity

    for node in G:
        if node != 0:
            flow_into_node = xsum(flow_over_edge[other_node][node] for other_node in G)
            flow_out_of_node = xsum(flow_over_edge[node][other_node] for other_node in G)
            model += flow_into_node - flow_out_of_node == 1 - skipped_nodes[node]

    model += xsum([var for var, _ in skipped_edges]) - xsum([skipped_nodes[i] * len(G[i]) for i in G]) <= max_k
    model += xsum(skipped_nodes) <= max_c

    if target_distance:
        model += distance_to_node[len(G) - 1] >= target_distance
        model += distance_to_node[len(G) - 1] <= target_distance

    model.objective = maximize(distance_to_node[len(G) - 1])
    # these cuts are used more often but aggressively using them doesn't seem to help
    # model.solver.set_int_param("FlowCoverCuts", 2)
    # model.solver.set_int_param("MIRCuts", 2)

    if existing_solution:
        solution_variables = []
        for node in G:
            solution_variables.append((skipped_nodes[node], 1.0 if node in existing_solution[0] else 0.0))
        for edge_var, edge in skipped_edges:
            is_skipped = (edge in existing_solution[1]) or ((edge[1], edge[0]) in existing_solution[1]) or \
                         (edge[0] in existing_solution[0]) or (edge[1] in existing_solution[0])
            solution_variables.append((edge_var, 1.0 if is_skipped else 0.0))
        model.start = solution_variables

    status = model.optimize(max_seconds=timeout)
    if model.num_solutions > 0:
        c = []
        for node in G:
            if skipped_nodes[node].x > 0.99:
                c.append(node)
        k = []
        for skip_var, edge in skipped_edges:
            if skip_var.x > 0.99 and not ((edge[0] in c) or (edge[1] in c)):
                k.append(edge)
        return c, k, status == OptimizationStatus.OPTIMAL, model.gap
    else:
        return None
def tsp_mip_solver(input_data):
    # parse the input
    lines = input_data.split('\n')

    nodeCount = int(lines[0])

    points = []
    for i in range(1, nodeCount + 1):
        line = lines[i]
        parts = line.split()
        points.append(Point(float(parts[0]), float(parts[1])))
    print('Points parsed!')

    # calculate distance matrix
    d_m = [[length(q, p) for q in points] for p in points]
    print('Distance matrix ready!')

    # declare MIP model
    m = Model(solver_name='GRB')
    print('-Model instatiated!', datetime.datetime.now())

    # states search emphasis
    #     - '0' (default) balanced approach
    #     - '1' (feasibility) aggressively searches for feasible solutions
    #     - '2' (optimality) explores search space to tighten dual gap
    m.emphasis = 0

    # whenever the distance of the lower and upper bounds is less or
    # equal max_gap*100%, the search can be finished
    m.max_gap = 0.05

    # specifies number of used threads
    # 0 uses solver default configuration,
    # -1 uses the number of available processing cores
    # ≥1 uses the specified number of threads.
    # An increased number of threads may improve the solution time but also increases
    # the memory consumption. Each thread needs to store a different model instance!
    m.threads = 0

    # controls the generation of cutting planes
    # cutting planes usually improve the LP relaxation bound but also make the solution time of the LP relaxation larger
    # -1 means automatic
    #  0 disables completely
    #  1 (default) generates cutting planes in a moderate way
    #  2 generates cutting planes aggressively
    #  3 generates even more cutting planes
    m.cuts = -1

    m.preprocess = 1
    m.pump_passes = 20
    m.sol_pool_size = 1

    nodes = set(range(nodeCount))

    # instantiate "entering and leaving" variables
    x = [[m.add_var(name="x{}_{}".format(p, q), var_type='B') for q in nodes]
         for p in nodes]
    # instantiate subtour elimination variables
    y = [m.add_var(name="y{}".format(i)) for i in nodes]
    print('-Variables instantiated', datetime.datetime.now())

    # declare objective function
    m.objective = minimize(
        xsum(d_m[i][j] * x[i][j] for i in nodes for j in nodes))

    print('-Objective declared!', datetime.datetime.now())

    # declare constraints
    # leave each city only once
    for i in tqdm(nodes):
        m.add_constr(xsum(x[i][j] for j in nodes - {i}) == 1)

    # enter each city only once
    for i in tqdm(nodes):
        m.add_constr(xsum(x[j][i] for j in nodes - {i}) == 1)

    # subtour elimination constraints
    for (i, j) in tqdm(product(nodes - {0}, nodes - {0})):
        if i != j:
            m.add_constr(y[i] - (nodeCount + 1) * x[i][j] >= y[j] - nodeCount)

    print('-Constraints declared!', datetime.datetime.now())

    #Maximum time in seconds that the search can go on if a feasible solution
    #is available and it is not being improved
    mssi = 1000  #default = inf
    # specifies maximum number of nodes to be explored in the search tree (default = inf)
    mn = 1000000  #default = 1073741824
    # optimize model m within a processing time limit of 'ms' seconds
    ms = 3000  #default = inf

    # executes the optimization
    print('-Optimizer start.', datetime.datetime.now())
    #status = m.optimize(max_seconds = ms,max_seconds_same_incumbent = mssi,max_nodes = mn)
    status = m.optimize(max_seconds=ms, max_seconds_same_incumbent=mssi)

    print('Opt. Status:', status)
    print('MIP Sol. Obj.:', m.objective_value)
    print('Dual Bound:', m.objective_bound)
    print('Dual gap:', m.gap)

    sol = [0]
    c_node = 0
    for j in range(nodeCount - 1):
        for i in range(nodeCount):
            if round(m.var_by_name("x{}_{}".format(c_node, i)).x) != 0:
                sol.append(i)
                c_node = i
                break

    obj = m.objective_value

    # prepare the solution in the specified output format
    if status == OptimizationStatus.OPTIMAL:
        output_data = '%.2f' % obj + ' ' + str(1) + '\n'
        output_data += ' '.join(map(str, sol))
    elif status == OptimizationStatus.FEASIBLE:
        output_data = '%.2f' % obj + ' ' + str(0) + '\n'
        output_data += ' '.join(map(str, sol))

    return output_data
Example #6
0
def solve(list_of_locations, list_of_homes, starting_car_location, adjacency_matrix, params=[]):
    """
    Write your algorithm here.
    Input:
        list_of_locations: A list of locations such that node i of the graph corresponds to name at index i of the list
        list_of_homes: A list of homes
        starting_car_location: The name of the starting location for the car
        adjacency_matrix: The adjacency matrix from the input file
    Output:
        A list of locations representing the car path
        A dictionary mapping drop-off location to a list of homes of TAs that got off at that particular location
        NOTE: both outputs should be in terms of indices not the names of the locations themselves
    """

    location_name_to_index = {}
    for i in range(0, len(list_of_locations)):
        location_name_to_index[list_of_locations[i]] = i

    tas = range(0, len(list_of_homes))
    nTas = len(tas)
    home_indices = list(map(lambda home: location_name_to_index[home], list_of_homes))

    starting_car_index = location_name_to_index[starting_car_location]

    (G, message) = adjacency_matrix_to_graph(adjacency_matrix)
    shortest_paths = nx.floyd_warshall(G)
    #shortest_paths_uw = nx.floyd_warshall(G, weight=None)
    L = range(0, len(list_of_locations))
    nL = len(L)

    model = Model()
    model.threads = 8

    edge_taken = [[model.add_var(var_type=BINARY) for j in L] for i in L]
    drop_ta_at_stop = [[model.add_var(var_type=BINARY) for stop in L] for ta in tas]

    driving_cost = (2 / 3) * xsum(
        G.edges[i, j]["weight"] * edge_taken[i][j]
        for i in L for j in L if G.has_edge(i, j)
    )

    walking_cost = xsum(
        shortest_paths[home_indices[ta]][stop] * drop_ta_at_stop[ta][stop]
        for stop in L for ta in tas
    )

    model.objective = minimize(driving_cost + walking_cost)

    # only take edges that exist
    for i in L:
        for j in L:
            if not G.has_edge(i, j):
                model += edge_taken[i][j] == 0

    # enter city same number of times as we exist the city
    for i in L:
        model += xsum(edge_taken[incoming][i] for incoming in L) == xsum(edge_taken[i][to] for to in L)

    # every TA is dropped off at exactly one stop
    for ta in tas:
        model += xsum(drop_ta_at_stop[ta][stop] for stop in L) == 1

    if False: # MCF formulation
        ta_over_edge = [[[model.add_var(var_type=BINARY) for ta in tas] for j in L] for i in L]
        
        # each TA gets dropped off at their stop
        for node in (set(L) - {starting_car_index}):
            for ta in tas:
                ta_entering_node = xsum(
                    ta_over_edge[prev][node][ta]
                    for prev in L
                )

                ta_leaving_node = xsum(
                    ta_over_edge[node][nxt][ta]
                    for nxt in L
                )

                ta_dropped_at_stop = drop_ta_at_stop[ta][node]

                model += ta_entering_node == ta_leaving_node + ta_dropped_at_stop

        # each TA must be dropped off somewhere along the route
        for ta in tas:
            leaving_start = xsum(ta_over_edge[starting_car_index][nxt][ta] for nxt in L)
            returning_start = xsum(ta_over_edge[prev][starting_car_index][ta] for prev in L)
            model += leaving_start == 1 - drop_ta_at_stop[ta][starting_car_index] # drop TA off right before we leave
            model += returning_start == 0

        # if a TA goes over an edge, we must take it as well
        for i in L:
            for j in L:
                for ta in tas:
                    model += edge_taken[i][j] >= ta_over_edge[i][j][ta]
    else: # SCF formulation
        flow_over_edge = [[model.add_var(var_type=INTEGER) for j in L] for i in L]

        # flow decreases only when TAs are dropped off
        for node in (set(L) - {starting_car_index}):
            tas_entering_node = xsum(
                flow_over_edge[prev][node]
                for prev in L
            )

            tas_leaving_node = xsum(
                flow_over_edge[node][nxt]
                for nxt in L
            )

            tas_dropped_at_stop = xsum(drop_ta_at_stop[ta][node] for ta in tas)

            model += tas_entering_node == tas_leaving_node + tas_dropped_at_stop

        # each TA must be dropped off somewhere along the route
        leaving_start = xsum(flow_over_edge[starting_car_index][nxt] for nxt in L)
        returning_start = xsum(flow_over_edge[prev][starting_car_index] for prev in L)
        model += leaving_start == nTas - xsum(drop_ta_at_stop[ta][starting_car_index] for ta in tas)
        model += returning_start == 0

        # if flow goes over an edge, we must take it as well
        for i in L:
            for j in L:
                shortest_paths = nx.all_simple_paths(G,starting_car_index,i)
                r_vals = []
                for path in shortest_paths:
                    r_val = 0
                    for node in path:
                        if node in home_indices:
                            r_val+=1
                    r_vals.append(r_val)
                model += edge_taken[i][j] * (nTas - min(r_vals)) >= flow_over_edge[i][j]

    print(model.constrs)
    status = model.optimize(max_seconds=60*15)
    if model.num_solutions > 0:
        edge_graph = nx.DiGraph()
        print("Edges taken:")
        count = 0
        for i in range(0, nL):
            for j in range(0, nL):
                if (edge_taken[i][j].x >= 0.99):
                    edge_graph.add_edge(i, j)
                    print(i, j, G.edges[i, j]["weight"])
                    count += 1
        
        if count == 0:
            print("Path is empty")
            path = [starting_car_index]
        else:
            print("Path:")
            path = [u for u, v in nx.eulerian_circuit(edge_graph, source=starting_car_index)] + [starting_car_index]
            for node in path:
                print(node)

        dropoffs = {}
        for ta in tas:
            drop_off_stop = [i for i in L if drop_ta_at_stop[ta][i].x >= 0.99][0]
            if not drop_off_stop in dropoffs:
                dropoffs[drop_off_stop] = []
            dropoffs[drop_off_stop].append(home_indices[ta])
        return (path, dropoffs, status == OptimizationStatus.OPTIMAL)

    return None
Example #7
0
def fl_mip_solver(input_data):
    # Modify this code to run your optimization algorithm

    # parse the input
    lines = input_data.split('\n')

    parts = lines[0].split()
    facility_count = int(parts[0])
    customer_count = int(parts[1])
    
    facilities = []
    for i in range(1, facility_count+1):
        parts = lines[i].split()
        facilities.append(Facility(i-1, float(parts[0]), int(parts[1]), Point(float(parts[2]), float(parts[3])) ))

    customers = []
    for i in range(facility_count+1, facility_count+1+customer_count):
        parts = lines[i].split()
        customers.append(Customer(i-1-facility_count, int(parts[0]), Point(float(parts[1]), float(parts[2]))))
    
    print('F count:',facility_count)
    print('C count:',customer_count)
    
    # instantiates setup cost vector
    set_cost = [f.setup_cost for f in facilities]
    
    # instantiates proportional capacity matrix such that Cap[i,j] represents the demand of 
    # customer 'j' as a fraction of the total capacity of facility 'i'
    Cap = [[c.demand/f.capacity  for c in customers] for f in facilities]
    
    # instantiates distance matrix in such a way that "sum_{j \in M} D[i,j]*at[i,j]" is the total
    # distance cost for facility 'i'
    D = [[length(c.location, f.location)  for c in customers] for f in facilities]
    
    # declares MIP model
    #m = Model(solver_name=CBC)
    m = Model(solver_name='GRB')
    print('-Model instatiated!',datetime.datetime.now())
    
    # states search emphasis
    #     - '0' (default) balanced approach
    #     - '1' (feasibility) aggressively searches for feasible solutions
    #     - '2' (optimality) explores search space to tighten dual gap
    m.emphasis = 2
    
    # whenever the distance of the lower and upper bounds is less or 
    # equal max_gap*100%, the search can be finished
    m.max_gap = 0.05
    
    # specifies number of used threads
    # 0 uses solver default configuration, 
    # -1 uses the number of available processing cores 
    # ≥1 uses the specified number of threads. 
    # An increased number of threads may improve the solution time but also increases 
    # the memory consumption. Each thread needs to store a different model instance!
    m.threads = -1
    
    
    # controls the generation of cutting planes
    # cutting planes usually improve the LP relaxation bound but also make the solution time of the LP relaxation larger
    # -1 means automatic
    #  0 disables completely
    #  1 (default) generates cutting planes in a moderate way
    #  2 generates cutting planes aggressively
    #  3 generates even more cutting planes
    m.cuts=-1

    m.preprocess=1
    m.pump_passes=10
    m.sol_pool_size=1

    # instantiates open facilities variables
    op = [m.add_var(name="op{}".format(i),var_type='B') for i in range(facility_count)]
    
    # instantiates matrix of atribution variables 
    # (line 'i' is an atribution vector for facility 'i')
    # e.g.: At[3][4] returns whether customer 4 is assigned to facility 3
    At = [[m.add_var(name="At{},{}".format(i,j) ,var_type='B') for j in range(customer_count)] 
          for i in range(facility_count)]
    
    print('-Variables declared!',datetime.datetime.now())
    
    # instantiates objective function
    # m.objective = minimize("setup costs" + "distance costs")
    # Form example:
    # m.objective = minimize(
    # xsum(dist[i, j] * x[i, j] for (i, j) in product(F, C)) + xsum(y[i] for i in F) )
    
    m.objective = minimize(
        xsum(set_cost[i]*op[i] for i in range(facility_count)) +
        xsum(sum(D[i][j]*At[i][j] for j in range(customer_count)) for i in range(facility_count)) 
            )
    
    print('-Objective declared!',datetime.datetime.now())
    
    # instatiates capacity constraints
    #     -can be expressed as "sum_{j \in M} Cap[i,j]*At[i,j] <= op[i]" for all facilities 'i'
    #     -if a facility is closed (op[i]=0), its effective capacity is 0
    for i in range(facility_count):
        m.add_lazy_constr( xsum(Cap[i][j]*At[i][j] for j in range(customer_count)) <= op[i] )
    
    # instantiates assignment constraints (maximum of 1 facility per customer)
    #     -can be expressed as: "sum of elements over of each column of 'At' == 1"     
    for i in range(customer_count):
        m += xsum(At[j][i] for j in range(facility_count)) == 1 
    
    print('-Contraints processed!',datetime.datetime.now())
    
    #Maximum time in seconds that the search can go on if a feasible solution 
    #is available and it is not being improved
    mssi = 1000 #default = inf
    # specifies maximum number of nodes to be explored in the search tree (default = inf)
    mn = 40000 #default = 1073741824
    # optimize model m within a processing time limit of 'ms' seconds
    ms = 3000 #default = inf
    
    print('-Optimizer start.',datetime.datetime.now())
    # executes the optimization
    #status = m.optimize(max_seconds = ms,max_seconds_same_incumbent = mssi,max_nodes = mn)
    status = m.optimize(max_seconds = ms , max_seconds_same_incumbent = mssi)
    final_obj = m.objective_value

    print('Opt. Status:',status)
    print('MIP Sol. Obj.:',final_obj)
    print('Dual Bound:',m.objective_bound)
    print('Dual gap:',m.gap)
    
    
    used=[i for i in range(facility_count) if m.vars[i].x==1]

    solution=[None] * customer_count
    for i in range(facility_count):
        for j in range(customer_count):
            if round(m.vars[i*customer_count + j + facility_count].x) == 1:
                solution[j]=i
    # parse output varibles and convert to conventional solution output format
    
    # calculate the cost of the solution
    obj = sum([facilities[f].setup_cost for f in used])
    
    for customer in customers:
        obj += length(customer.location, facilities[solution[customer.index]].location)

    if status == OptimizationStatus.OPTIMAL:
    	# prepare the solution in the specified output format
    	output_data = '%.2f' % obj + ' ' + str(1) + '\n'
    	output_data += ' '.join(map(str, solution))
    elif status == OptimizationStatus.FEASIBLE:
    	output_data = '%.2f' % obj + ' ' + str(0) + '\n'
    	output_data += ' '.join(map(str, solution))
    
    return output_data
Example #8
0
def mip_solver(input_data):
    # Modify this code to run your optimization algorithm

    # parse the input
    lines = input_data.split('\n')

    firstLine = lines[0].split()
    item_count = int(firstLine[0])
    capacity = int(firstLine[1])

    items = []

    for i in range(1, item_count+1):
        line = lines[i]
        parts = line.split()
        items.append(Item(i-1, int(parts[0]), int(parts[1])))

    # a trivial algorithm for filling the knapsack
    # it takes items in-order until the knapsack is full
    value = 0
    weight = 0
    taken = [0]*len(items)
    
    # declare value vector
    val_vec = [item.value for item in items]
    
    # declare weight vector scaled by capacity
    wgt_vec = [item.weight/capacity for item in items]
    
    # start MIP solver
    m = Model(solver_name='GRB')
    print('-Model instatiated!',datetime.datetime.now())
    
    # states search emphasis
    #     - '0' (default) balanced approach
    #     - '1' (feasibility) aggressively searches for feasible solutions
    #     - '2' (optimality) explores search space to tighten dual gap
    m.emphasis = 2
    
    # whenever the distance of the lower and upper bounds is less or 
    # equal max_gap*100%, the search can be finished
    m.max_gap = 0.05
    
    # specifies number of used threads
    # 0 uses solver default configuration, 
    # -1 uses the number of available processing cores 
    # ≥1 uses the specified number of threads. 
    # An increased number of threads may improve the solution time but also increases 
    # the memory consumption. Each thread needs to store a different model instance!
    m.threads = -1
    
    
    # controls the generation of cutting planes
    # cutting planes usually improve the LP relaxation bound but also make the solution time of the LP relaxation larger
    # -1 means automatic
    #  0 disables completely
    #  1 (default) generates cutting planes in a moderate way
    #  2 generates cutting planes aggressively
    #  3 generates even more cutting planes
    m.cuts=-1

    m.preprocess=1
    m.pump_passes=10
    m.sol_pool_size=1
    
    # instantiates taken items variable vector
    taken = [m.add_var(name="it{}".format(i),var_type='B') for i in range(item_count)]
    print('-Variables declared!',datetime.datetime.now())
    
    # instantiates objective function
    m.objective = maximize( xsum( val_vec[i]*taken[i] for i in range(item_count) ) )
    print('-Objective declared!',datetime.datetime.now())
    
    m.add_constr( xsum( wgt_vec[i]*taken[i] for i in range(item_count)) <=1 )
    print('-Contraints processed!',datetime.datetime.now())
    
    #Maximum time in seconds that the search can go on if a feasible solution 
    #is available and it is not being improved
    mssi = 1000 #default = inf
    # specifies maximum number of nodes to be explored in the search tree (default = inf)
    mn = 40000 #default = 1073741824
    # optimize model m within a processing time limit of 'ms' seconds
    ms = 3000 #default = inf
    
    print('-Optimizer start.',datetime.datetime.now())
    # executes the optimization
    #status = m.optimize(max_seconds = ms,max_seconds_same_incumbent = mssi,max_nodes = mn)
    status = m.optimize(max_seconds = ms , max_seconds_same_incumbent = mssi)
    final_obj = m.objective_value
    
    print('Opt. Status:',status)
    print('MIP Sol. Obj.:',final_obj)
    print('Dual Bound:',m.objective_bound)
    print('Dual gap:',m.gap)
    
    sol = [round(it.x) for it in taken]
    value = int(final_obj)
    
    taken = sol
    # prepare the solution in the specified output format
    if status == OptimizationStatus.OPTIMAL:
        output_data = str(value) + ' ' + str(1) + '\n'
        output_data += ' '.join(map(str, taken))
    elif status == OptimizationStatus.FEASIBLE:
        output_data = str(value) + ' ' + str(0) + '\n'
        output_data += ' '.join(map(str, taken))


    if item_count == 10000:
        output_data=greedy(input_data)

    return output_data