添加三个新的计划变量来护理名册解决方案

时间:2017-04-13 22:10:28

标签: java drools optaplanner

我正在尝试向Optaplanner Nurse Roster Solution添加三个新的计划变量。这三个计划变量是shortTask,mainTask和fullTask​​,它们是ShiftTaskAllocation类:

public class ShiftTaskAllocation extends AbstractPersistable {

   private Task task;
   private Shift shift;
   private ShiftAssignment shiftAssignment;

   .... // getter and setter methods and other complex methods 
}

这是Task类:

public class Task extends AbstractPersistable {
   private String code;
   private String description;

   .... // getter and setter methods and other complex methods 
}   

以下是任务技能要求类,该类包含有关完成任务所需技能的信息:

public class TaskSkillRequirement extends AbstractPersistable { 
   private Task task;
   private Skill skill;

   .... // getter and setter methods and other complex methods 
}   

以下是技能水平课程,该课程包含员工具备的技能信息:

public class SkillProficiency extends AbstractPersistable {

   private Employee employee;
   private List<Skill> skillList;

   .... // getter and setter methods and other complex methods 
}   

这个想法是针对每个班次,我们可以设置一些需要在特定班次中完成的任务。 这是我的计划实体类已经修改以符合此要求:

public class ShiftAssignment extends AbstractPersistable {
   private Shift shift;
   private int indexInShift;

   // Planning variables: changes during planning, between score calculations.
   private Employee employee;
   private ShiftTaskAllocation shortTask;
   private ShiftTaskAllocation mainTask;
   private ShiftTaskAllocation fullTask;

   .... // getter and setter methods and other complex methods 

   @PlanningVariable(valueRangeProviderRefs = {"shiftTaskAllocationRange"}, nullable = true, strengthComparatorClass = ShiftTaskAllocationStrengthComparator.class)
   public ShiftTaskAllocation getShortTask() {
      return shortTask;
   }

   @PlanningVariable(valueRangeProviderRefs = {"shiftTaskAllocationRange"}, nullable = true, strengthComparatorClass = ShiftTaskAllocationStrengthComparator.class)
   public ShiftTaskAllocation getMainTask() {
      return mainTask;
   }

   @PlanningVariable(valueRangeProviderRefs = {"shiftTaskAllocationRange"}, nullable = true, strengthComparatorClass = ShiftTaskAllocationStrengthComparator.class)
   public ShiftTaskAllocation getFullTask() {
      return fullTask;
   }

   public int getSumOfTasks() {
      int sum = 0;

      if (shortTask != null) {
         sum += shortTask.getTask().getType().getValue();
      }

      if (mainTask != null) {
         sum += mainTask.getTask().getType().getValue();
      }

      if (fullTask != null) {
         sum += fullTask.getTask().getType().getValue();
      }

      return sum;
   }
}

这是TaskType枚举:

public enum TaskType {
   SHORT(1), MAIN(2), FULL(3);

   private final int value;

   private TaskType(int value) {
      this.value = value;
   }

   public int getValue() {
      return value;
   }
}

在一个班次中,员工应该只处理一个简短的任务+主要任务,或者只处理一个完整的任务。 我需要制定哪些规则才能确保optaplanner能够正确地将任务和员工分配给班次? 任何帮助或评论将不胜感激。 感谢致敬。

**

更新1

**

我试着写一些规则,指导optaplanner找到可行的解决方案。我认为我需要记下的规则是:

  1. ShiftTaskAllocation只能分配给一个ShiftAssignment(无重复项)
  2. 必须分配所有ShiftTaskAllocation(没有未分配给ShiftAssignment的ShiftTaskAllocation)
  3. 应将ShiftTaskAllocation分配给适当的班次(例如,如果早班班次的ShiftTaskAllocation,则应仅分配给具有早班班次的ShiftAssignment)。
  4. ShiftAssignment的Sum任务应该是精确的3(无论是shortTask + mainTask还是fullTask​​)
  5. 员工分配没有无与伦比的技能(适用于所有shortTask,mainTask或fullTask​​)
  6. 只能将类型为SHORT的任务分配给shortTask
  7. 只有MAIN类型的任务可以分配给mainTask
  8. 只能将类型为FULL的任务分配给fullTask​​
  9. 以下是我试图自行写下的规则:

    ############################################################################
    // Hard constraints
    ############################################################################
    
    // a nurse can only work one shift per day, i.e. no two shift can be assigned to the same nurse on a day.
    rule "oneShiftPerDay"
        when
            $s1 : ShiftAssignment(employee != null,   
                $leftId : id, $employee : employee, $shiftDate : shiftDate)
            $s2 : ShiftAssignment(employee == $employee, shiftDate == $shiftDate, id > $leftId)
        then
            scoreHolder.addHardConstraintMatch(kcontext, -1);
    end
    
    // a short shift task can only assigned to one shift only.
    rule "oneShortShiftTaskPerShiftAssignment"
        when
            $s1 : ShiftAssignment(shortTask != null,    
                $leftId : id, $shortTask : shortTask)
            $s2 : ShiftAssignment(shortTask == $shortTask, id != $leftId)
        then
            scoreHolder.addHardConstraintMatch(kcontext, -1);
    end
    
    // a main shift task can only assigned to one shift only.
    rule "oneMainShiftTaskPerShiftAssignment"
        when
            $s1 : ShiftAssignment(mainTask != null,    
                $leftId : id, $mainTask : mainTask)
            $s2 : ShiftAssignment(mainTask == $mainTask, id != $leftId)
        then
            scoreHolder.addHardConstraintMatch(kcontext, -1);
    end
    
    // a full shift task can only assigned to one shift only.
    rule "oneFullShiftTaskPerShiftAssignment"
        when
            $s1 : ShiftAssignment(fullTask != null,    
                $leftId : id, $fullTask : fullTask)
            $s2 : ShiftAssignment(fullTask == $fullTask, id != $leftId)
        then
            scoreHolder.addHardConstraintMatch(kcontext, -1);
    end
    
    // a short task can only assigned to one shift assignment
    rule "allShiftTaskAllocationMustBeAssigned"
        when
            ShiftTaskAllocation(shiftAssignment == null)
        then
            scoreHolder.addHardConstraintMatch(kcontext, -1);
    end
    
    // a short shift task assignment must assigned to appropriate shift.
    rule "shortShiftTaskAssignmentMustAssignedToAppropriateShiftAssignment"
        when
            ShiftAssignment(shortTask != null, shortTask.shift != shift)
        then
            scoreHolder.addHardConstraintMatch(kcontext, -1);
    end
    
    // a main shift task assignment must assigned to appropriate shift.
    rule "mainShiftTaskAssignmentMustAssignedToAppropriateShiftAssignment"
        when
            ShiftAssignment(mainTask != null, mainTask.shift != shift)
        then
            scoreHolder.addHardConstraintMatch(kcontext, -1);
    end
    
    // a full shift task assignment must assigned to appropriate shift.
    rule "fullShiftTaskAssignmentMustAssignedToAppropriateShiftAssignment"
        when
            ShiftAssignment(fullTask != null, fullTask.shift != shift)
        then
            scoreHolder.addHardConstraintMatch(kcontext, -1);
    end
    
    // sum of tasks on a shift is lower than 3.
    rule "sumTasksOfShiftLowerThan3"
        when
            $s1 : ShiftAssignment(sumOfTasks < 3, $sumOfTasks : sumOfTasks)
        then
            scoreHolder.addHardConstraintMatch(kcontext, $sumOfTasks - 3);
    end
    
    // sum of tasks on a shift is higher than 3.
    rule "sumTasksOfShiftHigherThan3"
        when
            $s1 : ShiftAssignment(sumOfTasks > 3, $sumOfTasks : sumOfTasks)
        then
            scoreHolder.addHardConstraintMatch(kcontext, 3- $sumOfTasks);
    end
    
    // a nurse can't be assigned to short and main task that he/she didn't has the skill to do it
    rule "noUnmatchedSkillEmployeeAssignmentToShortMainTask"
        when 
            ShiftAssignment( employee != null, 
                $employee : employee, shortTask != null, mainTask != null, fullTask == null, 
                $shortTask : shortTask, $mainTask : mainTask )
    
            not ( forall ( TaskSkillRequirement( $shortTask.task == task, $skill : skill ),
                     SkillProficiency( employee == $employee, skillList contains $skill ) 
                  )
                )
    
            not ( forall ( TaskSkillRequirement( $mainTask.task == task, $skill : skill ),
                     SkillProficiency( employee == $employee, skillList contains $skill ) 
                  )
                )       
        then
            scoreHolder.addHardConstraintMatch(kcontext, -1);
    end
    
    // a nurse can't be assigned to full task that he/she didn't has the skill to do it
    rule "noUnmatchedSkillEmployeeAssignmentToFullTask"
        when 
            ShiftAssignment( employee != null, 
                $employee : employee, shortTask == null, mainTask == null, fullTask != null, 
                $fullTask : fullTask )
    
            not ( forall ( TaskSkillRequirement( $fullTask.task == task, $skill : skill ),
                     SkillProficiency( employee == $employee, skillList contains $skill ) 
                  )
                )
        then
            scoreHolder.addHardConstraintMatch(kcontext, -1);
    end
    
    // only task with type SHORT that can be assigned to shortTask
    rule "noUmatchedShortTaskAssignment"
        when
            ShiftAssignment(shortTask != null, shortTask.task.type.value != 1, $shortValue : shortTask.task.type.value, 
                shortTask.shift != shift)
        then
            scoreHolder.addHardConstraintMatch(kcontext, $shortValue * -1);
    end
    
    // only task with type MAIN that can be assigned to mainTask
    rule "noUmatchedMainTaskAssignment"
        when
            ShiftAssignment(mainTask != null, mainTask.task.type.value != 2, $mainValue : mainTask.task.type.value, 
                mainTask.shift != shift)
        then
            scoreHolder.addHardConstraintMatch(kcontext, $mainValue * -1);
    end
    
    // only task with type FULLL that can be assigned to fullTask
    rule "noUmatchedFullTaskAssignment"
        when
            ShiftAssignment(fullTask != null, fullTask.task.type.value != 3, $fullValue : fullTask.task.type.value, 
                fullTask.shift != shift)
        then
            scoreHolder.addHardConstraintMatch(kcontext, $fullValue * -1);
    end
    

    但是当我尝试使用简单的样本数据集运行求解阶段时(求解阶段运行2分钟),它只产生一个不可行的解决方案:

    2017-04-14 05:52:11,276 [main] INFO  KieModule was added: MemoryKieModule[releaseId=org.default:artifact:1.0.0-SNAPSHOT]
    2017-04-14 05:52:12,300 [main] DEBUG Starting Engine in PHREAK mode
    2017-04-14 05:52:12,923 [main] INFO  Solving started: time spent (273), best score (uninitialized/-30hard/-75soft), environment mode (REPRODUCIBLE), random (JDK with seed 0).
    2017-04-14 05:52:24,265 [main] DEBUG     CH step (0), time spent (11616), score (-28hard/-74soft), selected move count (49130), picked move ([2017-01-01(Sun)_MORN@null => DAI(Daisy), 2017-01-01(Sun)_MORN@null => ShiftTaskAllocation-63, 2017-01-01(Sun)_MORN@null => null, 2017-01-01(Sun)_MORN@null => null]).
    2017-04-14 05:52:32,375 [main] DEBUG     CH step (1), time spent (19726), score (-26hard/-73soft), selected move count (49130), picked move ([2017-01-01(Sun)_MORN@null => HAN(Hanks), 2017-01-01(Sun)_MORN@null => ShiftTaskAllocation-73, 2017-01-01(Sun)_MORN@null => null, 2017-01-01(Sun)_MORN@null => null]).
    2017-04-14 05:52:39,478 [main] DEBUG     CH step (2), time spent (26829), score (-24hard/-73soft), selected move count (49130), picked move ([2017-01-01(Sun)_MORN@null => ANT(Anton), 2017-01-01(Sun)_MORN@null => ShiftTaskAllocation-68, 2017-01-01(Sun)_MORN@null => null, 2017-01-01(Sun)_MORN@null => null]).
    2017-04-14 05:52:45,838 [main] DEBUG     CH step (3), time spent (33189), score (-22hard/-73soft), selected move count (49130), picked move ([2017-01-01(Sun)_AFNO@null => CHR(Christine), 2017-01-01(Sun)_AFNO@null => ShiftTaskAllocation-74, 2017-01-01(Sun)_AFNO@null => null, 2017-01-01(Sun)_AFNO@null => null]).
    2017-04-14 05:52:51,926 [main] DEBUG     CH step (4), time spent (39277), score (-21hard/-72soft), selected move count (49130), picked move ([2017-01-01(Sun)_AFNO@null => FRA(Frans), 2017-01-01(Sun)_AFNO@null => null, 2017-01-01(Sun)_AFNO@null => ShiftTaskAllocation-64, 2017-01-01(Sun)_AFNO@null => null]).
    2017-04-14 05:52:57,902 [main] DEBUG     CH step (5), time spent (45253), score (-20hard/-71soft), selected move count (49130), picked move ([2017-01-01(Sun)_AFNO@null => IRE(Irene), 2017-01-01(Sun)_AFNO@null => null, 2017-01-01(Sun)_AFNO@null => ShiftTaskAllocation-65, 2017-01-01(Sun)_AFNO@null => null]).
    2017-04-14 05:53:03,956 [main] DEBUG     CH step (6), time spent (51307), score (-19hard/-70soft), selected move count (49130), picked move ([2017-01-01(Sun)_AFNO@null => JUS(Justin), 2017-01-01(Sun)_AFNO@null => null, 2017-01-01(Sun)_AFNO@null => ShiftTaskAllocation-69, 2017-01-01(Sun)_AFNO@null => null]).
    2017-04-14 05:53:10,393 [main] DEBUG     CH step (7), time spent (57744), score (-18hard/-70soft), selected move count (49130), picked move ([2017-01-01(Sun)_NIGH@null => BRA(Brandon), 2017-01-01(Sun)_NIGH@null => null, 2017-01-01(Sun)_NIGH@null => ShiftTaskAllocation-70, 2017-01-01(Sun)_NIGH@null => null]).
    2017-04-14 05:53:15,975 [main] DEBUG     CH step (8), time spent (63326), score (-17hard/-70soft), selected move count (49130), picked move ([2017-01-01(Sun)_NIGH@null => EMI(Emily), 2017-01-01(Sun)_NIGH@null => null, 2017-01-01(Sun)_NIGH@null => ShiftTaskAllocation-75, 2017-01-01(Sun)_NIGH@null => null]).
    2017-04-14 05:53:21,517 [main] DEBUG     CH step (9), time spent (68867), score (-16hard/-70soft), selected move count (49130), picked move ([2017-01-01(Sun)_NIGH@null => GEO(George), 2017-01-01(Sun)_NIGH@null => null, 2017-01-01(Sun)_NIGH@null => ShiftTaskAllocation-76, 2017-01-01(Sun)_NIGH@null => null]).
    2017-01-01-MORN : DAI(Daisy) : Blood Test(63)
    2017-01-01-MORN : HAN(Hanks) : Blood Test(73)
    2017-01-01-MORN : ANT(Anton) : Urine Test(68)
    2017-01-01-AFNO : CHR(Christine) : Urine Test(74)
    2017-01-01-AFNO : FRA(Frans) : Clean Patient(64)
    2017-01-01-AFNO : IRE(Irene) : Clean Patient(65)
    2017-01-01-AFNO : JUS(Justin) : Clean Patient(69)
    2017-01-01-NIGH : BRA(Brandon) : Clean Patient(70)
    2017-01-01-NIGH : EMI(Emily) : Clean Patient(75)
    2017-01-01-NIGH : GEO(George) : Clean Patient(76)
    2017-04-14 05:53:21,519 [main] INFO  Construction Heuristic phase (0) ended: step total (10), time spent (68870), best score (-16hard/-70soft).
    2017-04-14 05:53:22,327 [main] DEBUG     LS step (0), time spent (69678), score (-16hard/-70soft),     best score (-16hard/-70soft), accepted/selected move count (800/800), picked move (2017-01-01(Sun)_MORN@DAI(Daisy) <=> 2017-01-01(Sun)_MORN@ANT(Anton)).
    2017-04-14 05:53:22,783 [main] DEBUG     LS step (1), time spent (70134), score (-16hard/-70soft),     best score (-16hard/-70soft), accepted/selected move count (800/993), picked move (2017-01-01(Sun)_AFNO@IRE(Irene) => ShiftTaskAllocation-71).
    2017-04-14 05:53:23,176 [main] DEBUG     LS step (2), time spent (70527), score (-16hard/-70soft),     best score (-16hard/-70soft), accepted/selected move count (800/1152), picked move ([[2017-01-01(Sun)_AFNO@FRA(Frans)] => JUS(Justin), [2017-01-01(Sun)_AFNO@JUS(Justin)] => FRA(Frans)]).
    2017-04-14 05:53:23,522 [main] DEBUG     LS step (3), time spent (70873), score (-16hard/-70soft),     best score (-16hard/-70soft), accepted/selected move count (800/1195), picked move (2017-01-01(Sun)_AFNO@JUS(Justin) => ShiftTaskAllocation-66).
    2017-04-14 05:53:23,886 [main] DEBUG     LS step (4), time spent (71237), score (-16hard/-70soft),     best score (-16hard/-70soft), accepted/selected move count (800/1452), picked move ([[2017-01-01(Sun)_NIGH@BRA(Brandon)] => IRE(Irene), [2017-01-01(Sun)_AFNO@IRE(Irene)] => BRA(Brandon)]).
    2017-04-14 05:53:24,326 [main] DEBUG     LS step (5), time spent (71677), score (-16hard/-70soft),     best score (-16hard/-70soft), accepted/selected move count (800/1508), picked move (2017-01-01(Sun)_AFNO@FRA(Frans) <=> 2017-01-01(Sun)_NIGH@GEO(George)).
    ..........
    2017-04-14 05:54:08,226 [main] DEBUG     LS step (69), time spent (115577), score (-16hard/-70soft),     best score (-16hard/-70soft), accepted/selected move count (800/5125), picked move (2017-01-01(Sun)_MORN@DAI(Daisy) <=> 2017-01-01(Sun)_AFNO@EMI(Emily)).
    2017-04-14 05:54:10,613 [main] DEBUG     LS step (70), time spent (117964), score (-16hard/-70soft),     best score (-16hard/-70soft), accepted/selected move count (800/13436), picked move ([[2017-01-01(Sun)_AFNO@CHR(Christine)] => IRE(Irene), [2017-01-01(Sun)_NIGH@IRE(Irene)] => CHR(Christine)]).
    2017-04-14 05:54:12,649 [main] DEBUG     LS step (71), time spent (120000), score (-16hard/-70soft),     best score (-16hard/-70soft), accepted/selected move count (591/10978), picked move ([[2017-01-01(Sun)_AFNO@DAI(Daisy)] => EMI(Emily), [2017-01-01(Sun)_MORN@EMI(Emily)] => DAI(Daisy)]).
    2017-04-14 05:54:12,649 [main] INFO  Local Search phase (1) ended: step total (72), time spent (120000), best score (-16hard/-70soft).
    2017-04-14 05:54:12,649 [main] INFO  Solving ended: time spent (120000), best score (-16hard/-70soft), average calculate count per second (6544), environment mode (REPRODUCIBLE).
    

    匹配约束:

    mainShiftTaskAssignmentMustAssignedToAppropriateShiftAssignment=-6
    org.optaplanner.webexamples.nurserostering.rest.solver/mainShiftTaskAssignmentMustAssignedToAppropriateShiftAssignment/level0/[2017-01-01(Sun)_NIGH@GEO(George)]=-1
    org.optaplanner.webexamples.nurserostering.rest.solver/mainShiftTaskAssignmentMustAssignedToAppropriateShiftAssignment/level0/[2017-01-01(Sun)_NIGH@EMI(Emily)]=-1
    org.optaplanner.webexamples.nurserostering.rest.solver/mainShiftTaskAssignmentMustAssignedToAppropriateShiftAssignment/level0/[2017-01-01(Sun)_NIGH@BRA(Brandon)]=-1
    org.optaplanner.webexamples.nurserostering.rest.solver/mainShiftTaskAssignmentMustAssignedToAppropriateShiftAssignment/level0/[2017-01-01(Sun)_AFNO@JUS(Justin)]=-1
    org.optaplanner.webexamples.nurserostering.rest.solver/mainShiftTaskAssignmentMustAssignedToAppropriateShiftAssignment/level0/[2017-01-01(Sun)_AFNO@IRE(Irene)]=-1
    org.optaplanner.webexamples.nurserostering.rest.solver/mainShiftTaskAssignmentMustAssignedToAppropriateShiftAssignment/level0/[2017-01-01(Sun)_AFNO@FRA(Frans)]=-1
    fullShiftTaskAssignmentMustAssignedToAppropriateShiftAssignment=-4
    org.optaplanner.webexamples.nurserostering.rest.solver/fullShiftTaskAssignmentMustAssignedToAppropriateShiftAssignment/level0/[2017-01-01(Sun)_AFNO@CHR(Christine)]=-1
    org.optaplanner.webexamples.nurserostering.rest.solver/fullShiftTaskAssignmentMustAssignedToAppropriateShiftAssignment/level0/[2017-01-01(Sun)_MORN@ANT(Anton)]=-1
    org.optaplanner.webexamples.nurserostering.rest.solver/fullShiftTaskAssignmentMustAssignedToAppropriateShiftAssignment/level0/[2017-01-01(Sun)_MORN@HAN(Hanks)]=-1
    org.optaplanner.webexamples.nurserostering.rest.solver/fullShiftTaskAssignmentMustAssignedToAppropriateShiftAssignment/level0/[2017-01-01(Sun)_MORN@DAI(Daisy)]=-1
    sumTasksOfShiftLowerThan3=-6
    org.optaplanner.webexamples.nurserostering.rest.solver/sumTasksOfShiftLowerThan3/level0/[2017-01-01(Sun)_NIGH@GEO(George)]=-1
    org.optaplanner.webexamples.nurserostering.rest.solver/sumTasksOfShiftLowerThan3/level0/[2017-01-01(Sun)_NIGH@EMI(Emily)]=-1
    org.optaplanner.webexamples.nurserostering.rest.solver/sumTasksOfShiftLowerThan3/level0/[2017-01-01(Sun)_NIGH@BRA(Brandon)]=-1
    org.optaplanner.webexamples.nurserostering.rest.solver/sumTasksOfShiftLowerThan3/level0/[2017-01-01(Sun)_AFNO@JUS(Justin)]=-1
    org.optaplanner.webexamples.nurserostering.rest.solver/sumTasksOfShiftLowerThan3/level0/[2017-01-01(Sun)_AFNO@IRE(Irene)]=-1
    org.optaplanner.webexamples.nurserostering.rest.solver/sumTasksOfShiftLowerThan3/level0/[2017-01-01(Sun)_AFNO@FRA(Frans)]=-1
    

    我现在很困惑,为什么optaplanner会产生不可行的解决方案,无论我花多长时间,它仍然会产生不可行的解决方案。有谁知道,这个问题的根本原因是什么?谢谢和问候。

0 个答案:

没有答案