How to get routes from TSP/VRP solution?

96 views
Skip to first unread message

Jordan Makansi

unread,
Jun 12, 2023, 5:11:06 PM6/12/23
to or-tools-discuss
I would like to get access to the list of nodes in the final route found by the VRP solver.    

There is code here for getting the routes:  

I used this code exactly, but instead of returning routes, it gives me a segmentation fault. 

My code is below, notice that it is almost exactly the same as https://developers.google.com/optimization/routing/routing_tasks.  Except, at the end, I highlighted the line that causes a segmentation fault. 
Can someone help me understand how I can access the solution object to obtain the final route?  Or more general options for accessing the solution object?  

Thank you.

"""Simple Travelling Salesperson Problem (TSP) between cities."""

from ortools.constraint_solver import routing_enums_pb2
from ortools.constraint_solver import pywrapcp


def create_data_model():
    """Stores the data for the problem."""
    data = {}
    D = np.array([[0,0,0,0,0],[0, 0., 4., 5., 5.], [0, 5., 0., 3., 3.], [0, 5., 3., 0., 4.], [0, 5., 5., 5., 0.]])
    D = D.astype('int32')
    data['distance_matrix'] = D
    data['num_vehicles'] = 1
    data['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'
    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 += ' {}\n'.format(manager.IndexToNode(index))
    print(plan_output)
    plan_output += 'Route distance: {}miles\n'.format(route_distance)

def get_route_and_distance(manager, routing, solution):
    """Prints solution on console."""
    print('Objective: {} miles'.format(solution.ObjectiveValue()))
    index = routing.Start(0)
    plan_output = 'Route for vehicle 0:\n'
    route_distance = 0
    node_ix_list = []
    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)
        node_ix_list.append(manager.IndexToNode(index))
    plan_output += ' {}\n'.format(manager.IndexToNode(index))
    print(plan_output)
    plan_output += 'Route distance: {}miles\n'.format(route_distance)
    return node_ix_list


def get_routes(solution, routing, manager):
    """Get vehicle routes from a solution and store them in an array."""
    # Get vehicle routes and store them in a two dimensional array whose
    # i,j entry is the jth location visited by vehicle i along its route.
    routes = []
    for route_nbr in range(routing.vehicles()):
        index = routing.Start(route_nbr)
        route = [manager.IndexToNode(index)]
    while not routing.IsEnd(index):
        index = solution.Value(routing.NextVar(index))
        route.append(manager.IndexToNode(index))
    routes.append(route)
    return routes

def main():
    """Entry point of the program."""
    # Instantiate the data problem.
    data = create_data_model()
   
    # 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)

    return solution

data = create_data_model()

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

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

routes = get_routes(solution, routing, manager) # <---- throws a segmentation fault

Can someone help me understand how I can access the solution object to obtain the final route?  Or more general options for accessing the solution object?  

Thank you.

Mizux Seiha

unread,
Jun 13, 2023, 4:15:20 AM6/13/23
to or-tools-discuss
first check main() is correctly returning a solution before calling get_routes(...)...

```py
...
solution = main()
if solution:

  routes = get_routes(solution, routing, manager)|
else:
  print("No solution found !")
```

makansij

unread,
Jun 13, 2023, 4:18:15 PM6/13/23
to or-tools-discuss
Yes that's a good suggestion, but it's irrelevant to the question.

Mizux Seiha

unread,
Jun 14, 2023, 2:38:32 AM6/14/23
to or-tools-discuss
trying to access to a empty solution (nullptr_t) could lead to a segfault imho...

Otherwise in main() you are defining a data model, manager and routing BUT before the call to main(), in the global scope, you also create a data model, manager and routing -> not sure how both will interact in your get_routes() you are mixin both...
Why not adding the get_route() directly at the end of the main function and returning the routes ?

Also variable scoping/gc in python can be tricky... 
Reply all
Reply to author
Forward
0 new messages