update
Browse files
README.md
CHANGED
@@ -13,7 +13,17 @@ tags:
|
|
13 |
|
14 |
# ✊GreedRL
|
15 |
|
16 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
|
18 |
|
19 |
## Architecture design
|
@@ -46,43 +56,29 @@ The neural network adopts the Seq2Seq architecture commonly used in Natural Lang
|
|
46 |
|
47 |
## Modeling examples
|
48 |
|
49 |
-
|
|
|
50 |
<details>
|
51 |
-
<summary>
|
52 |
|
53 |
```python
|
54 |
-
from greedrl import Problem, Solution, Solver
|
55 |
from greedrl.feature import *
|
56 |
from greedrl.variable import *
|
57 |
from greedrl.function import *
|
58 |
-
from greedrl
|
59 |
-
from greedrl
|
60 |
|
61 |
-
features = [continuous_feature('
|
62 |
-
continuous_feature('
|
63 |
-
continuous_feature('
|
64 |
-
|
65 |
-
|
66 |
-
continuous_feature('task_demand'),
|
67 |
-
continuous_feature('task_weight'),
|
68 |
-
continuous_feature('task_ready_time'),
|
69 |
-
continuous_feature('task_due_time'),
|
70 |
-
continuous_feature('task_service_time'),
|
71 |
-
continuous_feature('distance_matrix')]
|
72 |
|
73 |
variables = [task_demand_now('task_demand_now', feature='task_demand'),
|
74 |
task_demand_now('task_demand_this', feature='task_demand', only_this=True),
|
75 |
feature_variable('task_weight'),
|
76 |
-
feature_variable('task_due_time'),
|
77 |
-
feature_variable('task_ready_time'),
|
78 |
-
feature_variable('task_service_time'),
|
79 |
worker_variable('worker_weight_limit'),
|
80 |
-
worker_variable('worker_due_time'),
|
81 |
-
worker_variable('worker_basic_cost'),
|
82 |
-
worker_variable('worker_distance_cost'),
|
83 |
worker_used_resource('worker_used_weight', task_require='task_weight'),
|
84 |
-
worker_used_resource('worker_used_time', 'distance_matrix', 'task_service_time', 'task_ready_time',
|
85 |
-
'worker_ready_time'),
|
86 |
edge_variable('distance_last_to_this', feature='distance_matrix', last_to_this=True),
|
87 |
edge_variable('distance_this_to_task', feature='distance_matrix', this_to_task=True),
|
88 |
edge_variable('distance_task_to_end', feature='distance_matrix', task_to_end=True)]
|
@@ -99,15 +95,6 @@ class Constraint:
|
|
99 |
# 车辆容量限制
|
100 |
worker_weight_limit = self.worker_weight_limit - self.worker_used_weight
|
101 |
mask |= self.task_demand_now * self.task_weight > worker_weight_limit[:, None]
|
102 |
-
|
103 |
-
worker_used_time = self.worker_used_time[:, None] + self.distance_this_to_task
|
104 |
-
mask |= worker_used_time > self.task_due_time
|
105 |
-
|
106 |
-
worker_used_time = torch.max(worker_used_time, self.task_ready_time)
|
107 |
-
worker_used_time += self.task_service_time
|
108 |
-
worker_used_time += self.distance_task_to_end
|
109 |
-
mask |= worker_used_time > self.worker_due_time[:, None]
|
110 |
-
|
111 |
return mask
|
112 |
|
113 |
def finished(self):
|
@@ -116,14 +103,11 @@ class Constraint:
|
|
116 |
|
117 |
class Objective:
|
118 |
|
119 |
-
def step_worker_start(self):
|
120 |
-
return self.worker_basic_cost
|
121 |
-
|
122 |
def step_worker_end(self):
|
123 |
-
return self.distance_last_to_this
|
124 |
|
125 |
def step_task(self):
|
126 |
-
return self.distance_last_to_this
|
127 |
```
|
128 |
|
129 |
</details>
|
@@ -208,10 +192,382 @@ class Objective:
|
|
208 |
|
209 |
</details>
|
210 |
|
211 |
-
## 🏆Award
|
212 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
213 |
|
214 |
-
# 🤠GreedRL-VRP-pretrained model
|
215 |
|
216 |
## Model description
|
217 |
|
@@ -236,13 +592,13 @@ You need to compile first and add the resulting library `greedrl_c` to the `PYTH
|
|
236 |
```aidl
|
237 |
python setup.py build
|
238 |
|
239 |
-
export PYTHONPATH={root_path}/greedrl/build/lib.linux-x86_64-cpython-38
|
240 |
```
|
241 |
|
242 |
|
243 |
### Training
|
244 |
|
245 |
-
We provide
|
246 |
|
247 |
1. Training data
|
248 |
|
@@ -253,7 +609,7 @@ For the CVRP, we assume that the demand of each node is a discrete number in {1,
|
|
253 |
|
254 |
2. Start training
|
255 |
```python
|
256 |
-
cd
|
257 |
|
258 |
python train.py --model_filename cvrp_5000.pt --problem_size 5000
|
259 |
```
|
@@ -263,7 +619,7 @@ python train.py --model_filename cvrp_5000.pt --problem_size 5000
|
|
263 |
We provide some pretrained models for different CVRP problem sizes, such as `cvrp_100`, `cvrp_1000`, `cvrp_2000` and `cvrp_5000`, that you can directly use for inference.
|
264 |
|
265 |
```python
|
266 |
-
cd
|
267 |
|
268 |
python solve.py --device cuda --model_name cvrp_5000.pt --problem_size 5000
|
269 |
```
|
|
|
13 |
|
14 |
# ✊GreedRL
|
15 |
|
16 |
+
## 🏆Award
|
17 |
+
|
18 |
+
|
19 |
+
## Introduction
|
20 |
+
|
21 |
+
* **GENERAL**
|
22 |
+
|
23 |
+
|
24 |
+
* **HIGH-PERFORMANCE**
|
25 |
+
|
26 |
+
* **USER-FRIENDLY**
|
27 |
|
28 |
|
29 |
## Architecture design
|
|
|
56 |
|
57 |
## Modeling examples
|
58 |
|
59 |
+
|
60 |
+
### Capacitated Vehicle Routing Problem (CVRP)
|
61 |
<details>
|
62 |
+
<summary>CVRP</summary>
|
63 |
|
64 |
```python
|
|
|
65 |
from greedrl.feature import *
|
66 |
from greedrl.variable import *
|
67 |
from greedrl.function import *
|
68 |
+
from greedrl import Problem, Solution, Solver
|
69 |
+
from greedrl import runner
|
70 |
|
71 |
+
features = [continuous_feature('task_demand'),
|
72 |
+
continuous_feature('worker_weight_limit'),
|
73 |
+
continuous_feature('distance_matrix'),
|
74 |
+
variable_feature('distance_this_to_task'),
|
75 |
+
variable_feature('distance_task_to_end')]
|
|
|
|
|
|
|
|
|
|
|
|
|
76 |
|
77 |
variables = [task_demand_now('task_demand_now', feature='task_demand'),
|
78 |
task_demand_now('task_demand_this', feature='task_demand', only_this=True),
|
79 |
feature_variable('task_weight'),
|
|
|
|
|
|
|
80 |
worker_variable('worker_weight_limit'),
|
|
|
|
|
|
|
81 |
worker_used_resource('worker_used_weight', task_require='task_weight'),
|
|
|
|
|
82 |
edge_variable('distance_last_to_this', feature='distance_matrix', last_to_this=True),
|
83 |
edge_variable('distance_this_to_task', feature='distance_matrix', this_to_task=True),
|
84 |
edge_variable('distance_task_to_end', feature='distance_matrix', task_to_end=True)]
|
|
|
95 |
# 车辆容量限制
|
96 |
worker_weight_limit = self.worker_weight_limit - self.worker_used_weight
|
97 |
mask |= self.task_demand_now * self.task_weight > worker_weight_limit[:, None]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
98 |
return mask
|
99 |
|
100 |
def finished(self):
|
|
|
103 |
|
104 |
class Objective:
|
105 |
|
|
|
|
|
|
|
106 |
def step_worker_end(self):
|
107 |
+
return self.distance_last_to_this
|
108 |
|
109 |
def step_task(self):
|
110 |
+
return self.distance_last_to_this
|
111 |
```
|
112 |
|
113 |
</details>
|
|
|
192 |
|
193 |
</details>
|
194 |
|
|
|
195 |
|
196 |
+
### VRP with Time Windows(VRPTW)
|
197 |
+
<details>
|
198 |
+
<summary>VRPTW</summary>
|
199 |
+
|
200 |
+
```python
|
201 |
+
from greedrl import Problem, Solution, Solver
|
202 |
+
from greedrl.feature import *
|
203 |
+
from greedrl.variable import *
|
204 |
+
from greedrl.function import *
|
205 |
+
from greedrl.model import runner
|
206 |
+
from greedrl.myenv import VrptwEnv
|
207 |
+
|
208 |
+
features = [continuous_feature('worker_weight_limit'),
|
209 |
+
continuous_feature('worker_ready_time'),
|
210 |
+
continuous_feature('worker_due_time'),
|
211 |
+
continuous_feature('worker_basic_cost'),
|
212 |
+
continuous_feature('worker_distance_cost'),
|
213 |
+
continuous_feature('task_demand'),
|
214 |
+
continuous_feature('task_weight'),
|
215 |
+
continuous_feature('task_ready_time'),
|
216 |
+
continuous_feature('task_due_time'),
|
217 |
+
continuous_feature('task_service_time'),
|
218 |
+
continuous_feature('distance_matrix')]
|
219 |
+
|
220 |
+
variables = [task_demand_now('task_demand_now', feature='task_demand'),
|
221 |
+
task_demand_now('task_demand_this', feature='task_demand', only_this=True),
|
222 |
+
feature_variable('task_weight'),
|
223 |
+
feature_variable('task_due_time'),
|
224 |
+
feature_variable('task_ready_time'),
|
225 |
+
feature_variable('task_service_time'),
|
226 |
+
worker_variable('worker_weight_limit'),
|
227 |
+
worker_variable('worker_due_time'),
|
228 |
+
worker_variable('worker_basic_cost'),
|
229 |
+
worker_variable('worker_distance_cost'),
|
230 |
+
worker_used_resource('worker_used_weight', task_require='task_weight'),
|
231 |
+
worker_used_resource('worker_used_time', 'distance_matrix', 'task_service_time', 'task_ready_time',
|
232 |
+
'worker_ready_time'),
|
233 |
+
edge_variable('distance_last_to_this', feature='distance_matrix', last_to_this=True),
|
234 |
+
edge_variable('distance_this_to_task', feature='distance_matrix', this_to_task=True),
|
235 |
+
edge_variable('distance_task_to_end', feature='distance_matrix', task_to_end=True)]
|
236 |
+
|
237 |
+
|
238 |
+
class Constraint:
|
239 |
+
|
240 |
+
def do_task(self):
|
241 |
+
return self.task_demand_this
|
242 |
+
|
243 |
+
def mask_task(self):
|
244 |
+
# 已经完成的任务
|
245 |
+
mask = self.task_demand_now <= 0
|
246 |
+
# 车辆容量限制
|
247 |
+
worker_weight_limit = self.worker_weight_limit - self.worker_used_weight
|
248 |
+
mask |= self.task_demand_now * self.task_weight > worker_weight_limit[:, None]
|
249 |
+
|
250 |
+
worker_used_time = self.worker_used_time[:, None] + self.distance_this_to_task
|
251 |
+
mask |= worker_used_time > self.task_due_time
|
252 |
+
|
253 |
+
worker_used_time = torch.max(worker_used_time, self.task_ready_time)
|
254 |
+
worker_used_time += self.task_service_time
|
255 |
+
worker_used_time += self.distance_task_to_end
|
256 |
+
mask |= worker_used_time > self.worker_due_time[:, None]
|
257 |
+
|
258 |
+
return mask
|
259 |
+
|
260 |
+
def finished(self):
|
261 |
+
return torch.all(self.task_demand_now <= 0, 1)
|
262 |
+
|
263 |
+
|
264 |
+
class Objective:
|
265 |
+
|
266 |
+
def step_worker_start(self):
|
267 |
+
return self.worker_basic_cost
|
268 |
+
|
269 |
+
def step_worker_end(self):
|
270 |
+
return self.distance_last_to_this * self.worker_distance_cost
|
271 |
+
|
272 |
+
def step_task(self):
|
273 |
+
return self.distance_last_to_this * self.worker_distance_cost
|
274 |
+
```
|
275 |
+
|
276 |
+
</details>
|
277 |
+
|
278 |
+
### Travelling Salesman Problem(TSP)
|
279 |
+
<details>
|
280 |
+
<summary>TSP</summary>
|
281 |
+
|
282 |
+
```python
|
283 |
+
from greedrl.feature import *
|
284 |
+
from greedrl.variable import *
|
285 |
+
from greedrl import Problem
|
286 |
+
from greedrl import runner
|
287 |
+
|
288 |
+
features = [continuous_feature('task_location'),
|
289 |
+
variable_feature('distance_this_to_task'),
|
290 |
+
variable_feature('distance_task_to_end')]
|
291 |
+
|
292 |
+
variables = [task_demand_now('task_demand_now', feature='task_demand'),
|
293 |
+
task_demand_now('task_demand_this', feature='task_demand', only_this=True),
|
294 |
+
edge_variable('distance_last_to_this', feature='distance_matrix', last_to_this=True),
|
295 |
+
edge_variable('distance_this_to_task', feature='distance_matrix', this_to_task=True),
|
296 |
+
edge_variable('distance_task_to_end', feature='distance_matrix', task_to_end=True),
|
297 |
+
edge_variable('distance_last_to_loop', feature='distance_matrix', last_to_loop=True)]
|
298 |
+
|
299 |
+
|
300 |
+
class Constraint:
|
301 |
+
|
302 |
+
def do_task(self):
|
303 |
+
return self.task_demand_this
|
304 |
+
|
305 |
+
def mask_task(self):
|
306 |
+
mask = self.task_demand_now <= 0
|
307 |
+
return mask
|
308 |
+
|
309 |
+
def mask_worker_end(self):
|
310 |
+
return torch.any(self.task_demand_now > 0, 1)
|
311 |
+
|
312 |
+
def finished(self):
|
313 |
+
return torch.all(self.task_demand_now <= 0, 1)
|
314 |
+
|
315 |
+
|
316 |
+
class Objective:
|
317 |
+
|
318 |
+
def step_worker_end(self):
|
319 |
+
return self.distance_last_to_loop
|
320 |
+
|
321 |
+
def step_task(self):
|
322 |
+
return self.distance_last_to_this
|
323 |
+
```
|
324 |
+
|
325 |
+
</details>
|
326 |
+
|
327 |
+
### Split Delivery Vehicle Routing Problem(SDVRP)
|
328 |
+
<details>
|
329 |
+
<summary>SDVRP</summary>
|
330 |
+
|
331 |
+
```python
|
332 |
+
from greedrl.feature import *
|
333 |
+
from greedrl.variable import *
|
334 |
+
from greedrl import Problem
|
335 |
+
from greedrl import runner
|
336 |
+
|
337 |
+
features = [continuous_feature('task_demand'),
|
338 |
+
continuous_feature('worker_weight_limit'),
|
339 |
+
continuous_feature('distance_matrix'),
|
340 |
+
variable_feature('distance_this_to_task'),
|
341 |
+
variable_feature('distance_task_to_end')]
|
342 |
+
|
343 |
+
variables = [task_demand_now('task_demand'),
|
344 |
+
task_demand_now('task_demand_this', feature='task_demand', only_this=True),
|
345 |
+
feature_variable('task_weight'),
|
346 |
+
task_variable('task_weight_this', feature='task_weight'),
|
347 |
+
worker_variable('worker_weight_limit'),
|
348 |
+
worker_used_resource('worker_used_weight', task_require='task_weight'),
|
349 |
+
edge_variable('distance_last_to_this', feature='distance_matrix', last_to_this=True)]
|
350 |
+
|
351 |
+
|
352 |
+
class Constraint:
|
353 |
+
|
354 |
+
def do_task(self):
|
355 |
+
worker_weight_limit = self.worker_weight_limit - self.worker_used_weight
|
356 |
+
return torch.min(self.task_demand_this, worker_weight_limit // self.task_weight_this)
|
357 |
+
|
358 |
+
def mask_task(self):
|
359 |
+
mask = self.task_demand <= 0
|
360 |
+
worker_weight_limit = self.worker_weight_limit - self.worker_used_weight
|
361 |
+
mask |= self.task_weight > worker_weight_limit[:, None]
|
362 |
+
return mask
|
363 |
+
|
364 |
+
def finished(self):
|
365 |
+
return torch.all(self.task_demand <= 0, 1)
|
366 |
+
|
367 |
+
|
368 |
+
class Objective:
|
369 |
+
|
370 |
+
def step_worker_end(self):
|
371 |
+
return self.distance_last_to_this
|
372 |
+
|
373 |
+
def step_task(self):
|
374 |
+
return self.distance_last_to_this
|
375 |
+
```
|
376 |
+
|
377 |
+
</details>
|
378 |
+
|
379 |
+
### Realistic Business Scenario
|
380 |
+
<details>
|
381 |
+
<summary>real-time Dynamic Pickup and Delivery Problem(DPDP)</summary>
|
382 |
+
|
383 |
+
```python
|
384 |
+
from greedrl.feature import *
|
385 |
+
from greedrl.variable import *
|
386 |
+
from greedrl.function import *
|
387 |
+
from greedrl import Problem
|
388 |
+
from greedrl import runner
|
389 |
+
|
390 |
+
features = [local_category('task_order'),
|
391 |
+
global_category('task_type', 2),
|
392 |
+
global_category('task_new_order', 2),
|
393 |
+
variable_feature('time_this_to_task'),
|
394 |
+
continuous_feature('x_time_matrix'),
|
395 |
+
continuous_feature('task_due_time_x'),
|
396 |
+
continuous_feature('worker_task_mask')]
|
397 |
+
|
398 |
+
variables = [task_demand_now('task_demand_now', feature='task_demand'),
|
399 |
+
task_demand_now('task_demand_this', feature='task_demand', only_this=True),
|
400 |
+
task_variable('task_pickup_this', feature='task_pickup'),
|
401 |
+
task_variable('task_due_time_this', feature='task_due_time'),
|
402 |
+
feature_variable('task_order', feature='task_order'),
|
403 |
+
feature_variable('task_type', feature='task_type'),
|
404 |
+
feature_variable('task_new_pickup', feature='task_new_pickup'),
|
405 |
+
feature_variable('worker_task_mask', feature='worker_task_mask'),
|
406 |
+
worker_count_now('worker_count_now', feature='worker_count'),
|
407 |
+
worker_variable('worker_min_old_task_this', feature='worker_min_old_task'),
|
408 |
+
worker_variable('worker_max_new_order_this', feature='worker_max_new_order'),
|
409 |
+
worker_variable('worker_task_mask_this', feature='worker_task_mask'),
|
410 |
+
worker_used_resource('worker_used_old_task', task_require='task_old'),
|
411 |
+
worker_used_resource('worker_used_new_order', task_require='task_new_pickup'),
|
412 |
+
worker_used_resource('worker_used_time', edge_require='time_matrix'),
|
413 |
+
edge_variable('time_this_to_task', feature='x_time_matrix', this_to_task=True)]
|
414 |
+
|
415 |
+
|
416 |
+
class Constraint:
|
417 |
+
|
418 |
+
def do_task(self):
|
419 |
+
return self.task_demand_this
|
420 |
+
|
421 |
+
def mask_worker_start(self):
|
422 |
+
mask = self.worker_count_now <= 0
|
423 |
+
|
424 |
+
finished = self.task_demand_now <= 0
|
425 |
+
worker_task_mask = self.worker_task_mask | finished[:, None, :]
|
426 |
+
mask |= torch.all(worker_task_mask, 2)
|
427 |
+
|
428 |
+
return mask
|
429 |
+
|
430 |
+
def mask_worker_end(self):
|
431 |
+
mask = self.worker_used_old_task < self.worker_min_old_task_this
|
432 |
+
mask |= task_group_split(self.task_order, self.task_demand_now <= 0)
|
433 |
+
return mask
|
434 |
+
|
435 |
+
def mask_task(self):
|
436 |
+
mask = self.task_demand_now <= 0
|
437 |
+
|
438 |
+
mask |= task_group_priority(self.task_order, self.task_type, mask)
|
439 |
+
|
440 |
+
worker_max_new_order = self.worker_max_new_order_this - self.worker_used_new_order
|
441 |
+
mask |= self.task_new_pickup > worker_max_new_order[:, None]
|
442 |
+
|
443 |
+
mask |= self.worker_task_mask_this
|
444 |
+
|
445 |
+
return mask
|
446 |
+
|
447 |
+
def finished(self):
|
448 |
+
worker_mask = self.worker_count_now <= 0
|
449 |
+
task_mask = self.task_demand_now <= 0
|
450 |
+
worker_task_mask = worker_mask[:, :, None] | task_mask[:, None, :]
|
451 |
+
|
452 |
+
worker_task_mask |= self.worker_task_mask
|
453 |
+
batch_size = worker_task_mask.size(0)
|
454 |
+
worker_task_mask = worker_task_mask.view(batch_size, -1)
|
455 |
+
return worker_task_mask.all(1)
|
456 |
+
|
457 |
+
|
458 |
+
class Objective:
|
459 |
+
|
460 |
+
def step_task(self):
|
461 |
+
over_time = (self.worker_used_time - self.task_due_time_this).clamp(min=0)
|
462 |
+
pickup_time = self.worker_used_time * self.task_pickup_this
|
463 |
+
return self.worker_used_time + over_time + pickup_time
|
464 |
+
|
465 |
+
def step_finish(self):
|
466 |
+
return self.task_demand_now.sum(1) * 1000
|
467 |
+
```
|
468 |
+
|
469 |
+
</details>
|
470 |
+
|
471 |
+
### Order Batching Problem
|
472 |
+
<details>
|
473 |
+
<summary>Batching</summary>
|
474 |
+
|
475 |
+
```python
|
476 |
+
from greedrl import Problem, Solver
|
477 |
+
from greedrl.feature import *
|
478 |
+
from greedrl.variable import *
|
479 |
+
from greedrl import runner
|
480 |
+
|
481 |
+
|
482 |
+
features = [local_feature('task_area'),
|
483 |
+
local_feature('task_roadway'),
|
484 |
+
local_feature('task_area_group'),
|
485 |
+
sparse_local_feature('task_item_id', 'task_item_num'),
|
486 |
+
sparse_local_feature('task_item_owner_id', 'task_item_num'),
|
487 |
+
variable_feature('worker_task_item'),
|
488 |
+
variable_feature('worker_used_roadway'),
|
489 |
+
variable_feature('worker_used_area')]
|
490 |
+
|
491 |
+
variables = [task_demand_now('task_demand_now', feature='task_demand'),
|
492 |
+
task_demand_now('task_demand_this', feature='task_demand', only_this=True),
|
493 |
+
feature_variable('task_item_id'),
|
494 |
+
feature_variable('task_item_num'),
|
495 |
+
feature_variable('task_item_owner_id'),
|
496 |
+
feature_variable('task_area'),
|
497 |
+
feature_variable('task_area_group'),
|
498 |
+
feature_variable('task_load'),
|
499 |
+
feature_variable('task_group'),
|
500 |
+
worker_variable('worker_load_limit'),
|
501 |
+
worker_variable('worker_area_limit'),
|
502 |
+
worker_variable('worker_area_group_limit'),
|
503 |
+
worker_task_item('worker_task_item', item_id='task_item_id', item_num='task_item_num'),
|
504 |
+
worker_task_item('worker_task_item_owner', item_id='task_item_owner_id', item_num='task_item_num'),
|
505 |
+
worker_used_resource('worker_used_load', task_require='task_load'),
|
506 |
+
worker_used_resource('worker_used_area', task_require='task_area'),
|
507 |
+
worker_used_resource('worker_used_roadway', task_require='task_roadway'),
|
508 |
+
worker_used_resource('worker_used_area_group', task_require='task_area_group')]
|
509 |
+
|
510 |
+
|
511 |
+
class Constraint:
|
512 |
+
|
513 |
+
def do_task(self):
|
514 |
+
return self.task_demand_this
|
515 |
+
|
516 |
+
def mask_worker_end(self):
|
517 |
+
return self.worker_used_load < self.worker_load_limit
|
518 |
+
|
519 |
+
def mask_task(self):
|
520 |
+
# completed tasks
|
521 |
+
mask = self.task_demand_now <= 0
|
522 |
+
# mask |= task_group_priority(self.task_group, self.task_out_stock_time, mask)
|
523 |
+
|
524 |
+
NT = self.task_item_id.size(1)
|
525 |
+
worker_task_item = self.worker_task_item[:, None, :]
|
526 |
+
worker_task_item = worker_task_item.expand(-1, NT, -1)
|
527 |
+
task_item_in_worker = worker_task_item.gather(2, self.task_item_id.long())
|
528 |
+
task_item_in_worker = (task_item_in_worker > 0) & (self.task_item_num > 0)
|
529 |
+
|
530 |
+
worker_task_item_owner = self.worker_task_item_owner[:, None, :]
|
531 |
+
worker_task_item_owner = worker_task_item_owner.expand(-1, NT, -1)
|
532 |
+
task_item_owner_in_worker = worker_task_item_owner.gather(2, self.task_item_owner_id.long())
|
533 |
+
task_item_owner_in_worker = (task_item_owner_in_worker > 0) & (self.task_item_num > 0)
|
534 |
+
|
535 |
+
#
|
536 |
+
mask |= torch.any(task_item_in_worker & ~task_item_owner_in_worker, 2)
|
537 |
+
|
538 |
+
worker_load_limit = self.worker_load_limit - self.worker_used_load
|
539 |
+
mask |= (self.task_load > worker_load_limit[:, None])
|
540 |
+
|
541 |
+
task_area = self.task_area + self.worker_used_area[:, None, :]
|
542 |
+
task_area_num = task_area.clamp(0, 1).sum(2, dtype=torch.int32)
|
543 |
+
mask |= (task_area_num > self.worker_area_limit[:, None])
|
544 |
+
|
545 |
+
tak_area_group = self.task_area_group + self.worker_used_area_group[:, None, :]
|
546 |
+
tak_area_group_num = tak_area_group.clamp(0, 1).sum(2, dtype=torch.int32)
|
547 |
+
mask |= (tak_area_group_num > self.worker_area_group_limit[:, None])
|
548 |
+
|
549 |
+
return mask
|
550 |
+
|
551 |
+
def finished(self):
|
552 |
+
return torch.all(self.task_demand_now <= 0, 1)
|
553 |
+
|
554 |
+
|
555 |
+
class Objective:
|
556 |
+
|
557 |
+
def step_worker_end(self):
|
558 |
+
area_num = self.worker_used_area.clamp(0, 1).sum(1)
|
559 |
+
roadway_num = self.worker_used_roadway.clamp(0, 1).sum(1)
|
560 |
+
item_num = self.worker_task_item.clamp(0, 1).sum(1)
|
561 |
+
penalty = (self.worker_load_limit - self.worker_used_load) * 10
|
562 |
+
return area_num * 100 + roadway_num * 10 + item_num + penalty
|
563 |
+
```
|
564 |
+
|
565 |
+
</details>
|
566 |
+
|
567 |
+
#
|
568 |
+
|
569 |
+
# 🤠GreedRL-CVRP-pretrained model
|
570 |
|
|
|
571 |
|
572 |
## Model description
|
573 |
|
|
|
592 |
```aidl
|
593 |
python setup.py build
|
594 |
|
595 |
+
export PYTHONPATH={root_path}/greedrl/build/lib.linux-x86_64-cpython-38/:$PYTHONPATH
|
596 |
```
|
597 |
|
598 |
|
599 |
### Training
|
600 |
|
601 |
+
We provide example of Capacitated VRP(CVRP) for training and inference.
|
602 |
|
603 |
1. Training data
|
604 |
|
|
|
609 |
|
610 |
2. Start training
|
611 |
```python
|
612 |
+
cd examples/cvrp
|
613 |
|
614 |
python train.py --model_filename cvrp_5000.pt --problem_size 5000
|
615 |
```
|
|
|
619 |
We provide some pretrained models for different CVRP problem sizes, such as `cvrp_100`, `cvrp_1000`, `cvrp_2000` and `cvrp_5000`, that you can directly use for inference.
|
620 |
|
621 |
```python
|
622 |
+
cd examples/cvrp
|
623 |
|
624 |
python solve.py --device cuda --model_name cvrp_5000.pt --problem_size 5000
|
625 |
```
|