I am using Timefold to schedule staff based on a set of shift data and a set of employee data, aiming to achieve the highest score within a limited time. However, if the score of the hard constraints is less than zero, this set of scheduling data is unusable for me. What I want to implement is that if the score of the hard constraints calculated within the limited time is less than zero, I would like to mark the shift object that violates the hard constraints. This way, I can filter out the combinations of shifts and employees that meet the hard constraints based on the final results. I'm not sure if Timefold can meet this requirement. If it can, could you please let me know? Thank you very much.
Here is part of my code
SolverFactory<EmployeeSchedule> solverFactory = SolverFactory.create(new SolverConfig()
.withSolutionClass(EmployeeSchedule.class)
.withEntityClasses(Shift.class)
.withConstraintProviderClass(EmployeeSchedulingConstraintProvider.class)
// The solver runs only for 5 seconds on this small dataset.
// It's recommended to run for at least 5 minutes ("5m") otherwise.
.withTerminationSpentLimit(Duration.ofMinutes(1)));
// Load the problem
EmployeeSchedule problem = new EmployeeSchedule();
List<Shift> shiftList = dateShifts.stream().filter(o -> o.getShiftDate().equals(LocalDate.of(2023, 10, 4))).findFirst().get().getShifts();
Long shiftId = 1L;
for (Shift shift : shiftList) {
shift.setShiftId(shiftId);
shiftId++;
}
problem.setShiftList(shiftList);
problem.setEmployeeList(employees);
// Solve the problem
Solver<EmployeeSchedule> solver = solverFactory.buildSolver();
EmployeeSchedule solution = solver.solve(problem);
System.out.println(solution.getScore());
SolverFactory<EmployeeSchedule> solverFactory = SolverFactory.create(new SolverConfig()
.withSolutionClass(EmployeeSchedule.class)
.withEntityClasses(Shift.class)
.withConstraintProviderClass(EmployeeSchedulingConstraintProvider.class)
// The solver runs only for 5 seconds on this small dataset.
// It's recommended to run for at least 5 minutes ("5m") otherwise.
.withTerminationSpentLimit(Duration.ofMinutes(1)));
// Load the problem
EmployeeSchedule problem = new EmployeeSchedule();
List<Shift> shiftList = dateShifts.stream().filter(o -> o.getShiftDate().equals(LocalDate.of(2023, 10, 4))).findFirst().get().getShifts();
Long shiftId = 1L;
for (Shift shift : shiftList) {
shift.setShiftId(shiftId);
shiftId++;
}
problem.setShiftList(shiftList);
if (employees.size() < shiftList.size()) {
Long employeeId = 1L;
for (int i = employees.size(); i < shiftList.size(); i++) {
Employee employee = new Employee();
employee.setEmployeeId(employeeId);
employees.add(employee);
employeeId++;
}
}
problem.setEmployeeList(employees);
// Solve the problem
Solver<EmployeeSchedule> solver = solverFactory.buildSolver();
EmployeeSchedule solution = solver.solve(problem);
System.out.println(solution.getScore());
@Getter
@Setter
@PlanningSolution
public class EmployeeSchedule {
@ProblemFactCollectionProperty
@ValueRangeProvider
List<Employee> employeeList;
@PlanningEntityCollectionProperty
List<Shift> shiftList;
@PlanningScore
HardSoftScore score;
SolverStatus solverStatus;
}
Something like this is possible, and we have some tools to help you. Look into explaining the score in Timefold docs.
Once you have the constraint justifications, you will be able to see which constraints are broken and by which objects. Do with that information what you will.
In your ConstraintProvider
, you can use justifyWith(...)
on a constraint stream to greatly enhance the information you get in your score explanations. Read the chapter on penalties in the docs.