如何从 OptaPlanner 中的解决方案访问聚合结果

How to access an aggregate result from a solution in OptaPlanner

问题

我想评估流口水规则中解决方案的聚合值。例如,在学校调度场景中,员工每周工作时间不得超过 40 小时。我如何使用 TimeGrain 模式在约束中实现这种情况?

实验

我已经使用 EasyScoreCalculator 和 Drools 实现了这个场景的两个实现(参见下面的代码)。关系模型如下所示:

  1. 时间表代表一周。

  2. 方法 getTotalHours(...) returns 一周内安排的总小时数。

简易分数计算器

    public HardSoftScore calculateScore(Schedule schedule) {
        int hardScore = 0;
        int softScore = 0;
        int maxTimePerWeek = 40;

        for (Teacher teacher : schedule.getTeachers()) {
            int totalDuration = Schedule.getTotalHours(schedule.getScheduleTimes(),teacher);
            System.out.println("totalDuration for teacher: " + totalDuration);
            if(totalDuration > maxTimePerWeek)
                softScore += totalDuration - maxTimePerWeek;
        }
        return HardSoftScore.of(hardScore, softScore);
    }

流口水


rule "Teacher should have max 40 hours"
    when
        ScheduleTime(teacher != null, $teacher : teacher)
        Schedule($scheduleTimes: scheduleTimes)

    then
        int totalDuration = Schedule.getTotalHours($scheduleTimes, $teacher);
        System.out.println("totalDuration for teacher: " + totalDuration);
        if( totalDuration > 40){
            scoreHolder.penalize(kcontext, totalDuration - 40);
        }
end

流口水文件已正确配置,因为其他约束正常工作。

结果

第一个解决方案运行良好,但根据文档,EasyScoreCalculator 可能会导致性能问题。

第二个不起作用,因为控制台输出永远不会执行。但是,如果我删除 Schedule($scheduleTimes: scheduleTimes) 和相关代码,则会执行 "then" 子句。如何在 Drools 中获取此案例场景的聚合结果?

在 drools 中,你可以使用 accumulate 来对特定的老师进行分组(我假设你的 Teacher 对象中有一个 id,这样你就可以按特定的老师分组,并在你的计划时间)。这是关于累积 http://blog.athico.com/2009/06/how-to-implement-accumulate-functions.html and https://docs.jboss.org/drools/release/6.2.0.Final/drools-docs/html/ch07.html 的更详细的文章(搜索 "accumulate")。此检查将在所有 Teacher 实例上执行。

当教师的作业总时长超过 40 小时时,应匹配 "when" 条件。在"when"中已经过滤掉了,就不用在then中放条件了。 ` 什么时候 $teacher : 教师( $tId:id != null)

    $totalMinutes: Long(longValue > 40 * 60 )       
    accumulate( 
        ScheduleTime( 
            teacher != null, 
            teacher.id == $tId
        ),sum(minutes)
    )   
then    

    System.out.println("totalDuration for teacher: " + $totalMinutes/60);
    scoreHolder.penalize(kcontext, $totalMinutes/60- 40);

`