Corin1998's picture
Update services/optimizer.py
beb683a verified
from __future__ import annotations
from typing import List
# Try OR-Tools; fallback to a simple nearest-neighbor if unavailable
try:
from ortools.constraint_solver import routing_enums_pb2
from ortools.constraint_solver import pywrapcp
except Exception: # pragma: no cover
routing_enums_pb2 = None
pywrapcp = None
def solve_tsp(durations: List[List[float]]) -> List[int]:
"""
Solve a single-vehicle TSP with start/end at node 0 on the given duration matrix (seconds).
Returns a list of node indices in visiting order, including the final 0 (start=end).
Falls back to nearest-neighbor if OR-Tools is not available.
"""
n = len(durations)
if n == 0:
return []
if n == 1:
return [0]
# Fallback: nearest-neighbor tour + return to start
if pywrapcp is None:
order = [0]
remaining = set(range(1, n))
cur = 0
while remaining:
nxt = min(remaining, key=lambda j: durations[cur][j])
order.append(nxt)
remaining.remove(nxt)
cur = nxt
order.append(0)
return order
# OR-Tools exact/heuristic solution
manager = pywrapcp.RoutingIndexManager(n, 1, 0) # 1 vehicle, depot=0
routing = pywrapcp.RoutingModel(manager)
def time_cb(from_index, to_index):
i = manager.IndexToNode(from_index)
j = manager.IndexToNode(to_index)
return int(max(0, durations[i][j]))
transit_cb_index = routing.RegisterTransitCallback(time_cb)
routing.SetArcCostEvaluatorOfAllVehicles(transit_cb_index)
search_params = pywrapcp.DefaultRoutingSearchParameters()
search_params.first_solution_strategy = routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC
search_params.local_search_metaheuristic = routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH
search_params.time_limit.FromSeconds(5)
solution = routing.SolveWithParameters(search_params)
if solution is None:
# Fallback if solver fails
order = [0]
remaining = set(range(1, n))
cur = 0
while remaining:
nxt = min(remaining, key=lambda j: durations[cur][j])
order.append(nxt)
remaining.remove(nxt)
cur = nxt
order.append(0)
return order
# Extract route
index = routing.Start(0)
order = []
while not routing.IsEnd(index):
order.append(manager.IndexToNode(index))
index = solution.Value(routing.NextVar(index))
order.append(manager.IndexToNode(index)) # return to depot
return order