Module tsp.concorde

TSP - Python interface to Concorde - A C-library for solving TSPs.

Last update: 22 July, 2022

Remark: Concorde is a TSP solver written in C and created by D. Applegate, R. Bixby, V. Chvatal and W. Cook. The code is available from http://www.math.uwaterloo.ca/tsp/concorde and permission to use it is granted for academic purposes only.

Expand source code
"""
    TSP - Python interface to Concorde - A C-library for solving TSPs.
    
    Last update: 22 July, 2022

    Remark: 
        Concorde is a TSP solver written in C and created by D. Applegate, 
        R. Bixby, V. Chvatal and W. Cook.  The code is available from
        http://www.math.uwaterloo.ca/tsp/concorde and permission to use it 
        is granted for academic purposes only.
"""
import numpy as np
import ctypes, pathlib, platform, time
from tsplib95 import load_problem
import os, sys

__CC_Lib = None
__CPX_Lib = None

#---------------------------------------------------------------------

def __loadConcorde():
    """ 
    Find and load cplex and concorde library.
    """    

    cplexLib = None
    concordeLib = None

    onWindows = 'Windows' in platform.system()
    onMac = 'darwin' in platform.system().lower()
    if onWindows:
        cplexDLL = 'cplex*.dll'
        concordeDLL = 'libconcorde.dll'
    else:
        # Not sure if this works on OSX/Darwin
        cplexDLL = 'libcplex*.so'
        concordeDLL = 'libconcorde.so'
   
    # Check if the CPLEX callable library is present. On Linux and Mac, we
    # will also have to explictly load this library before Concorde is
    # is loaded, as Concorde depends on Cplex. On Windows, it suffices
    # to load Concorde as the the Concorde Library has been linked with 
    # the Cplex static library. If we find more than one Cplex dynamic
    # link library, take the most recent version.

    try:
        cpxEnv = next( p for p in os.environ if 'CPLEX_STUDIO_BINARIES' in p )
    except:
        cpxEnv = 'PATH'
    cpxBins = os.getenv(cpxEnv)
    sep = ';' if onWindows else ':'
    dirLst = [ p for p in cpxBins.split(sep) if 'cplex' in p.lower()]
    lastVer= 0
    for direc in dirLst:
        for pathName in pathlib.Path( direc ).rglob( cplexDLL ):
            testLib = str(pathlib.Path.resolve(pathName))
            if not testLib is None:
                fname = pathName.stem
                if fname[-1].isdigit():
                    numVer = int( ''.join([c for c in fname if c.isdigit()]) )
                    if numVer > lastVer:
                        lastVer = numVer         
                        cplexLib = testLib
    if cplexLib is None:
        print("Could not find CPLEX dynamic link library.")
        print("Make sure that Cplex's binary folder is in the PATH environment variable or")
        print("that environment variable CPLEX_STUDIO_BINARIES is defined and set correctly!")
        return( False )   

    # The dynamic link library libconcorde.so (libconcorde.dll on Windows)
    # is expected to reside in the same directory as this module "concorde.py"
    if onWindows:
        prefix = 'win//'
    elif onMac:
        prefix = 'mac/'
    else:
        prefix = 'linux/'
    concordeHome=pathlib.Path(__file__).resolve().parent
    concordeFile = pathlib.PurePath.joinpath(concordeHome,prefix+concordeDLL)
    if concordeFile.is_file():
        concordeLib = str( concordeFile )
    else:
        print("Could not find Concorde's dynamic link library.")
        return( False )

    # Libraries found. Now try to load them. Cplex first, as Concorde
    # depends on Cplex
    global __CPX_Lib
    global __CC_Lib
    if not onWindows:
        __CPX_Lib = ctypes.CDLL(cplexLib, mode=ctypes.RTLD_GLOBAL)
        if __CPX_Lib is None: return( False )
    
    __CC_Lib = ctypes.CDLL(concordeLib, mode=ctypes.RTLD_GLOBAL)
    if __CC_Lib is None:
        print("Error occured when loading library ",concordeLib )
        return( False )

    return( True )
    
#---------------------------------------------------------------------

def solveTSP( problem, route=None, exact=True, logFile=None ):
    """
    Invokes Concorde's TSP solver on the TSP instance *problem*.

    Parameters:
        **problem** : class  
            TSP problem object has returned by TSPlib95.  
        **route** : list of int  
            If not none it should contain a permutation list of cities
            to be used as initial solution.  
        **exact** : bool  
            If true (default) it is tried to solve the instance exactly 
            by means of Concorde's branch-and-cut solver; otherwise 
            Concorde only applies the Lin-Kernighan heuristic.  
        **logFile** : str  
            If not None, it need to be the name of file, where to 
            redirect output from Concorde.  

    Returns:
        **routeLen** : int   
            Length of the route.  
        **route** : list of int  
            Computed route (route[0] is the depot and route[-1] is the
            last customer).
    """
    if __CC_Lib is None:
        print("Concorde Library not loaded!")
        return 0, []
    
    try:
        _ = problem.wfunc
    except:
        problem.wfunc = problem._wfunc
    
    nn = problem.dimension
    nodeLst = list( problem.get_nodes() )

    n = ctypes.c_int( nn )
    seed = ctypes.c_int( int( time.time() ) )
    tiLim = ctypes.c_double(0.0)
    LKonly = ctypes.c_char(1-int(exact))
    
    # Compute the distance matrix
    dist=np.array( list(problem.wfunc(nodeLst[i],nodeLst[j]) for i in range(1,nn) \
                   for j in range(i)), dtype = ctypes.c_int )

    pdist = dist.ctypes.data_as( ctypes.POINTER( ctypes.c_int ) )        

    # Number the nodes from 0 to n-1
    nodeIdx = dict(zip(nodeLst,range(nn)))
   
    # Redirect output from Concorde?
    if logFile is None:
        logPtr = ctypes.c_char_p(0)
    else:
        logPtr = ctypes.c_char_p(logFile.encode('utf-8'))
        old_out = sys.stdout # saver when on windows
    
    # Create integer array representing the tour  
    if route is None:
        tLen = ctypes.c_double( 0.0 )
        tour = np.zeros(nn, dtype=ctypes.c_int)
    else:
        tLen = ctypes.c_double( 1.0 )
        tour = np.array(list(nodeIdx[i] for i in route), dtype=ctypes.c_int)
    ptour= tour.ctypes.data_as( ctypes.POINTER( ctypes.c_int ) )
        
    # Call concorde for computing TSP tour
    __CC_Lib.solve_STSP.restype = ctypes.c_int
    status = __CC_Lib.solve_STSP( LKonly, n, seed, tiLim, pdist,\
                 logPtr, ctypes.byref(tLen), ptour )
    
    # Following is safer when on Windows
    if not logFile is None: sys.stdout = old_out
            
    if status < 2:
        routeLen = int(tLen.value)
        route = [ nodeLst[i] for i in tour ]
        return routeLen, route
    else:
        return np.Inf, []

#---------------------------------------------------------------------

def solveTSPmatrix( dist, route=None, exact=True, logFile = None ):
    """
    Invokes Concorde's TSP solver.

    Parameters:
        **dist** : numpy array of c_int  
            Contains the lower triangle part of the symmetric
            distance matrix. That is the distance between the
            i-th node and the j-th node (i=0,...,n-1 and i > j)
            is given by dist[(i-1)i/2 + j].  
        **route** : list of int, optional  
            If not None, it need to be a permutation of the n nodes
            which is used as an initial route.  
        **exact** : bool  
            If true (default) it is tried to solve the instance
            exactly by means of Concorde's branch-and-cut solver;
            otherwise Concorde only applies the Lin-Kernighan
            heuristic.  
        **logFile** : str  
            If not None, it need to be the name of file, where to 
            redirect output from Concorde.

    Returns:
        **routeLen** : int  
            Length of the route (routeLen).  
        **route** : list of int  
            Computed route (route[0] is the depot and route[-1] is the
            last customer).
    """
    if __CC_Lib is None:
        print("Concorde Library not loaded!")
        return 0, []
    
    # Number of nodes
    n = int(0.5+np.sqrt(0.25+2*len(dist) + 1.0E-7))
    nn = ctypes.c_int( n )
    seed = ctypes.c_int( int( time.time() ) )
    tiLim = ctypes.c_double(0.0)
    LKonly = ctypes.c_char(1-int(exact))
    
    minIndx = 0
    if route is None:
        tLen = ctypes.c_double( 0.0 )
        tour = np.zeros(n, dtype=ctypes.c_int)
    else:
        minIndx = min(route)
        tour = np.array( route, dtype=ctypes.c_int ) - minIndx
        tLen = ctypes.c_double( 1.0 )
    ptour= tour.ctypes.data_as( ctypes.POINTER( ctypes.c_int ) )

    # Redirect output from Concorde?
    if logFile is None:
        logPtr = ctypes.c_char_p(0)
    else:
        logPtr = ctypes.c_char_p(logFile.encode('utf-8'))
        old_out = sys.stdout 
    
    pdist = dist.ctypes.data_as( ctypes.POINTER( ctypes.c_int ) )
    
    # Call concorde for computing TSP tour
    __CC_Lib.solve_STSP.restype = ctypes.c_int
    status = __CC_Lib.solve_STSP( LKonly, nn, seed, tiLim, pdist,\
                 logPtr, ctypes.byref(tLen), ptour )
        
    # Following is safer when on Windows
    if not logFile is None: sys.stdout = old_out
            
    if status < 2:
        routeLen = int(tLen.value)
        route = [ i+minIndx for i in tour ]
        return routeLen, route
    else:
        return np.Inf, []

#---------------------------------------------------------------------

def solveTSPgraph( G, length='weight', route=None, exact=True, logFile = None ):
    """
    Invokes Concorde's TSP solver and tries to solve a TSP
    on the graph G.

    Parameters:
        **G** : networkx graph object  
            Undirected graph on which the TSP should be solved.  
        **length** : str  
            attribute/key in the graph's edge data that specifies
            the edge length, default='weight'.  
        **route** : list of int, optional  
            If not None, it need to be a permutation of the n nodes
            which is used as an initial route.  
        **exact** : bool, optional  
            If true (default) it is tried to solve the instance
            exactly by means of Concorde's branch-and-cut solver;
            otherwise Concorde only applies the Lin-Kernighan
            heuristic.  
        **logFile** : str, optional  
            If not None, it need to be the name of file, where to 
            redirect output from Concorde.

    Returns:
        **routeLen** : int  
            Length of the route (routeLen).  
        **route** : list of int  
            Computed route (route[0] is the depot and route[-1] is the
            last customer).
    """
    if __CC_Lib is None:
        print("Concorde Library not loaded!")
        return 0, []
    
    seed = ctypes.c_int( int( time.time() ) )
    tiLim = ctypes.c_double(0.0)
    LKonly = ctypes.c_char(1-int(exact))

    # number of nodes and edges
    n = G.number_of_nodes()
    m = G.number_of_edges()
    nn = ctypes.c_int( n )
    mm = ctypes.c_int( m )

    # Number the nodes from 0 to n-1
    nodeIdx = dict(zip(G.nodes,range(n)))
    
    # Edge lengths required for Concorde
    elen = np.array([G[e[0]][e[1]][length] for e in G.edges],dtype=ctypes.c_int )
    p_elen = elen.ctypes.data_as( ctypes.POINTER( ctypes.c_int ) )
    
    # End nodes of edges required for Concorde
    elist = np.zeros(2*m, dtype=ctypes.c_int)
    for i, e in enumerate(G.edges): 
        elist[2*i], elist[2*i+1] = nodeIdx[e[0]], nodeIdx[e[1]]
    p_elist = elist.ctypes.data_as( ctypes.POINTER( ctypes.c_int ) )
    
    # Set up C array for the initial tour
    if route is None:
        tLen = ctypes.c_double( 0.0 )
        tour = np.zeros(n, dtype=ctypes.c_int)
    else:
        tour = np.array( [nodeIdx[i] for i in route], dtype=ctypes.c_int )
        rLen = sum( G[route[i-1]][route[i]][length] for i in range(1,n) )\
               + G[route[-1]][route[0]][length]
        tLen = ctypes.c_double( rLen )
        
    ptour= tour.ctypes.data_as( ctypes.POINTER( ctypes.c_int ) )        
            
    # Redirect output from Concorde?
    if logFile is None:
        logPtr = ctypes.c_char_p(0)
    else:
        logPtr = ctypes.c_char_p(logFile.encode('utf-8'))
        old_out = sys.stdout 
    
    # Call concorde for computing TSP tour
    __CC_Lib.solve_STSP.restype = ctypes.c_int
    status = __CC_Lib.solve_sparseTSP( LKonly, nn, mm, seed, tiLim,\
                 p_elist, p_elen, logPtr, ctypes.byref(tLen), ptour )
        
    # Following is safer when on Windows
    if not logFile is None: sys.stdout = old_out
            
    if status < 2:
        routeLen = int(tLen.value)
        route = list( np.array( G.nodes)[tour] )
        return routeLen, route
    else:
        return np.Inf, []
       
#---------------------------------------------------------------------

def solveTSPdat( n, dist=None, route=None, exact=True, logFile = None ):
    """
    Invokes Concorde's TSP solver. This function is DEPRECATED and is
    actually to be replaced by either calling solveTSPgraph or
    solveTSPmatrix. The function is only kept for reasons of
    compatibility with functions 'postOptim' and 'improveRoute'
    in the module 'vrp.vrp_util'. All parameters are the same
    as for function 'solveTSPmatrix', except n which is the number
    of nodes. Note that dist need actually be present and should
    not be None (otherwise None is also returned).  

    Returns:
        Same as for solveTSPmatrix
    """

    if __CC_Lib is None:
        print("Concorde Library not loaded!")
        return 0, []
    
    if dist is None: return 0, []
    
    return solveTSPmatrix(dist, route=route, exact=exact, logFile=logFile)

#---------------------------------------------------------------------

def solveTSPLib( fname, exact=True, logFile=None ):
    """
    Solve a TSPLIB instance stored in file *fname* by means of
    Concorde's TSP solver.

    Parameters:
        **fname** : str  
            Name (including) the path to the data file (format TSPLIB).  
        **exact** : bool  
            If true (default) it is tried to solve the instance exactly
            by means of Concorde's branch-and-cut solver; otherwise 
            Concorde only applies the Lin-Kernighan heuristic.  
        **logFile** : str  
            If not None, it need to be the name of file, where to 
            redirect output from Concorde.

    Returns:
        **routeLen** : int  
            Length of the route (routeLen).  
        **route** : list of int  
            Computed route (route[0] is the depot and route[-1] is the
            last customer).
    """

    if __CC_Lib is None:
        print("Concorde Library not loaded!")
        return 0, []
    else:
        # We first create a pointer to an integer array
        problem = load_problem(fname)
        tour = np.zeros(problem.dimension, dtype=ctypes.c_int)
        iptr = ctypes.POINTER( ctypes.c_int )
        ptour= tour.ctypes.data_as( iptr )
        # Redirect output from Concorde?
        if logFile is None:
            logPtr = ctypes.c_char_p(0)
        else:
            logPtr = ctypes.c_char_p(logFile.encode('utf-8'))
            old_out = sys.stdout
        
        # Initialize other parameters of c-function solve_TSLPlib
        seed    = ctypes.c_int( int( time.time() ) )
        status  = ctypes.c_int(0)
        tiLim   = ctypes.c_double(0.0)
        fnmeptr = ctypes.c_char_p(fname.encode('utf-8'))
        LKonly  = ctypes.c_char(1-int(exact))
        __CC_Lib.solve_TSPlib.restype = ctypes.c_double
        tLen    = __CC_Lib.solve_TSPlib( LKonly, fnmeptr, seed, tiLim,\
                      logPtr, ptour, ctypes.byref(status) );
        routeLen = tLen
        nodeLst = [node for node in problem.get_nodes()]
        route = [ nodeLst[i] for i in tour ]
        
        # Following is safer when on Windows
        if not logFile is None: sys.stdout = old_out
            
        return routeLen, route

#---------------------------------------------------------------------

is_ready = __loadConcorde()

Functions

def solveTSP(problem, route=None, exact=True, logFile=None)

Invokes Concorde's TSP solver on the TSP instance problem.

Parameters

problem : class
TSP problem object has returned by TSPlib95.
route : list of int
If not none it should contain a permutation list of cities to be used as initial solution.
exact : bool
If true (default) it is tried to solve the instance exactly by means of Concorde's branch-and-cut solver; otherwise Concorde only applies the Lin-Kernighan heuristic.
logFile : str
If not None, it need to be the name of file, where to redirect output from Concorde.

Returns

routeLen : int
Length of the route.
route : list of int
Computed route (route[0] is the depot and route[-1] is the last customer).

Expand source code
def solveTSP( problem, route=None, exact=True, logFile=None ):
    """
    Invokes Concorde's TSP solver on the TSP instance *problem*.

    Parameters:
        **problem** : class  
            TSP problem object has returned by TSPlib95.  
        **route** : list of int  
            If not none it should contain a permutation list of cities
            to be used as initial solution.  
        **exact** : bool  
            If true (default) it is tried to solve the instance exactly 
            by means of Concorde's branch-and-cut solver; otherwise 
            Concorde only applies the Lin-Kernighan heuristic.  
        **logFile** : str  
            If not None, it need to be the name of file, where to 
            redirect output from Concorde.  

    Returns:
        **routeLen** : int   
            Length of the route.  
        **route** : list of int  
            Computed route (route[0] is the depot and route[-1] is the
            last customer).
    """
    if __CC_Lib is None:
        print("Concorde Library not loaded!")
        return 0, []
    
    try:
        _ = problem.wfunc
    except:
        problem.wfunc = problem._wfunc
    
    nn = problem.dimension
    nodeLst = list( problem.get_nodes() )

    n = ctypes.c_int( nn )
    seed = ctypes.c_int( int( time.time() ) )
    tiLim = ctypes.c_double(0.0)
    LKonly = ctypes.c_char(1-int(exact))
    
    # Compute the distance matrix
    dist=np.array( list(problem.wfunc(nodeLst[i],nodeLst[j]) for i in range(1,nn) \
                   for j in range(i)), dtype = ctypes.c_int )

    pdist = dist.ctypes.data_as( ctypes.POINTER( ctypes.c_int ) )        

    # Number the nodes from 0 to n-1
    nodeIdx = dict(zip(nodeLst,range(nn)))
   
    # Redirect output from Concorde?
    if logFile is None:
        logPtr = ctypes.c_char_p(0)
    else:
        logPtr = ctypes.c_char_p(logFile.encode('utf-8'))
        old_out = sys.stdout # saver when on windows
    
    # Create integer array representing the tour  
    if route is None:
        tLen = ctypes.c_double( 0.0 )
        tour = np.zeros(nn, dtype=ctypes.c_int)
    else:
        tLen = ctypes.c_double( 1.0 )
        tour = np.array(list(nodeIdx[i] for i in route), dtype=ctypes.c_int)
    ptour= tour.ctypes.data_as( ctypes.POINTER( ctypes.c_int ) )
        
    # Call concorde for computing TSP tour
    __CC_Lib.solve_STSP.restype = ctypes.c_int
    status = __CC_Lib.solve_STSP( LKonly, n, seed, tiLim, pdist,\
                 logPtr, ctypes.byref(tLen), ptour )
    
    # Following is safer when on Windows
    if not logFile is None: sys.stdout = old_out
            
    if status < 2:
        routeLen = int(tLen.value)
        route = [ nodeLst[i] for i in tour ]
        return routeLen, route
    else:
        return np.Inf, []
def solveTSPLib(fname, exact=True, logFile=None)

Solve a TSPLIB instance stored in file fname by means of Concorde's TSP solver.

Parameters

fname : str
Name (including) the path to the data file (format TSPLIB).
exact : bool
If true (default) it is tried to solve the instance exactly by means of Concorde's branch-and-cut solver; otherwise Concorde only applies the Lin-Kernighan heuristic.
logFile : str
If not None, it need to be the name of file, where to redirect output from Concorde.

Returns

routeLen : int
Length of the route (routeLen).
route : list of int
Computed route (route[0] is the depot and route[-1] is the last customer).

Expand source code
def solveTSPLib( fname, exact=True, logFile=None ):
    """
    Solve a TSPLIB instance stored in file *fname* by means of
    Concorde's TSP solver.

    Parameters:
        **fname** : str  
            Name (including) the path to the data file (format TSPLIB).  
        **exact** : bool  
            If true (default) it is tried to solve the instance exactly
            by means of Concorde's branch-and-cut solver; otherwise 
            Concorde only applies the Lin-Kernighan heuristic.  
        **logFile** : str  
            If not None, it need to be the name of file, where to 
            redirect output from Concorde.

    Returns:
        **routeLen** : int  
            Length of the route (routeLen).  
        **route** : list of int  
            Computed route (route[0] is the depot and route[-1] is the
            last customer).
    """

    if __CC_Lib is None:
        print("Concorde Library not loaded!")
        return 0, []
    else:
        # We first create a pointer to an integer array
        problem = load_problem(fname)
        tour = np.zeros(problem.dimension, dtype=ctypes.c_int)
        iptr = ctypes.POINTER( ctypes.c_int )
        ptour= tour.ctypes.data_as( iptr )
        # Redirect output from Concorde?
        if logFile is None:
            logPtr = ctypes.c_char_p(0)
        else:
            logPtr = ctypes.c_char_p(logFile.encode('utf-8'))
            old_out = sys.stdout
        
        # Initialize other parameters of c-function solve_TSLPlib
        seed    = ctypes.c_int( int( time.time() ) )
        status  = ctypes.c_int(0)
        tiLim   = ctypes.c_double(0.0)
        fnmeptr = ctypes.c_char_p(fname.encode('utf-8'))
        LKonly  = ctypes.c_char(1-int(exact))
        __CC_Lib.solve_TSPlib.restype = ctypes.c_double
        tLen    = __CC_Lib.solve_TSPlib( LKonly, fnmeptr, seed, tiLim,\
                      logPtr, ptour, ctypes.byref(status) );
        routeLen = tLen
        nodeLst = [node for node in problem.get_nodes()]
        route = [ nodeLst[i] for i in tour ]
        
        # Following is safer when on Windows
        if not logFile is None: sys.stdout = old_out
            
        return routeLen, route
def solveTSPdat(n, dist=None, route=None, exact=True, logFile=None)

Invokes Concorde's TSP solver. This function is DEPRECATED and is actually to be replaced by either calling solveTSPgraph or solveTSPmatrix. The function is only kept for reasons of compatibility with functions 'postOptim' and 'improveRoute' in the module 'vrp.vrp_util'. All parameters are the same as for function 'solveTSPmatrix', except n which is the number of nodes. Note that dist need actually be present and should not be None (otherwise None is also returned).

Returns

Same as for solveTSPmatrix

Expand source code
def solveTSPdat( n, dist=None, route=None, exact=True, logFile = None ):
    """
    Invokes Concorde's TSP solver. This function is DEPRECATED and is
    actually to be replaced by either calling solveTSPgraph or
    solveTSPmatrix. The function is only kept for reasons of
    compatibility with functions 'postOptim' and 'improveRoute'
    in the module 'vrp.vrp_util'. All parameters are the same
    as for function 'solveTSPmatrix', except n which is the number
    of nodes. Note that dist need actually be present and should
    not be None (otherwise None is also returned).  

    Returns:
        Same as for solveTSPmatrix
    """

    if __CC_Lib is None:
        print("Concorde Library not loaded!")
        return 0, []
    
    if dist is None: return 0, []
    
    return solveTSPmatrix(dist, route=route, exact=exact, logFile=logFile)
def solveTSPgraph(G, length='weight', route=None, exact=True, logFile=None)

Invokes Concorde's TSP solver and tries to solve a TSP on the graph G.

Parameters

G : networkx graph object
Undirected graph on which the TSP should be solved.
length : str
attribute/key in the graph's edge data that specifies the edge length, default='weight'.
route : list of int, optional
If not None, it need to be a permutation of the n nodes which is used as an initial route.
exact : bool, optional
If true (default) it is tried to solve the instance exactly by means of Concorde's branch-and-cut solver; otherwise Concorde only applies the Lin-Kernighan heuristic.
logFile : str, optional
If not None, it need to be the name of file, where to redirect output from Concorde.

Returns

routeLen : int
Length of the route (routeLen).
route : list of int
Computed route (route[0] is the depot and route[-1] is the last customer).

Expand source code
def solveTSPgraph( G, length='weight', route=None, exact=True, logFile = None ):
    """
    Invokes Concorde's TSP solver and tries to solve a TSP
    on the graph G.

    Parameters:
        **G** : networkx graph object  
            Undirected graph on which the TSP should be solved.  
        **length** : str  
            attribute/key in the graph's edge data that specifies
            the edge length, default='weight'.  
        **route** : list of int, optional  
            If not None, it need to be a permutation of the n nodes
            which is used as an initial route.  
        **exact** : bool, optional  
            If true (default) it is tried to solve the instance
            exactly by means of Concorde's branch-and-cut solver;
            otherwise Concorde only applies the Lin-Kernighan
            heuristic.  
        **logFile** : str, optional  
            If not None, it need to be the name of file, where to 
            redirect output from Concorde.

    Returns:
        **routeLen** : int  
            Length of the route (routeLen).  
        **route** : list of int  
            Computed route (route[0] is the depot and route[-1] is the
            last customer).
    """
    if __CC_Lib is None:
        print("Concorde Library not loaded!")
        return 0, []
    
    seed = ctypes.c_int( int( time.time() ) )
    tiLim = ctypes.c_double(0.0)
    LKonly = ctypes.c_char(1-int(exact))

    # number of nodes and edges
    n = G.number_of_nodes()
    m = G.number_of_edges()
    nn = ctypes.c_int( n )
    mm = ctypes.c_int( m )

    # Number the nodes from 0 to n-1
    nodeIdx = dict(zip(G.nodes,range(n)))
    
    # Edge lengths required for Concorde
    elen = np.array([G[e[0]][e[1]][length] for e in G.edges],dtype=ctypes.c_int )
    p_elen = elen.ctypes.data_as( ctypes.POINTER( ctypes.c_int ) )
    
    # End nodes of edges required for Concorde
    elist = np.zeros(2*m, dtype=ctypes.c_int)
    for i, e in enumerate(G.edges): 
        elist[2*i], elist[2*i+1] = nodeIdx[e[0]], nodeIdx[e[1]]
    p_elist = elist.ctypes.data_as( ctypes.POINTER( ctypes.c_int ) )
    
    # Set up C array for the initial tour
    if route is None:
        tLen = ctypes.c_double( 0.0 )
        tour = np.zeros(n, dtype=ctypes.c_int)
    else:
        tour = np.array( [nodeIdx[i] for i in route], dtype=ctypes.c_int )
        rLen = sum( G[route[i-1]][route[i]][length] for i in range(1,n) )\
               + G[route[-1]][route[0]][length]
        tLen = ctypes.c_double( rLen )
        
    ptour= tour.ctypes.data_as( ctypes.POINTER( ctypes.c_int ) )        
            
    # Redirect output from Concorde?
    if logFile is None:
        logPtr = ctypes.c_char_p(0)
    else:
        logPtr = ctypes.c_char_p(logFile.encode('utf-8'))
        old_out = sys.stdout 
    
    # Call concorde for computing TSP tour
    __CC_Lib.solve_STSP.restype = ctypes.c_int
    status = __CC_Lib.solve_sparseTSP( LKonly, nn, mm, seed, tiLim,\
                 p_elist, p_elen, logPtr, ctypes.byref(tLen), ptour )
        
    # Following is safer when on Windows
    if not logFile is None: sys.stdout = old_out
            
    if status < 2:
        routeLen = int(tLen.value)
        route = list( np.array( G.nodes)[tour] )
        return routeLen, route
    else:
        return np.Inf, []
def solveTSPmatrix(dist, route=None, exact=True, logFile=None)

Invokes Concorde's TSP solver.

Parameters

dist : numpy array of c_int
Contains the lower triangle part of the symmetric distance matrix. That is the distance between the i-th node and the j-th node (i=0,…,n-1 and i > j) is given by dist[(i-1)i/2 + j].
route : list of int, optional
If not None, it need to be a permutation of the n nodes which is used as an initial route.
exact : bool
If true (default) it is tried to solve the instance exactly by means of Concorde's branch-and-cut solver; otherwise Concorde only applies the Lin-Kernighan heuristic.
logFile : str
If not None, it need to be the name of file, where to redirect output from Concorde.

Returns

routeLen : int
Length of the route (routeLen).
route : list of int
Computed route (route[0] is the depot and route[-1] is the last customer).

Expand source code
def solveTSPmatrix( dist, route=None, exact=True, logFile = None ):
    """
    Invokes Concorde's TSP solver.

    Parameters:
        **dist** : numpy array of c_int  
            Contains the lower triangle part of the symmetric
            distance matrix. That is the distance between the
            i-th node and the j-th node (i=0,...,n-1 and i > j)
            is given by dist[(i-1)i/2 + j].  
        **route** : list of int, optional  
            If not None, it need to be a permutation of the n nodes
            which is used as an initial route.  
        **exact** : bool  
            If true (default) it is tried to solve the instance
            exactly by means of Concorde's branch-and-cut solver;
            otherwise Concorde only applies the Lin-Kernighan
            heuristic.  
        **logFile** : str  
            If not None, it need to be the name of file, where to 
            redirect output from Concorde.

    Returns:
        **routeLen** : int  
            Length of the route (routeLen).  
        **route** : list of int  
            Computed route (route[0] is the depot and route[-1] is the
            last customer).
    """
    if __CC_Lib is None:
        print("Concorde Library not loaded!")
        return 0, []
    
    # Number of nodes
    n = int(0.5+np.sqrt(0.25+2*len(dist) + 1.0E-7))
    nn = ctypes.c_int( n )
    seed = ctypes.c_int( int( time.time() ) )
    tiLim = ctypes.c_double(0.0)
    LKonly = ctypes.c_char(1-int(exact))
    
    minIndx = 0
    if route is None:
        tLen = ctypes.c_double( 0.0 )
        tour = np.zeros(n, dtype=ctypes.c_int)
    else:
        minIndx = min(route)
        tour = np.array( route, dtype=ctypes.c_int ) - minIndx
        tLen = ctypes.c_double( 1.0 )
    ptour= tour.ctypes.data_as( ctypes.POINTER( ctypes.c_int ) )

    # Redirect output from Concorde?
    if logFile is None:
        logPtr = ctypes.c_char_p(0)
    else:
        logPtr = ctypes.c_char_p(logFile.encode('utf-8'))
        old_out = sys.stdout 
    
    pdist = dist.ctypes.data_as( ctypes.POINTER( ctypes.c_int ) )
    
    # Call concorde for computing TSP tour
    __CC_Lib.solve_STSP.restype = ctypes.c_int
    status = __CC_Lib.solve_STSP( LKonly, nn, seed, tiLim, pdist,\
                 logPtr, ctypes.byref(tLen), ptour )
        
    # Following is safer when on Windows
    if not logFile is None: sys.stdout = old_out
            
    if status < 2:
        routeLen = int(tLen.value)
        route = [ i+minIndx for i in tour ]
        return routeLen, route
    else:
        return np.Inf, []