|
2 | 2 | import numpy as np |
3 | 3 | import scipy as sp # type: ignore[import-untyped] |
4 | 4 | import torch |
5 | | -from ortools.constraint_solver import pywrapcp, routing_enums_pb2 # type: ignore[import-untyped] |
| 5 | +from python_tsp.heuristics import solve_tsp_simulated_annealing # type: ignore[import-untyped] |
6 | 6 |
|
7 | 7 | from . import functions # noqa |
8 | 8 |
|
@@ -95,45 +95,23 @@ def route(start_point: np.ndarray, points: np.ndarray, dim_weights: float | np.n |
95 | 95 | Returns the indices of the most efficient way to visit `points`, starting from `start_point`. |
96 | 96 | """ |
97 | 97 |
|
98 | | - total_points = np.r_[np.atleast_2d(start_point), points] |
99 | | - points_scale = np.ptp(total_points, axis=0) |
100 | | - dim_mask = points_scale > 0 |
| 98 | + total_points = np.concatenate( |
| 99 | + [start_point[None], points], axis=0 |
| 100 | + ) # add the starting point, even though we won't go there |
| 101 | + points_dim_range = np.ptp(total_points, axis=0) |
| 102 | + dim_mask = points_dim_range > 0 |
| 103 | + scaled_points = (total_points - total_points.min(axis=0)) * ( |
| 104 | + dim_weights / np.where(points_dim_range > 0, points_dim_range, 1) |
| 105 | + ) |
| 106 | + D = np.sqrt(np.square(scaled_points[:, None, :] - scaled_points[None, :, :]).sum(axis=-1)) |
| 107 | + D = (D / np.where(D > 0, D, np.inf).min()).astype(int) |
| 108 | + D[:, 0] = 0 # zero cost to return, since we don't care where we end up |
101 | 109 |
|
102 | 110 | if dim_mask.sum() == 0: |
103 | 111 | return np.arange(len(points)) |
104 | 112 |
|
105 | | - scaled_points = (total_points - total_points.min(axis=0)) * (dim_weights / np.where(points_scale > 0, points_scale, 1)) |
106 | | - |
107 | | - delay_matrix = np.sqrt(np.square(scaled_points[:, None, :] - scaled_points[None, :, :]).sum(axis=-1)) |
108 | | - delay_matrix = (1e4 * delay_matrix).astype(int) # it likes integers idk |
109 | | - |
110 | | - manager = pywrapcp.RoutingIndexManager(len(total_points), 1, 0) # number of depots, number of salesmen, starting index |
111 | | - routing = pywrapcp.RoutingModel(manager) |
112 | | - |
113 | | - def delay_callback(from_index, to_index): |
114 | | - to_node = manager.IndexToNode(to_index) |
115 | | - if to_node == 0: |
116 | | - return 0 # it is free to return to the depot from anywhere; we just won't do it |
117 | | - from_node = manager.IndexToNode(from_index) |
118 | | - return delay_matrix[from_node][to_node] |
119 | | - |
120 | | - transit_callback_index = routing.RegisterTransitCallback(delay_callback) |
121 | | - routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index) |
122 | | - search_parameters = pywrapcp.DefaultRoutingSearchParameters() |
123 | | - search_parameters.first_solution_strategy = routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC |
124 | | - |
125 | | - solution = routing.SolveWithParameters(search_parameters) |
126 | | - |
127 | | - index = routing.Start(0) |
128 | | - route_indices, route_delays = [0], [] |
129 | | - while not routing.IsEnd(index): |
130 | | - previous_index = index |
131 | | - index = solution.Value(routing.NextVar(index)) |
132 | | - route_delays.append(routing.GetArcCostForVehicle(previous_index, index, 0)) |
133 | | - route_indices.append(index) |
134 | | - |
135 | | - # omit the first and last indices, which correspond to the start |
136 | | - return np.array(route_indices)[1:-1] - 1 |
| 113 | + permutation, _ = solve_tsp_simulated_annealing(D / np.where(D > 0, D, np.inf).min()) |
| 114 | + return np.array(permutation[1:]) - 1 # ignore the starting point since we're there already |
137 | 115 |
|
138 | 116 |
|
139 | 117 | def get_movement_time(x: float | np.ndarray, v_max: float, a: float) -> float | np.ndarray: |
|
0 commit comments