Vehicle shifts
You will learn how to use the Shifts option with a practical example.
In vehicle routing problems (VRPs) it is often necessary to not only know the best route but also the estimated time of arrival (ETA) per stop on the route to inform customers. By default the router engine provides the route duration but not the ETA because the time reference for the start of the route is missing. To add this information the router engine provides the Shifts option. If the given shifts not only specify the start times but also a shift's end time, the router engine will make sure that the routes are completed within the given shift's start and end.
The Shifts option is often combined with the Windows option and the Services option.

Example

The router example is used as a base, where routes are created to visit seven landmarks in Kyoto using two vehicles. This time, we define shifts for both vehicles, but only one vehicle has a start and end time defined.
shifts-input
Save the following information in an input.json file (see input and output for more information on working with input files).
JSON
1
{
2
"stops": [
3
{
4
"id": "Fushimi Inari Taisha",
5
"position": { "lon": 135.772695, "lat": 34.967146 }
6
},
7
{
8
"id": "Kiyomizu-dera",
9
"position": { "lon": 135.78506, "lat": 34.994857 }
10
},
11
{
12
"id": "Nijō Castle",
13
"position": { "lon": 135.748134, "lat": 35.014239 }
14
},
15
{
16
"id": "Kyoto Imperial Palace",
17
"position": { "lon": 135.762057, "lat": 35.025431 }
18
},
19
{
20
"id": "Gionmachi",
21
"position": { "lon": 135.775682, "lat": 35.002457 }
22
},
23
{
24
"id": "Kinkaku-ji",
25
"position": { "lon": 135.728898, "lat": 35.039705 }
26
},
27
{
28
"id": "Arashiyama Bamboo Forest",
29
"position": { "lon": 135.672009, "lat": 35.017209 }
30
}
31
],
32
"vehicles": ["v1", "v2"],
33
"shifts": [
34
{
35
"start": "2020-10-17T10:00:00-06:00",
36
"end": "2020-10-17T10:15:00-06:00"
37
},
38
{
39
"start": "2020-10-17T12:00:00-06:00"
40
}
41
]
42
}
Copied!

Code

The following program uses the CLI Runner to obtain a solution and requires access to the Nextmv code repository on GitHub. To request access, please contact [email protected].
To proceed with running the example, create a main.go file and use the code snippet below.
Go
1
package main
2
3
import (
4
"github.com/nextmv-io/code/engines/route"
5
"github.com/nextmv-io/code/hop/run/cli"
6
"github.com/nextmv-io/code/hop/solve"
7
)
8
9
// Struct to read from JSON in.
10
type input struct {
11
Stops []route.Stop `json:"stops,omitempty"`
12
Vehicles []string `json:"vehicles,omitempty"`
13
Shifts []route.TimeWindow `json:"shifts,omitempty"`
14
}
15
16
// Use the CLI runner to solve a Vehicle Routing Problem.
17
func main() {
18
f := func(i input, opt solve.Options) (solve.Solver, error) {
19
router, err := route.NewRouter(
20
i.Stops,
21
i.Vehicles,
22
route.Shifts(i.Shifts),
23
)
24
if err != nil {
25
return nil, err
26
}
27
28
return router.Solver(opt)
29
}
30
31
cli.Run(f)
32
}
Copied!
To execute the example, specify the path to the input.json file using command-line flags and use jq to extract the solution state (see runners for more information on building and running programs).
Bash
1
go run main.go -hop.runner.input.path input.json | jq .state
Copied!

Solution

The solution should look similar to this one:
JSON
1
{
2
"unassigned": [],
3
"vehicles": [
4
{
5
"id": "v1",
6
"route": [
7
{
8
"id": "Arashiyama Bamboo Forest",
9
"position": {
10
"lon": 135.672009,
11
"lat": 35.017209
12
},
13
"estimated_arrival": "2020-10-17T10:00:00-06:00",
14
"estimated_departure": "2020-10-17T10:00:00-06:00"
15
}
16
],
17
"route_duration": 0
18
},
19
{
20
"id": "v2",
21
"route": [
22
{
23
"id": "Fushimi Inari Taisha",
24
"position": {
25
"lon": 135.772695,
26
"lat": 34.967146
27
},
28
"estimated_arrival": "2020-10-17T12:00:00-06:00",
29
"estimated_departure": "2020-10-17T12:00:00-06:00"
30
},
31
{
32
"id": "Kiyomizu-dera",
33
"position": {
34
"lon": 135.78506,
35
"lat": 34.994857
36
},
37
"estimated_arrival": "2020-10-17T12:05:28-06:00",
38
"estimated_departure": "2020-10-17T12:05:28-06:00"
39
},
40
{
41
"id": "Gionmachi",
42
"position": {
43
"lon": 135.775682,
44
"lat": 35.002457
45
},
46
"estimated_arrival": "2020-10-17T12:07:28-06:00",
47
"estimated_departure": "2020-10-17T12:07:28-06:00"
48
},
49
{
50
"id": "Kyoto Imperial Palace",
51
"position": {
52
"lon": 135.762057,
53
"lat": 35.025431
54
},
55
"estimated_arrival": "2020-10-17T12:12:12-06:00",
56
"estimated_departure": "2020-10-17T12:12:12-06:00"
57
},
58
{
59
"id": "Nijō Castle",
60
"position": {
61
"lon": 135.748134,
62
"lat": 35.014239
63
},
64
"estimated_arrival": "2020-10-17T12:15:10-06:00",
65
"estimated_departure": "2020-10-17T12:15:10-06:00"
66
},
67
{
68
"id": "Kinkaku-ji",
69
"position": {
70
"lon": 135.728898,
71
"lat": 35.039705
72
},
73
"estimated_arrival": "2020-10-17T12:20:43-06:00",
74
"estimated_departure": "2020-10-17T12:20:43-06:00"
75
}
76
],
77
"route_duration": 1242
78
}
79
]
80
}
Copied!
The output now includes estimated time of arrival and departure (note, these times are equal for each stop in this example) in addition to route duration. To take into account service times at a stop, please use the Services option. Because of the short time window that the shift offers for v1, most stops are assigned to v2.
shifts-output
Export as PDF
Copy link