简体   繁体   中英

How to set a parameter based on the first element added in OptaPlanner?

Each job has own location (city). Each employee can take jobs only in one location. If an employee took a job in one location, he cannot take orders from another location. How I can do this? Given:

@Data
@PlanningEntity
public class Job {

    @PlanningId
    private Long id;

    private String location;

    @PlanningVariable(valueRangeProviderRefs = "employeeRange")
    private Employee employee;

}

@Data
@PlanningEntity
public class Employee {

    @PlanningId
    private Long id;

    @InverseRelationShadowVariable(sourceVariableName = "employee")
    private List<Job> jobs = new ArrayList<>();

}

@Data
@PlanningSolution
public class Solution {

    @PlanningEntityCollectionProperty
    @ValueRangeProvider(id = "employeeRange")
    private List<Emoloyee> employees;

    @PlanningEntityCollectionProperty
    private List<Job> jobs;

    @PlanningScore
    private HardMediumSoftScore score;

    public Solution(List<Emoloyee> employees, List<Job> jobs) {
        this.employees = employees;
        this.jobs = jobs;
    }
}

Here is constraint for considering the location:

private Constraint locationIsConsidered(ConstraintFactory constraintFactory) {
    return constraintFactory.forEach(Job.class)
            .filter(job -> !job.getEmployee().getLocation()
                    .equals(job.getLocation()))
            .penalize("Location conflict", HardMediumSoftScore.ONE_HARD);
}

the another constraint (location is not considered): But it's not working

private Constraint directionIsNotConsidered(ConstraintFactory constraintFactory) {
    return constraintFactory.forEach(Job.class)
        .groupBy(Job::getInspector)
        .filter(employee -> employee.getJobs().stream()
            .anyMatch(job -> !Objects.equals(job.getLocation(),
                employee.getJobs().get(0).getLocation()))
        ).penalize("Location Conflict", HardMediumSoftScore.ONE_HARD);
}

Thanks in advance!

I think I have found a solution.

private Constraint directionIsNotConsidered(ConstraintFactory constraintFactory) {
    return constraintFactory.forEach(Job.class)
        .groupBy(Job::getInspector)
        .filter(employee -> employee.getJobs().stream()
            .anyMatch(job -> !Objects.equals(job.getLocation(),
                employee.getJobs().get(0).getLocation()))
        ).penalize("Location Conflict", HardMediumSoftScore.ONE_HARD);
}

I'm afraid that what you're trying to solve here is actually two distinct planning problems. Assigning employees to locations, and then assigning jobs to employees in those locations. The outcome of the latter depends on the outcome of the former; if the outcome of the former changes, the entire outcome for the latter needs to be significantly recomputed.

Attempting to solve both of these issues in a single solver is bound to cause issues. Once you assign an employee to a location, and you assign some jobs to that employee, reassigning the employee to another location is then guaranteed to break hard constraints, and so are moves that try to assign jobs from a different city - therefore no move will actually get you anywhere, unless you write a custom move that reassigns all employee jobs when the location moves, plus a whole bunch of move filters eliminating useless moves. Although this problem will not prevent the solver from working, the solver will not be too efficient.

A better approach would be to implement multi-stage planning . Solve the problem of assigning employees to cities first. If it's a simple enough problem, use brute force; if it's hard, use OptaPlanner. Once you have an allocation, use OptaPlanner to assign jobs to those employees, with their cities now immutable. This will bring its own inefficiencies, but it will be relatively simple to implement and understand.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM