예제 #1
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
예제 #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
예제 #3
0
print("Optimization process started.")

# get time limit in seconds for optimization
timeInSeconds = optimization_parameters.loc["timeInSeconds", "Value"]
# get mip gap
mipGap = optimization_parameters.loc["mipGap", "Value"]

# if time limit was given use this time limit else default value: +inf
if not pd.isna(timeInSeconds):
    model.max_seconds = timeInSeconds
    print("Maximal solution time: " + str(timeInSeconds) + " seconds")

# if mip gap was given use this gap else default value: 1e-4
if not pd.isna(mipGap):
    model.max_mip_gap = mipGap
    print("MIP gap set to " + str(mipGap * 100) + "%")

# start optimizing
status = model.optimize()

solved = False

# optimal solution found
if status == OptimizationStatus.OPTIMAL:
    if model.gap < 1e-4:
        print("Optimal solution found.")
    else:
        print("Found a solution with gap <= set mip gap.")

    gap = round(model.gap * 100, 3)