# constraint : enter each city only once for i in V: model += xsum(x[j][i] for j in V - {i}) == 1 # (weak) subtour elimination # 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 # no subtours of size 2 for (i, j) in Arcs: model += x[i][j] + x[j][i] <= 1 # computing farthest point for each point, these will be checked first for # isolated subtours F, G = [], nx.DiGraph() for (i, j) in Arcs: G.add_edge(i, j, weight=c[i][j]) for i in V: P, D = nx.dijkstra_predecessor_and_distance(G, source=i) DS = list(D.items()) DS.sort(key=lambda x: x[1]) F.append((i, DS[-1][0])) model.cuts_generator = SubTourCutGenerator(F, x, V) model.optimize() print('status: %s route length %g' % (model.status, model.objective_value))
def test_tsp_cuts(solver: str): """tsp related tests""" N = ["a", "b", "c", "d", "e", "f", "g"] n = len(N) i0 = N[0] A = { ("a", "d"): 56, ("d", "a"): 67, ("a", "b"): 49, ("b", "a"): 50, ("d", "b"): 39, ("b", "d"): 37, ("c", "f"): 35, ("f", "c"): 35, ("g", "b"): 35, ("b", "g"): 25, ("a", "c"): 80, ("c", "a"): 99, ("e", "f"): 20, ("f", "e"): 20, ("g", "e"): 38, ("e", "g"): 49, ("g", "f"): 37, ("f", "g"): 32, ("b", "e"): 21, ("e", "b"): 30, ("a", "g"): 47, ("g", "a"): 68, ("d", "c"): 37, ("c", "d"): 52, ("d", "e"): 15, ("e", "d"): 20, } # input and output arcs per node Aout = {n: [a for a in A if a[0] == n] for n in N} Ain = {n: [a for a in A if a[1] == n] for n in N} m = Model(solver_name=solver) m.verbose = 0 x = { a: m.add_var(name="x({},{})".format(a[0], a[1]), var_type=BINARY) for a in A } m.objective = xsum(c * x[a] for a, c in A.items()) for i in N: m += xsum(x[a] for a in Aout[i]) == 1, "out({})".format(i) m += xsum(x[a] for a in Ain[i]) == 1, "in({})".format(i) # continuous variable to prevent subtours: each # city will have a different "identifier" in the planned route y = {i: m.add_var(name="y({})".format(i), lb=0.0) for i in N} # subtour elimination for (i, j) in A: if i0 not in [i, j]: m.add_constr(y[i] - (n + 1) * x[(i, j)] >= y[j] - n) m.cuts_generator = SubTourCutGenerator() # tiny model, should be enough to find the optimal m.max_seconds = 10 m.max_nodes = 100 m.max_solutions = 1000 m.optimize() assert m.status == OptimizationStatus.OPTIMAL # "mip model status" assert abs(m.objective_value - 262) <= TOL # "mip model objective"
F, G = [], nx.DiGraph() for (i, j) in Routes: G.add_edge(i, j, weight=cost[i][j]) # nx.draw(G, with_labels = True) for i in places: P, D = nx.dijkstra_predecessor_and_distance(G, source=i) # print(P, D) DS = list(D.items()) # print('\n', DS) DS.sort(key=lambda x: x[1]) # print('\n', DS, '\n') F.append((i, DS[-1][0])) m.cuts_generator = SubTourCutGenerator(F, x, places) for i in places: for j in places: # prob += vars_2[i][j] >= max(0,-demand[i],demand[j])*vars[i][j] if i != j: m += f[i][j] >= 0 for i in places: for j in places: # prob += vars_2[i][j] <= min(Q,Q-demand[i],Q + demand[j])*vars[i][j] if i != j: m += f[i][j] <= Q * x[i][j] # The problem is solved using PuLP's choice of Solver m.optimize()