I'm trying to implement the Recommended Fit API in my Timefold project which is a modified version of the maintenance scheduling template.
My planning entity 'Job' has multiple Planning Variables: Crew and startDate and a Shadow Variable endDate. When a Job is not initialized, these values equal to null.
Now what I've tried with the Recommended Fit API is to return a list of startDate's, which leaves the Crew at null. Trying it the other way around works exactly vice versa. Returning the entire entity leaves all values at null as expected from the documentation on Timefold's website.
I'm pretty new to planning and Java in general, so any help / explanation is appreciated! Thanks.
solverConfig.xml:
<?xml version="1.0" encoding="UTF-8"?>
<solver xmlns="https://timefold.ai/xsd/solver" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://timefold.ai/xsd/solver https://timefold.ai/xsd/solver/solver.xsd">
<!-- Define the model -->
<solutionClass>nl.heijmans.teamplanning.domain.Teamplanning</solutionClass>
<entityClass>nl.heijmans.teamplanning.domain.Opdracht</entityClass>
<!-- Define the score function -->
<scoreDirectorFactory>
<constraintProviderClass>nl.heijmans.teamplanning.solver.TeamplanningConstraintProvider</constraintProviderClass>
</scoreDirectorFactory>
<!-- Configure the optimization algorithms -->
<termination>
<spentLimit>PT30S</spentLimit>
</termination>
<constructionHeuristic>
<constructionHeuristicType>FIRST_FIT</constructionHeuristicType>
</constructionHeuristic>
<localSearch>
<!-- <localSearchType>HILL_CLIMBING</localSearchType> -->
</localSearch>
</solver>
Call to Recommended Fit API:
@GET
@Path("opdracht/proposition")
// @Transactional
public List<RecommendedFit<LocalDateTime, HardMediumSoftLongScore>> fetch(@QueryParam("id") Long id) {
// Check if corresponding opdracht exists and get it
Opdracht entity = opdrachtRepository.findById(id);
if(entity == null) {
throw new NotFoundException();
}
Teamplanning solution = findById(SINGLETON_SCHEDULE_ID);
List<RecommendedFit<LocalDateTime, HardMediumSoftLongScore>> recommendations =
solutionManager.recommendFit(solution, entity, Opdracht::getStartDate);
return recommendations;
}
Your question boils down to returning two things from a single method call. This is more of a Java question than a Timefold Solver question, but it's related enough.
Start with creating a custom carrier type for the information you need. In this case, it can be a simple Java record:
public record Pair(Crew crew, LocalDate startDate) {}
Once you have that, you can call the Recommended Fit API like so:
List<RecommendedFit<Pair, HardMediumSoftLongScore>> recommendations =
solutionManager.recommendFit(solution, entity,
entity -> new Pair(entity.getCrew(), entity.getStartDate()));