This feature is only available for Platform. You can find a list of all available features here.

This how-to guide assumes you already completed the get started with vehicle routing tutorial.

An objective is a soft rule that is used to evaluate the quality of a solution, by giving it a numeric value. It is also known as value function. In vehicle routing it is common to search for the solution that minimizes the total value of the objective. For example, you may want to minimize the total travel duration across all vehicles in a solution.

We offer the `ModelObjectiveSum`

as the main objective that is optimized by the solver. It is a linear sum of multiple objectives, each with a factor that determines its weight, as given by the following expression:

Where:

`factor_i`

is the weight of the`objective_i`

.`objective_i`

is the`i`

-th objective and is represented by a`float`

value.

This `model_objective_sum`

, by default, is composed of one objective. This objective is the travel duration of all the vehicles in the solution, using a factor of 1.0. You can always override this behavior using the `-model.objectives.travelduration`

option.

We offer many objectives that can be added to the `model_objective_sum`

out of the box, for example:

- Early arrival time penalty
- Late arrival time penalty
- Stop unplanned penalty
- Vehicle activation penalty
- Min stops objective
- Cluster objective

Of course, you may have your own objectives that are specific to your business needs. In this how-to guide, we will show you how to implement a custom objective.

The `nextroute`

Go package provides the `ModelObjective`

interface. To create a custom objective, you have to implement this interface with the specific business rules you want to model.

Once the custom type implements the `ModelObjective`

interface, you pass it to the `Model`

, by means of the `Objective()`

method. This method returns the `ModelObjectiveSum`

objective inteface. The interface offers the `NewTerm`

method which you can use to add as many objective terms as you want.

The most important methods of the `ModelObjective`

interface are `EstimateDeltaValue`

and `Value`

.

`EstimateDeltaValue`

returns`float`

value that represents the estimated change in the score (the objective's value) if the`Move`

is executed on the`Solution`

. Note that executing a move not necessarily improves the score. An improvement is represented by a negative value being returned.`Value`

returns the`float`

value that represents the score for the given solution.`Value`

answers the question "what is the value of this solution?", whereas`EstimateDeltaValue`

answers the question "what would happen to the score if I were to execute this move on this solution?"

The methods make use of the following arguments:

`Move`

. A move in a solution. It is a potential change in the solution that can be executed, such as planning a stop on a vehicle or unplanning multiple stops from one. A move may or may not result in changing the solution, as it not always results in an improvement. We encourage you to read the package documentation for more information.`Solution`

. Pretty self-explanatory: a solution to a vehicle routing problem. A solution contains all the information about how the problem is solved, such as which stops are planned on which vehicles and in which order. A solution also contains the values for the different objectives that were given. We encourage you to read the package documentation for more information.

Let's see an example of implementing a custom objective.

## Example

Let's say in addition to minimizing the travel duration, you want to balance the stops that are planned across vehicles. This is, you want to make the routes as even as possible as determined by the number of stops that are planned. Ideally, all vehicles have the same number of stops assigned.

Consider the following input, where we use the `custom_data`

field in the `input`

to define a penalty for our objective. The penalty will be used to balance out the value against the travel duration objective.

You can customize the `main.go`

file that you initialized in the get started with platform tutorial. The following code snippet shows how to implement the `ModelObjective`

interface to model the balance objective and how to pass it to the `Model`

:

Please consider the following.

- The
`customObjective`

`struct`

implements the`ModelObjective`

interface because the`EstimateDeltaValue`

and`Value`

methods are defined on that type. - In the
`solver`

function, we unmarshal the`custom_data`

defined on the`input`

into the`customObjective`

`struct`

. We pass it to the`Model`

by means of the`Objective().NewTerm()`

method. For simplicity, a term of 1.0 is used. - The value of the balance objective will be calculated by looping over all the vehicles in the solution to find the maximum and minimum number of stops that are planned. The difference between the maximum and minimum, if minimized to zero, implies that all vehicles have the same number of stops assigned to them.
- The difference between the maximum and minimum stops is multiplied by the penalty read from the
`custom_data`

to balance out the value against the travel duration. Given that the travel duration may be several hundreds or thousands of seconds (depending on the problem of course), we want the balance objective to be on the same order of magnitude so that the solver has a proper incentive to distribute stops across vehicles. - In the
`EstimateDeltaValue`

method:- We first calculate the baseline of the objective by calculating the maximum and minimum number of stops that are planned across all vehicles in the current solution.
- In case the move's vehicle holds the minimum number of stops, executing that move will imply that the minum number of stops will not necessarily belong to that vehicle, given that it will increase the stops planned on it. For this reason, we calculate the second minimum number of stops.
- The delta is calculated by determining how the difference between maximum and minimum changes. The increase is multiplied by the penalty. If there is no change in the difference, the delta is zero.

- In the
`Value`

method:- We use the same logic to calculate the difference between the maximum and minimum number of stops and mutliply it by the penalty.

- We implement the
`String() string`

method on the`customObjective`

type to get a human-readable representation of the objective.

After running the code, you should get an output similar to this one:

Please note that the value of the `balance_penalty`

objective reflects that there is a difference of 1 stop between the two vehicles, given that the penalty is set to be 1000.

By comparison, this is the output of running the same problem without the `balance_penalty`

:

This solution shows that the custom objective is influential enough to the solver to worsen the travel duration, by balancing out the stops.