Beispiel #1
1
def solve_tsp(V,c):
    """solve_tsp -- solve the traveling salesman problem
       - start with assignment model
       - check flow from a source to every other node;
          - if no flow, a sub-cycle has been found --> add cut
          - otherwise, the solution is optimal
    Parameters:
        - V: set/list of nodes in the graph
        - c[i,j]: cost for traversing edge (i,j)
    Returns the optimum objective value and the list of edges used.
    """

    def addcut(X):
        for sink in V[1:]:
            mflow = maxflow(V,X,V[0],sink)
            mflow.optimize()
            f,cons = mflow.data
            if mflow.ObjVal < 2-EPS:  # no flow to sink, can add cut
                break
        else:
            return False

        #add a cut/constraint
        CutA = set([V[0]])
        for i in cons:
            if cons[i].Pi <= -1+EPS:
                CutA.add(i)
        CutB = set(V) - CutA
        main.addCons(
            quicksum(x[i,j] for i in CutA for j in CutB if j>i) + \
            quicksum(x[j,i] for i in CutA for j in CutB if j<i) >= 2)
        print("mflow:",mflow.getObjVal(),"cut:",CutA,"+",CutB,">= 2")
        print("mflow:",mflow.getObjVal(),"cut:",[(i,j) for i in CutA for j in CutB if j>i],"+",[(j,i) for i in CutA for j in CutB if j<i],">= 2")
        return True

    def isMIP(x):
        for var in x:
            if var.vtype == "CONTINUOUS":
                return False
        return True

    # main part of the solution process:
    main = Model("tsp")
    x = {}
    for i in V:
        for j in V:
            if j > i:
                x[i,j] = main.addVar(ub=1, vtype="C", name="x(%s,%s)"%(i,j))

    for i in V:
        main.addCons(quicksum(x[j,i] for j in V if j < i) + \
                       quicksum(x[i,j] for j in V if j > i) == 2, "Degree(%s)"%i)

    main.setObjective(quicksum(c[i,j]*x[i,j] for i in V for j in V if j > i), "minimize")

    while True:
        main.optimize()
        z = main.getObjVal()
        X = {}
        for (i,j) in x:
            if main.getVal(x[i,j]) > EPS:
                X[i,j] = main.getVal(x[i,j])

        if addcut(X) == False:  # i.e., components are connected
            if isMIP():      # integer variables, components connected: solution found
                break
            for (i,j) in x:     # all components connected, switch to integer model
                main.chgVarType(x[i,j], "BINARY")

    # process solution
    edges = []
    for (i,j) in x:
        if main.getVal(x[i,j]) > EPS:
            edges.append((i,j))
    return main.getObjVal(),edges
Beispiel #2
1
def solve_tsp(V,c):
    """solve_tsp -- solve the traveling salesman problem
       - start with assignment model
       - add cuts until there are no sub-cycles
    Parameters:
        - V: set/list of nodes in the graph
        - c[i,j]: cost for traversing edge (i,j)
    Returns the optimum objective value and the list of edges used.
    """

    def addcut(cut_edges):
        G = networkx.Graph()
        G.add_edges_from(cut_edges)
        Components = list(networkx.connected_components(G))
        if len(Components) == 1:
            return False
        model.freeTransform()
        for S in Components:
            model.addCons(quicksum(x[i,j] for i in S for j in S if j>i) <= len(S)-1)
            print("cut: len(%s) <= %s" % (S,len(S)-1))
        return True


    def addcut2(cut_edges):
        G = networkx.Graph()
        G.add_edges_from(cut_edges)
        Components = list(networkx.connected_components(G))

        if len(Components) == 1:
            return False
        model.freeTransform()
        for S in Components:
            T = set(V) - set(S)
            print("S:",S)
            print("T:",T)
            model.addCons(quicksum(x[i,j] for i in S for j in T if j>i) +
                          quicksum(x[i,j] for i in T for j in S if j>i) >= 2)
            print("cut: %s >= 2" % "+".join([("x[%s,%s]" % (i,j)) for i in S for j in T if j>i]))
        return True

    # main part of the solution process:
    model = Model("tsp")

    model.hideOutput() # silent/verbose mode
    x = {}
    for i in V:
        for j in V:
            if j > i:
                x[i,j] = model.addVar(ub=1, name="x(%s,%s)"%(i,j))

    for i in V:
        model.addCons(quicksum(x[j,i] for j in V if j < i) + \
                        quicksum(x[i,j] for j in V if j > i) == 2, "Degree(%s)"%i)

    model.setObjective(quicksum(c[i,j]*x[i,j] for i in V for j in V if j > i), "minimize")

    EPS = 1.e-6
    isMIP = False
    while True:
        model.optimize()
        edges = []
        for (i,j) in x:
            if model.getVal(x[i,j]) > EPS:
                edges.append( (i,j) )

        if addcut(edges) == False:
            if isMIP:     # integer variables, components connected: solution found
                break
            model.freeTransform()
            for (i,j) in x:     # all components connected, switch to integer model
                model.chgVarType(x[i,j], "B")
                isMIP = True

    return model.getObjVal(),edges
Beispiel #3
0
def test_vtype():
    m = Model()

    x = m.addVar(vtype='C', lb=-5.5, ub=8)
    y = m.addVar(vtype='I', lb=-5.2, ub=8)
    z = m.addVar(vtype='B', lb=-5.2, ub=8)
    w = m.addVar(vtype='M', lb=-5.2, ub=8)

    assert x.vtype() == "CONTINUOUS"
    assert y.vtype() == "INTEGER"
    assert z.vtype() == "BINARY"
    assert w.vtype() == "IMPLINT"

    m.chgVarType(x, 'I')
    assert x.vtype() == "INTEGER"

    m.chgVarType(y, 'M')
    assert y.vtype() == "IMPLINT"
Beispiel #4
0
def scip_solver(graph, weight='weight'):
    try:
        from pyscipopt import Model, quicksum
    except ImportError:
        raise ImportError(
            'SCIP Optimization Suit with Python support not found')
    nodes = graph.nodes()
    num_nodes = graph.number_of_nodes()
    c = nx.adjacency_matrix(graph, weight=weight)
    # Define the optimization problem
    model = Model('tsp')
    model.hideOutput()  # silent/verbose mode
    # Create the variables
    x = {}
    for i in xrange(num_nodes):
        for j in xrange(i + 1, num_nodes):
            x[i, j] = model.addVar(ub=1, name='x(%s,%s)' % (i, j))
    # Add the constraints
    for i in xrange(num_nodes):
        model.addCons(
            quicksum([x[j, i] for j in xrange(i)]) +
            quicksum([x[i, j] for j in xrange(i + 1, num_nodes)]) == 2,
            'Degree(%s)' % i)
    # Set minimization objective
    model.setObjective(
        quicksum(c[i, j] * x[i, j] for i in xrange(num_nodes)
                 for j in xrange(i + 1, num_nodes)), 'minimize')

    # Limit the number of edges in a connected component S to |S|-1
    def addcut(cut_edges):
        G = nx.Graph()
        G.add_edges_from(cut_edges)
        Components = list(nx.connected_components(G))
        if len(Components) == 1:
            return False
        model.freeTransform()
        for S in Components:
            model.addCons(
                quicksum(x[i, j] for i in S for j in S if j > i) <= len(S) - 1)
        return True

    # Solve
    EPS = 1.e-6
    isMIP = False
    while True:
        model.optimize()
        edges = []
        for (i, j) in x:
            if model.getVal(x[i, j]) > EPS:
                edges.append((i, j))
        if addcut(edges) == False:
            if isMIP:  # integer variables, components connected: solution found
                break
            model.freeTransform()
            for (i,
                 j) in x:  # all components connected, switch to integer model
                model.chgVarType(x[i, j], 'B')
                isMIP = True
    # Extract the tour from the edges
    G = nx.Graph()
    for e in edges:
        G.add_edge(nodes[e[0]], nodes[e[1]])
    tour_edges = nx.eulerian_circuit(G, source=graph.nodes_iter().next())
    tour = [e[0] for e in tour_edges]
    return tour
Beispiel #5
0
def solve_tsp(V, c):
    """solve_tsp -- solve the traveling salesman problem
       - start with assignment model
       - check flow from a source to every other node;
          - if no flow, a sub-cycle has been found --> add cut
          - otherwise, the solution is optimal
    Parameters:
        - V: set/list of nodes in the graph
        - c[i,j]: cost for traversing edge (i,j)
    Returns the optimum objective value and the list of edges used.
    """
    def addcut(X):
        for sink in V[1:]:
            mflow = maxflow(V, X, V[0], sink)
            mflow.optimize()
            f, cons = mflow.data
            if mflow.ObjVal < 2 - EPS:  # no flow to sink, can add cut
                break
        else:
            return False

        #add a cut/constraint
        CutA = set([V[0]])
        for i in cons:
            if cons[i].Pi <= -1 + EPS:
                CutA.add(i)
        CutB = set(V) - CutA
        main.addCons(
            quicksum(x[i,j] for i in CutA for j in CutB if j>i) + \
            quicksum(x[j,i] for i in CutA for j in CutB if j<i) >= 2)
        print("mflow:", mflow.getObjVal(), "cut:", CutA, "+", CutB, ">= 2")
        print("mflow:", mflow.getObjVal(), "cut:",
              [(i, j) for i in CutA for j in CutB if j > i], "+",
              [(j, i) for i in CutA for j in CutB if j < i], ">= 2")
        return True

    def isMIP(x):
        for var in x:
            if var.vtype == "CONTINUOUS":
                return False
        return True

    # main part of the solution process:
    main = Model("tsp")
    x = {}
    for i in V:
        for j in V:
            if j > i:
                x[i, j] = main.addVar(ub=1,
                                      vtype="C",
                                      name="x(%s,%s)" % (i, j))

    for i in V:
        main.addCons(quicksum(x[j,i] for j in V if j < i) + \
                       quicksum(x[i,j] for j in V if j > i) == 2, "Degree(%s)"%i)

    main.setObjective(
        quicksum(c[i, j] * x[i, j] for i in V for j in V if j > i), "minimize")

    while True:
        main.optimize()
        z = main.getObjVal()
        X = {}
        for (i, j) in x:
            if main.getVal(x[i, j]) > EPS:
                X[i, j] = main.getVal(x[i, j])

        if addcut(X) == False:  # i.e., components are connected
            if isMIP(
            ):  # integer variables, components connected: solution found
                break
            for (i,
                 j) in x:  # all components connected, switch to integer model
                main.chgVarType(x[i, j], "BINARY")

    # process solution
    edges = []
    for (i, j) in x:
        if main.getVal(x[i, j]) > EPS:
            edges.append((i, j))
    return main.getObjVal(), edges
def tsp_solver(c, customers, vehicle_tours):
    def addcut(cut_edges):
        G = networkx.Graph()
        G.add_edges_from(cut_edges)
        Components = list(networkx.connected_components(G))
        if len(Components) == 1:
            return False
        model.freeTransform()
        for S in Components:
            model.addCons(
                quicksum(x[i, j] for i in S for j in S) <= len(S) - 1)

        return True

    # Add the depot on each vehicle
    vehicle_tours = {k: vehicle_tours[k] + [0] for k in vehicle_tours.keys()}
    final_obj = 0
    final_tours = []
    for key, value in vehicle_tours.iteritems():
        v_customers = value
        model = Model("vrp_tsp")
        #model.hideOutput()
        x = {}

        for i in v_customers:
            for j in v_customers:
                # vehicle moves from customer i to customer j
                x[i, j] = model.addVar(vtype="B", name="x(%s,%s)" % (i, j))

        for i in v_customers:
            # Constraint: every customer can only be visited once
            # (or, every node must be connected and connect to another node)
            model.addCons(quicksum(x[i, j] for j in v_customers) == 1)
            model.addCons(quicksum(x[j, i] for j in v_customers) == 1)

            for j in v_customers:
                if i == j:
                    # Constraint: a node cannot conect to itself
                    model.addCons(x[i, j] == 0)

        # Objective function: minimize total distance of the tour
        model.setObjective(
            quicksum(x[i, j] * c[(i, j)] for i in v_customers
                     for j in v_customers), "minimize")

        EPS = 1.e-6
        isMIP = False
        while True:
            model.optimize()
            edges = []
            for (i, j) in x:
                if model.getVal(x[i, j]) > EPS:
                    edges.append((i, j))

            if addcut(edges) == False:
                if isMIP:  # integer variables, components connected: solution found
                    break
                model.freeTransform()
                for (
                        i, j
                ) in x:  # all components connected, switch to integer model
                    model.chgVarType(x[i, j], "B")
                    isMIP = True

        # model.optimize()
        best_sol = model.getBestSol()
        sub_tour = []

        # Build the graph path
        # Retrieve the last node of the graph, i.e., the last one connecting to the depot
        last_node = [n for n in edges if n[1] == 0][0][0]
        G = networkx.Graph()
        G.add_edges_from(edges)
        path = list(networkx.all_simple_paths(G, source=0, target=last_node))
        path.sort(reverse=True, key=lambda u: len(u))

        if len(path) > 0:
            path = path[0][1:]
        else:
            path = path[1:]

        obj = model.getSolObjVal(best_sol)
        final_obj += obj
        final_tours.append([customers[i] for i in path])

        # print("Customers visited by vehicle %s: %s" % (key, value))
        # print("Objective cost for vehicle %s: %s" % (key, obj))
        # print("Edges visited by vehicle %s: %s" % (key, edges))
        # print("Path visited by vehicle %s: %s" % (key, path))
    return final_obj, final_tours
Beispiel #7
0
def tsp2lp(V, c, filename):
    def addcut(cut_edges):

        # Initialize graph
        G = nx.Graph()
        G.add_edges_from(cut_edges)

        if nx.number_connected_components(G) == 1:
            return False

        model.freeTransform()

        for S in nx.connected_components(G):
            model.addCons(
                quicksum(x[i, j] for i in S for j in S if j > i) <= len(S) - 1)
            # print("cut: len(%s) <= %s" % (S, len(S) - 1))
        return True

    def addcut2(cut_edges):

        # Initialize graph
        G = nx.Graph()
        G.add_edges_from(cut_edges)

        if nx.number_connected_components(G) == 1:
            return False

        model.freeTransform()

        for S in nx.connected_components(G):
            T = set(V) - set(S)
            model.addCons(
                quicksum(x[i, j] for i in S for j in T if j > i) +
                quicksum(x[i, j] for i in T for j in S if j > i) >= 2)
        return True

    model = Model()
    model.hideOutput()  # silent/verbose mode
    x = {}
    for i in V:
        for j in V:
            if j > i:
                x[i, j] = model.addVar(ub=1, name="x(%s,%s)" % (i, j))

    for i in V:
        model.addCons(
            quicksum(x[j, i]
                     for j in V if j < i) + quicksum(x[i, j]
                                                     for j in V if j > i) == 2,
            "Degree(%s)" % i,
        )

    model.setObjective(
        quicksum(c[i, j] * x[i, j] for i in V for j in V if j > i), "minimize")

    EPS = 1.e-6
    isMIP = False
    while True:
        model.optimize()
        edges = []
        for (i, j) in x:
            if model.getVal(x[i, j]) > EPS:
                edges.append((i, j))

        if addcut(edges) == False:
            if isMIP:  # integer variables, components connected: solution found
                break
            model.freeTransform()
            for (i,
                 j) in x:  # all components connected, switch to integer model
                model.chgVarType(x[i, j], "B")
                isMIP = True

    model.writeProblem(filename)

    return model
Beispiel #8
0
def solve_tsp(V, c):
    """solve_tsp -- solve the traveling salesman problem
       - start with assignment model
       - add cuts until there are no sub-cycles
    Parameters:
        - V: set/list of nodes in the graph
        - c[i,j]: cost for traversing edge (i,j)
    Returns the optimum objective value and the list of edges used.
    """
    def addcut(cut_edges):
        G = networkx.Graph()
        G.add_edges_from(cut_edges)
        Components = networkx.connected_components(G)
        if len(Components) == 1:
            return False
        for S in Components:
            model.addCons(
                quicksum(x[i, j] for i in S for j in S if j > i) <= len(S) - 1)
            print("cut: len(%s) <= %s" % (S, len(S) - 1))
        return True

    def addcut2(cut_edges):
        G = networkx.Graph()
        G.add_edges_from(cut_edges)
        Components = networkx.connected_components(G)

        if len(Components) == 1:
            return False
        for S in Components:
            T = set(V) - set(S)
            print("S:", S)
            print("T:", T)
            model.addCons(
                quicksum(x[i, j] for i in S for j in T if j > i) +
                quicksum(x[i, j] for i in T for j in S if j > i) >= 2)
            print("cut: %s <--> %s >= 2" % (S, T),
                  [(i, j) for i in S for j in T if j > i])
        return True

    def isMIP(x):
        for var in x:
            if var.vtype == "CONTINUOUS":
                return False
        return True

    # main part of the solution process:
    model = Model("tsp")

    # model.Params.OutputFlag = 0 # silent/verbose mode
    x = {}
    for i in V:
        for j in V:
            if j > i:
                x[i, j] = model.addVar(ub=1, name="x(%s,%s)" % (i, j))

    for i in V:
        model.addCons(quicksum(x[j,i] for j in V if j < i) + \
                        quicksum(x[i,j] for j in V if j > i) == 2, "Degree(%s)"%i)

    model.setObjective(
        quicksum(c[i, j] * x[i, j] for i in V for j in V if j > i), "minimize")

    EPS = 1.e-6
    while True:
        model.optimize()
        edges = []
        for (i, j) in x:
            if model.getVal(x[i, j]) > EPS:
                edges.append((i, j))

        model.freeTransform()
        if addcut(edges) == False:
            if isMIP(
            ):  # integer variables, components connected: solution found
                break
            for (i,
                 j) in x:  # all components connected, switch to integer model
                model.chgVarType(x[i, j], "B")

    return model.getObjVal(), edges
Beispiel #9
0
def solve_tsp(V, c):
    """solve_tsp -- solve the traveling salesman problem
       - start with assignment model
       - add cuts until there are no sub-cycles
    Parameters:
        - V: set/list of nodes in the graph
        - c[i,j]: cost for traversing edge (i,j)
    Returns the optimum objective value and the list of edges used.
    """
    def addcut(cut_edges, fn_constrain):
        G = networkx.Graph()
        G.add_edges_from(cut_edges)
        Components = list(networkx.connected_components(G))
        print('len(components): %s' % len(Components))
        if len(Components) == 1:
            return False
        model.freeTransform()
        fn_constrain(Components)
        return True

    def subtour_elim(Components):
        for S in Components:
            model.addCons(
                quicksum(x[i, j] for i in S for j in S if j > i) <= len(S) - 1)
            # print("cut: len(%s) <= %s" % (S, len(S) - 1))

    def cutset(Components):
        for S in Components:
            T = set(V) - set(S)
            # print("S:", S)
            # print("T:", T)
            model.addCons(
                quicksum(x[i, j] for i in S for j in T if j > i) +
                quicksum(x[i, j] for i in T for j in S if j > i) >= 2)


#             print("cut: %s >= 2" % "+".join(
#                 [("x[%s,%s]" % (i, j)) for i in S for j in T if j > i]))

# main part of the solution process:

    model = Model("tsp")
    model.hideOutput()  # silent/verbose mode
    x = {}
    for i in V:
        for j in V:
            if j > i:  # for symmetric graph, only consider direction: j > i
                x[i, j] = model.addVar(ub=1, name="x(%s,%s)" % (i, j))

    for i in V:
        model.addCons(
            quicksum(x[j, i]
                     for j in V if j < i) + quicksum(x[i, j]
                                                     for j in V if j > i) == 2,
            "Degree(%s)" % i)
    model.setObjective(
        quicksum(c[i, j] * x[i, j] for i in V for j in V if j > i), "minimize")

    EPS = 1.e-6
    isMIP = False
    while True:
        model.optimize()
        edges = []
        for (i, j) in x:
            if model.getVal(x[i, j]) > EPS:
                edges.append((i, j))
        if not addcut(edges, subtour_elim):
            if isMIP:  # integer variables, components connected: solution found
                break
            model.freeTransform()
            print('chgVarType to Integer')
            for (i,
                 j) in x:  # all components connected, switch to integer model
                model.chgVarType(x[i, j], "B")
                isMIP = True

    return model.getObjVal(), edges
def solve(event):
	global n, trucks, solver, li, ax, warehouses, loc_x, loc_y, lines_x, lines_y, obj_value, exec_value
	global distances, distances2
	print("solving...")

	ax.clear()
	ax.set_xlim(0,1000)
	ax.set_ylim(0,1000)
	plot_data(ax, loc_x, loc_y, warehouses)
	ax.text(400, 1075, "solving...", family="serif", horizontalalignment='left', verticalalignment='top')
	plt.draw()
	fig.canvas.draw()

	start_time = time.time()

	# Calculate constraint for Li
	total_l = 0
	total_c = 0.0
	paths = 0
	slack = False
	while total_c < n or paths < trucks:
		c1 = min(c,n)
		total_c += c1
		total_l += c1*(c1+1)/2
		paths += 1

	if total_c > n:
		slack = True

	if solver=="cbc" or solver=="glpk" or solver=="gurobi":
		# Objective function
		z = pulp.LpProblem('Test', pulp.LpMinimize)

		# Generate decision variables
		x = {}
		y = {}
		variables = []
		l = {}
		s = {}
		for i in range(n+warehouses):
			for j in range(n+warehouses):
				if i==j:
					continue
				x[i,j] = pulp.LpVariable('x_' + str(i) + '_' + str(j), 0, 1, pulp.LpInteger)
			if i >= warehouses:
				l[i] = pulp.LpVariable('l_' + str(i), 1, min(n,c), pulp.LpInteger)

		# Objective function
		z += pulp.lpSum([distances[i][j] * x[i,j] for i in range(n+warehouses) for j in 
			list(range(i)) + list(range(i+1,n+warehouses))])

		# Constraints
		constraintSeq = []
		constraintTrucks = []
		for i in range(n+warehouses):
			if i>=warehouses:
				constraintSeq.append(l[i])
			constraintFrom = []

			constraintTo = []
			for j in range(n+warehouses):
				if i==j:
					continue
				if i>=warehouses and j>=warehouses:
					z += pulp.lpSum([l[i], -1*l[j], n*x[i,j], -n+1]) <= 0

				if i>=warehouses:
					constraintFrom.append(x[i,j])
					constraintTo.append(x[j,i])
				if i<warehouses:
					constraintTrucks.append(x[j,i])
				if i>=warehouses:
					z += pulp.lpSum(constraintFrom) == 1 # paths from location
					z += pulp.lpSum(constraintTo) == 1 # paths to location
				if i==warehouses and (paths > 1 or warehouses>1):
					z += pulp.lpSum(constraintTrucks) == paths # paths to warehouse

		if not slack:
		z += pulp.lpSum(constraintSeq) == total_l
		else:
		z += pulp.lpSum(constraintSeq) <= total_l

		# Solve
		if solver=="cbc":
			status = z.solve()
		if solver=="glpk":
			status = z.solve(pulp.GLPK())		
		if solver=="gurobi":
			status = z.solve(pulp.GUROBI_CMD())

		# should be 'Optimal'
		if pulp.LpStatus[status]!="Optimal":
			print("RESULT: ".pulp.LpStatus[status])
		print("Objective function value: "+str(z.objective.value()))

		# Print variables & save path
		lines_x = []
		lines_y = []
		li = [0] * n
		for i in range(n+warehouses):
			if i>=warehouses:
				li[i-warehouses] = pulp.value(l[i])
			for j in range(n+warehouses):
				if i==j:
					continue
				if pulp.value(x[i,j]) == 1:
					lines_x.append(loc_x[i])
					lines_x.append(loc_x[j])
					lines_y.append(loc_y[i])
					lines_y.append(loc_y[j])
					lines_x.append(np.nan)
					lines_y.append(np.nan)

		obj_value = "c=" + str(round(z.objective.value(),2))

	elif solver=="cut plane":
		model = Model("tsp")
		model.hideOutput()
		x = {}
		l = {}
		for i in range(n+warehouses):
			for j in range(n+warehouses):
				if i != j:
					x[i,j] = model.addVar(ub=1, name="x(%s,%s)"%(i,j))
			if (paths > 1 or warehouses > 1) and i >= warehouses:
				l[i] = model.addVar(ub=min(c,n),lb=1, name="l(%s)"%(i))

		if paths == 1 and warehouses == 1:

			# SYMMETRIC DISTANCE MATRIX ONLY
			#for i in range(n+warehouses):
			#model.addCons(quicksum(x[j,i] for j in range(n+warehouses) if j != i) + \
			# quicksum(x[i,j] for j in range(n+warehouses) if j != i) == 2,"Degree(%s)"%i)

			# ASYMMETRIC DISTANCE MATRIX
			for i in range(n+warehouses):
				model.addCons(quicksum(x[j,i] for j in range(n+warehouses) if j != i) == 1,"In(%s)"%i)
				model.addCons(quicksum(x[i,j] for j in range(n+warehouses) if j != i) == 1,"Out(%s)"%i)
		
		else:
			for i in range(warehouses, n+warehouses):
				model.addCons(quicksum(x[j,i] for j in range(n+warehouses) if j != i) == 1,"In(%s)"%i)
				model.addCons(quicksum(x[i,j] for j in range(n+warehouses) if j != i) == 1,"Out(%s)"%i)

			for i in range(warehouses, n+warehouses):
				for j in range(warehouses, n+warehouses):
					if i!=j:
						model.addCons(l[i] -l[j] +n*x[i,j] <= n-1, "Li(%s,%s)"%(i,j))
			model.addCons(quicksum(x[j,i] for i in range(warehouses) for j in range(n+warehouses) if i!=j) == paths, "Paths(%s)"%paths)

			if not slack:
				model.addCons(quicksum(l[i] for i in range(warehouses,n+warehouses)) == total_l,"TotalL")
			
			else:
				model.addCons(quicksum(l[i] for i in range(warehouses,n+warehouses)) <= total_l, "TotalL")
		
		model.setObjective(quicksum(distances2[i,j]*x[i,j] for (i,j) in x), "minimize")

		EPS = 1.e-6
		isMIP = False
		model.setPresolve(SCIP_PARAMSETTING.OFF)

		while True:
			model.optimize()
			#edges = []
			lines_x = []
			lines_y = []
			edges = []
			li = [0] * n
			for (i,j) in x:
				# i=j already skipped
				if model.getVal(x[i,j]) > EPS:
					#edges.append( (i,j) )
					lines_x.append(loc_x[i])
					lines_x.append(loc_x[j])
					lines_y.append(loc_y[i])
					lines_y.append(loc_y[j])
					lines_x.append(np.nan)
					lines_y.append(np.nan)
					edges.append( (i,j) )
			if paths>1 or warehouses>1:
				for i in range(warehouses, n+warehouses):
					li[i-warehouses] = int(model.getVal(l[i]))

			obj_value = "c=" + str(round(model.getObjVal(),2))

			ax.clear()
			ax.set_xlim(0,1000)
			ax.set_ylim(0,1000)

			plot_data_lines(lines_x,lines_y)
			plot_data(ax, loc_x, loc_y, warehouses)
			ax.text(400, 1075, "solving...", family="serif", horizontalalignment='left', verticalalignment='top')

			fig.canvas.draw()

			if addcut(edges,model,x,warehouses) == False:
				if isMIP: # integer variables, components connected: solution found
					break
				model.freeTransform()
				for (i,j) in x: # all components connected, switch to integer model
					model.chgVarType(x[i,j], "B")
				if paths > 1 or warehouses > 1:
					for i in range(warehouses,n+warehouses):
						model.chgVarType(l[i], "I")
				isMIP = True

		sol_li = [0] * (n+warehouses)
		sol_xij = {}

		print('solved.')
	elif solver == 'held-karp':
		li = [0] * n
		opt, path = held_karp(distances)
		print(path)
		obj_value = "c=" + str(round(opt,2))
		x = [[0 for x in range(n+warehouses)] for y in range(n+warehouses)]
		for idx, val in enumerate(path):
			if idx < (len(path)-1):
				x[val][path[idx+1]] = 1;
			elif idx == (len(path)-1):
				x[val][path[0]] = 1;

		for i in range(n+warehouses):
			for j in range(n+warehouses):
				if x[i][j] == 1:
					#edges.append( (i,j) )
					lines_x.append(loc_x[i])
					lines_x.append(loc_x[j])
					lines_y.append(loc_y[i])
					lines_y.append(loc_y[j])
					lines_x.append(np.nan)
					lines_y.append(np.nan)
					#edges.append( (i,j) )

	# Print computation time
	time2 = time.time() - start_time
	exec_value = time2
	units = 'secs'
	if time2 > 60:
		time2 /= 60
		units = 'mins'
	if time2 > 60:
		time2 /= 60
		units = 'hours'

	time2 = round(time2,2)

	exec_value = "exec=" + str(time2) + " " + units

	print("--- " + str(time2) + " " + units + " ---")

	# Redraw points
	ax.clear()
	ax.set_xlim(0,1000)
	ax.set_ylim(0,1000)

	plot_data_lines(lines_x,lines_y)
	plot_data(ax, loc_x, loc_y, warehouses)

	ax.text(350, 1075, obj_value, family="serif", horizontalalignment='right', verticalalignment='top')
	ax.text(450, 1075, exec_value, family="serif", horizontalalignment='left', verticalalignment='top')
	fig.canvas.draw()