Vehicle sorter
You will learn how to use the Sorter option with a practical example.
By default the router engine determines the order in which the solver attempts to assign locations to vehicles by predicting the cost of that assignment. However, when the value function is overridden, the cost prediction for an assignment should also be adjusted. Its goal is to order vehicles in such a way that those vehicles come first where the assignment of the given locations is cheap in terms of the overridden value function. The router engine provides the Sorter option to set up a custom sorter for vehicles. This is done by writing a function that returns a sorted list of vehicle indices.

Example

The router example is used as a base, where routes are created to visit seven landmarks in Kyoto using two vehicles and a score for each stop. This time, we define a function in code, not in the input file, to create a custom sorter. Note that this change is only needed when you also change the value function and this example is only to demonstrate the usage of the option. To keep the example simple it does not evaluate the cost of an assignment in terms of a changed value function.
sorter-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
"score": [1, 2]
34
}
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. We create a function to sort the vehicles tried for assignment based on a score which we then pass into the Sorter option.
Go
1
package main
2
3
import (
4
"sort"
5
6
"github.com/nextmv-io/code/engines/route"
7
fleetEngine "github.com/nextmv-io/code/engines/route/fleet"
8
"github.com/nextmv-io/code/hop/model"
9
"github.com/nextmv-io/code/hop/run/cli"
10
"github.com/nextmv-io/code/hop/solve"
11
)
12
13
// Struct to read from JSON in.
14
type input struct {
15
Stops []route.Stop `json:"stops,omitempty"`
16
Vehicles []string `json:"vehicles,omitempty"`
17
Score []int `json:"score,omitempty"`
18
}
19
20
// Use the CLI runner to solve a Vehicle Routing Problem.
21
func main() {
22
f := func(i input, opt solve.Options) (solve.Solver, error) {
23
// Define a vehicle sorter. This vehicle sorter sorts the vehicles that
24
// can service the selected stops and returns their vehicle indices in
25
// the sorted order.
26
sorter := func(s fleetEngine.State, stops, vehicles model.IntDomain) []int {
27
vehicleIndices := vehicles.Slice()
28
sort.SliceStable(vehicleIndices, func(j, k int) bool {
29
return i.Score[j] > i.Score[k]
30
})
31
return vehicleIndices
32
}
33
router, err := route.NewRouter(
34
i.Stops,
35
i.Vehicles,
36
route.Sorter(sorter),
37
)
38
if err != nil {
39
return nil, err
40
}
41
42
return router.Solver(opt)
43
}
44
45
cli.Run(f)
46
}
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": "Kinkaku-ji",
9
"position": {
10
"lon": 135.728898,
11
"lat": 35.039705
12
}
13
},
14
{
15
"id": "Nijō Castle",
16
"position": {
17
"lon": 135.748134,
18
"lat": 35.014239
19
}
20
},
21
{
22
"id": "Kyoto Imperial Palace",
23
"position": {
24
"lon": 135.762057,
25
"lat": 35.025431
26
}
27
},
28
{
29
"id": "Gionmachi",
30
"position": {
31
"lon": 135.775682,
32
"lat": 35.002457
33
}
34
},
35
{
36
"id": "Kiyomizu-dera",
37
"position": {
38
"lon": 135.78506,
39
"lat": 34.994857
40
}
41
},
42
{
43
"id": "Fushimi Inari Taisha",
44
"position": {
45
"lon": 135.772695,
46
"lat": 34.967146
47
}
48
}
49
],
50
"route_duration": 1242
51
},
52
{
53
"id": "v2",
54
"route": [
55
{
56
"id": "Arashiyama Bamboo Forest",
57
"position": {
58
"lon": 135.672009,
59
"lat": 35.017209
60
}
61
}
62
],
63
"route_duration": 0
64
}
65
]
66
}
Copied!
To find this solution, the highest priority stops were selected and assigned first.
sorter-output
Export as PDF
Copy link