首页 > 解决方案 > 为什么在调用 calculateScore 之前没有设置我的 PlanningVariables?

问题描述

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

使用 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( ) 调用返回这种更坏的情况(所有夹具 PlanningEntity 的 PlanningVariables 都未设置(特定团队实例)。总共计算 337 个分数,其中 261 个是最坏的情况 - 当调用 calculateScore() 时,PlanningEntity 的两个 PlanningVariables 都未设置。

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

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

也许我的问题是我没有正确建模我的域。与示例 Traveling Tournament 的建模方式相比,但两者必须不同。在我的领域中,设置了整套固定装置的日期(计划事实),但我需要解决的是固定装置匹配满足所有约束 - 无论是硬约束还是软约束 - 在“最佳”案例解决方案中,对于所有“季节”中的固定装置(规划解决方案由 PlanningEntity 列表(固定装置类实例)组成。谢谢,

标签: optaplanner

解决方案


推荐阅读