Upload 4 files
Browse files- google_solver/Untitled.ipynb +99 -0
- google_solver/convert_data.py +57 -0
- google_solver/google_model.py +128 -0
- google_solver/scratch.py +27 -0
google_solver/Untitled.ipynb
ADDED
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"cells": [
|
3 |
+
{
|
4 |
+
"cell_type": "code",
|
5 |
+
"execution_count": 3,
|
6 |
+
"metadata": {},
|
7 |
+
"outputs": [],
|
8 |
+
"source": [
|
9 |
+
"import numpy as np\n",
|
10 |
+
"import torch"
|
11 |
+
]
|
12 |
+
},
|
13 |
+
{
|
14 |
+
"cell_type": "code",
|
15 |
+
"execution_count": 6,
|
16 |
+
"metadata": {},
|
17 |
+
"outputs": [
|
18 |
+
{
|
19 |
+
"data": {
|
20 |
+
"text/plain": [
|
21 |
+
"[array([0.01560026, 0.14899027, 0.8198149 ], dtype=float32),\n",
|
22 |
+
" array([0.9768128 , 0.06915247, 0.42297667], dtype=float32),\n",
|
23 |
+
" array([0.9447805 , 0.9968865 , 0.85253155], dtype=float32),\n",
|
24 |
+
" array([0.4674251, 0.9449542, 0.6850118], dtype=float32),\n",
|
25 |
+
" array([0.8488321 , 0.9191937 , 0.33687925], dtype=float32)]"
|
26 |
+
]
|
27 |
+
},
|
28 |
+
"execution_count": 6,
|
29 |
+
"metadata": {},
|
30 |
+
"output_type": "execute_result"
|
31 |
+
}
|
32 |
+
],
|
33 |
+
"source": [
|
34 |
+
"torch.rand(5, 3).numpy()"
|
35 |
+
]
|
36 |
+
},
|
37 |
+
{
|
38 |
+
"cell_type": "code",
|
39 |
+
"execution_count": 7,
|
40 |
+
"metadata": {},
|
41 |
+
"outputs": [],
|
42 |
+
"source": [
|
43 |
+
"x = torch.rand(5, 3).numpy()"
|
44 |
+
]
|
45 |
+
},
|
46 |
+
{
|
47 |
+
"cell_type": "code",
|
48 |
+
"execution_count": 8,
|
49 |
+
"metadata": {},
|
50 |
+
"outputs": [
|
51 |
+
{
|
52 |
+
"data": {
|
53 |
+
"text/plain": [
|
54 |
+
"[[0.6611388, 0.24161768, 0.35973948],\n",
|
55 |
+
" [0.28449154, 0.81414443, 0.17932409],\n",
|
56 |
+
" [0.65817285, 0.1036877, 0.8726997],\n",
|
57 |
+
" [0.05815494, 0.3649323, 0.6273505],\n",
|
58 |
+
" [0.57139295, 0.16107935, 0.08857018]]"
|
59 |
+
]
|
60 |
+
},
|
61 |
+
"execution_count": 8,
|
62 |
+
"metadata": {},
|
63 |
+
"output_type": "execute_result"
|
64 |
+
}
|
65 |
+
],
|
66 |
+
"source": [
|
67 |
+
"[list(x[i]) for i in range(x.shape[0])]"
|
68 |
+
]
|
69 |
+
},
|
70 |
+
{
|
71 |
+
"cell_type": "code",
|
72 |
+
"execution_count": null,
|
73 |
+
"metadata": {},
|
74 |
+
"outputs": [],
|
75 |
+
"source": []
|
76 |
+
}
|
77 |
+
],
|
78 |
+
"metadata": {
|
79 |
+
"kernelspec": {
|
80 |
+
"display_name": "Python 3",
|
81 |
+
"language": "python",
|
82 |
+
"name": "python3"
|
83 |
+
},
|
84 |
+
"language_info": {
|
85 |
+
"codemirror_mode": {
|
86 |
+
"name": "ipython",
|
87 |
+
"version": 3
|
88 |
+
},
|
89 |
+
"file_extension": ".py",
|
90 |
+
"mimetype": "text/x-python",
|
91 |
+
"name": "python",
|
92 |
+
"nbconvert_exporter": "python",
|
93 |
+
"pygments_lexer": "ipython3",
|
94 |
+
"version": "3.7.3"
|
95 |
+
}
|
96 |
+
},
|
97 |
+
"nbformat": 4,
|
98 |
+
"nbformat_minor": 2
|
99 |
+
}
|
google_solver/convert_data.py
ADDED
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
import torch
|
3 |
+
import numpy as np
|
4 |
+
|
5 |
+
|
6 |
+
def convert_tensor(x):
|
7 |
+
x = x.long().numpy().astype('int')
|
8 |
+
|
9 |
+
if len(x.shape) == 1:
|
10 |
+
return list(x)
|
11 |
+
else:
|
12 |
+
return [list(x[i]) for i in range(x.shape[0])]
|
13 |
+
|
14 |
+
|
15 |
+
|
16 |
+
def make_time_windows(start_time, end_time):
|
17 |
+
return torch.cat([start_time, end_time], dim=2)
|
18 |
+
|
19 |
+
|
20 |
+
|
21 |
+
def convert_data(input, scale_factor):
|
22 |
+
|
23 |
+
graph_data, fleet_data = input
|
24 |
+
|
25 |
+
start_times = graph_data['start_times']
|
26 |
+
end_times = graph_data['end_times']
|
27 |
+
|
28 |
+
distance_matrix = graph_data['distance_matrix']
|
29 |
+
time_matrix = graph_data['time_matrix']
|
30 |
+
|
31 |
+
time_windows = make_time_windows(start_times, end_times)
|
32 |
+
|
33 |
+
|
34 |
+
batch_size = distance_matrix.shape[0]
|
35 |
+
data = []
|
36 |
+
for i in range(batch_size):
|
37 |
+
|
38 |
+
num_vehicles = distance_matrix[i].shape[1]
|
39 |
+
|
40 |
+
space_mat = distance_matrix[i] * scale_factor
|
41 |
+
time_mat = time_matrix[i] * scale_factor
|
42 |
+
windows = time_windows[i] * scale_factor
|
43 |
+
|
44 |
+
space_mat = convert_tensor(space_mat)
|
45 |
+
time_mat = convert_tensor(time_mat)
|
46 |
+
windows = convert_tensor(windows)
|
47 |
+
|
48 |
+
|
49 |
+
D = {'distance_matrix': space_mat,
|
50 |
+
'time_matrix': time_mat,
|
51 |
+
'time_windows': windows,
|
52 |
+
'depot': 0,
|
53 |
+
'num_vehicles': num_vehicles
|
54 |
+
}
|
55 |
+
|
56 |
+
data.append(D)
|
57 |
+
return data
|
google_solver/google_model.py
ADDED
@@ -0,0 +1,128 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from __future__ import print_function
|
2 |
+
from ortools.constraint_solver import routing_enums_pb2
|
3 |
+
from ortools.constraint_solver import pywrapcp
|
4 |
+
import torch
|
5 |
+
from google_solver.convert_data import convert_data
|
6 |
+
|
7 |
+
|
8 |
+
class GoogleActor(object):
|
9 |
+
|
10 |
+
def __init__(self, scale_factor=100):
|
11 |
+
|
12 |
+
if scale_factor is None:
|
13 |
+
self.scale_factor = 1
|
14 |
+
else:
|
15 |
+
self.scale_factor = scale_factor
|
16 |
+
|
17 |
+
|
18 |
+
def __call__(self, input):
|
19 |
+
|
20 |
+
drive_times = []
|
21 |
+
data = convert_data(input, self.scale_factor)
|
22 |
+
batch_size = len(data)
|
23 |
+
for datum in data:
|
24 |
+
routing, assignment = self.compute_route(datum)
|
25 |
+
total_time = self.compute_total_time(datum, routing, assignment)
|
26 |
+
drive_times.append(total_time)
|
27 |
+
|
28 |
+
drive_times = torch.tensor(drive_times).float()
|
29 |
+
return drive_times
|
30 |
+
|
31 |
+
|
32 |
+
def compute_distance(self, routing, assignment, num_nodes):
|
33 |
+
"""Prints solution on console."""
|
34 |
+
cumulative_route_distance = 0
|
35 |
+
for vehicle_id in range(num_nodes):
|
36 |
+
index = routing.Start(vehicle_id)
|
37 |
+
route_distance = 0
|
38 |
+
while not routing.IsEnd(index):
|
39 |
+
previous_index = index
|
40 |
+
index = assignment.Value(routing.NextVar(index))
|
41 |
+
route_distance += routing.GetArcCostForVehicle(
|
42 |
+
previous_index, index, vehicle_id)
|
43 |
+
cumulative_route_distance += route_distance
|
44 |
+
|
45 |
+
cumulative_route_distance = cumulative_route_distance / self.scale_factor
|
46 |
+
return cumulative_route_distance
|
47 |
+
|
48 |
+
|
49 |
+
def compute_total_time(self, data, routing, assignment):
|
50 |
+
time_dimension = routing.GetDimensionOrDie('Time')
|
51 |
+
total_time = 0
|
52 |
+
for vehicle_id in range(data['num_vehicles']):
|
53 |
+
index = routing.Start(vehicle_id)
|
54 |
+
while not routing.IsEnd(index):
|
55 |
+
index = assignment.Value(routing.NextVar(index))
|
56 |
+
time_var = time_dimension.CumulVar(index)
|
57 |
+
total_time += assignment.Min(time_var)
|
58 |
+
total_time = total_time/self.scale_factor
|
59 |
+
return total_time
|
60 |
+
|
61 |
+
|
62 |
+
|
63 |
+
def compute_route(self, input):
|
64 |
+
|
65 |
+
distance_matrix = input['distance_matrix']
|
66 |
+
time_matrix = input['time_matrix']
|
67 |
+
time_windows = input['time_windows']
|
68 |
+
num_vehicles = input['num_vehicles']
|
69 |
+
depot = input['depot']
|
70 |
+
|
71 |
+
|
72 |
+
manager = pywrapcp.RoutingIndexManager(len(time_matrix), num_vehicles, depot)
|
73 |
+
routing = pywrapcp.RoutingModel(manager)
|
74 |
+
|
75 |
+
def time_callback(from_index, to_index):
|
76 |
+
"""Returns the travel time between the two nodes."""
|
77 |
+
# Convert from routing variable Index to time matrix NodeIndex.
|
78 |
+
from_node = manager.IndexToNode(from_index)
|
79 |
+
to_node = manager.IndexToNode(to_index)
|
80 |
+
return time_matrix[from_node][to_node]
|
81 |
+
|
82 |
+
transit_callback_index = routing.RegisterTransitCallback(time_callback)
|
83 |
+
routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)
|
84 |
+
time = 'Time'
|
85 |
+
routing.AddDimension(
|
86 |
+
transit_callback_index,
|
87 |
+
10000, # allow waiting time
|
88 |
+
10000, # maximum time per vehicle
|
89 |
+
False, # Don't force start cumul to zero.
|
90 |
+
time)
|
91 |
+
|
92 |
+
time_dimension = routing.GetDimensionOrDie(time)
|
93 |
+
# Add time window constraints for each location except depot.
|
94 |
+
for location_idx, time_window in enumerate(time_windows):
|
95 |
+
if location_idx == 0:
|
96 |
+
continue
|
97 |
+
index = manager.NodeToIndex(location_idx)
|
98 |
+
a, b = int(time_window[0]), int(time_window[1])
|
99 |
+
time_dimension.CumulVar(index).SetRange(a, b)
|
100 |
+
|
101 |
+
# Add time window constraints for each vehicle start node.
|
102 |
+
for vehicle_id in range(num_vehicles):
|
103 |
+
index = routing.Start(vehicle_id)
|
104 |
+
a, b = int(time_windows[0][0]), int(time_windows[0][1])
|
105 |
+
time_dimension.CumulVar(index).SetRange(a, b)
|
106 |
+
|
107 |
+
for i in range(num_vehicles):
|
108 |
+
routing.AddVariableMinimizedByFinalizer(
|
109 |
+
time_dimension.CumulVar(routing.Start(i)))
|
110 |
+
routing.AddVariableMinimizedByFinalizer(
|
111 |
+
time_dimension.CumulVar(routing.End(i)))
|
112 |
+
|
113 |
+
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
|
114 |
+
search_parameters.first_solution_strategy = (routing_enums_pb2.FirstSolutionStrategy.AUTOMATIC)
|
115 |
+
assignment = routing.SolveWithParameters(search_parameters)
|
116 |
+
|
117 |
+
return routing, assignment
|
118 |
+
|
119 |
+
|
120 |
+
|
121 |
+
|
122 |
+
def evaluate_google_model(validation_dataset):
|
123 |
+
validation_dataset.device = 'cpu'
|
124 |
+
data = validation_dataset.get_data()
|
125 |
+
model = GoogleActor(scale_factor=100)
|
126 |
+
scores = model(data)
|
127 |
+
return scores
|
128 |
+
|
google_solver/scratch.py
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
|
3 |
+
from just_time_windows.google_solver.google_model import evaluate_google_model
|
4 |
+
from just_time_windows.Actor.actor import Actor as NN_Actor
|
5 |
+
from just_time_windows.build_data import Raw_VRP_Data
|
6 |
+
from just_time_windows.dataloader import VRP_Dataset
|
7 |
+
|
8 |
+
|
9 |
+
dataset = VRP_Dataset(dataset_size=10, num_depots=1, num_nodes=12)
|
10 |
+
|
11 |
+
batch = dataset.get_batch(0, 10)
|
12 |
+
|
13 |
+
nn_actor = NN_Actor(model=None, num_movers=10, num_neighbors_action=1)
|
14 |
+
|
15 |
+
|
16 |
+
nn_output = nn_actor(batch)
|
17 |
+
time = nn_output['total_time']
|
18 |
+
arrival_times = nn_output['arrival_times']
|
19 |
+
|
20 |
+
|
21 |
+
output = evaluate_google_model(dataset)
|
22 |
+
|
23 |
+
|
24 |
+
|
25 |
+
print(arrival_times)
|
26 |
+
print(time.mean().item())
|
27 |
+
print(output.mean().item())
|