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.

simple request
{
  "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

Solution to the simple request
{
  "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 explanation
{
  "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:

request with alternatives
{
  "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.

explanation with alternatives
{
  "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"
          }
        ]
      }
    ]
  }
}