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 <ccv@ampl.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