Explain
The explain endpoint provides the score and a break-down of all the constraints and their contribution to the score.
This information can be very useful to understand why a solution is unfeasible and what makes it unfeasible.
Additionally, it can provide an exhaustive list of alternative positions that can be used to understand why
a job has been planned in a certain way. For each job it evaluates all its possible positions inside the planning period
providing, for each alternative, the score and the list of violated constrains.
Example:
Let's take a simple request where we have two jobs and a resource.
{
"resources": [
{
"name": "worker1",
"start": {
"latitude": 51.056,
"longitude": 3.7267
},
"shifts": [
{
"from": "2023-01-01T09:00:00",
"to": "2023-01-01T20:00:00"
}
]
}
],
"jobs": [
{
"name": "job2",
"location": {
"latitude": 51.0538,
"longitude": 3.7233
},
"duration": 1800,
"windows": [
{
"from": "2023-01-01T09:00:00",
"to": "2023-01-01T20:00:00",
"hard": true,
"weight": 1
}
]
},
{
"name": "job1",
"location": {
"latitude": 51.0541,
"longitude": 3.7227
},
"duration": 1800,
"windows": [
{
"from": "2023-01-01T09:00:00",
"to": "2023-01-01T09:20:00",
"hard": true,
"weight": 1
}
]
}
]
}
Our solver will quickly find a feasible solution where job1 is executed before job2
{
"score": {
"hardScore": 0,
"mediumScore": 0,
"softScore": -40,
"feasible": true
},
"trips": [
{
"visits": [
{
"arrival": "2023-01-01T09:00:35",
"job": "job1",
"location": {
"latitude": 51.0541,
"longitude": 3.7227
}
},
{
"arrival": "2023-01-01T09:30:40",
"job": "job2",
"location": {
"latitude": 51.0538,
"longitude": 3.7233
}
}
],
"start": {
"location": {
"latitude": 51.056,
"longitude": 3.7267
}
},
"resource": "worker1",
"date": "2023-01-01",
"departureTime": "2023-01-01T09:00:00",
"travelTime": 40,
"distance": 0,
"workTime": 3640,
"serviceTime": 3600
}
],
"totalTravelTimeInSeconds": 40,
"totalTravelDistanceInMeters": 0,
"totalServiceTimeInSeconds": 3600,
"workloadFairness": 1,
"status": "SOLVED"
}
The result of a request to the explain endpoint will be something like this
{
"score": {
"hardScore": 0,
"mediumScore": 0,
"softScore": -40,
"feasible": true
},
"unresolved": [
{
"constraint": "TRAVEL_TIME",
"score": "-40soft"
}
]
}
The response reports the score and the list of contributions of the various constraints (In this example the score is determined only by the travel time).
The explanation can be extended to a list of alternative positions for each job in the request. By showing different what-if scenarios,
these alternatives can provide a better comprehension of the solution. To instruct the solver to compute the alternatives the option
options.explanation.enabled
should be set to true
like this:
{
"resources": [
{
"name": "worker1",
"start": {
"latitude": 51.056,
"longitude": 3.7267
},
"shifts": [
{
"from": "2023-01-01T09:00:00",
"to": "2023-01-01T20:00:00"
}
]
}
],
"jobs": [
{
"name": "job2",
"location": {
"latitude": 51.0538,
"longitude": 3.7233
},
"duration": 1800,
"windows": [
{
"from": "2023-01-01T09:00:00",
"to": "2023-01-01T20:00:00",
"hard": true,
"weight": 1
}
]
},
{
"name": "job1",
"location": {
"latitude": 51.0541,
"longitude": 3.7227
},
"duration": 1800,
"windows": [
{
"from": "2023-01-01T09:00:00",
"to": "2023-01-01T09:20:00",
"hard": true,
"weight": 1
}
]
}
],
"options": {
"partialPlanning": false,
"explanationOptions": {
"enabled": true
}
}
}
This option will trigger an additional stage at the end of the solve request where the solver will produce a list of alternative
positions for each job and add it to the explanation. Each alternative reports, the job subject of the scenario, its position in the scenario
and the annotated score. Here is an example where job2
, that in the solution is executed after job1
, is executed first. We see that
this scenario was discarded because job2
is executed outside its time window.
{
"job": "job2",
"resource": "worker1",
"suggestedArrival": "2023-01-01T09:00:34",
"latestArrival": "2023-01-01T09:00:34",
"executedAfter": "worker12023-01-01",
"score": {
"hardScore": -639,
"mediumScore": 0,
"softScore": -39,
"feasible": false
},
"violations": [
{
"constraint": "DATE_TIME_WINDOW_CONFLICT",
"score": "-639hard"
},
{
"constraint": "TRAVEL_TIME",
"score": "-39soft"
}
]
}
We can retrieve all the alternative positions by sending an explanation request.
{
"score": {
"hardScore": 0,
"mediumScore": 0,
"softScore": -40,
"feasible": true
},
"unresolved": [
{
"constraint": "TRAVEL_TIME",
"score": "-40soft"
}
],
"alternatives": {
"job2": [
{
"job": "job2",
"resource": "worker1",
"suggestedArrival": "2023-01-01T09:00:34",
"latestArrival": "2023-01-01T09:00:34",
"executedAfter": "worker12023-01-01",
"score": {
"hardScore": -639,
"mediumScore": 0,
"softScore": -39,
"feasible": false
},
"violations": [
{
"constraint": "DATE_TIME_WINDOW_CONFLICT",
"score": "-639hard"
},
{
"constraint": "TRAVEL_TIME",
"score": "-39soft"
}
]
},
{
"job": "job2",
"resource": "worker1",
"suggestedArrival": "2023-01-01T09:30:40",
"latestArrival": "2023-01-01T19:30:00",
"executedAfter": "job1",
"score": {
"hardScore": 0,
"mediumScore": 0,
"softScore": -40,
"feasible": true
},
"violations": [
{
"constraint": "TRAVEL_TIME",
"score": "-40soft"
}
]
}
],
"job1": [
{
"job": "job1",
"resource": "worker1",
"suggestedArrival": "2023-01-01T09:00:35",
"latestArrival": "2023-01-01T09:00:35",
"executedAfter": "worker12023-01-01",
"score": {
"hardScore": 0,
"mediumScore": 0,
"softScore": -40,
"feasible": true
},
"violations": [
{
"constraint": "TRAVEL_TIME",
"score": "-40soft"
}
]
},
{
"job": "job1",
"resource": "worker1",
"suggestedArrival": "2023-01-01T09:30:39",
"latestArrival": "2023-01-01T19:30:00",
"executedAfter": "job2",
"score": {
"hardScore": -639,
"mediumScore": 0,
"softScore": -39,
"feasible": false
},
"violations": [
{
"constraint": "DATE_TIME_WINDOW_CONFLICT",
"score": "-639hard"
},
{
"constraint": "TRAVEL_TIME",
"score": "-39soft"
}
]
}
]
}
}
Updated 7 months ago