SolverController.java 4.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. package fr.jaquin.bdlg.planner.controller;
  2. import java.util.UUID;
  3. import java.util.concurrent.ExecutionException;
  4. import java.util.stream.Collector;
  5. import java.util.stream.Collectors;
  6. import org.optaplanner.core.api.score.ScoreExplanation;
  7. import org.optaplanner.core.api.score.ScoreManager;
  8. import org.optaplanner.core.api.score.buildin.hardmediumsoft.HardMediumSoftScore;
  9. import org.optaplanner.core.api.solver.SolverJob;
  10. import org.optaplanner.core.api.solver.SolverManager;
  11. import org.springframework.beans.factory.annotation.Autowired;
  12. import org.springframework.web.bind.annotation.PostMapping;
  13. import org.springframework.web.bind.annotation.RequestBody;
  14. import org.springframework.web.bind.annotation.RestController;
  15. import fr.jaquin.bdlg.planner.solver.domain.Assignement;
  16. import fr.jaquin.bdlg.planner.solver.domain.MealAssignement;
  17. import fr.jaquin.bdlg.planner.solver.domain.MealSlot;
  18. import fr.jaquin.bdlg.planner.solver.domain.Planning;
  19. import fr.jaquin.bdlg.planner.solver.domain.PlanningInput;
  20. import fr.jaquin.bdlg.planner.solver.domain.PlanningSolution;
  21. @RestController
  22. public class SolverController {
  23. @Autowired
  24. private SolverManager<Planning, UUID> solverManager;
  25. @Autowired
  26. private ScoreManager<Planning, HardMediumSoftScore> scoreManager;
  27. private Collector<CharSequence, ?, String> listCollector = Collectors.joining(",", "[", "]");
  28. @PostMapping("/planning/solve")
  29. public PlanningSolution solve(@RequestBody PlanningInput inputs) {
  30. Planning problem = inputs.generatePlanningProblem();
  31. UUID problemId = UUID.randomUUID();
  32. // Submit the problem to start solving
  33. SolverJob<Planning, UUID> solverJob = solverManager.solve(problemId, problem);
  34. Planning solution;
  35. try {
  36. // Wait until the solving ends
  37. solution = solverJob.getFinalBestSolution();
  38. } catch (InterruptedException | ExecutionException e) {
  39. throw new IllegalStateException("Solving failed.", e);
  40. }
  41. ScoreExplanation<Planning, HardMediumSoftScore> explanation =
  42. scoreManager.explainScore(solution);
  43. PlanningSolution output = PlanningSolution.from(solution);
  44. output.setExplanation(stringifyExplanation(explanation));
  45. // System.out.println(explanation.getSummary());
  46. return output;
  47. }
  48. private String stringifyExplanation(ScoreExplanation<Planning, HardMediumSoftScore> explanation) {
  49. return explanation.getConstraintMatchTotalMap().values().stream()
  50. // Return only Hard Constraint
  51. .filter(predicate -> predicate.getScore().getHardScore() < 0)
  52. // Create a dictionnary with the name of the constraint as key
  53. .map(constraint -> "\"" + constraint.getConstraintName() + "\":"
  54. // Populate the value with an array of justification objects
  55. + constraint.getConstraintMatchSet().stream()
  56. // Filter pair that have a negative impact on the score
  57. .filter(matchElt -> matchElt.getScore().getHardScore() < 0)
  58. .map(match -> match.getJustificationList().stream()
  59. .map(elt -> stringifyConstraint(elt)).collect(listCollector))
  60. .collect(listCollector))
  61. .collect(Collectors.joining(",", "{", "}"));
  62. }
  63. private String stringifyConstraint(Object val) {
  64. if (val instanceof Assignement) {
  65. Assignement casted = (Assignement) val;
  66. return "{\"type\":\"Assignement\",\"slotId\":\"" + casted.getSlot().getId()
  67. + "\",\"volonteerId\":" + casted.getVolonteer().getId().toString() + "}";
  68. }
  69. if (val instanceof MealAssignement) {
  70. MealAssignement casted = (MealAssignement) val;
  71. return "{\"type\":\"MealAssignement\",\"slotId\":\"" + casted.getMealSlot().getId()
  72. + "\",\"volonteerId\":" + casted.getVolonteer().getId().toString() + "}";
  73. }
  74. if (val instanceof MealSlot) {
  75. MealSlot casted = (MealSlot) val;
  76. return "{\"type\":\"MealSlot\",\"slotId\":\"" + casted.getId() + "\"}";
  77. }
  78. if (val instanceof Integer) {
  79. return val.toString();
  80. }
  81. return "\"" + val.getClass().getName() + "\"";
  82. }
  83. }