optaplanner - 为什么在调用 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 列表(固定装置类实例)组成。谢谢,
解决方案
推荐阅读
- python - 使用 nltk word_tokenize 进行标记后重新加入原始句子
- android - 用于 android 开发的 IntelliJ IDEA 属性部分在哪里?
- java - SpringFox:枚举数组显示为字符串数组
- html - 在 HTML 中调整图像相对于彼此的大小
- azure-node-sdk - ms-rest-nodeauth (azure-sdk-for-js) :错误:凭据参数需要实现 signRequest 方法
- python - 使用迭代的 Kivy 下拉列表
- python - Matplotlib - 图形图例不适用于具有多个轴的多个子图
- c# - 如何在不丢失输入数据的情况下在两个表单之间来回移动
- keycloak - 在 keycloak 中设置两层角色模型
- flutter - 如何在 google_maps_flutter 中隐藏 3d 建筑物?