-1

I found code for the traveling salesman algorithm in python. But I want a different version, where the salesman will not return to the starting position. The path should be the shortest. Please suggest the solution.

I also tried the Held-Karp algorithm (this uses dynamic programming to solve TSP), but it returns a path that is not the shortest.

import json

import flask
import numpy as np
from flask import request, jsonify
from ortools.constraint_solver import routing_enums_pb2
from ortools.constraint_solver import pywrapcp

app = flask.Flask(__name__)
app.config["DEBUG"] = True


def create_data_model(distance_input_matrix):
    """Stores the data for the problem."""
    data = {'distance_matrix': distance_input_matrix, 'num_vehicles': 1, 'depot': 0}
    return data


def print_solution(manager, routing, solution):
    """Prints solution on console."""
    # print('Objective: {} miles'.format(solution.ObjectiveValue()))
    index = routing.Start(0)
    # plan_output = 'Route for vehicle 0:\n'
    plan_output = ''
    route_distance = 0
    while not routing.IsEnd(index):
        plan_output += '{}->'.format(manager.IndexToNode(index))
        previous_index = index
        index = solution.Value(routing.NextVar(index))
        route_distance += routing.GetArcCostForVehicle(previous_index, index, 0)
    plan_output += '{}'.format(manager.IndexToNode(index))
    return plan_output
    # print(plan_output)
    # plan_output += 'Route distance: {}miles\n'.format(route_distance)


def main(distance_input_matrix=None):
    """Entry point of the program."""
    # Instantiate the data problem.
    data = create_data_model(distance_input_matrix)

    # Create the routing index manager.
    manager = pywrapcp.RoutingIndexManager(len(data['distance_matrix']),
                                           data['num_vehicles'], data['depot'])

    # Create Routing Model.
    routing = pywrapcp.RoutingModel(manager)

    def distance_callback(from_index, to_index):
        """Returns the distance between the two nodes."""
        # Convert from routing variable Index to distance matrix NodeIndex.
        from_node = manager.IndexToNode(from_index)
        to_node = manager.IndexToNode(to_index)
        return data['distance_matrix'][from_node][to_node]

    transit_callback_index = routing.RegisterTransitCallback(distance_callback)

    # Define cost of each arc.
    routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)

    # Setting first solution heuristic.
    search_parameters = pywrapcp.DefaultRoutingSearchParameters()
    search_parameters.first_solution_strategy = (
        routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)

    # Solve the problem.
    solution = routing.SolveWithParameters(search_parameters)

    # Print solution on console.
    if solution:
        return print_solution(manager, routing, solution)


@app.route('/', methods=['GET'])
def home():
    return main()


@app.route('/optimize', methods=['POST'])
def optimize():
    if request.json:
        data_matrix = request.json['data']
        narrows = len(data_matrix)  # 3 rows in your example
        narcs = len(data_matrix[0])
        a = np.zeros((narrows + 1, narcs + 1), dtype='int32').tolist()
        for i in range(len(data_matrix)):
            for j in range(len(data_matrix[i])):
                a[i][j] = data_matrix[i][j]
        #result = main(a)
        result = main(data_matrix)
        r_l = result.split('->')
        #r_l.remove(str(narrows))
        return jsonify({'data': r_l})


app.run()

I also tried to add a new location which is at zero distance from all the existing points. Then solved it using TSP, and then removed the point. But it didn't give the correct result either.

trincot
  • 317,000
  • 35
  • 244
  • 286
dev21
  • 520
  • 2
  • 8
  • 22
  • Perhaps ask this question on the Operations Research StackExchange? https://or.stackexchange.com/ – Cerberton Apr 26 '21 at 09:55
  • 1
    Adding the dummy location should be the way to go. Which are the inputs you gave the program and what does it output for reproduction? – Daniel Gomez Apr 28 '21 at 15:30

1 Answers1

2

If I'm reading the code correctly, an adjustment to the distance_callback function might work:

def distance_callback(from_index, to_index):
        """Returns the distance between the two nodes."""
        # Convert from routing variable Index to distance matrix NodeIndex.
        HUGE_DISTANCE = 1e9 # Pick something suitable
        if to_index == 0:
            return HUGE_DISTANCE
        else:
            from_node = manager.IndexToNode(from_index)
            to_node = manager.IndexToNode(to_index)
            return data['distance_matrix'][from_node][to_node]

What this (hopefully) does is make the cost of returning to the start node so expensive (massive distance) that any such solutions are strongly discouraged. Note that leaving the start node is not affected - the 'natural' distance remains intact.

The above assumes that the starting node has index 0. If this is wrong the code can be adapted easily enough. If the elements of data['distance_matrix'][x][y] are something other than just a number (I couldn't tell from what you've posted) then you will need a version of the above that surgically alters only the distance/cost of returning to the starting node.

Jamie Deith
  • 706
  • 2
  • 4