Use objective functions for specific optimization goals
An objective is the goal in an optimization problem. It is the value to be minimized or maximized over the set of all feasible solutions that satisfy the problem constraints. When objectives are used, they are specified together as an array using the objectives property. Not all objectives need to be specified every time, and their presence/absence and the sequence in which they are listed is important, with objectives appearing on top of the list being more important than those appearing further down. A soft constraint considers other settings; a hard constraint is an imperative. Using the sequence allows you to achieve a multi-objective solution.
Note
The objectives
minimizeUnassignedandminimizeCostare mandatory. There will be an API error if they are omitted.
To understand the power of these objectives, consider a scenario where drivers are paid based on how long they work. In such cases, the dispatcher tries to structure the tour to take as little time as possible to reduce operational costs and improve efficiency. Objective functions allow you to impose this logic on your solution.
The objective minimizeDuration allows you to reduce the time of the tour. Here is an example of the objectives array you would add to your problem:
"objectives": [
{
"type": "minimizeUnassigned"
},
{
"type": "minimizeDuration"
},
{
"type": "minimizeCost"
}
]If you do not specify any objectives, by default the solver follows this logic:
- With highest importance, it minimizes the amount of unassigned jobs.
- With medium importance, it minimizes the number of tours.
- With least importance, it minimizes the total cost.
This is equivalent to the following definition:
"objectives": [
{
"type": "minimizeUnassigned"
},
{
"type": "optimizeTourCount"
"action": "minimize"
},
{
"type": "minimizeCost"
}
]For more information, including a list of all objectives, see Objectives.
Optimize task sequence use case
This section looks at how to use the objective optimizeTaskOrder to control the order of job task activities in the tour. Task order can be used as either a hard or soft constraint. When optimizeTaskOrder is a soft constraint, we still try to follow the order property value, but if it can lead to the job being unassigned, we let the order constraint be violated so that the job can still be assigned.
There are three different problem formulations, and the job type is pickup:
- First, there is a simple problem with a task order that cannot be fulfilled for some reason, for instance, due to conflicting time windows.
- Then, there is a problem to show that without the objective no tour will be created, but with the objective it will still create the tour.
- In the third problem, the time windows are removed and this shows that in that case the job order is obeyed. In this example, the shift end is not specified, so the shortest route is using open VRP and putting one job close to the depot and one further away. Then the solver will by default try first to serve the job close to the depot, because that gives an overall shorter tour.
Problem: No optimizeTaskOrder specification
Here is the base problem.
- VRP with a shift with distinct depot locations for the start and the end
- 3 jobs to be served
Job_1(with specified time window)Job_2Job_3can be comfortably served in sequence on the road from the location of the beginning of the shift.
See the solution.
{
"plan": {
"jobs": [
{
"id": "Job_1_1h_driving",
"tasks": {
"pickups": [
{
"places": [
{
"location": {
"lat": 49.066063,
"lng": 11.917153
},
"times": [
[
"2023-09-20T08:00:00Z",
"2023-09-20T10:00:00Z"
]
],
"duration": 3600
}
],
"demand": [
1
]
}
]
}
},
{
"id": "Job_2_2h_driving",
"tasks": {
"pickups": [
{
"places": [
{
"location": {
"lat": 49.526849,
"lng": 11.066198
},
"duration": 3600
}
],
"demand": [
1
]
}
]
}
},
{
"id": "Job_3_3h_driving",
"tasks": {
"pickups": [
{
"places": [
{
"location": {
"lat": 49.785296,
"lng": 10.129362
},
"duration": 3600
}
],
"demand": [
1
]
}
]
}
}
]
},
"fleet": {
"types": [
{
"id": "vehicle_1",
"profile": "normal_car",
"costs": {
"fixed": 22,
"distance": 0.0002,
"time": 0.0048
},
"shifts": [
{
"start": {
"time": "2023-09-20T08:00:00Z",
"location": {
"lat": 48.821421,
"lng": 12.931591
}
},
"end": {
"time": "2023-09-22T23:00:00Z",
"location": {
"lat": 50.912724,
"lng": 7.1055
}
}
}
],
"capacity": [
10
],
"amount": 1
}
],
"profiles": [
{
"name": "normal_car",
"type": "car",
"traffic": {
"mode": "disabled"
}
}
]
}
}Solution: No optimizeTaskOrder specification
{
"statistic": {
"cost": 275.6742,
"distance": 559867,
"duration": 29521,
"times": {
"driving": 18721,
"serving": 10800,
"waiting": 0,
"stopping": 0,
"break": 0
}
},
"tours": [
{
"vehicleId": "vehicle_1_1",
"typeId": "vehicle_1",
"stops": [
{
"time": {
"arrival": "2023-09-20T08:00:00Z",
"departure": "2023-09-20T08:00:00Z"
},
"load": [
0
],
"activities": [
{
"jobId": "departure",
"type": "departure",
"location": {
"lat": 48.821421,
"lng": 12.931591
},
"time": {
"start": "2023-09-20T08:00:00Z",
"end": "2023-09-20T08:00:00Z"
}
}
],
"location": {
"lat": 48.821421,
"lng": 12.931591
},
"distance": 0
},
{
"time": {
"arrival": "2023-09-20T08:50:15Z",
"departure": "2023-09-20T09:50:15Z"
},
"load": [
1
],
"activities": [
{
"jobId": "Job_1_1h_driving",
"type": "pickup",
"location": {
"lat": 49.066063,
"lng": 11.917153
},
"time": {
"start": "2023-09-20T08:50:15Z",
"end": "2023-09-20T09:50:15Z"
}
}
],
"location": {
"lat": 49.066063,
"lng": 11.917153
},
"distance": 86277
},
{
"time": {
"arrival": "2023-09-20T10:38:58Z",
"departure": "2023-09-20T11:38:58Z"
},
"load": [
2
],
"activities": [
{
"jobId": "Job_2_2h_driving",
"type": "pickup",
"location": {
"lat": 49.526849,
"lng": 11.066198
},
"time": {
"start": "2023-09-20T10:38:58Z",
"end": "2023-09-20T11:38:58Z"
}
}
],
"location": {
"lat": 49.526849,
"lng": 11.066198
},
"distance": 174491
},
{
"time": {
"arrival": "2023-09-20T12:29:23Z",
"departure": "2023-09-20T13:29:23Z"
},
"load": [
3
],
"activities": [
{
"jobId": "Job_3_3h_driving",
"type": "pickup",
"location": {
"lat": 49.785296,
"lng": 10.129362
},
"time": {
"start": "2023-09-20T12:29:23Z",
"end": "2023-09-20T13:29:23Z"
}
}
],
"location": {
"lat": 49.785296,
"lng": 10.129362
},
"distance": 258526
},
{
"time": {
"arrival": "2023-09-20T16:12:01Z",
"departure": "2023-09-20T16:12:01Z"
},
"load": [
0
],
"activities": [
{
"jobId": "arrival",
"type": "arrival",
"location": {
"lat": 50.912724,
"lng": 7.1055
},
"time": {
"start": "2023-09-20T16:12:01Z",
"end": "2023-09-20T16:12:01Z"
}
}
],
"location": {
"lat": 50.912724,
"lng": 7.1055
},
"distance": 559867
}
],
"statistic": {
"cost": 275.6742,
"distance": 559867,
"duration": 29521,
"times": {
"driving": 18721,
"serving": 10800,
"waiting": 0,
"stopping": 0,
"break": 0
}
},
"shiftIndex": 0
}
]
}
Problem: Task order as hard constraint
When optimizeTaskOrder is not specified, but the order property is defined on the job's level, it is considered as a hard constraint, which cannot be violated.
In this example, modify the initial file and change the order. Assume the need to serve Job_3 first, Job_2 second, and Job_1 should be third, while at the same time, Job_1 still needs to be served in the 8:00 - 9:00 time window.
See the solution.
{
"plan": {
"jobs": [
{
"id": "Job_1_1h_driving",
"tasks": {
"pickups": [
{
"places": [
{
"location": {
"lat": 49.066063,
"lng": 11.917153
},
"times": [
[
"2023-09-20T08:00:00Z",
"2023-09-20T10:00:00Z"
]
],
"duration": 3600
}
],
"demand": [
1
],
"order": 3
}
]
}
},
{
"id": "Job_2_2h_driving",
"tasks": {
"pickups": [
{
"places": [
{
"location": {
"lat": 49.526849,
"lng": 11.066198
},
"duration": 3600
}
],
"demand": [
1
],
"order": 2
}
]
}
},
{
"id": "Job_3_3h_driving",
"tasks": {
"pickups": [
{
"places": [
{
"location": {
"lat": 49.785296,
"lng": 10.129362
},
"duration": 3600
}
],
"demand": [
1
],
"order": 1
}
]
}
}
]
},
"fleet": {
"types": [
{
"id": "vehicle_1",
"profile": "normal_car",
"costs": {
"fixed": 22,
"distance": 0.0002,
"time": 0.0048
},
"shifts": [
{
"start": {
"time": "2023-09-20T08:00:00Z",
"location": {
"lat": 48.821421,
"lng": 12.931591
}
},
"end": {
"time": "2023-09-22T23:00:00Z",
"location": {
"lat": 50.912724,
"lng": 7.1055
}
}
}
],
"capacity": [
10
],
"amount": 1
}
],
"profiles": [
{
"name": "normal_car",
"type": "car",
"traffic": {
"mode": "disabled"
}
}
]
}
}Solution: Task order as hard constraint
Now the solution is different.
- Start with
Job_3, as required - Visit the location of
Job_2 - It is impossible to serve
Job_1third in the required time window, soJob_1is unassigned now - when a hard constraint is violated.
{
"statistic": {
"cost": 330.42359999999996,
"distance": 751702,
"duration": 32934,
"times": {
"driving": 25734,
"serving": 7200,
"waiting": 0,
"stopping": 0,
"break": 0
}
},
"tours": [
{
"vehicleId": "vehicle_1_1",
"typeId": "vehicle_1",
"stops": [
{
"time": {
"arrival": "2023-09-20T08:00:00Z",
"departure": "2023-09-20T08:00:00Z"
},
"load": [
0
],
"activities": [
{
"jobId": "departure",
"type": "departure",
"location": {
"lat": 48.821421,
"lng": 12.931591
},
"time": {
"start": "2023-09-20T08:00:00Z",
"end": "2023-09-20T08:00:00Z"
}
}
],
"location": {
"lat": 48.821421,
"lng": 12.931591
},
"distance": 0
},
{
"time": {
"arrival": "2023-09-20T10:29:23Z",
"departure": "2023-09-20T11:29:23Z"
},
"load": [
1
],
"activities": [
{
"jobId": "Job_3_3h_driving",
"type": "pickup",
"location": {
"lat": 49.785296,
"lng": 10.129362
},
"time": {
"start": "2023-09-20T10:29:23Z",
"end": "2023-09-20T11:29:23Z"
}
}
],
"location": {
"lat": 49.785296,
"lng": 10.129362
},
"distance": 258526
},
{
"time": {
"arrival": "2023-09-20T12:35:50Z",
"departure": "2023-09-20T13:35:50Z"
},
"load": [
2
],
"activities": [
{
"jobId": "Job_2_2h_driving",
"type": "pickup",
"location": {
"lat": 49.526849,
"lng": 11.066198
},
"time": {
"start": "2023-09-20T12:35:50Z",
"end": "2023-09-20T13:35:50Z"
}
}
],
"location": {
"lat": 49.526849,
"lng": 11.066198
},
"distance": 366326
},
{
"time": {
"arrival": "2023-09-20T17:08:54Z",
"departure": "2023-09-20T17:08:54Z"
},
"load": [
0
],
"activities": [
{
"jobId": "arrival",
"type": "arrival",
"location": {
"lat": 50.912724,
"lng": 7.1055
},
"time": {
"start": "2023-09-20T17:08:54Z",
"end": "2023-09-20T17:08:54Z"
}
}
],
"location": {
"lat": 50.912724,
"lng": 7.1055
},
"distance": 751702
}
],
"statistic": {
"cost": 330.42359999999996,
"distance": 751702,
"duration": 32934,
"times": {
"driving": 25734,
"serving": 7200,
"waiting": 0,
"stopping": 0,
"break": 0
}
},
"shiftIndex": 0
}
],
"unassigned": [
{
"jobId": "Job_1_1h_driving",
"reasons": [
{
"code": "TIME_WINDOW_CONSTRAINT",
"description": "cannot be assigned due to violation of time window",
"details": [
{
"vehicleId": "vehicle_1_1",
"shiftIndex": 0
}
]
}
]
}
]
}
Problem: Task order as soft constraint with time windows
When specified, optimizeTaskOrder is considered as a soft constraint, and the solver tries to minimize the number of violations, using time windows.
Here, you try to keep the new order, but instead of being unassigned, the constraint will be violated.
- Start with
Job_1, against the specified order, but within the specified time window. - Try to keep the order as much as possible, so go to the third job, that should have been first.
- Last, visit
Job_2. This is a compromise: the order is preserved whenever possible, but not at the cost of unassigned jobs, as that objective has been specified to be more important.
See the solution.
{ "objectives": [
{
"type": "minimizeUnassigned"
},
{
"type": "optimizeTaskOrder"
},
{
"type": "minimizeCost"
}
],
"plan": {
"jobs": [
{
"id": "Job_1_1h_driving",
"tasks": {
"pickups": [
{
"places": [
{
"location": {
"lat": 49.066063,
"lng": 11.917153
},
"times": [
[
"2023-09-20T08:00:00Z",
"2023-09-20T10:00:00Z"
]
],
"duration": 3600
}
],
"demand": [
1
],
"order": 3
}
]
}
},
{
"id": "Job_2_2h_driving",
"tasks": {
"pickups": [
{
"places": [
{
"location": {
"lat": 49.526849,
"lng": 11.066198
},
"duration": 3600
}
],
"demand": [
1
],
"order": 2
}
]
}
},
{
"id": "Job_3_3h_driving",
"tasks": {
"pickups": [
{
"places": [
{
"location": {
"lat": 49.785296,
"lng": 10.129362
},
"duration": 3600
}
],
"demand": [
1
],
"order":1
}
]
}
}
]
},
"fleet": {
"types": [
{
"id": "vehicle_1",
"profile": "normal_car",
"costs": {
"fixed": 22,
"distance": 0.0002,
"time": 0.0048
},
"shifts": [
{
"start": {
"time": "2023-09-20T08:00:00Z",
"location": {
"lat": 48.821421,
"lng": 12.931591
}
},
"end": {
"time": "2023-09-22T23:00:00Z",
"location": {
"lat": 50.912724,
"lng": 7.1055
}
}
}
],
"capacity": [
10
],
"amount": 1
}
],
"profiles": [
{
"name": "normal_car",
"type": "car",
"traffic": {
"mode": "disabled"
}
}
]
}
}Solution: Task order as soft constraint with time windows
{
"statistic": {
"cost": 347.7036,
"distance": 751702,
"duration": 36534,
"times": {
"driving": 25734,
"serving": 10800,
"waiting": 0,
"stopping": 0,
"break": 0
}
},
"tours": [
{
"vehicleId": "vehicle_1_1",
"typeId": "vehicle_1",
"stops": [
{
"time": {
"arrival": "2023-09-20T08:00:00Z",
"departure": "2023-09-20T08:00:00Z"
},
"load": [
0
],
"activities": [
{
"jobId": "departure",
"type": "departure",
"location": {
"lat": 48.821421,
"lng": 12.931591
},
"time": {
"start": "2023-09-20T08:00:00Z",
"end": "2023-09-20T08:00:00Z"
}
}
],
"location": {
"lat": 48.821421,
"lng": 12.931591
},
"distance": 0
},
{
"time": {
"arrival": "2023-09-20T08:50:15Z",
"departure": "2023-09-20T09:50:15Z"
},
"load": [
1
],
"activities": [
{
"jobId": "Job_1_1h_driving",
"type": "pickup",
"location": {
"lat": 49.066063,
"lng": 11.917153
},
"time": {
"start": "2023-09-20T08:50:15Z",
"end": "2023-09-20T09:50:15Z"
}
}
],
"location": {
"lat": 49.066063,
"lng": 11.917153
},
"distance": 86277
},
{
"time": {
"arrival": "2023-09-20T11:29:23Z",
"departure": "2023-09-20T12:29:23Z"
},
"load": [
2
],
"activities": [
{
"jobId": "Job_3_3h_driving",
"type": "pickup",
"location": {
"lat": 49.785296,
"lng": 10.129362
},
"time": {
"start": "2023-09-20T11:29:23Z",
"end": "2023-09-20T12:29:23Z"
}
}
],
"location": {
"lat": 49.785296,
"lng": 10.129362
},
"distance": 258526
},
{
"time": {
"arrival": "2023-09-20T13:35:50Z",
"departure": "2023-09-20T14:35:50Z"
},
"load": [
3
],
"activities": [
{
"jobId": "Job_2_2h_driving",
"type": "pickup",
"location": {
"lat": 49.526849,
"lng": 11.066198
},
"time": {
"start": "2023-09-20T13:35:50Z",
"end": "2023-09-20T14:35:50Z"
}
}
],
"location": {
"lat": 49.526849,
"lng": 11.066198
},
"distance": 366326
},
{
"time": {
"arrival": "2023-09-20T18:08:54Z",
"departure": "2023-09-20T18:08:54Z"
},
"load": [
0
],
"activities": [
{
"jobId": "arrival",
"type": "arrival",
"location": {
"lat": 50.912724,
"lng": 7.1055
},
"time": {
"start": "2023-09-20T18:08:54Z",
"end": "2023-09-20T18:08:54Z"
}
}
],
"location": {
"lat": 50.912724,
"lng": 7.1055
},
"distance": 751702
}
],
"statistic": {
"cost": 347.7036,
"distance": 751702,
"duration": 36534,
"times": {
"driving": 25734,
"serving": 10800,
"waiting": 0,
"stopping": 0,
"break": 0
}
},
"shiftIndex": 0
}
]
}
Problem: Task order as soft constraint with no time windows
In this problem, the task order is considered a soft constraint and the resolution tries to minimize the number of violations using no time windows.
Assume now that it is not necessary to deliver Job_1 within a specified time window. In this situation, all the jobs can be served in the desired order without unassignment. Therefore, whether you use task order as a hard or soft constraint, you would get the same job order - Job_3, Job_2 and then Job_1.
See the solution.
{ "objectives": [
{
"type": "minimizeUnassigned"
},
{
"type": "optimizeTaskOrder"
},
{
"type": "minimizeCost"
}
],
"plan": {
"jobs": [
{
"id": "Job_1_1h_driving",
"tasks": {
"pickups": [
{
"places": [
{
"location": {
"lat": 49.066063,
"lng": 11.917153
},
"duration": 3600
}
],
"demand": [
1
],
"order": 3
}
]
}
},
{
"id": "Job_2_2h_driving",
"tasks": {
"pickups": [
{
"places": [
{
"location": {
"lat": 49.526849,
"lng": 11.066198
},
"duration": 3600
}
],
"demand": [
1
],
"order": 2
}
]
}
},
{
"id": "Job_3_3h_driving",
"tasks": {
"pickups": [
{
"places": [
{
"location": {
"lat": 49.785296,
"lng": 10.129362
},
"duration": 3600
}
],
"demand": [
1
],
"order":1
}
]
}
}
]
},
"fleet": {
"types": [
{
"id": "vehicle_1",
"profile": "normal_car",
"costs": {
"fixed": 22,
"distance": 0.0002,
"time": 0.0048
},
"shifts": [
{
"start": {
"time": "2023-09-20T08:00:00Z",
"location": {
"lat": 48.821421,
"lng": 12.931591
}
},
"end": {
"time": "2023-09-22T23:00:00Z",
"location": {
"lat": 50.912724,
"lng": 7.1055
}
}
}
],
"capacity": [
10
],
"amount": 1
}
],
"profiles": [
{
"name": "normal_car",
"type": "car",
"traffic": {
"mode": "disabled"
}
}
]
}
}Solution: Task order as soft constraint with no time windows
{
"statistic": {
"cost": 418.1166,
"distance": 945823,
"duration": 43115,
"times": {
"driving": 32315,
"serving": 10800,
"waiting": 0,
"stopping": 0,
"break": 0
}
},
"tours": [
{
"vehicleId": "vehicle_1_1",
"typeId": "vehicle_1",
"stops": [
{
"time": {
"arrival": "2023-09-20T08:00:00Z",
"departure": "2023-09-20T08:00:00Z"
},
"load": [
0
],
"activities": [
{
"jobId": "departure",
"type": "departure",
"location": {
"lat": 48.821421,
"lng": 12.931591
},
"time": {
"start": "2023-09-20T08:00:00Z",
"end": "2023-09-20T08:00:00Z"
}
}
],
"location": {
"lat": 48.821421,
"lng": 12.931591
},
"distance": 0
},
{
"time": {
"arrival": "2023-09-20T10:29:23Z",
"departure": "2023-09-20T11:29:23Z"
},
"load": [
1
],
"activities": [
{
"jobId": "Job_3_3h_driving",
"type": "pickup",
"location": {
"lat": 49.785296,
"lng": 10.129362
},
"time": {
"start": "2023-09-20T10:29:23Z",
"end": "2023-09-20T11:29:23Z"
}
}
],
"location": {
"lat": 49.785296,
"lng": 10.129362
},
"distance": 258526
},
{
"time": {
"arrival": "2023-09-20T12:35:50Z",
"departure": "2023-09-20T13:35:50Z"
},
"load": [
2
],
"activities": [
{
"jobId": "Job_2_2h_driving",
"type": "pickup",
"location": {
"lat": 49.526849,
"lng": 11.066198
},
"time": {
"start": "2023-09-20T12:35:50Z",
"end": "2023-09-20T13:35:50Z"
}
}
],
"location": {
"lat": 49.526849,
"lng": 11.066198
},
"distance": 366326
},
{
"time": {
"arrival": "2023-09-20T14:36:48Z",
"departure": "2023-09-20T15:36:48Z"
},
"load": [
3
],
"activities": [
{
"jobId": "Job_1_1h_driving",
"type": "pickup",
"location": {
"lat": 49.066063,
"lng": 11.917153
},
"time": {
"start": "2023-09-20T14:36:48Z",
"end": "2023-09-20T15:36:48Z"
}
}
],
"location": {
"lat": 49.066063,
"lng": 11.917153
},
"distance": 472233
},
{
"time": {
"arrival": "2023-09-20T19:58:35Z",
"departure": "2023-09-20T19:58:35Z"
},
"load": [
0
],
"activities": [
{
"jobId": "arrival",
"type": "arrival",
"location": {
"lat": 50.912724,
"lng": 7.1055
},
"time": {
"start": "2023-09-20T19:58:35Z",
"end": "2023-09-20T19:58:35Z"
}
}
],
"location": {
"lat": 50.912724,
"lng": 7.1055
},
"distance": 945823
}
],
"statistic": {
"cost": 418.1166,
"distance": 945823,
"duration": 43115,
"times": {
"driving": 32315,
"serving": 10800,
"waiting": 0,
"stopping": 0,
"break": 0
}
},
"shiftIndex": 0
}
]
}
Optimize vehicle use case
The objective optimizeTourCount controls the use of vehicles to optimize the number of tours in the solution.
In this case the depot is in between the two jobs to be delivered.
-
In this example, there are 2 identical vehicles with capacity 2 and no fixed cost, so whether you use one or two vehicles to deliver the job, only time and distance are taken into account.
-
If you use one vehicle, both time and duration will be higher. This is because you need to visit the first job, then visit the second job and only then come back to the depot.
You can do this with the objective optimizeTourCount using the action attributes maximize and minimize.
- If
action=maximize, both vehicles are used. - If
action=minimize, then only one vehicle is used for both jobs.
Problem: Omit optimizeTourCount
This problem is expressed without using the optimizeTourCount objective.
Without specified objectives, as mentioned previously, optimizeTourCount is higher than minimizeCost. That means, by default, you will use the fewest vehicles possible, even if it will make the cost higher.
See the solution.
{
"fleet": {
"types": [
{
"profile": "vehicle",
"amount": 2,
"capacity": [
2
],
"costs": {
"distance": 5,
"time": 5,
"fixed": 0
},
"id": "vehicle_1",
"shifts": [
{
"start": {
"location": {
"lat": 52.55364,
"lng": 13.41467
},
"time": "2023-11-30T08:00:00+01:00"
}
}
]
}
],
"profiles": [
{
"name": "vehicle",
"type": "car"
}
]
},
"plan": {
"jobs": [
{
"id": "Job_1",
"tasks": {
"deliveries": [
{
"places": [
{
"duration": 300,
"location": {
"lat": 52.55635,
"lng": 13.38465
}
}
],
"demand": [
1
]
}
]
}
},
{
"id": "Job_2",
"tasks": {
"deliveries": [
{
"places": [
{
"duration": 300,
"location": {
"lat": 52.55162,
"lng": 13.43017
}
}
],
"demand": [
1
]
}
]
}
}
]
}
}Solution: Omit optimizeTourCount
{
"statistic": {
"cost": 28475,
"distance": 4521,
"duration": 1174,
"times": {
"driving": 574,
"serving": 600,
"waiting": 0,
"stopping": 0,
"break": 0
}
},
"tours": [
{
"vehicleId": "vehicle_1_2",
"typeId": "vehicle_1",
"stops": [
{
"location": {
"lat": 52.55364,
"lng": 13.41467
},
"time": {
"arrival": "2023-11-30T07:00:00Z",
"departure": "2023-11-30T07:00:00Z"
},
"load": [
2
],
"activities": [
{
"jobId": "departure",
"type": "departure",
"location": {
"lat": 52.55364,
"lng": 13.41467
},
"time": {
"start": "2023-11-30T07:00:00Z",
"end": "2023-11-30T07:00:00Z"
}
}
],
"distance": 0
},
{
"location": {
"lat": 52.55162,
"lng": 13.43017
},
"time": {
"arrival": "2023-11-30T07:03:06Z",
"departure": "2023-11-30T07:08:06Z"
},
"load": [
1
],
"activities": [
{
"jobId": "Job_2",
"type": "delivery",
"location": {
"lat": 52.55162,
"lng": 13.43017
},
"time": {
"start": "2023-11-30T07:03:06Z",
"end": "2023-11-30T07:08:06Z"
}
}
],
"distance": 1280
},
{
"location": {
"lat": 52.55635,
"lng": 13.38465
},
"time": {
"arrival": "2023-11-30T07:14:34Z",
"departure": "2023-11-30T07:19:34Z"
},
"load": [
0
],
"activities": [
{
"jobId": "Job_1",
"type": "delivery",
"location": {
"lat": 52.55635,
"lng": 13.38465
},
"time": {
"start": "2023-11-30T07:14:34Z",
"end": "2023-11-30T07:19:34Z"
}
}
],
"distance": 4521
}
],
"statistic": {
"cost": 28475,
"distance": 4521,
"duration": 1174,
"times": {
"driving": 574,
"serving": 600,
"waiting": 0,
"stopping": 0,
"break": 0
}
},
"shiftIndex": 0
}
]
}Problem: Specify optimizeTourCount to use whole fleet
You can use objectives to change this behavior. If you set the objective to use the whole fleet, you achieve a cheaper solution. You use both vehicles, each of them starts at the depot, delivers only one job, and comes back to the depot. The vehicles use the shortest east-west routes.
Note
Since more tours often incur higher cost, you should place the
optimizeTourCountobjective with"action": "maximize"above theminimizeCostobjective so that it will not overwrite its effect.
See the solution.
{
"objectives": [
{
"type": "minimizeUnassigned"
},
{
"type": "optimizeTourCount",
"action": "maximize"
},
{
"type": "minimizeCost"
}
],
"fleet": {
"types": [
{
"profile": "vehicle",
"amount": 2,
"capacity": [
2
],
"costs": {
"distance": 5,
"time": 5,
"fixed": 0
},
"id": "vehicle_1",
"shifts": [
{
"start": {
"location": {
"lat": 52.55364,
"lng": 13.41467
},
"time": "2023-11-30T08:00:00+01:00"
}
}
]
}
],
"profiles": [
{
"name": "vehicle",
"type": "car"
}
]
},
"plan": {
"jobs": [
{
"id": "Job_1",
"tasks": {
"deliveries": [
{
"places": [
{
"duration": 300,
"location": {
"lat": 52.55635,
"lng": 13.38465
}
}
],
"demand": [
1
]
}
]
}
},
{
"id": "Job_2",
"tasks": {
"deliveries": [
{
"places": [
{
"duration": 300,
"location": {
"lat": 52.55162,
"lng": 13.43017
}
}
],
"demand": [
1
]
}
]
}
}
]
}
}Solution: Specify optimizeTourCount to use whole fleet
{
"statistic": {
"cost": 22315,
"distance": 3421,
"duration": 1042,
"times": {
"driving": 442,
"serving": 600,
"waiting": 0,
"stopping": 0,
"break": 0
}
},
"tours": [
{
"vehicleId": "vehicle_1_2",
"typeId": "vehicle_1",
"stops": [
{
"location": {
"lat": 52.55364,
"lng": 13.41467
},
"time": {
"arrival": "2023-11-30T07:00:00Z",
"departure": "2023-11-30T07:00:00Z"
},
"load": [
1
],
"activities": [
{
"jobId": "departure",
"type": "departure",
"location": {
"lat": 52.55364,
"lng": 13.41467
},
"time": {
"start": "2023-11-30T07:00:00Z",
"end": "2023-11-30T07:00:00Z"
}
}
],
"distance": 0
},
{
"location": {
"lat": 52.55162,
"lng": 13.43017
},
"time": {
"arrival": "2023-11-30T07:03:06Z",
"departure": "2023-11-30T07:08:06Z"
},
"load": [
0
],
"activities": [
{
"jobId": "Job_2",
"type": "delivery",
"location": {
"lat": 52.55162,
"lng": 13.43017
},
"time": {
"start": "2023-11-30T07:03:06Z",
"end": "2023-11-30T07:08:06Z"
}
}
],
"distance": 1280
}
],
"statistic": {
"cost": 8830,
"distance": 1280,
"duration": 486,
"times": {
"driving": 186,
"serving": 300,
"waiting": 0,
"stopping": 0,
"break": 0
}
},
"shiftIndex": 0
},
{
"vehicleId": "vehicle_1_1",
"typeId": "vehicle_1",
"stops": [
{
"location": {
"lat": 52.55364,
"lng": 13.41467
},
"time": {
"arrival": "2023-11-30T07:00:00Z",
"departure": "2023-11-30T07:00:00Z"
},
"load": [
1
],
"activities": [
{
"jobId": "departure",
"type": "departure",
"location": {
"lat": 52.55364,
"lng": 13.41467
},
"time": {
"start": "2023-11-30T07:00:00Z",
"end": "2023-11-30T07:00:00Z"
}
}
],
"distance": 0
},
{
"location": {
"lat": 52.55635,
"lng": 13.38465
},
"time": {
"arrival": "2023-11-30T07:04:16Z",
"departure": "2023-11-30T07:09:16Z"
},
"load": [
0
],
"activities": [
{
"jobId": "Job_1",
"type": "delivery",
"location": {
"lat": 52.55635,
"lng": 13.38465
},
"time": {
"start": "2023-11-30T07:04:16Z",
"end": "2023-11-30T07:09:16Z"
}
}
],
"distance": 2141
}
],
"statistic": {
"cost": 13485,
"distance": 2141,
"duration": 556,
"times": {
"driving": 256,
"serving": 300,
"waiting": 0,
"stopping": 0,
"break": 0
}
},
"shiftIndex": 0
}
]
}Problem: Specify optimizeTourCount to use fewest vehicles
This solution uses the fewest number of vehicles. minimizeCost often pushes to this solution.
If you use optimizeTourCount with "action": "minimize" explicitly, you will get the same solution as the default. In the majority of cases, fewer trips would lead to cost savings.
{
"objectives": [
{
"type": "minimizeUnassigned"
},
{
"type": "optimizeTourCount",
"action": "minimize"
},
{
"type": "minimizeCost"
}
],
"fleet": {
"types": [
{
"profile": "vehicle",
"amount": 2,
"capacity": [
2
],
"costs": {
"distance": 5,
"time": 5,
"fixed": 0
},
"id": "vehicle_1",
"shifts": [
{
"start": {
"location": {
"lat": 52.55364,
"lng": 13.41467
},
"time": "2023-11-30T08:00:00+01:00"
}
}
]
}
],
"profiles": [
{
"name": "vehicle",
"type": "car"
}
]
},
"plan": {
"jobs": [
{
"id": "Job_1",
"tasks": {
"deliveries": [
{
"places": [
{
"duration": 300,
"location": {
"lat": 52.55635,
"lng": 13.38465
}
}
],
"demand": [
1
]
}
]
}
},
{
"id": "Job_2",
"tasks": {
"deliveries": [
{
"places": [
{
"duration": 300,
"location": {
"lat": 52.55162,
"lng": 13.43017
}
}
],
"demand": [
1
]
}
]
}
}
]
}
}Solution: Specify optimizeTourCount to use fewest vehicles
{
"statistic": {
"cost": 28475,
"distance": 4521,
"duration": 1174,
"times": {
"driving": 574,
"serving": 600,
"waiting": 0,
"stopping": 0,
"break": 0
}
},
"tours": [
{
"vehicleId": "vehicle_1_1",
"typeId": "vehicle_1",
"stops": [
{
"location": {
"lat": 52.55364,
"lng": 13.41467
},
"time": {
"arrival": "2023-11-30T07:00:00Z",
"departure": "2023-11-30T07:00:00Z"
},
"load": [
2
],
"activities": [
{
"jobId": "departure",
"type": "departure",
"location": {
"lat": 52.55364,
"lng": 13.41467
},
"time": {
"start": "2023-11-30T07:00:00Z",
"end": "2023-11-30T07:00:00Z"
}
}
],
"distance": 0
},
{
"location": {
"lat": 52.55162,
"lng": 13.43017
},
"time": {
"arrival": "2023-11-30T07:03:06Z",
"departure": "2023-11-30T07:08:06Z"
},
"load": [
1
],
"activities": [
{
"jobId": "Job_2",
"type": "delivery",
"location": {
"lat": 52.55162,
"lng": 13.43017
},
"time": {
"start": "2023-11-30T07:03:06Z",
"end": "2023-11-30T07:08:06Z"
}
}
],
"distance": 1280
},
{
"location": {
"lat": 52.55635,
"lng": 13.38465
},
"time": {
"arrival": "2023-11-30T07:14:34Z",
"departure": "2023-11-30T07:19:34Z"
},
"load": [
0
],
"activities": [
{
"jobId": "Job_1",
"type": "delivery",
"location": {
"lat": 52.55635,
"lng": 13.38465
},
"time": {
"start": "2023-11-30T07:14:34Z",
"end": "2023-11-30T07:19:34Z"
}
}
],
"distance": 4521
}
],
"statistic": {
"cost": 28475,
"distance": 4521,
"duration": 1174,
"times": {
"driving": 574,
"serving": 600,
"waiting": 0,
"stopping": 0,
"break": 0
}
},
"shiftIndex": 0
}
]
}Updated 28 days ago