OptaPlanner,显示排除的解决方案

OptaPlanner, excluded solution shows up

请通过这个简单的任务分配项目帮助我理解 DRL。 2 个工人 id=1 和 2,3 个任务 id=1,2,3,每个任务的持续时间以秒为单位。任务 1 和 3 的持续时间比任务 2 多一点。开始时我使用以下规则(只有一个规则)试图平衡每个工人的总时间,所以我希望一个工人完成任务 1 和 3,而另一个则需要任务 2.

rule "fairness"
    when
        $worker: Worker()
        accumulate(
            Task(worker == $worker, $d: durationInSec);
            $s: sum($d*$d)
        )       
    then
        scoreHolder.addSoftConstraintMatch(kcontext, -$s.longValue());
end

结果是所有任务总是分配给工作人员 1。我想调试这个问题并将上面的规则替换为以下 2 条规则:

rule "A" 
    when
        $worker: Worker(id == 1) // for worker 1
    then
        scoreHolder.addHardConstraintMatch(kcontext, -1); // hard score -1
end

rule "B"
    when
        $worker: Worker(id == 2) // for worker 2
    then
        scoreHolder.addSoftConstraintMatch(kcontext, 1); // soft score +1
end

我以为工人 1 会因为规则“A”而被排除在外,但结果仍然是,工人 1 获得了所有 3 个任务,输出如下:

o.o.c.i.l.DefaultLocalSearchPhase : Local Search phase (1) ended: time spent (30001), best score (-3hard/3soft), score calculation speed (7309/sec), step total (12948).
o.o.core.impl.solver.DefaultSolver: Solving ended: time spent (30002), best score (-3hard/3soft), score calculation speed (7258/sec), phase total (2), environment mode (NON_REPRODUCIBLE)

我希望工人 2 完成所有 3 个任务,分数为 0hard/3soft。以下是我的部分解决方案class,问题出在哪里?

@PlanningSolution
@Entity
public class Solution {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    
    private SolverStatus solverStatus;
    
    @PlanningScore
    private HardSoftLongScore score;
    
    @ProblemFactCollectionProperty
    @OneToMany
    @ValueRangeProvider(id = "workerRange")
    private List<Worker> workerList;

    @PlanningEntityCollectionProperty
    @OneToMany
    private List<Task> taskList;
    ...
    
    
@Entity
@PlanningEntity
public class Task implements Comparable<Task> {
    
    @PlanningId
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @ManyToOne(cascade=CascadeType.ALL)
    @PlanningVariable(valueRangeProviderRefs = "workerRange")
    private Worker worker;

当在 RHS 中放置 println 时,我确实看到 worker 1 和 2 都被打印出来了:

System.out.println("worker=" + $worker.toString() + ", task=" + $tb.toString() + "; " +
    scoreHolder.getHardScore() + "hard/" + scoreHolder.getHardScore() + "soft");

...
worker=1, task=2; -3hard/-3soft
worker=1, task=1; -3hard/-3soft
worker=2, task=3; -3hard/-3soft
worker=2, task=2; -3hard/-3soft
worker=2, task=1; -3hard/-3soft
worker=1, task=3; -3hard/-3soft
worker=2, task=1; -3hard/-3soft
worker=2, task=1; -3hard/-3soft
...

规则“A”和“B”penalize/reward纯粹是为了这两个工人的存在,他们没有说任何分配给他们的任务。

乍一看,我没有发现第一个约束(“公平”)有什么问题。您始终可以在规则的 then 部分添加 System.out.println(...) 以对其进行调试。

或者,如果您更喜欢 Java 而不是 DRL,也许 ConstraintStreams API 对您来说可能会很有趣。