Update services/optimizer.py
Browse files- services/optimizer.py +53 -12
services/optimizer.py
CHANGED
|
@@ -1,20 +1,50 @@
|
|
| 1 |
from __future__ import annotations
|
| 2 |
from typing import List
|
| 3 |
-
from ortools.constraint_solver import routing_enums_pb2
|
| 4 |
-
from ortools.constraint_solver import pywrapcp
|
| 5 |
|
| 6 |
-
#
|
| 7 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
n = len(durations)
|
| 9 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
routing = pywrapcp.RoutingModel(manager)
|
| 11 |
|
| 12 |
def time_cb(from_index, to_index):
|
| 13 |
i = manager.IndexToNode(from_index)
|
| 14 |
j = manager.IndexToNode(to_index)
|
| 15 |
-
return int(durations[i][j])
|
| 16 |
-
|
| 17 |
-
transit_cb_index = routing.
|
| 18 |
routing.SetArcCostEvaluatorOfAllVehicles(transit_cb_index)
|
| 19 |
|
| 20 |
search_params = pywrapcp.DefaultRoutingSearchParameters()
|
|
@@ -23,13 +53,24 @@ def solver_tsp(durations: List[List[float]]) -> List[int]:
|
|
| 23 |
search_params.time_limit.FromSeconds(5)
|
| 24 |
|
| 25 |
solution = routing.SolveWithParameters(search_params)
|
| 26 |
-
if solution is None
|
| 27 |
-
|
| 28 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
index = routing.Start(0)
|
| 30 |
order = []
|
| 31 |
while not routing.IsEnd(index):
|
| 32 |
order.append(manager.IndexToNode(index))
|
| 33 |
index = solution.Value(routing.NextVar(index))
|
| 34 |
-
order.append(manager.IndexToNode(index))
|
| 35 |
return order
|
|
|
|
| 1 |
from __future__ import annotations
|
| 2 |
from typing import List
|
|
|
|
|
|
|
| 3 |
|
| 4 |
+
# Try OR-Tools; fallback to a simple nearest-neighbor if unavailable
|
| 5 |
+
try:
|
| 6 |
+
from ortools.constraint_solver import routing_enums_pb2
|
| 7 |
+
from ortools.constraint_solver import pywrapcp
|
| 8 |
+
except Exception: # pragma: no cover
|
| 9 |
+
routing_enums_pb2 = None
|
| 10 |
+
pywrapcp = None
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def solve_tsp(durations: List[List[float]]) -> List[int]:
|
| 14 |
+
"""
|
| 15 |
+
Solve a single-vehicle TSP with start/end at node 0 on the given duration matrix (seconds).
|
| 16 |
+
Returns a list of node indices in visiting order, including the final 0 (start=end).
|
| 17 |
+
Falls back to nearest-neighbor if OR-Tools is not available.
|
| 18 |
+
"""
|
| 19 |
n = len(durations)
|
| 20 |
+
if n == 0:
|
| 21 |
+
return []
|
| 22 |
+
if n == 1:
|
| 23 |
+
return [0]
|
| 24 |
+
|
| 25 |
+
# Fallback: nearest-neighbor tour + return to start
|
| 26 |
+
if pywrapcp is None:
|
| 27 |
+
order = [0]
|
| 28 |
+
remaining = set(range(1, n))
|
| 29 |
+
cur = 0
|
| 30 |
+
while remaining:
|
| 31 |
+
nxt = min(remaining, key=lambda j: durations[cur][j])
|
| 32 |
+
order.append(nxt)
|
| 33 |
+
remaining.remove(nxt)
|
| 34 |
+
cur = nxt
|
| 35 |
+
order.append(0)
|
| 36 |
+
return order
|
| 37 |
+
|
| 38 |
+
# OR-Tools exact/heuristic solution
|
| 39 |
+
manager = pywrapcp.RoutingIndexManager(n, 1, 0) # 1 vehicle, depot=0
|
| 40 |
routing = pywrapcp.RoutingModel(manager)
|
| 41 |
|
| 42 |
def time_cb(from_index, to_index):
|
| 43 |
i = manager.IndexToNode(from_index)
|
| 44 |
j = manager.IndexToNode(to_index)
|
| 45 |
+
return int(max(0, durations[i][j]))
|
| 46 |
+
|
| 47 |
+
transit_cb_index = routing.RegisterTransitCallback(time_cb)
|
| 48 |
routing.SetArcCostEvaluatorOfAllVehicles(transit_cb_index)
|
| 49 |
|
| 50 |
search_params = pywrapcp.DefaultRoutingSearchParameters()
|
|
|
|
| 53 |
search_params.time_limit.FromSeconds(5)
|
| 54 |
|
| 55 |
solution = routing.SolveWithParameters(search_params)
|
| 56 |
+
if solution is None:
|
| 57 |
+
# Fallback if solver fails
|
| 58 |
+
order = [0]
|
| 59 |
+
remaining = set(range(1, n))
|
| 60 |
+
cur = 0
|
| 61 |
+
while remaining:
|
| 62 |
+
nxt = min(remaining, key=lambda j: durations[cur][j])
|
| 63 |
+
order.append(nxt)
|
| 64 |
+
remaining.remove(nxt)
|
| 65 |
+
cur = nxt
|
| 66 |
+
order.append(0)
|
| 67 |
+
return order
|
| 68 |
+
|
| 69 |
+
# Extract route
|
| 70 |
index = routing.Start(0)
|
| 71 |
order = []
|
| 72 |
while not routing.IsEnd(index):
|
| 73 |
order.append(manager.IndexToNode(index))
|
| 74 |
index = solution.Value(routing.NextVar(index))
|
| 75 |
+
order.append(manager.IndexToNode(index)) # return to depot
|
| 76 |
return order
|