Jose Mari
Jose Mari

Reputation: 11

OptaPlanner returning empty solution

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

Answers (1)

Bessem ben afia
Bessem ben afia

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

Related Questions