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 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
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"
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
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
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
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
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()