使用红帽流程自动化管理器中的 OptaPlanner 构建解决者
摘要
前言 复制链接链接已复制到粘贴板!
作为业务决策和流程的开发人员,您可以使用红帽构建的 OptaPlanner 构建来开发可决定最佳解决方案以规划问题的最佳解决方案。OptaPlanner 是 Red Hat Process Automation Manager 的内置组件。您可以使用 solution 作为 Red Hat Process Automation Manager 中的服务的一部分,以根据特定限制优化有限资源。
使开源包含更多 复制链接链接已复制到粘贴板!
红帽致力于替换我们的代码、文档和 Web 属性中存在问题的语言。我们从这四个术语开始: master、slave、blacklist 和 whitelist。这些更改将在即将发行的几个发行本中逐渐实施。详情请查看 CTO Chris Wright 的信息。
部分 I. 将 OptaPlanner 项目的红帽构建升级到 OptaPlanner 8 复制链接链接已复制到粘贴板!
如果您有使用 OptaPlanner 7 或更早的 pubic API 创建的 OptaPlanner 项目,并希望将项目代码升级到 OptaPlanner 8,请参阅本指南中的信息。本指南还包括对在 pubic API 之外的实施类的更改。
OptaPlanner 公共 API 是 OptaPlanner 源代码的子集,可让您通过 Java 代码与 OptaPlanner 进行交互。因此,您可以在同一主发行版本中将 OptaPlanner 版本升级到更高的 OptaPlanner 版本,OptaPlanner 会遵循语义版本。这意味着,您可以在不破坏使用 OptaPlanner public API 的代码的情况下,从 OptaPlanner 7.44 升级到 OptaPlanner 7.48。OptaPlanner 公共 API 类在主版本的 OptaPlanner 发行版本中兼容。但是,当红帽发布一个新的主发行版本时,可能会破坏对公共 API 的更改。
OptaPlanner 8 是一个新的主发行版本,对公共 API 的一些更改与早期版本的 OptaPlanner 不兼容。OptaPlanner 8 将成为未来几年 8.x 系列的基础。对于这个项目的长期优势,对与这个版本要求的早期版本不兼容的公共 API 的更改。
| 进程自动化管理器 | OptaPlanner |
|---|---|
| 7.7 | 7.33 |
| 7.8 | 7.39 |
| 7.9 | 7.44 |
| 7.10 | 7.48 |
| 7.11 | 8.5 |
每个升级注意事项都有一个标签,表明您的代码会受到该更改的影响。下表描述了每个标签:
| 标签 | 影响 |
|---|---|
| 主要 | 可能会影响您的代码。 |
| 次 | 不太可能影响您的代码,特别是如果您进行了大量自定义代码,尤其是在遵循示例时。 |
任何与早期版本的 OptaPlanner 不兼容的更改都会使用 Public API 标签进行注解。
第 1 章 与 OptaPlanner 7.x 或更早版本不兼容的更改 复制链接链接已复制到粘贴板!
本节中列出的更改与 OptaPlanner 7.x 或早期版本的 OptaPlanner 不兼容。
需要 Java 11 或更高版本
主要,Public API
如果您使用 JRE 或 JDK 8,升级到 JDK 11 或更高版本。
在 Linux 上,从您的 Linux 软件存储库获取 OpenJDK。在 Fedora 和 Red Hat Enterprise Linux 中输入以下命令:
sudo dnf install java-11-openjdk-devel
sudo dnf install java-11-openjdk-develCopy to Clipboard Copied! Toggle word wrap Toggle overflow - 在 Windows 和 macOS 中,从 AdoptOpenJDK 网站下载 OpenJDK。
SolverFactory 和 PlannerBenchmarkFactory 不再支持 KIE 容器
主要,Public API
因为 OptaPlanner 现在与 Kogito 一致,所以 KIE 容器概念不再适用。因此,SolverFactory 不再允许从 KIE 容器创建 Solver 实例。这也适用于 PlannerBenchmarkFactory 和 基准。
已删除 OSGi 元数据
主要,Public API
由于 OSGi 有限的使用及其带来的维护负担,OptaPlanner 8.x 系列中的 OptaPlanner JAR 文件不再包括在 META-INF/MANIFEST.MF 文件中。
使用 Java 序列化
小、公共 API
在 OptaPlanner 8 中,大多数使用 Serializable 标记接口已从公共 API 中删除。考虑使用 JSON 或其他格式序列化。
SolverFactory.getScoreDirectorFactory() 替换为 ScoreManager
主要,Public API
在 OptaPlanner 版本 7 中,需要使用 ScoreDirectorFactory 来解释分数。在 OptaPlanner 版本 8 中,新的功能被添加到 ScoreManager 中,因此不再有理由创建 ScoreDirector 的新实例。
OptaPlanner 7 中的 *.java 文件示例:
ScoreDirectorFactory<CloudBalance> scoreDirectorFactory = solverFactory.getScoreDirectorFactory();
try (ScoreDirector<CloudBalance> scoreDirector = scoreDirectorFactory.buildScoreDirector()) {
scoreDirector.setWorkingSolution(solution);
Score score = scoreDirector.calculateScore();
}
ScoreDirectorFactory<CloudBalance> scoreDirectorFactory = solverFactory.getScoreDirectorFactory();
try (ScoreDirector<CloudBalance> scoreDirector = scoreDirectorFactory.buildScoreDirector()) {
scoreDirector.setWorkingSolution(solution);
Score score = scoreDirector.calculateScore();
}
OptaPlanner 8 中的 *.java 文件示例:
ScoreManager<CloudBalance> scoreManager = ScoreManager.create(solverFactory); Score score = scoreManager.updateScore(solution);
ScoreManager<CloudBalance> scoreManager = ScoreManager.create(solverFactory);
Score score = scoreManager.updateScore(solution);
允许您检索 ScoreDirector 和 ScoreDirectorFactory 实例的方法已从公共 API 中删除,无需替换。缩减了 ScoreDirector 接口版本被提升到公共 API,以将 ProblemFactChange 接口提升到公共 API。
SolverFactory:getSolverConfig() 删除
小、公共 API
SolverFactory.getSolverConfig() 方法已弃用,并被 SolverFactory.create(SolverConfig) 方法替代。SolverConfig 实例现在在实例化 SolverFactory 实例之前被实例化,这是更自然的。以上顺序已被删除。
OptaPlanner 7 中的 *.java 文件示例:
SolverFactory<MySolution> solverFactory = SolverFactory.createFromXmlResource(".../mySolverConfig.xml");
SolverConfig solverConfig = solverFactory.getSolverConfig();
...
Solver<MySolution> solver = solverFactory.buildSolver();
SolverFactory<MySolution> solverFactory = SolverFactory.createFromXmlResource(".../mySolverConfig.xml");
SolverConfig solverConfig = solverFactory.getSolverConfig();
...
Solver<MySolution> solver = solverFactory.buildSolver();
OptaPlanner 8 中的 *.java 文件示例:
SolverConfig solverConfig = SolverConfig.createFromXmlResource(".../mySolverConfig.xml");
...
SolverFactory<MySolution> solverFactory = SolverFactory.create(solverConfig);
Solver<MySolution> solver = solverFactory.buildSolver();
SolverConfig solverConfig = SolverConfig.createFromXmlResource(".../mySolverConfig.xml");
...
SolverFactory<MySolution> solverFactory = SolverFactory.create(solverConfig);
Solver<MySolution> solver = solverFactory.buildSolver();
如果您还通过了类 Loader,请将其传递到 SolverConfig.createFromXmlResource() 和 SolverFactory.create()。
SolverConfig:buildSolver() 删除
小、公共 API
SolverConfig.buildSolver() 方法是不属于公共 API 的内向方法。改为使用 SolverFactory.buildSolver() 方法。
OptaPlanner 7 中的 *.java 文件示例:
SolverConfig solverConfig = SolverConfig.createFromXmlResource(".../mySolverConfig.xml");
...
Solver<MySolution> solver = solverConfig.buildSolver();
SolverConfig solverConfig = SolverConfig.createFromXmlResource(".../mySolverConfig.xml");
...
Solver<MySolution> solver = solverConfig.buildSolver();
OptaPlanner 8 中的 *.java 文件示例:
SolverConfig solverConfig = SolverConfig.createFromXmlResource(".../mySolverConfig.xml");
...
SolverFactory<MySolution> solverFactory = SolverFactory.create(solverConfig);
Solver<MySolution> solver = solverFactory.buildSolver();
SolverConfig solverConfig = SolverConfig.createFromXmlResource(".../mySolverConfig.xml");
...
SolverFactory<MySolution> solverFactory = SolverFactory.create(solverConfig);
Solver<MySolution> solver = solverFactory.buildSolver();
PlannerBenchmarkConfig:buildPlannerBenchmark() 删除
小、公共 API
PlannerBenchmarkConfig.buildPlannerBenchmark() 方法是不属于公共 API 的内建方法。使用 PlannerBenchmarkFactory.buildPlannerBenchmark() 方法。
OptaPlanner 7 中的 *.java 文件示例:
PlannerBenchmarkConfig benchmarkConfig = PlannerBenchmarkConfig.createFromXmlResource(
".../cloudBalancingBenchmarkConfig.xml");
...
PlannerBenchmark benchmark = benchmarkFactory.buildPlannerBenchmark();
PlannerBenchmarkConfig benchmarkConfig = PlannerBenchmarkConfig.createFromXmlResource(
".../cloudBalancingBenchmarkConfig.xml");
...
PlannerBenchmark benchmark = benchmarkFactory.buildPlannerBenchmark();
OptaPlanner 8 中的 *.java 文件示例:
PlannerBenchmarkConfig benchmarkConfig = PlannerBenchmarkConfig.createFromXmlResource(
".../cloudBalancingBenchmarkConfig.xml");
...
PlannerBenchmarkFactory benchmarkFactory = PlannerBenchmarkFactory.create(benchmarkConfig);
PlannerBenchmark benchmark = benchmarkFactory.buildPlannerBenchmark();
PlannerBenchmarkConfig benchmarkConfig = PlannerBenchmarkConfig.createFromXmlResource(
".../cloudBalancingBenchmarkConfig.xml");
...
PlannerBenchmarkFactory benchmarkFactory = PlannerBenchmarkFactory.create(benchmarkConfig);
PlannerBenchmark benchmark = benchmarkFactory.buildPlannerBenchmark();
solverFactory:cloneSolverFactory() 已删除
小、公共 API
SolverFactory.cloneSolverFactory() 方法已弃用,并被新的 SolverConfig(SolverConfig) copyors 和 SolverFactory.cloneSolverFactory() 方法替代。
OptaPlanner 7 中的 *.java 文件示例:
OptaPlanner 8 中的 *.java 文件示例:
SolverFactory:createEmpty() 删除
小、公共 API
SolverFactory.createEmpty() 方法已弃用,并被 新的 SolverConfig() 方法替代。SolverFactory.createEmpty() 方法已被删除。
OptaPlanner 7 中的 *.java 文件示例:
SolverFactory<MySolution> solverFactory = SolverFactory.createEmpty(); SolverConfig solverConfig = solverFactory.getSolverConfig() ... Solver<MySolution> solver = solverFactory.buildSolver();
SolverFactory<MySolution> solverFactory = SolverFactory.createEmpty();
SolverConfig solverConfig = solverFactory.getSolverConfig()
...
Solver<MySolution> solver = solverFactory.buildSolver();
OptaPlanner 8 中的 *.java 文件示例:
SolverConfig solverConfig = new SolverConfig(); ... SolverFactory<MySolution> solverFactory = SolverFactory.create(solverConfig); Solver<MySolution> solver = solverFactory.buildSolver();
SolverConfig solverConfig = new SolverConfig();
...
SolverFactory<MySolution> solverFactory = SolverFactory.create(solverConfig);
Solver<MySolution> solver = solverFactory.buildSolver();
XML <solver/> root 元素现在属于 http://www.optaplanner.org/xsd/solver 命名空间
主要,Public API
OptaPlanner 现在为解决器配置提供了一个 XML 模式定义。虽然 OptaPlanner 保留与现有 XML 配置的早期版本的兼容性,但强烈建议迁移到 XSD,因为 OptaPlanner 可能在以后只支持有效的配置 XML。
OptaPlanner 7 中的 *SolverConfig.xml 文件中的示例:
<?xml version="1.0" encoding="UTF-8"?> <solver> ... </solver>
<?xml version="1.0" encoding="UTF-8"?>
<solver>
...
</solver>
OptaPlanner 8 中的 *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"> ... </solver>
<?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">
...
</solver>
使用 XSD 可能需要重新排序配置的一些 XML 元素。在 IDE 中使用代码完成迁移到有效的 XML。
移动选择器配置中的属性 subPillarEnabled 已被删除
小、公共 API
PillarSwapMoveSelector 和 PillarChangeMoveSelector 中的 subPillarEnabled 属性已弃用,并被一个新的属性 subPillarType 替代。subPillarEnabled 属性已被删除。
OptaPlanner 7 中的 *SolverConfig.xml 和 *BenchmarkConfig.xml 文件中的示例:
OptaPlanner 8 中的 *SolverConfig.xml 和 *BenchmarkConfig.xml 文件中的示例:
solver:getScoreDirectorFactory() 已删除
主要,Public API
getScoreDirectorFactory() 方法已被弃用,现已从 Solver 和 类中删除。
Solver Factory
您不再需要创建一个 Solver 实例来计算或解释 UI 中的分数。使用 ScoreManager API 替代。
OptaPlanner 7 中的 *.java 文件示例:
SolverFactory<VehicleRoutingSolution> solverFactory = SolverFactory.createFromXmlResource(...); Solver<VehicleRoutingSolution> solver = solverFactory.buildSolver(); uiScoreDirectorFactory = solver.getScoreDirectorFactory(); ...
SolverFactory<VehicleRoutingSolution> solverFactory = SolverFactory.createFromXmlResource(...);
Solver<VehicleRoutingSolution> solver = solverFactory.buildSolver();
uiScoreDirectorFactory = solver.getScoreDirectorFactory();
...
OptaPlanner 8 中的 *.java 文件示例:
SolverFactory<VehicleRoutingSolution> solverFactory = SolverFactory.createFromXmlResource(...); ScoreManager<VehicleRoutingSolution> scoreManager = ScoreManager.create(solverFactory); ...
SolverFactory<VehicleRoutingSolution> solverFactory = SolverFactory.createFromXmlResource(...);
ScoreManager<VehicleRoutingSolution> scoreManager = ScoreManager.create(solverFactory);
...
ScoreDirectorFactory 不再应当使用,因为它始终不在公共 API 外部,且其功能都由公共 API 的不同部分公开。
solver.explainBestScore() 已被删除
主要,Public API
Solver 接口上的 explainBestScore() 方法已在 7.x 中弃用,现在已被删除。您可以通过新的 ScoreManager API 获取相同的信息。
红帽建议您不要以任何方式解析这个方法调用的结果。
OptaPlanner 7 中的 *.java 文件示例:
solver = ...; scoreExplanation = solver.explainBestScore();
solver = ...;
scoreExplanation = solver.explainBestScore();
OptaPlanner 8 中的 *.java 文件示例:
MySolution solution = ...; ScoreManager<MySolution> scoreManager = ...; scoreExplanation = scoreManager.explainScore(solution);
MySolution solution = ...;
ScoreManager<MySolution> scoreManager = ...;
scoreExplanation = scoreManager.explainScore(solution);
Solver 接口方法 getBestSolution ()、getBestScore() 和 getTimeMillisSpent() 已被删除
小、公共 API
Solver 接口中的几种方法已在 7.x 中弃用,并已被删除。您可以通过通过 Solver.add 注册 EventListener 来获取相同的信息。
EventListener (…)
OptaPlanner 7 中的 *.java 文件示例:
solver = ...; solution = solver.getBestSolution(); score = solver.getBestScore(); timeMillisSpent = solver.getTimeMillisSpent();
solver = ...;
solution = solver.getBestSolution();
score = solver.getBestScore();
timeMillisSpent = solver.getTimeMillisSpent();
OptaPlanner 8 中的 *.java 文件示例:
已删除注解扫描
主要,Public API
solver 配置中的 <scanAnnotatedClasses /> 指令已在 7.x 中弃用,现已被删除。
OptaPlanner 7 中的 *.xml 文件示例:
<solver>
...
<scanAnnotatedClasses/>
...
</solver>
<solver>
...
<scanAnnotatedClasses/>
...
</solver>
OptaPlanner 8 中的 *.xml 文件示例:
用于 @PlanningFactProperty 和 @PlanningFactCollection的新软件包
主要,Public API
@PlanningFactProperty 和 @PlanningFactCollectionProperty 注释现在与其他类似注释(如 @PlanningSolution )共享相同的软件包。旧注解在 7.x 中弃用并被删除。
OptaPlanner 7 中的 *.java 文件示例:
import org.optaplanner.core.api.domain.solution.drools.ProblemFactCollectionProperty; import org.optaplanner.core.api.domain.solution.drools.ProblemFactProperty;
import org.optaplanner.core.api.domain.solution.drools.ProblemFactCollectionProperty;
import org.optaplanner.core.api.domain.solution.drools.ProblemFactProperty;
OptaPlanner 8 中的 *.java 文件示例:
import org.optaplanner.core.api.domain.solution.ProblemFactCollectionProperty; import org.optaplanner.core.api.domain.solution.ProblemFactProperty;
import org.optaplanner.core.api.domain.solution.ProblemFactCollectionProperty;
import org.optaplanner.core.api.domain.solution.ProblemFactProperty;
filterClassList 替换为单个过滤器类
小、公共 API
EntitySelector 的配置ValueSelector 和 MoveSelector 现在在配置 API 和 addressr 配置 XML 中均有一个过滤器类。
在实践中,您不需要多个选择过滤类,您可以使用单一选择过滤器类替换它们,该类实施所有这些逻辑。现在,传递单个选择类需要较少的样板代码。
OptaPlanner 7 中的 *.java 文件示例:
ValueSelectorConfig valueSelectorConfig = new ValueSelectorConfig(); valueSelectorConfig.setFilterClassList(Collections.singletonList(MySelectionFilterClass.class));
ValueSelectorConfig valueSelectorConfig = new ValueSelectorConfig();
valueSelectorConfig.setFilterClassList(Collections.singletonList(MySelectionFilterClass.class));
OptaPlanner 8 中的 *.java 文件示例:
ValueSelectorConfig valueSelectorConfig = new ValueSelectorConfig(); valueSelectorConfig.setFilterClass(MySelectionFilterClass.class);
ValueSelectorConfig valueSelectorConfig = new ValueSelectorConfig();
valueSelectorConfig.setFilterClass(MySelectionFilterClass.class);
使用单个选择过滤器类替换多个选择过滤器类
OptaPlanner 7 中的 *.xml 文件示例:
OptaPlanner 7 中的 *.java 文件示例:
OptaPlanner 8 中的 *.xml 文件示例:
<swapMoveSelector>
<entitySelector>
<filterClass>com.example.SingleEntityFilter</filterClass>
</entitySelector>
</swapMoveSelector>
<swapMoveSelector>
<entitySelector>
<filterClass>com.example.SingleEntityFilter</filterClass>
</entitySelector>
</swapMoveSelector>
OptaPlanner 8 中的 *.java 文件示例:
AcceptorConfig 重命名为 LocalSearchAcceptorConfig
次
这只会影响配置 API。解决器配置 XML 文件保持不变。
实施了与其他本地搜索特定配置类的命名一致性。
OptaPlanner 7 中的 *.java 文件示例:
LocalSearchPhaseConfig localSearchPhaseConfig = new LocalSearchPhaseConfig()
.withAcceptorConfig(new AcceptorConfig().withEntityTabuSize(5));
LocalSearchPhaseConfig localSearchPhaseConfig = new LocalSearchPhaseConfig()
.withAcceptorConfig(new AcceptorConfig().withEntityTabuSize(5));
OptaPlanner 8 中的 *.java 文件示例:
LocalSearchPhaseConfig localSearchPhaseConfig = new LocalSearchPhaseConfig()
.withAcceptorConfig(new LocalSearchAcceptorConfig().withEntityTabuSize(5));
LocalSearchPhaseConfig localSearchPhaseConfig = new LocalSearchPhaseConfig()
.withAcceptorConfig(new LocalSearchAcceptorConfig().withEntityTabuSize(5));
自定义属性 XML 配置格式更改
小、公共 API
此问题只影响解决器配置 XML,特别是 < scoreDirectorFactory/>, < moveIteratoronnectionFactoryy/>, < moveListFactory/> , & lt; partitionedSearch/> 和 & lt ;customPhase/>。
这个更改的目的是在构建时强制配置 XML 的结构。
OptaPlanner 7 中的 *.xml 文件示例:
OptaPlanner 8 中的 *.xml 文件示例:
<variableNameInclude/& gt; 元素现在被 < variableNameIncludes/> 元素嵌套
小、公共 API
这个版本只影响解决器配置 XML,特别是 < swapMoveSelector/> 和 & lt ;pillarSwapMoveSelector/> 元素。
这个更改的目的是在构建时强制配置 XML 的结构。
OptaPlanner 7 中的 *.xml 文件示例:
<swapMoveSelector> <variableNameInclude>variableA</variableNameInclude> <variableNameInclude>variableB</variableNameInclude> </swapMoveSelector>
<swapMoveSelector>
<variableNameInclude>variableA</variableNameInclude>
<variableNameInclude>variableB</variableNameInclude>
</swapMoveSelector>
OptaPlanner 8 中的 *.xml 文件示例:
删除 解决方案 接口
小、公共 API
Solution 接口已弃用并删除。同时还删除了 AbstractSolution 接口(仅适用于 Business Central)。
删除 Solution 接口,将 getScore() 方法标上 @PlanningScore,并将 getProblemFacts() 方法替换为 @ProblemFactCollectionProperty 注释(或字段)。
OptaPlanner 7 中的 *.java 文件示例:
OptaPlanner 8 中的 *.java 文件示例:
对于没有嵌套在 Collection 中的单个问题事实,请使用 @ProblemFactProperty 注释,如下例所示,其中字段注解如下:
OptaPlanner 7 中的 *.java 文件示例:
OptaPlanner 8 中的 *.java 文件示例:
不要在 getters(或字段)上添加 @ProblemFactCollectionProperty 注释,该注释具有 @PlanningEntityCollectionProperty 注释。
BestSolutionChangedEvent:isNewBestSolutionInitialized() 已删除
小、公共 API
best SolutionChangedEvent.isNewBestSolutionInitialized() 方法已弃用,并替换为 BestSolutionChangedEvent.getNewBestSolution().getScore().isSolutionInitialized() 方法。best SolutionChangedEvent.isNewBestSolutionInitialized() 方法已被删除。
OptaPlanner 7 中的 *.java 文件示例:
OptaPlanner 8 中的 *.java 文件示例:
如果您检查 是否可行(),它会检查是否初始化解决方案。
OptaPlanner 8 中的 *.java 文件示例:
<valueSelector > : variableName 现在是一个属性
小、公共 API
当 power-tweaking 选择器(如 < ;changeMoveSelector& gt;)时,带有多个规划变量的用例中,< ;variableName& gt; XML 元素已被一个 variableName="…" XML 属性替代。这个变化减少了解决器配置详细程度。在整个 7.x 系列中被弃用后,旧方法现已被删除。
OptaPlanner 7 中的 *SolverConfig.xml 和 *BenchmarkConfig.xml 文件中的示例:
<valueSelector>
<variableName>room</variableName>
</valueSelector>
<valueSelector>
<variableName>room</variableName>
</valueSelector>
OptaPlanner 8 中的 *SolverConfig.xml 和 *BenchmarkConfig.xml 文件中的示例:
<valueSelector variableName="room"/>
<valueSelector variableName="room"/>
分区搜索: 已删除 threadFactoryClass
小、公共 API
因为 <solver& gt; 已在某些情况下支持 <threadFactoryClass > 元素,所以 & lt; gt; 元素已被删除。
partitionedSearch> 下的 <threadFactoryClass &
OptaPlanner 7 中的 *SolverConfig.xml 和 *BenchmarkConfig.xml 文件中的示例:
OptaPlanner 8 中的 *SolverConfig.xml 和 *BenchmarkConfig.xml 文件中的示例:
SimpleDoubleScore 和 HardSoftDoubleScore 已删除
小、公共 API
不建议使用基于双倍的分数类型,因为它们可能会造成损坏。它们已被删除。
OptaPlanner 7 中的 *.java 文件示例:
OptaPlanner 8 中的 *.java 文件示例:
Score.toInitializedScore() removed
小、公共 API
Score.toInitializedScore() 方法已弃用,并替换为 7.x 中的 Score.withInitScore(int) 方法,现已被删除。
OptaPlanner 7 中的 *.java 文件示例:
score = score.toInitializedScore();
score = score.toInitializedScore();
OptaPlanner 8 中的 *.java 文件示例:
score = score.withInitScore(0);
score = score.withInitScore(0);
各种 验证工具 被删除
小、公共 API
在 7.x 中弃用了以下 Comparator 实现:
-
org.optaplanner.core.api.score.comparator.NaturalScoreComparator -
org.optaplanner.core.api.score.constraint.ConstraintMatchScoreComparator -
org.optaplanner.core.api.score.constraint.ConstraintMatchTotalScoreComparator -
org.optaplanner.core.api.score.constraint.IndictmentScoreComparator
OptaPlanner 7 中的 *.java 文件示例:
NaturalScoreComparator comparator = new NaturalScoreComparator(); ConstraintMatchScoreComparator comparator2 = new ConstraintMatchScoreComparator();
NaturalScoreComparator comparator = new NaturalScoreComparator();
ConstraintMatchScoreComparator comparator2 = new ConstraintMatchScoreComparator();
OptaPlanner 8 中的 *.java 文件示例:
Comparator<Score> comparator = Comparable::compareTo; Comparator<ConstraintMatch> comparator2 = Comparator.comparing(ConstraintMatch::getScore);
Comparator<Score> comparator = Comparable::compareTo;
Comparator<ConstraintMatch> comparator2 = Comparator.comparing(ConstraintMatch::getScore);
移除了 FeasibilityScore
小、公共 API
在 7.x 中弃用了 FeasibilityScore 接口,它 的唯一方法是Feasible() 移到 Score 超级类型。接口现已被删除。
您应该按最终类型引用 分数,例如 HardSoftScore 而不是 分数。
@PlanningEntity.movableEntitySelectionFilter removed
小、公共 API
@PlanningEntity 注解上的 movableEntitySelectionFilter 字段已在 7.x 中弃用,一个新的字段 pinningFilter 已被引入,显示与 @PlanningPin 注解相关的名称。此过滤器实施新的 PinningFilter 接口,如果实体被固定,则返回 true;如果有禁止。因此,与旧过滤器相比,这个新过滤器的逻辑已被验证。
您应该通过提供新过滤器而不是旧的过滤器来更新 @PlanningEntity 注解。旧过滤器现已被删除。
OptaPlanner 7 中的 *.java 文件示例:
@PlanningEntity(movableEntitySelectionFilter = MyMovableEntitySelectionFilter.class)
@PlanningEntity(movableEntitySelectionFilter = MyMovableEntitySelectionFilter.class)
OptaPlanner 8 中的 *.java 文件示例:
@PlanningEntity(pinningFilter = MyPinningFilter.class)
@PlanningEntity(pinningFilter = MyPinningFilter.class)
@PlanningVariable.reinitializeVariableEntityFilter removed
小、公共 API
@Planning 字段已在 7.x 中被弃用,现已被删除。
Variable 注解上的 reinitializeVariableFilter
* 被设置为接口(ScoreHolder 类)
小、公共 API
在 OptaPlanner 7 中,ScoreHolder 类专门用于 Drools 分数计算,公开一些公共方法(如果使用),允许用户意外破坏或对它们的分数造成负面影响。
在 OptaPlanner 8 中,这些方法已被删除,并将类转换为接口。大多数用户不使用移除且有害的方法。
但是,如果您使用这些方法,您可以在分数解释和约束配置方面在公共 API 中找到合适的替换。
ValueRangeFactory 类现在最后
次
ValueRangeFactory 类是仅具有静态方法的工厂类。您不需要扩展这个类,因此最终是 最终的。
OptaPlanner 7 中的 *.java 文件示例:
class MyValueRangeFactory extends ValueRangeFactory {
...
}
class MyValueRangeFactory extends ValueRangeFactory {
...
}
OptaPlanner 8 中的 *.java 文件示例:
class MyValueRangeFactory {
...
}
class MyValueRangeFactory {
...
}
现在,ConstraintMatchTotal 和 Indictment 是接口
小、公共 API
ConstraintMatchTotal 和 Indictment 类已转换为接口。因此,他们的实施过程从公共 API 中移出,并附带允许它们修改其状态的方法。这些方法绝不能用于公共 API,因此没有替换它们。
如果您选择实施 ConstraintMatchAwareIncrementalScoreCalculator,您可能仍需要实例本身:
ConstraintMatchTotal maximumCapacityMatchTotal = new ConstraintMatchTotal(...);
ConstraintMatchTotal maximumCapacityMatchTotal = new ConstraintMatchTotal(...);
OptaPlanner 8 中的 *.java 文件示例:
ConstraintMatchTotal maximumCapacityMatchTotal = new DefaultConstraintMatchTotal(...);
ConstraintMatchTotal maximumCapacityMatchTotal = new DefaultConstraintMatchTotal(...);
分数管理器 :添加了通用类型 Score
主要,Public API
ScoreManager 和 ScoreExplanation API 现在具有通用类型 Score,以避免在您的代码中进行停机,例如:从 Score 到 HardSoftScore。
OptaPlanner 7 中的 *.java 文件示例:
@Inject // or @Autowired
ScoreManager<TimeTable> scoreManager;
@Inject // or @Autowired
ScoreManager<TimeTable> scoreManager;
OptaPlanner 8 中的 *.java 文件示例:
@Inject // or @Autowired
ScoreManager<TimeTable, HardSoftScore> scoreManager;
@Inject // or @Autowired
ScoreManager<TimeTable, HardSoftScore> scoreManager;
OptaPlanner 7 中的 *.java 文件示例:
ScoreExplanation<TimeTable> explanation = scoreManager.explainScore(timeTable);
HardSoftScore score = (HardSoftScore) explanation.getScore();
ScoreExplanation<TimeTable> explanation = scoreManager.explainScore(timeTable);
HardSoftScore score = (HardSoftScore) explanation.getScore();
OptaPlanner 8 中的 *.java 文件示例:
ScoreExplanation<TimeTable, HardSoftScore> explanation = scoreManager.explainScore(timeTable);
HardSoftScore score = explanation.getScore();
ScoreExplanation<TimeTable, HardSoftScore> explanation = scoreManager.explainScore(timeTable);
HardSoftScore score = explanation.getScore();
ConstraintMatchTotal、ConstraintMatch 和 Indictment: 通用类型 Score 添加
主要
与 ScoreManager 和 ScoreExplanation 类似,ConstraintMatchTotal ConstraintMatch, 和 Indictment API 现在有一个通用类型 Score 来避免代码中的广播,例如:从 Score 到 HardSoftScore。
OptaPlanner 7 中的 *.java 文件示例:
ScoreExplanation<TimeTable> explanation = scoreManager.explainScore(timeTable);
Map<String, ConstraintMatchTotal> constraintMatchTotalMap = scoreExplanation.getConstraintMatchTotalMap();
ConstraintMatchTotal constraintMatchTotal = constraintMatchTotalMap.get(contraintId);
HardSoftScore totalScore = (HardSoftScore) constraintMatchTotal.getScore();
ScoreExplanation<TimeTable> explanation = scoreManager.explainScore(timeTable);
Map<String, ConstraintMatchTotal> constraintMatchTotalMap = scoreExplanation.getConstraintMatchTotalMap();
ConstraintMatchTotal constraintMatchTotal = constraintMatchTotalMap.get(contraintId);
HardSoftScore totalScore = (HardSoftScore) constraintMatchTotal.getScore();
OptaPlanner 8 中的 *.java 文件示例:
ScoreExplanation<TimeTable, HardSoftScore> explanation = scoreManager.explainScore(timeTable);
Map<String, ConstraintMatchTotal<HardSoftScore>> constraintMatchTotalMap = scoreExplanation.getConstraintMatchTotalMap();
ConstraintMatchTotal<HardSoftScore> constraintMatchTotal = constraintMatchTotalMap.get(contraintId);
HardSoftScore totalScore = constraintMatchTotal.getScore();
ScoreExplanation<TimeTable, HardSoftScore> explanation = scoreManager.explainScore(timeTable);
Map<String, ConstraintMatchTotal<HardSoftScore>> constraintMatchTotalMap = scoreExplanation.getConstraintMatchTotalMap();
ConstraintMatchTotal<HardSoftScore> constraintMatchTotal = constraintMatchTotalMap.get(contraintId);
HardSoftScore totalScore = constraintMatchTotal.getScore();
OptaPlanner 7 中的 *.java 文件示例:
ScoreExplanation<TimeTable> explanation = scoreManager.explainScore(timeTable);
Map<Object, Indictment> indictmentMap = scoreExplanation.getIndictmentMap();
Indictment indictment = indictmentMap.get(lesson);
HardSoftScore totalScore = (HardSoftScore) indictment.getScore();
ScoreExplanation<TimeTable> explanation = scoreManager.explainScore(timeTable);
Map<Object, Indictment> indictmentMap = scoreExplanation.getIndictmentMap();
Indictment indictment = indictmentMap.get(lesson);
HardSoftScore totalScore = (HardSoftScore) indictment.getScore();
OptaPlanner 8 中的 *.java 文件示例:
ScoreExplanation<TimeTable, HardSoftScore> explanation = scoreManager.explainScore(timeTable);
Map<Object, Indictment<HardSoftScore>> indictmentMap = scoreExplanation.getIndictmentMap();
Indictment<HardSoftScore> indictment = indictmentMap.get(lesson);
HardSoftScore totalScore = indictment.getScore();
ScoreExplanation<TimeTable, HardSoftScore> explanation = scoreManager.explainScore(timeTable);
Map<Object, Indictment<HardSoftScore>> indictmentMap = scoreExplanation.getIndictmentMap();
Indictment<HardSoftScore> indictment = indictmentMap.get(lesson);
HardSoftScore totalScore = indictment.getScore();
ConstraintMatchAwareIncrementalScoreCalculator: generic type Score added
次
接口 ConstraintMatchAwareIncrementalScoreCalculator 还有一个通用类型参数,用于 Score 以避免 ConstraintMatchTotal 和 Indictment 的原始类型使用。
OptaPlanner 7 中的 *.java 文件示例:
OptaPlanner 8 中的 *.java 文件示例:
AbstractCustomPhaseCommand 被删除
小、公共 API
抽象类 AbstractCustomPhaseCommand 被删除。任何扩展该类都应直接实施 CustomPhaseCommand 接口。
OptaPlanner 7 中的 *.java 文件示例:
OptaPlanner 8 中的 *.java 文件示例:
分数计算器移到公共 API
主要
接口 EasyScoreCalculator,IncrementalScoreCalculator 和 ConstraintMatchAwareIncrementalScoreCalculator 已移至公共 API 中的新软件包。它们已弃用的对应项已被删除。弃用的类 org.optaplanner.core.impl.score.director.incremental.AbstractIncrementalScoreCalculator 已被删除。使用删除的接口和类替换其在公共 API 中的对应接口。
OptaPlanner 7 中的 EasyScoreCalculator.java 文件中的示例:
OptaPlanner 8 中的 EasyScoreCalculator.java 文件中的示例:
OptaPlanner 7 中的 IncrementalScoreCalculator.java 文件中的示例:
OptaPlanner 8 中的 IncrementalScoreCalculator.java 文件中的示例:
OptaPlanner 7 中的 ConstraintMatchAwareIncrementalScoreCalculator.java 文件中的示例:
OptaPlanner 8 中的 ConstraintMatchAwareIncrementalScoreCalculator.java 文件中的示例:
PlannerBenchmarkFactory:createFromSolverFactory() 已删除
主要,Public API
PlannerBenchmarkFactory.createFromSolverFactory() 方法已弃用,并被 PlannerBenchmarkFactory.createFromSolverConfigXmlResource(String) 方法替代。PlannerBenchmarkFactory.createFromSolverFactory() 方法已被删除。
OptaPlanner 7 中的 *.java 文件示例:
SolverFactory<CloudBalance> solverFactory = SolverFactory.createFromXmlResource(
".../cloudBalancingSolverConfig.xml");
PlannerBenchmarkFactory benchmarkFactory = PlannerBenchmarkFactory.createFromSolverFactory(solverFactory);
SolverFactory<CloudBalance> solverFactory = SolverFactory.createFromXmlResource(
".../cloudBalancingSolverConfig.xml");
PlannerBenchmarkFactory benchmarkFactory = PlannerBenchmarkFactory.createFromSolverFactory(solverFactory);
OptaPlanner 8 中的 *.java 文件示例:
PlannerBenchmarkFactory benchmarkFactory = PlannerBenchmarkFactory.createFromSolverConfigXmlResource(
".../cloudBalancingSolverConfig.xml");
PlannerBenchmarkFactory benchmarkFactory = PlannerBenchmarkFactory.createFromSolverConfigXmlResource(
".../cloudBalancingSolverConfig.xml");
如果您以编程方式调整了解决器配置,您可以使用 PlannerBenchmarkConfig.createFromSolverConfig(SolverConfig),然后运行 PlannerBenchmarkFactory.create(PlannerBenchmarkConfig)。
PlannerBenchmarkFactory:getPlannerBenchmarkConfig() 已删除
小、公共 API
PlannerBenchmarkFactory.getPlannerBenchmarkConfig() 方法已弃用,并被 PlannerBenchmarkFactory.create(PlannerBenchmarkConfig) 方法替代。现在,PlannerBenchmarkConfig 实例在 PlannerBenchmark factory 实例被实例化前。这个顺序更为逻辑。PlannerBenchmarkFactory.getPlannerBenchmarkConfig() 已被删除。
OptaPlanner 7 中的 *.java 文件示例:
PlannerBenchmarkFactory benchmarkFactory = PlannerBenchmarkFactory.createFromXmlResource(
".../cloudBalancingBenchmarkConfig.xml");
PlannerBenchmarkConfig benchmarkConfig = benchmarkFactory.getPlannerBenchmarkConfig();
...
PlannerBenchmark benchmark = benchmarkFactory.buildPlannerBenchmark();
PlannerBenchmarkFactory benchmarkFactory = PlannerBenchmarkFactory.createFromXmlResource(
".../cloudBalancingBenchmarkConfig.xml");
PlannerBenchmarkConfig benchmarkConfig = benchmarkFactory.getPlannerBenchmarkConfig();
...
PlannerBenchmark benchmark = benchmarkFactory.buildPlannerBenchmark();
OptaPlanner 8 中的 *.java 文件示例:
PlannerBenchmarkConfig benchmarkConfig = PlannerBenchmarkConfig.createFromXmlResource(
".../cloudBalancingBenchmarkConfig.xml");
...
PlannerBenchmarkFactory benchmarkFactory = PlannerBenchmarkFactory.create(benchmarkConfig);
PlannerBenchmark benchmark = benchmarkFactory.buildPlannerBenchmark();
PlannerBenchmarkConfig benchmarkConfig = PlannerBenchmarkConfig.createFromXmlResource(
".../cloudBalancingBenchmarkConfig.xml");
...
PlannerBenchmarkFactory benchmarkFactory = PlannerBenchmarkFactory.create(benchmarkConfig);
PlannerBenchmark benchmark = benchmarkFactory.buildPlannerBenchmark();
XML <plannerBenchmark/> root 元素现在属于 http://www.optaplanner.org/xsd/benchmark 命名空间
小、公共 API
OptaPlanner 现在为基准配置提供一个 XML 架构定义(XSD)。虽然 OptaPlanner 与现有 XML 配置的早期版本保持兼容性,但强烈建议迁移到 XSD,因为 OptaPlanner 可能在以后仅支持有效的配置 XML。
OptaPlanner 7 中的 *BenchmarkConfig.xml 文件中的示例:
<?xml version="1.0" encoding="UTF-8"?> <plannerBenchmark> ... </plannerBenchmark>
<?xml version="1.0" encoding="UTF-8"?>
<plannerBenchmark>
...
</plannerBenchmark>
OptaPlanner 8 中的 *BenchmarkConfig.xml 文件中的示例:
<?xml version="1.0" encoding="UTF-8"?> <plannerBenchmark xmlns="https://www.optaplanner.org/xsd/benchmark" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://www.optaplanner.org/xsd/benchmark https://www.optaplanner.org/xsd/benchmark/benchmark.xsd"> ... </plannerBenchmark>
<?xml version="1.0" encoding="UTF-8"?>
<plannerBenchmark xmlns="https://www.optaplanner.org/xsd/benchmark" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://www.optaplanner.org/xsd/benchmark https://www.optaplanner.org/xsd/benchmark/benchmark.xsd">
...
</plannerBenchmark>
使用 XSD 可能需要重新排序配置的一些 XML 元素。在 IDE 中使用代码完成迁移到有效的 XML。
ProblemBenchmarksConfig:xStreamAnnotatedClass 删除
主要,Public API
{mAnnotatedClas} 已从 < problemBenchmarks/> 配置以及对应的 getXStreamAnnotatedClassList() 和 setXStreamAnnotatedClassList() 方法中删除。
OptaPlanner 7 中的 *.java 文件示例:
ProblemBenchmarksConfig problemBenchmarksConfig = new ProblemBenchmarksConfig(); problemBenchmarksConfig.setXStreamAnnotatedClassList(MySolution.class);
ProblemBenchmarksConfig problemBenchmarksConfig = new ProblemBenchmarksConfig();
problemBenchmarksConfig.setXStreamAnnotatedClassList(MySolution.class);
OptaPlanner 8 中的 *.java 文件示例:
OptaPlanner 7 中的 *BenchmarkConfig.xml 文件中的示例:
OptaPlanner 8 中的 *BenchmarkConfig.xml 文件中的示例:
BenchmarkAggregatorFrame.createAndDisplay(PlannerBenchmark factory) removed
次
BenchmarkAggregatorFrame.createAndDisplay(PlannerBenchmarkFactory) 方法已弃用,并替换为 BenchmarkAggregatorFrame.createAndDisplayFromXmlResource(String) 方法。BenchmarkAggregatorFrame.createAndDisplay(PlannerBenchmark factory) 方法已被删除。
OptaPlanner 7 中的 *.java 文件示例:
PlannerBenchmarkFactory benchmarkFactory = PlannerBenchmarkFactory.createFromXmlResource(
".../cloudBalancingBenchmarkConfig.xml");
BenchmarkAggregatorFrame.createAndDisplay(benchmarkFactory);
PlannerBenchmarkFactory benchmarkFactory = PlannerBenchmarkFactory.createFromXmlResource(
".../cloudBalancingBenchmarkConfig.xml");
BenchmarkAggregatorFrame.createAndDisplay(benchmarkFactory);
OptaPlanner 8 中的 *.java 文件示例:
BenchmarkAggregatorFrame.createAndDisplayFromXmlResource(
".../cloudBalancingBenchmarkConfig.xml");
BenchmarkAggregatorFrame.createAndDisplayFromXmlResource(
".../cloudBalancingBenchmarkConfig.xml");
如果您以编程方式调整基准配置,可以使用 BenchmarkAggregatorFrame.createAndDisplay(PlannerBenchmarkConfig)。
删除了配置中的 JavaScript 表达式支持
次
解决程序配置和基准配置的不同元素不再支持嵌套的 JavaScript 表达式。您必须使用自动配置或整数常量替换它们。
OptaPlanner 7 中的 solverConfig.xml 文件中的示例:
<solver>
...
<moveThreadCount>availableProcessorCount - 1</moveThreadCount>
...
</solver>
<solver>
...
<moveThreadCount>availableProcessorCount - 1</moveThreadCount>
...
</solver>
OptaPlanner 8 中的 'solverConfig.xml'file 示例:
<solver>
...
<moveThreadCount>1</moveThreadCount> <!-- Alternatively, use "AUTO" or omit entirely. -->
...
</solver>
<solver>
...
<moveThreadCount>1</moveThreadCount> <!-- Alternatively, use "AUTO" or omit entirely. -->
...
</solver>
OptaPlanner 7 中的 benchmarkConfig.xml 文件示例:
<plannerBenchmark>
...
<parallelBenchmarkCount>availableProcessorCount - 1</parallelBenchmarkCount>
...
</plannerBenchmark>
<plannerBenchmark>
...
<parallelBenchmarkCount>availableProcessorCount - 1</parallelBenchmarkCount>
...
</plannerBenchmark>
OptaPlanner 8 中的 benchmarkConfig.xml 文件示例:
<plannerBenchmark>
...
<parallelBenchmarkCount>1</parallelBenchmarkCount> <!-- Alternatively, use "AUTO" or omit entirely. -->
...
</plannerBenchmark>
<plannerBenchmark>
...
<parallelBenchmarkCount>1</parallelBenchmarkCount> <!-- Alternatively, use "AUTO" or omit entirely. -->
...
</plannerBenchmark>
删除已弃用的变量监听程序
主要,Public API
来自软件包 org.optaplanner.core.impl.domain.variable.listener 已弃用的接口 VariableListener 已被删除,以及已弃用的 interface StatefulVariableListener 以及同一软件包中的已弃用的类 VariableListenerAdapter。改为使用 org.optaplanner.core.api.domain.variable 软件包中的 VariableListener 接口。
OptaPlanner 7 中的 VariableListener.java 文件示例:
OptaPlanner 8 中的 VariableListener.java 文件示例:
OptaPlanner 7 中的 StatefulVariableListener.java 文件示例:
OptaPlanner 8 中的 StatefulVariableListener.java 文件示例:
第 2 章 OptaPlanner 8.2.0 和 OptaPlanner 8.3.0 间的更改 复制链接链接已复制到粘贴板!
本节中列出的更改在 OptaPlanner 8.2.0 和 OptaPlanner 8.3.0 之间进行。
ConstraintMatch.compareTo() 与 equals()不一致
次
ConstraintMatch 中的 equals() 覆盖已被删除。因此,两个不同的 ConstraintMatch 实例永远不会被视为相等。这与 compareTo() 方法相对,如果所有字段值都相等,它会继续考虑两个实例相等。
部分 II. 开始使用红帽构建的 OptaPlanner 复制链接链接已复制到粘贴板!
作为业务规则开发人员,您可以使用红帽构建的 OptaPlanner 来查找最佳解决方案,以根据一组有限的资源以及在特定限制约束下规划问题。
使用本文档开始使用 OptaPlanner 解决问题。
第 3 章 红帽构建的 OptaPlanner 简介 复制链接链接已复制到粘贴板!
OptaPlanner 是一个轻量级、可嵌入的规划引擎,可优化计划问题。它帮助普通 Java 编程人员有效地解决规划问题,它可将优化性与风险计算相结合。
例如,OptaPlanner 帮助解决各种用例:
- staff/Patient Ros ters :它还有助于为 nurses 创建定时表并跟踪病人管理。
- 教育 时间表:它有助于安排课程、课程、考试和会议演示。
- shop Schedules :它跟踪车装行、计算机队列计划和 workforce 任务规划。
- 危机 :通过减少资源消耗(如文章和发证)来最小化浪费。
每个组织都面临规划问题;也就是说,它们提供有限的资源(员工、资产、时间和金钱)。
OptaPlanner 是 Apache 软件许可证 2.0 下的开源软件。它是 100% 的纯 Java,在大多数 Java 虚拟机(JVM)上运行。
3.1. 计划问题 复制链接链接已复制到粘贴板!
根据有限的资源及特定限制,计划问题 是一种最佳目标。最佳目标可以是任何数量,例如:
- 最大化利润 - 实现最大利润最佳目标。
- 最小化原则占用 - 最佳目标具有最低程度的环境影响。
- 最大化员工或客户的满意度 - 最佳目标将优先考虑员工或客户的需求。
实现这些目标的能力取决于可用的资源数量。例如,以下资源可能会受限制:
- 人员数量
- 时间量
- budget
- 物理资产,如机器、车、车、计算机、构建
您还必须考虑与这些资源相关的特定限制,如个人工作小时数、使用某些机器或其它设备间的兼容性。
红帽构建的 OptaPlanner 帮助 Java 编程人员有效地解决约束满意度问题。它将优化 heuristics 和 metaheuristics 合并起来,且有有效的分数计算。
3.2. 规划问题中的 NP 完整性 复制链接链接已复制到粘贴板!
提供的用例 可能是 NP-complete 或 NP-hard,这意味着应用以下语句:
- 在合理时间,轻松地验证特定解决方案以解决问题。
- 在合理时间无法找到问题的最佳解决方案。
含义在于解决您的问题可能比您预期的问题更难,因为两个常见的技术并不知道:
- brute 强制算法(即使更高级的变体)过长。
- 例如,一个快速算法,例如在 组合问题 中,首先放入最大项目,这是目前从最佳效果最远的解决方案。
通过使用高级优化算法,OptaPlanner 在解决此类计划问题的适当解决方案。
3.3. 用于规划问题的解决方案 复制链接链接已复制到粘贴板!
计划问题有很多解决方案。
多种解决方案类别如下:
- 可能的解决方案
- 可能的解决方法是任何解决方案,无论它是否会破坏任何数量的限制。规划问题通常存在大量可能的解决方案。其中很多解决方案都非常有用。
- 可行的解决方案
- 可行的解决方案是不会破坏任何(负)硬约束的解决方案。可行的解决方案数量相对于可能的解决方案。有时没有可行的解决方案。每个可行的解决方案都是一个可能的解决方案。
- 最佳解决方案
- 最佳解决方案是具有最高分数的解决方案。规划问题通常具有较少的最佳解决方案。即使没有可行的解决方案,它们始终只有一个最佳解决方案,且最佳解决方案并不可行。
- 找到最佳解决方案
- 最好的解决方法是解决方案,在指定时间内按实施获得的最高分值。找到的最佳解决方案可能可行,并有足够的时间,这是最佳的解决方案。
Counterintuively,可能的解决方案数量非常大(如果被正确计算),即使是很小的数据集。
在 planner-engine 分发文件夹中提供的示例中,大多数实例都有大量可能的解决方案。由于无法获得最佳解决方案,因此任何实施都强制评估所有可能的解决方案的子集。
OptaPlanner 支持一些优化算法,以便高效地通过这些算法来有效地处理大量可能的解决方案。
根据用例,某些优化算法会比其他性能更好,但无法预先了解。使用 OptaPlanner,您可以在 XML 或代码的几行中更改解决器配置来切换优化算法。
3.4. 有关规划问题的约束 复制链接链接已复制到粘贴板!
通常,计划问题至少有两个级别限制:
(负)硬约束 不能出现问题。
例如,一个老师无法同时教授两个不同的课时。
如果可以避免,则应该损坏 (负)软约束。
例如,Teacher A 不希望在星期五下午进行教学。
有些问题也有正的约束:
如果可能,应达到 正的软约束(或好处)。
例如,Teacher B 喜欢在上星期一早上教学。
一些基本问题只具有硬限制。有些问题有三个或更多限制级别,如 hard、medium 和 soft 约束。
这些限制定义了计划问题 的分数计算 (也称为适合性 功能)。规划问题的每个解决方案都会以分数为准。使用 OptaPlanner 时,分数限制以面向对象的语言编写,如 Java,或者在 Drools 规则中使用。
这种类型的代码非常灵活,可扩展。
3.5. 红帽构建的 OptaPlanner 示例 复制链接链接已复制到粘贴板!
Red Hat Process Automation Manager 提供了几个 OptaPlanner 示例。您可以检查示例代码,并根据需要对其进行修改,以满足您的需要。
红帽不提供对 Red Hat Process Automation Manager 发行版本中包含的示例代码的支持。
某些 OptaPlanner 示例可以解决问题,这些问题在学术 contests中呈现。下表中的 Contest 列列出了 contests。它还标识了示例,作为 一项测试的目的或不切实际的。真实的竞争 测试是满足以下标准的官方独立测试:
- 明确定义实际用例
- 实际限制
- 多个真实数据集
- 在特定硬件的特定时间限制内可重复生成的结果
- 来自学术和/或企业运营研究社区的严重参与。
真实的 Contests 提供了 OptaPlanner 与竞争软件和学术研究的目标比较。
| 示例 | 域 | 大小 | Contest | 目录名称 |
|---|---|---|---|---|
| 1 个实体类 (1 变量) |
值
搜索空间 | 无得分(cheatable) |
| |
| 1 个实体类 (1 变量) |
entity abrt
值 IFL
搜索空间 Warning | 否(由我们定义) |
| |
| 1 个实体类 (1链的变量) |
entity dropped
值 InventoryService
搜索空间 768 | 不切实际的 TSP web |
| |
| 1 个实体类 (1 变量) |
entity abrt
值 InventoryService
搜索空格 Warning | 否(由我们定义) |
| |
| 1 个实体类 (2 变量) |
entity abrt
值 abrt
搜索空间 768 | 否(由我们定义) |
| |
| 1 个实体类 (2 变量) |
entity Equal
值 abrt
搜索空格 Warning |
| ||
| 1 个实体类 (1 变量) |
entity Equal
值
搜索空间 |
| ||
| 1 个实体类 (1链的变量) 1 个影子实体类 (1 个自动影子变量) |
entity abrt
值 InventoryService
搜索空间 768 | 无切实际的 VRP 网络 |
| |
| 使用时间窗 的载设备路由 | 所有 Vehicle 路由 (1 个影子变量) |
entity abrt
值 InventoryService
搜索空间 768 | 无切实际的 VRP 网络 |
|
| 1 个实体类 (2 变量) (1 个影子变量) |
entity Equal
值 fsanitize
搜索空格 |
| ||
| 1 个实体类 (1链的变量) (1 个影子变量) 1 个影子实体类 (1 个自动影子变量) |
entity Equal
值 InventoryService
搜索空间 768 | 没有被我们定义 |
| |
| 2 个实体类(层次结构) (2 变量) |
entity abrt
值 InventoryService
搜索空格 >_< | 真实的 ITC 2007 年跟踪 1 |
| |
| 1 个实体类 (1 变量) |
entity abrt
值 IFL
搜索空间 768 | 现实 INRC 2010 |
| |
| 1 个实体类 (1 变量) |
entity abrt
值 sHistoryLimit
搜索空间 Warning | 不切实际的 TTP |
| |
| 1 个实体类 (2 变量) |
entity Equal
值 InventoryService
搜索空间 768 | 几乎真实的 ICON Energy |
| |
| 1 个实体类 (1 变量) |
entity abrt
值 =
搜索空间 768 | 没有被我们定义 |
| |
| 1 个实体类 (2 变量) |
IFL
值 abrt
搜索空间 768 | 没有被我们定义 |
| |
| 1 个实体类 (1链的变量) (4 个影子变量) 1 个影子实体类 (1 个自动影子变量) |
entity dropped
值 abrt
搜索空格 768 | 没有被我们定义 |
| |
| 1 个实体类 (1 变量) 1 个影子实体类 (1 个自动影子变量) |
entity dropped
值 abrt
搜索空间 2.4 | 没有被我们定义 |
|
3.6. N queens 复制链接链接已复制到粘贴板!
在 n 个大小小板上放置 n queens,以便无法互相攻击。最常见的 n queens puzzle 是 8 个queuzzle,有 n = 8 :
约束:
- 使用主板的 n 列和 n 行。
- 在主板上放置 n queens。
- 无法相互攻击两个频率。queen 可攻击同一横向、垂直或部门其他任何频率的其他频率。
本文档主要使用四个不同点。
建议的解决方案可能是:
图 3.1. 四点欺诈解决方案
以上解决方案是错误的,因为 queens A1 和 B0 可以相互攻击(因此可以说 B0 和 D0)。删除 queen B0 会尊重"不两种 queens"约束,但会破坏 "place n queens" 约束。
以下是一个正确的解决方案:
图 3.2. Four quele queuzzle 的正确解决方案
所有约束都已满足,因此解决方案正确。
请注意,大多数 n 个词汇都有多个正确的解决方案。我们将重点介绍查找特定 n 的单一正确的解决方案,而不是为特定 n 查找可能的正确解决方案。
问题大小
N queens 示例的实现还没有优化,因为它作为新手示例的功能。然而,它可以轻松地处理 64 queens。出现了一些变化,它已被显示,可轻松处理 5000 queens 等等。
3.6.1. N queens 的域模型 复制链接链接已复制到粘贴板!
这个示例使用域模型解决四条问题。
创建域模型
好的域模型可以方便理解和解决您的规划问题。
这是 n queens 示例的域模型:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow Copy to Clipboard Copied! Toggle word wrap Toggle overflow Copy to Clipboard Copied! Toggle word wrap Toggle overflow 计算搜索空间.
Queen实例有一个Column(例如:0 为列 A,1 为列 B, …)和一个Row(its 行,例如 0 代表行 0,1 是行 1, …)。可以根据列和行计算升序行和降序行。
列和行索引从主板的左上角开始。
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 查找解决方案
单个
NQueens实例包含所有Queen实例的列表。它是Solution实施,它将提供给、被解决并从 Solver 中检索。
请注意,在四个 queens 示例中,NQueens getN() 方法总是返回 4。
图 3.3. Four Queens 的解决方案
| columnIndex | rowIndex | ascendingDiagonalIndex (columnIndex + rowIndex) | descendingDiagonalIndex (columnIndex - rowIndex) | |
|---|---|---|---|---|
| A1 | 0 | 1 | 1 (**) | -1 |
| B0 | 1 | 0 (*) | 1 (**) | 1 |
| C2 | 2 | 2 | 4 | 0 |
| D0 | 3 | 0 (*) | 3 | 3 |
当两个 queens 共享同一列时,行或横线,如(*)和(**),它们可以相互攻击。
3.7. 云平衡 复制链接链接已复制到粘贴板!
有关本示例的详情,请参阅 Red Hat build of OptaPlanner quick start Guide。
3.8. traveling salesman(TSP - traveling Salesman 问题) 复制链接链接已复制到粘贴板!
给定城市列表,找到一个只访问每个城市的 salesman 最短导览。
该问题由 Wikipedia 定义。它是计算 计算中最广泛调查的问题之一。然而,在现实世界中,这通常只是规划问题的一部分,以及其他约束,如员工切换限制。
问题大小
dj38 has 38 cities with a search space of 10^43. europe40 has 40 cities with a search space of 10^46. st70 has 70 cities with a search space of 10^98. pcb442 has 442 cities with a search space of 10^976. lu980 has 980 cities with a search space of 10^2504.
dj38 has 38 cities with a search space of 10^43.
europe40 has 40 cities with a search space of 10^46.
st70 has 70 cities with a search space of 10^98.
pcb442 has 442 cities with a search space of 10^976.
lu980 has 980 cities with a search space of 10^2504.
问题困难
尽管 TSP 的简单定义,但问题很难解决。因为这是一个 NP-hard 问题(如大多数规划问题),当问题数据集中稍有改变时,特定问题 dataset 的最佳解决方案可能会改变:
3.9. Tennis club 调度 复制链接链接已复制到粘贴板!
每周 10nis club 都有四个团队相互推断。将这四个点分配给团队。
硬约束:
- 冲突:团队每天只能每周一次。
- 不可用:一些团队在一些日期不可用。
Medium 约束:
- 公平分配:所有团队都应扮演(几乎)相同的时间。
软限制:
- 均匀的前端:每个团队应针对其他每个团队都有一个相同的次数。
问题大小
munich-7teams has 7 teams, 18 days, 12 unavailabilityPenalties and 72 teamAssignments with a search space of 10^60.
munich-7teams has 7 teams, 18 days, 12 unavailabilityPenalties and 72 teamAssignments with a search space of 10^60.
图 3.4. 域模型
3.10. 会议调度 复制链接链接已复制到粘贴板!
为启动时间和房间分配每个会议。会议有不同的持续时间。
硬约束:
- 间冲突:两个会议不能同时使用同一空间。
- 所需参与者:一个人在同时无法进行两个所需的会议。
- 必需的房间容量:会议不能适合于所有会议的参与者。
- 在同一天开始和结束:无法安排在多天的会议。
Medium 约束:
- 首选参与人:一个人不能同时拥有两个首选会议,而且是首选,而且是需要同时进行的会议。
软限制:
- 更快而不是之后:尽快计划所有会议。
- 会议间的休息:任何两个会议至少应在这两间中断。
- 重叠会议:为了尽量减少并行会议的数量,用户不必选择另外一种会议。
- 首先分配较大的空间:如果有一个大的房间可用,应该分配给该会议,以便适应尽可能多的人,即使他们尚未签到该会议。
- 房间稳定性:如果一个人连续有两个或小时间会议,它们之间的时间较差在同一房里。
问题大小
50meetings-160timegrains-5rooms has 50 meetings, 160 timeGrains and 5 rooms with a search space of 10^145. 100meetings-320timegrains-5rooms has 100 meetings, 320 timeGrains and 5 rooms with a search space of 10^320. 200meetings-640timegrains-5rooms has 200 meetings, 640 timeGrains and 5 rooms with a search space of 10^701. 400meetings-1280timegrains-5rooms has 400 meetings, 1280 timeGrains and 5 rooms with a search space of 10^1522. 800meetings-2560timegrains-5rooms has 800 meetings, 2560 timeGrains and 5 rooms with a search space of 10^3285.
50meetings-160timegrains-5rooms has 50 meetings, 160 timeGrains and 5 rooms with a search space of 10^145.
100meetings-320timegrains-5rooms has 100 meetings, 320 timeGrains and 5 rooms with a search space of 10^320.
200meetings-640timegrains-5rooms has 200 meetings, 640 timeGrains and 5 rooms with a search space of 10^701.
400meetings-1280timegrains-5rooms has 400 meetings, 1280 timeGrains and 5 rooms with a search space of 10^1522.
800meetings-2560timegrains-5rooms has 800 meetings, 2560 timeGrains and 5 rooms with a search space of 10^3285.
3.11. 课程时间表(ITC 2007 年跟踪 3 - 日程表课程安排) 复制链接链接已复制到粘贴板!
安排每个授课内容进入一个时间,并入一个房间。
硬约束:
- 老师冲突:老师不能在同一期限内没有两个讲义。
- 课程冲突:课程不能在同一时间内拥有两个讲义。
- 房间:两个讲话不能在同一时间段内。
- 不可用周期(为每个数据集指定):必须将特定的课程分配给特定的时间段。
软限制:
- 房间容量:房间容量不应少于学员在演讲中的数量。
- 最小工作日:在同一课程的讲义应分发至最少的天数。
- 课程紧凑:属于相同课程的演讲应相互相邻(在连续的期间内)。
- 房间稳定性:指示相同课程的演讲应该分配到相同的房间。
这个问题由 国际时间选项卡 2007 年跟踪 3 定义。
问题大小
图 3.5. 域模型
3.12. 机器重新分配(Google ROADEF 2012) 复制链接链接已复制到粘贴板!
为机器分配每个进程。所有进程已经有原始(未优化)分配。每个进程需要每个资源(如 CPU 或 RAM)的数量。这是 Cloud Balancing 示例的一个更为复杂的版本。
硬约束:
- 最大容量:不能超过每台机器的每个资源的最大容量。
- 冲突:同一服务的进程必须在不同的机器上运行。
- 分布:必须将同一服务的进程分散到各个位置。
- 依赖项:取决于其他服务的进程必须在其他服务的邻居中运行。
- 临时使用:有些资源是临时的,可以计入原始机器作为新分配机器的最大容量。
软限制:
- load:不应超过每台机器的每个资源的安全容量。
- balances:通过平衡每台机器上的可用资源,为以后分配空间。
- 流程移动成本:流程具有移动成本。
- 服务迁移成本:服务具有移动成本。
- 机器迁移成本:将进程从机器 A 移到机器 B 还有另一个特定于 A-B 的移动成本。
这个问题由 Google ROADEF/EURO Challenge 定义,2012 年。
图 3.6. 价值主张
问题大小
图 3.7. 域模型
3.13. 载体路由 复制链接链接已复制到粘贴板!
使用车队车队,选择每个客户的对象并将其带给它。每个车块都可以为多个客户提供服务,但其容量有限。
除了基本情况(CVRP)外,还有一个包含时间窗(CVRPTW)的变体。
硬约束:
- 载体容量:一种车块无法获取更多项目,然后它的容量。
时间窗(仅在 CVRPTW 中):
- 差时:从一个位置到另一个位置的差差时间。
- 客户服务持续时间:消费者必须保持在服务期间的长度。
- 客户准备时间:在客户准备时间之前,车辆可能已经进入,但必须等到提供时间后再提供服务。
- 客户经过时限:在客户因时间而进行前,车辆必须经过时间。
软限制:
- 总距离:让所有车上的总距离驱动(精简资源占用率)
VRP Web 定义了容量设施设施路由问题(CVRP)及其时间序列(CVRPTW)。
图 3.8. 价值主张
问题大小
CVRP 实例(没有时间窗):
CVRP 两个实例(带时间窗):
3.13.1. Vehicle 路由的域模型 复制链接链接已复制到粘贴板!
使用时间窗域模型的载体路由大量使用 shadow 变量功能。这使得它能够更自然地表达其限制,因为 arrivalTime 和 departureTime 等属性可以在域模型上直接可用。
巴林林特利斯特利斯特区
在现实世界中,车辆不能直接跟随位置到位置:他们必须使用路路和高路。从业务的角度来说,这很重要:
对于优化算法,这并不重要,只要可以查找两个点之间的距离(并最好预先计算)。未来成本甚至不需要成为距离。它还可以是差时间、增加成本或更加权的功能。有几种方法可以预先计算出您的成本,如 GraphHopper (嵌入式、离线 Java 引擎)、Open MapQuest (Web 服务)和 Google Maps 客户端 API (Web 服务)。
还有一些技术 可以呈现它,例如针对开发人员的 Leaflet 和 Google Maps。
甚至可以使用 GraphHopper 或 Google Map Directions 呈现实际规划路由,但是由于路由在高路上的重叠,它可能会变得难以看到反引号:
请特别小心,在两个点之间采用与 OptaPlanner 中使用的相同优化标准。例如: GraphHopper 默认返回最快的路由,而不是最短的路由。不要使用 km(或 miles)最快的 GPS 路由来优化 OptaPlanner 中最短的差差:
与热门相对应,大多数用户都不希望最短的路由:他们想要使用最快的路由。它们比一般的路高。它们优先于有变化的障碍。在现实世界中,最快且最短的路由很少是相同的。
3.14. 项目作业调度 复制链接链接已复制到粘贴板!
以时间和执行模式调度所有作业,以最小化项目延迟。每个作业都是项目的一部分。作业可以通过不同的方式执行:每个方法都是一种执行模式,它代表了不同的持续时间,但也可以使用不同的资源。这是一种灵活的 作业商店调度 的一种形式。
硬约束:
- 作业优先级:作业只能在其所有前身作业完成后启动。
资源容量:不要使用超过可用的资源。
- 资源是本地(在同一项目中的作业之间共享)或全局(在所有作业间共享)
- 资源是可续订(每天可用容量)或不可续订(所有天可用的容量)
Medium 约束:
- 项目延迟总量:尽量减少每个项目的持续时间(makespan)。
软限制:
- Total makespan:最小化整个多项目调度的持续时间。
该问题由 MISTA 2013 挑战 定义。
问题大小
3.15. 任务分配 复制链接链接已复制到粘贴板!
将每个任务分配到员工队列中的 spot。每个任务都有一个持续时间,它受到员工的关联性级别的影响。
硬约束:
- 技能:每个任务都需要一个或多个技能。员工必须拥有所有这些技能。
软级别 0 约束:
- Critical 任务:首先完成关键的任务,比主要任务和次要任务快。
软级别 1 的限制:
最小化 makespan:缩短完成所有任务的时间。
- 首先,首先从工作最多的工作员工开始,即员工数量最长,从而创建公平和负载平衡。
软级别 2 约束:
- 主要任务:在可能的情况下尽快完成主要任务,比小任务更快。
软级别 3 的限制:
- 次要任务:尽快完成次要任务。
图 3.9. 价值主张
问题大小
24tasks-8employees has 24 tasks, 6 skills, 8 employees, 4 task types and 4 customers with a search space of 10^30. 50tasks-5employees has 50 tasks, 5 skills, 5 employees, 10 task types and 10 customers with a search space of 10^69. 100tasks-5employees has 100 tasks, 5 skills, 5 employees, 20 task types and 15 customers with a search space of 10^164. 500tasks-20employees has 500 tasks, 6 skills, 20 employees, 100 task types and 60 customers with a search space of 10^1168.
24tasks-8employees has 24 tasks, 6 skills, 8 employees, 4 task types and 4 customers with a search space of 10^30.
50tasks-5employees has 50 tasks, 5 skills, 5 employees, 10 task types and 10 customers with a search space of 10^69.
100tasks-5employees has 100 tasks, 5 skills, 5 employees, 20 task types and 15 customers with a search space of 10^164.
500tasks-20employees has 500 tasks, 6 skills, 20 employees, 100 task types and 60 customers with a search space of 10^1168.
图 3.10. 域模型
3.16. 考试时间表(ITC 2007 年跟踪 1 - 考试) 复制链接链接已复制到粘贴板!
将各考试安排为时段和上门。多个考试可以在同一期间内共享相同的空间。
硬约束:
- 考试冲突:必须在同一时间段内共享学员的两个考试。
- 房间容量:房座容量必须随时可用。
- 期间持续时间:其所有考试的持续时间必须有效。
相关的硬限制(为每个数据集指定):
- coindence:两个指定的考试必须使用相同的期限(但可能还会使用其它课程)。
- 排除:两个指定的考试不得使用相同的期限。
- 后:在另一个指定的考试期后,必须在一段时间内进行指定的考试。
相关硬约束(为每个数据集指定):
- 排行:一个指定的考试不必与任何其他考试共享其空间。
软限制(其各自具有重要优势):
- 同一人不能连续有两个考试。
- 同一人不能同时拥有两个考试。
- 周期分布:共享学员的两个考试应该是间隔很多句点。
- 混合持续时间:共享房间应该没有不同持续时间的两种考试。
- 前端加载:在计划前面应预先调度大型考试。
- 周期 penalty(每个数据集指定):有些句点(使用时指定句点)。
- room penalty(每个数据集指定):有些房间在使用时有感激。
它使用大量实时测试数据集。
这个问题由 国际时间选项卡 2007 年跟踪 1 定义。Geoffrey De Smet 在竞争中,使用非常早的 OptaPlanner 版本。然后,从那时起已进行了很多改进。
问题大小
3.16.1. 用于考试时间设置的域模型 复制链接链接已复制到粘贴板!
下图显示了考试的主要课程:
图 3.11. 检查域类图
请注意,我们已将考试概念分成 考试 课程和主题 课程。在解决过程中,考试 实例会改变(这是计划实体类),当它们的期间或房间属性改变时。主题、Period 和 Room 实例在解决过程中不会发生改变(它们有问题的事实,就像其他一些课程一样)。
3.17. Nurse roster(2010INRC 2010) 复制链接链接已复制到粘贴板!
对于每个转变,请安排相关变化。
硬约束:
- 无未分配的转变 (内置):所有变化都需要分配给员工。
- 发生冲突 :员工每天只能进行一次转换。
软限制:
合同义务。业务经常违反这些情况,因此他们决定将这些内容定义为软限制,而不是硬约束。
- 最小和最大分配 :每个员工需要工作超过 x 个变化,超过 y 个变化(取决于其合约)。
- 最少和连续工作天数 :每个员工需要在一行中的 x 到 y 天(取决于其合约)之间工作。
- 最少和最多的空闲天 :每个员工都需要在一行中的 x 到 y 天之间释放(取决于其合约)。
- 最小和最大连续工作每周 :每个员工在一行中的 x 和 y 周期限之间工作(取决于其合约)。
- 完整的周末 :每个员工都需要在一个周末或根本上每天工作。
- 周末时相同的转换类型 :对于同一员工同一周末的每周发生变化都必须是相同的转变类型。
- 不需要的模式 :一行中不需要的转变类型组合,例如,较早的转变,后再进行较晚的转变。
雇员:
- 申请 当日:员工希望处理某个特定天。
- 天下请求 :员工不希望在特定一天上工作。
- 申请 转变:员工希望分配到特定的转变。
- 转移请求 :员工不想分配到特定的转变。
- 备选技能 :分配给技能的员工应掌握这种转变所需的各项技能。
这个问题由 2010 年国际 Nurse Rosterionion 定义。
图 3.12. 价值主张
问题大小
有三种 dataset 类型:
- Sprint:必须以秒为单位解决。
- Medium: 必须以分钟为单位解决。
- long:必须在几小时内解决。
图 3.13. 域模型
3.18. traveling tournament 问题(TTP) 复制链接链接已复制到粘贴板!
计划在 n 个团队数之间的匹配。
硬约束:
- 每个团队会针对其他每个团队进行两次操作:一次,一次。
- 每个团队在每个时间上都有完全匹配。
- 团队不能连续三个以上的主页或连续三部的匹配。
- 无重复者:与团队相比,不连续匹配两个。
软限制:
- 最大程度降低所有团队的距离。
在 Michael Trick 网站(其中包含世界记录) 上也定义了问题。
问题大小
3.19. 更低的时间调度 复制链接链接已复制到粘贴板!
以时间和方式调度所有任务,以最大程度降低电源成本。电源价格因时间而异。这是 作业权利调度的一种形式。
硬约束:
- 开始时间限制:每个任务必须在最早的开始和最新开始限制之间启动。
- 最大容量:不能超过每台机器的每个资源的最大容量。
- 启动和关闭:在分配任务的期间,每台机器都必须处于激活状态。在任务之间,可以闲置它以避免启动和关闭成本。
Medium 约束:
电源成本:降低整个计划的总功耗。
- 机器电源成本:每个活跃或闲置机器都会消耗电源成本,这导致了电源成本(取决于该期间的电源价格)。
- 任务电源成本:每个任务都消耗了电源成本,这导致了电源成本(取决于一段时间内的电源价格)。
- 机器启动和关闭成本:计算机启动或关闭的所有时间都会产生额外的成本。
软限制(在原始问题定义中添加):
- 早开始:请参阅立即启动任务,而不是稍后启动。
这个问题由 ICON 质询 定义。
问题大小
3.20. 投资资产类分配(Portfolio 优化) 复制链接链接已复制到粘贴板!
决定在每个资产类投资的相对数量。
硬约束:
风险最大:标准开发总量不得高于标准开发最大值。
- 标准开发总体计算通过应用 Markowitz 组合 Theory 将资产类关联起来考虑起来。
- 地区上限:每个地区具有最大数量。
- 最大扇区数:每个扇区具有最大数量。
软限制:
- 最大化预期的回报。
问题大小
de_smet_1 has 1 regions, 3 sectors and 11 asset classes with a search space of 10^4. irrinki_1 has 2 regions, 3 sectors and 6 asset classes with a search space of 10^3.
de_smet_1 has 1 regions, 3 sectors and 11 asset classes with a search space of 10^4.
irrinki_1 has 2 regions, 3 sectors and 6 asset classes with a search space of 10^3.
较大的数据集尚未创建或测试,但不应构成问题。好的数据来源是 这一资产关联网站。
3.21. 会议调度 复制链接链接已复制到粘贴板!
将每个会议分配给一个小时和房间。Timeslots 可能会重叠。在 *.xlsx 文件中读取和写入,该文件可使用 libreoffice 或 Excel 编辑。
硬约束:
- talk type of timeslot:讨论的类型必须与 timeslot 的对话类型匹配。
- 房间不可用:讨论的房间必须在对话期间可用。
- 间冲突:两个对话在重叠时不能使用相同的空间。
- 发言人不可用时间:所有对话的发言人必须在对话期间可用。
- 发言人冲突:两个对话在重叠时无法共享发言人。
通用用途和房间标签:
- 发言人所需时间标签:如果发言人具有所需的 timelot 标签,则所有其或她的讨论都必须使用该标签分配给一个小时。
- 发言人禁止标记:如果发言人具有禁止的时间板板,那么他或她的所有话都不能被分配给具有该标签的时期。
- talk required timelot tag:如果一个 talk 具有所需的 timelot 标签,则必须将其分配给带有该标签的 timeslot。
- 禁止时间标签:如果对话具有禁止的计时标签,则无法使用该标签将其分配给时间。
- speaker required room tag:如果发言人具有必需的 room 标签,则必须将其所有或她的讨论分配到具有该标签的房间。
- 演讲者禁止房间标签:如果发言人有禁止的房间标签,那么他或她的所有客户都不能分配给具有该标签的房间。
- talk required room tag: 如果 talk 具有必需的 room 标签,则必须将其分配给具有该标签的房间。
- 谈话空间标签:如果对话有一个禁止的房间标签,则无法将其分配给具有该标签的房间。
- 互斥标签:共享这样的标签不能调度在重叠的时间里。
- 对话:所有预备讨论之后必须调度 talk talk。
软限制:
- me track 冲突:减少在重叠时间内共享主题标签的讨论数量。
- 扇区冲突:减少在重叠期内共享相同的扇区标签的讨论数量。
- 内容受众级别流违反:对于每内容标签,在高级讨论前,安排简介。
- 受众级多样性:对于每个时间,请尽量减少与不同受众级别的讨论数量。
- 语言多样性:对于每一时间来说,最大程度地提高与不同语言的沟通数量。
通用用途和房间标签:
- 发言人首选的时间标签:如果发言人具有首选 timelot 标签,则本标签中的所有讨论都应分配给一个小时。
- 发言人不需要的时间lot 标签: 如果发言人具有不所需时间标签,那么他或她的任何客户都不能被分配给具有该标签的时间段。
- talk preferred timeslot tag:如果一个 talk 具有首选 timeslot 标签,则应使用该标签将其分配给一个 timeslot。
- talk undesired timeslot tag: 如果 talk 带有不良的 timelot 标签,则不应将其分配给具有该标签的运行时间。
- 演讲者首选的房间标签:如果发言人拥有首选房间标签,则本人或她的所有讨论都应分配给具有该标签的房间标签。
- speaker undesired room 标签: 如果发言人具有不需要的房间标签,则不应将任何其讨论分配给具有该标签的房间。
- talk preferred room tag:如果 talk 具有首选空间标签,则应将其分配给具有该标签的房间。
- talk undesired room tag: 如果 talk anirdesired room 标签,则不应将其分配给具有该标签的房间。
- 第二天讨论:所有共享主题标签或内容标签都应以最少的天数调度(在同一天内)。
图 3.14. 价值主张
问题大小
18talks-6timeslots-5rooms has 18 talks, 6 timeslots and 5 rooms with a search space of 10^26. 36talks-12timeslots-5rooms has 36 talks, 12 timeslots and 5 rooms with a search space of 10^64. 72talks-12timeslots-10rooms has 72 talks, 12 timeslots and 10 rooms with a search space of 10^149. 108talks-18timeslots-10rooms has 108 talks, 18 timeslots and 10 rooms with a search space of 10^243. 216talks-18timeslots-20rooms has 216 talks, 18 timeslots and 20 rooms with a search space of 10^552.
18talks-6timeslots-5rooms has 18 talks, 6 timeslots and 5 rooms with a search space of 10^26.
36talks-12timeslots-5rooms has 36 talks, 12 timeslots and 5 rooms with a search space of 10^64.
72talks-12timeslots-10rooms has 72 talks, 12 timeslots and 10 rooms with a search space of 10^149.
108talks-18timeslots-10rooms has 108 talks, 18 timeslots and 10 rooms with a search space of 10^243.
216talks-18timeslots-20rooms has 216 talks, 18 timeslots and 20 rooms with a search space of 10^552.
3.22. sttour 复制链接链接已复制到粘贴板!
从展到演示到展示的车车,但计划仅显示可用日。
硬约束:
- 安排每个必需显示。
- 计划尽可能多的显示。
Medium 约束:
- 最大化收入机会。
- 最小化驱动时间。
- 快于稍后访问。
软限制:
- 避免长时间推动时间。
问题大小
47shows has 47 shows with a search space of 10^59.
47shows has 47 shows with a search space of 10^59.
3.23. flight crew 调度 复制链接链接已复制到粘贴板!
为试点和航班出员指派航班。
硬约束:
- 所需技能:每个班级分配都具有必要的技能。例如,flight AB0001 需要 2 个试点和 3 班级参加。
- 动态冲突:每个员工只能同时参加一个动态
- 在两个航班之间传输:在两个机之间,员工必须能够从 arrival airport 转让到 departure airport。例如,An 到达 Brussels at 10:00,在位于 15:00 的阿姆斯特区。
- 员工不可用:该员工必须在航班的某一天可用。例如,An 是 1-Feb 上的 PTO。
软限制:
- 首次分配来自家
- 最后分配到达家
- 每个员工的负载均衡持续时间
问题大小
175flights-7days-Europe has 2 skills, 50 airports, 150 employees, 175 flights and 875 flight assignments with a search space of 10^1904. 700flights-28days-Europe has 2 skills, 50 airports, 150 employees, 700 flights and 3500 flight assignments with a search space of 10^7616. 875flights-7days-Europe has 2 skills, 50 airports, 750 employees, 875 flights and 4375 flight assignments with a search space of 10^12578. 175flights-7days-US has 2 skills, 48 airports, 150 employees, 175 flights and 875 flight assignments with a search space of 10^1904.
175flights-7days-Europe has 2 skills, 50 airports, 150 employees, 175 flights and 875 flight assignments with a search space of 10^1904.
700flights-28days-Europe has 2 skills, 50 airports, 150 employees, 700 flights and 3500 flight assignments with a search space of 10^7616.
875flights-7days-Europe has 2 skills, 50 airports, 750 employees, 875 flights and 4375 flight assignments with a search space of 10^12578.
175flights-7days-US has 2 skills, 48 airports, 150 employees, 175 flights and 875 flight assignments with a search space of 10^1904.
第 4 章 下载红帽构建的 OptaPlanner 示例 复制链接链接已复制到粘贴板!
您可以下载 Red Hat build of OptaPlanner 示例,作为红帽客户门户网站中提供的 {PRODUCTPAM} 附加组件软件包的一部分。
流程
进入红帽客户门户网站中的 Software Downloads 页面(需要登录),然后从下拉列表中选择产品和版本:
- 产品: 流程自动化管理器
- 版本: 7.13.4
- 下载 Red Hat Process Automation Manager 7.13 Add Ons。
-
提取
rhpam-7.13.4-add-ons.zip文件。提取的附加组件文件夹包含文件。rhpam-7.13.4-planner-engine.zip -
提取
rhpam-7.13.4-planner-engine.zip文件。
结果
提取的 rhpam-7.13.4-planner-engine 目录包含以下子目录下的示例源代码:
-
示例/源/src/main/java/org/optaplanner/examples -
示例/源/src/main/resources/org/optaplanner/examples
4.1. 运行 OptaPlanner 示例 复制链接链接已复制到粘贴板!
红帽构建的 OptaPlanner 包含多个示例,演示各种计划用例。下载并使用示例来探索不同类型的规划解决方案。
先决条件
- 您已下载并提取示例,如 第 4 章 下载红帽构建的 OptaPlanner 示例 所述。
流程
要运行示例,请在
rhpam-7.13.4-planner-engine/examples目录中输入以下命令之一:Linux 或 Mac:
./runExamples.sh
$ ./runExamples.shCopy to Clipboard Copied! Toggle word wrap Toggle overflow Windows:
runExamples.bat
$ runExamples.batCopy to Clipboard Copied! Toggle word wrap Toggle overflow OptaPlanner Examples 窗口将打开。
- 选择一个示例来运行该示例。
Red Hat build of OptaPlanner 没有 GUI 依赖项。它还可在服务器或移动 JVM 上运行,就像桌面一样。
如果您使用集成开发环境(IDE),如 IntelliJ、Eclipse 或 Netbeans,您可以在开发环境中运行下载的 OptaPlanner 示例。
先决条件
- 您已下载并提取了 OptaPlanner 示例,如 第 4 章 下载红帽构建的 OptaPlanner 示例 所述。
流程
打开 OptaPlanner 示例作为新项目:
-
对于 IntelliJ 或 Netbeans,开放
示例/源/pom.xml作为新项目。Maven 集成指南,指导您完成剩余的安装。跳过这个过程中的其余步骤。 -
对于 Eclipse,为
/examples/binaries目录打开一个新项目,它位于rhpam-7.13.4-planner-engine目录下。
-
对于 IntelliJ 或 Netbeans,开放
-
将所有位于
二进制文件目录中的所有 JAR 文件添加到 classpath 中,但examples/binaries/optaplanner-examples-7.67.0.Final-redhat-00024.jar文件除外。 -
添加 Java 源目录
src/main/java和 Java 资源目录src/main/resources,它位于rhpam-7.13.4-planner-engine/examples/sources/目录下。 创建运行配置:
-
main class:
org.optaplanner.examples.app.OptaPlannerExamplesApp -
VM 参数(可选):
-Xmx512M -server -Dorg.optaplanner.examples.dataDir=examples/sources/data -
工作目录:
example/sources
-
main class:
- 运行 run 配置。
第 5 章 Business Central 中的 OptaPlanner 入门:员工名单示例 复制链接链接已复制到粘贴板!
您可以在 Business Central 中构建和部署员工 模板示例项目。该项目演示了如何创建解决传统规划问题所需的每个业务中心资产,并使用红帽构建的 OptaPlanner 来查找可能的解决方案。
您可以在 Business Central 中部署预配置的 员工 -rostering 项目。或者,您可以使用 Business Central 自行创建项目。
Business Central 中的 employees -rostering 示例项目不包括数据集。您必须使用 REST API 调用以 XML 格式提供数据集。
5.1. 在 Business Central 中部署 employees rostering 示例项目 复制链接链接已复制到粘贴板!
Business Central 包括很多可用于熟悉产品和其功能的示例项目。员工的 rostering 示例项目经过设计和创建,以展示红帽构建 OptaPlanner 的变速用例。使用以下步骤部署和运行 Business Central 中的员工降级示例。
先决条件
- Red Hat Process Automation Manager 下载并安装。有关安装选项,请参阅 规划 Red Hat Process Automation Manager 安装。
-
您已启动了 Red Hat Process Automation Manager,如安装文档,且您使用具有
admin权限的用户身份登录 Business Central。
流程
- 在 Business Central 中,点击 Menu → Design → Projects。
-
在预配置的
MySpace空间中,点 Try Samples。 - 从示例项目列表中选择 employees -rostering,然后单击右上角的 Ok 来导入项目。
- 资产列表变得复杂后,请单击 Build & Deploy 来部署员工的 rostering 示例。
本文档的其余部分介绍了各个项目资产及其配置。
5.2. 重新排序员工降级示例项目 复制链接链接已复制到粘贴板!
员工的 rostering 示例项目是 Business Central 中提供的一个预配置的项目。您可以在 第 5.1 节 “在 Business Central 中部署 employees rostering 示例项目” 中了解如何部署此项目。
您可以创建员工的名册示例"全新"。您可以使用本例中的工作流在 Business Central 中创建您自己的类似项目。
5.2.1. 设置员工的 rostering 项目 复制链接链接已复制到粘贴板!
要在 Business Central 中开始开发解决方法,您必须设置该项目。
先决条件
- Red Hat Process Automation Manager 下载并安装。
-
您已部署了 Business Central,并使用具有管理员角色的用户登录。
流程
- 在 Business Central 中点 Menu → Design → Projects → Add Project 创建一个新项目。
在 Add Project 窗口中,填写以下字段:
-
Name
: staff-rostering - Description(可选):使用 OptaPlanner 的 Employee rostering 问题优化。可根据自身技能分配员工进行转变。
可选:点 Configure Advanced Options 填充
组 ID、Artifact ID和版本信息。-
组 ID
:员工 -
工件 ID
:员工 -
版本 1:1
NAPSHOT
-
Name
- 单击 Add,将项目添加到 Business Central 项目存储库。
5.2.2. 问题事实和规划实体 复制链接链接已复制到粘贴板!
员工时间表问题中的每个域类都归类为以下一种:
- 不相关的类:未由任何分数限制使用。从规划角度来说,这个数据已过时。
-
问题事实 类:在分数约束下使用,但在规划期间不会改变(只要问题仍保持相同),例如
Shift和Employee。问题事实类的所有属性都是问题属性。 计划实体类:在规划 过程中对分数约束和更改使用,例如
ShiftAssignment。规划期间更改的属性是 规划变量。其他属性是问题属性。询问以下问题:
- 计划过程中发生了哪些类变化?
哪个类具有我想要
更改的变量?该类是规划实体。
计划实体类需要使用
@PlanningEntity注释注释,或使用红帽在域设计器中的 OptaPlanner dock 进行定义。每个规划实体类都有一个或多个 规划变量,并且还必须有一个或多个定义属性。
大多数用例只有一个规划实体类,每个计划实体类只有一个规划变量。
5.2.3. 为员工降级项目创建数据模型 复制链接链接已复制到粘贴板!
使用这个部分,创建在 Business Central 中运行员工指定示例项目所需的数据对象。
先决条件
- 您已完成 第 5.2.1 节 “设置员工的 rostering 项目” 中描述的项目设置。
流程
- 使用新项目时,点击项目视角中的 Data Object,或者点击 Add Asset → Data Object 以创建新的数据对象。
将第一个数据对象
时间命名为,然后选择 employeesrostering.employeerostering作为 软件包。点 确定。
-
在 Data Objects 视角中,点 +add 字段将字段 添加到
Timeslotdata 对象。 -
在 id 字段中,键入
endTime。 -
单击 Type 旁边的下拉菜单,然后选择
LocalDateTime。 - 点击 Create 并继续 添加另一个字段。
-
添加另一个字段,其中包含 id
startTime和 TypeLocalDateTime。 - 点 Create。
-
单击右上角的 Save,以保存
Timeslot数据对象。 - 单击右上角的 x 以关闭 Data Objects 透视图,再返回到 Assets 菜单。
使用前面的步骤创建以下数据对象及其属性:
Expand 表 5.1. 技巧 id 类型 name字符串Expand 表 5.2. 员工 id 类型 name字符串技能employeerostering.employeerostering.Skill[List]Expand 表 5.3. 改变 id 类型 requiredSkillemployeerostering.employeerostering.Skilltimeslotemployeerostering.employeerostering.TimeslotExpand 表 5.4. DayOffRequest id 类型 dateLocalDate员工employeerostering.employeerostering.EmployeeExpand 表 5.5. ShiftAssignment id 类型 员工employeerostering.employeerostering.Employee改变employeerostering.employeerostering.Shift
有关创建数据对象的更多示例,请参阅开始使用决策服务。
5.2.3.1. 创建员工的 roster 计划实体 复制链接链接已复制到粘贴板!
为了解决员工问答规划问题,您必须创建一个计划实体和解决方法。计划实体在域设计人员中使用 OptaPlanner dock 中可用的属性定义。
使用以下步骤将 ShiftAssignment 数据对象定义为员工名册的规划实体。
先决条件
- 您已通过完成 第 5.2.3 节 “为员工降级项目创建数据模型” 中的步骤创建运行员工代理示例所需的相关数据对象和规划实体。
流程
-
在项目 Assets 菜单中,打开
ShiftAssignmentdata 对象。 -
在 Data Objects 视角中,点右侧的
来打开 OptaPlanner dock。
- 选择 计划实体。
-
从ShiftAssignmentdata 对象下的字段列表中选择 employees。 在 OptaPlanner dock 中,选择 规划变量。
在 Value Range Id 输入字段中,键入
employeesRange。这会在计划实体中添加@ValueRangeProvider注释,您可以通过单击设计者中的Source选项卡来查看。planning 变量的值范围通过
@ValueRangeProvider注释定义。@ValueRangeProvider注释始终具有属性id,它被@PlanningVariable属性值RangeProviderRefs引用。- 关闭 dock 并单击 Save 以保存数据对象。
5.2.3.2. 创建员工的 roster 计划解决方案 复制链接链接已复制到粘贴板!
员工的 roster 问题依赖于定义的规划解决方案。计划解决方案由红帽构建 OptaPlanner dock 中的属性在域设计器中定义。
先决条件
- 您已通过完成 第 5.2.3 节 “为员工降级项目创建数据模型” 和 第 5.2.3.1 节 “创建员工的 roster 计划实体” 中的步骤创建运行员工指定示例所需的相关数据对象和规划实体。
流程
-
使用标识符
EmployeeRoster创建一个新的数据对象。 创建以下字段:
Expand 表 5.6. EmployeeRoster id 类型 dayOffRequestListemployeerostering.employeerostering.DayOffRequest[List]shiftAssignmentListemployeerostering.employeerostering.ShiftAssignment[List]shiftListemployeerostering.employeerostering.Shift[List]skillListemployeerostering.employeerostering.Skill[List]timeslotListemployeerostering.employeerostering.Timeslot[List]-
在 Data Objects 视角中,点右侧的
来打开 OptaPlanner dock。
- 选择 规划解决方案.
-
将默认的
hard soft 分数保留为 Solution Score Type。这会在EmployeeRoster数据对象中自动生成分数,该分数作为类型。 使用以下属性添加新字段:
Expand id 类型 employeeListemployeerostering.employeerostering.Employee[List]选择 employees
List字段后,打开 OptaPlanner dock,再选择 Planning Value Range Provider 框。在 id 字段中,键入 staff
Range。关闭 dock。- 点击右上角的 Save 保存资产。
5.2.4. 员工的降级限制 复制链接链接已复制到粘贴板!
员工名单是一个规划问题。所有规划问题都包括必须满足约束才能找到最佳解决方案。
Business Central 中的员工指定示例项目包括以下硬和软限制:
- 硬约束
- 员工仅针对每天进行一次转变。
- 所有需要特定员工技能的改变都会为拥有该特定技能的员工分配。
- 软限制
- 所有员工都被分配了一项转变。
- 如果雇员请求一天,其变化会被重新分配给其他员工。
硬和软限制在 Business Central 中通过自由-form DRL 设计器定义,也可以使用指导规则。
5.2.4.1. DRL(Drools 规则语言)规则 复制链接链接已复制到粘贴板!
DRL(Drools Rule)规则是您在 .drl 文本文件中直接定义的业务规则。这些 DRL 文件是 Business Central 中所有其他规则资产渲染的源。您可以在 Business Central 界面中创建和管理 DRL 文件,或使用 Red Hat CodeReady Studio 或其他集成开发环境(IDE)在外部创建它们。DRL 文件可以包含一个或多个规则,它们至少定义规则条件(在时)和操作(然后再)。Business Central 中的 DRL 设计器为 Java、DRL 和 XML 提供语法高亮显示。
DRL 文件由以下组件组成:
DRL 文件中的组件
以下示例 DRL 规则决定了 loan 应用程序决策服务中的年龄限制:
loan Application age 限制的规则示例
DRL 文件包含单个或多个规则、查询和函数,并可以定义由规则和查询分配和使用的属性等资源声明。DRL 软件包必须在 DRL 文件的顶部列出,规则通常最后列出。所有其他 DRL 组件可遵循任何顺序。
每个规则必须在规则软件包中具有唯一的名称。如果您在软件包中的任何 DRL 文件中使用相同的规则名称多次,则规则无法编译。始终使用双引号括起规则名称(规则"rule name"),以防止可能出现的编译错误,特别是在规则名称中使用空格。
与 DRL 规则相关的所有数据对象都必须位于与 Business Central 中的 DRL 文件相同的项目软件包中。默认导入同一软件包中的资产。其他软件包中的现有资产可以通过 DRL 规则导入。
5.2.4.2. 使用 DRL designer 定义员工指定限制 复制链接链接已复制到粘贴板!
您可以使用 Business Central 中的自由-form DRL 设计器为员工名册创建约束定义。
使用此流程创建一个 硬约束,使其没有员工分配在之前的 10 小时后开始的转换。
流程
- 在 Business Central 中,前往 Menu → Design → Projects 并点项目名称。
- 点 Add Asset → DRL 文件。
-
在 DRL 文件名 字段中,键入
complexScoreRules。 -
选择 employees
rostering.employeerostering软件包。 - 点 +Ok 创建 DRL 文件。
在 DRL 设计器的 Model 选项卡中,将
Employee10HourShiftSpace规则定义为 DRL 文件:Copy to Clipboard Copied! Toggle word wrap Toggle overflow - 点 Save 保存 DRL 文件。
有关创建 DRL 文件的更多信息,请参阅使用 DRL 规则设计决策服务。
5.2.5. 使用指导规则为员工名单创建规则 复制链接链接已复制到粘贴板!
您可以使用 Business Central 中的指导规则设计人员创建为员工名单定义硬和软约束的规则。
5.2.5.1. 指导规则 复制链接链接已复制到粘贴板!
指导规则是您通过规则创建在业务中心基于 UI 的指导规则设计者中创建的业务规则。指导规则设计器根据所定义规则的数据对象,提供可接受输入的字段和选项。您定义的指南规则与所有其他规则资产一样编译为 Drools Rule Language(DRL)规则。
与指导规则相关的所有数据对象都必须位于与指导规则相同的项目软件包中。默认导入同一软件包中的资产。创建必要的数据对象和指导规则后,您可以使用指南规则设计器的 Data Objects 选项卡来验证所有所需数据对象是否已列出或导入其他现有数据对象,方法是添加新项目。
5.2.5.2. 创建用于平衡员工切换数量的指导规则 复制链接链接已复制到粘贴板!
BalanceEmployeesShiftNumber 指导规则创建一个软约束,确保以尽可能均匀地平衡的方式为员工分配切换。它通过创建一个分数影响,在变送变小时增加。分数公式由规则实施,使 Solver 以更均衡的方式分发转换。
流程
- 在 Business Central 中,前往 Menu → Design → Projects 并点项目名称。
- 点 Add Asset → Guided Rule。
-
输入
BalanceEmployeesShiftNumber作为 引导规则 名称,再选择 employeesrostering.employeerostering软件包。 - 点 Ok 创建规则资产。
-
点 WHEN 字段中的
来添加 WHEN 条件。
-
在规则窗口的 Add a 条件 中选择
Employee。单击 +Ok。 -
单击
Employee条件来修改限制,并添加变量名称$employee。 添加来自 加速
的 WHEN 条件。-
在
From Accumulate条件 上方点击 添加模式 并选择Number作为事实类型,从下拉列表中选择 Number。 -
将变量名称
$shiftCount添加到Number条件中。 -
在
From Accumulate条件下,单击添加模式,然后从下拉列表中选择ShiftAssignmentfact 类型。 -
将变量名称
$shiftAssignment添加到ShiftAssignmentfact 类型。 -
再次单击
ShiftAssignment条件,然后从 Add a restrictions on a field 下拉列表中选择 employees。 -
从 employees 约束旁边的下拉列表中选择
等于。 -
点击下拉按钮旁的
图标添加变量,然后点击 Field value 窗口中的 Bound 变量。
-
从下拉列表中选择
$employee。 -
在 Function 框中,键入
count($shiftAssignment)。
-
在
-
点 wordpress N 字段中的
来添加 wordpressN 条件。
在 Add a new action 窗口中选择
Modify Soft Score。单击 +Ok。-
在方框中输入以下表达式:
-($shiftCount.intValue()*$shiftCount.intValue())
-
在方框中输入以下表达式:
- 单击右上角的 Validate 来检查所有规则条件是否有效。如果规则验证失败,解决错误消息中描述的任何问题,查看规则中的所有组件,然后重试验证规则直到规则通过为止。
- 单击 Save 以保存该规则。
有关创建指南规则的更多信息,请参阅使用指导规则 设计决策服务。
5.2.5.3. 为每天不一换个变化创建指导规则 复制链接链接已复制到粘贴板!
OneEmployeeShiftPerDay 指导规则创建了一个硬约束,员工不会超过一天的转变。在员工指定示例中,这个约束是使用指导的规则设计人员创建的。
流程
- 在 Business Central 中,前往 Menu → Design → Projects 并点项目名称。
- 点 Add Asset → Guided Rule。
-
输入
OneEmployeeShiftPerDay作为 引导规则 名称,再选择 employeesrostering.employeerostering软件包。 - 点 Ok 创建规则资产。
-
点 WHEN 字段中的
来添加 WHEN 条件。
- 从 Add a condition to rule 窗口中选择 Free form DRL。
在自由表单 DRL 框中,输入以下条件:
$shiftAssignment : ShiftAssignment( employee != null ) ShiftAssignment( this != $shiftAssignment , employee == $shiftAssignment.employee , shift.timeslot.startTime.toLocalDate() == $shiftAssignment.shift.timeslot.startTime.toLocalDate() )
$shiftAssignment : ShiftAssignment( employee != null ) ShiftAssignment( this != $shiftAssignment , employee == $shiftAssignment.employee , shift.timeslot.startTime.toLocalDate() == $shiftAssignment.shift.timeslot.startTime.toLocalDate() )Copy to Clipboard Copied! Toggle word wrap Toggle overflow 此条件指出,不能分配给已经在同一天进行另一个切换分配的员工。
-
点 wordpress N 字段中的
来添加 wordpressN 条件。
- 从 Add a new action 窗口中选择 Add free form DRL。
在自由表单 DRL 框中,输入以下条件:
scoreHolder.addHardConstraintMatch(kcontext, -1);
scoreHolder.addHardConstraintMatch(kcontext, -1);Copy to Clipboard Copied! Toggle word wrap Toggle overflow - 单击右上角的 Validate 来检查所有规则条件是否有效。如果规则验证失败,解决错误消息中描述的任何问题,查看规则中的所有组件,然后重试验证规则直到规则通过为止。
- 单击 Save 以保存该规则。
有关创建指南规则的更多信息,请参阅使用指导规则 设计决策服务。
5.2.5.4. 创建一条指导规则以满足不断变化的要求技能 复制链接链接已复制到粘贴板!
ShiftReqiredSkillsAreMet guided 规则创建了一个硬约束,以确保所有转换都被分配了一组正确技能的员工。在员工指定示例中,这个约束是使用指导的规则设计人员创建的。
流程
- 在 Business Central 中,前往 Menu → Design → Projects 并点项目名称。
- 点 Add Asset → Guided Rule。
-
输入
ShiftReqiredSkillsAreMet作为 引导规则 名称并选择 employeesrostering.employeerostering软件包。 - 点 Ok 创建规则资产。
-
点 WHEN 字段中的
来添加 WHEN 条件。
-
选择 Add a condition to rule 窗口中的
ShiftAssignment。单击 +Ok。 -
单击
ShiftAssignment条件,然后从 Add a limits on a field 下拉列表中选择 employees。 -
在设计人员中,单击
员工旁边的下拉列表,然后选择不是 null。 单击
ShiftAssignment条件,然后单击 Expression 编辑器。-
在设计人员中,单击
[not bound]以打开 Expression 编辑器,并将表达式绑定到变量$requiredSkill。点 Set。 -
在设计人员(
/requiredSkill旁边),从第一个下拉列表中选择转换,然后从下一下拉列表中选择requiredSkill。
-
在设计人员中,单击
单击
ShiftAssignment条件,然后单击 Expression 编辑器。-
在设计人员(
[not bound]旁边),从第一个下拉列表中选择员工,然后从下一下拉列表中选择技能。 -
将下一个下拉列表保留为
选择。 -
在下一个下拉菜单中,请更改
以排除。 -
点击
排除的
图标,在 Field value 窗口中点击 New formula 按钮。
-
在公式框中输入
$requiredSkill。
-
在设计人员(
-
点 wordpress N 字段中的
来添加 wordpressN 条件。
-
在 Add a new action 窗口中选择
Modify Hard Score。单击 +Ok。 -
在分数操作框中输入
-1。 - 单击右上角的 Validate 来检查所有规则条件是否有效。如果规则验证失败,解决错误消息中描述的任何问题,查看规则中的所有组件,然后重试验证规则直到规则通过为止。
- 单击 Save 以保存该规则。
有关创建指南规则的更多信息,请参阅使用指导规则 设计决策服务。
5.2.5.5. 创建用于管理开箱即用请求的指南规则 复制链接链接已复制到粘贴板!
DayOffRequest guided 规则会创建一个软约束。如果最初被分配了该转换的人,则这个限制可以重新分配给另一位员工。在员工指定示例中,这个约束是使用指导的规则设计人员创建的。
流程
- 在 Business Central 中,前往 Menu → Design → Projects 并点项目名称。
- 点 Add Asset → Guided Rule。
-
输入
DayOffRequest作为 引导规则 名称,再选择 employeesrostering.employeerostering软件包。 - 点 Ok 创建规则资产。
-
点 WHEN 字段中的
来添加 WHEN 条件。
- 从 Add a condition to rule 窗口中选择 Free form DRL。
在自由表单 DRL 框中,输入以下条件:
$dayOffRequest : DayOffRequest( ) ShiftAssignment( employee == $dayOffRequest.employee , shift.timeslot.startTime.toLocalDate() == $dayOffRequest.date )
$dayOffRequest : DayOffRequest( ) ShiftAssignment( employee == $dayOffRequest.employee , shift.timeslot.startTime.toLocalDate() == $dayOffRequest.date )Copy to Clipboard Copied! Toggle word wrap Toggle overflow 如果转移被分配给一个进行当天发出的某个员工,则该条件将指出,该条件可以取消对当天的转换进行取消签名。
-
点 wordpress N 字段中的
来添加 wordpressN 条件。
- 从 Add a new action 窗口中选择 Add free form DRL。
在自由表单 DRL 框中,输入以下条件:
scoreHolder.addSoftConstraintMatch(kcontext, -100);
scoreHolder.addSoftConstraintMatch(kcontext, -100);Copy to Clipboard Copied! Toggle word wrap Toggle overflow - 单击右上角的 Validate 来检查所有规则条件是否有效。如果规则验证失败,解决错误消息中描述的任何问题,查看规则中的所有组件,然后重试验证规则直到规则通过为止。
- 单击 Save 以保存该规则。
有关创建指南规则的更多信息,请参阅使用指导规则 设计决策服务。
5.2.6. 为员工降级创建解决器配置 复制链接链接已复制到粘贴板!
您可以在 Business Central 中创建并编辑 Solver 配置。Solver 配置设计器会创建一个解决器配置,可在项目部署后运行。
先决条件
- Red Hat Process Automation Manager 下载并安装。
- 您已为员工名录示例创建并配置了所有相关资产。
流程
- 在 Business Central 中,点击 Menu → Projects,然后点击您的项目打开它。
- 在 Assets 视角中,点 Add Asset → Solver 配置
在 Create new Solver 配置 窗口中,为您的 Solver 键入名称
EmployeeRosteringSolverConfig,然后点击 Ok。这会打开 Solv er 配置 设计器。
在 Score Director Factory 配置部分中,定义包含评分规则定义的 KIE 基础。员工的 rostering 示例项目使用
defaultKieBase。-
选择在 KIE 基本中定义的一个 KIE 会话。员工的 rostering 示例项目使用
defaultKieSession。
-
选择在 KIE 基本中定义的一个 KIE 会话。员工的 rostering 示例项目使用
- 单击右上角的 Validate,以检查 Score Director Factory 配置是否正确。如果验证失败,解决错误消息中描述的任何问题,然后重试进行验证,直到配置通过。
- 点 Save 保存 Solver 配置。
5.2.7. 为员工降级项目配置 Solver 终止 复制链接链接已复制到粘贴板!
您可以将 Solver 配置为在指定时间后终止。默认情况下,计划引擎会提供无限的时间段来解决问题实例。
员工的 rostering 示例项目设置为运行 30 秒。
先决条件
-
您已为员工名册项目创建了所有相关资产,并在 Business Central 中创建了
EmployeeRosteringSolverConfigsolver 配置,如 第 5.2.6 节 “为员工降级创建解决器配置” 所述。
流程
-
从 Assets 视角打开
EmployeeRosteringSolverConfig。这将打开 Solv er 配置 设计器。 - 在 Termination 部分,点 Add 在所选逻辑组中创建新终止元素。
-
从下拉列表中选择所花费的终止类型。这会在终止配置中添加为输入字段。 - 使用时间元素旁边的箭头来调整 30 秒的时间。
- 单击右上角的 Validate,以检查 Score Director Factory 配置是否正确。如果验证失败,解决错误消息中描述的任何问题,然后重试进行验证,直到配置通过。
- 点 Save 保存 Solver 配置。
5.3. 使用 REST API 访问解决问题 复制链接链接已复制到粘贴板!
在部署或重新创建示例地址后,您可以使用 REST API 访问它。
您必须使用 REST API 注册解决器实例。然后,您可以提供数据集并检索优化的解决方案。
先决条件
- 根据本文档中前面的章节设置和部署员工的 rostering 项目。您可以部署示例项目,如 第 5.1 节 “在 Business Central 中部署 employees rostering 示例项目” 所述,或者重新创建项目,如 第 5.2 节 “重新排序员工降级示例项目” 所述。
5.3.1. 使用 REST API 注册 Solver 复制链接链接已复制到粘贴板!
您必须使用 REST API 注册 solver 实例,然后才能使用 solver。
每个临时解决方案都能够一次优化一个规划问题。
流程
使用以下标头创建 HTTP 请求:
authorization: admin:admin X-KIE-ContentType: xstream content-type: application/xml
authorization: admin:admin X-KIE-ContentType: xstream content-type: application/xmlCopy to Clipboard Copied! Toggle word wrap Toggle overflow 使用以下请求注册 Solver:
- PUT
-
http://localhost:8080/kie-server/services/rest/server/containers/employeerostering_1.0.0-SNAPSHOT/solvers/EmployeeRosteringSolver - 请求正文
<solver-instance> <solver-config-file>employeerostering/employeerostering/EmployeeRosteringSolverConfig.solver.xml</solver-config-file> </solver-instance>
<solver-instance> <solver-config-file>employeerostering/employeerostering/EmployeeRosteringSolverConfig.solver.xml</solver-config-file> </solver-instance>Copy to Clipboard Copied! Toggle word wrap Toggle overflow
5.3.2. 使用 REST API 调用 Solver 复制链接链接已复制到粘贴板!
在注册了解决器实例后,您可以使用 REST API 将数据设置为解决方法,并检索优化的解决方案。
流程
使用以下标头创建 HTTP 请求:
authorization: admin:admin X-KIE-ContentType: xstream content-type: application/xml
authorization: admin:admin X-KIE-ContentType: xstream content-type: application/xmlCopy to Clipboard Copied! Toggle word wrap Toggle overflow 使用数据收集向 Solver 提交请求,如下例所示:
- POST
-
http://localhost:8080/kie-server/services/rest/server/containers/employeerostering_1.0.0-SNAPSHOT/solvers/EmployeeRosteringSolver/state/solving - 请求正文
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
请求最佳解决方案以寻求规划问题:
- GET
http://localhost:8080/kie-server/services/rest/server/containers/employeerostering_1.0.0-SNAPSHOT/solvers/EmployeeRosteringSolver/bestsolution响应示例
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
第 6 章 OptaPlanner 和 Quarkus 入门 复制链接链接已复制到粘贴板!
您可以使用 https://code.quarkus.redhat.com 网站生成红帽构建的 OptaPlanner Quarkus Maven 项目,并自动添加并配置要在应用程序中使用的扩展。然后您可以下载 Quarkus Maven 存储库,或使用您项目中的在线 Maven 存储库。
6.1. Apache Maven 和 Red Hat build of Quarkus 复制链接链接已复制到粘贴板!
Apache Maven 是 Java 应用程序开发中使用的分布式构建自动化工具,用于创建、管理和构建软件项目。Maven 使用名为 Project Object Model(POM)文件的标准配置文件来定义项目并管理构建流程。POM 文件描述使用 XML 文件生成的项目打包和输出的模块和组件依赖关系、构建顺序和目标。这可确保以正确、一致的方式构建项目。
Maven 存储库
Maven 存储库存储 Java 库、插件和其他构建构件。默认公共存储库是 Maven 2 中央存储库,但存储库可以是专有仓库和内部的,以在开发团队之间共享常见工件。还可从第三方提供存储库。
您可以将在线 Maven 存储库与 Quarkus 项目一起使用,也可以下载红帽 Quarkus Maven 存储库构建。
Maven 插件
Maven 插件是定义实现一个或多个目标的 POM 文件的组成部分。Quarkus 应用程序使用以下 Maven 插件:
-
Quarkus Maven 插件(
quarkus-maven-plugin): Enables Maven 创建 Quarkus 项目,支持生成 uber-JAR 文件,并提供开发模式。 -
Maven Surefire 插件(
maven-surefire-plugin): 在构建生命周期的测试阶段用来对应用程序执行单元测试。插件会生成包含测试报告的文本和 XML 文件。
6.1.1. 为在线存储库配置 Maven settings.xml 文件 复制链接链接已复制到粘贴板!
您可以通过配置用户 settings.xml 文件,将在线 Maven 存储库用于 Maven 项目。这是推荐的方法。与共享服务器上的存储库管理器或存储库一起使用的 Maven 设置可以提供更好的控制和管理项目。
当您通过修改 Maven settings.xml 文件来配置存储库时,更改会应用到所有 Maven 项目。
流程
在文本编辑器中打开 Maven
~/.m2/settings.xml文件或集成开发环境(IDE)。注意如果
~/.m2/目录中没有settings.xml文件,请将$MAVEN_HOME/.m2/conf/目录中的settings.xml文件复制到~/.m2/目录中。在
settings.xml文件的<profiles> 元素中添加以下行:Copy to Clipboard Copied! Toggle word wrap Toggle overflow 将以下行添加到
settings.xml文件的 <activeProfiles> 元素,并保存文件。<activeProfile>red-hat-enterprise-maven-repository</activeProfile>
<activeProfile>red-hat-enterprise-maven-repository</activeProfile>Copy to Clipboard Copied! Toggle word wrap Toggle overflow
6.1.2. 下载并配置 Quarkus Maven 存储库 复制链接链接已复制到粘贴板!
如果您不想使用在线 Maven 存储库,您可以下载并配置 Quarkus Maven 存储库,以使用 Maven 创建 Quarkus 应用程序。Quarkus Maven 存储库包含许多 Java 开发人员通常用来构建应用程序的要求。这个步骤描述了如何编辑 settings.xml 文件来配置 Quarkus Maven 存储库。
当您通过修改 Maven settings.xml 文件来配置存储库时,更改会应用到所有 Maven 项目。
流程
- 从红帽客户门户网站的 Software Downloads 页面(需要登录)下载 Quarkus Maven repository ZIP 文件。
- 展开下载的存档。
-
将目录改为
~/.m2/目录,并在文本编辑器或集成开发环境(IDE)中打开 Mavensettings.xml文件。 将以下行添加到
settings.xml文件的<profiles> 元素,其中QUARKUS_MAVEN_REPOSITORY是您下载的 Quarkus Maven 存储库的路径。QUARKUS_MAVEN_REPOSITORY的格式必须是file://$PATH,如file:///home/userX/rh-quarkus-2.13.GA-maven-repository/maven-repository。Copy to Clipboard Copied! Toggle word wrap Toggle overflow 将以下行添加到
settings.xml文件的 <activeProfiles> 元素,并保存文件。<activeProfile>red-hat-enterprise-maven-repository</activeProfile>
<activeProfile>red-hat-enterprise-maven-repository</activeProfile>Copy to Clipboard Copied! Toggle word wrap Toggle overflow
如果您的 Maven 存储库包含过时的工件,您可能会在构建或部署项目时遇到以下 Maven 错误消息之一,其中 ARTIFACT_NAME 是缺少的工件和 PROJECT_NAME 的名称,即您要构建的项目的名称:
-
缺少工件 PROJECT_NAME -
[ERROR] Failed on project ARTIFACT_NAME; Could not resolve dependencies for PROJECT_NAME
要解决这个问题,删除位于 ~/.m2/repository 目录中的本地存储库的缓存版本,以强制下载最新的 Maven 工件。
您可以使用 Apache Maven 和 Quarkus Maven 插件,使用红帽构建的 OptaPlanner 和 Quarkus 应用程序启动并运行。
先决条件
- 安装了 OpenJDK 11 或更高版本。红帽构建的 Open JDK 可从红帽客户门户网站的 Software Downloads 页面获取(需要登录)。
- 已安装 Apache Maven 3.6 或更高版本。Maven 可以从 Apache Maven Project 网站获得。
流程
在命令终端中,输入以下命令验证 Maven 是否使用 JDK 11,并且 Maven 版本为 3.6 或更高版本:
mvn --version
mvn --versionCopy to Clipboard Copied! Toggle word wrap Toggle overflow - 如果前面的命令没有返回 JDK 11,请将到 JDK 11 的路径添加到 PATH 环境变量,然后再次输入前面的命令。
要生成 Quarkus OptaPlanner quickstart 项目,请输入以下命令:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 此命令在
./optaplanner-quickstart目录中创建以下元素:- Maven 结构
-
src/main/docker中的Dockerfile文件示例 应用程序配置文件
Expand 表 6.1. mvn io.quarkus:quarkus-maven-plugin:2.13.Final-redhat-00006:create 命令中使用的属性 属性 描述 projectGroupId项目的组 ID。
projectArtifactId项目的工件 ID。
extensions用于此项目的以逗号分隔的 Quarkus 扩展列表。如需 Quarkus 扩展的完整列表,请在命令行中输入
mvn quarkus:list-extensions。noExamples使用项目结构创建项目,但不包括测试或类。
projectGroupID和projectArtifactID属性的值用于生成项目版本。默认项目版本为 1InventoryServiceSNAPSHOT。
要查看您的 OptaPlanner 项目,请将目录改为 OptaPlanner Quickstarts 目录:
cd optaplanner-quickstart
cd optaplanner-quickstartCopy to Clipboard Copied! Toggle word wrap Toggle overflow 检查
pom.xml文件。内容应类似以下示例:Copy to Clipboard Copied! Toggle word wrap Toggle overflow
6.3. 使用 code.quarkus.redhat.com 创建 Quarkus Maven 项目 复制链接链接已复制到粘贴板!
您可以使用 code.quarkus.redhat.com 网站生成红帽构建的 OptaPlanner Quarkus Maven 项目,并自动添加并配置要在应用程序中使用的扩展。另外,code.quarkus.redhat.com 会自动管理将项目编译到原生可执行文件所需的配置参数。
本节介绍了如何生成 OptaPlanner Maven 项目并包含以下主题:
- 指定应用程序的基本详情。
- 选择您要包含在项目中的扩展。
- 使用您的项目文件生成可下载归档。
- 使用自定义命令编译和启动应用程序。
先决条件
- 您有一个 Web 浏览器。
流程
-
在网页浏览器中打开 https://code.quarkus.redhat.com: - 指定项目详情:
-
输入项目的组名称。名称的格式遵循 Java 软件包命名约定,如
com.example。 -
输入您要用于从项目生成的 Maven 工件的名称,如
code-with-quarkus。 选择 Build Tool > Maven 指定要创建 Maven 项目。您选择的构建工具决定了项目:
- 生成的项目的目录结构
- 生成的项目中使用的配置文件格式
用于编译和启动应用程序的自定义构建脚本和命令,会在您生成项目后为您显示
code.quarkus.redhat.com注意红帽只支持使用
code.quarkus.redhat.com创建 OptaPlanner Maven 项目。红帽不支持生成 Gradle 项目。
-
输入要在项目生成的工件中使用的版本。此字段的默认值为 15000
SNAPSHOT。建议使用 语义版本,但若愿意,您可以使用不同类型的版本。 输入构建工具在打包项目时生成的工件名称。
根据 Java 软件包命名惯例,软件包名称应与软件包名称匹配您用于项目的组名称,但您可以指定不同的名称。
注意code.quarkus.redhat.com网站自动使用 OptaPlanner 的最新版本。您可以在生成项目后手动更改pom.xml文件中的 BOM 版本。选择以下扩展以作为依赖项包含:
- RESTEasy JAX-RS (quarkus-resteasy)
- resteasy Jackson(quarkus-resteasy-jackson)
- OptaPlanner AI constraint solver(optaplanner-quarkus)
OptaPlanner Jackson(optaplanner-quarkus-jackson)
红帽为列表上的单个扩展提供不同级别的支持,这些扩展由每个扩展名称旁的标签来表示:
- 红帽完全支持 SUPPORTED 扩展用于生产环境中的企业应用程序。
- TECH-PREVIEW 扩展受红帽在生产环境中的支持,在 技术预览功能支持范围 下受到红帽的支持。
- 红帽在生产环境中不支持 DEV-SUPPORT 扩展,但红帽提供的核心功能则由红帽开发人员用于开发新的应用程序。
计划使用具有相同功能的较新的技术或实现替换 DEPRECATED 扩展。
红帽不支持在生产环境中使用的未标记扩展。
- 选择 Generate your application 确认您的选择并显示覆盖屏幕,其中包含包含您生成的项目的存档的下载链接。overlay 屏幕还显示可用于编译和启动应用程序的自定义命令。
- 选择 Download the ZIP,以使用生成的项目文件将存档保存到您的系统中。
- 提取存档的内容。
进入包含您提取的项目文件的目录:
cd <directory_name>
cd <directory_name>Copy to Clipboard Copied! Toggle word wrap Toggle overflow 以开发模式编译并启动应用程序:
./mvnw compile quarkus:dev
./mvnw compile quarkus:devCopy to Clipboard Copied! Toggle word wrap Toggle overflow
6.4. 使用 Quarkus CLI 创建红帽 Quarkus Maven 项目构建 复制链接链接已复制到粘贴板!
您可以使用 Quarkus 命令行界面(CLI)创建 Quarkus OptaPlanner 项目。
先决条件
- 已安装 Quarkus CLI。如需更多信息,请参阅使用 Quarkus 命令行界面 构建 Quarkus 应用程序。
流程
创建 Quarkus 应用程序:
quarkus create app -P io.quarkus:quarkus-bom:2.13.Final-redhat-00006
quarkus create app -P io.quarkus:quarkus-bom:2.13.Final-redhat-00006Copy to Clipboard Copied! Toggle word wrap Toggle overflow 要查看可用的扩展,请输入以下命令:
quarkus ext -i
quarkus ext -iCopy to Clipboard Copied! Toggle word wrap Toggle overflow 这个命令返回以下扩展:
optaplanner-quarkus optaplanner-quarkus-benchmark optaplanner-quarkus-jackson optaplanner-quarkus-jsonb
optaplanner-quarkus optaplanner-quarkus-benchmark optaplanner-quarkus-jackson optaplanner-quarkus-jsonbCopy to Clipboard Copied! Toggle word wrap Toggle overflow 输入以下命令在项目的
pom.xml文件中添加扩展:quarkus ext add resteasy-jackson quarkus ext add optaplanner-quarkus quarkus ext add optaplanner-quarkus-jackson
quarkus ext add resteasy-jackson quarkus ext add optaplanner-quarkus quarkus ext add optaplanner-quarkus-jacksonCopy to Clipboard Copied! Toggle word wrap Toggle overflow 在文本编辑器中打开
pom.xml文件。该文件应该类似以下示例:Copy to Clipboard Copied! Toggle word wrap Toggle overflow
部分 III. 红帽构建的 OptaPlanner solver 复制链接链接已复制到粘贴板!
OptaPlanner 解决一个规划问题包括以下步骤:
-
将计划问题作为标上
@PlanningSolution注释的类建模 (例如,NQueens类)。 -
配置 Solver (例如,第一个 Fit 和 Tabu Search solver 用于任何
NQueens实例)。 - 从您的 数据层加载 一个问题数据(例如 Four Queens 实例)。这是规划问题。
-
使用 Solv
er.solve(problem)解决问题,这将返回找到最佳解决方案。
第 7 章 配置红帽构建的 OptaPlanner solver 复制链接链接已复制到粘贴板!
您可以使用以下方法配置 OptaPlanner solver:
- 使用 XML 文件。
-
使用
SolverConfigAPI。 - 在域模型上添加类注释和 JavaBean 属性注解。
- 控制 OptaPlanner 用于访问您的域的方法。
- 定义自定义属性。
7.1. 使用 XML 文件配置 OptaPlanner solver 复制链接链接已复制到粘贴板!
您可以使用 XML 文件配置解决器。在遵循 Maven 目录结构的典型项目中,在构建带有 SolverFactory 的 Solver Factory 后,address rConfig XML 文件位于 $PROJECT_DIR/src/main/resources/org/optaplanner/examples/<PROJECT>/solver 目录,其中 & lt;PROJECT > 是 OptaPlanner 项目的名称。或者,也可以通过 SolverFactory .createFromXmlFile()从文件创建一个 SolververFactory。但是,出于便携性的原因,建议使用 classpath 资源。
Solver 和 SolverFactory 都有一个名为 Solution_ 的通用类型,它是代表规划问题和解决方案的类。
OptaPlanner 通过更改配置相对容易地切换优化算法。
流程
-
使用
SolverFactory 构建 Solverver实例。 配置解析器配置 XML 文件:
- 定义模型。
- 定义分数函数。
可选:配置优化算法。
以下示例是 NQueens 问题的解析器 XML 文件:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 注意在某些环境中,如 OSGi 和 JBoss 模块,您的 JAR 文件中的解决方案、分数 DRL 和域类等类路径资源可能不适用于
optaplanner-coreJAR 文件的默认ClassLoader。在这些情况下,以参数的形式为您的类提供类加载器:SolverFactory<NQueens> solverFactory = SolverFactory.createFromXmlResource( ".../nqueensSolverConfig.xml", getClass().getClassLoader());SolverFactory<NQueens> solverFactory = SolverFactory.createFromXmlResource( ".../nqueensSolverConfig.xml", getClass().getClassLoader());Copy to Clipboard Copied! Toggle word wrap Toggle overflow
使用解决器配置 XML 文件配置
SolverFactory,以类路径资源形式提供,如ClassLoader.getResource()所定义:SolverFasctory<NQueens> solverFactory = SolverFactory.createFromXmlResource( "org/optaplanner/examples/nqueens/solver/nqueensSolverConfig.xml"); Solver<NQueens> solver = solverFactory.buildSolver();SolverFasctory<NQueens> solverFactory = SolverFactory.createFromXmlResource( "org/optaplanner/examples/nqueens/solver/nqueensSolverConfig.xml"); Solver<NQueens> solver = solverFactory.buildSolver();Copy to Clipboard Copied! Toggle word wrap Toggle overflow
7.2. 使用 Java API 配置 OptaPlanner solver 复制链接链接已复制到粘贴板!
您可以使用 SolverConfig API 配置一个临时解决方案。这在运行时动态更改值特别有用。以下示例在 NQueens 项目中构建 Solver 之前,会根据系统属性更改正在运行的时间:
解决器配置 XML 文件中的每个元素都作为软件包命名空间 org.optaplanner.core.config 中的 类或一个属性提供。Config 这些配置 类是 XML 格式的 Java 表示。它们构建软件包命名空间的运行时组件 org.optaplanner.core.impl.,并将它们汇编为有效的 Solver。
要为每个用户请求动态配置 SolverFactory,在初始化过程中构建模板 SolverConfig,并将其复制为每个用户请求的复制构造器。以下示例演示了如何使用 NQueens 问题进行此操作:
7.3. OptaPlanner 注解 复制链接链接已复制到粘贴板!
您必须指定域模型中的哪些类是规划实体、哪些属性是计划变量等。使用以下方法之一在 OptaPlanner 项目中添加注解:
- 在域模型上添加类注释和 JavaBean 属性注解。属性注释必须位于 getter 方法上,而不是在 setter 方法上。注解的 getter 方法不需要公开。这是推荐的方法。
- 在域模型上添加类注释和字段注解。注解的字段不需要公共字段。
7.4. 指定 OptaPlanner 域访问 复制链接链接已复制到粘贴板!
默认情况下,OptaPlanner 使用反射访问您的域。与直接访问相比,反射比较可靠,但速度慢。另外,您还可以使用 Gizmo 将 OptaPlanner 配置为访问您的域,这将生成字节码来直接访问您的域的字段和方法,而无需反映。但是,此方法有以下限制:
- 计划注解只能基于公共字段和公共 getter。
-
io.quarkus.gizmo:gizmo必须在 classpath 上。
当使用 OptaPlanner with Quarkus 时,不能应用这些限制,因为 Gizmo 是默认的域访问类型。
流程
要在 Quarkus 之外使用 Gizmo,在 workr 配置中设置 domainAccessType :
<solver>
<domainAccessType>GIZMO</domainAccessType>
</solver>
<solver>
<domainAccessType>GIZMO</domainAccessType>
</solver>
7.5. 配置自定义属性 复制链接链接已复制到粘贴板!
在 OptaPlanner 项目中,您可以添加自定义属性来解决配置元素,这些元素实例化类,并明确提及自定义属性的文档。
先决条件
- 您有一个解决方法。
流程
添加自定义属性。
例如,如果您的
EasyScoreCalculator具有大量缓存的计算结果,并且您希望在一个基准中增加缓存大小,请添加myCacheSize属性:Copy to Clipboard Copied! Toggle word wrap Toggle overflow 为每个自定义属性添加公共集器,在构建
Solver时调用该属性。Copy to Clipboard Copied! Toggle word wrap Toggle overflow 大多数值类型都被支持,包括
布尔值、int、double、BigDecimal、String和enums。
第 8 章 OptaPlanner Solver 复制链接链接已复制到粘贴板!
解决者找到规划问题的最佳的最佳解决方案。解决者一次只能解决一个规划问题实例。解决者通过 SolverFactory 方法构建:
只能从单一线程访问地址,除了 javadoc 中被记录为线程安全的方法除外。solve() 方法处理当前的线程。Hogging 线程可能会导致 REST 服务的 HTTP 超时,它需要额外的代码来并行解决多个数据集。要避免这个问题,请使用 SolverManager。
8.1. 解决问题 复制链接链接已复制到粘贴板!
使用解决方法解决规划问题。
先决条件
-
从解决器配置构建的
Solver -
代表规划问题实例的
@PlanningSolution注解
流程
提供规划问题作为 solve() 方法的参数。解决者将返回找到的最佳解决方案。
以下示例解决了 NQueens 问题:
NQueens problem = ...;
NQueens bestSolution = solver.solve(problem);
NQueens problem = ...;
NQueens bestSolution = solver.solve(problem);
在本示例中,solution () 方法会返回一个 NQueens 实例,并且每个 Queen 分配给 行。
提供给 解决方法(Solution) 方法的解决方案实例可以是部分或完全初始化,这通常是重复规划的情况。
图 8.1. 8ms 中的 Four Queens Puzzle best Solution(Also a Optimal Solution)
解决(Solution) 方法可能需要很长时间,具体要看问题的大小和解决方法配置。Solver 智能地处理可能解决方案的搜索空间,并记住在解决过程中遇到的最佳解决方案。根据很多因素,包括问题的大小、Solver 的时间、解决器配置等等,最佳解决方案 可能是或可能不是最佳解决方案。
提供给方法 解决(Solution) 的解决方案实例已被 Solver 更改,但不要为最好的解决方案出现错误。
方法返回的解决方案实例 (Solution) 或 getBestSolution() 很可能是向方法 解决(Solution) 方法的规划克隆,这意味着它是不同的实例。
8.2. solver 环境模式 复制链接链接已复制到粘贴板!
解决方案环境模式允许您检测实施中的常见错误。它不会影响日志级别。
寻址程序只有一个随机实例。有些解决方法配置使用随机实例,其随机实例数量不止于其他实例。例如,Simulated Anneing 算法依赖于随机数字,而 Tabu Search 仅依赖于它解析分数绑定。环境模式会影响该随机实例的影响。
您可以在解决器配置 XML 文件中设置环境模式。以下示例设置了 FAST_ASSERT 模式:
<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>FAST_ASSERT</environmentMode>
...
</solver>
<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>FAST_ASSERT</environmentMode>
...
</solver>
以下列表描述了您可以在解析器配置文件中使用的环境模式:
-
FULL_ASSERT模式会开启所有断言,例如,在每次移动中递增分数计算未损坏,导致移动实施、约束、引擎本身等问题的快速失败。这个模式是可重复生成的。它还容易受到入侵,因为它调用了方法computeScore()比非assert 模式更频繁。FULL_ASSERT模式非常慢,因为它不依赖于增量分数计算。
-
NON_INTRUSIVE_FULL_ASSERT模式开启一些断言,在移动实施、约束、引擎本身等方面快速失败。这个模式是可重复生成的。它是非侵入性,因为它不调用计算Score()比一个非断模式更频繁。NON_INTRUSIVE_FULL_ASSERT模式非常慢,因为它不依赖于增量分数计算。
-
FAST_ASSERT模式开启大多数断言,例如,撤消Move 的分数与移动前的分数相同,以在 Move 实施、移动实施、约束、引擎本身等时快速出现失败。这个模式是可重复生成的。它还容易受到入侵,因为它调用了方法computeScore()比非assert 模式更频繁。FAST_ASSERT模式很慢。编写与FAST_ASSERT模式的简短运行情况的测试案例。
REPRODUCIBLE模式是默认模式,因为建议在开发过程中。在这个模式中,两个以相同的 OptaPlanner 版本运行,会以相同的顺序执行相同的代码。这两个运行在每个步骤都具有相同的结果,但适用下列注释时除外。这可让您持续复制错误。它还可让您对某些重构进行基准测试,如分数约束性能优化,并在运行时非常公平。注意尽管使用
REPRODCIBLE模式,但出于以下原因,您的应用程序可能仍不能完全重复:-
使用
HashSet或另一个集合,JVM 运行之间有不一致的顺序,用于计划实体或规划值的集合,但并非正常问题事实,特别是在解决方案实现中。使用LinkedHashSet替换它。 - 将有时间的依赖算法合并起来,特别是模拟算法,以及所花费的时间和终止时间。分配的 CPU 时间有足够大的差异会影响时间值。将 Simulated Anneing 算法替换为 Late Acceptance 算法,或者用步骤数终止替换终止所花费的时间。
-
使用
-
REPRODUCIBLE模式可能比NON_REPRODUCIBLE模式稍慢。如果您的生产环境可从可重复生成的可复制性中受益,则在生产环境中使用此模式。在实践中,如果未指定任何 seed,则REPRODUCIBLE模式使用默认固定随机 seed,同时还会禁用某些并发优化,比如加速工作。
-
NON_REPRODUCIBLE模式可能比REPRODUCIBLE模式更快。避免在开发过程中使用它,因为它进行了调试和修复困难。如果在生产环境中复制性不重要,请在生产中使用NON_REPRODUCIBLE模式。在实践中,如果没有指定任何 seed,这个模式不使用固定的随机 seed。
8.3. 更改 OptaPlanner solver 日志记录级别 复制链接链接已复制到粘贴板!
您可以在 OptaPlanner solver 中更改日志记录级别,以查看解决器活动。以下列表描述了不同的日志记录级别:
错误 :日志错误,除了以
RuntimeException格式发送到调用代码的除外。如果发生错误,OptaPlanner 通常会快速失败。它将子类
RuntimeException引发到调用代码的详细消息。为了避免重复的日志消息,它不会以错误的形式将其记录。除非调用代码明确捕获并消除RuntimeException,否则Thread's default 'ExceptionHandler会将它作为错误记录。同时,代码会破坏进一步危害或模糊处理错误。- Warn :日志可疑情况
- info :每个阶段日志和解决方法
- 调试 :记录每个阶段的每个步骤
- Trace :记录每个阶段每个步骤的每个步骤
指定 trace 日志记录会显著降低性能。但是,在开发过程中,追踪 日志不足以发现瓶颈。
对于快速步骤算法(如 Late Acceptance 和 Simulated Anneing,但不适用于慢速步骤算法,如 Tabu Search),甚至可能会降低性能的速度。
trace' 和 debug 日志记录都会导致使用大多数附加器进行多线程解决。
在 Eclipse 中,对控制台的调试日志往往会导致拥塞,分数计算速度超过 10000 秒。这个问题都不是 IntelliJ 或 Maven 命令行。
流程
将日志记录级别设置为 debug 日志记录来查看阶段结束的时间以及执行速度。
以下示例显示了 debug 日志记录的输出:
所有消耗值的时间都为毫秒。
所有内容都记录到 SLF4J,它是一个简单的日志 facade,它将每个日志消息委派给 Logback、Apache Commons Logging、Log4j 或 java.util.logging。为您选择的日志记录框架添加依赖项到日志适应器。
8.4. 使用 Logback 记录 OptaPlanner solver 活动 复制链接链接已复制到粘贴板!
Logback 是推荐的日志框架,用于 OptaPlanner。使用 Logback 记录 OptaPlanner solver 活动。
先决条件
- 您有一个 OptaPlanner 项目。
流程
将以下 Maven 依赖项添加到 OptaPlanner 项目的
pom.xml文件中:注意您不需要添加额外的网桥依赖项。
<dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.x</version> </dependency><dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.x</version> </dependency>Copy to Clipboard Copied! Toggle word wrap Toggle overflow 在
logback.xml文件中的org.optaplanner软件包配置日志记录级别,如下例所示,其中 <LEVEL> 是一个在 第 8.4 节 “使用 Logback 记录 OptaPlanner solver 活动” 中列出的日志记录级别。Copy to Clipboard Copied! Toggle word wrap Toggle overflow 可选:如果您有一个多租户应用程序,其中可能会有多个
Solver实例同时运行,请将每个实例的日志记录分开到单独的文件中:使用映射 诊断上下文 (MDC)周围 address
()调用:MDC.put("tenant.name",tenantName); MySolution bestSolution = solver.solve(problem); MDC.remove("tenant.name");MDC.put("tenant.name",tenantName); MySolution bestSolution = solver.solve(problem); MDC.remove("tenant.name");Copy to Clipboard Copied! Toggle word wrap Toggle overflow 将您的日志记录器配置为为每个
${tenant.name}使用不同的文件。例如,在logback.xml文件中使用SiftingAppender:Copy to Clipboard Copied! Toggle word wrap Toggle overflow 注意当运行多个 solvers 或一个多线程解决时,大多数附加程序(包括控制台)时,会导致使用
debug和trace日志记录进行拥塞。切换到 async 附加程序以避免出现这个问题或关闭调试日志记录。
-
如果 OptaPlanner 不识别新级别,请临时添加系统属性
-Dlogback.LEVEL=true以进行故障排除。
8.5. 使用 Log4J 记录 OptaPlanner solver 活动 复制链接链接已复制到粘贴板!
如果您已经使用 Log4J,且您不想切换到更快速的后续后继而使用 Log4J,您可以为 Log4J 配置 OptaPlanner 项目。
先决条件
- 您有一个 OptaPlanner 项目
- 您使用 Log4J 日志框架
流程
将网桥依赖项添加到项目
pom.xml文件中:<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.x</version> </dependency><dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.x</version> </dependency>Copy to Clipboard Copied! Toggle word wrap Toggle overflow 在
log4j.xml文件中在软件包org.optaplanner上配置日志级别,如下例所示,其中 <LEVEL> 是 第 8.4 节 “使用 Logback 记录 OptaPlanner solver 活动” 中列出的日志记录级别。Copy to Clipboard Copied! Toggle word wrap Toggle overflow 可选:如果您有一个多租户应用程序,其中可能会有多个
Solver实例同时运行,请将每个实例的日志记录分开到单独的文件中:使用映射 诊断上下文 (MDC)周围 address
()调用:MDC.put("tenant.name",tenantName); MySolution bestSolution = solver.solve(problem); MDC.remove("tenant.name");MDC.put("tenant.name",tenantName); MySolution bestSolution = solver.solve(problem); MDC.remove("tenant.name");Copy to Clipboard Copied! Toggle word wrap Toggle overflow 将您的日志记录器配置为为每个
${tenant.name}使用不同的文件。例如,在logback.xml文件中使用SiftingAppender:Copy to Clipboard Copied! Toggle word wrap Toggle overflow 注意当运行多个 solvers 或一个多线程解决时,大多数附加程序(包括控制台)时,会导致使用
debug和trace日志记录进行拥塞。切换到 async 附加程序以避免出现这个问题或关闭调试日志记录。
8.6. 监控解决器 复制链接链接已复制到粘贴板!
OptaPlanner 通过 Micrometer 公开指标数据,这是 Java 应用程序的指标检测库。您可以将 Micrometer 与流行监控系统一起使用来监控 OptaPlanner solver。
8.6.1. 为 Micrometer 配置 Quarkus OptaPlanner 应用程序 复制链接链接已复制到粘贴板!
要将 OptaPlanner Quarkus 应用程序配置为使用 Micrometer 和指定的监控系统,请将 Micrometer 依赖项添加到 pom.xml 文件中。
先决条件
- 您有一个 Quarkus OptaPlanner 应用程序。
流程
将以下依赖项添加到应用程序的
pom.xml文件中,其中 <MONITORING_SYSTEM> 是一个由 Micrometer 和 Quarkus 支持的监控系统:注意Prometheus 目前是唯一由 Quarkus 支持的监控系统。
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-micrometer-registry-<MONITORING_SYSTEM></artifactId> </dependency>
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-micrometer-registry-<MONITORING_SYSTEM></artifactId> </dependency>Copy to Clipboard Copied! Toggle word wrap Toggle overflow 要在 development 模式下运行应用程序,请输入以下命令:
mvn compile quarkus:dev
mvn compile quarkus:devCopy to Clipboard Copied! Toggle word wrap Toggle overflow 要查看应用程序的指标,请在浏览器中输入以下 URL:
http://localhost:8080/q/metrics
http://localhost:8080/q/metricsCopy to Clipboard Copied! Toggle word wrap Toggle overflow
8.6.2. 为 Micrometer 配置 Spring Boot OptaPlanner 应用程序 复制链接链接已复制到粘贴板!
要将 Spring Boot OptaPlanner 应用程序配置为使用 Micrometer 和指定的监控系统,请将 Micrometer 依赖项添加到 pom.xml 文件中。
先决条件
- 您有一个 Spring Boot OptaPlanner 应用程序。
流程
将以下依赖项添加到应用程序的
pom.xml文件中,其中 <MONITORING_SYSTEM> 是一个由 Micrometer 和 Spring Boot 支持的监控系统:Copy to Clipboard Copied! Toggle word wrap Toggle overflow -
在应用程序的
application.properties文件中添加配置信息。如需更多信息,请参阅 Micrometer 网站。 运行以下命令,请运行以下命令:
mvn spring-boot:run
mvn spring-boot:runCopy to Clipboard Copied! Toggle word wrap Toggle overflow 要查看应用程序的指标,请在浏览器中输入以下 URL:
http://localhost:8080/actuator/metrics
注意使用以下 URL 作为 Prometheus scraper 路径:
http://localhost:8080/actuator/prometheus
8.6.3. 为 Micrometer 配置一个普通 Java OptaPlanner 应用程序 复制链接链接已复制到粘贴板!
要将普通 Java OptaPlanner 应用程序配置为使用 Micrometer,您必须将您选择的监控系统的 Micrometer 依赖项和配置信息添加到项目的 POM.XML 文件中。
先决条件
- 您有一个普通 Java OptaPlanner 应用程序。
流程
将以下依赖项添加到应用程序的
pom.xml文件中,其中 <MONITORING_SYSTEM> 是一个使用 Micrometer 配置的监控系统,<VERSION> 是您使用的 Micrometer 的版本:Copy to Clipboard Copied! Toggle word wrap Toggle overflow -
将监控系统的 Micrometer 配置信息添加到项目
pom.xml文件的开头。如需更多信息,请参阅 Micrometer 网站。 在配置信息下方添加以下行,其中 <
;MONITORING_SYSTEM> 是您添加的监控系统:Metrics.addRegistry(<MONITORING_SYSTEM>);
Metrics.addRegistry(<MONITORING_SYSTEM>);Copy to Clipboard Copied! Toggle word wrap Toggle overflow 以下示例演示了如何添加 Prometheus 监控系统:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 打开监控系统,查看您的 OptaPlanner 项目的指标。公开以下指标:
注意指标的名称和格式因 registry 而异。
-
OptaPlanner.solver.errors:从测量开始解决过程中发生的错误总数。 -
OptaPlanner.solver.solve-length.active-count:当前解决的解决方法数量。 -
OptaPlanner.solver.solve-length.seconds-max: run time of longest-running currently active solver. -
OptaPlanner.solver.solve-length.seconds-sum:每个活跃解决问题持续时间的总和。例如,如果存在两个活跃的 solvers,一个只运行三分钟,另一个为一分钟,总计解决时间为 4 分钟。
-
8.6.4. 其他指标 复制链接链接已复制到粘贴板!
如需了解更多详细的监控,您可以在解决器配置中配置 OptaPlanner 以监控性能成本的额外指标。以下示例使用 BEST_SCORE 和 SCORE_CALCULATION_COUNT 指标 :
您可以在这个配置中启用以下指标:
-
SOLVE_DURATION(默认是 Micrometer 计量 ID:optaplanner.solver.solve.duration):衡量解决最长活动解决时间、活动解决者数量以及所有活跃解决者的累计持续时间。 -
ERROR_COUNT(默认为启用 Micrometer 量表 ID:optaplanner.solver.errors):衡量在解决问题时发生的错误数量。 -
SCORE_CALCULATION_COUNT(默认为 Micrometer 量表 ID:optaplanner.solver.score.calculation.count):测量执行 OptaPlanner 的分数计算次数。 -
BEST_SCORE(Micrometer 计量 ID:optaplanner.solver.best.score.*):衡量 OptaPlanner 最佳解决方案的性能。分数的每个级别都有单独的计量。例如,对于HardSoftScore,有optaplanner.solver.best.score.score 和 optaplanner.best.score量表。 -
STEP_SCORE(Micrometer 计量 ID:optaplanner.solver.step.score.*):衡量 OptaPlanner 采用每个步骤的分数。分数的每个级别都有单独的计量。例如,对于HardSoftScore,有optaplanner.solver.step.score和optaplanner.solver.step.score.soft.score计量。 -
BEST_SOLUTION_MUTATION(Micrometer meter ID:optaplanner.solver.butation):测量连续最佳解决方案之间更改的规划变量数量。 -
MOVE_COUNT_PER_STEP(Micrometer 计量 ID:optaplanner.solver.step.move.count):测量步骤中评估的移动数量。 -
MEMORY_USE(Micrometer 计量 ID:jvm.memory.used):测量 JVM 中使用的内存量。此指标不衡量被解析器使用的内存量;同一 JVM 上的两个解决者将报告此指标的相同值。 -
CONSTRAINT_MATCH_TOTAL_BEST_SCORE(Micrometer 量表 ID:optaplanner.solver.constraint.match.best.score.*):衡量 OptaPlanner 已发现的最佳解决方案分数影响。分数的每个级别都有单独的计量,每个约束都有标签。例如,对于在软件包 "com.example" 中的约束"Minimize Cost"的HardSoftScore,有optaplanner.solver.constraint.match.best.score.hard.score和optaplanner.solver.constraint.match。 best.score.soft.score量表,带有标签 "constraint.package=com.example" 和 "constraint.name=Minimize Cost"。 -
CONSTRAINT_MATCH_TOTAL_STEP_SCORE(Micrometer 计量 ID:optaplanner.solver.constraint.match.step.score.*):测量当前步骤中每个约束的影响。分数的每个级别都有单独的计量,每个约束都有标签。例如,对于在软件包 "com.example" 中的约束 "Minimize Cost" 约束一个HardSoftScore,有optaplanner.solver.constraint.match.step.score.hard.score和optaplanner.solver.constraint.match.step 带有标签 "constraint.com.example" 和 "constraint.name=Minimize Cost" 的 .score.soft.score 量表。 -
PICKED_MOVE_TYPE_BEST_SCORE_DIFF(Micrometer meter ID:optaplanner.solver.move.type.best.score.diff.*):衡量特定移动类型如何改进最佳解决方案。每个分数级别都有单独的计量,即移动类型的标签。例如,对于进程的计算机的HardSoftScore和ChangeMove,有optaplanner.solver.move.type.best.score.diff.hard.score和optaplanner.solver.move.type.best.score.diff.soft.scoremeters with tagmove.type=ChangeMveoProcess.compute. -
PICKED_MOVE_TYPE_STEP_SCORE_DIFF(Micrometer 计量 ID:optaplanner.solver.move.type.step.score.diff.*):衡量特定移动类型可以改进最佳解决方案。每个分数级别都有单独的计量,即移动类型的标签。例如,对于进程的计算机的HardSoftScore和ChangeMove,有optaplanner.solver.move.type.step.score.diff.hard.score和optaplanner.solver.move.type.step.score.diff.soft.scoremeters with tagmove.type=Move(compute.compute.Process).
8.7. 配置随机数生成器 复制链接链接已复制到粘贴板!
许多 heuristics 和 metaheuristics 依赖于伪随机数字生成器来移动选择,以解决分数绑定、根据移动接受度等问题。在解决过程中,使用相同的随机实例可以重复使用,以提高随机值的可重复性、性能和统一分布。
随机 seed 是一个数字,用于初始化伪随机数字生成器。
流程
可选:要更改随机实例的随机看到,请指定
一个随机的:<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"> <randomSeed>0</randomSeed> ... </solver><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"> <randomSeed>0</randomSeed> ... </solver>Copy to Clipboard Copied! Toggle word wrap Toggle overflow 可选:要更改伪随机数字生成器实现,请为下面的解析器配置文件中列出的
randomType属性指定一个值,其中 <RANDOM_NUMBERATOR> 是一个伪随机数字生成器:<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"> <randomType><RANDOM_NUMBER_GENERATOR></randomType> ... </solver><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"> <randomType><RANDOM_NUMBER_GENERATOR></randomType> ... </solver>Copy to Clipboard Copied! Toggle word wrap Toggle overflow 支持以下 pseudorandom 编号生成器:
-
JDK(默认):标准随机数字生成器实施(java.util.Random) -
MERSENNE_TWISTER: Commons Math的实施随机数 -
WELL512A,WELL1024A,WELL19937A,WELL19937C,WELL44497A和WELL44497B: Random number generator by Commons Math
-
在大多数情况下,randomType 属性的值对多个数据集最佳解决方案的平均质量没有重大影响。
第 9 章 OptaPlanner SolverManager 复制链接链接已复制到粘贴板!
是一个或多个 Solver 实例的常见问题,旨在简化 REST 和其他企业服务中的规划问题。
Solver Manager
与 Solver.solve(…) 方法不同,SolverManager 具有以下特征:
-
SolverManager.solve(…)立即返回:它会调度一个问题,以便在不阻止调用线程的情况下进行异步解决。这可避免 HTTP 和其他技术的超时问题。 -
SolverManager.solve(…)可并行解决同一域的多个规划问题。
在内部,SolverManager 管理了一个临时解决方案程序线程的线程池,它调用 Solver.solve(…),以及处理最佳解决方案更改事件的线程池。
在 Quarkus 和 Spring Boot 中,SolverManager 实例会在您的代码中自动注入。如果您使用 Quarkus 或 Spring Boot 以外的平台,请使用 create(…) 方法构建 SolverManager 实例:
SolverConfig solverConfig = SolverConfig.createFromXmlResource(".../cloudBalancingSolverConfig.xml");
SolverManager<CloudBalance, UUID> solverManager = SolverManager.create(solverConfig, new SolverManagerConfig());
SolverConfig solverConfig = SolverConfig.createFromXmlResource(".../cloudBalancingSolverConfig.xml");
SolverManager<CloudBalance, UUID> solverManager = SolverManager.create(solverConfig, new SolverManagerConfig());
提交至 Solv erManager.solve(…)方法的 每个问题都必须有唯一的问题 ID。之后调用 getSolverStatus(problemId) 或终止Early(problemId) 使用问题 ID 来区分规划问题。问题 ID 必须是一个不可变的类,如 Long、String 或 java.util.UUID。
SolverManagerConfig 类有一个 parallelSolverCount 属性,用于控制并行运行了多少 solvers。例如,如果 parallelSolverCount 属性被设置为 4,您提交了五个问题,则四个问题开始立即解决,在第一个问题之一结束时启动第五个问题。如果这些问题每五分钟解决,则第五个问题需要 10 分钟完成。默认情况下,parallelSolverCount 设置为 AUTO,它解析为 CPU 内核的一半,无论解决者的 moveThreadCount 是什么。
要获得最佳解决方案,在解决终止后通常会使用 SolverJob.getFinalBestSolution() :
然而,在用户需要解决方案前解决批处理问题以及用户正在主动等待解决方案时进行实时解决问题。
当前的 SolverManager 实施在一个计算机节点上运行,但未来的工作旨在跨云分发解决方法。
9.1. 批量解决问题 复制链接链接已复制到粘贴板!
批量解决问题正在并行解决多个数据集。批量解决问题尤其有用:
- 夜之间通常没有问题变化,或者没有问题变化。例如,某些组织强制执行截止时间,在 午夜前提交所有关闭请求。
- 解决者会运行时间更长的时间,因为 nobody 正在等待结果,而 CPU 资源通常更便宜。
- 当员工到达下一工作日时,可以使用解决方案。
流程
为批量解决并行解决的问题,通过 parallelSolverCount 限制,每个数据集的 address (…) 被调用:
9.2. 解决和倾听显示进度 复制链接链接已复制到粘贴板!
当用户等待某个解决方案时,当解决问题时,可能需要等待几分钟或几小时后才会收到结果。为确保一切顺利的用户,通过显示最佳解决方案以及目前获得的最佳分数来显示进度。
流程
要处理中间最佳解决方案,请使用
solveAndListen(…):Copy to Clipboard Copied! Toggle word wrap Toggle overflow 此实施使用数据库与 UI 通信,后者轮询数据库。更高级的实施将最佳的解决方案直接推送到 UI 或消息传递队列。
-
用户对中间最佳解决方案满意且不想等待更长的时间,请致电
SolverManager.terminateEarly(problemId)。
部分 IV. Red Hat build of OptaPlanner quick start Guide 复制链接链接已复制到粘贴板!
Red Hat build of OptaPlanner 提供以下快速启动指南,以演示 OptaPlanner 如何与不同的 techologies 集成:
- Red Hat build of OptaPlanner on Red Hat build of Quarkus: a school timetable quick start Guide
- Red Hat build of OptaPlanner on Red Hat build of Quarkus: a vaccination appointment 调度程序快速启动指南
- Red Hat build of OptaPlanner on Spring Boot: a school timetable quick start Guide
- Red Hat build of OptaPlanner with Java solvers:云平衡快速启动指南
本指南指导您使用红帽构建的 OptaPlanner 约束完成了创建红帽 Quarkus 应用程序的构建过程。您将构建一个针对学生和生生而优化院长的 REST 应用程序
您的服务会自动将 Lesson 实例分配给 Timeslot 和 Room 实例,方法是使用 AI 遵循以下硬和软 调度限制 :
- 个空间最多可以同时有一处。
- 老师可以同时在上课时进行教学。
- 学员最多可以同时参加。
- 教授者更倾向于在单一房间学习。
- 指导者更倾向于在课间学习顺序课时和不类差距。
数学会上讲,该学校的时间范围是一个 NP-hard 问题。这意味着很难扩展。简单地说,通过带有 brute 强制的可能组合将花费数百万年时间,而在超级计算机中,甚至在超级计算机中也要花费数百万年时间。幸运的是,AI 约束寻址者(如红帽构建的 OptaPlanner)具有高级算法,可在合理的时间内提供最接近的解决方案。被视为有合理的时间,它取决于问题的目标。
先决条件
- 安装了 OpenJDK 11 或更高版本。红帽构建的 Open JDK 可从红帽客户门户网站的 Software Downloads 页面获取(需要登录)。
- 已安装 Apache Maven 3.6 或更高版本。Maven 可以从 Apache Maven Project 网站获得。
- 使用 IntelliJ IDEA、VS Code、Eclipse 或 NetBeans 等 IDE。
您可以使用 Apache Maven 和 Quarkus Maven 插件,使用红帽构建的 OptaPlanner 和 Quarkus 应用程序启动并运行。
先决条件
- 安装了 OpenJDK 11 或更高版本。红帽构建的 Open JDK 可从红帽客户门户网站的 Software Downloads 页面获取(需要登录)。
- 已安装 Apache Maven 3.6 或更高版本。Maven 可以从 Apache Maven Project 网站获得。
流程
在命令终端中,输入以下命令验证 Maven 是否使用 JDK 11,并且 Maven 版本为 3.6 或更高版本:
mvn --version
mvn --versionCopy to Clipboard Copied! Toggle word wrap Toggle overflow - 如果前面的命令没有返回 JDK 11,请将到 JDK 11 的路径添加到 PATH 环境变量,然后再次输入前面的命令。
要生成 Quarkus OptaPlanner quickstart 项目,请输入以下命令:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 此命令在
./optaplanner-quickstart目录中创建以下元素:- Maven 结构
-
src/main/docker中的Dockerfile文件示例 应用程序配置文件
Expand 表 10.1. mvn io.quarkus:quarkus-maven-plugin:2.13.Final-redhat-00006:create 命令中使用的属性 属性 描述 projectGroupId项目的组 ID。
projectArtifactId项目的工件 ID。
extensions用于此项目的以逗号分隔的 Quarkus 扩展列表。如需 Quarkus 扩展的完整列表,请在命令行中输入
mvn quarkus:list-extensions。noExamples使用项目结构创建项目,但不包括测试或类。
projectGroupID和projectArtifactID属性的值用于生成项目版本。默认项目版本为 1InventoryServiceSNAPSHOT。
要查看您的 OptaPlanner 项目,请将目录改为 OptaPlanner Quickstarts 目录:
cd optaplanner-quickstart
cd optaplanner-quickstartCopy to Clipboard Copied! Toggle word wrap Toggle overflow 检查
pom.xml文件。内容应类似以下示例:Copy to Clipboard Copied! Toggle word wrap Toggle overflow
10.2. 对域对象建模 复制链接链接已复制到粘贴板!
红帽构建的 OptaPlanner 时间表项目的目标是将每节分配给一个时间段和一个房间。为此,请添加三个类: Timeslot、Lesson 和 Room,如下图所示:
Timeslot
Timeslot 类表示在未教授课程的时间间隔,例如: Monday 10:30 - 11:30 或 Tuesday 13:30 - 14:30 - 14:30。在这个示例中,所有时间插槽都有相同的持续时间,且在 lunch 或其他中断期间没有时间插槽。
时间段没有日期,因为高校的计划只每周重复。无法进行持续 规划。次性被称为 问题事实,因为在解决过程中不会有定时实例变化。此类类不需要任何特定于 OptaPlanner 的注解。
room
Room 类代表了课程讲授的方式,例如 Room A 或 Room B。在本例中,所有空间都没有容量限制,它们可容纳所有课时。
房间 实例在解决过程中不会改变,因此 Room 也是 问题。
lesson
在课时,由课课课表示,向班级教授一个主题,例如: A.Turing for a 9th grade 或 Chemistry by M.Curie 为 10th grade。如果一个主题每周都会为同一学员组讲授多次,则有多个小的、只能由 id 区分的实例。例如,第 9 分之时,每周有 6 个数学。
在寻求过程中,OptaPlanner 改变了 lesson 类的 timelot 和 room 字段,从而将每个较小的时间分配给一个时间段和一个房间。 因为 OptaPlanner 更改了这些字段,所以 lesson 是一个 规划实体 :
上图中的大多数字段包含输入数据,但 orange 字段除外。在输入数据中 未分配的 时间和房间 字段(null),并在输出数据中分配(而不是 null)。OptaPlanner 在解决过程中更改这些字段。这些字段称为规划变量。为了 OptaPlanner 可以识别它们,timelot 和 room 字段都需要 @PlanningVariable 注解。它们包含类( Lesson )需要 @PlanningEntity 注释。
流程
创建
src/main/java/com/example/domain/Timeslot.java类:Copy to Clipboard Copied! Toggle word wrap Toggle overflow 请注意,
toString()方法保留了输出短片,以便可以更轻松地读取 OptaPlanner 的DEBUG或 abrt 日志,如稍后所示。创建
src/main/java/com/example/domain/Room.java类:Copy to Clipboard Copied! Toggle word wrap Toggle overflow 创建
src/main/java/com/example/domain/Lesson.java类:Copy to Clipboard Copied! Toggle word wrap Toggle overflow Lesson类具有@PlanningEntity注释,因此 OptaPlanner 知道这个类在解决过程中发生了变化,因为它包含一个或多个计划变量。time
lot字段具有@PlanningVariable注释,因此 OptaPlanner 知道它可以更改其值。为了查找要分配给此字段的潜在的Timeslot实例,OptaPlanner 使用valueRangeProviderRefs属性连接到一个提供List<Timeslot>的值供应商。有关值范围供应商的信息,请参阅 第 10.4 节 “在规划解决方案中收集域对象”。出于同样原因,
room字段还具有@PlanningVariable注释。
10.3. 定义限制并计算分数 复制链接链接已复制到粘贴板!
当解决问题时,分数 代表特定解决方案的质量。得分越高。红帽构建的 OptaPlanner 寻找最佳解决方案,这是可用时间获得最高分数的解决方案。这可能是 最佳解决方案。
因为可时间的示例用例具有硬和软约束,因此请使用 HardSoftScore 类来代表分数:
- 硬限制不得被破坏。例如: 一个房间可以有以上时间。
- 软限制不应该被破坏。例如: 更喜欢在单个房里的教学方式。
硬约束的加法是相对于其他硬约束的权重。软限制会高于其他软限制。硬限制始终大于软限制,无论其对应的权重如何。
要计算分数,您可以实施 EasyScoreCalculator 类:
不幸的是,这个解决方案无法进行很好的扩展,因为它是非增量:每次分配给不同的时间段或房间,都会重新评估所有课程来计算新分数。
更好的解决方法是创建一个 src/main/java/com/example/solver/TimeTableConstraintProvider.java 类来执行增量分数计算。此类使用 OptaPlanner 的 ConstraintStream API,它由 Java 8 Streams 和 SQL 产生。ConstraintProvider 缩放比 EasyScoreCalculator 更好的度量:O(n)而不是 O(n)。
流程
创建以下 src/main/java/com/example/solver/TimeTableConstraintProvider.java 类:
10.4. 在规划解决方案中收集域对象 复制链接链接已复制到粘贴板!
TimeTable 实例嵌套了单个 dataset 的所有 Timeslot、Room 和 Lesson 实例。另外,由于它包含了 allon,每个都具有特定规划变量状态,所以它是一个 规划解决方案,并有分数:
-
如果课程仍然没有被签名,那么它是一个 未初始化 的解决方案,例如分数为
-4init/0hard/0soft的解决方案。 -
如果损坏硬限制,那么它是一种 不可预见的 解决方案,例如分数为
-2hard/-3soft的解决方案。 -
如果遵循所有硬约束,那么它是一个可行的解决方案,例如,分数为
0hard/-7soft的解决方案。
TimeTable 类有一个 @PlanningSolution 注释,因此红帽构建的 OptaPlanner 知道该类包含所有输入和输出数据。
具体来说,这个类是问题的输入:
带有所有时间窗的
timeslotList字段- 这是问题事实列表,因为它们在解决过程中不会改变。
包含所有房间有一个
roomList字段- 这是问题事实列表,因为它们在解决过程中不会改变。
带有所有节节的
lessonList字段- 这是计划实体列表,因为他们在解决过程中发生了变化。
在每个课
中:-
timeslot和room字段的值通常为null,因此取消分配。它们是规划变量。 -
其他字段(如
主题、Squillr 和studentGroup)已被填写。这些字段是问题属性。
-
但是,这个类也是解决方案的输出:
-
每个
lessonList字段在解决后都有非 nulltimelot 和room字段 -
代表输出解决方案质量
的分数字段,如0hard/-5soft
流程
创建 src/main/java/com/example/domain/TimeTable.java 类:
值范围供应商
timeslotList 字段是一个值范围 provider。它拥有 OptaPlanner 可以从中选择的 Timeslot 实例,分配给 lesson 实例的 timeslot 字段。time lotList 字段具有 @ValueRangeProvider 注释,可通过在 Lesson 中将 id 与 @PlanningVariable 的 valueRangeProviderRefs 匹配。
遵循相同的逻辑,roomList 字段也有 @ValueRangeProvider 注释。
问题事实和规划实体属性
另外,OptaPlanner 需要知道它可能会更改哪些较小的实例,以及如何检索由 TimeTableConstraintProvider 计算的 Timeslot 和 Room 实例。
time lotList 和 roomList 字段具有 @ProblemFactCollectionProperty 注释,因此您的 TimeTableConstraintProvider 可以从这些实例中进行选择。
lessonList 有一个 @PlanningEntityCollectionProperty 注解,因此 OptaPlanner 可在解决过程中更改它们,并且您的 TimeTableConstraintProvider 也可从那些中选择。
10.5. 创建 solver 服务 复制链接链接已复制到粘贴板!
解决 REST 线程中的规划问题会导致 HTTP 超时问题。因此,Quarkus 扩展注入 SolverManager,它会在单独的线程池中运行 solvers,并可并行处理多个数据集。
流程
创建 src/main/java/org/acme/optaplanner/rest/TimeTableResource.java 类:
此初始实施会等待解决方法完成,这仍然可能导致 HTTP 超时。完整的实现可避免 HTTP 超时更明确。
10.6. 设置解决者终止时间 复制链接链接已复制到粘贴板!
如果您的计划应用程序没有终止设置或终止事件,它理论上会永久运行,并最终会导致 HTTP 超时错误。要防止这种情况发生,请使用 optaplanner.solver.termination.spent-limit 参数指定应用程序终止的时间长度。在大多数应用程序中,将时间至少设置为五分钟(5m)。但是,在 Timetable 示例中,将解决时间限制为 5 秒,这不足以避免 HTTP 超时。
流程
使用以下内容创建 src/main/resources/application.properties 文件:
quarkus.optaplanner.solver.termination.spent-limit=5s
quarkus.optaplanner.solver.termination.spent-limit=5s
10.7. 运行 school timetable 应用程序 复制链接链接已复制到粘贴板!
创建了 school timetable 项目后,以开发模式运行它。在开发模式中,您可以在应用程序运行时更新应用程序源和配置。您的更改将显示在正在运行的应用程序中。
先决条件
- 您已创建了 school timetable 项目。
流程
要在开发模式中编译应用程序,请从项目目录中输入以下命令:
./mvnw compile quarkus:dev
./mvnw compile quarkus:devCopy to Clipboard Copied! Toggle word wrap Toggle overflow 测试 REST 服务。您可以使用任何 REST 客户端。以下示例使用 Linux 命令
curl发送 POST 请求:curl -i -X POST http://localhost:8080/timeTable/solve -H "Content-Type:application/json" -d '{"timeslotList":[{"dayOfWeek":"MONDAY","startTime":"08:30:00","endTime":"09:30:00"},{"dayOfWeek":"MONDAY","startTime":"09:30:00","endTime":"10:30:00"}],"roomList":[{"name":"Room A"},{"name":"Room B"}],"lessonList":[{"id":1,"subject":"Math","teacher":"A. Turing","studentGroup":"9th grade"},{"id":2,"subject":"Chemistry","teacher":"M. Curie","studentGroup":"9th grade"},{"id":3,"subject":"French","teacher":"M. Curie","studentGroup":"10th grade"},{"id":4,"subject":"History","teacher":"I. Jones","studentGroup":"10th grade"}]}'$ curl -i -X POST http://localhost:8080/timeTable/solve -H "Content-Type:application/json" -d '{"timeslotList":[{"dayOfWeek":"MONDAY","startTime":"08:30:00","endTime":"09:30:00"},{"dayOfWeek":"MONDAY","startTime":"09:30:00","endTime":"10:30:00"}],"roomList":[{"name":"Room A"},{"name":"Room B"}],"lessonList":[{"id":1,"subject":"Math","teacher":"A. Turing","studentGroup":"9th grade"},{"id":2,"subject":"Chemistry","teacher":"M. Curie","studentGroup":"9th grade"},{"id":3,"subject":"French","teacher":"M. Curie","studentGroup":"10th grade"},{"id":4,"subject":"History","teacher":"I. Jones","studentGroup":"10th grade"}]}'Copy to Clipboard Copied! Toggle word wrap Toggle overflow 在在
application.properties文件中指定的时间被终止后,服务会返回类似以下示例的输出:HTTP/1.1 200 Content-Type: application/json ... {"timeslotList":...,"roomList":...,"lessonList":[{"id":1,"subject":"Math","teacher":"A. Turing","studentGroup":"9th grade","timeslot":{"dayOfWeek":"MONDAY","startTime":"08:30:00","endTime":"09:30:00"},"room":{"name":"Room A"}},{"id":2,"subject":"Chemistry","teacher":"M. Curie","studentGroup":"9th grade","timeslot":{"dayOfWeek":"MONDAY","startTime":"09:30:00","endTime":"10:30:00"},"room":{"name":"Room A"}},{"id":3,"subject":"French","teacher":"M. Curie","studentGroup":"10th grade","timeslot":{"dayOfWeek":"MONDAY","startTime":"08:30:00","endTime":"09:30:00"},"room":{"name":"Room B"}},{"id":4,"subject":"History","teacher":"I. Jones","studentGroup":"10th grade","timeslot":{"dayOfWeek":"MONDAY","startTime":"09:30:00","endTime":"10:30:00"},"room":{"name":"Room B"}}],"score":"0hard/0soft"}HTTP/1.1 200 Content-Type: application/json ... {"timeslotList":...,"roomList":...,"lessonList":[{"id":1,"subject":"Math","teacher":"A. Turing","studentGroup":"9th grade","timeslot":{"dayOfWeek":"MONDAY","startTime":"08:30:00","endTime":"09:30:00"},"room":{"name":"Room A"}},{"id":2,"subject":"Chemistry","teacher":"M. Curie","studentGroup":"9th grade","timeslot":{"dayOfWeek":"MONDAY","startTime":"09:30:00","endTime":"10:30:00"},"room":{"name":"Room A"}},{"id":3,"subject":"French","teacher":"M. Curie","studentGroup":"10th grade","timeslot":{"dayOfWeek":"MONDAY","startTime":"08:30:00","endTime":"09:30:00"},"room":{"name":"Room B"}},{"id":4,"subject":"History","teacher":"I. Jones","studentGroup":"10th grade","timeslot":{"dayOfWeek":"MONDAY","startTime":"09:30:00","endTime":"10:30:00"},"room":{"name":"Room B"}}],"score":"0hard/0soft"}Copy to Clipboard Copied! Toggle word wrap Toggle overflow 请注意,您的应用程序将全部四节课时分配给两个时间段之一,另一个是两个房间。另请注意,它符合所有硬约束。例如,M. Curie 的两节课时间不同。
要在解决时间内查看 OptaPlanner 的作用,请查看服务器端的信息日志。以下是 info 日志输出示例:
... Solving started: time spent (33), best score (-8init/0hard/0soft), environment mode (REPRODUCIBLE), random (JDK with seed 0). ... Construction Heuristic phase (0) ended: time spent (73), best score (0hard/0soft), score calculation speed (459/sec), step total (4). ... Local Search phase (1) ended: time spent (5000), best score (0hard/0soft), score calculation speed (28949/sec), step total (28398). ... Solving ended: time spent (5000), best score (0hard/0soft), score calculation speed (28524/sec), phase total (2), environment mode (REPRODUCIBLE).
... Solving started: time spent (33), best score (-8init/0hard/0soft), environment mode (REPRODUCIBLE), random (JDK with seed 0). ... Construction Heuristic phase (0) ended: time spent (73), best score (0hard/0soft), score calculation speed (459/sec), step total (4). ... Local Search phase (1) ended: time spent (5000), best score (0hard/0soft), score calculation speed (28949/sec), step total (28398). ... Solving ended: time spent (5000), best score (0hard/0soft), score calculation speed (28524/sec), phase total (2), environment mode (REPRODUCIBLE).Copy to Clipboard Copied! Toggle word wrap Toggle overflow
10.8. 测试应用程序 复制链接链接已复制到粘贴板!
良好的应用程序包括测试覆盖。测试您时间项目中的限制和解决方法。
10.8.1. 测试 school timetable 约束 复制链接链接已复制到粘贴板!
要测试隔离中可时间项目的每个约束,请在单元测试中使用 ConstraintVerifier。这会测试每个约束的基点,与其他测试隔离开来,这在添加新限制时降低了维护。
此测试会验证约束 TimeTableConstraintProvider::roomConflict 在同一房间,且两节课中的课程具有相同的 timeslot,具有匹配权重 1。因此,如果约束 weight 为 10 个 位,它将分数减少 -10hard。
流程
创建 src/test/java/org/acme/optaplanner/solver/TimeTableConstraintProviderTest.java 类:
请注意,ConstraintVerifier 在测试过程中如何忽略约束权重,即使这些约束 weight 在 ConstraintProvider 中是硬编码的。这是因为约束权重在进行生产环境前定期有所改变。这样,约束加权调整不会破坏单元测试。
10.8.2. 测试 school timetable solver 复制链接链接已复制到粘贴板!
这个示例在 Red Hat build of Quarkus 上测试红帽构建的 OptaPlanner school timetable 项目。它使用 JUnit 测试来生成测试数据集,并将其发送到 TimeTableController 以解决。
流程
使用以下内容创建
src/test/java/com/example/rest/TimeTableResourceTest.java类:Copy to Clipboard Copied! Toggle word wrap Toggle overflow 此测试会验证在解决后,所有课程都会分配给一个时间窗和房间。它还会验证它是否发现可行的解决方案(没有硬限制)。
在
src/main/resources/application.properties文件中添加 test 属性:Copy to Clipboard Copied! Toggle word wrap Toggle overflow
通常,该解决方法在 200 毫秒内找到可行的解决方案。注意 application.properties 文件如何在测试过程中覆盖解决程序终止,以便在找到可行的解决方案 (0hard/*soft) 时立即终止。这可避免硬编码解决程序时间,因为单元测试可能会在任意硬件上运行。这种方法可确保测试用时足够长来查找可行的解决方案,即使在较慢的系统上也是如此。但是,即使在快速系统中,它也无法运行时间比严格要长的时间更长。
10.9. 日志记录 复制链接链接已复制到粘贴板!
完成 OptaPlanner school 时间项目的红帽构建后,您可以使用日志信息帮助您微调 ConstraintProvider 中的限制。查看 info 日志文件中的分数计算速度,以评估对您的限制更改的影响。以 debug 模式运行应用程序,显示应用程序接受的每个步骤,或使用 trace 日志记录记录每一步和每次移动。
流程
- 运行 school timetable 应用以获得固定时间,如五分钟。
查看
日志文件中的分数计算速度,如下例所示:... Solving ended: ..., score calculation speed (29455/sec), ...
... Solving ended: ..., score calculation speed (29455/sec), ...Copy to Clipboard Copied! Toggle word wrap Toggle overflow -
更改约束,再次运行计划应用程序相同的时间,并查看
日志文件中记录的分数计算速度。 以 debug 模式运行应用程序,以记录应用程序所做的每个步骤:
-
要从命令行运行调试模式,请使用
-D系统属性。 要永久启用调试模式,请在
application.properties文件中添加以下行:quarkus.log.category."org.optaplanner".level=debug
quarkus.log.category."org.optaplanner".level=debugCopy to Clipboard Copied! Toggle word wrap Toggle overflow 以下示例显示了在 debug 模式下
日志文件的输出:... Solving started: time spent (67), best score (-20init/0hard/0soft), environment mode (REPRODUCIBLE), random (JDK with seed 0). ... CH step (0), time spent (128), score (-18init/0hard/0soft), selected move count (15), picked move ([Math(101) {null -> Room A}, Math(101) {null -> MONDAY 08:30}]). ... CH step (1), time spent (145), score (-16init/0hard/0soft), selected move count (15), picked move ([Physics(102) {null -> Room A}, Physics(102) {null -> MONDAY 09:30}]). ...... Solving started: time spent (67), best score (-20init/0hard/0soft), environment mode (REPRODUCIBLE), random (JDK with seed 0). ... CH step (0), time spent (128), score (-18init/0hard/0soft), selected move count (15), picked move ([Math(101) {null -> Room A}, Math(101) {null -> MONDAY 08:30}]). ... CH step (1), time spent (145), score (-16init/0hard/0soft), selected move count (15), picked move ([Physics(102) {null -> Room A}, Physics(102) {null -> MONDAY 09:30}]). ...Copy to Clipboard Copied! Toggle word wrap Toggle overflow
-
要从命令行运行调试模式,请使用
-
使用
tracelogging 显示每个步骤以及每个步骤的每一个移动。
10.10. 将数据库与您的 Quarkus OptaPlanner school 时间相集成 复制链接链接已复制到粘贴板!
创建 Quarkus OptaPlanner school 时间应用程序后,您可以将其与数据库集成,并创建一个基于 Web 的用户界面来显示时间稳定。
先决条件
- 您有一个 Quarkus OptaPlanner school timetable 应用程序。
流程
-
使用 Hibernate 和 Panache 将
Timeslot、Room和Lesson实例存储在数据库中。如需更多信息,请参阅使用 Panache 的简化 Hibernate ORM。 - 通过 REST 公开实例。如需更多信息,请参阅 编写 JSON REST 服务。
更新
类,以读取和写入一个事务中的可时间实例:TimeTableResourceCopy to Clipboard Copied! Toggle word wrap Toggle overflow 这个示例包含一个可
时间实例。但是,您可以并行为多个学校启用多租户和处理定时实例。getTimeTable()方法返回来自数据库的最新时间表。它使用自动注入的ScoreManager方法来计算该时间表的分数,并使其可用于 UI。solve()方法启动一个作业,以解决当前可时间表,并将时间窗和房分配存储在数据库中。它使用SolverManager.solveAndListen()方法侦听中间最佳解决方案,并相应地更新数据库。UI 使用它来显示后端仍在解决过程中的进度。更新
TimeTableResourceTest类,以反映solve()方法立即返回并轮询最新的解决方案,直到解决解决为止:Copy to Clipboard Copied! Toggle word wrap Toggle overflow - 在这些 REST 方法上构建 Web UI,以提供时间表的视觉表示。
- 查看 快速入门源代码。
OptaPlanner 通过 Micrometer 公开指标数据,这是 Java 应用程序的指标检测库。您可以将 Micrometer 与 Prometheus 搭配使用以监控 school timetable 应用程序中 OptaPlanner solver。
先决条件
- 您已创建了 Quarkus OptaPlanner school timetable 应用程序。
- 已安装 Prometheus。有关安装 Prometheus 的详情,请查看 Prometheus 网站。
流程
将 Micrometer Prometheus 依赖项添加到 school timetable
pom.xml文件中:<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-micrometer-registry-prometheus</artifactId> </dependency>
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-micrometer-registry-prometheus</artifactId> </dependency>Copy to Clipboard Copied! Toggle word wrap Toggle overflow 启动 school timetable 应用程序:
mvn compile quarkus:dev
mvn compile quarkus:devCopy to Clipboard Copied! Toggle word wrap Toggle overflow -
在 Web 浏览器中打开
http://localhost:8080/q/metric。
您可以使用 OptaPlanner vaccination appointment 调度程序快速启动,以开发一个有效和公平的 vaccination 调度。vaccination appointment 调度程序使用 artificial 智能(AI)来根据多个限制和优先级排列人并分配时间插槽。
先决条件
- 安装了 OpenJDK 11 或更高版本。红帽构建的 Open JDK 可从红帽客户门户网站的 Software Downloads 页面获取(需要登录)。
- 已安装 Apache Maven 3.6 或更高版本。Maven 可以从 Apache Maven Project 网站获得。
- 使用 IntelliJ IDEA、VS Code、Eclipse 或 NetBeans 等 IDE。
- 您已创建了 Quakus OptaPlanner 项目,如 第 6 章 OptaPlanner 和 Quarkus 入门 所述。
11.1. OptaPlanner vaccination appointment 调度程序如何工作 复制链接链接已复制到粘贴板!
调度 appointments 的方法主要有两种。系统可以允许个人选择一个选项插槽(用户选择)或者系统分配一个插槽并告知个人何时和位置参加(系统自动自动分配分配)。OptaPlanner vaccination appointment 调度程序使用 system-automatically-assigns 方法。通过 OptaPlanner vaccination appointment 调度程序,您可以创建一个应用程序,其中人们为系统提供信息,系统会分配一个 appointment。
这个方法的特性:
- appointment 插槽根据优先级分配。
- 系统根据预配置的规划限制分配最佳的 appointment 时间和位置。
- 该系统不再被大量用户对受制约数量有限。
这个方法可以通过使用计划限制为每个人创建分数来解决 vaccinating 的问题。个人的分数决定何时获得指示。个人得分越大,他们收到之前的发言的机会越大。
11.1.1. OptaPlanner vaccination appointment 调度程序限制 复制链接链接已复制到粘贴板!
OptaPlanner vaccination appointment 调度程序限制可以是 hard、medium 或 soft:
硬约束不能被破坏。如果有任何硬约束被破坏,则计划不可行,且无法执行:
- 容量:在任何位置,不要随时通过手册 vaccine 容量。
- Vaccine 最大年龄:如果 vaccine 具有最长期限,则不像首次进行的 vaccine 最长期限更久时对其进行管理,而不是 vaccine 最长期限。确保为人赋予一个适合其年龄的 vaccine 类型。例如,不要为 vaccine 分配 75 年旧个人,其最长期限限制为 65 年。
- 所需的 vaccine type:使用所需的 vaccine 类型。例如,vaccine 的第二代的 vaccine 类型必须与第一个 dose 相同。
- 就绪日期:管理指定日期或之后的 vaccine。例如,如果某个人收到第二个操作,则在建议尽快进行 vaccine 类型之前,不要对其进行管理,例如在第一个操作后 26 天。
- 到期日期:管理指定日期之前或之前的 vaccine。例如,如果某个人收到第二个操作,请在推荐的 vaccination 最终因特定 vaccine 日期前对其进行管理,例如在第一个操作后的三个月。
- 限制最大差距离:将每个人分配给最接近其一组 vaccination 中心之一。这通常是三个数据中心中的一个。这种限制的计算时间是差差时间,而不是距离距离,因此那些在维系地区持有的人通常比农区的差时限度要低。
Medium 约束决定,当没有足够容量为每个人分配点时,他们不会得到指出。这被称为约束规划:
- schedule second dose vaccinations:不要使任何第二个确实 vaccination 没有被分配,除非理想的日期不在计划窗口之外。
- 根据优先级评级计划人员:每个人具有优先级评级。这通常是其年龄,但如果它们来说,这要高得多,例如一个健康护程序。只有具有最低优先级的人们才是未分配的。在下次运行时将考虑它们。这个约束比上一个约束的软限制,因为第二个操作总是优先级在优先级评级上。
软限制不应该有问题:
- 首选 vaccination 中心:如果一个人拥有首选的 vaccination 中心,请在该中心给出一个选择点。
- 距离:让人必须转入其分配的 vaccination 中心的距离。
- 理想日期:管理 vaccine on 或接近指定日期。例如,如果某个人收到第二个操作,则根据特定 vaccine 的基本日期对其进行管理,例如在第 28 天之后。这个约束比距离约束的软,以避免在国家间发送一半,使其更接近其理想的日期。
- 优先级评级:在规划窗口之前调度优先级较高的人。这个约束比距离约束的软,以避免在国家之间发送一半。这个约束也比理想日期约束的软,因为第二个操作的优先级高于优先级评级。
硬约束的加法是相对于其他硬约束的权重。软限制会高于其他软限制。但是,硬约束总是优先于中和软限制。如果硬约束被破坏,则计划不可行。但是,如果没有硬的限制,则考虑软限制和中等限制来确定优先级。因为只有更多的人会超过可用代表的插槽,所以您必须优先选择。第二个确实需要首先被分配,以避免在以后创建不堪重负您的系统的积压。之后,会根据优先级的等级分配用户。每个人都从优先级评级开始,是其年龄。这样做会优先考虑旧人,而非年轻的人。之后,处于特定优先级组的人接收,例如几百个额外的点。这与其组的优先级不同。例如,nurses 可能会接收额外的 1000 点。这样,旧的 nurses 的优先级高于年轻的 nurses,而年轻的 nurses 则高于非 nurses 的人。下表演示了这个概念:
| age | 作业 | 优先级评级 |
|---|---|---|
| 60 | nurse | 1060 |
| 33 | nurse | 1033 |
| 71 | 弃用 | 71 |
| 52 | 办公室 worker | 52 |
11.1.2. OptaPlanner solver 复制链接链接已复制到粘贴板!
OptaPlanner 的核心是解决方法,引擎用于提取问题数据集并覆盖规划限制和配置。问题数据集包含人们、vaccines 和 vaccination 中心的所有信息。解决器通过各种数据组合进行工作,最终决定一个经过优化的指示计划,与分配给特定中心的 vaccination appointments 的人员进行优化。以下示例显示一个临时解决方案创建的调度:
11.1.3. 持续规划 复制链接链接已复制到粘贴板!
持续计划是同时管理一个或多个即将举办的规划期的技术,并经常重复该过程每月、每周、每天、每小时或更频繁地重复该过程。计划窗口按指定间隔递增。下图显示了两周计划窗口,每天更新:
两周规划窗口被分为一半。第一周处于已发布的状态,第二周处于草案状态。人们被分配到计划窗口发布和部分的发言。但是,只有计划窗口发布的一部分的用户才会获得其发言通知。其他的 appointments 仍可在下一次运行时轻松更改。这样做可让 OptaPlanner 灵活地在再次运行问题时更改草案部分中的提示(如果需要)。例如,如果一个需要第二个操作的人准备了星期一,OptaPlanner 认为是星期三的准备日期,如果您可以证明 OptaPlanner,则可以为星期一给出一个草案。
您可以确定规划窗口的大小,但只了解问题空间的大小。问题空间就是创建计划的所有元素。您提前计划的时间越大,问题就越大。
11.1.4. 固定计划实体 复制链接链接已复制到粘贴板!
如果您每天持续规划,在两个周的时间内将出现指出,该周期已分配给个人。要确保 appointments 没有双引号,OptaPlanner 将现有的 appointments 标记为固定值分配。固定( pinning)用于定位一个或多个特定的分配,并强制 OptaPlanner 围绕这些固定分配进行调度。固定规划实体(如 appointment)在解决过程中不会改变。
某个实体是固定还是不由 appointment 状态决定。appointment 可以具有五个状态: Open、Invited、Accepted、Rejected 或 Rescheduled。
您实际上不会在快速启动演示代码中直接看到这些状态,因为 OptaPlanner 引擎只对 appointment 是否固定。
您需要能够规划已调度的 appointments。固定具有 Invited 或 Accepted 状态的 appointment。没有固定状态,带有 Open、Reschedule 和 Rejected 状态没有固定状态,并可用于调度。
在这个示例中,当解决器在已发布和草案范围内搜索整个两周计划窗口时。除非计划的输入数据外,解决者还考虑了任何未固定实体、Open、Reschedule 或 Rejected 状态的任何未固定实体。如果每天运行解决方法,您会在运行解决者之前看到在计划中添加的新日期。
请注意,新天的 appointments 已被分配,在计划窗口的草案中调度了 Amy 和 Edna。这是因为 Gus 和 Hugo 请求重新调度。这不会造成任何混淆,因为 Amy 和 Edna 不会被通知其草案日期。现在,由于他们在规划窗口的已发布部分有指出,因此将收到通知并要求接受或拒绝其提示,并且现在固定了。
11.2. 下载并运行 OptaPlanner vaccination appointment 调度程序 复制链接链接已复制到粘贴板!
下载 OptaPlanner vaccination appointment 调度程序快速启动存档,以 Quarkus 开发模式启动存档,并在浏览器中查看应用程序。Quarkus 开发模式允许您在运行过程中进行更改和更新应用程序。
流程
进入红帽客户门户网站中的 Software Downloads 页面(需要登录),然后从下拉列表中选择产品和版本:
- 产品: 流程自动化管理器
- 版本: 7.13.4
-
下载 Red Hat Process Automation Manager 7.13.4 Kogito 和 OptaPlanner 8 Decision Services Quickstarts (
rhpam-7.13.4-kogito-and-optaplanner-quickstarts.zip)。 -
提取
rhpam-7.13.4-kogito-and-optaplanner-quickstarts.zip文件。 -
导航到
optaplanner-quickstarts-8.13.0.Final-redhat-00013目录。 -
导航到
optaplanner-quickstarts-8.13.0.Final-redhat-00013/use-cases/vaccination-scheduling目录。 输入以下命令在开发模式中启动 OptaPlanner vaccination appointment 调度程序:
mvn quarkus:dev
$ mvn quarkus:devCopy to Clipboard Copied! Toggle word wrap Toggle overflow 要查看 OptaPlanner vaccination appointment 调度程序,在 web 浏览器中打开以下 URL。
http://localhost:8080/
http://localhost:8080/Copy to Clipboard Copied! Toggle word wrap Toggle overflow - 要运行 OptaPlanner vaccination appointment 调度程序,点 Solve。
- 对源代码进行更改,然后按 F5 键刷新浏览器。请注意,您所做的更改现已可用。
11.3. 软件包并运行 OptaPlanner vaccination appointment 调度程序 复制链接链接已复制到粘贴板!
当您完成了对 OptaPlanner vaccination appointment 调度程序的开发工作后,以 quarkus:dev 模式运行应用程序,作为传统的 jar 文件运行。
先决条件
- 您已下载了 OptaPlanner vaccination appointment 调度程序快速启动。更多信息请参阅 第 11.2 节 “下载并运行 OptaPlanner vaccination appointment 调度程序”。
流程
-
进入
/use-cases/vaccination-scheduling目录。 要编译 OptaPlanner vaccination appointment 调度程序,请输入以下命令:
mvn package
$ mvn packageCopy to Clipboard Copied! Toggle word wrap Toggle overflow 要运行编译的 OptaPlanner vaccination appointment 调度程序,请输入以下命令:
java -jar ./target/*-runner.jar
$ java -jar ./target/*-runner.jarCopy to Clipboard Copied! Toggle word wrap Toggle overflow 注意要在端口 8081 上运行应用程序,请在前面的命令中添加
-Dquarkus.http.port=8081。要启动 OptaPlanner vaccination appointment 调度程序,在 web 浏览器中打开以下 URL。
http://localhost:8080/
http://localhost:8080/Copy to Clipboard Copied! Toggle word wrap Toggle overflow
要利用 Quarkus 提供的小型内存占用和访问速度,在 Quarkus 原生模式下编译 OptaPlanner vaccination appointment 调度程序。
流程
-
安装 GraalVM 和
native-image工具。如需更多信息,请参阅 Quarkus 网站上 配置 GraalVMl。 -
进入
/use-cases/vaccination-scheduling目录。 要原生编译 OptaPlanner vaccination appointment 调度程序,请输入以下命令:
mvn package -Dnative -DskipTests
$ mvn package -Dnative -DskipTestsCopy to Clipboard Copied! Toggle word wrap Toggle overflow 要运行原生可执行文件,请输入以下命令:
./target/*-runner
$ ./target/*-runnerCopy to Clipboard Copied! Toggle word wrap Toggle overflow 要启动 OptaPlanner vaccination appointment 调度程序,在 web 浏览器中打开以下 URL。
http://localhost:8080/
http://localhost:8080/Copy to Clipboard Copied! Toggle word wrap Toggle overflow
本指南指导您了解使用 OptaPlanner 的约束解决 artificial 智能(AI)的 Spring Boot 应用程序的过程。您将构建一个 REST 应用程序,针对学生和老师优化院校时间。
您的服务会自动将 Lesson 实例分配给 Timeslot 和 Room 实例,方法是使用 AI 遵循以下硬和软 调度限制 :
- 个空间最多可以同时有一处。
- 老师可以同时在上课时进行教学。
- 学员最多可以同时参加。
- 教授者更倾向于在单一房间学习。
- 指导者更倾向于在课间学习顺序课时和不类差距。
数学会上讲,该学校的时间范围是一个 NP-hard 问题。这意味着很难扩展。简单地说,通过带有 brute 强制的可能组合将花费数百万年时间,而在超级计算机中,甚至在超级计算机中也要花费数百万年时间。幸运的是,AI 约束解决诸如 OptaPlanner 等高级算法具有在合理的时间内提供最接近的解决方案。被视为有合理的时间,它取决于问题的目标。
先决条件
- 安装了 OpenJDK 11 或更高版本。红帽构建的 Open JDK 可从红帽客户门户网站的 Software Downloads 页面获取(需要登录)。
- 已安装 Apache Maven 3.6 或更高版本。Maven 可以从 Apache Maven Project 网站获得。
- 使用 IntelliJ IDEA、VS Code、Eclipse 或 NetBeans 等 IDE。
12.1. 下载并构建 Spring Boot school 快速入门 复制链接链接已复制到粘贴板!
要查看红帽使用 Spring Boot 产品构建 OptaPlanner 的 school 时间项目的已完成示例,请从红帽客户门户网站下载入门应用程序。
流程
进入红帽客户门户网站中的 Software Downloads 页面(需要登录),然后从下拉列表中选择产品和版本:
- 产品: 流程自动化管理器
- 版本: 7.13.4
-
下载 Red Hat Process Automation Manager 7.13.4 Kogito 和 OptaPlanner 8 Decision Services Quickstarts (
rhpam-7.13.4-kogito-and-optaplanner-quickstarts.zip)。 -
提取
rhpam-7.13.4-kogito-and-optaplanner-quickstarts.zip文件。 -
下载 Red Hat Process Automation Manager 7.13.4 Kogito 和 OptaPlanner 8 Decision Services Maven Repositroy (
rhpam-7.13.4-kogito-maven-repository.zip)。 -
提取
rhpam-7.13.4-kogito-maven-repository.zip文件。 -
将
rhpam-7.13.4-kogito-maven-repository/maven-repository子目录的内容复制到~/.m2/repository目录中。 -
导航到
optaplanner-quickstarts-8.13.0.Final-redhat-00013/ Technology/java-spring-boot目录。 输入以下命令来构建 Spring Boot school timetabling 项目:
mvn clean install -DskipTests
mvn clean install -DskipTestsCopy to Clipboard Copied! Toggle word wrap Toggle overflow 要构建 Spring Boot school timetabling 项目,请输入以下命令:
mvn spring-boot:run -DskipTests
mvn spring-boot:run -DskipTestsCopy to Clipboard Copied! Toggle word wrap Toggle overflow 要查看项目,在网页浏览器中输入以下 URL:
http://localhost:8080/
http://localhost:8080/Copy to Clipboard Copied! Toggle word wrap Toggle overflow
12.2. 对域对象建模 复制链接链接已复制到粘贴板!
红帽构建的 OptaPlanner 时间表项目的目标是将每节分配给一个时间段和一个房间。为此,请添加三个类: Timeslot、Lesson 和 Room,如下图所示:
Timeslot
Timeslot 类表示在未教授课程的时间间隔,例如: Monday 10:30 - 11:30 或 Tuesday 13:30 - 14:30 - 14:30。在这个示例中,所有时间插槽都有相同的持续时间,且在 lunch 或其他中断期间没有时间插槽。
时间段没有日期,因为高校的计划只每周重复。无法进行持续 规划。次性被称为 问题事实,因为在解决过程中不会有定时实例变化。此类类不需要任何特定于 OptaPlanner 的注解。
room
Room 类代表了课程讲授的方式,例如 Room A 或 Room B。在本例中,所有空间都没有容量限制,它们可容纳所有课时。
房间 实例在解决过程中不会改变,因此 Room 也是 问题。
lesson
在课时,由课课课表示,向班级教授一个主题,例如: A.Turing for a 9th grade 或 Chemistry by M.Curie 为 10th grade。如果一个主题每周都会为同一学员组讲授多次,则有多个小的、只能由 id 区分的实例。例如,第 9 分之时,每周有 6 个数学。
在寻求过程中,OptaPlanner 改变了 lesson 类的 timelot 和 room 字段,从而将每个较小的时间分配给一个时间段和一个房间。 因为 OptaPlanner 更改了这些字段,所以 lesson 是一个 规划实体 :
上图中的大多数字段包含输入数据,但 orange 字段除外。在输入数据中 未分配的 时间和房间 字段(null),并在输出数据中分配(而不是 null)。OptaPlanner 在解决过程中更改这些字段。这些字段称为规划变量。为了 OptaPlanner 可以识别它们,timelot 和 room 字段都需要 @PlanningVariable 注解。它们包含类( Lesson )需要 @PlanningEntity 注释。
流程
创建
src/main/java/com/example/domain/Timeslot.java类:Copy to Clipboard Copied! Toggle word wrap Toggle overflow 请注意,
toString()方法保留了输出短片,以便可以更轻松地读取 OptaPlanner 的DEBUG或 abrt 日志,如稍后所示。创建
src/main/java/com/example/domain/Room.java类:Copy to Clipboard Copied! Toggle word wrap Toggle overflow 创建
src/main/java/com/example/domain/Lesson.java类:Copy to Clipboard Copied! Toggle word wrap Toggle overflow Lesson类具有@PlanningEntity注释,因此 OptaPlanner 知道这个类在解决过程中发生了变化,因为它包含一个或多个计划变量。time
lot字段具有@PlanningVariable注释,因此 OptaPlanner 知道它可以更改其值。为了查找要分配给此字段的潜在的Timeslot实例,OptaPlanner 使用valueRangeProviderRefs属性连接到一个提供List<Timeslot>的值供应商。有关值范围供应商的信息,请参阅 第 12.4 节 “在规划解决方案中收集域对象”。出于同样原因,
room字段还具有@PlanningVariable注释。
12.3. 定义限制并计算分数 复制链接链接已复制到粘贴板!
当解决问题时,分数 代表特定解决方案的质量。得分越高。红帽构建的 OptaPlanner 寻找最佳解决方案,这是可用时间获得最高分数的解决方案。这可能是 最佳解决方案。
因为可时间的示例用例具有硬和软约束,因此请使用 HardSoftScore 类来代表分数:
- 硬限制不得被破坏。例如: 一个房间可以有以上时间。
- 软限制不应该被破坏。例如: 更喜欢在单个房里的教学方式。
硬约束的加法是相对于其他硬约束的权重。软限制会高于其他软限制。硬限制始终大于软限制,无论其对应的权重如何。
要计算分数,您可以实施 EasyScoreCalculator 类:
不幸的是,这个解决方案无法进行很好的扩展,因为它是非增量:每次分配给不同的时间段或房间,都会重新评估所有课程来计算新分数。
更好的解决方法是创建一个 src/main/java/com/example/solver/TimeTableConstraintProvider.java 类来执行增量分数计算。此类使用 OptaPlanner 的 ConstraintStream API,它由 Java 8 Streams 和 SQL 产生。ConstraintProvider 缩放比 EasyScoreCalculator 更好的度量:O(n)而不是 O(n)。
流程
创建以下 src/main/java/com/example/solver/TimeTableConstraintProvider.java 类:
12.4. 在规划解决方案中收集域对象 复制链接链接已复制到粘贴板!
TimeTable 实例嵌套了单个 dataset 的所有 Timeslot、Room 和 Lesson 实例。另外,由于它包含了 allon,每个都具有特定规划变量状态,所以它是一个 规划解决方案,并有分数:
-
如果课程仍然没有被签名,那么它是一个 未初始化 的解决方案,例如分数为
-4init/0hard/0soft的解决方案。 -
如果损坏硬限制,那么它是一种 不可预见的 解决方案,例如分数为
-2hard/-3soft的解决方案。 -
如果遵循所有硬约束,那么它是一个可行的解决方案,例如,分数为
0hard/-7soft的解决方案。
TimeTable 类有一个 @PlanningSolution 注释,因此红帽构建的 OptaPlanner 知道该类包含所有输入和输出数据。
具体来说,这个类是问题的输入:
带有所有时间窗的
timeslotList字段- 这是问题事实列表,因为它们在解决过程中不会改变。
包含所有房间有一个
roomList字段- 这是问题事实列表,因为它们在解决过程中不会改变。
带有所有节节的
lessonList字段- 这是计划实体列表,因为他们在解决过程中发生了变化。
在每个课
中:-
timeslot和room字段的值通常为null,因此取消分配。它们是规划变量。 -
其他字段(如
主题、Squillr 和studentGroup)已被填写。这些字段是问题属性。
-
但是,这个类也是解决方案的输出:
-
每个
lessonList字段在解决后都有非 nulltimelot 和room字段 -
代表输出解决方案质量
的分数字段,如0hard/-5soft
流程
创建 src/main/java/com/example/domain/TimeTable.java 类:
值范围供应商
timeslotList 字段是一个值范围 provider。它拥有 OptaPlanner 可以从中选择的 Timeslot 实例,分配给 lesson 实例的 timeslot 字段。time lotList 字段具有 @ValueRangeProvider 注释,可通过在 Lesson 中将 id 与 @PlanningVariable 的 valueRangeProviderRefs 匹配。
遵循相同的逻辑,roomList 字段也有 @ValueRangeProvider 注释。
问题事实和规划实体属性
另外,OptaPlanner 需要知道它可能会更改哪些较小的实例,以及如何检索由 TimeTableConstraintProvider 计算的 Timeslot 和 Room 实例。
time lotList 和 roomList 字段具有 @ProblemFactCollectionProperty 注释,因此您的 TimeTableConstraintProvider 可以从这些实例中进行选择。
lessonList 有一个 @PlanningEntityCollectionProperty 注解,因此 OptaPlanner 可在解决过程中更改它们,并且您的 TimeTableConstraintProvider 也可从那些中选择。
12.5. 创建 Timetable 服务 复制链接链接已复制到粘贴板!
现在,您已准备好将所有内容放在一起并创建 REST 服务。但解决 REST 线程中的规划问题会导致 HTTP 超时问题。因此,Spring Boot 启动程序注入 SolverManager,它将在一个单独的线程池中运行 solvers,并可以并行解决多个数据集。
流程
创建 src/main/java/com/example/solver/TimeTableController.java 类:
在本例中,初始实现会等待解决方法完成,这仍然可能导致 HTTP 超时。完整的 实现可避免 HTTP 超时更明确。
12.6. 设置解决者终止时间 复制链接链接已复制到粘贴板!
如果您的计划应用程序没有终止设置或终止事件,它理论上会永久运行,并最终会导致 HTTP 超时错误。要防止这种情况发生,请使用 optaplanner.solver.termination.spent-limit 参数指定应用程序终止的时间长度。在大多数应用程序中,将时间至少设置为五分钟(5m)。但是,在 Timetable 示例中,将解决时间限制为 5 秒,这不足以避免 HTTP 超时。
流程
使用以下内容创建 src/main/resources/application.properties 文件:
quarkus.optaplanner.solver.termination.spent-limit=5s
quarkus.optaplanner.solver.termination.spent-limit=5s
12.7. 使应用程序可执行 复制链接链接已复制到粘贴板!
完成 OptaPlanner Spring Boot timetable 项目的红帽构建后,将所有内容打包成由标准 Java main() 方法驱动的单个可执行 JAR 文件。
先决条件
- 您已完成了 OptaPlanner Spring Boot timetable 项目。
流程
使用以下内容创建
TimeTableSpringBootApp.java类:Copy to Clipboard Copied! Toggle word wrap Toggle overflow -
将 Spring Initializr 创建的
src/main/java/com/example/DemoApplication.java类替换为TimeTableSpringBootApp.java类。 -
将
TimeTableSpringBootApp.java类作为常规 Java 应用程序的主类运行。
12.7.1. 试用 timetable 应用程序 复制链接链接已复制到粘贴板!
启动红帽构建的 OptaPlanner Spring Boot timetable 应用程序后,您可以使用您想要的任何 REST 客户端测试 REST 服务。这个示例使用 Linux curl 命令发送 POST 请求。
先决条件
- OptaPlanner Spring Boot timetable 应用程序正在运行。
流程
使用以下命令:
curl -i -X POST http://localhost:8080/timeTable/solve -H "Content-Type:application/json" -d '{"timeslotList":[{"dayOfWeek":"MONDAY","startTime":"08:30:00","endTime":"09:30:00"},{"dayOfWeek":"MONDAY","startTime":"09:30:00","endTime":"10:30:00"}],"roomList":[{"name":"Room A"},{"name":"Room B"}],"lessonList":[{"id":1,"subject":"Math","teacher":"A. Turing","studentGroup":"9th grade"},{"id":2,"subject":"Chemistry","teacher":"M. Curie","studentGroup":"9th grade"},{"id":3,"subject":"French","teacher":"M. Curie","studentGroup":"10th grade"},{"id":4,"subject":"History","teacher":"I. Jones","studentGroup":"10th grade"}]}'
$ curl -i -X POST http://localhost:8080/timeTable/solve -H "Content-Type:application/json" -d '{"timeslotList":[{"dayOfWeek":"MONDAY","startTime":"08:30:00","endTime":"09:30:00"},{"dayOfWeek":"MONDAY","startTime":"09:30:00","endTime":"10:30:00"}],"roomList":[{"name":"Room A"},{"name":"Room B"}],"lessonList":[{"id":1,"subject":"Math","teacher":"A. Turing","studentGroup":"9th grade"},{"id":2,"subject":"Chemistry","teacher":"M. Curie","studentGroup":"9th grade"},{"id":3,"subject":"French","teacher":"M. Curie","studentGroup":"10th grade"},{"id":4,"subject":"History","teacher":"I. Jones","studentGroup":"10th grade"}]}'
大约 5 秒后,在 application.properties 中定义的终止时间花费了时间,服务会返回类似以下示例的输出:
HTTP/1.1 200
Content-Type: application/json
...
{"timeslotList":...,"roomList":...,"lessonList":[{"id":1,"subject":"Math","teacher":"A. Turing","studentGroup":"9th grade","timeslot":{"dayOfWeek":"MONDAY","startTime":"08:30:00","endTime":"09:30:00"},"room":{"name":"Room A"}},{"id":2,"subject":"Chemistry","teacher":"M. Curie","studentGroup":"9th grade","timeslot":{"dayOfWeek":"MONDAY","startTime":"09:30:00","endTime":"10:30:00"},"room":{"name":"Room A"}},{"id":3,"subject":"French","teacher":"M. Curie","studentGroup":"10th grade","timeslot":{"dayOfWeek":"MONDAY","startTime":"08:30:00","endTime":"09:30:00"},"room":{"name":"Room B"}},{"id":4,"subject":"History","teacher":"I. Jones","studentGroup":"10th grade","timeslot":{"dayOfWeek":"MONDAY","startTime":"09:30:00","endTime":"10:30:00"},"room":{"name":"Room B"}}],"score":"0hard/0soft"}
HTTP/1.1 200
Content-Type: application/json
...
{"timeslotList":...,"roomList":...,"lessonList":[{"id":1,"subject":"Math","teacher":"A. Turing","studentGroup":"9th grade","timeslot":{"dayOfWeek":"MONDAY","startTime":"08:30:00","endTime":"09:30:00"},"room":{"name":"Room A"}},{"id":2,"subject":"Chemistry","teacher":"M. Curie","studentGroup":"9th grade","timeslot":{"dayOfWeek":"MONDAY","startTime":"09:30:00","endTime":"10:30:00"},"room":{"name":"Room A"}},{"id":3,"subject":"French","teacher":"M. Curie","studentGroup":"10th grade","timeslot":{"dayOfWeek":"MONDAY","startTime":"08:30:00","endTime":"09:30:00"},"room":{"name":"Room B"}},{"id":4,"subject":"History","teacher":"I. Jones","studentGroup":"10th grade","timeslot":{"dayOfWeek":"MONDAY","startTime":"09:30:00","endTime":"10:30:00"},"room":{"name":"Room B"}}],"score":"0hard/0soft"}
请注意,应用程序被分配了这四个的时间插槽之一,以及两个房间的一个。另请注意,它符合所有硬约束。例如,M. Curie 的两节课时间不同。
在服务器端,info 日志显示在 5 秒内执行什么 OptaPlanner:
... Solving started: time spent (33), best score (-8init/0hard/0soft), environment mode (REPRODUCIBLE), random (JDK with seed 0). ... Construction Heuristic phase (0) ended: time spent (73), best score (0hard/0soft), score calculation speed (459/sec), step total (4). ... Local Search phase (1) ended: time spent (5000), best score (0hard/0soft), score calculation speed (28949/sec), step total (28398). ... Solving ended: time spent (5000), best score (0hard/0soft), score calculation speed (28524/sec), phase total (2), environment mode (REPRODUCIBLE).
... Solving started: time spent (33), best score (-8init/0hard/0soft), environment mode (REPRODUCIBLE), random (JDK with seed 0).
... Construction Heuristic phase (0) ended: time spent (73), best score (0hard/0soft), score calculation speed (459/sec), step total (4).
... Local Search phase (1) ended: time spent (5000), best score (0hard/0soft), score calculation speed (28949/sec), step total (28398).
... Solving ended: time spent (5000), best score (0hard/0soft), score calculation speed (28524/sec), phase total (2), environment mode (REPRODUCIBLE).
12.7.2. 测试应用 复制链接链接已复制到粘贴板!
良好的应用程序包括测试覆盖。这个示例测试了 OptaPlanner Spring Boot 应用程序的 Timetable Red Hat build。它使用 JUnit 测试来生成测试数据集,并将其发送到 TimeTableController 以解决。
流程
使用以下内容创建 src/test/java/com/example/solver/TimeTableControllerTest.java 类:
此测试会验证在解决后,所有课程都会分配给一个时间窗和房间。它还会验证它是否发现可行的解决方案(没有硬限制)。
通常,该解决方法在 200 毫秒内找到可行的解决方案。请注意,@SpringBootTest 注释 的属性 如何覆盖解决器终止,以便在找到可行的解决方案(0hard/*soft)时立即终止。这可避免硬编码解决程序时间,因为单元测试可能会在任意硬件上运行。这种方法可确保测试用时足够长来查找可行的解决方案,即使在较慢的系统上也是如此。但是,即使在快速系统中,它也无法运行时间比严格要长的时间更长。
12.7.3. 日志记录 复制链接链接已复制到粘贴板!
完成 OptaPlanner Spring Boot timetable 应用程序的构建后,您可以使用日志信息来帮助微调 ConstraintProvider 中的约束。查看 info 日志文件中的分数计算速度,以评估对您的限制更改的影响。以 debug 模式运行应用程序,显示应用程序接受的每个步骤,或使用 trace 日志记录记录每一步和每次移动。
流程
- 运行定时应用程序的时间,例如五分钟。
查看
日志文件中的分数计算速度,如下例所示:... Solving ended: ..., score calculation speed (29455/sec), ...
... Solving ended: ..., score calculation speed (29455/sec), ...Copy to Clipboard Copied! Toggle word wrap Toggle overflow -
更改约束,再次运行计划应用程序相同的时间,并查看
日志文件中记录的分数计算速度。 以 debug 模式运行应用程序以记录每个步骤:
-
要从命令行运行调试模式,请使用
-D系统属性。 要更改
application.properties文件中的日志记录,请在该文件中添加以下行:logging.level.org.optaplanner=debug
logging.level.org.optaplanner=debugCopy to Clipboard Copied! Toggle word wrap Toggle overflow 以下示例显示了在 debug 模式下
日志文件的输出:... Solving started: time spent (67), best score (-20init/0hard/0soft), environment mode (REPRODUCIBLE), random (JDK with seed 0). ... CH step (0), time spent (128), score (-18init/0hard/0soft), selected move count (15), picked move ([Math(101) {null -> Room A}, Math(101) {null -> MONDAY 08:30}]). ... CH step (1), time spent (145), score (-16init/0hard/0soft), selected move count (15), picked move ([Physics(102) {null -> Room A}, Physics(102) {null -> MONDAY 09:30}]). ...... Solving started: time spent (67), best score (-20init/0hard/0soft), environment mode (REPRODUCIBLE), random (JDK with seed 0). ... CH step (0), time spent (128), score (-18init/0hard/0soft), selected move count (15), picked move ([Math(101) {null -> Room A}, Math(101) {null -> MONDAY 08:30}]). ... CH step (1), time spent (145), score (-16init/0hard/0soft), selected move count (15), picked move ([Physics(102) {null -> Room A}, Physics(102) {null -> MONDAY 09:30}]). ...Copy to Clipboard Copied! Toggle word wrap Toggle overflow
-
要从命令行运行调试模式,请使用
-
使用
tracelogging 显示每个步骤以及每个步骤的每一个移动。
12.8. 添加数据库和 UI 集成 复制链接链接已复制到粘贴板!
在使用 Spring Boot 创建红帽构建的 OptaPlanner 应用程序示例后,添加数据库和 UI 集成。
前提条件
- 您已创建了 OptaPlanner Spring Boot timetable 示例。
流程
-
为
Timeslot、Room和Lesson创建 Java Persistence API(JPA)存储库。有关创建 JPA 软件仓库的详情,请参考 Spring 网站上 使用 JPA 访问数据。 - 通过 REST 公开 JPA 存储库。有关公开软件仓库的详情,请参阅 Spring 网站上 使用 REST 访问 JPA 数据。
-
构建一个可
时间的Repository常见问题解答,以在单个事务中读取和写入一个TimeTable。 调整
TimeTableController,如下例所示:Copy to Clipboard Copied! Toggle word wrap Toggle overflow 为简单起见,此代码只处理一个
可维护的实例,但要直接启用多租户,并并行处理不同高中中不同中级的多个可时间实例。getTimeTable()方法返回来自数据库的最新时间表。它使用ScoreManager(自动注入)来计算该时间表的分数,以便 UI 可以显示分数。solve()方法启动一个作业,以解决当前可时间表,并将时间窗和房分配存储在数据库中。它使用SolverManager.solveAndListen()方法侦听中间最佳解决方案,并相应地更新数据库。这可让 UI 显示后端仍在解决过程中的进度。现在,
solve()方法会立即返回,请调整TimeTableControllerTest,如下例所示:Copy to Clipboard Copied! Toggle word wrap Toggle overflow - 轮询最新的解决方案,直到解决解决为止。
- 为了视觉化呈现时间,请在这些 REST 方法之上构建具有吸引力的 Web UI。
OptaPlanner 通过 Micrometer 公开指标数据,这是 Java 应用程序的指标检测库。您可以将 Micrometer 与 Prometheus 搭配使用以监控 school timetable 应用程序中 OptaPlanner solver。
先决条件
- 您已创建了 Spring Boot OptaPlanner school 时间应用程序。
- 已安装 Prometheus。有关安装 Prometheus 的详情,请查看 Prometheus 网站。
流程
-
导航至
技术/java-spring-boot目录。 将 Micrometer Prometheus 依赖项添加到 school timetable
pom.xml文件中:Copy to Clipboard Copied! Toggle word wrap Toggle overflow 在 application.properties 文件中添加以下属性:
management.endpoints.web.exposure.include=metrics,prometheus
management.endpoints.web.exposure.include=metrics,prometheusCopy to Clipboard Copied! Toggle word wrap Toggle overflow 启动 school timetable 应用程序:
mvn spring-boot:run
mvn spring-boot:runCopy to Clipboard Copied! Toggle word wrap Toggle overflow -
在 Web 浏览器中打开
http://localhost:8080/actuator/prometheus。
第 13 章 红帽构建的 OptaPlanner 和 Java:一个生态快速入门指南 复制链接链接已复制到粘贴板!
本指南指导您了解通过 OptaPlanner 的约束解决 artificial 智能(AI)创建简单 Java 应用程序的过程。您将构建能为学生和老师优化学校的命令行应用程序:
您的应用程序会自动将 Lesson 实例分配给 Timeslot 和 Room 实例,方法是使用 AI 遵循硬和软调度 限制,例如:
- 个空间最多可以同时有一处。
- 老师可以同时在上课时进行教学。
- 学员最多可以同时参加。
- 老师喜欢在同一房间教授所有课程。
- 指导者更倾向于在课间学习顺序课时和不类差距。
- 学员对相同主题的后续课类不同。
数学会上讲,该学校的时间范围是一个 NP-hard 问题。这意味着很难扩展。只需浏览所有可能的组合,需要数以百万计的、非内部的数据集,即使在超级计算机中也是如此。幸运的是,AI 约束解决诸如 OptaPlanner 等高级算法具有在合理的时间内提供最接近的解决方案。
先决条件
- 已安装了 OpenJDK(JDK)11。红帽构建的 Open JDK 可从红帽客户门户网站的 Software Downloads 页面获取(需要登录)。
- 已安装 Apache Maven 3.6 或更高版本。Maven 可以从 Apache Maven Project 网站获得。
- IDE,如 IntelliJ IDEA、VS Code 或 Eclipse
13.1. 创建 Maven 或 Gradle 构建文件并添加依赖项 复制链接链接已复制到粘贴板!
您可以将 Maven 或 Gradle 用于 OptaPlanner unitable 应用程序。创建构建文件后,添加以下依赖项:
-
OptaPlanner-core(编译范围)以解决 school timetable 问题 -
OptaPlanner-test(测试 JUnit 范围)测试 schooltabling 约束 -
一个实现(如
logback-classic(runtime 范围)来查看 OptaPlanner 采用的步骤
流程
- 创建 Maven 或 Gradle 构建文件。
将
optaplanner-core、optaplanner-test和logback-classic依赖项添加到构建文件中:对于 Maven,在
pom.xml文件中添加以下依赖项:Copy to Clipboard Copied! Toggle word wrap Toggle overflow 以下示例显示了完整的
pom.xml文件。Copy to Clipboard Copied! Toggle word wrap Toggle overflow 对于 Gradle,请在
gradle.build文件中添加以下依赖项:Copy to Clipboard Copied! Toggle word wrap Toggle overflow 以下示例显示了完成的
gradle.build文件。Copy to Clipboard Copied! Toggle word wrap Toggle overflow
13.2. 对域对象建模 复制链接链接已复制到粘贴板!
红帽构建的 OptaPlanner 时间表项目的目标是将每节分配给一个时间段和一个房间。为此,请添加三个类: Timeslot、Lesson 和 Room,如下图所示:
Timeslot
Timeslot 类表示在未教授课程的时间间隔,例如: Monday 10:30 - 11:30 或 Tuesday 13:30 - 14:30 - 14:30。在这个示例中,所有时间插槽都有相同的持续时间,且在 lunch 或其他中断期间没有时间插槽。
时间段没有日期,因为高校的计划只每周重复。无法进行持续 规划。次性被称为 问题事实,因为在解决过程中不会有定时实例变化。此类类不需要任何特定于 OptaPlanner 的注解。
room
Room 类代表了课程讲授的方式,例如 Room A 或 Room B。在本例中,所有空间都没有容量限制,它们可容纳所有课时。
房间 实例在解决过程中不会改变,因此 Room 也是 问题。
lesson
在课时,由课课课表示,向班级教授一个主题,例如: A.Turing for a 9th grade 或 Chemistry by M.Curie 为 10th grade。如果一个主题每周都会为同一学员组讲授多次,则有多个小的、只能由 id 区分的实例。例如,第 9 分之时,每周有 6 个数学。
在寻求过程中,OptaPlanner 改变了 lesson 类的 timelot 和 room 字段,从而将每个较小的时间分配给一个时间段和一个房间。 因为 OptaPlanner 更改了这些字段,所以 lesson 是一个 规划实体 :
上图中的大多数字段包含输入数据,但 orange 字段除外。在输入数据中 未分配的 时间和房间 字段(null),并在输出数据中分配(而不是 null)。OptaPlanner 在解决过程中更改这些字段。这些字段称为规划变量。为了 OptaPlanner 可以识别它们,timelot 和 room 字段都需要 @PlanningVariable 注解。它们包含类( Lesson )需要 @PlanningEntity 注释。
流程
创建
src/main/java/com/example/domain/Timeslot.java类:Copy to Clipboard Copied! Toggle word wrap Toggle overflow 请注意,
toString()方法保留了输出短片,以便可以更轻松地读取 OptaPlanner 的DEBUG或 abrt 日志,如稍后所示。创建
src/main/java/com/example/domain/Room.java类:Copy to Clipboard Copied! Toggle word wrap Toggle overflow 创建
src/main/java/com/example/domain/Lesson.java类:Copy to Clipboard Copied! Toggle word wrap Toggle overflow Lesson类具有@PlanningEntity注释,因此 OptaPlanner 知道这个类在解决过程中发生了变化,因为它包含一个或多个计划变量。time
lot字段具有@PlanningVariable注释,因此 OptaPlanner 知道它可以更改其值。为了查找要分配给此字段的潜在的Timeslot实例,OptaPlanner 使用valueRangeProviderRefs属性连接到一个提供List<Timeslot>的值供应商。有关值范围供应商的信息,请参阅 第 13.4 节 “在规划解决方案中收集域对象”。出于同样原因,
room字段还具有@PlanningVariable注释。
13.3. 定义限制并计算分数 复制链接链接已复制到粘贴板!
当解决问题时,分数 代表特定解决方案的质量。得分越高。红帽构建的 OptaPlanner 寻找最佳解决方案,这是可用时间获得最高分数的解决方案。这可能是 最佳解决方案。
因为可时间的示例用例具有硬和软约束,因此请使用 HardSoftScore 类来代表分数:
- 硬限制不得被破坏。例如: 一个房间可以有以上时间。
- 软限制不应该被破坏。例如: 更喜欢在单个房里的教学方式。
硬约束的加法是相对于其他硬约束的权重。软限制会高于其他软限制。硬限制始终大于软限制,无论其对应的权重如何。
要计算分数,您可以实施 EasyScoreCalculator 类:
不幸的是,这个解决方案无法进行很好的扩展,因为它是非增量:每次分配给不同的时间段或房间,都会重新评估所有课程来计算新分数。
更好的解决方法是创建一个 src/main/java/com/example/solver/TimeTableConstraintProvider.java 类来执行增量分数计算。此类使用 OptaPlanner 的 ConstraintStream API,它由 Java 8 Streams 和 SQL 产生。ConstraintProvider 缩放比 EasyScoreCalculator 更好的度量:O(n)而不是 O(n)。
流程
创建以下 src/main/java/com/example/solver/TimeTableConstraintProvider.java 类:
13.4. 在规划解决方案中收集域对象 复制链接链接已复制到粘贴板!
TimeTable 实例嵌套了单个 dataset 的所有 Timeslot、Room 和 Lesson 实例。另外,由于它包含了 allon,每个都具有特定规划变量状态,所以它是一个 规划解决方案,并有分数:
-
如果课程仍然没有被签名,那么它是一个 未初始化 的解决方案,例如分数为
-4init/0hard/0soft的解决方案。 -
如果损坏硬限制,那么它是一种 不可预见的 解决方案,例如分数为
-2hard/-3soft的解决方案。 -
如果遵循所有硬约束,那么它是一个可行的解决方案,例如,分数为
0hard/-7soft的解决方案。
TimeTable 类有一个 @PlanningSolution 注释,因此红帽构建的 OptaPlanner 知道该类包含所有输入和输出数据。
具体来说,这个类是问题的输入:
带有所有时间窗的
timeslotList字段- 这是问题事实列表,因为它们在解决过程中不会改变。
包含所有房间有一个
roomList字段- 这是问题事实列表,因为它们在解决过程中不会改变。
带有所有节节的
lessonList字段- 这是计划实体列表,因为他们在解决过程中发生了变化。
在每个课
中:-
timeslot和room字段的值通常为null,因此取消分配。它们是规划变量。 -
其他字段(如
主题、Squillr 和studentGroup)已被填写。这些字段是问题属性。
-
但是,这个类也是解决方案的输出:
-
每个
lessonList字段在解决后都有非 nulltimelot 和room字段 -
代表输出解决方案质量
的分数字段,如0hard/-5soft
流程
创建 src/main/java/com/example/domain/TimeTable.java 类:
值范围供应商
timeslotList 字段是一个值范围 provider。它拥有 OptaPlanner 可以从中选择的 Timeslot 实例,分配给 lesson 实例的 timeslot 字段。time lotList 字段具有 @ValueRangeProvider 注释,可通过在 Lesson 中将 id 与 @PlanningVariable 的 valueRangeProviderRefs 匹配。
遵循相同的逻辑,roomList 字段也有 @ValueRangeProvider 注释。
问题事实和规划实体属性
另外,OptaPlanner 需要知道它可能会更改哪些较小的实例,以及如何检索由 TimeTableConstraintProvider 计算的 Timeslot 和 Room 实例。
time lotList 和 roomList 字段具有 @ProblemFactCollectionProperty 注释,因此您的 TimeTableConstraintProvider 可以从这些实例中进行选择。
lessonList 有一个 @PlanningEntityCollectionProperty 注解,因此 OptaPlanner 可在解决过程中更改它们,并且您的 TimeTableConstraintProvider 也可从那些中选择。
13.5. TimeTableApp.java 类 复制链接链接已复制到粘贴板!
在创建了 school timetable 应用的所有组件后,您将把它们放到 TimeTableApp.java 类中。
main() 方法执行以下任务:
-
创建
Solvy,为每个数据集构建 Solver。eronnectionFactory - 加载数据集。
-
通过 Solv
er.solve()解决它。 - 视觉化呈现该数据集的解决方案。
通常,一个应用程序有一个 SolverFactory,用于为每个问题数据设置一个新的 Solver 实例,用于解决每个问题数据。SolverFactory 是线程安全,但 Solver 不是。对于 school timetable 应用,只有一个数据集,因此只有一个 Solver 实例。
以下是完成的 TimeTableApp.java 类:
main() 方法首先创建 SolverFactory :
SolverFactory 创建注册 @PlanningSolution 类、@PlanningEntity 类和 ConstraintProvider 类(您之前创建的所有组件)。
如果没有终止设置或 terminationEarly() 事件,解决者会永久运行。为避免这种情况,解决问题将解决时间限制为 5 秒。
5 秒后,main() 方法加载问题,可解决这个问题并打印解决方案:
solve() 方法不会立即返回。在返回最佳解决方案前,它会持续 5 秒。
OptaPlanner 返回可用终止时间里 的最佳解决方案。由于 NP 划分问题的性质,最佳解决方案可能不是最佳的解决方案,特别是对于更大的数据集。增加终止时间,以有可能获得更好的解决方案。
generateDemoData() 方法会生成 school timetable 问题来解决。
printTimetable() 方法给控制台而生动,因此易于视觉确定是否适合自己的时间表。
13.6. 创建并运行 school timetable 应用程序 复制链接链接已复制到粘贴板!
您已完成了 school timetable Java 应用程序的所有组件,您已准备好将它们放在 TimeTableApp.java 类中并运行它。
先决条件
- 您已创建了 school timetable 应用的所有必要组件。
流程
创建
src/main/java/org/acme/schooltimetabling/TimeTableApp.java类:Copy to Clipboard Copied! Toggle word wrap Toggle overflow 运行
TimeTableApp类作为普通 Java 应用程序的主要类。以下输出应结果:Copy to Clipboard Copied! Toggle word wrap Toggle overflow -
验证控制台输出。它是否符合所有硬约束?如果您在
TimeTableConstraintProvider中注释掉roomConflict约束,会发生什么?
info 日志显示 OptaPlanner 在 5 秒里执行的操作:
... Solving started: time spent (33), best score (-8init/0hard/0soft), environment mode (REPRODUCIBLE), random (JDK with seed 0). ... Construction Heuristic phase (0) ended: time spent (73), best score (0hard/0soft), score calculation speed (459/sec), step total (4). ... Local Search phase (1) ended: time spent (5000), best score (0hard/0soft), score calculation speed (28949/sec), step total (28398). ... Solving ended: time spent (5000), best score (0hard/0soft), score calculation speed (28524/sec), phase total (2), environment mode (REPRODUCIBLE).
... Solving started: time spent (33), best score (-8init/0hard/0soft), environment mode (REPRODUCIBLE), random (JDK with seed 0).
... Construction Heuristic phase (0) ended: time spent (73), best score (0hard/0soft), score calculation speed (459/sec), step total (4).
... Local Search phase (1) ended: time spent (5000), best score (0hard/0soft), score calculation speed (28949/sec), step total (28398).
... Solving ended: time spent (5000), best score (0hard/0soft), score calculation speed (28524/sec), phase total (2), environment mode (REPRODUCIBLE).
13.7. 测试应用 复制链接链接已复制到粘贴板!
良好的应用程序包括测试覆盖。测试您时间项目中的限制和解决方法。
13.7.1. 测试 school timetable 约束 复制链接链接已复制到粘贴板!
要测试隔离中可时间项目的每个约束,请在单元测试中使用 ConstraintVerifier。这会测试每个约束的基点,与其他测试隔离开来,这在添加新限制时降低了维护。
此测试会验证约束 TimeTableConstraintProvider::roomConflict 在同一房间,且两节课中的课程具有相同的 timeslot,具有匹配权重 1。因此,如果约束 weight 为 10 个 位,它将分数减少 -10hard。
流程
创建 src/test/java/org/acme/optaplanner/solver/TimeTableConstraintProviderTest.java 类:
请注意,ConstraintVerifier 在测试过程中如何忽略约束权重,即使这些约束 weight 在 ConstraintProvider 中是硬编码的。这是因为约束权重在进行生产环境前定期有所改变。这样,约束加权调整不会破坏单元测试。
13.7.2. 测试 school timetable solver 复制链接链接已复制到粘贴板!
这个示例在 Red Hat build of Quarkus 上测试红帽构建的 OptaPlanner school timetable 项目。它使用 JUnit 测试来生成测试数据集,并将其发送到 TimeTableController 以解决。
流程
使用以下内容创建
src/test/java/com/example/rest/TimeTableResourceTest.java类:Copy to Clipboard Copied! Toggle word wrap Toggle overflow 此测试会验证在解决后,所有课程都会分配给一个时间窗和房间。它还会验证它是否发现可行的解决方案(没有硬限制)。
在
src/main/resources/application.properties文件中添加 test 属性:Copy to Clipboard Copied! Toggle word wrap Toggle overflow
通常,该解决方法在 200 毫秒内找到可行的解决方案。注意 application.properties 文件如何在测试过程中覆盖解决程序终止,以便在找到可行的解决方案 (0hard/*soft) 时立即终止。这可避免硬编码解决程序时间,因为单元测试可能会在任意硬件上运行。这种方法可确保测试用时足够长来查找可行的解决方案,即使在较慢的系统上也是如此。但是,即使在快速系统中,它也无法运行时间比严格要长的时间更长。
13.8. 日志记录 复制链接链接已复制到粘贴板!
完成 OptaPlanner school 时间项目的红帽构建后,您可以使用日志信息帮助您微调 ConstraintProvider 中的限制。查看 info 日志文件中的分数计算速度,以评估对您的限制更改的影响。以 debug 模式运行应用程序,显示应用程序接受的每个步骤,或使用 trace 日志记录记录每一步和每次移动。
流程
- 运行 school timetable 应用以获得固定时间,如五分钟。
查看
日志文件中的分数计算速度,如下例所示:... Solving ended: ..., score calculation speed (29455/sec), ...
... Solving ended: ..., score calculation speed (29455/sec), ...Copy to Clipboard Copied! Toggle word wrap Toggle overflow -
更改约束,再次运行计划应用程序相同的时间,并查看
日志文件中记录的分数计算速度。 以 debug 模式运行应用程序,以记录应用程序所做的每个步骤:
-
要从命令行运行调试模式,请使用
-D系统属性。 要永久启用调试模式,请在
application.properties文件中添加以下行:quarkus.log.category."org.optaplanner".level=debug
quarkus.log.category."org.optaplanner".level=debugCopy to Clipboard Copied! Toggle word wrap Toggle overflow 以下示例显示了在 debug 模式下
日志文件的输出:... Solving started: time spent (67), best score (-20init/0hard/0soft), environment mode (REPRODUCIBLE), random (JDK with seed 0). ... CH step (0), time spent (128), score (-18init/0hard/0soft), selected move count (15), picked move ([Math(101) {null -> Room A}, Math(101) {null -> MONDAY 08:30}]). ... CH step (1), time spent (145), score (-16init/0hard/0soft), selected move count (15), picked move ([Physics(102) {null -> Room A}, Physics(102) {null -> MONDAY 09:30}]). ...... Solving started: time spent (67), best score (-20init/0hard/0soft), environment mode (REPRODUCIBLE), random (JDK with seed 0). ... CH step (0), time spent (128), score (-18init/0hard/0soft), selected move count (15), picked move ([Math(101) {null -> Room A}, Math(101) {null -> MONDAY 08:30}]). ... CH step (1), time spent (145), score (-16init/0hard/0soft), selected move count (15), picked move ([Physics(102) {null -> Room A}, Physics(102) {null -> MONDAY 09:30}]). ...Copy to Clipboard Copied! Toggle word wrap Toggle overflow
-
要从命令行运行调试模式,请使用
-
使用
tracelogging 显示每个步骤以及每个步骤的每一个移动。
OptaPlanner 通过 Micrometer 公开指标数据,这是 Java 应用程序的指标检测库。您可以将 Micrometer 与 Prometheus 搭配使用以监控 school timetable 应用程序中 OptaPlanner solver。
先决条件
- 您已使用 Java 创建 OptaPlanner school timetable 应用程序。
- 已安装 Prometheus。有关安装 Prometheus 的详情,请查看 Prometheus 网站。
流程
将 Micrometer Prometheus 依赖项添加到 school timetable
pom.xml文件中,其中 <MICROMETER_VERSION> 是您安装的 Micrometer 的版本:<dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-registry-prometheus</artifactId> <version><MICROMETER_VERSION></version> </dependency>
<dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-registry-prometheus</artifactId> <version><MICROMETER_VERSION></version> </dependency>Copy to Clipboard Copied! Toggle word wrap Toggle overflow 注意还需要
micrometer-core依赖项。但是,这个依赖项包含在optaplanner-core依赖项中,您不需要将其添加到pom.xml文件中。将以下导入语句添加到
TimeTableApp.java类中。import io.micrometer.core.instrument.Metrics; import io.micrometer.prometheus.PrometheusConfig; import io.micrometer.prometheus.PrometheusMeterRegistry;
import io.micrometer.core.instrument.Metrics; import io.micrometer.prometheus.PrometheusConfig; import io.micrometer.prometheus.PrometheusMeterRegistry;Copy to Clipboard Copied! Toggle word wrap Toggle overflow 将下面几行添加到
TimeTableApp.java类的主方法的顶部,以便 Prometheus 可以从com.sun.net.net.httpserver.HttpServer中获取数据:Copy to Clipboard Copied! Toggle word wrap Toggle overflow 添加以下行,以控制解决时间。通过调整解决时间,您可以了解指标如何根据要解决的时间而变化。
withTerminationSpentLimit(Duration.ofMinutes(5)));
withTerminationSpentLimit(Duration.ofMinutes(5)));Copy to Clipboard Copied! Toggle word wrap Toggle overflow - 启动 school timetable 应用。
-
在 Web 浏览器中打开
http://localhost:8080/prometheus,查看 Prometheus 中的可时间应用程序。 打开监控系统,查看您的 OptaPlanner 项目的指标。
公开以下指标:
-
optaplanner_solver_errors_total:从测量开始解决时出现的错误总数。 -
optaplanner_solve_duration_seconds_active_count:当前解决的解决方法数量。 -
optaplanner_solve_duration_seconds_max: run time of longest-running currently active solver. -
optaplanner_solve_duration_seconds_duration_sum:每个活动解决持续时间的总和。例如,如果存在两个活跃的 solvers,一个只运行三分钟,另一个为一分钟,总计解决时间为 4 分钟。
-
部分 V. Red Hat build of OptaPlanner starter 应用程序 复制链接链接已复制到粘贴板!
Red Hat build of OptaPlanner 提供了以下入门应用程序,您可以在 Red Hat OpenShift Container Platform 上部署:
- staff staff Rostering starter 应用程序
- Vechile Route Planning starter 应用程序
OptaPlanner 启动程序应用程序比示例和快速启动指南的开发更多。它们专注于特定的用例,并使用可用于构建规划解决方案的最佳技术。
作为业务规则开发人员,您可以使用 IDE 来构建、运行和修改 optaweb-employee-rostering starter 应用程序,使用 Red Hat build of OptaPlanner 功能。
先决条件
- 您可以使用集成的开发环境,如 Red Hat CodeReady Studio 或 IntelliJ IDEA。
- 您对 Java 语言有了了解。
- 您对 React 和 TypeScript 的了解。需要此要求才能开发 OptaWeb UI。
14.1. 员工指定入门应用程序概述 复制链接链接已复制到粘贴板!
员工名单的入门应用程序为员工分配组织内各种位置的员工。例如,您可以使用应用程序在 nurses、guard duty 跨多个位置转移,或在 worker 之间的装配行转移。
最佳员工名单必须考虑很多变量。例如,不同的位置上会需要不同的技能来改变。另外,某些员工对于某些时间段不可用,或者可能首选特定时间段。此外,员工也可以有合同,该合同限制员工可在单个时间段内工作的小时数。
对于这个启动程序应用程序,红帽构建的 OptaPlanner 规则都使用 hard 和 soft 约束。在优化过程中,计划引擎可能不会违反硬约束,例如,如果员工不可用(开单位置),或者某个员工无法在单一转移中的两个位置工作。计划引擎会尝试遵守软限制,例如员工首选项不做特定的转变,但如果最佳解决方案需要,则可能会违反它们。
14.2. 构建并运行员工名语入门应用程序 复制链接链接已复制到粘贴板!
您可以从源代码构建员工级名入门应用程序,并将其作为 JAR 文件运行。
另外,您可以使用 IDE,如 Eclipse(包括 Red Hat CodeReady Studio)来构建和运行应用程序。
14.2.1. 准备部署文件 复制链接链接已复制到粘贴板!
在构建和部署应用程序前,您必须下载并准备部署文件。
流程
进入红帽客户门户网站中的 Software Downloads 页面(需要登录),然后从下拉列表中选择产品和版本:
- 产品: 流程自动化管理器
- 版本: 7.13.4
-
下载 Red Hat Process Automation Manager 7.13.4 Kogito 和 OptaPlanner 8 Decision Services Quickstarts (
rhpam-7.13.4-kogito-and-optaplanner-quickstarts.zip)。 -
提取
rhpam-7.13.4-kogito-and-optaplanner-quickstarts.zip文件。 -
下载 Red Hat Process Automation Manager 7.13 Maven Repository Kogito 和 OptaPlanner 8 Maven 存储库 (
rhpam-7.13.4-kogito-maven-repository.zip)。 -
提取
rhpam-7.13.4-kogito-maven-repository.zip文件。 -
将
rhpam-7.13.4-kogito-maven-repository/maven-repository子目录的内容复制到~/.m2/repository目录中。 导航到
optaweb-8.13.0.Final-redhat-00013/optaweb-employee-rostering目录。这个文件夹是本文档的后续部分中的基础文件夹。注意文件和文件夹名称可能比本文档中特别记录的版本号更高。
14.2.2. 运行 Employee Rostering starter application JAR 文件 复制链接链接已复制到粘贴板!
您可以从 Red Hat Process Automation Manager 7.13.4 Kogito 和 OptaPlanner 8 Decision Services Quickstarts 中包含的 JAR 文件运行 Employee Rostering starter 应用程序。
先决条件
-
您已下载并提取
rhpam-7.13.4-kogito-and-optaplanner-quickstarts.zip文件,如 第 14.2.1 节 “准备部署文件” 所述。 - 安装了 Java Development Kit。
- 已安装 Maven。
- 主机可以访问互联网。构建过程使用互联网从外部存储库下载 Maven 软件包。
流程
-
在命令终端中,更改为
rhpam-7.13.4-kogito-and-optaplanner-quickstarts/optaweb-8.13.0.Final-redhat-00013/optaweb-employee-rostering目录。 使用以下命令:
mvn clean install -DskipTests
mvn clean install -DskipTestsCopy to Clipboard Copied! Toggle word wrap Toggle overflow - 等待构建过程完成。
-
导航到
rhpam-7.13.4-kogito-and-optaplanner-quickstarts/optaweb-8.13.0.Final-redhat-00013/optaweb-employee-rostering/optaweb-employee-rostering-standalone/target目录。 输入以下命令来运行 Employee Rostering JAR 文件:
java -jar quarkus-app/quarkus-run.jar
java -jar quarkus-app/quarkus-run.jarCopy to Clipboard Copied! Toggle word wrap Toggle overflow 注意在构建时,
quarkus.datasource.db-kind参数的值默认设置为H2。要使用不同的数据库,您必须重建独立模块,并在命令行中指定数据库类型。例如,要使用 PostgreSQL 数据库,请输入以下命令:mvn clean install -DskipTests -Dquarkus.profile=postgres-
要访问应用程序,在网页浏览器中输入
http://localhost:8080/。
14.2.3. 使用 Maven 构建并运行 Employee Rostering starter 应用 复制链接链接已复制到粘贴板!
您可以使用命令行构建并运行员工名单的入门应用程序。
如果您使用此步骤,数据将保存在内存中,并在服务器停止后丢失。要使用持久性存储的数据库服务器构建并运行应用程序,请参阅 第 14.2.4 节 “从命令行构建并运行带有持久数据存储的员工级入门应用程序”。
先决条件
- 您已准备了部署文件,如 第 14.2.1 节 “准备部署文件” 所述。
- 安装了 Java Development Kit。
- 已安装 Maven。
- 主机可以访问互联网。构建过程使用互联网从外部存储库下载 Maven 软件包。
流程
-
进入
optaweb-employee-rostering-backend目录。 使用以下命令:
mvn quarkus:dev
mvn quarkus:devCopy to Clipboard Copied! Toggle word wrap Toggle overflow -
进入
optaweb-employee-rostering-frontend目录。 使用以下命令:
npm start
npm startCopy to Clipboard Copied! Toggle word wrap Toggle overflow 注意如果使用
npm启动服务器,则npm会监控代码更改。-
要访问应用程序,在网页浏览器中输入
http://localhost:3000/。
14.2.4. 从命令行构建并运行带有持久数据存储的员工级入门应用程序 复制链接链接已复制到粘贴板!
如果使用命令行构建员工的入门应用程序并运行它,则可以为持久数据存储提供数据库服务器。
先决条件
- 您已准备了部署文件,如 第 14.2.1 节 “准备部署文件” 所述。
- 安装了 Java Development Kit。
- 已安装 Maven。
- 主机可以访问互联网。构建过程使用互联网从外部存储库下载 Maven 软件包。
- 您已部署 MySQL 或 PostrgeSQL 数据库服务器。
流程
-
在命令终端中,导航到
optaweb-employee-rostering-standalone/target目录。 输入以下命令来运行 Employee Rostering JAR 文件:
java \ -Dquarkus.datasource.username=<DATABASE_USER> \ -Dquarkus.datasource.password=<DATABASE_PASSWORD> \ -Dquarkus.datasource.jdbc.url=<DATABASE_URL> \ -jar quarkus-app/quarkus-run.jar
java \ -Dquarkus.datasource.username=<DATABASE_USER> \ -Dquarkus.datasource.password=<DATABASE_PASSWORD> \ -Dquarkus.datasource.jdbc.url=<DATABASE_URL> \ -jar quarkus-app/quarkus-run.jarCopy to Clipboard Copied! Toggle word wrap Toggle overflow 在本例中,替换以下占位符:
-
<DATABASE_URL> : 要连接到数据库的 URL -
<DATABASE_USER> :要连接到数据库的用户 -
<DATABASE_PASSWORD> : <DATABASE_USER>的密码
-
在构建时,quarkus.datasource.db-kind 参数的值默认设置为 H2。要使用不同的数据库,您必须重建独立模块,并在命令行中指定数据库类型。例如,要使用 PostgreSQL 数据库,请输入以下命令:
mvn clean install -DskipTests -Dquarkus.profile=postgres
14.2.5. 使用 IntelliJ IDEA 构建并运行员工名单入门应用程序 复制链接链接已复制到粘贴板!
您可以使用 IntelliJ IDEA 构建并运行员工问候入门应用程序。
先决条件
- 您已下载了 Employee Rostering 源代码,它位于 Employee Rostering GitHub 页面中。
- 已安装 Intellij IDEA、Maven 和 Node.js。
- 主机可以访问互联网。构建过程使用互联网从外部存储库下载 Maven 软件包。
流程
- Start IntelliJ IDEA.
- 在 IntelliJ IDEA 主菜单中选择 File → Open。
- 选择应用程序源的根目录并点 OK。
- 在主菜单中选择 Run → Edit Configuration。
- 在出现的窗口中,展开 Templates 并选择 Maven。此时会出现 Maven 侧栏。
-
在 Maven 边栏中,从 Working Directory 菜单中选择
optaweb-employee-rostering-backend。 -
在命令行中输入
mvn quarkus:dev。 - 要启动后端,请单击 OK。
-
在命令终端中,导航到
optaweb-employee-rostering-frontend目录。 输入以下命令启动前端:
npm start
npm startCopy to Clipboard Copied! Toggle word wrap Toggle overflow -
要访问应用程序,在网页浏览器中输入
http://localhost:3000/。
14.3. 员工指定入门应用程序的源代码概述 复制链接链接已复制到粘贴板!
员工的入门应用程序由以下主要组件组成:
-
使用红帽构建的 OptaPlanner 并提供 REST API,实现 rostering 逻辑
的后端 -
使用 React 实施用户界面并通过 REST API 与
backend模块交互的frontend模块
您可以独立构建和使用这些组件。特别是,您可以实施不同的用户界面,并使用 REST API 调用服务器。
除两个主要组件外,员工的 rostering 模板还包含随机源数据生成器(用于演示和测试目的)和基准测试应用程序。
模块和密钥类
staff rostering 模板的 Java 源代码包含多个 Maven 模块。这些模块各自包含一个单独的 Maven 项目文件(pom.xml),但它们旨在在通用项目中进行构建。
模块包含多个文件,包括 Java 类。本文档列出了所有模块,以及包含员工计算计算关键信息的类和其他文件。
-
optaweb-employee-rostering-benchmark模块:包含生成随机数据并基准解决方案的额外应用程序。 -
optaweb-employee-rostering-distribution模块:包含 README 文件。 -
optaweb-employee-rostering-docs模块:包含文档文件。 -
optaweb-employee-rostering-frontend模块:使用在 React 中开发的用户界面包含客户端应用程序。 optaweb-employee-rostering-backend模块:包含使用 OptaPlanner 执行恶意计算的服务器应用程序。-
src/main/java/org.optaweb.employeerostering.service.rosterGenerator.java: 为演示和测试目的生成随机输入数据。如果您更改了所需的输入数据,请相应地更改生成器。 -
src/main/java/org.optaweb.employeerostering.domain.employee/EmployeeAvailability.java:定义员工的可用性信息。对于每个时间插槽,员工都可以不可用,或者指定该员工的首选时间段。 -
src/main/java/org.optaweb.employeerostering.domain.employee/Employee.java:定义员工。员工的名称、技术技能列表和合同工作。技能由技能对象表示。 -
src/main/java/org.optaweb.employeerostering.domain.roster/Roster.java:定义计算的 rostering 信息。 -
src/main/java/org.optaweb.employeerostering.domain.shift/Shift.java:定义可为其分配员工的转换。转换由一个时间窗和 spot 定义。例如,在外围的位置可能会发生 20 8AM-4PM 时间段 2 月 20 8AM-4PM 的位置。可以为特定的位置和时间插槽定义多个转换。在这种情况下,这个位置和时间插槽需要多个员工。 -
src/main/java/org.optaweb.employeeroeerostering.domain.skill/Skill.java:定义员工可以拥有的技术。 -
src/main/java/org.optaweb.employeerostering.domain.spot/Spot.java:定义可放置员工的位置。例如,Kitchen可以是 spot。 -
src/main/java/org.optaweb.employeeroeerostering.domain.contract.java: 定义在各种时间段内为员工设置工作时间的合同。 -
src/main/java/org.optaweb.employeerostering.domain.tenant/Tenant.java: Defines a tenant.每个租户代表一组独立的数据。个租户的数据更改不会影响任何其他租户。 -
*view.java:与域对象相关的类定义从其他信息计算的值集;客户端应用可以通过 REST API 读取这些值,但不写入它们。 -
*service.java :位于定义 REST API 的服务软件包中的接口。服务器和客户端应用都单独定义对这些接口的实施。
-
-
optaweb-employee-rostering-standalone模块:包含独立应用程序的装配配置。
14.4. 修改 employees rostering starter 应用 复制链接链接已复制到粘贴板!
要修改 employees rostering starter 应用以满足您的需要,您必须更改管理优化过程的规则。您还必须确保数据结构包含所需的数据,并提供规则所需的计算结果。如果用户界面中不存在所需数据,还必须修改用户界面。
以下过程概述了修改员工的入门应用的一般方法。
先决条件
- 您有一个成功构建应用程序的构建环境。
- 您可以阅读和修改 Java 代码。
流程
计划所需的更改。回答以下问题:
- 必须避免 的其他场景是什么?这些场景是 硬性限制。
- 优化程序必须尽量避免 的其它 场景是什么?这些场景是 软限制。
- 如果每个方案发生在潜在解决方案中,则需要什么数据计算?
- 可以从用户在现有版本中输入的信息派生哪些数据?
- 哪些数据可以被硬编码?
- 哪个数据必须由用户输入,且在当前版本中不会输入?
- 如果可以从当前数据计算任何所需数据或硬编码,请将计算或硬编码添加到现有的视图或实用程序类。如果数据必须在服务器端计算,请添加 REST API 端点来读取它。
-
如果用户必须输入所需的数据,将数据添加到代表数据实体(例如
Employee类)的类中,添加 REST API 端点来读取和写入数据,并修改用户界面进入数据。 -
当所有数据都可用时,修改规则。对于大多数修改,您必须添加新的规则。规则位于
文件中。optaweb-employee-rostering-backend模块的 src/main/java/optaweb/employeerostering/service/solver/EmployeeRosteringConstraintProvider.java - 修改应用程序后,构建并运行它。
作为业务规则开发人员,您可以快速部署 Red Hat Process Automation Manager 发行版本中包含的 optaweb-employee-rostering starter 项目,从而测试与红帽构建的 OptaPlanner 功能进行交互。
先决条件
- 您可以访问部署的 OpenShift 环境。详情请参阅您使用的 OpenShift 产品的文档。
15.1. 员工指定入门应用程序概述 复制链接链接已复制到粘贴板!
员工名单的入门应用程序为员工分配组织内各种位置的员工。例如,您可以使用应用程序在 nurses、guard duty 跨多个位置转移,或在 worker 之间的装配行转移。
最佳员工名单必须考虑很多变量。例如,不同的位置上会需要不同的技能来改变。另外,某些员工对于某些时间段不可用,或者可能首选特定时间段。此外,员工也可以有合同,该合同限制员工可在单个时间段内工作的小时数。
对于这个启动程序应用程序,红帽构建的 OptaPlanner 规则都使用 hard 和 soft 约束。在优化过程中,计划引擎可能不会违反硬约束,例如,如果员工不可用(开单位置),或者某个员工无法在单一转移中的两个位置工作。计划引擎会尝试遵守软限制,例如员工首选项不做特定的转变,但如果最佳解决方案需要,则可能会违反它们。
15.2. 在 OpenShift 上安装并启动员工问候入门应用程序 复制链接链接已复制到粘贴板!
使用 runOnOpenShift.sh 脚本将 Employee Rostering 入门程序应用程序部署到 Red Hat OpenShift Container Platform。runOnOpenShift.sh shell 脚本在 Red Hat Process Automation Manager 7.13.4 Kogito 和 OptaPlanner 8 Decision Services Quickstarts 发行版本中提供。
runOnOpenShift.sh 脚本在本地构建并打包应用程序源代码,并将它上传到 OpenShift 环境以进行部署。这个方法需要 Java Development Kit、Apache Maven 和 bash shell 命令行。
15.2.1. 使用提供的脚本部署应用程序 复制链接链接已复制到粘贴板!
您可以使用提供的脚本将 Employee Rostering starter 应用程序部署到 Red Hat OpenShift Container Platform。该脚本在本地构建并打包应用源代码,并将它上传到 OpenShift 环境以进行部署。
先决条件
-
您可以使用
oc命令行工具登录到目标 OpenShift 环境。有关此工具的更多信息,请参阅 OpenShift Container Platform CLI 参考。 - 安装了 OpenJDK 11 或更高版本。红帽构建的 Open JDK 可从红帽客户门户网站的 Software Downloads 页面获取(需要登录)。
- 已安装 Apache Maven 3.6 或更高版本。Maven 可以从 Apache Maven Project 网站获得。
-
您的本地系统中有
bashshell 环境。
流程
进入红帽客户门户网站中的 Software Downloads 页面(需要登录),然后从下拉列表中选择产品和版本:
- 产品: 流程自动化管理器
- 版本: 7.13.4
-
下载 Red Hat Process Automation Manager 7.13 Maven Repository Kogito 和 OptaPlanner 8 Maven 存储库 (
rhpam-7.13.4-kogito-maven-repository.zip)文件。 -
提取
rhpam-7.13.4-kogito-maven-repository.zip文件。 -
将
rhpam-7.13.4-kogito-maven-repository/maven-repository子目录的内容复制到~/.m2/repository目录中。 -
从红帽客户门户的软件 下载页面,下载
rhpam-7.13.4-kogito-and-optaplanner-quickstarts.zip文件。 - 提取下载的存档。
-
进入
optaweb-employee-rostering文件夹。 要构建 Employee Rostering 应用程序,请运行以下命令:
mvn clean install -DskipTests -DskipITs
mvn clean install -DskipTests -DskipITsCopy to Clipboard Copied! Toggle word wrap Toggle overflow 登录 OpenShift 帐户或 Red Hat Code Ready Container 实例。在以下示例中,<
account-url> 是 OpenShift 帐户或 Red Hat Code Ready Container 实例的 URL,<login-token> 是该帐户的登录令牌:oc login <account-url> --token <login-token>
oc login <account-url> --token <login-token>Copy to Clipboard Copied! Toggle word wrap Toggle overflow 创建一个新项目来托管 Employee Rostering:
oc new-project optaweb-employee-rostering
oc new-project optaweb-employee-rosteringCopy to Clipboard Copied! Toggle word wrap Toggle overflow 运行置备脚本以构建和部署应用程序:
./runOnOpenShift.sh
./runOnOpenShift.shCopy to Clipboard Copied! Toggle word wrap Toggle overflow 完成编译和打包可能需要长达 10 分钟。这些进程在命令行输出中持续显示进度。
当操作完成后,会显示以下信息,其中 <
URL> 是部署的 URL:You can access the application at <URL> once the deployment is done.
You can access the application at <URL> once the deployment is done.Copy to Clipboard Copied! Toggle word wrap Toggle overflow 为 OpenShift 帐户或 Red Hat Code Ready Container 实例输入您之前在流程中使用的 URL,以访问已部署的应用程序。首次启动最多可能需要一分钟,因为其他构建在 OpenShift 平台上完成。
注意如果应用程序点击链接后没有打开一分钟,请对浏览器页面执行硬刷新。
15.3. 使用员工指定入门应用程序 复制链接链接已复制到粘贴板!
您可以使用 Web 界面使用 Employee Rostering 入门应用程序。接口在 ReactJS 中开发。您还可以根据需要访问 REST API 来创建自定义用户界面。
15.3.1. 草稿和发布周期 复制链接链接已复制到粘贴板!
在任何特定时刻,您可以使用应用程序为一个时间段内创建 roster,称为 草案 周期。默认情况下,草案周期的长度为 3 周。
截止到草案前一周的 roster 后,您可以 发布 roster。目前,当前期第一周的名册会成为 已发布的 期间。在已发布的时间内,roster 已被修复,您无法自动修改它(方式,紧急手动更改仍有可能)。然后,这个 roster 可以分配给员工,以便他们能够规划他们周围的时间。草案日期稍后会转移一个星期。
例如,假设设置了 9 月 1 日至 9 月 21 日的草案。您可以在此期限内自动创建员工名册。然后,当您发布 roster 时,周期最多会发布 7 年 9 月 7 日。新的草案周期为 9 月 8 日至 9 月 28 日。
有关发布 roster 的步骤,请参考 第 15.3.12 节 “发布动向语”。
15.3.2. 轮转模式 复制链接链接已复制到粘贴板!
员工名单应用程序支持转换和员工的 轮转模式。
轮转模式是从两个天开始的任何时间的"model"周期。模式没有与特定日期绑定。
您可以为轮转每天创建时间存储桶。每次存储桶都设置变化的时间。(可选)模板可以包括转换的默认员工的名称。
当您发布 roster 时,应用程序会在草案中添加新周。此时,转移,如果适用,则默认员工名称从轮转模式复制到草案期间的新部分。
当达到轮转模式结束时,它会自动从开始重启。
如果您的组织中的周末切换模式与工作日变化模式不同,请使用一周的轮转模式或数周的轮转模式,例如 14、21 或 28 天。默认长度为 28 天。然后,模式总是在同一工作日重复,您可以为不同的工作日设置转换。
有关编辑轮转模式的步骤,请参阅 第 15.3.13 节 “查看并编辑轮转模式”。
15.3.3. employees Rostering 租户 复制链接链接已复制到粘贴板!
Employee Rostering 应用程序支持多个 租户。每个租户都是一个独立的数据,包括输入和 roster 输出。更改一个租户的数据不会影响其他租户。您可以在租户间切换以使用多个独立的数据集,例如,为员工准备不同的位置。
安装后会存在多个示例租户,这代表一些典型的企业类型,如工厂或医院等。您可以选择这些租户中的任何一个,并对其进行修改以符合您的需要。您还可以创建新租户,以从空白处输入数据。
15.3.3.1. 更改一个 Employee Rostering 租户 复制链接链接已复制到粘贴板!
您可以更改当前的租户。选择了不同的租户后,所有显示的信息都引用此租户,您进行的任何更改仅会影响到此租户。
流程
- 在 Employee Rostering application Web 界面中,在浏览器窗口的右上角,单击 Tenant 列表。
- 从列表中选择一个租户。
15.3.3.2. 创建租户 复制链接链接已复制到粘贴板!
您可以创建新租户,从空白处输入数据。在创建租户时,您可以设置几个参数来确定应用如何为这个租户准备输出。
在创建租户后,您无法更改租户参数。
流程
- 要在 Employee Rostering 应用 Web 界面中创建一个新租户,请在浏览器窗口右上角单击设置(gear)图标,然后单击添加。
设置以下值:
- 名称 :新租户的名称。此名称显示在租户列表中。
- 计划开始日期 :初始草案时间段的开始日期。发布 roster 后,此日期将变为已发布期限的开始日期。此日期的工作日始终保持在一周日,这些日期开始周期草案,任何特定公布的期限,以及首次使用轮转模式。因此,通常很方便地将开始日期设置为一周(星期一或周一)的开始。
- 草案长度(天 ):周期的长度。草案周期在租户生命周期内保持相同的长度。
- publish Notice(days) :发布通知周期的长度。我们希望提前为任何一天发布最终的名册,因此员工有足够的通知规划其发生时间的个人生命周期。在当前版本中,此设置不会以任何方式强制执行。
- 发布长度(天 ):您每次发布出 roster 时都会发布(修复)的周期长度。在当前版本中,此设置在 7 天时被修复。
- 轮转长度(天): 轮转模式长度。
- timezone : roster 应用到的环境的时区。此时区用于决定显示用户界面的"当前"日期。
- 点击 Save。
租户使用空白数据创建。
15.3.4. 掌握技能 复制链接链接已复制到粘贴板!
您可设置在 roster 中的任何位置需要的所有 技能。例如,除了一般人力资源和其他人操作等技能外,24 小时的工员可能需要中断、服务、总线和托管技能。
流程
在 Employee Rostering application Web 界面中,单击 Skills 选项卡。
您可以看到浏览器窗口右上角的当前可见技能的数量,例如,1-
15 代表 34。您可以使用 < ; 和 & gt; 按钮来显示列表中的其他技能。您可以在搜索框中输入技能名称以 搜索 技能。
完成以下步骤以添加新技能:
- 点击 Add。
- 在名称 处在文本字段中输入新技能的名称。
- 点 Save 图标。
- 要编辑技能名称,请点击 技能旁边的编辑 技巧图标(铅笔图标)。
- 要删除技能,请单击技能旁边的 删除技能 图标(垃圾形图标)。
在每个租户中,技能名称必须是唯一的。如果技能与员工或点相关联,则您无法删除技能。
15.3.5. 输入位置 复制链接链接已复制到粘贴板!
您必须进入 spots 列表 , 这代表着业务中的各种位置。就职,点数包括该栏、总线站、前端计数器、各种工具包、服务站、服务区域和办公室。
对于每个位置,您可以从您在 Skills 选项卡输入的列表中选择一个或多个所需技能。应用程序 rosters 仅拥有该位置上所有必要技能的人员。如果位置没有所需技能,则应用程序会将任何员工降级为点。
流程
- 要在 Employee Rostering 应用程序 Web 界面中输入或更改位置,点 Spots 选项卡。您可以在搜索框中输入任意部分来搜索 spot。
完成以下步骤以添加新位置:
- 点 Add Spot。
- 在 名称 下面的文本字段中,输入新 spot 的名称。
- 可选:根据所需技能 集的下拉列表中选择一个或多个技能。
- 点 Save 图标。
- 要编辑 spot 的名称和所需技能,点 spot 旁边的 Edit Spot 图标(铅笔图标)。
- 要删除一个 spot,点 spot 旁边的 Delete Spot 图标(trashcan shape)。
在每个租户中,位置名称必须是唯一的。当为其创建任何变化时,您无法删除该位置。
15.3.6. 输入合同列表 复制链接链接已复制到粘贴板!
您必须进入业务为员工使用的所有合同类型列表。
合同决定了员工每天、日历、日历月或日历年可工作的最大时间。
在创建合同时,您可随意设置任何限制或任何限制。例如,一个部分员工可能不允许在一周内工作超过 20 小时,而一次性员工在一天和 1800 小时内可能限于 10 小时。其他合同可能不包括在工作时间内没有限制。
您必须在分钟内输入合同的所有工作时间限制。
流程
要在 Employee Rostering 应用程序 Web 界面输入或更改 合同 列表,点 contracts 选项卡。
您可以查看浏览器窗口右上角的当前可见合同的数量,例如,1-
15 代表 34。您可以使用 < ; 和 & gt; 按钮在列表中显示其他合同。您可以在搜索框中输入合同名称的任何部分来搜索合同。
完成以下步骤以添加新合同:
- 点击 Add。
- 在名称 处文本字段中输入合同 名称。
在 Maximum minutes 下输入所需的时间限制:
- 如果员工不能每天工作超过设定的时间,请在 Per Day 中启用复选框,然后在此复选框旁边的字段中输入分钟数。
- 如果员工不能按日历星期工作,请在 Per Week 中启用复选框,然后在此复选框旁边输入分钟数。
- 如果员工不能按每个日历月工作的间隔时间,请在 Per Month 中启用复选框,然后在此复选框旁边的字段输入分钟数。
- 如果员工不能以日历年为单位工作,请在日历年 内启用 复选框,然后在此复选框旁边的字段输入分钟数。
- 点 Save 图标。
- 要编辑合同的名称和时间限制,请点击合同名称旁边的 Edit Contract 图标(创建铅笔图标)。
- 要删除合同,请点击 合同名称旁边的删除合同 图标(手形)。
在每个租户中,合同名称必须是唯一的。如果合同已分配给任何员工,则您无法删除它。
15.3.7. 输入员工列表 复制链接链接已复制到粘贴板!
您必须进入所有业务员工、拥有相关技能以及适用于他们的合约的合同。根据相关合同中的工作时间限制,应用程序认为这些员工能够发现其技能。
流程
要在 Employee Rostering 应用程序 Web 界面中输入或更改员工列表,请单击 Employees 选项卡。
您可以看到浏览器窗口右上角的当前可见员工数量,例如,1-
15 代表 34。您可以使用 < ; 和 & gt; 按钮显示列表中的其他员工。您可以在搜索框中输入员工名称的任何部分来搜索员工。
完成以下步骤以添加新员工:
- 点击 Add。
- 在名称 下的文本字段中,输入员工 名称。
- 可选:在 Skill 集的下拉列表中选择一个或多个技能。
- 从合同下的下拉列表中选择 合同。
- 点 Save 图标。
- 要编辑员工的名称和技能,请单击员工名称旁边的 Edit Employee 图标(列形)。
- 要删除员工,请单击员工名称旁边的 删除 Employee 图标(垃圾形形)。
在每个租户中,员工名称必须是唯一的。如果这些员工对任何变化的影响,则无法删除它们。
15.3.8. 设置员工可用性 复制链接链接已复制到粘贴板!
您可以为特定时间范围设置员工可用性。
如果某个员工对于某个特定的时间范围 不可用,则在此时间段内,从不分配员工进行任何转换(例如,如果员工在精度或在度中调用 sick 或 监管),则不能分配给任何变化。不 所需 且需要的是特定时间范围内的员工首选项;应用程序尽可能适应它们。
流程
要在 Employee Rostering 应用程序 Web 界面中查看并编辑员工可用性,请单击 Availability Roster 选项卡。
在窗口的左上角,您可以看到显示 roster 的日期。要查看其他周,您可以使用字段旁边的 & lt ; 和 > ; 按钮。或者,您可以单击日期字段并更改要查看包含此日期的每周的日期。
-
要为员工创建可用性条目,请单击计划中的空空间,然后选择一个员工。最初,创建整个一天的
Unavailable条目。 要更改可用性条目,点该条目。您可以更改以下设置:
- 从 和 到日期和时间:可用性条目应用到的时间范围。
status:您可以从下拉列表中选择 Unavailable、Desired 或 Undesired 状态。
要保存该条目,请单击 Apply。
要删除可用性条目,请单击 条目,然后单击删除 可用性。
您还可以通过将鼠标指针移到该条目上,然后点击该条目上显示的图标之一,以更改或删除可用性条目:
-
点击
图标将条目的状态设置为 Unavailable。
-
点击
图标将条目的状态设置为 Undesired。
-
点击
图标将条目的状态设置为 Desired。
-
点击
图标删除该条目。
-
点击
如果员工已分配给更改,然后在这一转变过程中创建或更改可用性条目,则此分配不会自动更改。您必须再次创建员工转换 roster,以应用新的或更改的可用性条目。
15.3.9. 在 shift roster 中查看和编辑变化 复制链接链接已复制到粘贴板!
Shift Roster 是所有位置以及所有可能的时间范围的列表。
如果一个员工在某个时间段内必须出现在 spot 中,就必须存在一个 转变。如果位置需要同时有多个员工,则可以为同一位置和时间范围创建多个转换。
每个转换都由位置(行)和时间范围(列)的交集表示。
当新的时间添加到草案周期中时,应用程序会从轮转模式将转换(和默认员工)从轮转模式复制到这一草案周期的新部分。您还可以在草稿时间内手动添加并编辑变化。
流程
要查看并编辑 Employee Rostering 应用程序 Web 界面中的 shift roster,请单击 Shift 选项卡。
在窗口的左上角,您可以看到显示 roster 的日期。要查看其他周,您可以使用字段旁边的 & lt ; 和 > ; 按钮。或者,您可以单击日期字段并更改要查看包含此日期的每周的日期。
- 要添加转变,请点击计划的开放式区域。应用程序会添加一个变化,从点击的位置自动决定插槽和时间范围。
要编辑转变,请点击转变。您可以为更改设置以下值:
- 日期和时间 :转换的确切时间和持续时间。
- 员工 :分配给转变的员工。
固定 :该员工是否 固定 为转换。如果员工固定,则自动员工名单无法改变员工的分配。固定员工不会自动复制到任何其他变化。
要保存更改,请单击" 应用 "。
- 要删除转变,请单击转变,然后单击 Delete shift。
15.3.10. 创建和查看员工转换语 复制链接链接已复制到粘贴板!
您可以使用应用程序为所有员工创建并查看最佳转变 roster。
流程
- 要查看并编辑 Employee Rostering 应用程序 Web 界面中的 shift roster,请单击 Shift 选项卡。
- 要创建最佳转变 roster,请单击 Schedule。应用程序需要 30 秒才能找到最佳解决方案。
结果
操作完成后,按 Shift Roster 视图包含最佳变速值。为草案周期创建一个新的 roster。该操作不会修改已发布的句点。
在窗口的左上角,您可以看到显示 roster 的日期。要查看其他周,您可以使用字段旁边的 & lt ; 和 > ; 按钮。或者,您可以单击日期字段并更改要查看包含此日期的每周的日期。
在草案期间,代表转换的框顺序是点线。在发布的时间段内,Borders 是不中断的行。
代表切换的方框的颜色显示每个切换的约束状态:
- 强绿色:Soft 约束匹配;例如,该转换在员工的"不良"时间内进行。
- Pale green:没有约束中断。
- grey: Soft 约束问题;例如,该转换处于员工的"不所需"时间。
- 黄色:Medium 约束中断,例如,没有员工被分配到转移。
- 红帽:硬盘约束损坏;例如,员工同时有两个变化。
15.3.11. 查看员工切换 复制链接链接已复制到粘贴板!
您可以在以员工为中心的表中查看特定员工的分配转变。这些信息与 Shift Roster 相同,但查看格式可能会更方便地告知员工所分配转变。
流程
要在 Employee Rostering 应用程序 Web 界面中查看员工和转变表,请单击 Availability Roster 选项卡。
在窗口的左上角,您可以看到显示 roster 的日期。要查看其他周,您可以使用字段旁边的 & lt ; 和 > ; 按钮。或者,您可以单击日期字段并更改要查看包含此日期的每周的日期。
您可以查看浏览器窗口右上角的当前可见员工数量,例如,1-10 为 34。您可以使用数字旁边的 & lt; 和 > 按钮来显示列表中的其他员工。
在草案期间,代表转换的框顺序是点线。在发布的时间段内,Borders 是不中断的行。
15.3.12. 发布动向语 复制链接链接已复制到粘贴板!
当您发布转变的 roster 时,第一周的草稿期将发布。虽然紧急的手动更改仍有可能,但自动员工的指定时间不再会改变已发布的期限内任何变化。草案周期将在一周之后被转移。有关草案和发布周期的详情,请参考 第 15.3.1 节 “草稿和发布周期”。
流程
- 要查看并编辑 Employee Rostering 应用程序 Web 界面中的 shift roster,请单击 Shift 选项卡。
- 审查草案周期的第一周的转换代码,以确保它可以接受。
- 单击 Publish。
15.3.13. 查看并编辑轮转模式 复制链接链接已复制到粘贴板!
通过轮转模式,您可以添加、移动和删除转变,以便您有效地管理员工资源。它通过时间存储桶和座位进行定义。
- 时间存储桶描述了一个时间插槽(例如,9:00 a.m. 至 5:00 p.m.)用于特定位置或位置 (例如,A) (例如:naestics)超过两天,以及需要(例如,开票培训)所需的任何技能。
- 座位 (B) 是特定日期在特定时间 bucket 中的员工分配。
- 员工存根是一个图标,表示可用于分配给时间 bucket 的员工。员工存根列在 Employee Stub 列表中。
有关轮转模式的更多信息,请参阅 第 15.3.2 节 “轮转模式”。
流程
- 点 Rotation 选项卡查看并编辑轮转模式。
- 从 Rotation 菜单中选择 spot。
- 点 Add New Time Bucket。此时会显示 Creating Working Time Bucket 对话框。
- 指定开始和结束时间,选择任何额外的所需技能,选择本时间存储桶的天数,然后单击 Save。此时间 bucket 的未分配位置显示在 Rotation 页面中,按时间范围组织。
- 要创建员工 stub 列表,以便您可以向轮转添加员工,单击 编辑 Employee Stub 列表。
- 在 Edit Employee Stub List 对话框中,单击 Add Employee,并从列表中选择一个员工。
- 添加此 stub 列表需要的所有员工,然后单击保存。员工在 Rotation 页面上显示时间存储桶之上。
- 单击员工图标,从员工 stub 列表中选择员工。
单击鼠标并拖动鼠标,将所选员工分配到座位。座位将用员工图标填充。
注意一个时间存储桶只能为每天分配一个员工。要向同一时间存储桶添加多个员工,请复制时间存储桶,并根据需要更改员工名称。
- 要置备计划,点 Scheduling 并选择您为其创建轮转的位置。
- 单击 Provision,再指定日期范围。
- 取消选择在此计划中包括的位置。
- 单击所选 spot 旁边的箭头,然后取消选择希望在您的计划中使用的任何时间存储桶。
- 单击 Provision Shifts。日历填充自时间存储桶生成的转换。
- 要修改转变,请点击日历上的生成的转变。
第 16 章 部署和使用红帽构建的 OptaPlanner 载体路由规划入门应用程序 复制链接链接已复制到粘贴板!
作为开发人员,您可以使用 OptaWeb Vehicle Routing starter 应用程序来优化您的载体发送。
先决条件
- 已安装了 OpenJDK(JDK)11。红帽构建的 Open JDK 可从红帽客户门户网站的 Software Downloads 页面获取(需要登录)。
- 已安装 Apache Maven 3.6 或更高版本。Maven 可以从 Apache Maven Project 网站获得。
16.1. 什么是 OptaWeb Vehicle 路由? 复制链接链接已复制到粘贴板!
许多企业的主要目的是传输各种类型的车go。这些企业的目标是以最有效的方式从加载点向目标交付一个车点,并使用其载体车队。主要目标之一是尽量缩短以时间或距离度衡量的差差成本。
这种优化问题被称为载体路由问题(VRP),并具有很多变体。
Red Hat build of OptaPlanner 可解决许多这些载体路由变化并提供解决方案示例。OptaPlanner 可让开发人员专注于对业务规则和需求建模,而不是学习 约束编程。OptaWeb Vehicle Routing 通过提供回答问题的入门应用程序,扩展了 OptaPlanner 的载路由功能:
- 我从哪里获得距离和出差时间?
- 如何视觉化 map 上的解决方案?
- 如何构建在云中运行的应用程序?
OptaWeb Vehicle Routing 使用 OpenStreetMap(OSM)数据文件。有关 OpenStreetMap 的详情,请查看 OpenStreetMap 网站。
在使用 OptaWeb Vehicle 路由时,请使用以下定义:
区域 :sarth 映射上的任意区域,由 OSM 文件表示。地区可以是国家/地区、城市、洲或一组经常一起使用的国家/地区。例如,DACH 区域包括德国(DE)、奥地利(AT)和瑞士(CH)。
国家/ 地区代码:由 ISO-3166 标准分配给国家的双字母代码。您可以使用国家代码过滤 geosearch 结果。由于您可以处理跨越多个国家(例如 DACH 区域)的区域,OptaWeb Vehicle Routing 接受国家代码列表,以便 geosearch 过滤可与此类区域一起使用。有关 国家代码列表,请参阅 ISO 3166 country Codes
Geosearch :一个查询类型,在其中提供区域的地址或放置名称作为搜索关键字,并收到多个 GPS 位置。返回的位置数量取决于搜索关键字的唯一情况。因为大多数位置名称没有唯一的,所以通过在您工作区域的国家或国家/地区中包括内容,过滤出非相关结果。
16.2. 下载并构建 OptaWeb Vehicle 路由部署文件 复制链接链接已复制到粘贴板!
在构建和部署 OptaWeb Vehicle 路由前,您必须下载并准备部署文件。
流程
进入红帽客户门户网站中的 Software Downloads 页面(需要登录),然后从下拉列表中选择产品和版本:
- 产品: 流程自动化管理器
- 版本: 7.13.4
-
下载 Red Hat Process Automation Manager 7.13.4 Kogito 和 OptaPlanner 8 Decision Services Quickstarts (
rhpam-7.13.4-kogito-and-optaplanner-quickstarts.zip)。 -
提取
rhpam-7.13.4-kogito-and-optaplanner-quickstarts.zip文件。 -
下载 Red Hat Process Automation Manager 7.13 Maven Repository Kogito 和 OptaPlanner 8 Maven 存储库 (
rhpam-7.13.4-kogito-maven-repository.zip)。 -
提取
rhpam-7.13.4-kogito-maven-repository.zip文件。 -
将
rhpam-7.13.4-kogito-maven-repository/maven-repository子目录的内容复制到~/.m2/repository目录中。 -
导航到
optaweb-8.13.0.Final-redhat-00013/optaweb-vehicle-routing目录。 输入以下命令来构建 OptaWeb Vehicle 路由:
mvn clean package -DskipTests
mvn clean package -DskipTestsCopy to Clipboard Copied! Toggle word wrap Toggle overflow
16.3. 使用 runLocally.sh 脚本在本地运行 OptaWeb Vehicle Routing 复制链接链接已复制到粘贴板!
Linux 用户可以使用 runLocally.sh Bash 脚本来运行 OptaWeb Vehicle 路由。
runLocally.sh 脚本不会在 macOS 中运行。如果您无法使用 runLocally.sh 脚本,请参阅 第 16.4 节 “手动配置并运行 OptaWeb Vehicle Routing”。
runLocally.sh 脚本自动执行以下设置步骤,否则必须手动执行:
- 创建数据目录。
- 从 Geofabrik 下载所选 OpenStreetMap(OSM)文件。
- 尝试自动将国家代码与每个下载的 OSM 文件关联。
- 如果独立 JAR 文件不存在,则构建项目。
- 使用单一地区参数或以交互方式选择地区,启动 OptaWeb Vehicle 路由。
有关执行 runLocally.sh 脚本的说明,请参见以下小节:
使用 OptaWeb Vehicle Routing 入门的最简单方法是运行不带任何参数的 runLocally.sh 脚本。
先决条件
- OptaWeb Vehicle Routing 已通过 Maven 成功构建,如 第 16.2 节 “下载并构建 OptaWeb Vehicle 路由部署文件” 所述。
- 可通过互联网访问。
流程
在
rhpam-7.13.4-kogito-and-optaplanner-quickstarts/optaweb-8.13.0.Final-redhat-00013/optaweb-vehicle-routing目录中输入以下命令。./runLocally.sh
./runLocally.shCopy to Clipboard Copied! Toggle word wrap Toggle overflow -
如果提示您创建
.optaweb-vehicle-routing目录,请输入y。第一次运行脚本时,会提示您输入创建此目录。 如果提示下载 OSM 文件,请输入
y。您第一次运行脚本时,OptaWeb Vehicle Routing 下载 Belgium OSM 文件。应用程序在下载 OSM 文件后启动。
要打开 OptaWeb Vehicle Routing 用户界面,在 web 浏览器中打开以下 URL:
http://localhost:8080
http://localhost:8080Copy to Clipboard Copied! Toggle word wrap Toggle overflow
第一次运行脚本时,需要几分钟才能启动,因为 OSM 文件必须由 GraphHopper 导入,并存储为路路图。下一次运行 runlocal.sh 脚本时,加载时间会非常快。
16.3.2. 以互动模式运行 OptaWeb Vehicle Routing runLocally.sh 脚本 复制链接链接已复制到粘贴板!
使用互动模式查看为每个地区分配了下载的 OSM 文件和国家代码的列表。您可以使用互动模式从 Geofabrik 下载其他 OSM 文件,而无需访问网站并选择下载目标。
先决条件
- OptaWeb Vehicle Routing 已通过 Maven 成功构建,如 第 16.2 节 “下载并构建 OptaWeb Vehicle 路由部署文件” 所述。
- 可通过互联网访问。
流程
-
将目录更改为
rhpam-7.13.4-kogito-and-optaplanner-quickstarts/optaweb-8.13.0.Final-redhat-00013/optaweb-vehicle-routing。 输入以下命令在互动模式中运行脚本:
./runLocally.sh -i
./runLocally.sh -iCopy to Clipboard Copied! Toggle word wrap Toggle overflow -
在您选择的提示下,输入d以显示下载菜单。系统将显示之前下载的区域列表,然后是您可以下载的区域列表。 可选:从之前下载的区域列表中选择区域:
- 在下载的地区列表中,输入与区域关联的号码。
- 按 Enter 键。
可选:下载区域:
-
输入与您要下载的区域关联的号码。例如,要选择欧洲的映射,请输入
5。 -
要下载映射,请输入
d,然后按 Enter 键。 要在映射中下载特定区域,请输入
e,然后输入与您要下载的区域关联的号码,然后按 Enter 键。使用大型 OSM 文件为获得最佳用户体验,请使用小地区(如欧洲或美国地区)。使用大于 1 GB 的 OSM 文件需要大量 RAM 大小,并需要大量时间(最多几小时)进行初始处理。
应用程序在下载 OSM 文件后启动。
-
输入与您要下载的区域关联的号码。例如,要选择欧洲的映射,请输入
要打开 OptaWeb Vehicle Routing 用户界面,在 web 浏览器中打开以下 URL:
http://localhost:8080
http://localhost:8080Copy to Clipboard Copied! Toggle word wrap Toggle overflow
以非互动模式使用 OptaWeb Vehicle Routing,通过单个命令启动 OptaWeb Vehicle 路由,其中包含您之前下载的 OSM 文件。当您想快速或进行演示时,这非常有用。
先决条件
- OptaWeb Vehicle Routing 已通过 Maven 成功构建,如 第 16.2 节 “下载并构建 OptaWeb Vehicle 路由部署文件” 所述。
- 要用于下载的区域的 OSM 文件。有关下载 OSM 文件的详情,请参考 第 16.3.2 节 “以互动模式运行 OptaWeb Vehicle Routing runLocally.sh 脚本”。
- 可通过互联网访问。
流程
-
将目录更改为
rhpam-7.13.4-kogito-and-optaplanner-quickstarts/optaweb-8.13.0.Final-redhat-00013/optaweb-vehicle-routing。 执行以下命令,其中 <
;OSM_FILE_NAME> 是您之前下载的 OSM 文件:./runLocally.sh <OSM_FILE_NAME>
./runLocally.sh <OSM_FILE_NAME>Copy to Clipboard Copied! Toggle word wrap Toggle overflow
16.3.4. 更新数据目录 复制链接链接已复制到粘贴板!
如果要使用不同的数据目录,您可以更新 OptaWeb Vehicle Routing 使用的数据目录。默认数据目录为 $HOME/.optaweb-vehicle-routing。
先决条件
- OptaWeb Vehicle Routing 已通过 Maven 成功构建,如 第 16.2 节 “下载并构建 OptaWeb Vehicle 路由部署文件” 所述。
流程
-
要使用不同的数据目录,请在当前数据目录中添加目录绝对路径到
.DATA_DIR_LAST文件。 要更改与区域关联的国家代码,编辑当前数据目录中的
country_codes目录中的对应文件。例如,如果您为 SVRF 下载了 OSM 文件,且该脚本无法猜测国家代码,将
country_codes/stunnel-latest的内容设置为 GB。-
要删除区域,请从数据目录中的
openstreetmap目录删除对应的 OSM 文件,并删除graphhopper目录中的区域目录。
16.4. 手动配置并运行 OptaWeb Vehicle Routing 复制链接链接已复制到粘贴板!
运行 OptaWeb Vehicle Routing 的最简单方法是使用 runlocal.sh 脚本。但是,如果您的系统中没有 Bash,您可以手动完成 run.sh 脚本执行的步骤。
先决条件
- OptaWeb Vehicle Routing 已通过 Maven 成功构建,如 第 16.2 节 “下载并构建 OptaWeb Vehicle 路由部署文件” 所述。
- 可通过互联网访问。
流程
下载路由数据。
路由引擎需要地理数据来计算在位置间传输的时间。在运行 OptaWeb Vehicle 路由前,您必须在本地文件系统中下载并存储 OpenStreetMap(OSM)数据文件。
注意OSM 数据文件通常只在 100 MB 到 1 GB 之间下载,因此最好先下载这些文件,然后再构建或启动 OptaWeb Vehicle 路由应用程序。
-
在 Web 浏览器中打开
http://download.geofabrik.de/。 - 点 Sub Region 列表中的一个区域,如 。这时将打开 subregion 页面。
-
在 Sub Regions 表中,为国家下载 OSM 文件(
.osm.pbf),例如 Belgium。
-
在 Web 浏览器中打开
创建数据结构。
OptaWeb Vehicle Routing 读取和写入文件系统上多种类型的数据。它从
openstreetmap目录中读取 OSM(OpenStreetMap)文件,将路网络图形写入图形目录,并将用户数据保存在名为db的目录中。创建一个用于存储所有这些数据的新目录,以便更轻松地升级到未来 OptaWeb Vehicle 路由的更新版本,并继续处理之前创建的数据。-
创建
$HOME/.optaweb-vehicle-routing目录。 在
$HOME/.optaweb-vehicle-routing目录中创建openstreetmap目录:$HOME/.optaweb-vehicle-routing └── openstreetmap
$HOME/.optaweb-vehicle-routing └── openstreetmapCopy to Clipboard Copied! Toggle word wrap Toggle overflow 将所有下载的 OSM 文件(扩展名
.osm.pbf)移动到openstreetmap目录。剩余的目录结构由 OptaWeb Vehicle Routing 应用程序创建,在第一次运行时由 OptaWeb Vehicle Routing 应用程序创建。之后,您的目录结构类似以下示例:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
-
创建
-
将目录更改为
rhpam-7.13.4-kogito-and-optaplanner-quickstarts/optaweb-8.13.0.Final-redhat-00013/optaweb-vehicle-routing/optaweb-vehicle-routing-standalone/target。 要运行 OptaWeb Vehicle Routing,请输入以下命令:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 在这个命令中,替换以下变量:
-
<OSM_FILE_NAME> : 要使用的区域 OSM 文件以及之前下载的区域 <COUNTRY_CODE_LIST> : 用于过滤 geosearch 查询的国家代码的逗号分隔列表。有关国家代码的列表,请参阅 ISO 3166 国家 代码。应用程序在下载 OSM 文件后启动。
在以下示例中,OptaWeb Vehicle Routing 下载中心美国(
中央-america-latest.osm.pbf)的 OSM 映射,并在 Belize(BZ)和 Guatemala(GT)的搜索。Copy to Clipboard Copied! Toggle word wrap Toggle overflow
-
要打开 OptaWeb Vehicle Routing 用户界面,在 web 浏览器中打开以下 URL:
http://localhost:8080
http://localhost:8080Copy to Clipboard Copied! Toggle word wrap Toggle overflow
Linux 用户可以使用 runOnOpenShift.sh Bash 脚本在 Red Hat OpenShift Container Platform 上安装 OptaWeb Vehicle 路由。
runOnOpenShift.sh 脚本不会在 macOS 上运行。
先决条件
-
您可以访问 OpenShift 集群,并安装了 OpenShift 命令行界面(
oc)。有关 Red Hat OpenShift Container Platform 的详情,请参阅安装 OpenShift Container Platform。 - OptaWeb Vehicle Routing 已通过 Maven 成功构建,如 第 16.2 节 “下载并构建 OptaWeb Vehicle 路由部署文件” 所述。
- 可通过互联网访问。
流程
- 登录或启动 Red Hat OpenShift Container Platform 集群。
输入以下命令,其中 &
lt;PROJECT_NAME> 是新项目的名称:oc new-project <PROJECT_NAME>
oc new-project <PROJECT_NAME>Copy to Clipboard Copied! Toggle word wrap Toggle overflow -
如有必要,将目录改为
rhpam-7.13.4-kogito-and-optaplanner-quickstarts/optaweb-8.13.0.Final-redhat-00013/optaweb-vehicle-routing。 输入以下命令执行
runOnOpenShift.sh脚本并下载 OpenStreetMap(OSM)文件:./runOnOpenShift.sh <OSM_FILE_NAME> <COUNTRY_CODE_LIST> <OSM_FILE_DOWNLOAD_URL>
./runOnOpenShift.sh <OSM_FILE_NAME> <COUNTRY_CODE_LIST> <OSM_FILE_DOWNLOAD_URL>Copy to Clipboard Copied! Toggle word wrap Toggle overflow 在这个命令中,替换以下变量:
-
<OSM_FILE_NAME> : 从 <OSM_FILE_DOWNLOAD_URL> 下载的文件名。 -
<COUNTRY_CODE_LIST> : 用于过滤 geosearch 查询的国家代码的逗号分隔列表。有关国家代码的列表,请参阅 ISO 3166 国家 代码。 <OSM_FILE_DOWNLOAD_URL> : PBF 格式的 OSM 数据文件的 URL 可从 OpenShift 访问。该文件将在后端启动期间下载,并保存为/deployments/local/<OSM_FILE_NAME>。在以下示例中,OptaWeb Vehicle Routing 下载中心美国(
中央-america-latest.osm.pbf)的 OSM 映射,并在 Belize(BZ)和 Guatemala(GT)的搜索。./runOnOpenShift.sh central-america-latest.osm.pbf BZ,GT http://download.geofabrik.de/europe/central-america-latest.osm.pbf
./runOnOpenShift.sh central-america-latest.osm.pbf BZ,GT http://download.geofabrik.de/europe/central-america-latest.osm.pbfCopy to Clipboard Copied! Toggle word wrap Toggle overflow
-
如需有关 runOnOpenShift.sh 脚本的帮助,请输入 ./runOnOpenShift.sh --help。
16.5.1. 使用本地更改更新部署的 OptaWeb Vehicle Routing 应用程序 复制链接链接已复制到粘贴板!
在 Red Hat OpenShift Container Platform 上部署 OptaWeb Vehicle Routing 应用程序后,您可以更新后端和前端。
先决条件
- OptaWeb Vehicle Routing 已通过 Maven 成功构建并部署在 OpenShift 中。
流程
要更新后端,请执行以下步骤:
- 更改源代码并使用 Maven 构建后端模块。
-
将目录更改为
rhpam-7.13.4-kogito-and-optaplanner-quickstarts/optaweb-8.13.0.Final-redhat-00013/optaweb-vehicle-routing。 输入以下命令启动 OpenShift 构建:
oc start-build backend --from-dir=. --follow
oc start-build backend --from-dir=. --followCopy to Clipboard Copied! Toggle word wrap Toggle overflow
要更新前端,请执行以下步骤:
-
更改源代码并使用
npm实用程序构建前端模块。 -
将目录改为 source
/optaweb-vehicle-routing-frontend。 输入以下命令启动 OpenShift 构建:
oc start-build frontend --from-dir=docker --follow
oc start-build frontend --from-dir=docker --followCopy to Clipboard Copied! Toggle word wrap Toggle overflow
-
更改源代码并使用
16.6. 使用 OptaWeb Vehicle 路由 复制链接链接已复制到粘贴板!
在 OptaWeb Vehicle Routing 应用程序中,您可以在映射中标记多个位置。第一个位置假定为 depot。车道必须从此指南到您标记的其他所有位置。
您可以设置载体的数量和每个载车的执行容量。但是,该路由并不保证使用所有载体。应用程序使用所需数量的载体来达到最佳路由要求。
当前版本有一些限制:
- 每个发送到位置的传送都应占用一个载体容量。例如,一个容量为 10 的载体,在返回到depot 之前,最多可访问 10 个位置。
- 不支持设置列车和位置的自定义名称。
16.6.1. 创建路由 复制链接链接已复制到粘贴板!
要创建最佳路由,请使用 OptaWeb Vehicle Routing 用户界面的 Demo 选项卡。
先决条件
- OptaWeb Vehicle Routing 正在运行,您可以访问用户界面。
流程
- 在 OptaWeb Vehicle Routing 中,点击 Demo 选项卡。
- 使用映射上方的蓝色减号和加按钮来设置载体数量。每个车辆的默认容量都是 10。
根据需要,使用 map 中的方块中的加号按钮来缩放。
注意不要双击 zoom。双击 也会创建位置。
- 点 depot 的位置。
- 单击地图上的其他位置以进行发送点。
如果要删除位置:
- 将鼠标光标悬停在位置上,以查看位置名称。
- 在屏幕左侧的列表中找到位置名称。
- 点名称旁边的 X 图标。
每次添加或删除位置或更改载体数量时,应用程序都会创建并显示新的最佳路由。如果解决方案使用多个载体,则应用程序会以不同的颜色显示每个载体的路由。
16.6.2. 查看和设置其他详情 复制链接链接已复制到粘贴板!
您可以使用 OptaWeb Vehicle Routing 用户界面中的其他标签页来查看和设置附加详情。
先决条件
- OptaWeb Vehicle Routing 正在运行,您可以访问用户界面。
流程
- 单击 Vehicles 选项卡,以查看、添加和删除车键,同时设置每个载体的容量。
- 单击 Visits 选项卡可以查看和删除位置。
- 单击 Route 选项卡,以选择每个载体并查看所选载体的路由。
16.6.3. 使用 OptaWeb Vehicle Routing 创建自定义数据集 复制链接链接已复制到粘贴板!
有一个由几个大型 Belgian 城市组成的内置演示数据集。如果要在 Load demo 菜单中有更多演示,您可以准备自己的数据集。
流程
- 在 OptaWeb Vehicle Routing 中,通过点击映射或使用 geosearch 添加 depot 和一个或多个 accesss。
点 Export,并将该文件保存到数据集目录中。
注意数据集目录是
app.demo.data-set-dir属性中指定的目录。如果应用程序通过
runLocally.sh脚本运行,则数据设置目录设置为$HOME/.optaweb-vehicle-routing/dataset。否则,属性取自
application.properties文件,默认为rhpam-7.13.4-kogito-and-optaplanner-quickstarts/optaweb-8.13.0.Final-redhat-00013/optaweb-vehicle-routing/optaweb-vehicle-routing-standalone/target/local/dataset。您可以编辑
app.demo.data-set-dir属性来指定 diffent 数据目录。- 编辑 YAML 文件,并为数据集选择唯一名称。
- 重启后端。
重新启动后端后,数据设置目录中的文件会出现在 Load demo 菜单中。
16.6.4. OptaWeb Vehicle 路由故障排除 复制链接链接已复制到粘贴板!
如果 OptaWeb Vehicle Routing 意外行为,请按照以下步骤排除。
先决条件
- OptaWeb Vehicle Routing 正在运行,并意外行为。
流程
- 要识别问题,请查看后端终端输出日志。
要解决这个问题,请删除后端数据库:
- 在后端终端窗口中按 Ctrl+C 来停止后端。
-
删除
optaweb-vehicle-routing/optaweb-vehicle-routing-backend/local/db目录。 - 重启 OptaWeb Vehicle 路由。
16.7. OptaWeb Vehicle Routing 开发指南 复制链接链接已复制到粘贴板!
本节论述了如何在开发模式中配置和运行后端模块。
16.7.1. OptaWeb Vehicle Routing 项目结构 复制链接链接已复制到粘贴板!
OptaWeb Vehicle Routing 项目是一个多模块 Maven 项目。
图 16.1. 模块依赖关系树图
后端和前端模块位于模块树的底部。这些模块包含应用源代码。
独立模块是组合后端和前端到单个可执行 JAR 文件的装配模块。
distribution 模块表示最终的 assembly 步骤。它使用独立应用程序和文档,并将它们打包成易于分发的存档中。
后端和前端是单独的项目,您可以单独构建和部署。实际上,它们使用完全不同的语言编写,并使用不同的工具进行构建。这两个项目都具有提供现代开发人员体验的工具,可在代码更改和正在运行的应用程序之间进行快速切换。
下面章节描述了如何在开发模式下运行后端和前端项目。
16.7.2. OptaWeb Vehicle Routing 后端模块 复制链接链接已复制到粘贴板!
后端模块包含一个服务器端应用程序,它使用红帽构建的 OptaPlanner 优化载体路由。优化是一种 CPU 密集型计算,必须避免任何 I/O 操作才能将其完整性能。由于目标之一是尽量减少差差成本,因此 OptaWeb Vehicle Routing 在 RAM 内存中保留出差成本信息。在寻求时,OptaPlanner 需要了解用户输入的每个位置之间的差成本。此信息存储在名为 distance matrix 的结构中。
当您进入新位置时,OptaWeb Vehicle Routing 会计算新位置和所有其他位置之间的差额成本,并在距离列表中保存出差成本。travel 成本计算由 GraphHopper 路由引擎执行。
后端模块实现以下额外功能:
- 持久性
- 前端的 websocket 连接
- 数据集加载、导出和导入
如需了解更多有关后端代码架构的信息,请参阅 第 16.8 节 “OptaWeb Vehicle 路由后端架构”。
下面章节描述了如何在开发模式中配置和运行后端。
16.7.2.1. 运行 OptaWeb Vehicle Routing 后端模块 复制链接链接已复制到粘贴板!
您可以在 Quarkus 开发模式下运行后端模块。
先决条件
- OptaWeb Vehicle Routing 被配置为 第 16.4 节 “手动配置并运行 OptaWeb Vehicle Routing” 所述。
流程
-
将目录更改为
rhpam-7.13.4-kogito-and-optaplanner-quickstarts/optaweb-8.13.0.Final-redhat-00013/optaweb-vehicle-routing/optaweb-vehicle-routing-backend。 要在 development 模式下运行后端,请输入以下命令:
mvn compile quarkus:dev
mvn compile quarkus:devCopy to Clipboard Copied! Toggle word wrap Toggle overflow
您可以使用 IntelliJ IDEA Ulitmate 运行 OptaWeb Vehicle 路由后端模块,以便更轻松地开发项目。Intellij IDEA Ultimate 包括 Quarkus 插件,它可自动为使用 Quarkus 框架的模块创建运行配置。
流程
使用 optaweb-vehicle-routing-backend 运行配置来运行后端。
其他资源
如需更多信息,请参阅 运行 Quarkus 应用程序。
16.7.2.3. Quarkus 开发模式 复制链接链接已复制到粘贴板!
在开发模式中,如果存在对后端源代码或配置的更改,并且刷新运行前端的浏览器选项卡,后端会自动重启。
了解有关 Quarkus 开发模式 的更多信息。
16.7.2.4. 更改 OptaWeb Vehicle Routing 后端模块系统属性值 复制链接链接已复制到粘贴板!
您可以临时或永久覆盖 OptaWeb Vehicle Routing 后端模块的默认系统属性值。
OptaWeb Vehicle Routing 后端模块系统属性存储在 /src/main/resources/application.properties 文件中。此文件受版本控制。使用它来永久存储默认配置属性值并定义 Quarkus 配置集。
先决条件
- OptaWeb Vehicle Routing starter 应用程序已下载并提取。有关详情请参考 第 16.2 节 “下载并构建 OptaWeb Vehicle 路由部署文件”。
流程
要临时覆盖默认系统属性值,请在运行
mvn或java命令时包括-D<PROPERTY>=<VALUE> 参数,其中 <PROPERTY> 是您要更改的属性值,而 <VALUE> 是您要暂时分配给该属性的值。以下示例演示了如何在使用 Maven 在dev模式中编译 Quarkus 项目时,将quarkus.http.port系统属性的值临时更改为8181:mvn compile quarkus:dev -Dquarkus.http.port=8181
mvn compile quarkus:dev -Dquarkus.http.port=8181Copy to Clipboard Copied! Toggle word wrap Toggle overflow 这会临时更改
/src/main/resources/application.properties文件中的属性值。要永久更改配置值,例如存储特定于您的开发环境的配置,请将
env-example文件的内容复制到optaweb-vehicle-routing-backend/.env文件。此文件不包括在版本控制中,因此克隆存储库时不存在此文件。您可以在
.env文件中进行更改,而不影响 Git 工作树。
其他资源
有关 OptaWeb Vehicle Routing 配置属性的完整列表,请参阅 第 16.9 节 “OptaWeb Vehicle 路由后端配置属性”。
16.7.2.5. OptaWeb Vehicle Routing backend logging 复制链接链接已复制到粘贴板!
OptaWeb Vehicle Routing 使用 SLF4J API 和 Logback 作为日志框架。如需更多信息,请参阅 Quarkus - 配置日志。
16.7.3. 使用 OptaWeb Vehicle Routing 前端模块 复制链接链接已复制到粘贴板!
前端项目通过 Create React App 启动。创建 React App 提供了多个脚本和依赖项,可帮助开发并构建用于生产环境的应用程序。
先决条件
- OptaWeb Vehicle Routing starter 应用程序已下载并提取。有关详情请参考 第 16.2 节 “下载并构建 OptaWeb Vehicle 路由部署文件”。
流程
在 Fedora 中,输入以下命令来设置开发环境:
sudo dnf install npm
sudo dnf install npmCopy to Clipboard Copied! Toggle word wrap Toggle overflow -
将目录更改为
rhpam-7.13.4-kogito-and-optaplanner-quickstarts/optaweb-8.13.0.Final-redhat-00013/optaweb-vehicle-routing/optaweb-vehicle-routing-frontend。 安装
npm依赖项:npm install
npm installCopy to Clipboard Copied! Toggle word wrap Toggle overflow 与 Maven 不同,
npm软件包管理器在项目目录下将依赖项安装到node_modules中,并且仅在您执行npm install时执行此操作。每当package.json更改后列出的依赖项时,例如,当您将更改拉取到 master 分支时,都必须在运行开发服务器前执行npm install。输入以下命令来运行开发服务器:
npm start
npm startCopy to Clipboard Copied! Toggle word wrap Toggle overflow 如果没有自动打开,在网页浏览器中打开
http://localhost:3000/。默认情况下,
npm start命令会尝试在您的默认浏览器中打开这个 URL。注意如果您不希望
npm start命令在每次运行时都打开一个新浏览器标签页,请导出BROWSER=none环境变量。您可以使用.env.local文件使这个首选项持久。要做到这一点,请输入以下命令:echo BROWSER=none >> .env.local
echo BROWSER=none >> .env.localCopy to Clipboard Copied! Toggle word wrap Toggle overflow 每当您在前端源代码中进行更改时,浏览器都会刷新该页面。在终端中运行的开发服务器进程会提取更改,并将编译和 lint 错误输出到控制台。
输入以下命令来运行测试:
npm test
npm testCopy to Clipboard Copied! Toggle word wrap Toggle overflow 在执行
或npmstartnpm run build时,更改REACT_APP_BACKEND_URL环境变量的值,以指定 npm 要使用的后端项目的位置,例如:REACT_APP_BACKEND_URL=http://10.0.0.123:8081
REACT_APP_BACKEND_URL=http://10.0.0.123:8081Copy to Clipboard Copied! Toggle word wrap Toggle overflow 注意在
npm构建过程中,环境变量在 JavaScript 捆绑包内被硬编码,因此您必须在构建和部署前端前指定后端位置。如需了解更多有关 React 环境变量的信息,请参阅添加 自定义环境变量。
要构建前端,请输入以下命令之一:
./mvnw install
./mvnw installCopy to Clipboard Copied! Toggle word wrap Toggle overflow mvn install
mvn installCopy to Clipboard Copied! Toggle word wrap Toggle overflow
16.8. OptaWeb Vehicle 路由后端架构 复制链接链接已复制到粘贴板!
对于应用程序来说,域模型和使用案例是必不可少的。OptaWeb Vehicle Routing 域模型位于架构中心,它包括在嵌入用例的应用程序层中。路由优化、距离计算、持久性和网络通信等功能被视为实施详情,并将其置于架构的最顶层。
图 16.2. 应用程序层图
16.8.1. 代码机构 复制链接链接已复制到粘贴板!
后端代码由三个层进行组织,在上图中显示。
service 软件包包含实施用例的应用层。plugin 软件包包含基础架构层。
每个层中的代码进一步由函数组织。这意味着每个服务或插件都有自己的软件包。
16.8.2. 依赖项规则 复制链接链接已复制到粘贴板!
编译时依赖项只能从外部层指向中心。遵循此规则有助于使领域模型独立于底层框架和其他实施细节,并更加精确地模拟业务实体的行为。通过演示和永久性推向外围,测试业务实体和使用案例的行为更加容易。
域没有依赖项。
服务仅依赖于域。如果服务需要发送结果(例如到数据库或客户端),它会使用输出边界接口。其实施由 上下文和依赖项注入 (CDI)容器注入。
插件以两种方式依赖服务。首先,根据用户输入或从优化引擎发出的路由更新等事件调用服务。服务注入插件,将其构造和依赖项解析到 IoC 容器的负担中。第二,插件实施服务输出边界接口来处理用例结果,例如对数据库进行永久性更改或向 Web UI 发送响应。
16.8.3. 域软件包 复制链接链接已复制到粘贴板!
domain 软件包包含为此项目域建模 的业务对象,如 Location、Vehicle、Route。这些对象严格以业务为导向,并且不得受到任何工具和框架的影响,例如对象关系映射工具和 Web 服务框架。
16.8.4. service 软件包 复制链接链接已复制到粘贴板!
service 软件包包含实施 用例 的类。用例描述了您要执行的操作,例如添加新的位置、更改载体容量或查找地址的协调。监管用例的业务规则使用域对象来表达。
服务通常需要与外层中的插件交互,如持久性、Web 和优化。为了满足层之间的依赖关系规则,服务和插件之间的交互以定义服务依赖项的接口表示。插件可通过提供实现服务边界接口的 bean 来满足服务的依赖项。CDI 容器创建了插件 bean 的实例,并在运行时将其注入服务。这是 control 原则的 inversion of example。
16.8.5. plugin 软件包 复制链接链接已复制到粘贴板!
插件 软件包包含基础架构功能,如优化、持久性、路由和网络。
16.9. OptaWeb Vehicle 路由后端配置属性 复制链接链接已复制到粘贴板!
您可以设置下表中列出的 OptaWeb Vehicle Routing 应用程序属性。
| 属性 | 类型 | 示例 | 描述 |
|---|---|---|---|
|
| 相对或绝对路径 |
|
从这个目录中载入自定义数据集。默认为 |
|
| 相对或绝对路径 |
|
H2 用于存储数据库文件的目录。默认为 |
|
|
|
| 限制 geosearch 结果。 |
|
| Enumeration |
|
路由引擎实施。默认为 |
|
| 相对或绝对路径 |
|
GraphHopper 用来存储移动网络图形的目录。默认为 |
|
| 相对或绝对路径 |
|
包含 OSM 文件的目录。默认为 |
|
| 文件名 |
|
由 GraphHopper 加载的 OSM 文件的名称。该文件必须放在 |
|
|
|
| 解决者在发生位置更改后应运行多长时间。 |
|
| IP 地址或主机名 |
| 要绑定服务器的网络地址。 |
|
| 端口号 |
| 服务器 HTTP 端口。 |
附录 A. 版本信息 复制链接链接已复制到粘贴板!
文档最后于 2023 年 9 月 5 日星期二更新。
附录 B. 联系信息 复制链接链接已复制到粘贴板!
Red Hat Process Automation Manager 文档团队 :brms-docs@redhat.com