Unassigned penalties
You will learn how to use the Unassigned option with a practical example.
Vehicle routing problems (VRPs) are infeasible if no solution satisfies the set of considerations specific to the problem. For example, stop quantities may exceed a vehicle's capacity, in which case the vehicle can not service all stops. Another example is a vehicle having to fulfill time windows but not being able to arrive at a certain stop in the desired time frame. When a problem is infeasible, the router engine will return an empty solution state because, by default, it must assign all stops to the solution.
The router engine provides the Unassigned option to configure penalties for stops. When the stops can not be serviced due to a consideration (constraint) being violated, the engine attempts to make the solution feasible by leaving stops unassigned. The penalties are added to the value of the solution for the stops that are not assigned. Normally, the value of these penalties is larger than the assignment cost.

Example

The capacity example is used as a base, where routes are created to visit seven landmarks in Kyoto using two vehicles that have capacities that must be respected. This time, the stops' demands exceed the vehicles' capacity and penalties are provided to leave stops unassigned.
unassigned-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
"quantities": [-1, -1, -1, -1, -1, -1, -1],
34
"capacities": [2, 2],
35
"penalties": [10000, 10000, 10000, 10000, 10000, 10000, 10000]
36
}
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
Quantities []int `json:"quantities,omitempty"`
14
Capacities []int `json:"capacities,omitempty"`
15
Penalties []int `json:"penalties,omitempty"`
16
}
17
18
// Use the CLI runner to solve a Vehicle Routing Problem.
19
func main() {
20
f := func(i input, opt solve.Options) (solve.Solver, error) {
21
router, err := route.NewRouter(
22
i.Stops,
23
i.Vehicles,
24
route.Capacity(i.Quantities, i.Capacities),
25
route.Unassigned(i.Penalties),
26
)
27
if err != nil {
28
return nil, err
29
}
30
31
return router.Solver(opt)
32
}
33
34
cli.Run(f)
35
}
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
{
4
"id": "Fushimi Inari Taisha",
5
"position": {
6
"lon": 135.772695,
7
"lat": 34.967146
8
}
9
},
10
{
11
"id": "Kinkaku-ji",
12
"position": {
13
"lon": 135.728898,
14
"lat": 35.039705
15
}
16
},
17
{
18
"id": "Arashiyama Bamboo Forest",
19
"position": {
20
"lon": 135.672009,
21
"lat": 35.017209
22
}
23
}
24
],
25
"vehicles": [
26
{
27
"id": "v1",
28
"route": [
29
{
30
"id": "Nijō Castle",
31
"position": {
32
"lon": 135.748134,
33
"lat": 35.014239
34
}
35
},
36
{
37
"id": "Kyoto Imperial Palace",
38
"position": {
39
"lon": 135.762057,
40
"lat": 35.025431
41
}
42
}
43
],
44
"route_duration": 177
45
},
46
{
47
"id": "v2",
48
"route": [
49
{
50
"id": "Kiyomizu-dera",
51
"position": {
52
"lon": 135.78506,
53
"lat": 34.994857
54
}
55
},
56
{
57
"id": "Gionmachi",
58
"position": {
59
"lon": 135.775682,
60
"lat": 35.002457
61
}
62
}
63
],
64
"route_duration": 120
65
}
66
]
67
}
Copied!
You can see that there are three stops that were not assigned, as the total stop quantity exceeds the total vehicle capacity. The stops that are assigned minimize the total distance.
unassigned-output
Export as PDF
Copy link