Reputation: 11
I am trying to solve a problem with OptaPlanner, but the program returns me and empty solution. I have followed this example to solve it: https://github.com/ge0ffrey/getting-started-guides/tree/gs-constraint-solving-ai-optaplanner/complete
I am using SpringBoot JPA integration.
Here you have the code for the implementation:
@PlanningSolution
public class HorarioComidas {
@ValueRangeProvider(id = "recetaRange")
@ProblemFactCollectionProperty
private List<Receta> recetaList;
@ValueRangeProvider(id = "fechaSemanaRange")
@ProblemFactCollectionProperty
private List<FechaSemana> fechaSemanaList;
@ValueRangeProvider(id = "comidaRange")
@ProblemFactCollectionProperty
private List<Comida> comidaList;
@PlanningEntityCollectionProperty
private List<UsuarioReceta> usuarioRecetas;
@PlanningScore
private HardSoftScore score;
// Ignored by OptaPlanner, used by the UI to display solve or stop solving button
private SolverStatus solverStatus;
...
}
@Service
public class OptaPlannerService {
@Autowired
private HorarioComidasService horarioComidasService;
@Autowired
private SolverManager<HorarioComidas, Usuario> solverManager;
@Autowired
private ScoreManager<HorarioComidas> scoreManager;
public HorarioComidas getTimeTable(Usuario usuario) {
// Get the solver status before loading the solution
// to avoid the race condition that the solver terminates between them
SolverStatus solverStatus = getSolverStatus(usuario);
HorarioComidas solution = horarioComidasService.findByUsuario(usuario);
scoreManager.updateScore(solution); // Sets the score
solution.setSolverStatus(solverStatus);
return solution;
}
public void solve(Usuario usuario) {
horarioComidasService.usuario = usuario;
solverManager.solveAndListen(usuario,
horarioComidasService::findByUsuario,
horarioComidasService::save);
}
public SolverStatus getSolverStatus(Usuario usuario) {
return solverManager.getSolverStatus(usuario);
}
public void stopSolving(Usuario usuario) {
solverManager.terminateEarly(usuario);
}
}
@Service
public class HorarioComidasService {
@Autowired
private UsuarioRecetaService usuarioRecetaService;
@Autowired
private RecetaService recetaService;
public Usuario usuario;
public HorarioComidas findByUsuario(Usuario usuario) {
if (!usuarioRecetaService.findByUsuario(usuario).isEmpty()) {
throw new IllegalStateException("No hay una lista de comida para este usuario.");
}
// Occurs in a single transaction, so each initialized lesson references the same timeslot/room instance
// that is contained by the timeTable's timeslotList/roomList.
return new HorarioComidas(recetaService.findAll(), usuarioRecetaService.findByUsuario(usuario), Arrays.asList(FechaSemana.values()), Arrays.asList(Comida.values()));
}
public void save(HorarioComidas horarioComidas) {
for (UsuarioReceta usuarioReceta : horarioComidas.getUsuarioRecetas()) {
usuarioReceta.setUsuario(usuario);
usuarioRecetaService.create(usuarioReceta);
}
}
}
public void generarListaCompra(Usuario usuario) throws InterruptedException {
optaPlannerService.solve(usuario);
HorarioComidas horarioComidas = optaPlannerService.getTimeTable(usuario);
while (horarioComidas.getSolverStatus() != SolverStatus.NOT_SOLVING) {
// Quick polling (not a Test Thread Sleep anti-pattern)
// Test is still fast on fast machines and doesn't randomly fail on slow machines.
Thread.sleep(20L);
horarioComidas = optaPlannerService.getTimeTable(usuario);
}
}
public class HorarioComidasConstraintProvider implements ConstraintProvider {
@Autowired
private IntoleranciaUsuarioService intoleranciaUsuarioService;
@Autowired
private IntoleranciaRecetaService intoleranciaRecetaService;
@Override
public Constraint[] defineConstraints(ConstraintFactory constraintFactory) {
return new Constraint[]{};
}
}
2020-06-04 14:12:42.186 INFO 10130 --- [ restartedMain] .ConditionEvaluationDeltaLoggingListener :
Condition evaluation unchanged
2020-06-04 14:21:37.238 INFO 10130 --- [pool-5-thread-1] o.o.core.impl.solver.DefaultSolver :
Solving started: time spent (3), best score (0hard/0soft), environment mode (REPRODUCIBLE), random (JDK with seed 0).
2020-06-04 14:21:37.239 INFO 10130 --- [pool-5-thread-1] o.o.core.impl.solver.DefaultSolver : Skipped all phases (2): out of 0 planning entities, none are movable (non-pinned).
2020-06-04 14:21:37.239 INFO 10130 --- [pool-5-thread-1] o.o.core.impl.solver.DefaultSolver : Solving ended: time spent (4), best score (0hard/0soft), score calculation speed (250/sec), phase total (2), environment mode (REPRODUCIBLE).
Upvotes: 0
Views: 480
Reputation: 56
I guess it would be better to add even a simple constraint (reward/penalty) in order to allow the solver to calculate score, which will help it to guess whether there will be change or not, could you please update your post with some log from your @PlanningSolution / Planning entity (PlanningEntityCollectionProperty in your case) before and after solving ? it would help to log score also.
PS: try override "HorarioComidasService::save" to log before saving.
Upvotes: 1