Module tsp.tsp_trees
TSP - Computation of 1-trees as relaxation of the symmetric TSP.
Expand source code
"""
TSP - Computation of 1-trees as relaxation of the symmetric TSP.
"""
import networkx as nx
#---------------------------------------------------------------------
def get1Tree( k, G, elen='weight', edgeLst = True ):
"""
Computes a minimum weight 1-tree of a graph G with special node k.
Parameters:
**k** : int
The special node of the graph.
**G** : object (networkx graph)
Connected undirected graph.
**elen** : str
The edge attribute to be used as edge lengths when
computing the tree. Default is the ordinary edge weight.
**edgeLst** : boolean
If true (the default), then the 1-tree is only returned
as the set of edges in the 1-tree; otherwise the 1-tree
is returned as a graph object.
Returns:
**weight** :
The weight of the 1-tree.
**tree** : list of pairs of int
The list of edges in the 1-tree.
"""
# Find cheapest edge adjacent to special node k
kadj = list( G.edges(k))
wk = [ G.get_edge_data(*e)[elen] for e in kadj]
emin = min(kadj, key = lambda e: G.get_edge_data(*e)[elen])
weight = G.get_edge_data(*emin)[elen]
# Temporarily set weight of cheapest edge to zero, all other to "big"
for e in kadj: G[e[0]][e[1]][elen] = 1.0E20
G[emin[0]][emin[1]][elen] = 0
# Obtain minimum-weight spanning tree of the graph
if edgeLst:
tree = [ (e[0],e[1]) for e in nx.minimum_spanning_edges(G,weight=elen)]
weight += sum(G.get_edge_data(*e)[elen] for e in tree )
else:
tree = nx.minimum_spanning_tree( G, weight=elen )
weight += sum([ tree[e[0]][e[1]][elen] for e in tree.edges] )
# Reset the weights of edges incident to k to the old values
cnt = 0
for e in kadj:
G[e[0]][e[1]][elen] = wk[cnt]
cnt += 1
# Include 2nd cheapest edge incident to special node k
kadj.remove(emin)
emin = min(kadj, key = lambda e: G.get_edge_data(*e)[elen])
weight += G.get_edge_data(*emin)[elen]
if edgeLst:
tree.append(emin)
else:
tree.add_edge(*emin)
# Return weight of the 1-tree and the list of edges
return weight, tree
#---------------------------------------------------------------------
def oneTree( problem, G = None, specNodes=None ):
"""
Computes the 1-tree lower bound by searching over a set of nodes
as special nodes.
Parameters:
**problem** : class
The TSP problem object has returned by function load_problem
of the package tsplib95.
**G** : object (networkx graph)
if not None, it need to be networkx graph representation of
the problem as returned by problem.get_graph(). Default=None.
**specNodes** : list of int
A list of nodes that are used as special nodes; if None
(the default) all nodes are tried as special nodes.
Returns:
**weight** :
The weight of the best 1-tree found.
**tree** : list of pairs of int
The networkx graph object representing this 1-tree.
"""
if G is None:
G = problem.get_graph()
G.remove_edges_from(nx.selfloop_edges(G))
if specNodes is None:
specNodes = list(G.nodes)
weight = 0
for k in specNodes:
wk, tree_k = get1Tree(k,G)
if wk > weight:
weight = wk
tree = tree_k
return weight, tree
#---------------------------------------------------------------------
def fast1Tree( problem, G = None ):
"""
Reinelt's fast method for computing a 1 tree lower bound.
Parameters:
**problem** : class
The TSP problem object has returned by function load_problem
of the package tsplib95.
**G** : object (networkx graph)
if not None, it need to be networkx graph representation of
the problem as returned by problem.get_graph(). Note that
selfloops must be removed from the graph.
Default=None.
Returns:
**weight** :
The weight of the best 1-tree found.
**tree** : list of pairs of int
List of the edges in the 1-tree.
"""
if G is None:
G = problem.get_graph()
G.remove_edges_from(nx.selfloop_edges(G))
# Compute minimum spanning tree using networkx
T = nx.minimum_spanning_tree(G)
tweight = sum(T.get_edge_data(*e)['weight'] for e in T.edges)
# Initialize weight of best 1-tree
weight = 0
# Investigate the leafs of the tree one by one
leafs = [i for i in T.nodes if T.degree(i)==1]
for l in leafs:
# Get the edge in the tree incident to node l
t_edge = list(T.edges(l))[0]
# Get the other edges in G incident to node l
l_edges = [e for e in G.edges(l) if e != t_edge]
# Obtain edge of smallest weight from "l_edges"
emin2 = min(l_edges, key = lambda e: G.get_edge_data(*e)['weight'])
# Get weight of 1-tree with special node l
l_weight = tweight + G.get_edge_data(*emin2)['weight']
# Store the edge if it improves the 1-tree lower bound
if l_weight > weight:
weight = l_weight
ebest = emin2
# Collect the edges of the 1-tree in a list
tree = list(T.edges)
tree.append( ebest)
return weight, tree
Functions
def fast1Tree(problem, G=None)
-
Reinelt's fast method for computing a 1 tree lower bound.
Parameters
problem : class
The TSP problem object has returned by function load_problem of the package tsplib95.
G : object (networkx graph)
if not None, it need to be networkx graph representation of the problem as returned by problem.get_graph(). Note that selfloops must be removed from the graph. Default=None.Returns
weight :
The weight of the best 1-tree found.
tree : list of pairs of int
List of the edges in the 1-tree.Expand source code
def fast1Tree( problem, G = None ): """ Reinelt's fast method for computing a 1 tree lower bound. Parameters: **problem** : class The TSP problem object has returned by function load_problem of the package tsplib95. **G** : object (networkx graph) if not None, it need to be networkx graph representation of the problem as returned by problem.get_graph(). Note that selfloops must be removed from the graph. Default=None. Returns: **weight** : The weight of the best 1-tree found. **tree** : list of pairs of int List of the edges in the 1-tree. """ if G is None: G = problem.get_graph() G.remove_edges_from(nx.selfloop_edges(G)) # Compute minimum spanning tree using networkx T = nx.minimum_spanning_tree(G) tweight = sum(T.get_edge_data(*e)['weight'] for e in T.edges) # Initialize weight of best 1-tree weight = 0 # Investigate the leafs of the tree one by one leafs = [i for i in T.nodes if T.degree(i)==1] for l in leafs: # Get the edge in the tree incident to node l t_edge = list(T.edges(l))[0] # Get the other edges in G incident to node l l_edges = [e for e in G.edges(l) if e != t_edge] # Obtain edge of smallest weight from "l_edges" emin2 = min(l_edges, key = lambda e: G.get_edge_data(*e)['weight']) # Get weight of 1-tree with special node l l_weight = tweight + G.get_edge_data(*emin2)['weight'] # Store the edge if it improves the 1-tree lower bound if l_weight > weight: weight = l_weight ebest = emin2 # Collect the edges of the 1-tree in a list tree = list(T.edges) tree.append( ebest) return weight, tree
def get1Tree(k, G, elen='weight', edgeLst=True)
-
Computes a minimum weight 1-tree of a graph G with special node k.
Parameters
k : int
The special node of the graph.
G : object (networkx graph)
Connected undirected graph.
elen : str
The edge attribute to be used as edge lengths when computing the tree. Default is the ordinary edge weight.
edgeLst : boolean
If true (the default), then the 1-tree is only returned as the set of edges in the 1-tree; otherwise the 1-tree is returned as a graph object.Returns
weight :
The weight of the 1-tree.
tree : list of pairs of int
The list of edges in the 1-tree.Expand source code
def get1Tree( k, G, elen='weight', edgeLst = True ): """ Computes a minimum weight 1-tree of a graph G with special node k. Parameters: **k** : int The special node of the graph. **G** : object (networkx graph) Connected undirected graph. **elen** : str The edge attribute to be used as edge lengths when computing the tree. Default is the ordinary edge weight. **edgeLst** : boolean If true (the default), then the 1-tree is only returned as the set of edges in the 1-tree; otherwise the 1-tree is returned as a graph object. Returns: **weight** : The weight of the 1-tree. **tree** : list of pairs of int The list of edges in the 1-tree. """ # Find cheapest edge adjacent to special node k kadj = list( G.edges(k)) wk = [ G.get_edge_data(*e)[elen] for e in kadj] emin = min(kadj, key = lambda e: G.get_edge_data(*e)[elen]) weight = G.get_edge_data(*emin)[elen] # Temporarily set weight of cheapest edge to zero, all other to "big" for e in kadj: G[e[0]][e[1]][elen] = 1.0E20 G[emin[0]][emin[1]][elen] = 0 # Obtain minimum-weight spanning tree of the graph if edgeLst: tree = [ (e[0],e[1]) for e in nx.minimum_spanning_edges(G,weight=elen)] weight += sum(G.get_edge_data(*e)[elen] for e in tree ) else: tree = nx.minimum_spanning_tree( G, weight=elen ) weight += sum([ tree[e[0]][e[1]][elen] for e in tree.edges] ) # Reset the weights of edges incident to k to the old values cnt = 0 for e in kadj: G[e[0]][e[1]][elen] = wk[cnt] cnt += 1 # Include 2nd cheapest edge incident to special node k kadj.remove(emin) emin = min(kadj, key = lambda e: G.get_edge_data(*e)[elen]) weight += G.get_edge_data(*emin)[elen] if edgeLst: tree.append(emin) else: tree.add_edge(*emin) # Return weight of the 1-tree and the list of edges return weight, tree
def oneTree(problem, G=None, specNodes=None)
-
Computes the 1-tree lower bound by searching over a set of nodes as special nodes.
Parameters
problem : class
The TSP problem object has returned by function load_problem of the package tsplib95.
G : object (networkx graph)
if not None, it need to be networkx graph representation of the problem as returned by problem.get_graph(). Default=None.
specNodes : list of int
A list of nodes that are used as special nodes; if None (the default) all nodes are tried as special nodes.Returns
weight :
The weight of the best 1-tree found.
tree : list of pairs of int
The networkx graph object representing this 1-tree.Expand source code
def oneTree( problem, G = None, specNodes=None ): """ Computes the 1-tree lower bound by searching over a set of nodes as special nodes. Parameters: **problem** : class The TSP problem object has returned by function load_problem of the package tsplib95. **G** : object (networkx graph) if not None, it need to be networkx graph representation of the problem as returned by problem.get_graph(). Default=None. **specNodes** : list of int A list of nodes that are used as special nodes; if None (the default) all nodes are tried as special nodes. Returns: **weight** : The weight of the best 1-tree found. **tree** : list of pairs of int The networkx graph object representing this 1-tree. """ if G is None: G = problem.get_graph() G.remove_edges_from(nx.selfloop_edges(G)) if specNodes is None: specNodes = list(G.nodes) weight = 0 for k in specNodes: wk, tree_k = get1Tree(k,G) if wk > weight: weight = wk tree = tree_k return weight, tree