A score corruption from Chained Through Time pattern

93 views Asked by At

I use the Chained Though Time pattern for task scheduling (or named job scheduling) scenes. There are predecessor/successor tasks (for example, tasks formed by two processes before and after the same product) tasks, that is, a task can only start after its predecessor tasks are completed. What I do is: Based on the TaskAssigning example, add a planning variable - delay, to the class - Task, which is the gap between a task and its predecessor task on the same employee. The startTime of a task is the completion time of its predecessor task, plus delay. When a task starts earlier than the completion time of its predecessor task, a penalty constraint will be triggered to keep the delay at an appropriate value to ensure that the task It must be started after its predecessor tasks are completed. But a Score Corruption throw for this constraint. I have carefully checked the logic of the constraints and no problems. Is there anything missing in this approach?

Following is the exception message: Exception in thread "main" java.lang.IllegalStateException: Score corruption (420medium): the workingScore (0hard/0medium/0soft) is not the uncorruptedScore (0hard/-420medium/0soft) after completedAction (M1J1[R2] {9 -> 9}): Score corruption analysis: The corrupted scoreDirector has no ConstraintMatch(s) which are in excess. The corrupted scoreDirector has 1 ConstraintMatch(s) which are missing: com.easyplan.domain/jobStartTimeLimitation/[M1J2[R1]]=0hard/-420medium/0soft Maybe there is a bug in the score constraints of those ConstraintMatch(s). Maybe a score constraint doesn't select all the entities it depends on, but finds some through a reference in a selected entity. This corrupts incremental score calculation, because the constraint is not re-evaluated if such a non-selected entity changes. Shadow variable corruption in the corrupted scoreDirector: None at org.optaplanner.core.impl.score.director.AbstractScoreDirector.assertScoreFromScratch(AbstractScoreDirector.java:627) at org.optaplanner.core.impl.score.director.AbstractScoreDirector.assertWorkingScoreFromScratch(AbstractScoreDirector.java:603) at org.optaplanner.core.impl.score.director.AbstractScoreDirector.doAndProcessMove(AbstractScoreDirector.java:210) at org.optaplanner.core.impl.localsearch.decider.LocalSearchDecider.doMove(LocalSearchDecider.java:117) at org.optaplanner.core.impl.localsearch.decider.LocalSearchDecider.decideNextStep(LocalSearchDecider.java:101) at org.optaplanner.core.impl.localsearch.DefaultLocalSearchPhase.solve(DefaultLocalSearchPhase.java:72) at org.optaplanner.core.impl.solver.AbstractSolver.runPhases(AbstractSolver.java:83) at org.optaplanner.core.impl.solver.DefaultSolver.solve(DefaultSolver.java:193) at com.easyplan.Main.main(Main.java:299)

// method in ConstraintProvider
protected Constraint jobStartBeforePredecessorEnd(ConstraintFactory factory) {
        return factory.forEach(Job.class)
                .filter(job -> job.getResource() != null && job.startedTooEarly() > 0)
                .penalizeLong(HardMediumSoftLongScore.ONE_MEDIUM,
                        Job::startedTooEarly)
                .asConstraint("jobStartTimeLimitation");

    }
// method in class Job
public Long startedTooEarly() {
        if(this.previousJobOrResource == null) {
            return 0L;
        }

        if(this.predecessorJobList != null && !this.predecessorJobList.isEmpty()) {

            LocalDateTime latestPredecessorEndTime = this.startTime;
            for (Job predecessorJob : this.predecessorJobList) {
                if(predecessorJob.getPreviousJobOrResource() == null) continue;

                if (predecessorJob.getEndTime().isAfter(latestPredecessorEndTime)) {
                    latestPredecessorEndTime = predecessorJob.getEndTime();
                }
            }

            if (latestPredecessorEndTime.isAfter(this.startTime)) {
                Duration leadingTime = Duration.between(this.startTime, latestPredecessorEndTime);
                return leadingTime.toMinutes();
            } else {
                return 0L;
            }
        }

        return 0L;
    }

The reason why I use Chained Through Time instead of PlanningListVariable is because my system requires real-time planning, and PlanningVarialbe still does not provide this feature.

Thank you in advance for your suggestion!

0

There are 0 answers