Travelling Salesman Problem with subtour elimination

tsp_simple_cuts_generic.ipynb Open In Colab Kaggle Gradient Open In SageMaker Studio Lab Hits

Description: this example shows how to solve a TSP by eliminating subtours using amplpy and ampls

Tags: callbacks, tsp

Notebook author: Christian Valente <christian.valente@gmail.com>

Model author: N/A

import os

if not os.path.isdir("tsp_inputs"):
    os.system("git clone https://github.com/ampl/colab.ampl.com.git")
    os.chdir("colab.ampl.com/authors/mapgccv/callbacks")

Setup and cloud integration

# Install dependencies
%pip install -q amplpy amplpy-gurobi amplpy-cplex
%pip install -q tsplib95 matplotlib pandas
# Google Colab & Kaggle integration
from amplpy import AMPL, ampl_notebook

ampl = ampl_notebook(
    modules=["coin"],  # modules to install
    license_uuid="default",  # license to use
)  # instantiate AMPL object and register magics

Options

import os

USE_CALLBACKS = True
PLOTSUBTOURS = True
TSP_FILE = "./tsp_inputs/a280.tsp"

Imports

SOLVER = "gurobi"
SOLVER_OPTIONS = ["outlev=1"]
# Import utilities
from amplpy import AMPL  # pip install amplpy

if SOLVER == "gurobi":
    import amplpy_gurobi as ampls  # pip install amplpy-gurobi
elif SOLVER == "cplex":
    import amplpy_cplex as ampls  # pip install amplpy-cplex
elif SOLVER == "xpress":
    import amplpy_xpress as ampls  # pip install amplpy-gurobi
import tsplib95 as tsp  # pip install tsplib95
import matplotlib.pyplot as plt  # pip install matplotlib
import matplotlib.colors as colors
from time import time
import pandas as pd

plt.rcParams["figure.dpi"] = 200

Define TSP model in AMPL

We are using amplpy to define the subtour elimination constraint in AMPL and instantiating it appropriately and ampls to add cuts directly from the solver callback.

%%ampl
set NODES ordered;
param hpos {NODES};
param vpos {NODES};

set PAIRS := {i in NODES, j in NODES: ord(i) < ord(j)};

param distance {(i,j) in PAIRS}
   := sqrt((hpos[j]-hpos[i])**2 + (vpos[j]-vpos[i])**2);

var X {PAIRS} binary;

minimize Tour_Length: sum {(i,j) in PAIRS} distance[i,j] * X[i,j];

subject to Visit_All {i in NODES}:
   sum {(i, j) in PAIRS} X[i,j] + sum {(j, i) in PAIRS} X[j,i] = 2;

Function to load TSP data files and return a dictionary of (nodeid : coordinate)

def getDictFromTspFile(tspFile):
    p = tsp.load(tspFile)
    if not p.is_depictable:
        print("Problem is not depictable!")

    # Amendments as we need the nodes lexographically ordered
    nnodes = len(list(p.get_nodes()))
    i = 0
    while nnodes > 1:
        nnodes = nnodes / 10
        i += 1
    formatString = f"{{:0{i}d}}"
    nodes = {
        formatString.format(value): p.node_coords[index + 1]
        for index, value in enumerate(p.get_nodes())
    }
    return nodes

Create AMPL object with amplpy and load model and data

# Get the model from the cell above
tsp_model = _ampl_cells[0]

# Load model in AMPL
ampl = AMPL()
ampl.eval(tsp_model)
ampl.option["solver"] = SOLVER
ampl.option[SOLVER + "_options"] = " ".join(SOLVER_OPTIONS)

# Set problem data from tsp file
nodes = getDictFromTspFile(TSP_FILE)
df = pd.DataFrame.from_dict(nodes, orient="index", columns=["hpos", "vpos"])
df.index.name = "NODES"
ampl.set_data(df, "NODES")

# Set some globals that never change during the execution of the problem
NODES = set(nodes.keys())
CPOINTS = {
    node: complex(coordinate[0], coordinate[1]) for (node, coordinate) in nodes.items()
}

Define some helpers functions to plot the tours

def plotTours(tours: list, points_coordinate: dict):
    # Plot all the tours in the list each with a different color
    colors = ["b", "g", "c", "m", "y", "k"]
    for i, tour in enumerate(tours):
        tourCoordinates = [points_coordinate[point.strip("'")] for point in tour]
        color = colors[i % len(colors)]
        plot_all(tourCoordinates, color=color)
    plt.show()


def plot_all(tour, alpha=1, color=None):
    # Plot the tour as blue lines between blue circles
    plotline(list(tour) + [tour[0]], alpha=alpha, color=color)
    plotline([tour[0]], "s", alpha=alpha, color=color)


def plotline(points, style="o-", alpha=1, color=None):
    "Plot a list of points (complex numbers) in the 2-D plane."
    X, Y = XY(points)
    if color:
        plt.plot(X, Y, style, alpha=alpha, color=color)
    else:
        plt.plot(X, Y, style, alpha=alpha)


def XY(points):
    "Given a list of points, return two lists: X coordinates, and Y coordinates."
    return [p.real for p in points], [p.imag for p in points]

Define some helper functions to help with the graphs (e.g. get the subtour given a set of arcs)

# Graphs helper routines
def trasverse(node, arcs: set, allnodes: set, subtour=None) -> list:
    # Trasverses all the arcs in the set arcs, starting from node
    # and returns the tour
    if not subtour:
        subtour = list()
    # Find arcs involving the current node
    myarcs = [(i, j) for (i, j) in arcs if node == i or node == j]
    if len(myarcs) == 0:
        return
    # Append the current node to the current subtour
    subtour.append(node)

    # Use the first arc found
    myarc = myarcs[0]
    # Find destination (or origin) node
    destination = next(i for i in myarc if i != node)
    # Remove from arcs and nodes to visit
    arcs.remove(myarc)
    if node in allnodes:
        allnodes.remove(node)

    trasverse(destination, arcs, allnodes, subtour)
    return subtour


def findSubTours(arcs: set, allnodes: set):
    """Find all the subtours defined by a set of arcs and
    return them as a list of list
    """
    subtours = list()
    allnodes = allnodes.copy()
    while len(allnodes) > 0:
        l = trasverse(next(iter(allnodes)), arcs, allnodes)
        subtours.append(l)
    return subtours

AMPLPY implementation of sub-tours elimination

def amplSubTourElimination(ampl: AMPL):
    # Add the constraint and the needed parameters
    subToursAMPL = """param nSubtours >= 0 integer, default 0;
    set SUB {1..nSubtours} within NODES;

    subject to Subtour_Elimination {k in 1..nSubtours}:
    sum {i in SUB[k], j in NODES diff SUB[k]}
    if (i, j) in PAIRS then X[i, j] else X[j, i] >= 2;"""
    ampl.eval(subToursAMPL)

    nSubtoursParam = ampl.get_parameter("nSubtours")
    SubtoursSet = ampl.get_set("SUB")

    allsubtours = list()
    while True:  # Repeat until the solution contains only one tour
        ampl.solve()
        # Get solution
        ARCS = ampl.get_data("{(i,j) in PAIRS : X[i,j] > 0} X[i,j];")
        ARCS = set([(i, j) for (i, j, k) in ARCS.toList()])
        subtours = findSubTours(ARCS, NODES)
        # If we have only one tour, the solution is valid
        if len(subtours) <= 1:
            break
        print(f"Found {len(subtours)} subtours, plotting them and adding cuts")
        if PLOTSUBTOURS:
            plotTours(subtours, CPOINTS)
        # Else add the current tours to the list of subtours
        allsubtours.extend(subtours)
        # And add those to the constraints by assigning the values to
        # the parameter and the set
        nSubtoursParam.set(len(allsubtours))
        for i, tour in enumerate(allsubtours):
            SubtoursSet[i + 1].set_values(tour)

ampls callbacks implementation of subtours elimination

# Callback class that actually add the cuts if subtours are found in a solution
class MyCallback(ampls.GenericCallback):
    def __init__(self):
        # Constructor, simply sets the iteration number to 0
        super().__init__()
        self.iteration = 0

    def run(self):
        try:
            # For each solution
            if self.getAMPLWhere() == ampls.Where.MIPSOL:
                self.iteration += 1
                print(f"\nIteration {self.iteration}: Finding subtours")
                sol = self.getSolutionVector()
                arcs = [xvars[i] for i, value in enumerate(sol) if value > 0]
                subTours = findSubTours(set(arcs), set(vertices))
                if len(subTours) == 1:
                    print("No subtours detected. Not adding any cut")
                    return 0
                print(f"Adding {len(subTours)} cuts")
                if PLOTSUBTOURS:
                    plotTours(subTours, CPOINTS)
                for subTour in subTours:
                    st1 = set(subTour)
                    nst1 = set(vertices) - st1
                    externalArcs = [
                        (i, j) if i < j else (j, i) for i in st1 for j in nst1
                    ]
                    varsExternalArcs = [xinverse[i, j] for (i, j) in externalArcs]
                    coeffs = [1 for i in range(len(varsExternalArcs))]
                    if PLOTSUBTOURS:
                        print("Adding cut for subtour:", st1)
                    self.addLazyIndices(
                        varsExternalArcs, coeffs, ampls.CutDirection.GE, 2
                    )
                    if len(subTours) == 2:
                        return 0
                print("Continue solving")
            return 0
        except Exception as e:
            print("Error:", e)
            return 1
# Global variables to store entities needed by the callbacks
# that never change
xvars = None
xinverse = None
vertices = None


def solverSubTourElimination(ampl: AMPL, solver, solver_options):
    global xvars, xinverse, vertices
    # Export the model using ampls
    model = ampl.to_ampls(solver, solver_options)
    model.enableLazyConstraints()

    # Get the global maps between solver vars and AMPL entities
    varMap = model.getVarMapFiltered("X")
    # print("varMap:", varMap)
    inverse = model.getVarMapInverse()
    xvars = {index: ampls.var2tuple(var)[1:] for var, index in varMap.items()}
    xinverse = {ampls.var2tuple(var)[1:]: index for index, var in inverse.items()}
    vertices = list(
        sorted(set([x[0] for x in xvars.values()] + [x[1] for x in xvars.values()]))
    )

    # Assign the callback
    callback = MyCallback()
    model.set_callback(callback)
    print("Start optimization")
    # Start the optimization
    model.optimize()
    # Import the solution back to AMPL
    ampl.import_solution(model)

Script running the optimization

t0 = time()
if not USE_CALLBACKS:
    amplSubTourElimination(ampl)
else:
    solverSubTourElimination(ampl, SOLVER, SOLVER_OPTIONS)
Start optimization

Iteration 1: Finding subtours
Adding 5 cuts
../_images/a47741d7b1d4ff7bf7943ca4bb0a8e42702e03ec1c833ba0875f95f822b0d8d7.png
Adding cut for subtour: {'089', '127', '252', '088', '196', '039', '192', '098', '218', '031', '124', '121', '012', '066', '045', '023', '025', '038', '212', '097', '237', '062', '071', '172', '270', '096', '219', '040', '259', '058', '111', '244', '197', '036', '238', '154', '188', '240', '140', '273', '222', '151', '167', '194', '001', '087', '236', '003', '019', '024', '079', '280', '072', '226', '160', '175', '022', '109', '020', '067', '110', '070', '050', '182', '275', '044', '147', '201', '263', '228', '061', '076', '060', '026', '035', '138', '170', '260', '037', '239', '198', '266', '029', '106', '249', '055', '074', '007', '278', '131', '028'}
Adding cut for subtour: {'171', '276', '130', '102', '184', '082', '049', '103', '030', '125', '169', '274', '217', '165', '248', '101', '235', '262', '021', '146'}
Adding cut for subtour: {'203', '005', '100', '209', '241', '205', '269', '141', '271', '245', '257', '225', '199', '052', '264', '180', '134', '090', '002', '114', '161', '094', '065', '078', '164', '168', '157', '190', '008', '250', '027', '085', '221', '133', '277', '265', '247', '115', '063', '123', '118', '202', '093', '206', '261', '178', '176', '211', '047', '159', '105', '075', '246', '051', '117', '173', '129', '186', '153', '112', '099', '144', '272', '011', '073', '004', '174', '006', '018', '148', '229', '195', '135', '069', '119', '231', '234', '034', '242', '095', '017', '009', '255', '256', '227', '104', '137', '042', '081', '010', '210', '013', '214', '187', '230', '043', '158', '126', '083', '216', '113', '116', '142', '080', '268', '086', '091', '232', '189', '181', '193', '150', '223', '155', '243', '120', '046', '162', '064', '204', '015', '107', '077', '191', '200', '033', '054', '224', '108', '267', '233', '032', '156', '053', '048', '092', '220', '149', '207', '041', '215', '059', '056', '128', '279', '014', '136'}
Adding cut for subtour: {'163', '253', '145', '258'}
Adding cut for subtour: {'208', '057', '152', '132', '185', '068', '166', '016', '084', '254', '143', '213', '179', '139', '122', '183', '251', '177'}
Continue solving

Iteration 2: Finding subtours
Adding 70 cuts
../_images/bab4a7a68052aad0abe59c4e6329c08f8024a7d84c9dcbfc0012d0268d666df1.png
Adding cut for subtour: {'089', '090', '192', '191'}
Adding cut for subtour: {'127', '154', '153', '128'}
Adding cut for subtour: {'172', '171', '110', '109'}
Adding cut for subtour: {'030', '251', '029', '252'}
Adding cut for subtour: {'006', '276', '275', '005'}
Adding cut for subtour: {'088', '193', '087', '194'}
Adding cut for subtour: {'210', '071', '209', '072'}
Adding cut for subtour: {'075', '205', '206', '076'}
Adding cut for subtour: {'129', '130', '151', '152'}
Adding cut for subtour: {'139', '141', '140', '142'}
Adding cut for subtour: {'272', '010', '009', '271'}
Adding cut for subtour: {'036', '246', '035', '245'}
Adding cut for subtour: {'056', '055', '225', '226'}
Adding cut for subtour: {'229', '052', '230', '051'}
Adding cut for subtour: {'263', '018', '264', '017'}
Adding cut for subtour: {'101', '179', '180', '102'}
Adding cut for subtour: {'134', '147', '148', '133'}
Adding cut for subtour: {'161', '162', '119', '120'}
Adding cut for subtour: {'183', '098', '097', '184'}
Adding cut for subtour: {'123', '124', '157', '158'}
Adding cut for subtour: {'092', '091', '189', '190'}
Adding cut for subtour: {'257', '024', '023', '258'}
Adding cut for subtour: {'254', '027', '028', '253'}
Adding cut for subtour: {'086', '085', '195', '196'}
Adding cut for subtour: {'122', '160', '121', '159'}
Adding cut for subtour: {'222', '221', '059', '060'}
Adding cut for subtour: {'248', '034', '247', '033'}
Adding cut for subtour: {'215', '216', '065', '066'}
Adding cut for subtour: {'003', '278', '277', '004'}
Adding cut for subtour: {'267', '268', '013', '014'}
Adding cut for subtour: {'208', '074', '207', '073'}
Adding cut for subtour: {'235', '045', '236', '046'}
Adding cut for subtour: {'080', '079', '201', '202'}
Adding cut for subtour: {'187', '093', '188', '094'}
Adding cut for subtour: {'043', '044', '238', '237'}
Adding cut for subtour: {'062', '219', '061', '220'}
Adding cut for subtour: {'057', '224', '058', '223'}
Adding cut for subtour: {'104', '178', '103', '177'}
Adding cut for subtour: {'185', '186', '095', '096'}
Adding cut for subtour: {'270', '011', '012', '269'}
Adding cut for subtour: {'176', '175', '106', '105'}
Adding cut for subtour: {'260', '022', '021', '259'}
Adding cut for subtour: {'112', '111', '170', '169'}
Adding cut for subtour: {'117', '164', '118', '163'}
Adding cut for subtour: {'068', '213', '214', '067'}
Adding cut for subtour: {'174', '108', '107', '173'}
Adding cut for subtour: {'099', '100', '182', '181'}
Adding cut for subtour: {'084', '197', '198', '083'}
Adding cut for subtour: {'168', '167', '113', '114'}
Adding cut for subtour: {'261', '019', '262', '020'}
Adding cut for subtour: {'146', '135', '136', '145'}
Adding cut for subtour: {'211', '212', '069', '070'}
Adding cut for subtour: {'232', '231', '050', '049'}
Adding cut for subtour: {'001', '280', '002', '279'}
Adding cut for subtour: {'144', '143', '138', '137'}
Adding cut for subtour: {'255', '025', '026', '256'}
Adding cut for subtour: {'156', '126', '125', '155'}
Adding cut for subtour: {'081', '199', '082', '200'}
Adding cut for subtour: {'054', '228', '227', '053'}
Adding cut for subtour: {'233', '234', '048', '047'}
Adding cut for subtour: {'239', '041', '240', '042'}
Adding cut for subtour: {'063', '064', '218', '217'}
Adding cut for subtour: {'165', '115', '116', '166'}
Adding cut for subtour: {'031', '250', '249', '032'}
Adding cut for subtour: {'204', '203', '077', '078'}
Adding cut for subtour: {'039', '241', '242', '040'}
Adding cut for subtour: {'274', '008', '273', '007'}
Adding cut for subtour: {'266', '016', '015', '265'}
Adding cut for subtour: {'038', '037', '243', '244'}
Adding cut for subtour: {'132', '150', '131', '149'}
Continue solving

Iteration 3: Finding subtours
Adding 19 cuts
../_images/8ca452aa491b3687b05ea586fefda61171d554269bb1b6ac6665f34ddbbdbafd.png
Adding cut for subtour: {'089', '127', '005', '088', '100', '130', '141', '271', '196', '039', '199', '166', '082', '180', '134', '090', '161', '065', '078', '164', '168', '030', '125', '031', '157', '008', '124', '027', '121', '012', '133', '277', '066', '139', '101', '123', '063', '023', '025', '038', '118', '062', '071', '152', '178', '270', '176', '040', '159', '075', '111', '145', '129', '122', '163', '186', '153', '112', '169', '099', '197', '144', '179', '011', '036', '073', '004', '154', '006', '273', '140', '148', '018', '151', '167', '195', '021', '194', '135', '119', '069', '034', '003', '017', '019', '009', '024', '079', '072', '137', '042', '081', '010', '013', '187', '043', '158', '126', '083', '160', '175', '022', '080', '268', '091', '177', '109', '020', '067', '110', '070', '150', '155', '016', '120', '201', '162', '064', '015', '077', '061', '076', '200', '033', '060', '026', '035', '108', '267', '138', '032', '156', '037', '092', '198', '149', '274', '041', '165', '029', '132', '068', '074', '128', '007', '279', '278', '131', '014', '136', '028'}
Adding cut for subtour: {'172', '171', '170'}
Adding cut for subtour: {'252', '209', '263', '255', '219', '211', '256', '228', '259', '227', '257', '225', '226', '264', '224', '254', '210', '213', '214', '260', '230', '218', '250', '258', '220', '216', '221', '207', '265', '217', '251', '215', '208', '222', '248', '249', '229', '262', '253', '212', '261', '223'}
Adding cut for subtour: {'266', '203', '205', '202', '206', '204'}
Adding cut for subtour: {'243', '241', '247', '240', '244', '242', '245'}
Adding cut for subtour: {'052', '050', '051', '049', '053', '048'}
Adding cut for subtour: {'093', '095', '096', '094', '098', '097'}
Adding cut for subtour: {'188', '269', '189', '190'}
Adding cut for subtour: {'087', '115', '084', '113', '116', '085', '117', '114', '086'}
Adding cut for subtour: {'045', '054', '055', '056', '046', '047'}
Adding cut for subtour: {'232', '239', '246', '238', '231', '237'}
Adding cut for subtour: {'057', '044', '059', '058'}
Adding cut for subtour: {'181', '184', '185', '272', '182', '183'}
Adding cut for subtour: {'104', '276', '105', '103', '102'}
Adding cut for subtour: {'106', '173', '174', '107', '275'}
Adding cut for subtour: {'233', '235', '234', '236'}
Adding cut for subtour: {'001', '280', '002'}
Adding cut for subtour: {'146', '147', '143', '142'}
Adding cut for subtour: {'193', '192', '191'}
Continue solving

Iteration 4: Finding subtours
Adding 8 cuts
../_images/f7cc5bc335b2e1fa4b3e7ede26d6638a8a2eaa6de1df5aef7f5bfbe5aafd69ab.png
Adding cut for subtour: {'089', '127', '171', '005', '088', '100', '130', '271', '039', '166', '052', '082', '090', '114', '161', '094', '065', '098', '078', '168', '030', '125', '031', '157', '190', '008', '124', '027', '085', '012', '133', '277', '121', '066', '045', '101', '115', '123', '063', '023', '025', '038', '118', '093', '097', '062', '057', '071', '152', '172', '178', '185', '176', '096', '047', '040', '159', '058', '105', '075', '111', '051', '117', '173', '129', '122', '163', '186', '153', '112', '169', '099', '272', '011', '036', '073', '004', '154', '188', '174', '006', '273', '018', '151', '167', '021', '119', '069', '034', '087', '276', '095', '017', '019', '009', '024', '102', '079', '104', '072', '042', '081', '010', '013', '103', '187', '043', '158', '126', '083', '160', '175', '022', '113', '116', '080', '086', '091', '177', '109', '020', '189', '181', '067', '110', '070', '050', '182', '275', '155', '044', '016', '120', '046', '162', '064', '015', '107', '077', '061', '076', '033', '184', '054', '060', '026', '035', '108', '049', '170', '032', '156', '053', '048', '037', '092', '274', '041', '165', '029', '106', '059', '056', '055', '132', '068', '074', '084', '128', '007', '131', '014', '028', '183'}
Adding cut for subtour: {'203', '252', '209', '147', '143', '205', '201', '263', '255', '141', '211', '204', '256', '219', '259', '196', '257', '137', '227', '228', '225', '200', '199', '226', '264', '224', '145', '254', '267', '210', '138', '213', '214', '260', '230', '218', '197', '198', '250', '258', '144', '216', '220', '142', '221', '207', '265', '139', '202', '217', '251', '215', '208', '266', '140', '222', '249', '229', '262', '195', '146', '194', '253', '212', '206', '261', '223'}
Adding cut for subtour: {'240', '232', '236', '243', '239', '235', '246', '241', '237', '233', '247', '238', '231', '234', '244', '242', '245'}
Adding cut for subtour: {'193', '180', '191', '192', '179', '164'}
Adding cut for subtour: {'270', '136', '134', '135', '269', '268'}
Adding cut for subtour: {'149', '150', '148'}
Adding cut for subtour: {'001', '280', '002', '003'}
Adding cut for subtour: {'278', '279', '248'}
Continue solving

Iteration 5: Finding subtours
Adding 12 cuts
../_images/90b85d516b1425cf5160d03d822498c4ab5d0eabf154dfd7a895a3aa4fba5f6d.png
Adding cut for subtour: {'089', '171', '088', '100', '039', '166', '082', '090', '114', '161', '065', '094', '098', '078', '164', '168', '030', '125', '031', '157', '124', '121', '085', '066', '101', '123', '063', '115', '038', '118', '093', '097', '062', '071', '172', '096', '040', '159', '075', '111', '117', '122', '163', '112', '169', '099', '036', '073', '167', '119', '069', '034', '087', '095', '102', '079', '072', '042', '081', '043', '158', '083', '160', '175', '113', '116', '080', '086', '091', '109', '067', '110', '070', '120', '162', '064', '077', '061', '076', '033', '060', '035', '108', '170', '032', '037', '092', '041', '165', '068', '074', '084'}
Adding cut for subtour: {'127', '203', '005', '241', '205', '130', '269', '271', '196', '245', '257', '225', '264', '180', '134', '192', '002', '218', '190', '008', '250', '258', '027', '221', '012', '247', '133', '265', '277', '023', '025', '202', '212', '206', '261', '152', '178', '185', '270', '176', '211', '219', '259', '246', '254', '213', '129', '244', '186', '153', '197', '272', '179', '011', '238', '004', '154', '188', '240', '006', '222', '018', '273', '151', '229', '262', '195', '021', '194', '135', '231', '242', '001', '276', '003', '017', '009', '024', '255', '280', '256', '227', '137', '226', '010', '210', '013', '214', '187', '230', '126', '216', '022', '268', '251', '177', '189', '181', '193', '150', '182', '223', '155', '275', '243', '016', '201', '263', '204', '191', '228', '015', '184', '026', '224', '267', '138', '156', '260', '239', '198', '220', '207', '274', '217', '215', '266', '029', '248', '249', '128', '007', '279', '278', '014', '136', '028', '183'}
Adding cut for subtour: {'208', '209', '252', '253'}
Adding cut for subtour: {'140', '148', '146', '147', '143', '142', '149', '139', '141'}
Adding cut for subtour: {'051', '052', '050', '049'}
Adding cut for subtour: {'057', '044', '059', '045', '058', '056'}
Adding cut for subtour: {'232', '236', '235', '233', '234', '237'}
Adding cut for subtour: {'104', '103', '105'}
Adding cut for subtour: {'106', '107', '174', '173'}
Adding cut for subtour: {'132', '019', '020', '131'}
Adding cut for subtour: {'054', '055', '046', '053', '048', '047'}
Adding cut for subtour: {'199', '145', '200', '144'}
Continue solving

Iteration 6: Finding subtours
Adding 6 cuts
../_images/cb6a3f01ece64fbd24768a818c79dc9e1d866cf3ee115c3bbc637ebf7fc26256.png
Adding cut for subtour: {'089', '171', '088', '100', '039', '166', '082', '090', '114', '161', '065', '094', '098', '078', '164', '168', '030', '125', '031', '157', '124', '121', '085', '066', '101', '123', '063', '115', '038', '118', '093', '097', '062', '071', '172', '096', '040', '159', '105', '075', '111', '117', '173', '122', '163', '112', '169', '099', '036', '073', '174', '167', '119', '069', '034', '087', '095', '102', '079', '104', '072', '042', '081', '103', '043', '158', '083', '160', '175', '113', '116', '080', '086', '091', '109', '067', '110', '070', '120', '162', '064', '107', '077', '061', '076', '033', '060', '035', '108', '170', '032', '037', '092', '041', '165', '106', '068', '074', '084'}
Adding cut for subtour: {'127', '276', '005', '016', '017', '019', '009', '024', '130', '271', '015', '010', '026', '013', '129', '126', '008', '272', '022', '011', '012', '133', '277', '004', '274', '027', '006', '273', '018', '029', '020', '132', '128', '021', '023', '007', '025', '131', '014', '028', '275'}
Adding cut for subtour: {'203', '252', '209', '241', '205', '269', '141', '196', '245', '257', '225', '199', '264', '180', '134', '192', '002', '218', '190', '250', '258', '221', '247', '265', '139', '208', '202', '212', '206', '261', '237', '185', '178', '270', '176', '211', '219', '259', '246', '145', '254', '213', '244', '186', '197', '144', '179', '238', '188', '240', '140', '222', '148', '229', '235', '262', '195', '194', '135', '231', '234', '242', '001', '236', '003', '143', '255', '280', '256', '227', '137', '226', '210', '214', '187', '230', '216', '142', '268', '251', '177', '189', '232', '181', '193', '150', '182', '223', '243', '147', '201', '263', '204', '191', '228', '200', '184', '224', '267', '233', '138', '260', '239', '198', '220', '149', '207', '217', '215', '266', '249', '248', '146', '279', '278', '253', '136', '183'}
Adding cut for subtour: {'057', '044', '058', '059', '045', '056', '055', '052', '054', '050', '051', '049', '048', '046', '053', '047'}
Adding cut for subtour: {'156', '151', '152'}
Adding cut for subtour: {'154', '153', '155'}
Continue solving

Iteration 7: Finding subtours
Adding 5 cuts
../_images/9893832340bed1363ffa26178e69109c2f930cc00ef5551c19757eaeaf3c6d1f.png
Adding cut for subtour: {'089', '171', '088', '100', '039', '166', '082', '090', '114', '161', '094', '098', '078', '164', '168', '030', '125', '031', '157', '124', '121', '085', '101', '123', '063', '115', '038', '118', '093', '097', '062', '071', '172', '096', '040', '159', '105', '058', '075', '111', '117', '173', '122', '163', '112', '169', '099', '036', '073', '174', '167', '119', '069', '034', '087', '095', '102', '079', '104', '072', '042', '081', '103', '043', '158', '083', '160', '175', '113', '116', '080', '086', '091', '109', '067', '110', '070', '120', '162', '107', '077', '061', '076', '033', '060', '035', '108', '170', '032', '037', '092', '041', '165', '106', '059', '068', '074', '084'}
Adding cut for subtour: {'127', '203', '252', '005', '209', '205', '130', '269', '141', '271', '196', '257', '225', '199', '264', '180', '134', '192', '002', '218', '190', '008', '250', '258', '027', '221', '012', '247', '133', '265', '139', '277', '014', '208', '023', '025', '202', '212', '206', '261', '152', '178', '185', '270', '176', '211', '219', '259', '145', '254', '213', '129', '186', '153', '197', '144', '272', '179', '011', '004', '154', '188', '006', '222', '140', '148', '018', '151', '273', '229', '262', '195', '021', '194', '135', '001', '276', '003', '017', '143', '019', '009', '024', '255', '280', '256', '227', '137', '226', '010', '210', '013', '214', '187', '230', '126', '216', '022', '142', '268', '251', '177', '020', '189', '181', '193', '150', '182', '223', '155', '275', '016', '147', '201', '263', '204', '191', '228', '015', '200', '184', '026', '224', '267', '138', '156', '260', '198', '220', '149', '207', '274', '217', '215', '266', '029', '248', '249', '132', '128', '146', '007', '279', '278', '131', '253', '136', '028', '183'}
Adding cut for subtour: {'240', '232', '236', '243', '239', '235', '246', '241', '237', '233', '238', '231', '234', '244', '242', '245'}
Adding cut for subtour: {'057', '044', '045', '056', '055', '052', '054', '050', '051', '049', '048', '046', '053', '047'}
Adding cut for subtour: {'065', '064', '066'}
Continue solving

Iteration 8: Finding subtours
Adding 20 cuts
../_images/dfa98cae8f0c461317aa58350f401667a5bdb57ac58a902acecbbaa428c61075.png
Adding cut for subtour: {'089', '171', '088', '100', '039', '166', '052', '082', '090', '161', '094', '098', '078', '164', '168', '030', '125', '031', '157', '124', '121', '045', '101', '123', '038', '093', '097', '057', '071', '172', '096', '047', '040', '159', '105', '058', '075', '111', '051', '173', '122', '163', '112', '169', '099', '036', '073', '174', '167', '119', '069', '034', '087', '095', '102', '079', '104', '072', '042', '081', '103', '043', '158', '083', '160', '175', '080', '091', '109', '067', '110', '070', '050', '044', '120', '046', '162', '107', '077', '061', '076', '033', '060', '054', '035', '108', '049', '170', '032', '053', '048', '037', '092', '041', '165', '106', '059', '056', '055', '068', '074', '084'}
Adding cut for subtour: {'127', '152', '178', '185', '176', '143', '147', '130', '201', '191', '196', '200', '184', '199', '180', '145', '192', '156', '187', '129', '186', '153', '190', '197', '198', '179', '142', '154', '188', '177', '020', '151', '181', '189', '193', '195', '128', '021', '194', '146', '150', '131', '182', '183', '155'}
Adding cut for subtour: {'208', '203', '252', '202', '205', '207', '253', '206', '204'}
Adding cut for subtour: {'001', '243', '005', '241', '002', '280', '242'}
Adding cut for subtour: {'209', '211', '219', '228', '227', '225', '226', '224', '210', '213', '214', '230', '244', '218', '250', '220', '216', '221', '247', '217', '251', '215', '222', '249', '248', '229', '212', '223'}
Adding cut for subtour: {'252', '263', '255', '141', '256', '259', '257', '137', '264', '267', '254', '138', '260', '258', '265', '139', '266', '148', '262', '261'}
Adding cut for subtour: {'006', '273', '276', '005', '008', '272', '007', '009', '277', '004', '274', '271', '275'}
Adding cut for subtour: {'240', '232', '236', '239', '235', '246', '237', '233', '238', '231', '234', '245'}
Adding cut for subtour: {'018', '132', '270', '016', '017', '134', '019', '133'}
Adding cut for subtour: {'026', '010', '023', '022', '027', '025', '024', '013', '012', '014', '011', '015'}
Adding cut for subtour: {'062', '115', '063', '065', '113', '116', '085', '117', '118', '114', '066', '086', '064'}
Adding cut for subtour: {'140'}
Adding cut for subtour: {'135', '136'}
Adding cut for subtour: {'003'}
Adding cut for subtour: {'126', '029', '028'}
Adding cut for subtour: {'268'}
Adding cut for subtour: {'279', '278'}
Adding cut for subtour: {'135', '269'}
Adding cut for subtour: {'144'}
Adding cut for subtour: {'149'}
Continue solving

Iteration 9: Finding subtours
Adding 11 cuts
../_images/b0dc9e0beec0bdf7a68100d3044508e3280c6e2df6c505666dc4064d5f0fe9ce.png
Adding cut for subtour: {'089', '171', '088', '100', '039', '166', '052', '082', '090', '161', '094', '098', '078', '164', '168', '030', '125', '031', '157', '124', '121', '045', '101', '123', '038', '093', '097', '057', '071', '172', '096', '047', '040', '159', '105', '058', '075', '111', '051', '173', '122', '163', '112', '169', '099', '036', '073', '174', '167', '119', '069', '034', '095', '102', '079', '104', '072', '042', '081', '103', '043', '158', '083', '160', '175', '080', '091', '109', '067', '110', '070', '050', '044', '120', '046', '162', '107', '077', '061', '076', '033', '060', '054', '035', '108', '049', '170', '032', '053', '048', '037', '092', '041', '165', '106', '059', '056', '055', '068', '074'}
Adding cut for subtour: {'127', '152', '178', '185', '176', '130', '201', '191', '196', '200', '184', '199', '180', '192', '156', '187', '129', '186', '153', '190', '197', '198', '179', '154', '188', '177', '020', '151', '181', '189', '193', '195', '128', '021', '194', '150', '131', '182', '183', '155'}
Adding cut for subtour: {'208', '203', '252', '202', '205', '207', '253', '206', '204'}
Adding cut for subtour: {'001', '276', '252', '003', '005', '270', '016', '209', '017', '019', '269', '263', '255', '211', '280', '271', '256', '219', '259', '227', '257', '137', '228', '225', '226', '264', '224', '134', '267', '254', '210', '002', '213', '214', '260', '230', '244', '218', '250', '258', '220', '272', '216', '221', '133', '277', '004', '274', '268', '265', '247', '217', '251', '215', '273', '018', '266', '222', '249', '248', '132', '229', '262', '135', '223', '279', '278', '212', '136', '261', '275'}
Adding cut for subtour: {'145', '144', '146', '143', '142', '147', '141'}
Adding cut for subtour: {'240', '232', '236', '243', '239', '235', '246', '241', '237', '233', '238', '231', '234', '242', '245'}
Adding cut for subtour: {'006', '008', '026', '010', '023', '022', '027', '025', '024', '013', '012', '014', '011', '009', '007', '015'}
Adding cut for subtour: {'062', '087', '115', '063', '084', '065', '113', '116', '085', '117', '118', '114', '066', '086', '064'}
Adding cut for subtour: {'140'}
Adding cut for subtour: {'140', '148', '149', '138', '139', '141'}
Adding cut for subtour: {'126', '029', '028'}
Continue solving

Iteration 10: Finding subtours
Adding 10 cuts
../_images/645e6fb86c36e753a5034a6fc4c01bb9bc948c504acfd4a66ae455e1de2a00ca.png
Adding cut for subtour: {'089', '171', '088', '100', '039', '166', '052', '082', '090', '161', '094', '098', '078', '164', '168', '030', '125', '031', '157', '124', '121', '045', '101', '123', '038', '093', '097', '057', '071', '172', '096', '047', '040', '159', '105', '058', '075', '111', '051', '173', '122', '163', '112', '169', '099', '036', '073', '174', '167', '119', '069', '034', '087', '095', '102', '079', '104', '072', '042', '081', '103', '043', '158', '083', '160', '175', '080', '091', '109', '067', '110', '070', '050', '044', '120', '046', '162', '107', '077', '076', '033', '060', '054', '035', '108', '049', '170', '032', '053', '048', '037', '092', '041', '165', '059', '056', '055', '068', '074', '084'}
Adding cut for subtour: {'127', '203', '252', '152', '178', '185', '176', '205', '130', '201', '204', '191', '196', '200', '184', '199', '180', '145', '254', '192', '156', '187', '129', '186', '153', '190', '197', '198', '144', '179', '207', '154', '188', '177', '208', '020', '151', '181', '189', '193', '195', '128', '021', '194', '150', '253', '131', '202', '206', '182', '183', '155'}
Adding cut for subtour: {'001', '276', '003', '005', '270', '016', '017', '019', '269', '263', '255', '280', '271', '256', '259', '257', '137', '264', '134', '267', '254', '002', '260', '258', '272', '133', '277', '004', '274', '268', '265', '273', '018', '266', '132', '262', '135', '279', '278', '136', '261', '275'}
Adding cut for subtour: {'209', '211', '219', '228', '227', '225', '226', '224', '210', '213', '214', '230', '244', '218', '250', '220', '216', '221', '247', '217', '251', '215', '222', '249', '248', '229', '212', '223'}
Adding cut for subtour: {'140', '148', '146', '147', '143', '142', '149', '138', '139', '141'}
Adding cut for subtour: {'240', '232', '236', '243', '239', '235', '246', '241', '237', '233', '238', '231', '234', '242', '245'}
Adding cut for subtour: {'006', '008', '026', '010', '023', '022', '027', '025', '024', '013', '012', '014', '011', '009', '007', '015'}
Adding cut for subtour: {'061', '062', '115', '063', '065', '113', '116', '085', '117', '118', '114', '066', '086', '064'}
Adding cut for subtour: {'126', '029', '028'}
Adding cut for subtour: {'106'}
Continue solving

Iteration 11: Finding subtours
Adding 17 cuts
../_images/c931b5914a89e964eae4c335b63c8ec6187d23f6eaf027c010f51c5eafdc28de.png
Adding cut for subtour: {'089', '104', '109', '110', '092', '108', '090', '103', '102', '091'}
Adding cut for subtour: {'127', '171', '100', '130', '039', '166', '052', '180', '161', '094', '098', '078', '164', '168', '030', '125', '031', '157', '124', '121', '101', '123', '038', '093', '097', '057', '071', '152', '172', '178', '185', '176', '096', '047', '040', '159', '058', '075', '051', '129', '122', '163', '186', '153', '169', '099', '179', '036', '073', '154', '188', '151', '167', '021', '069', '119', '034', '095', '072', '187', '158', '160', '175', '177', '020', '067', '181', '070', '050', '150', '182', '155', '120', '162', '077', '076', '033', '184', '054', '035', '049', '170', '032', '156', '053', '048', '037', '165', '068', '056', '055', '074', '128', '131', '183'}
Adding cut for subtour: {'208', '203', '252', '200', '199', '198', '145', '202', '205', '144', '143', '147', '207', '201', '253', '146', '206', '204'}
Adding cut for subtour: {'001', '276', '252', '003', '005', '270', '016', '209', '017', '019', '269', '263', '255', '211', '280', '271', '256', '219', '259', '227', '257', '137', '228', '225', '226', '264', '224', '134', '267', '254', '210', '002', '213', '214', '260', '230', '244', '218', '250', '258', '220', '272', '216', '221', '133', '277', '004', '274', '268', '265', '247', '217', '251', '215', '273', '018', '266', '222', '249', '248', '132', '229', '262', '135', '223', '279', '278', '212', '136', '261', '275'}
Adding cut for subtour: {'087', '112', '083', '088', '111', '084', '113', '114'}
Adding cut for subtour: {'140', '148', '149', '138', '142', '139', '141'}
Adding cut for subtour: {'240', '232', '236', '243', '239', '235', '246', '241', '237', '233', '238', '231', '234', '242', '245'}
Adding cut for subtour: {'190', '193', '197', '195', '194', '192', '191', '196'}
Adding cut for subtour: {'006', '008', '026', '010', '023', '022', '027', '025', '024', '013', '012', '014', '011', '009', '007', '015'}
Adding cut for subtour: {'061', '062', '115', '063', '065', '116', '085', '117', '118', '066', '086', '064'}
Adding cut for subtour: {'045', '046'}
Adding cut for subtour: {'106', '105', '107', '173', '174'}
Adding cut for subtour: {'126', '029', '028'}
Adding cut for subtour: {'079', '082', '081', '080'}
Adding cut for subtour: {'041', '043', '044', '042'}
Adding cut for subtour: {'045', '044', '059', '060'}
Adding cut for subtour: {'189'}
Continue solving

Iteration 12: Finding subtours
Adding 5 cuts
../_images/0c6e205a699e6fa76206970fbd4e4695fc47146a6e8bd94c43301f9e5ca38e25.png
Adding cut for subtour: {'089', '171', '088', '100', '039', '166', '052', '082', '090', '114', '161', '094', '065', '098', '078', '164', '168', '030', '125', '031', '157', '124', '121', '085', '066', '045', '101', '123', '063', '115', '038', '118', '093', '097', '062', '057', '071', '172', '096', '047', '040', '159', '105', '058', '075', '051', '117', '173', '122', '163', '112', '169', '099', '036', '073', '174', '167', '119', '069', '034', '087', '095', '102', '079', '104', '072', '042', '081', '103', '043', '158', '083', '160', '175', '113', '116', '080', '086', '091', '067', '070', '050', '044', '120', '046', '162', '064', '107', '077', '061', '076', '033', '060', '054', '035', '049', '170', '032', '053', '048', '037', '092', '041', '165', '106', '059', '056', '055', '068', '074', '084'}
Adding cut for subtour: {'127', '203', '252', '005', '209', '241', '205', '130', '269', '271', '196', '245', '257', '225', '264', '180', '134', '192', '002', '218', '190', '008', '250', '258', '027', '221', '012', '247', '133', '265', '277', '014', '208', '023', '025', '202', '212', '206', '261', '237', '152', '178', '185', '270', '176', '211', '219', '259', '246', '254', '213', '129', '244', '186', '153', '197', '272', '179', '011', '238', '004', '154', '188', '240', '006', '222', '018', '273', '151', '229', '235', '262', '195', '021', '194', '135', '231', '234', '242', '001', '276', '236', '003', '017', '019', '009', '024', '255', '280', '256', '227', '137', '226', '010', '210', '013', '214', '187', '230', '126', '216', '022', '268', '251', '177', '020', '189', '181', '232', '193', '150', '182', '223', '155', '275', '243', '016', '201', '263', '204', '191', '228', '015', '184', '026', '224', '267', '233', '156', '260', '239', '198', '220', '207', '274', '217', '215', '266', '029', '248', '249', '132', '128', '007', '279', '278', '131', '253', '136', '028', '183'}
Adding cut for subtour: {'140', '148', '200', '199', '145', '144', '147', '143', '142', '146', '149', '138', '139', '141'}
Adding cut for subtour: {'111'}
Adding cut for subtour: {'110', '109', '108'}
Continue solving

Iteration 13: Finding subtours
Adding 3 cuts
../_images/b03a1ab9d615ab91afcbc5a7c38416109fc080836e1b746c4e67ca26ea4582cd.png
Adding cut for subtour: {'089', '171', '088', '100', '039', '166', '052', '082', '090', '114', '161', '094', '065', '098', '078', '164', '168', '030', '125', '031', '157', '124', '121', '085', '066', '045', '101', '123', '063', '115', '038', '118', '093', '097', '062', '057', '071', '172', '096', '047', '040', '159', '105', '058', '075', '111', '051', '117', '173', '122', '163', '112', '169', '099', '036', '073', '174', '167', '119', '069', '034', '087', '095', '102', '079', '104', '072', '042', '081', '103', '043', '158', '083', '160', '175', '113', '116', '080', '086', '091', '109', '067', '110', '070', '050', '044', '120', '046', '162', '064', '107', '077', '061', '076', '033', '060', '054', '035', '108', '049', '170', '032', '053', '048', '037', '092', '041', '165', '059', '056', '055', '068', '074', '084'}
Adding cut for subtour: {'127', '203', '252', '005', '209', '241', '205', '130', '269', '141', '271', '196', '245', '257', '225', '199', '264', '180', '134', '192', '002', '218', '190', '008', '250', '258', '027', '221', '012', '247', '133', '265', '139', '277', '014', '208', '023', '025', '202', '212', '206', '261', '237', '152', '178', '185', '270', '176', '211', '219', '259', '246', '145', '254', '213', '129', '244', '186', '153', '197', '144', '272', '179', '011', '238', '004', '154', '188', '240', '006', '140', '222', '148', '018', '151', '273', '229', '235', '262', '195', '021', '194', '135', '231', '234', '242', '001', '276', '236', '003', '017', '143', '019', '009', '024', '255', '280', '256', '227', '137', '226', '010', '210', '013', '214', '187', '230', '126', '216', '022', '142', '268', '251', '177', '020', '189', '181', '232', '193', '150', '182', '223', '155', '275', '243', '016', '147', '201', '263', '204', '191', '228', '015', '200', '184', '026', '224', '267', '233', '138', '156', '260', '239', '198', '220', '149', '207', '274', '217', '215', '266', '029', '248', '249', '132', '128', '146', '007', '279', '278', '131', '253', '136', '028', '183'}
Adding cut for subtour: {'106'}
Continue solving

Iteration 14: Finding subtours
Adding 8 cuts
../_images/8682743b0100eb590c7212a213df7ccc25ac4272b5b2a5b595125566fe84ef1c.png
Adding cut for subtour: {'089', '171', '127', '100', '130', '196', '199', '166', '180', '090', '192', '161', '094', '098', '164', '168', '125', '157', '190', '124', '121', '101', '123', '093', '097', '152', '172', '178', '185', '176', '096', '159', '105', '173', '122', '129', '163', '186', '153', '169', '099', '197', '179', '154', '188', '174', '151', '167', '195', '021', '194', '119', '095', '102', '104', '103', '187', '158', '126', '160', '175', '091', '177', '020', '189', '181', '193', '150', '182', '155', '120', '201', '162', '107', '191', '200', '184', '108', '170', '156', '092', '198', '165', '106', '128', '131', '183'}
Adding cut for subtour: {'208', '203', '252', '202', '205', '207', '253', '206', '204'}
Adding cut for subtour: {'005', '209', '241', '269', '271', '245', '257', '225', '264', '134', '002', '218', '250', '258', '221', '133', '277', '265', '247', '212', '261', '237', '270', '211', '219', '259', '246', '254', '213', '244', '272', '238', '004', '240', '273', '018', '222', '229', '235', '262', '135', '231', '234', '242', '001', '276', '236', '003', '017', '019', '255', '280', '256', '227', '137', '226', '210', '214', '230', '216', '268', '251', '232', '223', '275', '243', '016', '263', '228', '224', '267', '233', '260', '239', '220', '274', '217', '215', '266', '249', '248', '132', '279', '278', '136'}
Adding cut for subtour: {'057', '087', '071', '044', '088', '009', '024', '046', '079', '047', '077', '040', '015', '072', '039', '042', '076', '081', '058', '075', '054', '052', '060', '082', '051', '035', '049', '026', '010', '013', '032', '053', '048', '078', '043', '037', '033', '030', '031', '083', '008', '022', '027', '011', '036', '012', '073', '080', '041', '006', '029', '045', '067', '059', '068', '056', '055', '070', '074', '084', '050', '023', '007', '025', '038', '069', '014', '028', '034'}
Adding cut for subtour: {'140', '148', '145', '144', '147', '143', '142', '146', '149', '138', '139', '141'}
Adding cut for subtour: {'109', '110', '113', '116', '085', '086'}
Adding cut for subtour: {'062', '061', '114', '112', '115', '063', '111', '117', '118', '066', '064'}
Adding cut for subtour: {'065'}
Continue solving

Iteration 15: Finding subtours
Adding 26 cuts
../_images/a48101a6ea56abfb3787a02da08e99dbf294df5218778d03471b762b38513012.png
Adding cut for subtour: {'089', '171', '152', '172', '185', '100', '178', '095', '120', '176', '096', '102', '191', '107', '159', '104', '105', '184', '166', '180', '108', '090', '192', '103', '170', '173', '156', '187', '094', '129', '098', '122', '161', '158', '168', '186', '126', '153', '125', '157', '169', '190', '099', '124', '160', '175', '092', '179', '121', '154', '091', '188', '174', '177', '165', '183', '106', '189', '151', '181', '101', '167', '193', '123', '110', '109', '128', '150', '119', '093', '182', '097', '155'}
Adding cut for subtour: {'127'}
Adding cut for subtour: {'203', '252', '270', '209', '205', '147', '143', '269', '201', '263', '141', '255', '211', '204', '256', '219', '259', '227', '257', '137', '228', '225', '200', '199', '226', '264', '224', '145', '134', '267', '254', '138', '210', '213', '214', '260', '230', '244', '218', '250', '198', '258', '144', '149', '216', '142', '220', '207', '221', '265', '139', '268', '247', '217', '251', '215', '208', '140', '266', '148', '222', '249', '248', '229', '262', '146', '135', '253', '202', '212', '136', '206', '261', '223'}
Adding cut for subtour: {'001', '243', '005', '241', '002', '280', '242'}
Adding cut for subtour: {'057', '087', '071', '044', '088', '079', '077', '040', '072', '039', '042', '076', '081', '058', '075', '060', '082', '035', '043', '078', '037', '083', '036', '073', '080', '041', '059', '067', '068', '070', '074', '084', '038', '069', '034'}
Adding cut for subtour: {'018', '132', '017', '019', '130', '131', '133'}
Adding cut for subtour: {'006', '273', '276', '005', '008', '272', '007', '009', '277', '004', '274', '271', '275'}
Adding cut for subtour: {'240', '232', '236', '239', '235', '246', '237', '233', '238', '231', '234', '245'}
Adding cut for subtour: {'045', '054', '055', '052', '046', '053'}
Adding cut for subtour: {'030', '031', '033', '032'}
Adding cut for subtour: {'026', '010', '023', '022', '027', '025', '024', '013', '012', '014', '011', '015'}
Adding cut for subtour: {'065', '085', '066'}
Adding cut for subtour: {'062', '063', '064'}
Adding cut for subtour: {'061', '115', '111', '117', '118', '114'}
Adding cut for subtour: {'112'}
Adding cut for subtour: {'194', '197', '195', '196'}
Adding cut for subtour: {'003'}
Adding cut for subtour: {'086', '113', '116'}
Adding cut for subtour: {'130', '020', '021'}
Adding cut for subtour: {'048', '047', '050', '049'}
Adding cut for subtour: {'162', '164', '163'}
Adding cut for subtour: {'056'}
Adding cut for subtour: {'279', '278'}
Adding cut for subtour: {'028', '029'}
Adding cut for subtour: {'051'}
Adding cut for subtour: {'016'}
Continue solving

Iteration 16: Finding subtours
Adding 5 cuts
../_images/8252bbeea45113842984de0fc6af058387b21955c3fe89332544f4140dd4cdd8.png
Adding cut for subtour: {'089', '171', '127', '203', '252', '005', '088', '100', '209', '241', '205', '269', '141', '271', '196', '245', '257', '039', '225', '199', '166', '264', '180', '082', '134', '090', '192', '161', '094', '098', '078', '164', '168', '030', '125', '218', '031', '157', '190', '008', '124', '250', '258', '121', '221', '012', '247', '133', '265', '139', '277', '014', '027', '208', '045', '101', '123', '023', '025', '038', '202', '212', '093', '206', '261', '097', '237', '057', '071', '152', '172', '178', '185', '270', '176', '096', '211', '219', '040', '259', '159', '058', '105', '075', '246', '145', '254', '213', '173', '122', '129', '244', '163', '186', '153', '169', '099', '197', '144', '272', '179', '011', '036', '238', '004', '073', '154', '188', '240', '174', '006', '140', '222', '148', '018', '151', '273', '167', '229', '235', '262', '195', '194', '135', '119', '069', '231', '234', '034', '242', '001', '276', '087', '236', '003', '095', '017', '143', '019', '009', '024', '102', '255', '280', '079', '256', '227', '072', '137', '104', '042', '081', '226', '010', '210', '013', '103', '214', '187', '230', '043', '158', '126', '083', '160', '175', '216', '022', '142', '080', '268', '091', '251', '177', '109', '189', '232', '181', '067', '193', '110', '070', '150', '182', '223', '155', '275', '044', '243', '016', '120', '147', '201', '046', '263', '162', '204', '191', '228', '015', '077', '107', '076', '200', '184', '033', '060', '026', '224', '035', '108', '267', '233', '138', '170', '032', '156', '260', '037', '239', '092', '198', '220', '149', '207', '274', '041', '217', '165', '215', '266', '029', '106', '248', '249', '059', '132', '055', '056', '068', '074', '128', '084', '146', '007', '279', '278', '253', '136', '028', '183'}
Adding cut for subtour: {'020', '130', '131', '021'}
Adding cut for subtour: {'054', '052', '050', '051', '049', '048', '053', '047'}
Adding cut for subtour: {'061', '062', '112', '115', '063', '111', '065', '113', '116', '085', '117', '118', '114', '066', '086', '064'}
Adding cut for subtour: {'002'}
Continue solving

Iteration 17: Finding subtours
Adding 2 cuts
../_images/e0d90c0530c8aed733f3e12e90008ea46f99d39c1cfc5d737d8643d7bbaf6e34.png
Adding cut for subtour: {'089', '171', '127', '203', '252', '005', '088', '100', '209', '241', '205', '269', '130', '141', '271', '196', '245', '257', '039', '225', '199', '166', '052', '264', '180', '082', '134', '090', '192', '114', '161', '094', '065', '098', '078', '164', '168', '030', '125', '218', '031', '157', '190', '008', '124', '250', '258', '121', '221', '012', '247', '133', '265', '139', '277', '014', '027', '066', '208', '045', '101', '123', '115', '063', '023', '025', '038', '118', '202', '212', '093', '206', '261', '097', '237', '062', '057', '071', '152', '172', '178', '185', '270', '176', '096', '211', '219', '047', '040', '259', '159', '105', '058', '075', '246', '051', '145', '111', '254', '213', '117', '173', '122', '129', '244', '163', '186', '153', '112', '169', '099', '197', '144', '272', '179', '011', '036', '238', '004', '073', '154', '188', '240', '174', '006', '140', '222', '148', '018', '151', '273', '085', '167', '229', '235', '262', '195', '021', '194', '135', '119', '069', '231', '234', '034', '242', '001', '276', '087', '236', '003', '095', '017', '143', '019', '009', '024', '102', '255', '280', '079', '256', '227', '104', '137', '072', '042', '081', '226', '010', '210', '103', '013', '214', '187', '230', '043', '158', '126', '083', '160', '175', '216', '022', '142', '116', '113', '080', '268', '091', '086', '251', '177', '109', '189', '232', '181', '020', '193', '067', '110', '070', '050', '150', '182', '223', '155', '275', '044', '243', '016', '120', '147', '201', '046', '263', '162', '064', '204', '107', '191', '228', '015', '077', '061', '076', '200', '184', '033', '060', '026', '054', '224', '035', '108', '049', '267', '233', '138', '170', '032', '156', '260', '053', '048', '037', '239', '092', '198', '220', '149', '207', '274', '041', '217', '165', '215', '266', '029', '106', '248', '249', '059', '132', '055', '056', '068', '074', '128', '084', '146', '007', '279', '278', '131', '253', '136', '028', '183'}

Iteration 18: Finding subtours
Adding 4 cuts
../_images/17eee763ffff0f05b8f89dc70b42f2fbc93381e4ab2611381a3798bbf48b9c21.png
Adding cut for subtour: {'089', '171', '127', '203', '252', '005', '088', '100', '209', '241', '205', '130', '269', '141', '271', '245', '196', '257', '039', '225', '199', '166', '264', '180', '082', '134', '090', '192', '002', '114', '161', '065', '098', '078', '164', '168', '030', '125', '218', '031', '157', '190', '008', '124', '250', '258', '121', '027', '012', '133', '277', '265', '247', '221', '139', '066', '085', '208', '045', '101', '123', '063', '115', '023', '025', '038', '118', '202', '212', '093', '206', '261', '062', '057', '071', '152', '172', '270', '185', '178', '176', '211', '219', '040', '259', '159', '105', '058', '075', '111', '145', '254', '213', '117', '173', '122', '129', '244', '163', '186', '153', '112', '169', '099', '197', '144', '272', '179', '011', '036', '073', '004', '154', '188', '240', '174', '006', '273', '018', '222', '148', '140', '151', '167', '229', '262', '195', '021', '194', '135', '119', '069', '034', '242', '001', '276', '087', '003', '017', '019', '009', '024', '143', '102', '255', '280', '079', '256', '227', '104', '137', '072', '042', '081', '226', '010', '210', '103', '013', '214', '187', '230', '043', '158', '126', '083', '160', '175', '022', '216', '142', '113', '116', '080', '268', '091', '086', '251', '177', '109', '020', '189', '181', '067', '193', '110', '070', '150', '182', '275', '223', '155', '044', '243', '016', '120', '147', '201', '263', '162', '064', '204', '107', '015', '228', '191', '061', '077', '076', '200', '184', '033', '060', '026', '224', '035', '108', '267', '138', '170', '032', '156', '260', '037', '092', '198', '220', '149', '207', '274', '041', '217', '165', '215', '266', '029', '106', '249', '248', '059', '132', '056', '068', '074', '128', '084', '146', '007', '253', '279', '278', '131', '014', '136', '028', '183'}
Adding cut for subtour: {'054', '055', '052', '050', '051', '049', '048', '046', '053', '047'}
Adding cut for subtour: {'094', '095', '097', '096'}
Adding cut for subtour: {'232', '236', '239', '235', '246', '233', '238', '231', '234', '237'}
Continue solving

Iteration 19: Finding subtours
Adding 3 cuts
../_images/5b7167950258ed67c68207339841434ad8716caa009b8707747b17bd133cb859.png
Adding cut for subtour: {'089', '171', '127', '005', '088', '100', '269', '271', '196', '039', '199', '166', '052', '082', '180', '134', '090', '192', '002', '114', '161', '094', '065', '098', '078', '164', '168', '030', '125', '031', '157', '190', '008', '124', '121', '027', '012', '085', '277', '066', '139', '045', '101', '123', '115', '063', '023', '025', '038', '118', '093', '097', '062', '057', '071', '152', '172', '178', '185', '270', '176', '096', '047', '040', '159', '105', '058', '075', '111', '051', '145', '117', '173', '122', '129', '163', '186', '153', '112', '169', '099', '197', '272', '179', '011', '036', '073', '004', '154', '188', '174', '006', '273', '148', '151', '167', '195', '194', '135', '119', '069', '034', '001', '276', '087', '003', '095', '009', '024', '102', '280', '079', '104', '137', '072', '042', '081', '010', '103', '013', '187', '043', '158', '126', '083', '160', '175', '022', '113', '116', '080', '268', '091', '086', '177', '109', '189', '067', '181', '193', '110', '070', '050', '150', '182', '275', '155', '044', '016', '120', '046', '162', '064', '107', '191', '015', '077', '061', '076', '033', '184', '060', '054', '026', '035', '108', '049', '267', '138', '170', '032', '156', '053', '048', '037', '092', '198', '149', '274', '041', '165', '029', '106', '059', '056', '055', '068', '074', '128', '084', '146', '007', '279', '278', '014', '136', '028', '183'}
Adding cut for subtour: {'203', '243', '252', '236', '209', '241', '205', '143', '147', '201', '263', '141', '255', '219', '204', '256', '228', '259', '245', '257', '227', '234', '225', '200', '226', '264', '246', '224', '254', '233', '210', '213', '214', '260', '230', '244', '218', '239', '231', '250', '258', '144', '220', '216', '142', '221', '207', '238', '265', '247', '240', '217', '251', '215', '208', '140', '266', '222', '249', '248', '232', '229', '235', '262', '211', '223', '253', '202', '212', '206', '261', '242', '237'}
Adding cut for subtour: {'018', '020', '132', '017', '021', '019', '133', '130', '131'}
Continue solving

Iteration 20: Finding subtours
Adding 3 cuts
../_images/38930a509867df1f7a65eab74c236a7b71f8291ae305e263fdad62aedd13f886.png
Adding cut for subtour: {'089', '171', '127', '203', '252', '005', '088', '100', '209', '241', '205', '130', '269', '141', '271', '196', '245', '257', '039', '225', '199', '166', '264', '180', '082', '134', '090', '192', '002', '114', '161', '094', '065', '098', '078', '164', '168', '030', '125', '218', '031', '157', '190', '008', '124', '250', '258', '121', '221', '012', '247', '133', '265', '139', '277', '014', '027', '066', '208', '101', '123', '063', '115', '023', '025', '038', '118', '202', '212', '093', '206', '261', '097', '237', '062', '057', '071', '152', '172', '178', '185', '270', '176', '096', '211', '219', '040', '259', '159', '105', '058', '075', '246', '111', '254', '213', '117', '173', '122', '129', '244', '163', '186', '153', '112', '169', '099', '197', '272', '179', '011', '036', '238', '004', '073', '154', '188', '240', '174', '006', '222', '140', '148', '018', '151', '273', '085', '167', '229', '235', '262', '195', '021', '194', '135', '119', '069', '231', '234', '034', '242', '001', '276', '087', '236', '003', '095', '017', '019', '009', '024', '102', '255', '280', '079', '256', '227', '104', '137', '072', '042', '081', '226', '010', '210', '103', '013', '214', '187', '230', '043', '158', '126', '083', '160', '175', '216', '022', '142', '113', '116', '080', '268', '091', '086', '251', '177', '109', '020', '189', '181', '232', '193', '067', '110', '070', '150', '182', '223', '155', '275', '044', '243', '016', '120', '147', '201', '263', '162', '064', '204', '107', '191', '228', '015', '061', '077', '076', '200', '184', '033', '060', '026', '224', '035', '108', '267', '233', '138', '170', '032', '156', '260', '037', '239', '092', '198', '220', '149', '207', '274', '041', '217', '165', '215', '266', '029', '106', '248', '249', '059', '132', '068', '074', '128', '084', '007', '279', '278', '131', '253', '136', '028', '183'}
Adding cut for subtour: {'045', '056', '055', '052', '054', '050', '051', '049', '048', '046', '053', '047'}
Adding cut for subtour: {'144', '143', '146', '145'}
Continue solving

Iteration 21: Finding subtours
No subtours detected. Not adding any cut

Iteration 22: Finding subtours
Adding 2 cuts
../_images/fb16890bfa6db59fb508878cd75b17b7d8d173ac717220322e26943a70fc9a43.png
Adding cut for subtour: {'089', '171', '127', '203', '005', '088', '100', '209', '241', '205', '130', '269', '141', '271', '196', '245', '257', '039', '225', '199', '166', '052', '264', '180', '082', '134', '090', '192', '002', '114', '161', '094', '065', '098', '078', '164', '168', '030', '125', '218', '031', '157', '190', '008', '124', '250', '258', '121', '221', '012', '247', '133', '265', '139', '277', '027', '066', '085', '208', '045', '101', '123', '063', '115', '023', '025', '038', '118', '202', '212', '093', '206', '261', '097', '237', '062', '057', '071', '152', '172', '178', '185', '270', '176', '096', '211', '219', '047', '040', '259', '159', '105', '058', '075', '246', '051', '145', '111', '213', '117', '173', '122', '129', '244', '163', '186', '153', '112', '169', '099', '197', '144', '272', '179', '011', '036', '238', '004', '073', '154', '188', '240', '174', '006', '140', '222', '148', '018', '151', '273', '167', '229', '235', '262', '195', '021', '194', '135', '119', '069', '231', '234', '034', '242', '001', '276', '087', '236', '003', '095', '017', '143', '019', '009', '024', '102', '280', '079', '256', '227', '104', '137', '072', '042', '081', '226', '010', '210', '103', '013', '214', '187', '230', '043', '158', '126', '083', '160', '175', '216', '022', '142', '116', '113', '080', '268', '091', '086', '251', '177', '109', '189', '232', '181', '020', '193', '067', '110', '070', '050', '150', '182', '223', '155', '275', '044', '243', '016', '120', '147', '201', '046', '263', '162', '064', '204', '107', '191', '228', '015', '061', '077', '076', '200', '184', '033', '060', '026', '054', '224', '035', '108', '049', '267', '233', '138', '170', '032', '156', '260', '053', '048', '037', '239', '092', '198', '220', '149', '207', '274', '041', '217', '165', '215', '266', '029', '106', '248', '249', '059', '132', '055', '056', '068', '074', '128', '084', '146', '007', '279', '278', '131', '014', '136', '028', '183'}

Iteration 23: Finding subtours
No subtours detected. Not adding any cut

Iteration 24: Finding subtours
Adding 2 cuts
../_images/b715fe05167476eb3605e3159a491e74b1fe67f9b97a2b08b24c9e68fa43334c.png
Adding cut for subtour: {'089', '171', '127', '005', '088', '100', '271', '039', '166', '052', '082', '090', '002', '114', '161', '094', '065', '098', '078', '164', '168', '030', '125', '031', '157', '008', '124', '121', '027', '012', '085', '277', '066', '045', '101', '123', '115', '063', '023', '025', '038', '118', '093', '097', '062', '057', '071', '172', '096', '047', '040', '159', '105', '058', '075', '111', '051', '117', '173', '122', '163', '112', '169', '099', '272', '011', '036', '073', '004', '174', '006', '273', '018', '167', '021', '119', '069', '034', '001', '276', '087', '003', '095', '017', '019', '009', '024', '102', '280', '079', '104', '072', '042', '081', '010', '103', '013', '043', '158', '126', '083', '160', '175', '022', '113', '116', '080', '086', '091', '109', '020', '067', '110', '070', '050', '275', '044', '016', '120', '046', '162', '064', '107', '015', '077', '061', '076', '033', '060', '054', '026', '035', '108', '049', '170', '032', '053', '048', '037', '092', '274', '041', '165', '029', '106', '059', '056', '055', '068', '074', '128', '084', '007', '279', '278', '014', '028'}

Iteration 25: Finding subtours
Adding 2 cuts
../_images/4c31311079a677c26dc725195b624efb6435d9fcc5ef749097ab85744add2435.png
Adding cut for subtour: {'089', '171', '127', '203', '005', '088', '100', '241', '205', '130', '269', '141', '271', '196', '245', '257', '039', '225', '199', '166', '052', '264', '180', '082', '134', '090', '192', '002', '114', '161', '094', '065', '098', '078', '164', '168', '030', '125', '218', '031', '157', '190', '008', '124', '250', '258', '121', '221', '012', '247', '133', '265', '139', '277', '027', '066', '085', '045', '101', '123', '063', '115', '023', '025', '038', '118', '202', '212', '093', '206', '261', '097', '237', '062', '057', '071', '152', '172', '178', '185', '270', '176', '096', '211', '219', '047', '040', '259', '159', '105', '058', '075', '246', '051', '145', '111', '213', '117', '173', '122', '129', '244', '163', '186', '153', '112', '169', '099', '197', '144', '272', '179', '011', '036', '238', '004', '073', '154', '188', '240', '174', '006', '222', '140', '148', '018', '151', '273', '167', '229', '235', '262', '195', '021', '194', '135', '119', '069', '231', '234', '034', '242', '001', '276', '087', '236', '003', '095', '017', '143', '019', '009', '024', '102', '280', '079', '256', '227', '104', '137', '072', '042', '081', '226', '010', '210', '103', '013', '214', '187', '230', '043', '158', '126', '083', '160', '175', '216', '022', '142', '113', '116', '080', '268', '091', '086', '251', '177', '109', '020', '189', '181', '232', '193', '067', '110', '070', '050', '150', '182', '223', '155', '275', '044', '243', '016', '120', '147', '201', '046', '263', '162', '064', '204', '107', '191', '228', '015', '061', '077', '076', '200', '184', '033', '060', '026', '054', '224', '035', '108', '049', '267', '233', '138', '170', '032', '156', '260', '053', '048', '037', '239', '092', '198', '220', '149', '207', '274', '041', '217', '165', '215', '266', '029', '106', '248', '249', '059', '132', '055', '056', '068', '074', '128', '084', '146', '007', '279', '278', '131', '014', '136', '028', '183'}

Iteration 26: Finding subtours
No subtours detected. Not adding any cut
Gurobi 10.0.2: optimal solution; objective 2586.76964756316
3196 simplex iterations
516 branching nodes
absmipgap=9.09495e-13, relmipgap=0

Get the solution, print it and display it

# Get the solution into ARCS
ARCS = ampl.get_data("{(i,j) in PAIRS : X[i,j] > 0} X[i,j];")
ARCS = set([(i, j) for (i, j, k) in ARCS.toList()])

# Display it
tours = findSubTours(ARCS, NODES)
for st in tours:
    print(st)
plotTours(tours, CPOINTS)
['089', '090', '091', '092', '093', '094', '095', '096', '097', '098', '099', '100', '101', '102', '103', '104', '105', '106', '107', '174', '173', '172', '171', '170', '169', '168', '167', '166', '165', '164', '163', '162', '161', '175', '160', '159', '158', '157', '119', '120', '121', '122', '123', '124', '125', '030', '126', '127', '128', '021', '020', '019', '132', '131', '130', '129', '154', '155', '153', '156', '152', '151', '177', '178', '150', '179', '180', '176', '181', '182', '183', '184', '185', '186', '187', '188', '189', '190', '191', '192', '193', '197', '194', '195', '196', '201', '198', '199', '200', '202', '203', '204', '205', '206', '207', '208', '209', '210', '211', '212', '213', '214', '215', '216', '217', '218', '219', '220', '221', '222', '223', '224', '225', '226', '227', '228', '229', '230', '251', '250', '247', '245', '246', '231', '232', '233', '234', '235', '236', '237', '238', '239', '240', '241', '244', '243', '242', '002', '001', '280', '003', '279', '278', '248', '249', '256', '255', '252', '253', '254', '257', '258', '259', '260', '261', '262', '263', '264', '265', '266', '140', '141', '142', '143', '144', '145', '146', '147', '148', '149', '139', '138', '137', '267', '268', '136', '135', '269', '270', '134', '133', '018', '017', '016', '271', '272', '273', '274', '275', '276', '277', '004', '005', '006', '007', '008', '009', '010', '011', '012', '013', '015', '014', '024', '023', '025', '022', '026', '027', '028', '029', '031', '032', '033', '034', '035', '036', '037', '038', '039', '040', '041', '042', '043', '060', '061', '118', '062', '063', '059', '044', '045', '046', '047', '048', '049', '050', '051', '052', '053', '054', '055', '056', '057', '058', '064', '065', '066', '067', '068', '069', '070', '071', '072', '073', '074', '075', '076', '077', '078', '079', '080', '081', '082', '083', '084', '085', '086', '116', '117', '115', '114', '113', '087', '088', '112', '111', '110', '108', '109']
../_images/2d8539483582107d10da2bfd9ad94fa5d0ef9c527bfe9bbfeaf9cec59cde8e19.png
ampl.get_value("Tour_Length")
2586.769647563161
time() - t0  # seconds
25.828405618667603