Movatterモバイル変換


[0]ホーム

URL:


ContentsMenuExpandLight modeDark modeAuto light/dark mode
Gurobi Example Tour
Light LogoDark Logo
Gurobi
Back to top

tsp.py#

#!/usr/bin/env python3# Copyright 2025, Gurobi Optimization, LLC# Solve a traveling salesman problem on a randomly generated set of points# using lazy constraints.  The base MIP model only includes 'degree-2'# constraints, requiring each node to have exactly two incident edges.# Solutions to this model may contain subtours - tours that don't visit every# city.  The lazy constraint callback adds new constraints to cut them off.importsysimportloggingimportmathimportrandomfromcollectionsimportdefaultdictfromitertoolsimportcombinationsimportgurobipyasgpfromgurobipyimportGRBdefshortest_subtour(edges):"""Given a list of edges, return the shortest subtour (as a list of nodes)    found by following those edges. It is assumed there is exactly one 'in'    edge and one 'out' edge for every node represented in the edge list."""# Create a mapping from each node to its neighboursnode_neighbors=defaultdict(list)fori,jinedges:node_neighbors[i].append(j)assertall(len(neighbors)==2forneighborsinnode_neighbors.values())# Follow edges to find cycles. Each time a new cycle is found, keep track# of the shortest cycle found so far and restart from an unvisited node.unvisited=set(node_neighbors)shortest=Nonewhileunvisited:cycle=[]neighbors=list(unvisited)whileneighbors:current=neighbors.pop()cycle.append(current)unvisited.remove(current)neighbors=[jforjinnode_neighbors[current]ifjinunvisited]ifshortestisNoneorlen(cycle)<len(shortest):shortest=cycleassertshortestisnotNonereturnshortestclassTSPCallback:"""Callback class implementing lazy constraints for the TSP.  At MIPSOL    callbacks, solutions are checked for subtours and subtour elimination    constraints are added if needed."""def__init__(self,nodes,x):self.nodes=nodesself.x=xdef__call__(self,model,where):"""Callback entry point: call lazy constraints routine when new        solutions are found. Stop the optimization if there is an exception in        user code."""ifwhere==GRB.Callback.MIPSOL:try:self.eliminate_subtours(model)exceptException:logging.exception("Exception occurred in MIPSOL callback")model.terminate()defeliminate_subtours(self,model):"""Extract the current solution, check for subtours, and formulate lazy        constraints to cut off the current solution if subtours are found.        Assumes we are at MIPSOL."""values=model.cbGetSolution(self.x)edges=[(i,j)for(i,j),vinvalues.items()ifv>0.5]tour=shortest_subtour(edges)iflen(tour)<len(self.nodes):# add subtour elimination constraint for every pair of cities in tourmodel.cbLazy(gp.quicksum(self.x[i,j]fori,jincombinations(tour,2))<=len(tour)-1)defsolve_tsp(nodes,distances):"""    Solve a dense symmetric TSP using the following base formulation:    min  sum_ij d_ij x_ij    s.t. sum_j x_ij == 2   forall i in V         x_ij binary       forall (i,j) in E    and subtours eliminated using lazy constraints.    """withgp.Env()asenv,gp.Model(env=env)asm:# Create variables, and add symmetric keys to the resulting dictionary# 'x', such that (i, j) and (j, i) refer to the same variable.x=m.addVars(distances.keys(),obj=distances,vtype=GRB.BINARY,name="e")x.update({(j,i):vfor(i,j),vinx.items()})# Create degree 2 constraintsforiinnodes:m.addConstr(gp.quicksum(x[i,j]forjinnodesifi!=j)==2)# Optimize model using lazy constraints to eliminate subtoursm.Params.LazyConstraints=1cb=TSPCallback(nodes,x)m.optimize(cb)# Extract the solution as a touredges=[(i,j)for(i,j),vinx.items()ifv.X>0.5]tour=shortest_subtour(edges)assertset(tour)==set(nodes)returntour,m.ObjValif__name__=="__main__":# Parse argumentsiflen(sys.argv)<2:print("Usage: tsp.py npoints <seed>")sys.exit(0)npoints=int(sys.argv[1])seed=int(sys.argv[2])iflen(sys.argv)>2else1# Create n random points in 2Drandom.seed(seed)nodes=list(range(npoints))points=[(random.randint(0,100),random.randint(0,100))foriinnodes]# Dictionary of Euclidean distance between each pair of pointsdistances={(i,j):math.sqrt(sum((points[i][k]-points[j][k])**2forkinrange(2)))fori,jincombinations(nodes,2)}tour,cost=solve_tsp(nodes,distances)print("")print(f"Optimal tour:{tour}")print(f"Optimal cost:{cost:g}")print("")

Help and Feedback


[8]
ページ先頭

©2009-2025 Movatter.jp