为什么在调用calculateScore 之前没有设置我的PlanningVariables?

时间:2021-01-16 22:13:13

标签: optaplanner

刚开始使用 Optaplanner 7.36.0。一个简单的项目开始。在我的解决方案类中有 1 个 @PlanningEntity 集合。每个@PlanningEntity 实例都有 2 个 @PlanningVariable 实例成员,两者都通过 valueRangeProviderRefs= 指定我的解决方案类中的 2 个 List @ProblemFactCollectionProperty 属性。所以,我期待solver.solve() 调用getter 来设置PlanningEntity 实例中的PlanningVariable 属性。但是当调用 calculateScore() 方法时,提供的解决方案实例具有 PlanningEntity 实例的集合,所有实例都具有未设置的 PlanningVariable 属性。并且我在 PlanningVariable 设置器上的断点从未被触发 - 这与未设置 PlanningVariables 的 PlanningEntity 实例一致,但为什么呢?

使用 optaplanner-examples 中的 Conferencescheduling 来指导我,但我显然遗漏了一些细节,对我来说不明显的是哪个可能负责未分配的 PlanningVariables。我没有在我的 @PlanningVariable 注释使用中使用 nullable = true 设置。但是其他原因会导致类似的行为吗?

我移交给solver.solve() 方法的解决方案实例如上所述具有未初始化的PlanningVariables,以及我的@PlanningSolution 类中的未初始化@PlanningScore 属性。我也没有在我的解决方案类中提供/覆盖 cloneSolution 方法,因为我没有任何从 PlanningEntity 实例返回到解决方案的引用。根据我的理解,将 PlanningVariables 统一化是预期状态,也许我遗漏/混淆了重要方面?

这是我正在使用的solverConfig.xml:

<?xml version="1.0" encoding="UTF-8"?>
<solver xmlns="https://www.optaplanner.org/xsd/solver" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="https://www.optaplanner.org/xsd/solver https://www.optaplanner.org/xsd/solver/solver.xsd">
  <environmentMode>FULL_ASSERT</environmentMode><!-- To slowly prove there are no bugs in this code -->
  <!-- Domain model configuration -->
  <solutionClass>org.shespelt.uslnj.domain.Season</solutionClass>
  <entityClass>org.shespelt.uslnj.domain.Fixture</entityClass>
  <!-- Score configuration -->
  <scoreDirectorFactory>
    <easyScoreCalculatorClass>org.shespelt.uslnj.score.SeasonEasyScoreCalculator</easyScoreCalculatorClass>
    </scoreDirectorFactory>
  <!-- Optimization algorithms configuration -->
  <termination>
    <secondsSpentLimit>300</secondsSpentLimit>
  </termination>
  <!-- Tabu Search performs much better than Late Acceptance (default algo) on this use case -->
  <constructionHeuristic/>

  <exhaustiveSearch>
    <exhaustiveSearchType>BRUTE_FORCE</exhaustiveSearchType>
  </exhaustiveSearch>
</solver>

@PlanningEntity 类

package org.shespelt.uslnj.domain;

import org.optaplanner.core.api.domain.entity.PlanningEntity;
import org.optaplanner.core.api.domain.variable.PlanningVariable;

/**
 * see section 18.4.1 regarding making a PlanningEntity immovable (ie. a match must be defined by the current
 * home & away teams for the given PlanningEntity (Fixture.class). Might be useful? along with the PlanningPin
 */

@PlanningEntity
public class Fixture extends AbstractPersistable {

    private Integer matchDay = null;

    @PlanningVariable( valueRangeProviderRefs = {"homeTeamRange"})
    private Team homeTeam=null;

    @PlanningVariable( valueRangeProviderRefs = {"awayTeamRange"})
    private Team awayTeam=null;

    public Fixture( long id, int matchDay, Team homeTeam, Team awayTeam ) {
        super.setId(id);
        this.matchDay = matchDay;
        this.homeTeam = homeTeam;
        this.awayTeam = awayTeam;
        System.out.println("Fixture() -> just created via non-empty CTOR. ID: " + this.getId().toString());
    }
    // the no-arg Ctor only needed by Optaplanner framework - when cloning so it ought to handle, through
    // reflection, the setting of all of the properties.
    public Fixture() {
        super();
        System.out.println("Fixture() - no-arg CTOR invoked. ID: " +
                ( this.getId() != null ? this.getId().toString():"NULL") );
    }

    //@PlanningVariable( valueRangeProviderRefs = { "matchDayRange"})
    public Integer getMatchDay() {
        return matchDay;
    }

    public void setMatchDay(Integer theMatchDay) {
        this.matchDay = theMatchDay;
    }


    public Team getHomeTeam() {
        return homeTeam;
    }

    public void setHomeTeam(Team homeTeam) {
        this.homeTeam = homeTeam;
    }


    public Team getAwayTeam() {
        return awayTeam;
    }

    public void setAwayTeam(Team awayTeam) {
        this.awayTeam = awayTeam;
    }

    @Override
    public String toString() {
        return "Fixture: " + this.getId() + ", matchDay: " + this.getMatchDay() +
                ", homeTeam: " + this.getHomeTeam() + ", awayTeam: " + this.getAwayTeam();
    }
}

这是我的 main() - 简单的开始...

// build the solver
SolverFactory<Season> solverFactory = SolverFactory.createFromXmlResource(
               "org/shespelt/uslnj/seasonSolverConfig.xml");
Solver<Season> solver = solverFactory.buildSolver();
// instantiate the uninitialized Season - with our Fixtures, Teams, Divisions,
Season unsolvedSeason = loadData();

// solve it
Season scheduledSeason = solver.solve( unsolvedSeason );
scheduledSeason.showSchedule();

更新观察:最终解决方案的得分很差(硬:-2000,软:-409,600),但这比最差的(硬:-214749364,软:-214748364)要好 并且我收到了许多 caculateScore() 调用返回这种更糟糕的情况(所有 Fixture PlanningEntity 的 PlanningVariables 都未设置(特定团队实例)。总共计算了 337 个分数,其中 261 个是最坏的情况 - 两个 PlanningVariables 的调用calculateScore() 时未设置PlanningEntity。

最好的情况仍然很糟糕,因为只有 1 支球队被设置为主队,也被设置为客队 - 本赛季所有赛程的配对都是相同的。因此,-2000 分数 - 逻辑返回 -1000 难于计算 Score() 情况,其中 2 个团队是相同的。

如果我认为我必须实现某种 MoveSelector ,我会不会走在“正确”的轨道上?因为我相当“担心”每个解决方案只考虑来自 homeTeamList 和 awayTeamList List 实例的每个团队的 1 个团队。因为我看到 1 个团队被用于两个声明的 PlanningEntity 集合的所有 Fixture 实例(所有 PlanningEntity 实例)。每个 PlanningVariable 都有一个提供的 valueRangeProviderRefs,它是我的 PlanningSolution 中的一个不同列表。够了吗?它只运行 1 秒。

也许我的问题是我没有正确建模我的域。与示例旅行锦标赛的建模方式进行比较,但两者必须不同。在我的领域中,整套装置的日期已设置(计划事实),但我需要解决的是什么装置匹配满足所有约束 - 硬和软 - 在“最佳”案例解决方案中,对于所有'Season' 中的灯具(由 PlanningEntity(灯具类实例)列表组成的规划解决方案)。 谢谢,

0 个答案:

没有答案