Source code for discrete_optimization.tsp.solver.solver_ortools
"""Simple travelling salesman problem between cities."""
# Copyright (c) 2022 AIRBUS and its affiliates.
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
from __future__ import print_function
import logging
from typing import Any, Optional
import numpy as np
from ortools.constraint_solver import pywrapcp, routing_enums_pb2
from discrete_optimization.generic_tools.do_problem import ParamsObjectiveFunction
from discrete_optimization.generic_tools.do_solver import ResultStorage
from discrete_optimization.tsp.common_tools_tsp import build_matrice_distance
from discrete_optimization.tsp.solver.tsp_solver import SolverTSP
from discrete_optimization.tsp.tsp_model import SolutionTSP, TSPModel
logger = logging.getLogger(__name__)
[docs]
class TSP_ORtools(SolverTSP):
def __init__(
self,
problem: TSPModel,
params_objective_function: Optional[ParamsObjectiveFunction] = None,
**kwargs: Any,
):
super().__init__(
problem=problem, params_objective_function=params_objective_function
)
self.node_count = self.problem.node_count
self.list_points = self.problem.list_points
self.start_index = self.problem.start_index
self.end_index = self.problem.end_index
[docs]
def init_model(self, **kwargs: Any) -> None:
# Create the routing index manager.
if self.node_count < 1000:
matrix = build_matrice_distance(
self.node_count,
method=self.problem.evaluate_function_indexes,
)
distance_matrix = 10**6 * matrix.astype(np.int_)
def distance_callback(from_index: int, to_index: int) -> int:
from_node = manager.IndexToNode(from_index)
to_node = manager.IndexToNode(to_index)
return distance_matrix[from_node, to_node]
else:
def distance_callback(from_index: int, to_index: int) -> int:
from_node = manager.IndexToNode(from_index)
to_node = manager.IndexToNode(to_index)
return int(
10**6
* self.problem.evaluate_function_indexes(
self.list_points[from_node], self.list_points[to_node]
)
)
# Create the routing index manager.
manager = pywrapcp.RoutingIndexManager(
self.node_count, 1, [self.start_index], [self.end_index]
)
# Create Routing Model.
routing = pywrapcp.RoutingModel(manager)
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
)
search_parameters.local_search_metaheuristic = (
routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH
)
search_parameters.time_limit.seconds = 30
self.manager = manager
self.routing = routing
self.search_parameters = search_parameters
[docs]
def solve(self, **kwargs: Any) -> ResultStorage:
"""Prints solution on console."""
solution = self.routing.SolveWithParameters(self.search_parameters)
logger.debug(f"Objective: {solution.ObjectiveValue()} miles")
index = self.routing.Start(0)
index_real = self.manager.IndexToNode(index)
sol = [index_real]
route_distance = 0
while not self.routing.IsEnd(index):
previous_index = index
index = solution.Value(self.routing.NextVar(index))
index_real = self.manager.IndexToNode(index)
sol += [index_real]
route_distance += self.routing.GetArcCostForVehicle(
previous_index, index, 0
)
variableTSP = SolutionTSP(
problem=self.problem,
start_index=self.problem.start_index,
end_index=self.problem.end_index,
permutation=sol[1:-1],
lengths=None,
length=None,
)
fitness = self.aggreg_from_sol(variableTSP)
return ResultStorage(
list_solution_fits=[(variableTSP, fitness)],
mode_optim=self.params_objective_function.sense_function,
)