使用红帽构建的 OptaPlanner 开发解析程序
前言 复制链接链接已复制到粘贴板!
您可以使用 OptaPlanner 开发决定规划问题的最佳解决方案的解析程序。OptaPlanner 是红帽构建的 OptaPlanner 的内置组件。您可以在 Red Hat Build of OptaPlanner 中使用 solvers 作为服务的一部分,以优化具有特定限制的有限资源。
使开源包含更多 复制链接链接已复制到粘贴板!
红帽致力于替换我们的代码、文档和 Web 属性中存在问题的语言。我们从这四个术语开始:master、slave、黑名单和白名单。由于此项工作十分艰巨,这些更改将在即将推出的几个发行版本中逐步实施。详情请查看 CTO Chris Wright 信息。
部分 I. 红帽构建的 OptaPlanner 8.29 发行注记 复制链接链接已复制到粘贴板!
本发行注记列出了新功能,并为 Red Hat Build of OptaPlanner 8.29 提供升级说明。
要从 OptaPlanner 8.13 升级到 Red Hat Build of OptaPlanner 8.29,按以下顺序合并以前的 OptaPlanner 版本:
流程
- 在浏览器中打开 OptaPlanner Upgrade Recipe 8 页面。
- 滚动到描述您要升级的第一个版本的部分,例如 From 8.13.0.Final to 8.14.0.Final,并按照说明操作。
- 滚动到描述您要升级的下一版本的部分,如 From 8.19.0.Final to 8.20.0.Final,并按照说明操作。
- 完成剩余的升级说明,直到您升级到 8.29.0.Final。
第 2 章 Red Hat build of OptaPlanner 8.29 新功能 复制链接链接已复制到粘贴板!
本节重点介绍红帽构建的 OptaPlanner 8.29 中的新功能。
Bavet 是用于快速分数计算的功能。Bavet 目前仅适用于 OptaPlanner 的社区版本。它不适用于 Red Hat Build of OptaPlanner 8.29。
2.1. 规划列表变量 复制链接链接已复制到粘贴板!
OptaPlanner 现在包括对计划列表变量的支持。规划列表变量可以包含多个规划值。您可以使用它作为之前使用链规划变量建模的规划问题的替代选择。
使用 planning list 变量或链规划变量,其中目标是以特定顺序在有限资源间分发多个工作负载元素。
例如,在 vehicle 路由问题中,vehicles 代表有限的资源,客户代表工作负载元素。链规划变量定义了递归数据结构,其中客户以载体结尾。另一方面,计划列表变量提供了一个更直观的模型,每个电话都包含一个客户列表。
使用新的 @PlaningListVariable 注释来定义计划列表变量。
Plan list 变量是仍在开发中的新功能。它不包括链计划变量可用的所有高级功能。
2.2. 新示例 复制链接链接已复制到粘贴板!
本发行版本中包括以下示例:
-
批量调度(
批量调度) -
指导准入计划(
pas) -
coach shutdownle collect (coach
shuttlegathering)
2.3. 员工调度快速入门 复制链接链接已复制到粘贴板!
Employee Rostering 引用实现已被 Employee Rostering Quickstart 替代。
部分 II. 红帽构建的 OptaPlanner 入门 复制链接链接已复制到粘贴板!
作为自定义规则开发人员,您可以使用 OptaPlanner 来查找根据一组有限资源和特定限制规划问题的最佳解决方案。
使用本文档开始使用 OptaPlanner 开发解析程序。
第 3 章 OptaPlanner 简介 复制链接链接已复制到粘贴板!
OptaPlanner 是一个轻量级、可嵌入的规划引擎,可优化规划问题。它帮助普通 Java 站有效地解决规划问题,它将优化 heuristics 和 metaheuristics 与非常有效的分数计算相结合。
例如,OptaPlanner 帮助解决各种用例:
- 员工/人员 :它有助于为 nurses 创建时间表并跟踪个人管理。
- 指导 时间表:帮助安排较少活动、课程、技术和技术演示。
- Shop Schedules :它跟踪库存装配行、机器队列规划和工作强制任务规划。
- Cutting Stock :通过减少纸张和钢材等消耗来最小化浪费。
每个机构都面临规划问题;也就是说,它们为产品和服务提供有限的受限资源集合(如员工、资产、时间和销售)。
OptaPlanner 是 Apache 软件许可证 2.0 下的开源软件。它是 100% 纯 Java,在大多数 Java 虚拟机(JVM)上运行。
3.1. 规划问题 复制链接链接已复制到粘贴板!
规划问题 具有最佳目标,基于有限资源和特定限制。最佳目标可以是任意数量的事情,例如:
- 最大化原位 - 最佳目标会导致最高的概率。
- 最小化托管空间 - 最佳目标对环境的影响最小。
- 最大化员工或客户的产品 - 优先选择员工或客户需求的最佳目标。
实现这些目标的能力取决于可用资源的数量。例如,以下资源可能会受限制:
- 人员数量
- 时间量
- 预算
- 物理资产,如 machinery, vehicles, computer, buildings
您还必须考虑与这些资源相关的特定限制,如个人工作时间、他们使用某些机器或设备间的兼容性的能力。
OptaPlanner 帮助 Java 人员有效地解决约束问题。它将优化 heuristics 和 metaheuristics 与有效的分数计算相结合。
3.2. 规划问题中的 NP-completeness 复制链接链接已复制到粘贴板!
提供的用例 可能是 NP-complete 或 NP-hard,这意味着适用以下语句:
- 在合理的时间内,容易验证特定问题的解决方案。
- 在合理的时间段内,无法简单查找问题的最佳解决方案。
这意味着您的问题可能比您预期的难度更大,因为这两种常见技术不会受到影响:
- 禁止算法(即使更高级的变体)用时过长。
- 一个快速算法,例如在 bin packing 问题 中,将放置在最大项目中,首先 返回来自于最佳解决方案。
通过使用高级优化算法,OptaPlanner 会在合理的时间为这些规划问题找到良好的解决方案。
3.3. 规划问题的解决方案 复制链接链接已复制到粘贴板!
规划问题有很多解决方案。
几种解决方案类别:
- 可能的解决方案
- 可能的解决方案是任何解决方案,无论它是否会破坏任意数量的限制。规划问题通常具有大量可能的解决方案。其中许多解决方案都并不有用。
- 可行的解决方案
- 可行的解决方案是不会破坏任何(负)硬限制的解决方案。可行的解决方案数量相对于可能的解决方案数量。有时没有可行的解决方案。每个可行的解决方案都是可能的解决方案。
- 最佳解决方案
- 最佳解决方案是具有最高分数的解决方案。规划问题通常有几个最佳解决方案。它们始终至少有一个最佳解决方案,即使没有可行的解决方案,并且最佳解决方案不可行。
- 找到最佳解决方案
- 最佳解决方案是在指定时间内实现的最高分数的解决方案。最佳解决方案可能很可行,给定有足够的时间,它是一个最佳解决方案。
因此,可能的解决方案数量较大(如果正确计算),即使设置了小的数据也是如此。
在 optaplanner-examples/src distribution 文件夹中提供的示例中,大多数实例都有大量可能的解决方案。因为无法保证查找最佳解决方案,因此任何实施都被强制评估所有可能的解决方案的子集。
OptaPlanner 支持多种优化算法,以便有效地利用这一大量可能的解决方案。
根据用例,一些优化算法性能比其他算法更好,但无法提前知道。使用 OptaPlanner,您可以通过在几个 XML 或代码中更改 solver 配置来切换优化算法。
3.4. 有关规划问题的限制 复制链接链接已复制到粘贴板!
通常,计划问题至少有两个级别的约束:
一个 (负)硬限制 不能被破坏。
例如,一个公司无法同时为两个不同的资源提供两个不同的项。
如果可以避免,则 (负)软约束 不应中断。
例如,Teacher A 不希望在每天的 afternoons 上参与。
有些问题也存在正限制:
如果可能,应满足 正的软约束(或返回)。
例如,Teacher B 与 Monday mornings 相似。
有些基本问题仅存在硬约束。有些问题有三个或更多级别的约束,如硬、中型和软限制。
这些限制定义了规划问题 的分数计算 (也称为适当的 功能)。规划问题的每个解决方案均以分数的形式进行评级。使用 OptaPlanner 时,分数约束使用面向对象的语言(如 Java 或 dols 规则)编写。
这种类型的代码灵活且可扩展的。
3.5. 红帽构建的 OptaPlanner 示例 复制链接链接已复制到粘贴板!
红帽构建的 OptaPlanner 示例附带几个 OptaPlanner 示例。您可以检查示例的代码,并根据需要对其进行修改以满足您的需要。
红帽不支持红帽构建的 OptaPlanner 发行版中包含的示例代码。
一些 OptaPlanner 示例解决了在技术 contests 中出出的问题。以下表中的 Contest 列列出了 contests。它也识别一个示例为 realistic 或 unrealistic 用于 contest 的目的。最后的 contest 是符合以下标准的官方独立 contest:
- 明确定义的实际用例
- 真实限制
- 多个真实数据集
- 在特定硬件的特定时间限制中可重复生成结果
- 领导和/或企业级操作人员的严重参与。
架构师 contests 提供 OptaPlanner 的目标比较,与研究软件和研究方面提供了目标比较。
| 示例 | Domain | 大小 | 法国 | 目录名称 |
|---|---|---|---|---|
| 1 个实体类 (1 个变量) |
实体 IANA
值 IANA
搜索空间 | Pointless (cheatable) |
| |
| 1 个实体类 (1 个变量) |
实体为
值 IANA
搜索空间 | 否(由我们定义) |
| |
| 1 个实体类 (1 个链变量) |
实体 IANA
值
搜索空间 | unrealistic TSP web |
| |
| 1 个实体类 (1 个变量) |
实体
值 IANA
搜索空间 | 否(由我们定义) |
| |
| 1 个实体类 (2 个变量) |
实体关联
值
搜索空间 | 否(由我们定义) |
| |
| 1 个实体类 (2 个变量) |
实体 IANA
值 IANA
搜索空间 | ITC 2007 跟踪 3 |
| |
| 1 个实体类 (1 个变量) |
实体为
值 IANA
搜索空间 | 最近迁移的 ROADEF 2012 |
| |
| 1 个实体类 (1 个链变量) 1 个影子实体类 (1 自动影子变量) |
实体
值为
搜索空间 | 非弹性 VRP Web |
| |
| 带有时间窗的 vehicle 路由 | 所有 Vehicle 路由 (1 shadow 变量) |
实体
值为
搜索空间 | 非弹性 VRP Web |
|
| 1 个实体类 (2 个变量) (1 shadow 变量) |
实体为
值 IANA
搜索空格 | 近远的 MISTA 2013 |
| |
| 1 个实体类 (1 列表变量) 1 个影子实体类 (1 自动影子变量) (1 影子变量) |
实体为
值为
搜索空间 | 没有定义 |
| |
| 2 个实体类(相同层次结构) (2 个变量) |
实体为
值 IANA
搜索空间 | ITC 2007 跟踪 1 |
| |
| 1 个实体类 (1 个变量) |
实体为
值 IANA
搜索空间 |
| ||
| 趋势 | 1 个实体类 (1 个变量) |
实体
值
搜索空间 | unrealistic TTP |
|
| 1 个实体类 (2 个变量) |
实体为
值 IANA
搜索空间 | 近似的 ICONHQ |
| |
| 1 个实体类 (1 个变量) |
实体关联
value =
搜索空间 | 没有定义 |
| |
| 1 个实体类 (2 个变量) |
实体 IANA
值 IANA
搜索空间 | 没有定义 |
| |
| 1 个实体类 (1 个链变量) (4 影子变量) 1 个影子实体类 (1 自动影子变量) |
实体依赖项
值 IANA
搜索空间 | 没有定义 |
| |
| 1 个实体类 (1 个变量) 1 个影子实体类 (1 自动影子变量) |
实体
值
搜索空间 | 没有定义 |
|
3.6. N queens 复制链接链接已复制到粘贴板!
在一个 n 大小的象棋盘中放置 n 个皇后,没有两个皇后可以相互攻击。最常见的 n queens puzzle 是 8 个 queens puzzle,带有 n = 8 :
约束:
- 使用 n 列的数量和 n 行。
- 将 n queens 放在按板上。
- 没有两个 queens 可以相互攻击。queen 可以在同一个水平、垂直或 diagonal 行中对任何其他 queen 进行攻击。
本文档主要使用 4 queens puzzle 作为主要示例。
建议的解决方案可以是:
图 3.1. 错误解决方案,用于 4 queens puzzle
以上解决方案是错误,因为 queens A1 和 B0 可以相互攻击(因此可能会 queens B0 和 D0)。删除 queen B0 会遵循 "no two queens 可相互攻击"约束,但会破坏 "place n queens" 约束。
以下是正确的解决方案:
图 3.2. Four queens puzzle 的正确解决方案
所有约束都已满足,因此解决方案正确。
请注意,大多数 queens puzzles 具有多个正确的解决方案。我们将专注于查找特定 n 的正确解决方案,而不是查找特定 n 可能的正确解决方案数量。
问题大小
n queens 示例的实现尚未优化,因为它作为 beginner 示例。然而,它可以轻松地处理 64 queens。通过一些更改,它已被显示,可以轻松地处理 5000 queens 等。
3.6.1. N queens 的域模型 复制链接链接已复制到粘贴板!
本例使用域模型来解决四个 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 is column B, …) 和一个Row(例如,0 为行 0,1 为行 1, …)。根据列和行计算升序行和降序行。
列和行索引从小时的左上角开始。
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 查找解决方案
单个
NQueens实例包含所有Queen实例的列表。它是解决方案实现,它将由 Solver 提供、解决并从 Solver 检索。
请注意,在四个 queens 示例中,NQueens getN () 方法始终返回四个。
图 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 共享相同的列时,行或 diagonal 行(如 ASP 和(DSL))可以相互攻击。
3.7. Cloud balancing 复制链接链接已复制到粘贴板!
有关本例的详情,请参考 OptaPlanner 快速启动指南。
3.8. Tending Salesman (TSP - 指导销售问题) 复制链接链接已复制到粘贴板!
给定一个城市列表,找到一次访问每个城市的 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 硬问题(如大多数计划问题),因此特定问题数据集的最佳解决方案可能会在对问题数据集稍有变化时改变:
3.9. Tennis club 调度 复制链接链接已复制到粘贴板!
每周 10nis club 都有四个团队相互活跃循环。为团队分配这四个点。
硬限制:
- 冲突 :团队一次只能播放一次。
- 不可用:有些团队在某些情况下不可用。
中等限制:
- 公平分配:所有团队都应该扮演了相等的次数。
软限制:
- 平均聚合:每个团队都应该在其他团队中达到相等次数。
问题大小
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. 会议调度 复制链接链接已复制到粘贴板!
为开始时间和房间分配每个会议。会议具有不同的持续时间。
硬限制:
- 房间冲突:两个会议不得同时使用同一空间。
- 需要的参与:人无法同时拥有两个需要满足的会议。
- 需要的房间容量: 会议不能处于不满足所有会议的房间。
- 在同一天开始和结束:该会议不应多次调度。
中等限制:
- 首选参与:人不能同时拥有两种偏好会议,也不能同时具有首选和必需的会议。
软限制:
- 更早的时间,而不是之后:尽快调度所有会议。
- 会议间的中断:任何两个会议都应该至少有一个时间中断。
- 重叠的会议:为了最大程度减少并行会议的数量,因此用户不必选择另一个会议。
- 首先分配更大的空间:如果一个更大的空间可用,则应给这一房间分配任何会议,以便满足尽可能多的人,即使他们尚未注册该会议。
- 房间稳定性:如果人连续满足两个或更短的时间中断,则他们最好处于同一间。
问题大小
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 Track 3 - Curriculum 时间表) 复制链接链接已复制到粘贴板!
将每位安排成一个 timeslot 并放入一个房间。
硬限制:
- 竞争器冲突:通常不能在同一时间段内有两个竞争。
- 领导冲突:通常不能在同一时间段内有两个竞争。
- 空间 occupancy:两个公司不能在同一时间内位于同一空间中。
- 不可用周期(为每个数据集指定):特定持久性不能分配给特定的周期。
软限制:
- 房间容量: 房间的容量不应小于其领导活动数量。
- 最小工作天数:同一课程的显示应被分成最少的天数。
- 领导压缩性:属于同一技术的视图应相互相邻(在连续的时间段内)。
- 房间稳定性:同一课程的了解应分配给同一房。
这个问题由 国际时间建立复合的 Competition 2007 跟踪 3 定义。
问题大小
图 3.5. 域模型
3.12. 机器重新分配(Google ROADEF 2012) 复制链接链接已复制到粘贴板!
为机器分配每个进程。所有进程都已有原始(未优化)分配。每个进程都需要每个资源(如 CPU 或 RAM)的数量。这是 Cloud Balancing 示例的复杂版本。
硬限制:
- 最大容量 :不得超过每台机器的每个资源的最大容量。
- 冲突:同一服务的处理必须在不同的计算机上运行。
- 分散:同一服务的处理必须在位置之间分散。
- dependencies :根据另一个服务,服务的进程必须在其他服务进程的邻居运行。
- 临时使用:有些资源是临时的,是原始机器作为新分配的机器的最大容量。
软限制:
- load :不应超过每台机器的每个资源的安全容量。
- Balance :通过平衡每台计算机上的可用资源来保留将来分配的空间。
- 流程移动成本:流程有移动成本。
- 服务移动成本:服务具有移动成本。
- 机器移动成本:将进程从机器 A 移动到机器 B 具有另一个 A-B 的移动成本。
这个问题由 Google ROADEF/EURO Challenge 2012 定义。
图 3.6. 价值
问题大小
图 3.7. 域模型
3.13. vehicle 路由 复制链接链接已复制到粘贴板!
使用一组电话,获取每个客户的对象并将其带入耗尽。每个 vehicle 都可以为多个客户提供服务,但它有有限的容量。
除了基本问题单(CVRP)外,还有一个带有时间窗(CVRPTW)的变体。
硬限制:
- vehicle 容量: vehicle 无法执行更多项目,然后其容量。
时间窗(只在 CVRPTW 中):
- 轮转时间:从一个位置传输到另一个地方需要时间。
- 客户服务持续时间: vehicle 必须在服务持续时间内保持给客户。
- 客户就绪时间: vehicle 可在客户就绪时间之前到达,但必须等到为工作前的时间才会等待。
- 客户到期时间:在客户到期前必须及时到达时间。
软限制:
- 总距离:最小化所有 vehicles 的距离驱动的总距离(fuel 消耗)。
带容量的载体路由问题(CVRP)及其时间同步变体(CVRPTW)由 网络和早期优化(NEO) VRP 网站定义。
图 3.8. 价值
问题大小
CVRP 实例(没有时间窗):
CVRPTW 实例(使用时间窗):
3.13.1. Vehicle 路由的域模型 复制链接链接已复制到粘贴板!
带有时间窗域模型的 vehicle 路由大量使用 shadow 变量功能。这允许它更自然地表达限制,因为 arrivalTime 和 departureTime 等属性直接在域模型中可用。
临时区分无线差异
在真实世界中,vehicles 不能从位置到位置跟随行:它们必须使用 roads 和 highways。从一个角度来说,这很重要:
对于优化算法,只要可以查找两个点之间的距离(最好是预先计算)。road 成本甚至不需要是一个距离。它还有可能是大量时间、经济的成本或加权功能。一些技术可用于预先计算 road 成本,如 GraphHopper (embeddable, offline Java engine), Open MapQuest (web service)和 Google Maps Client API (web service)。
另外,还有一些技术来呈现它,如 开发人员的 Leaflet 和 Google Map。
甚至可以使用 GraphHopper 或 Google Map Directions 呈现实际 road 路由,但由于路由对高路重叠,看不到顺序:
请特别注意两个点之间的 road 成本与 OptaPlanner 中使用的优化标准相同。例如,GraphHopper 默认返回最快的路由,而不是最短的路由。不要使用最快速的 GPS 路由的 km (或 miles)距离来优化 OptaPlanner 中最短的往返:这会导致子优化解决方案,如下所示:
与流行不同,大多数用户都不希望使用最短的路由:他们希望使用最快的路由。它们更喜欢通常高路。它们更喜欢在 dirt roads 上正常的 roads。在现实环境中,最快速且最短的路由很少相同。
3.14. 项目作业调度 复制链接链接已复制到粘贴板!
以时间和执行模式调度所有作业,以最小化项目延迟。每个作业都是项目的一部分。作业可以以不同的方式执行:每种方法是执行模式,它意味着不同的持续时间,但也不同的资源使用量。这是灵活的 作业跃点调度的形式。
硬限制:
- 作业优先级:只有所有前者作业都完成时,才可以启动作业。
资源容量:不使用超过可用资源的资源。
- 资源是本地的(同一项目的作业之间共享)或全局(在所有作业之间共享)
- 资源是可续订(每天可用的容量)或不可续订(可续订的容量)
中等限制:
- 项目总延迟:最小化每个项目的持续时间(makespan)。
软限制:
- Total makespan:最小化整个多项目调度的持续时间。
这个问题由 MISTA 2013 挑战 定义。
问题大小
3.15. 任务分配 复制链接链接已复制到粘贴板!
将每个任务分配给员工队列中的 spot。每个任务都有一个持续时间,该持续时间会受到员工对任务的关联性级别的影响。
硬限制:
- 行业:每个任务都需要一个或多个学习。员工必须拥有所有这些知识。
软级别 0 限制:
- 关键任务:首先完成关键任务,早于主要和次要任务。
软级别 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 - 评估) 复制链接链接已复制到粘贴板!
将每个课程计划到一个句点,并放入一个房间。多个公司可以同时共享同一房。
硬限制:
- 评估冲突:不能在同一时间段内同时发生共享参与的两个技术。
- 房间容量 :房间的容量必须始终足够高。
- 持续时间: 周期的持续时间必须足以满足其所有销售期。
与周期相关的硬限制(按数据集指定):
- coincidence:两个指定的参与必须使用相同的句点(但可能为其他房间)。
- 排除:两个指定的参与不得同时使用同一周期。
- 之后:在另一个指定测试期后,必须在一个时间段内进行指定的考试。
与空间相关的硬限制(每个数据集指定):
- exclusive:一个指定的基准测试不必与任何其他课程共享其房间。
软限制(其中每个都具有参数损失):
- 同一站不应在一行中有两个。
- 同一成员不应在同一天上有两个活动。
- 周期分布:共享的参与应是多个句点。
- 混合持续时间:共享一个房间不应具有不同的持续时间。
- 前端负载:在计划早期应该调度大量时间。
- 周期损失(指定每个数据集):一些周期在使用时都有损失。
- 空间损失(指定每个数据集):一些空间在使用时有损失。
它使用大量实际生命常量集。
这个问题由 国际时间建立复杂时间定义,即 2007 跟踪 1。Geoffrey De Smet 完成 4th,与非常早版本的 OptaPlanner 合作。因此,进行很多改进。
问题大小
3.16.1. 用于测试时间设置的域模型 复制链接链接已复制到粘贴板!
下图显示了主要的评估域类:
图 3.11. 评估域类图
请注意,我们已将考试概念分成 考试 课程和主题课程。在解决时(这是计划实体类)的考试实例在它们的期间或房间属性发生变化时发生了变化。主题 Period 和 Room 实例在参与期间永远不会改变(这些是问题事实,就像某些其他类一样)。
3.17. Nurse rostering (INRC 2010) 复制链接链接已复制到粘贴板!
对于每个转换,请分配一个操作方式进行转换。
硬限制:
- 没有未分配的 转换(内置):每个转换都需要分配给员工。
- 移动冲突 :员工每天只能有一个过渡。
软限制:
合同费用。业务经常违反了这些情况,因此他们决定将它们定义为软限制,而非硬限制。
- 最小和最大分配 :每个员工需要超过 x 过渡,且少于 y 过渡(取决于其合同)。
- 最小和最大连续工作天数 :每个员工都需要连续在 x 和 y 天之间工作(取决于其合同)。
- 最小和最大连续的免费天数 :每个员工都需要在 x 到 y 天之间自由使用(取决于其合同)。
- 最小和最大连续的工作日 :每个员工都需要按行在 x 和 y 周线(取决于其合同)之间工作。
- 完成 每周:每个员工都需要每天在一周内或根本不工作。
- 周例期间 的相同迁移类型:对于同一员工的同一周例,每个每周过渡都必须相同。
- 不支持的模式 :一行中不需要的转换类型的组合,例如,之后移动后接一个早期的转换,后接一个之后的移动。
员工希望:
- 请求的 一天:员工希望在特定日期上工作。
- 第一天请求 :员工不希望在特定日期上工作。
- 根据请求迁移 :员工希望分配给特定的移动。
- 移动请求 :员工不想分配给特定的移动。
- 备选技术 :分配给专家的员工应该在这一迁移所需的每个技术方面有障碍。
此问题由 国际 Nurse Rostering Competition 2010 定义。
图 3.12. 价值
问题大小
有三个数据集类型:
- 过程:必须以秒为单位解决。
- Medium:必须以分钟为单位解决。
- 长:必须以小时为单位解决。
图 3.13. 域模型
3.18. 指导准入调度 复制链接链接已复制到粘贴板!
指导准入调度(PAS),也称为 过程的计划,为每个接受到 站的个人分配一个 bed。在计划的调度期间,将 bed 分配给参与的持续时间。每个方面都属于一个房间,每个房间属于一个部门。修复了过期日期的 arrival 和 departure 日期。您只需要分配一个 bed。
这个问题的功能比有限制的数据集。当不需要分配所有计划实体时,最好根据需要分配任意数量的实体,而不会破坏硬约束。这在受限制的规划中称为。
硬限制:
-
在同一月上,不能将两个公司分配给同一月。weight:
-1000hard114 conflictNightCount. -
房间可以有 gender 限制:只有 females, only males, same gender in the same month or no gender limitations.weight:
-50hard047ightCount. -
一个部门可以最少或最长期限。weight:
-100hardöightCount. -
参与可能需要与特定设备相关的房间。weight:
-50hard047ightCount.
中等限制:
-
除非数据集超过限制,否则为 bed 分配每个优点。权重:
-1medium047 DayCount.
软限制:
-
公司可以为最大空间大小指定首选项,例如,如果公司需要单一空间。weight:
-8soft theightCount. -
行业最能为于美国技术问题的一个部门分配。weight:
-10soft theightCount. 行业最能成为在技术固有问题方面特有的房间。Weight:
-20soft theightCount.-
房间特殊性应该是优先级 1。Weight:
-10soft the (priority - 1) nightCount.
-
房间特殊性应该是优先级 1。Weight:
-
参与可以为具有特定设备的空间指定首选项。Weight:
-20soft theightCount.
这个问题是 Kaho 的 Patient schedule 的一个变体,数据集来自真实世界的动机。
问题大小
图 3.14. 域模型
3.19. 趋势问题(TTP) 复制链接链接已复制到粘贴板!
调度匹配 n 个团队数。
硬限制:
- 每个团队都针对其他团队做两次操作两次:当家和一次性。
- 每个团队在每个 timeslot 上都有一个匹配项。
- 团队不能有超过三个以上的家或连续三个匹配。
- 无重复者:与团队相比,不能连续地匹配同一两个。
软限制:
- 最小化所有团队相换的总距离。
此问题在 Dan Trick 的网站(也包含了全局记录) 上定义。
问题大小
3.20. 更便宜的时间调度 复制链接链接已复制到粘贴板!
以时间和机器上调度所有任务,以最大程度降低功耗。电源价格因时间而异。这是 作业跃点调度的形式。
硬限制:
- 开始时间限制:每个任务都必须在最早的开始和最新开始限制之间启动。
- 最大容量 :不得超过每台机器的每个资源的最大容量。
- 启动和关闭 :每台机器必须在分配了任务的期间处于活跃状态。在任务之间,允许闲置,以避免启动和关闭成本。
中等限制:
功耗:最小化整个时间表的总功耗。
- 机器功耗:每个活跃或空闲的机器都会消耗电源,这取决于功耗(取决于该期间的电源价格)。
- 任务功耗:每个任务都消耗电源,这推断了功耗(取决于其期间的电源价格)。
- 机器启动和关闭成本 :机器每次启动或关闭时,都会产生额外的成本。
软限制(在原始问题定义中添加):
- Start early: Prefer 更早地启动一个任务,而不是之后启动。
问题由 ICON 质询 定义。
问题大小
3.21. 资产类分配(Portfolio Optimization) 复制链接链接已复制到粘贴板!
决定每个资产类参与的相对数量。
硬限制:
风险最大值:标准总差异不得高于标准影响最大值。
- 总标准差异计算通过应用 Markowitz Portfolio 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.22. 指导调度 复制链接链接已复制到粘贴板!
为每个电信分配一个 timeslot 和一个房间。Timeslots 可以重叠。可以使用 LibreOffice 或 Excel 编辑, 该文件可读写到和写入。
硬限制:
- 对话类型 timeslot :对话的类型必须与 timeslot 的对话类型匹配。
- 房间不可用时间:对话的空间必须在对话的 timeslot 期间可用。
- 房间冲突:两个对话无法在重叠的 timeslot 期间使用相同的空间。
- speaker 不可用时间:每个对话的 speaker 都必须在对话的 timeslot 期间可用。
- speaker 冲突:两个对话无法在重叠的 timeslot 期间共享 speaker。
通用目的 timeslot 和 room 标签:
- speaker 所需的 timeslot 标签:如果 speaker 具有所需的 timeslot 标签,则必须将所有其或她的对话分配到与该标签的 timeslot。
- speaker 禁止的 timeslot 标签:如果 speaker 具有禁止的 timeslot 标签,则其所有或她都不能分配给与该标签的 timeslot。
- 对话所需的 timeslot 标签:如果通信具有所需的 timeslot 标签,则必须将其分配给具有该标签的 timeslot。
- 禁止的 timeslot 标签:如果对话有禁止的 timeslot 标签,则无法将其分配给具有该标签的 timeslot。
- speaker 所需的 room 标签:如果 speaker 具有必需的 room 标签,则必须将所有其或她的对话分配到具有该标签的房间。
- speaker forbidden room tag:如果 speaker 具有禁止的 room 标签,则所有其或她的对话都不能分配给与该标签的房间。
- 对话所需的 room 标签:如果对话具有所需的 room 标签,则必须将其分配给具有该标签的房间。
- 对话空间标签 :如果对话具有禁止的 room 标签,则无法将其分配给具有该标签的房间。
- 对话互斥的 timelousive-talks 标签:共享这样的标签不能调度到重叠的 timeslots 中。
- 对话先决条件:在所有先决条件通信后,必须调度一个对话。
软限制:
- 主题跟踪冲突:最小化在重叠 timeslots 期间共享主题标签的对话数量。
- 扇区冲突:最小化在重叠 timeslots 期间共享相同的扇区标签的通信数量。
- 内容使用者级别流程违反:对于每个内容标签,请在高级通信前调度简介。
- Audience 级别区分 :对于每个时间线,最大化与不同使用者级别的对话数量。
- 语言离散性:对于每个 timeslot,最大程度提高与不同语言对话的数量。
通用目的 timeslot 和 room 标签:
- speaker 首选 timeslot 标签:如果 speaker 具有首选的 timeslot 标签,则所有自己或她的对话都应分配给与该标签的 timeslot。
- speaker undesired timeslot tag:如果 speaker 有一个不需要的 timeslot 标签,则不应将自己或她的对话分配给与该标签的 timeslot。
- 与首选 timeslot 标签通信:如果对话有一个首选的 timeslot 标签,则应将其分配给具有该标签的 timeslot。
- 对话不需要的 timeslot 标签:如果通信具有不需要的 timeslot 标签,则不应将其分配给具有该标签的 timeslot。
- speaker 首选空间标签:如果 speaker 具有首选空间标签,则所有自己或她的对话都应分配给具有该标签的房间。
- speaker undesired room tag:如果 speaker 有一个不需要的 room 标签,则不应将自己或她的对话分配给具有该标签的房间。
- 通信首选空间标签:如果对话有一个首选的空间标签,则应将其分配给具有该标签的房间。
- 对话不需要的房间标签:如果对话具有不需要的空间标签,则不应将其分配给具有该标签的房间。
- 相同的日期:所有与共享主题标签或内容标签的对话都应在最少天数内调度(以同一天表示)。
图 3.15. 价值
问题大小
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.23. rock 导览 复制链接链接已复制到粘贴板!
从显示驱动电能总线显示,但计划仅显示可用天数。
硬限制:
- 调度每个所需显示。
- 调度尽可能显示。
中等限制:
- 最大化专家机会。
- 最小化推动时间。
- 早于更新版本。
软限制:
- 避免长时间花费时间。
问题大小
47shows has 47 shows with a search space of 10^59.
47shows has 47 shows with a search space of 10^59.
3.24. 飞行人员的调度 复制链接链接已复制到粘贴板!
将 flights 分配给 pilots 和 flight 参与。
硬限制:
- 所需技术:每个 flight 分配都有一个必需的技术。例如,flight AB0001 需要 2 个 pilots 和 3 个动态活动。
- flight 冲突:每个员工可以同时参与一个 flight
- 在两个 flights 之间传输:在两个 flights 之间,员工必须能够从 arrival airport 传输到 departure airport。例如,An 在 Brussels 到达 Brussels 于 10:00,在 Amsterdam 到达 15:00。
- 员工不可用:员工必须在 flight 之日可用。例如,An 位于 1-Feb 上的 PTO 上。
软限制:
- 首先从家分配
- 最后分配位于家
- 每个员工的负载均衡 flight 持续时间
问题大小
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 示例 复制链接链接已复制到粘贴板!
您可以下载 OptaPlanner 示例,作为红帽客户门户网站上提供的 OptaPlanner 源软件包的一部分。
流程
导航到红帽客户门户网站中的 Software Downloads 页面(需要登录),然后从下拉菜单中选择产品和版本:
- 产品: 红帽构建的 OptaPlanner
- Version: 8.29
- 下载 红帽构建的 OptaPlanner 8.29 Sources。
提取
rhbop-8.29.0-red_hat_build_of_optaplanner-sources.zip文件。提取的
org.red_hat_build_of_optaplanner-red_hat_build_of_optaplanner-8.29.0.Final-redhat-00009/red_hat_build_of_optaplanner-examples/src/main/java/org/optaplanner/examples目录包含示例源代码。要构建示例,在
org.red_hat_build_of_optaplanner-red_hat_build_of_optaplanner-8.29.0.Final-redhat-00009目录中输入以下命令:mvn clean install -Dquickly
mvn clean install -DquicklyCopy to Clipboard Copied! Toggle word wrap Toggle overflow 进入 examples 目录:
red_hat_build_of_optaplanner-examples
red_hat_build_of_optaplanner-examplesCopy to Clipboard Copied! Toggle word wrap Toggle overflow 要运行示例,请输入以下命令:
mvn exec java
mvn exec javaCopy to Clipboard Copied! Toggle word wrap Toggle overflow
第 5 章 OptaPlanner 和 Quarkus 入门 复制链接链接已复制到粘贴板!
您可以使用 https://code.quarkus.redhat.com 网站生成红帽构建的 OptaPlanner Quarkus Maven 项目,并自动添加并配置要在应用程序中使用的扩展。然后您可以下载 Quarkus Maven 存储库,或使用在线 Maven 存储库与项目一起使用。
5.1. Apache Maven 和 Red Hat build of Quarkus 复制链接链接已复制到粘贴板!
Apache Maven 是 Java 应用程序开发中使用的分布式构建自动化工具,用于创建、管理和构建软件项目。Maven 使用名为 Project Object Model(POM)文件的标准配置文件来定义项目并管理构建流程。POM 文件描述了使用 XML 文件生成项目打包和输出的模块和组件依赖项、构建顺序和目标。这可确保以正确、一致的方式构建项目。
Maven 存储库
Maven 存储库存储 Java 库、插件和其他构建构件。默认公共存储库是 Maven 2 Central Repository,但存储库可以是私有的和内部存储库,以在开发团队之间共享通用工件。也可从第三方提供存储库。
您可以将在线 Maven 存储库与 Quarkus 项目搭配使用,也可以下载 Red Hat build of Quarkus Maven 存储库。
Maven 插件
Maven 插件定义 POM 文件的一部分,该文件可以达到一个或多个目标。Quarkus 应用程序使用以下 Maven 插件:
-
Quarkus Maven 插件(
quarkus-maven-plugin):启用 Maven 创建 Quarkus 项目,支持生成 uber-JAR 文件,并提供开发模式。 -
Maven Surefire 插件(
maven-surefire-plugin):在构建生命周期的测试阶段使用,以便在应用程序上执行单元测试。插件生成包含测试报告的文本和 XML 文件。
5.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
5.1.2. 下载并配置 Quarkus Maven 存储库 复制链接链接已复制到粘贴板!
如果您不想使用在线 Maven 存储库,您可以下载并配置 Quarkus Maven 存储库,以使用 Maven 创建 Quarkus 应用程序。Quarkus Maven 存储库包含 Java 开发人员通常用于构建应用程序的许多要求。此流程描述了如何编辑 settings.xml 文件来配置 Quarkus Maven 存储库。
当您通过修改 Maven settings.xml 文件来配置存储库时,更改会应用到所有 Maven 项目。
流程
- 从红帽客户门户的软件下载页面(需要登录) 下载 Red Hat build of Quarkus Maven 存储库 ZIP 文件。
- 展开下载的存档。
-
将目录更改为
~/.m2/目录,并在文本编辑器或集成开发环境(IDE)中打开 Mavensettings.xml文件。 将以下行添加到
settings.xml文件的 <profiles> 元素中,其中QUARKUS_MAVEN_REPOSITORY是您下载的 Quarkus Maven 存储库的路径。QUARKUS_MAVEN_REPOSITORY的格式必须是file://$PATH,如file:///home/userX/rh-quarkus-2.7.6.GA-maven-repository/maven-repository。Copy to Clipboard Copied! Toggle word wrap Toggle overflow 将以下行添加到
settings.xml文件的 <activeProfiles> 元素中,并保存文件。<activeProfile>red-hat-quarkus-maven-repository</activeProfile>
<activeProfile>red-hat-quarkus-maven-repository</activeProfile>Copy to Clipboard Copied! Toggle word wrap Toggle overflow
如果您的 Maven 存储库包含过时的工件,您可能会在构建或部署项目时遇到以下 Maven 错误消息之一,其中 ARTIFACT_NAME 是缺少的工件的名称,PROJECT_NAME 是您要构建的项目的名称:
-
缺少工件 PROJECT_NAME -
[ERROR] 无法对项目 ARTIFACT_NAME; Could 没有解析 PROJECT_NAME的依赖关系
要解决这个问题,请删除 ~/.m2/repository 目录中的本地存储库的缓存版本,以强制下载最新的 Maven 工件。
5.2. 使用 Maven 插件创建 OptaPlanner 红帽构建的 Quarkus Maven 项目 复制链接链接已复制到粘贴板!
您可以使用 Apache Maven 和 Quarkus Maven 插件使用 OptaPlanner 和 Quarkus 应用程序启动并运行。
先决条件
- 已安装 OpenJDK 11 或更高版本。Red Hat build of Open JDK 位于红帽客户门户网站中的 Software Downloads 页面中(需要登录)。
- 已安装 Apache Maven 3.8 或更高版本。Maven 可从 Apache Maven 项目网站 获得。
流程
在命令终端中,输入以下命令验证 Maven 是否使用 JDK 11,并且 Maven 版本是否为 3.8 或更高版本:
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 表 5.1. mvn io.quarkus:quarkus-maven-plugin:2.7.6.Final-redhat-00006:create 命令中使用的属性 属性 描述 projectGroupId项目的组 ID。
projectArtifactId项目的工件 ID。
extensions用于此项目的 Quarkus 扩展列表。如需 Quarkus 扩展的完整列表,请在命令行中输入
mvn quarkus:list-extensions。noExamples创建带有项目结构的项目,但没有测试或类。
projectGroupID和projectArtifactID属性的值用于生成项目版本。默认项目版本为1.0.0-SNAPSHOT。
要查看您的 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
您可以使用 code.quarkus.redhat.com 网站生成 OptaPlanner Quarkus Maven 项目,并自动添加和配置您要在应用程序中使用的扩展。
本节介绍了生成 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 项目。
-
输入要在项目生成的工件中使用的版本。此字段的默认值为
1.0.0-SNAPSHOT。建议使用 语义版本 控制,但如果您偏好使用不同类型的版本。 输入构建工具在打包项目时生成的工件软件包名称。
根据 Java 软件包命名约定,软件包名称应与用于项目的组名称匹配,但您可以指定不同的名称。
注意code.quarkus.redhat.com网站自动使用最新版本的 OptaPlanner。您可在生成项目后手动更改pom.xml文件中的 BOM 版本。选择要作为依赖项包含的以下扩展:
- RESTEasy JAX-RS (quarkus-resteasy)
- resteasy Jackson (quarkus-resteasy-jackson)
- optaPlanner AI constraint resolver (optaplanner-quarkus)
optaPlanner Jackson (optaplanner-quarkus-jackson)
红帽为列表上的单个扩展提供不同级别的支持,由每个扩展名称旁的标签表示:
- 红帽完全支持 SUPPORTED 扩展,用于生产环境中的企业级应用程序。
- 在 技术预览功能支持范围 下,红帽对生产环境中的 支持 受到有限的支持。
- 红帽不支持将 DEV-SUPPORT 扩展用于生产环境,但红帽提供的核心功能由红帽开发人员支持用于开发新应用程序。
DEPRECATED 扩展计划被替换为提供相同功能的较新的技术或实施。
红帽不支持未标记的扩展用于生产环境。
- 选择 Generate your application 来确认您的选择并显示覆盖页面,其中包含包含您生成的项目的存档的下载链接。覆盖屏幕还显示可用于编译和启动应用程序的自定义命令。
- 选择 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
5.4. 使用 Quarkus CLI 创建 Red Hat build of Quarkus Maven 项目 复制链接链接已复制到粘贴板!
您可以使用 Quarkus 命令行界面(CLI)创建 Quarkus OptaPlanner 项目。
先决条件
- 已安装 Quarkus CLI。如需更多信息,请参阅使用 Quarkus 命令行界面构建 Quarkus 应用程序。
流程
创建 Quarkus 应用程序:
quarkus create app -P io.quarkus:quarkus-bom:2.7.6.Final-redhat-00006
quarkus create app -P io.quarkus:quarkus-bom:2.7.6.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 (例如,任何
NQueens实例的第一个 Fit 和 Tabu Search solver)。 - 从您的数据层 加载问题数据集 (例如 Four Queens 实例)。这是计划问题。
-
使用
Solver.solve (problem) 解决它,该 Solver.solve (problem)返回最佳解决方案。
第 6 章 配置 OptaPlanner solver 复制链接链接已复制到粘贴板!
您可以使用以下方法配置 OptaPlanner solver:
- 使用 XML 文件。
-
使用
SolverConfigAPI。 - 在域模型中添加类注解和 JavaBean 属性注解。
- 控制 OptaPlanner 用来访问域的方法。
- 定义自定义属性。
6.1. 使用 XML 文件配置 OptaPlanner solver 复制链接链接已复制到粘贴板!
每个项目都有一个可以编辑的 solver 配置文件。& lt;EXAMPLE>SolverConfig.xml 文件位于 org.red_hat_build_of_optaplanner-red_hat_build_of_optaplanner-8.29.0.Final-redhat-00009/red_hat_build_of_optaplanner-examples/src/main/resources/org/optaplanner/examples/<EXAMPLE > 目录,其中 < EXAMPLE > 是 OptaPlanner 示例项目的名称。或者,您可以从带有 Solver 的文件创建 SolverFactory。但是,出于可移植性的原因,建议使用 classpath 资源。
Factory.createFromXmlFile ()
Solver 和 SolverFactory 都有一个名为 Solution_ 的通用类型,这是代表规划问题和解决方案的类。
OptaPlanner 通过更改配置使其相对容易地切换优化算法。
流程
-
使用
构建 Solver 实例。SolverFactory 配置 solver 配置 XML 文件:
- 定义模型。
- 定义 score 功能。
可选:配置优化算法。
以下示例是 NQueens 问题的解析器 XML 文件:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 注意在某些环境中,如 192.168.1.0/24 和 JBoss 模块,在 JAR 文件中的 resolver 配置、分数 DRL 和域类等类路径资源可能不适用于
optaplanner-coreJAR 文件的默认ClassLoader。在这些情况下,为您的类的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/optional/nqueensSolverConfig.xml"); Solver<NQueens> solver = solverFactory.buildSolver();SolverFasctory<NQueens> solverFactory = SolverFactory.createFromXmlResource( "org/optaplanner/examples/nqueens/optional/nqueensSolverConfig.xml"); Solver<NQueens> solver = solverFactory.buildSolver();Copy to Clipboard Copied! Toggle word wrap Toggle overflow
6.2. 使用 Java API 配置 OptaPlanner solver 复制链接链接已复制到粘贴板!
您可以使用 SolverConfig API 配置 solver。这对在运行时动态更改值特别有用。以下示例在在 NQueens 项目中构建 Solver 前更改基于系统属性的运行时间:
solver 配置 XML 文件中的每个元素都作为 Config 类或软件包命名空间 org.optaplanner.core.config 中的 Config 类提供。这些配置 类是 XML 格式的 Java 表示。它们构建软件包命名空间 org.optaplanner.core.impl 的运行时组件,并将其编译成一个高效的 Solver。
要为每个用户请求动态配置 SolverFactory,请在初始化过程中构建模板 SolverConfig,并使用每个用户请求的复制构造器复制它。以下示例演示了如何在 NQueens 问题中执行此操作:
6.3. OptaPlanner 注解 复制链接链接已复制到粘贴板!
您必须指定域模型中的哪些类正在规划实体,哪些属性是规划变量等。使用以下方法之一为您的 OptaPlanner 项目添加注解:
- 在域模型中添加类注解和 JavaBean 属性注解。属性注解必须位于 getter 方法上,而不是在 setter 方法上。注解的 getter 方法不需要公共。这是推荐的方法。
- 在域模型中添加类注解和字段注解。注解的字段不需要是公共的。
6.4. 指定 OptaPlanner 域访问 复制链接链接已复制到粘贴板!
默认情况下,OptaPlanner 使用反映访问您的域。与直接访问相比,反映是可靠的,但会慢。或者,您可以配置 OptaPlanner 以使用 Gizmo 访问域,这将生成字节码来直接访问域的字段和方法,而无需反映。但是,此方法有以下限制:
- 规划注解只能在公共字段和公共 getters 上。
-
io.quarkus.gizmo:gizmo必须位于 classpath 上。
当您将 OptaPlanner 与 Quarkus 搭配使用时,这些限制不适用,因为 Gizmo 是默认域访问类型。
流程
要在 Quarkus 之外使用 Gizmo,请在 solver 配置中设置 domainAccessType :
<solver>
<domainAccessType>GIZMO</domainAccessType>
</solver>
<solver>
<domainAccessType>GIZMO</domainAccessType>
</solver>
6.5. 配置自定义属性 复制链接链接已复制到粘贴板!
在 OptaPlanner 项目中,您可以添加自定义属性来解析实例化类和明确提到自定义属性的文档。
先决条件
- 您有一个临时解决方案。
流程
添加自定义属性。
例如,如果您的
EasyScoreCalculator具有缓存的重度计算,并且您希望在一个基准中增加缓存大小,请添加myCacheSize属性:Copy to Clipboard Copied! Toggle word wrap Toggle overflow 为每个自定义属性添加公共集,该属性在构建
Solver时调用。Copy to Clipboard Copied! Toggle word wrap Toggle overflow 大多数值类型都被支持,包括
布尔值、Inint、双、MutitDecimal、string和enums。
第 7 章 OptaPlanner Solver 复制链接链接已复制到粘贴板!
解决方法可以找到您规划问题的最佳解决方案。解决者一次只能解决一个规划问题实例。solvers 使用 Solver Factory 方法 构建:
A solver 应该只从单一线程访问,除了在 javadoc 中作为 thread-safe 记录的方法除外。solve () 方法记录当前线程。处理线程可能会导致 REST 服务的 HTTP 超时,它需要额外的代码来并行处理多个数据集。要避免这个问题,请使用 SolverManager。
7.1. 解决问题 复制链接链接已复制到粘贴板!
使用 solver 解决规划问题。
先决条件
-
从 solver 配置构建的
Solver -
代表规划问题实例的
@PlanningSolution注释
流程
提供规划问题作为 resolve ()方法 的参数。该解决方案将返回最佳解决方案。
以下示例解决了 NQueens 问题:
NQueens problem = ...;
NQueens bestSolution = solver.solve(problem);
NQueens problem = ...;
NQueens bestSolution = solver.solve(problem);
在本例中,solv () 方法将返回 NQueens 实例,每个 Queen 分配给 Row。
提供给解析 (Solution) 方法的解决方案实例可以部分或完全初始化,通常是重复规划的情况。
图 7.1. 在 8ms 中为 Four Queens Puzzle (Also Optimal Solution)的最佳解决方案
根据问题大小和 solver 配置,解析 (Solution) 方法可能需要很长时间。Solver 智能处理可能解决方案的搜索空间,并记住其在技术期间遇到的最佳解决方案。根据多个因素,包括问题大小、Solver 提供的时间、解析器配置等,最佳解决方案 可能 是最佳解决方案。
提供给方法解析 (Solution) 的解决方案实例由 Solver 更改,但不会为最佳解决方案错误。
方法解析 (Solution) 或 getBestSolution () 返回的解决方案实例很可能是提供给方法解析 (Solution) 的计划克隆,这意味着它是一种不同的实例。
7.2. solver 环境模式 复制链接链接已复制到粘贴板!
通过 resolver 环境模式,您可以检测实施中的常见错误。它不会影响日志级别。
solver 有一个随机实例。有些解析器配置使用随机实例,很多超过其他实例。例如,Simulated Annealing 算法高度依赖于随机数字,而 Tabu Search 依赖于它来解决分数。环境模式会影响该随机实例的 seed。
您可以在 solver 配置 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>
以下列表描述了您可以在 resolver 配置文件中使用的环境模式:
-
FULL_ASSERT模式打开所有断言,例如,增量分数计算在移动实施、约束、引擎本身等中不可修正的断言,对移动实施、约束、引擎本身等错误而造成故障。这个模式是可重复生成的。它还是入侵的,因为它调用方法computeScore ()比非 assert 模式更频繁。FULL_ASSERT模式非常慢,因为它不依赖于增量分数计算。
-
NON_INTRUSIVE_FULL_ASSERT模式开启几个断言,针对移动实施中的一个错误、约束、引擎本身等。这个模式是可重复生成的。它不是预期的,因为它不会调用方法 computeScore (),比非 assert 模式更频繁。NON_INTRUSIVE_FULL_ASSERT模式非常慢,因为它不依赖于增量分数计算。
-
FAST_ASSERT模式打开大多数断言,例如 undoMove 的分数与 Move 的分数相同,对移动实施、约束、引擎本身等中的一个错误进行故障。这个模式是可重复生成的。它还是入侵的,因为它调用方法computeScore ()比非 assert 模式更频繁。FAST_ASSERT模式较慢。编写一个测试问题单,使用FAST_ASSERT模式运行您的规划问题。
REHQUCIBLE模式是默认模式,因为在开发过程中建议使用它。在这个模式中,两个以同一 OptaPlanner 版本按相同的顺序执行相同的代码。这两个运行在每个步骤中都有相同的结果,除了以下备注除外。这可让您一致地重现错误。它还允许您对某些重构进行基准测试,如分数约束性能优化。注意尽管使用
REHQCIBLE模式,但因为以下原因,您的应用程序可能仍然无法完全可重复生成:-
使用
HashSet或其他集合,其顺序在 JVM 运行用于规划实体或规划值的集合,而不是常规问题事实,特别是在解决方案实施中。使用LinkedHashSet替换它。 - 合并一个耗时的依赖算法,最重要的是 Simulated Annealing 算法,以及花费的时间终止。分配的 CPU 时间有足够大的区别会影响时间 gradient 值。将 Simulated Annealing 算法替换为 Late Acceptance 算法,或使用步骤计数终止替换终止时间。
-
使用
-
REHQUCIBLE模式可能比NON_REHQUCIBLE模式稍慢。如果您的生产环境可从可重复生成的中受益,请在生产环境中使用此模式。在实践中,如果没有指定 seed,则REHQUCIBLE模式使用默认的固定随机 seed,而且会禁用某些并发优化,如工作窃取。
-
NON_REHQUCIBLE模式比REHQUCIBLE模式稍快。在开发过程中避免使用它,因为它使调试和程序错误修复变得困难。如果在生产环境中可重复生成性并不重要,则在生产环境中使用NON_REHQUCIBLE模式。实际上,如果没有指定 seed,则此模式不使用固定的随机 seed。
7.3. 更改 OptaPlanner 解析器日志记录级别 复制链接链接已复制到粘贴板!
您可以更改 OptaPlanner solver 中的日志级别,以查看解析器活动。以下列表描述了不同的日志记录级别:
错误 :日志错误,但那些作为
RuntimeException抛出到调用代码的除外。如果发生错误,OptaPlanner 通常会快速失败。它将抛出一个
RuntimeException的子类,并将详细信息放到调用代码。为避免重复的日志消息,它不会将其记录为错误。除非调用代码明确捕获并消除RuntimeException,否则线程的默认 'ExceptionHandler会将它记录为错误。同时,代码被破坏导致进一步的损害或模糊处理错误。- 警告 :日志可疑的情况
- info :记录每个阶段和解析器本身
- Debug :记录每个阶段的每个步骤
- trace :记录每个阶段的每个步骤的每次移动
指定 trace 日志记录会大大降低性能。但是,在开发过程中,追踪 日志记录非常有用,以发现瓶颈。
即使 调试日志记录 对于快速步骤算法(如 Late Acceptance 和 Simulated Annealing)的性能要下降,但不适用于降低步骤算法,如 Tabu Search。
trace 和 debug 日志记录都会导致多线程与大多数附加者的拥塞。
在 Eclipse 中,调试 到控制台的日志记录往往会因为分数计算速度超过 10000 每秒造成拥塞。IntelliJ 或 Maven 命令行都不受此问题的影响。
流程
将日志记录级别设置为 debug 日志记录,以查看阶段何时结束以及执行快速步骤。
以下示例显示了 debug 日志记录的输出:
所有花费的时间都以毫秒为单位。
所有都记录到 SLF4J,这是一个一个简单的日志记录信息,它将每个日志消息委派给 Logback、Apache Commons Logging、Log4j 或 java.util.logging。为您选择的日志记录框架添加依赖项。
7.4. 使用 Logback 记录 OptaPlanner solver 活动 复制链接链接已复制到粘贴板!
logback 是推荐的日志框架,用于 OptaPlanner。使用 Logback 记录 OptaPlanner solver 活动。
先决条件
- 您有一个 OptaPlanner 项目。
流程
在您的 OptaPlanner 项目的
pom.xml文件中添加以下 Maven 依赖项:注意您不需要添加额外的网桥依赖项。
<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> 是 第 7.4 节 “使用 Logback 记录 OptaPlanner solver 活动” 中列出的日志记录级别。Copy to Clipboard Copied! Toggle word wrap Toggle overflow 可选:如果您有一个多租户应用程序,其中多个
Solver实例可能会同时运行,请将每个实例的日志分成单独的文件中:使用 Mapped mailbox Context (MDC)组成
solve ()调用: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 注意当运行多个解析程序或一个多线程解决时,大多数附加程序(包括控制台)会导致
debug和trace日志记录的拥塞。切换到 async 附加程序以避免出现这个问题或关闭debug日志记录。
-
如果 OptaPlanner 没有识别新级别,请临时添加系统属性
-Dlogback.LEVEL=true以进行故障排除。
7.5. 使用 Log4J 记录 OptaPlanner solver 活动 复制链接链接已复制到粘贴板!
如果您已使用 Log4J,且您不想切换到其更快的成功者 Logback,您可以为 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> 是 第 7.4 节 “使用 Logback 记录 OptaPlanner solver 活动” 中列出的日志级别。Copy to Clipboard Copied! Toggle word wrap Toggle overflow 可选:如果您有一个多租户应用程序,其中多个
Solver实例可能会同时运行,请将每个实例的日志分成单独的文件中:使用 Mapped mailbox Context (MDC)组成
solve ()调用: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 注意当运行多个解析程序或一个多线程解决时,大多数附加程序(包括控制台)会导致
debug和trace日志记录的拥塞。切换到 async 附加程序以避免出现这个问题或关闭debug日志记录。
7.6. 监控解析器 复制链接链接已复制到粘贴板!
OptaPlanner 通过 Micrometer (一个 Java 应用程序的指标检测库)公开指标。您可以将 Micrometer 与流行的监控系统一起使用,来监控 OptaPlanner solver。
7.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 要在开发模式下运行应用程序,请输入以下命令:
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
7.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
7.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.total:自测量开始开始时发生的错误总数。 -
optaPlanner.solver.solve-length.active-count: 当前解决者的数量。 -
optaPlanner.solver.solve-length.seconds-max: 运行 longest-running 当前活跃解析程序的时间。 -
optaPlanner.solver.solve-length.seconds-duration-sum: 每个活跃解析器解析持续时间的总和。例如,如果有两个活跃的解析器,一个会运行一个三分钟,另一个一分钟,总地址时间为 4 分钟。
-
7.6.4. 其他指标 复制链接链接已复制到粘贴板!
如需更详细的监控,您可以在 solver 配置中配置 OptaPlanner,以以性能成本监控其他指标。以下示例使用 BEST_SCORE 和 SCORE_CALCULATION_COUNT 指标:
您可以在此配置中启用以下指标:
-
SOLVE_DURATION(默认为 Micrometer 分段 ID:optaplanner.solver.solve.duration):测量活跃解析器的持续时间、活动解析器的数量以及所有活动解析器的累积持续时间。 -
ERROR_COUNT(默认启用, Micrometer measure ID:optaplanner.solver.errors):测量在过期时发生的错误数量。 -
SCORE_CALCULATION_COUNT(默认启用 Micrometer 分段 ID:optaplanner.solver.score.calculation.count) :测量执行 OptaPlanner 的分数计算数。 -
BEST_SCORE(Micrometer geo ID:optaplanner.solver.best.scoreö : 测量 OptaPlanner 已找到的最佳解决方案分数。每个分数级别都有单独的分段。例如,对于Hard SoftScore,有optaplanner.solver.best.score.hard.score和optaplanner.solver.best.score.soft.score修饰符。 -
STEP_SCORE(Micrometer geo ID:optaplanner.solver.step.scorethe): 测量 OptaPlanner 所采取的每个步骤的分数。每个分数级别都有单独的分段。例如,对于Hard SoftScore,有optaplanner.solver.step.score.hard.score和optaplanner.solver.step.score.soft.score修饰符。 -
BEST_SOLUTION_MUTATION(Micrometer measure ID:optaplanner.solver.best.solution.mutation: 测量连续最佳解决方案间更改的规划变量的数量。 -
MOVE_COUNT_PER_STEP(Micrometer geo ID:optaplanner.solver.step.move.count):测量步骤中评估的移动数量。 -
MEMORY_USE(Micrometer geo ID:jvm.memory.used):测量 JVM 之间使用的内存量。此指标不测量解析器使用的内存量;同一 JVM 上的两个地址将报告此指标的值相同的值。 -
CONSTRAINT_MATCH_TOTAL_BEST_SCORE(Micrometer the ID:optaplanner.solver.constraint.match.best.score : 评估每个约束对目前找到的最佳解决方案的影响。每个分数级别都有单独的分段,每个约束的标签。例如,对于软件包 "com.example" 中的HardSoftScore的约束"Minimize Cost",有带有标签 "constraint.package=com.example" 和 ".score.constraint.constraint.best.score.name=Minimize Cost"。constraint.name=Minimize Cost" 的 optaplanner.solver.match.best.score.soft -
CONSTRAINT_MATCH_TOTAL_STEP_SCORE(Micrometer measure ID:optaplanner.solver.constraint.match.step.score114 : 评估每个约束对当前步骤的分数影响。每个分数级别都有单独的分段,每个约束的标签。例如,对于软件包 "com.example" 中的HardSoftScore的约束"Minimize Cost",带有标签 "constraint.package=com.example" 和 "constraint.name=Minimimize Cost" 的选择器。 -
PICKED_MOVE_TYPE_BEST_SCORE_DIFF(Micrometer the ID:optaplanner.solver.move.type.best.score.diff114):衡量特定移动类型提高了最佳解决方案。每个分数级别都有单独的分段,带有移动类型的标签。例如,对于进程的计算机的Hard SoftScore和ChangeMove,有optaplanner.solver.move.type.best.type.best.diff.hard.score 和traffics,标签optaplanner.solver.move.type.best.score.diff.soft.scoremove.type=ChangeMove (Process.computer)。 -
PICKED_MOVE_TYPE_STEP_SCORE_DIFF(Micrometer the ID:optaplanner.solver.move.type.step.score.diff114):衡量特定移动类型提高了最佳解决方案。每个分数级别都有单独的分段,带有移动类型的标签。例如,对于进程的计算机,对于Hard SoftScore和ChangeMove,有optaplanner.solver.move.type.step.score.diff.hard.score和optaplanner.solver.move.type.step.score.diff.soft.scoretraffics,标签move.type=ChangeMove (Process.computer)。
7.7. 配置随机数生成器 复制链接链接已复制到粘贴板!
许多 heuristics 和 metaheuristics 依赖于伪随机数生成器来移动选择,以解决分数、基于概率的移动接受等。在此期间,会重复使用同一随机实例,以提高随机值的可重复生成性、性能和统一分发。
随机 seed 是一个数字,用于初始化伪随机数生成器。
流程
可选: 要更改随机实例的随机 seed,请指定
randomSeed:<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 可选: 要更改伪随机数生成器实现,请为下面的 resolver 配置文件中列出的
randomType属性指定一个值,其中 <RANDOM_NUMBER_GENERATOR> 是一个伪随机数生成器:<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 支持以下伪随机数生成器:
-
JDK(默认):标准随机数生成器实现(java.util.Random) -
MERSENNE_TWISTER: Commons Math的随机数生成器实现 -
WELL512A,WELL1024A,WELL19937A,WELL19937C,WELL44497A和WELL44497B: Commons Math的 Random 数生成器实现
-
对于大多数用例,randomType 属性的值不会影响对多个数据集的最佳解决方案的平均质量。
第 8 章 OptaPlanner SolverManager 复制链接链接已复制到粘贴板!
SolverManager 是一个或多个 Solver 实例的一个传真,以简化 REST 和其他企业服务中的潜在规划问题。
与 Solver.solve (…) 方法不同,S olverManager 具有以下特征:
-
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());
提交到 Solver Manager.solve (…)方法的 每个问题都必须具有唯一的问题 ID。稍后调用 getSolverStatus (problemId) 或 killEarly (problemId) 使用该问题 ID 来区分规划问题。问题 ID 必须是不可变类,如 Long、字符串 或 java.util.UUID。
SolverManagerConfig 类有一个 parallelSolverCount 属性,用于控制并行运行多少解析程序。例如,如果 parallelSolverCount 属性设置为 4,并且您提交了五个问题,则 4 个问题将立即开始,并且第五个问题在第一个问题结束时启动。如果这些问题每五分钟解决,则五个问题需要 10 分钟才能完成。默认情况下,parallelSolverCount 设置为 AUTO,它将解析为 CPU 内核一半,而不考虑解析者的 moveThreadCount。
要检索最佳解决方案,在参与后,通常会使用 SolverJob.getFinalBestSolution () :
但是,在用户需要解决方案以及用户主动等待解决方案时,在用户需要解决方案前,对业务批处理问题都有更好的方法。
当前 SolverManager 实施在一台计算机节点上运行,但将来的工作旨在在云中分发解析程序负载。
8.1. 批量问题 复制链接链接已复制到粘贴板!
批量参与是并行多个数据集。批量参与对时间特别有用:
- 在每天的中间,通常有一些或没有问题更改。有些组织实施截止时间,例如,在周等 前提交所有一天的请求。
- resolvers 可以运行更长的时间(通常为几小时),因为 nobody 等待结果,CPU 资源通常较低。
- 当员工到达下一工作日时,可以使用相应的解决方案。
流程
要批量解决问题,受 parallelSolverCount 限制,请为每个数据集调用 solve (…) 创建以下类:
8.2. 解决并侦听显示进度 复制链接链接已复制到粘贴板!
当用户等待解决方案时运行某个问题时,用户可能需要等待几分钟或小时才能收到结果。为确保用户一切正常,通过显示最佳解决方案和最佳分数来显示进度。
流程
要处理中间最佳解决方案,请使用
solveAndListen (…):Copy to Clipboard Copied! Toggle word wrap Toggle overflow 这种实施使用数据库与 UI 通信,该 UI 会轮询数据库。更高级的实施将最佳解决方案直接推送到 UI 或消息传递队列。
-
当用户满足中间最佳解决方案且不想等待任何更好解决方案时,请调用
SolverManager.terminateEarly (problemId)。
部分 IV. OptaPlanner 快速启动指南 复制链接链接已复制到粘贴板!
OptaPlanner 提供以下快速启动指南,以演示如何与不同的技术集成:
- OptaPlanner on Red Hat build of Quarkus: 一个 SVVP timetable 快速启动指南
- Red Hat build of Quarkus 的 OptaPlanner:一个 vaccination appointment scheduler quick Start 指南
- Red Hat build of Quarkus 的 OptaPlanner:一个员工调度程序快速启动指南
- Spring Boot 上的 OptaPlanner:一个可写入时间的快速启动指南
- 带有 Java solvers 的 OptaPlanner:一个 SVVP timetable 快速启动指南
本指南指导您完成创建带有 OptaPlanner 约束智能(AI)的红帽构建的 Quarkus 应用程序的过程。您将构建 REST 应用程序,该应用程序针对领导和专业者进行了优化。
您的服务将通过 AI 遵循以下硬和软 调度限制,自动将附件实例分配给 Timeslot 和 Room 实例:
- 空间最多可以同时有一个小时间。
- 一个架构师可以同时参与一个小时间。
- 每天最多可同时参与一个小时间。
- 交互式人员更喜欢在单个房间参与。
- 指导人员更喜欢参与较少的活动,并排解少量间的差距。
mathematly 认为是 NP 硬性问题。这意味着难以扩展。只需将所有可能的组合与括号一起进行迭代,可以在非平量数据集中使用数百万年,即使在超级计算器上也是如此。AI 约束解析器(如 OptaPlanner)有高级算法,可在合理的时间内提供最接近的解决方案。认为是合理的时间,这取决于您的问题的目标。
先决条件
- 已安装 OpenJDK 11 或更高版本。Red Hat build of Open JDK 位于红帽客户门户网站中的 Software Downloads 页面中(需要登录)。
- 已安装 Apache Maven 3.8 或更高版本。Maven 可从 Apache Maven 项目网站 获得。
- 提供了 IDE,如 IntelliJ IDEA、VSCode、Ecli 或 NetBeans。
- 红帽构建的 Quarkus 项目提供了一个 OptaPlanner。有关创建 OptaPlanner Red Hat build of Quarkus 项目的说明,请参阅 Red Hat Build of OptaPlanner 部分的"使用 OptaPlanner 和 Quarkus"。
9.1. 对域对象建模 复制链接链接已复制到粘贴板!
OptaPlanner timetable 项目的目标是将每个小项分配到一个时间插槽和房间。要做到这一点,添加三个类: Timeslot、init on 和 Room,如下图所示:
timeslot
Timeslot 类代表当减少活动为过期的时间间隔时,例如,Monday 10:30 - 11:30 或 Tuesday 13:30 - 14:30。在这个示例中,所有时间段都有相同的持续时间,在 lunch 或其他中断过程中没有时间插槽。
一个时间插槽没有日期,因为每周计划只重复一次。不需要 持续规划。timeslot 被称为 问题事实,因为公司不会更改 Timeslot 实例。此类类不需要任何特定于 OptaPlanner 的注解。
房间
Room 类代表更小活动的位置,例如 Room A 或 Room B。在本例中,所有房间都没有容量限制,它们可以容纳所有定义。
空间 实例在竞争过程中不会改变,因此 Room 也是 问题事实。
lesson
在小时间中,由周一类代表,受一组参与者代表的规定,例如,A.Turing for 9th rating 或 Chemistry by M.Curie 代表第 10 等级。如果一个主题是按同一站组相同的每周的多次,则有多个仅可通过 id 区分的实例。例如,第 9 等级每周有六个数学。
在 OptaPlanner 期间,OptaPlanner 会更改周一类的 timeslot 和 room 字段,以将每个小时间分配给一个时间插槽和房间。因为 OptaPlanner 更改这些字段,因此 Apple on 是一个 规划实体 :
上图中的大部分字段都包含输入数据,但 orange 字段除外。在输入数据中取消分配较少的 timeslot 和 room 字段,并在输出数据中分配(而非 )。OptaPlanner 在参与过程中会更改这些字段。此类字段称为 规划变量。为了使 OptaPlanner 能够识别它们,nulltimeslot 和 room 字段都需要 @PlanningVariable 注释。它们包含类属性需要 @PlanningEntity 注释。
流程
创建
src/main/java/com/example/domain/Timeslot.java类:Copy to Clipboard Copied! Toggle word wrap Toggle overflow 请注意,
toString ()方法保留输出短,因此更易于阅读 OptaPlanner 的DEBUG或TRACE日志,如稍后所示。创建
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 bring
on类有一个@PlanningEntity注释,因此 OptaPlanner 知道该类在周五期间发生了变化,因为它包含一个或多个规划变量。timeslot字段有一个@PlanningVariable注释,因此 OptaPlanner 知道它可以更改其值。要查找分配给此字段的潜在Timeslot实例,OptaPlanner 使用valueRangeProviderRefs属性连接到提供List<Timeslot> 的值范围供应商。有关值范围供应商的详情,请查看 第 9.3 节 “在计划解决方案中收集域对象”。由于同样原因,
room字段也有一个@PlanningVariable注释。
9.2. 定义约束并计算分数 复制链接链接已复制到粘贴板!
当遇到问题时,分数 代表特定解决方案的质量。分数越大。OptaPlanner 查找最佳解决方案,这是可用时间中发现的最高分数的解决方案。它可能 是最佳解决方案。
因为 timetable 示例用例有硬和软限制,因此请使用 Hard SoftScore 类来代表分数:
- 硬限制不能被破坏。例如: 一个房间可以同时有一个小时间。
- 软限制不应中断。例如: 一个公司更喜欢在单个房间参与。
硬限制会与其他硬限制进行权重。软限制会与其他软限制加权。硬限制始终超过软限制,无论它们对应的权重是什么。
要计算分数,您可以实施 EasyScoreCalculator 类:
不幸的是,这个解决方案无法很好地扩展,因为它不可缩小:每次将小时间分配给不同的时区或空间时,所有定义都会被重新评估以计算新的分数。
更好的解决方案是创建一个 src/main/java/com/example/solver/TimeTableConstraintProvider.java 类来执行增量分数计算。这个类使用 OptaPlanner 的 ConstraintStream API,该 API 被 Java 8 Streams 和 SQL 增加。ConstraintProvider 扩展比 EasyScoreCalculator: O (n)而不是O(n)而不是 O(n)更好的 magnitude 顺序。
流程
创建以下 src/main/java/com/example/solver/TimeTableConstraintProvider.java 类:
9.3. 在计划解决方案中收集域对象 复制链接链接已复制到粘贴板!
TimeTable 实例将单个数据集的所有 Timeslot、 和 Appleon 实例嵌套。另外,由于它包含所有 lessons,每个都具有特定的计划变量状态,所以它是一个 规划解决方案,它具有分数:
Room
-
如果仅未被分配,则它是一个 未初始化的 解决方案,例如,分数为
-4init/0hard/0soft的解决方案。 -
如果它破坏硬限制,则是一个不可行的解决方案,例如,分数为
-2hard/-3soft的解决方案。 -
如果它遵循所有硬限制,那么它是一种可行的解决方案,例如,分数为
0hard/-7soft的解决方案。
TimeTable 类有一个 @PlanningSolution 注释,因此 OptaPlanner 知道这个类包含所有输入和输出数据。
具体来说,这个类是问题的输入:
带有所有时间插槽的
timeslotList字段- 这是问题事实列表,因为它们在竞争过程中不会改变。
包含所有空间的
roomList字段- 这是问题事实列表,因为它们在竞争过程中不会改变。
带有所有 lessons 的
lessonList字段- 这是规划实体的列表,因为它们在竞争期间发生了变化。
每个
周一:-
timeslot和room字段的值通常仍然为空,因此未分配。它们正在规划变量。 -
其他字段(如
subject、Mrr和slirpGroup)已填写。这些字段是问题属性。
-
但是,此类也是解决方案的输出:
-
一个
lessonList字段,其中每个每天实例在周五后都有非空timeslot和room字段 -
代表输出解决方案的质量的
score字段,例如0hard/-5soft
流程
创建 src/main/java/com/example/domain/TimeTable.java 类:
值范围供应商
timeslotList 字段是一个值范围供应商。它包含 OptaPlanner 可以从中分配 to to the timeslot 字段的 Timeslot 实例。timeslotList 字段有一个 @ValueRangeProvider 注释来连接这两个注解,方法是将 id 与大括号中的 @PlanningVariable 的 valueRangeProviderRefs 匹配。
遵循同样的逻辑,roomList 字段也具有 @ValueRangeProvider 注释。
问题事实和规划实体属性
此外,OptaPlanner 需要知道它可以更改哪些附件,以及如何检索用于 TimeTableConstraintProvider 的 Timeslot 和 Room 实例。
timeslotList 和 roomList 字段有一个 @ProblemFactCollectionProperty 注解,因此您的 TimeTableConstraintProvider 可以从这些实例中选择。
lessonList 有一个 @PlanningEntityCollectionProperty 注解,因此 OptaPlanner 可以在将来更改它们,而您的 TimeTableConstraintProvider 也可以从这些中选择。
9.4. 创建 resolver 服务 复制链接链接已复制到粘贴板!
REST 线程上的规划问题会导致 HTTP 超时问题。因此,Quarkus 扩展注入 SolverManager,它会在一个单独的线程池中运行 solvers,并可并行解决多个数据集。
流程
创建 src/main/java/org/acme/optaplanner/rest/TimeTableResource.java 类:
此初始实施会等待 solver 完成,这仍然可能导致 HTTP 超时。完整的实现可避免 HTTP 超时更小。
9.5. 设置解析器终止时间 复制链接链接已复制到粘贴板!
如果您的计划应用程序没有终止设置或终止事件,则理论上会永久运行,并且实际上会导致 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
9.6. 运行 7000 timetable 应用程序 复制链接链接已复制到粘贴板!
创建 IANA 时间范围项目后,在开发模式下运行它。在开发模式中,您可以在应用程序运行时更新应用程序源和配置。您的更改将显示在正在运行的应用程序中。
先决条件
- 您已创建了 phone 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
9.7. 测试应用程序 复制链接链接已复制到粘贴板!
良好的应用程序包括测试覆盖。测试限制以及您的时间表项目中的 solver。
9.7.1. 测试 prompt 时间可写入限制 复制链接链接已复制到粘贴板!
要在隔离中测试 timetable 项目的约束,请在单元测试中使用 ConstraintVerifier。这会测试每个约束的发生与其他测试隔离的情况,这可在添加正确测试覆盖的新约束时降低维护。
当给定同一空间的三个较少时,此测试会验证约束 TimeTableConstraintProvider:: roomConflict,其中两个较少项具有相同的 timeslot,以匹配权重为 1。因此,如果约束权重为 10hard,它会按 -10hard 减少分数。
流程
创建 src/test/java/org/acme/optaplanner/solver/TimeTableConstraintProviderTest.java 类:
请注意,ConstraintVerifier 在测试过程中如何忽略约束权重,即使这些约束权重在 ConstraintProvider 中硬编码。这是因为在进入生产环境前定期更改约束权重。这样,约束权重调整不会影响单元测试。
9.7.2. 测试 7000 timetable resolver 复制链接链接已复制到粘贴板!
本例在 Red Hat build of Quarkus 上测试 OptaPlanner Demo 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 文件在测试期间如何覆盖 solver 终止,以便在找到可行的解决方案 (0hard thesoft) 时立即终止。这可避免硬编码一个固定时间,因为单元测试可能会在任意硬件上运行。这种方法可确保测试运行足够长,以找到可行的解决方案,即使在较慢的系统上也是如此。但是,即使在快速的系统上,它不会运行毫秒的时间超过其严格要求。
9.8. 日志记录 复制链接链接已复制到粘贴板!
完成 OptaPlanner 修订时间项目后,您可以使用日志信息来帮助微调 ConstraintProvider 中的限制。查看 info 日志文件中的分数计算速度,以评估对您的限制的影响。以调试模式运行应用程序,以显示应用程序采取的每个步骤,或使用 trace 日志记录来记录每个步骤和每次移动。
流程
- 在固定时间内运行 7000 timetable 应用程序,例如五分钟。
查看日志文件中的分数计算速度,如下例所示:
... Solving ended: ..., score calculation speed (29455/sec), ...
... Solving ended: ..., score calculation speed (29455/sec), ...Copy to Clipboard Copied! Toggle word wrap Toggle overflow -
更改约束,在相同时间内再次运行 planning 应用程序,并查看日志文件中记录的分数计算速度。
以 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
-
要从命令行运行调试模式,请使用
-
使用
trace日志记录来显示每个步骤,以及每个步骤的每次移动。
9.9. 将数据库与您的 Quarkus OptaPlanner Demo 时间应用程序集成 复制链接链接已复制到粘贴板!
创建 Quarkus OptaPlanner Evolution 时间后,您可以将其与数据库集成,并创建一个基于 Web 的用户界面来显示时间表。
先决条件
- 您有一个 Quarkus OptaPlanner eXecut timetable 应用程序。
流程
-
使用 Hibernate 和 Panache 将
Timeslot、Room和 fencingon 实例存储在数据库中。如需更多信息,请参阅使用 Panache 的 Simplified Hibernate ORM。 - 通过 REST 公开实例。如需更多信息,请参阅 编写 JSON REST 服务。
更新
TimeTableResource类,在单个事务中读取和写入TimeTable实例:Copy to Clipboard Copied! Toggle word wrap Toggle overflow 这个示例包含一个
TimeTable实例。但是,您可以为并行的多个站启用多租户和处理TimeTable实例。getTimeTable ()方法返回数据库的最新时间表。它使用ScoreManager方法(自动注入)计算该时间的分数,并使其可供 UI 使用。resolve
()方法启动一个作业,以解决当前时间集中,并将时间插槽和房间分配存储在数据库中。它使用SolverManager.solveAndListen ()方法侦听中间最佳解决方案并相应地更新数据库。UI 使用它来显示后端仍在竞争期间的进度。更新
TimeTableResourceTest类,以反映solve ()方法会立即返回,并轮询最新的解决方案,直到解析器完成:Copy to Clipboard Copied! Toggle word wrap Toggle overflow - 在这些 REST 方法之上构建 Web UI,以提供时间表的可视化表示。
- 查看 快速启动源代码。
OptaPlanner 通过 Micrometer (一个 Java 应用程序的指标检测库)公开指标。您可以将 Micrometer 与 Prometheus 搭配使用,来监控 IANA 时间应用程序中的 OptaPlanner solver。
先决条件
- 您已创建了 Quarkus OptaPlanner eXecut timetable 应用程序。
- 已安装 Prometheus。有关安装 Prometheus 的详情,请查看 Prometheus 网站。
流程
将 Micrometer Prometheus 依赖项添加到 IANA 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 启动 7000 timetable 应用程序:
mvn compile quarkus:dev
mvn compile quarkus:devCopy to Clipboard Copied! Toggle word wrap Toggle overflow -
在网页浏览器中打开
http://localhost:8080/q/metric。
您可以使用 OptaPlanner vaccination appointment scheduler 快速启动来开发一个高效和公平的调度。vaccination appointment scheduler 使用人工智能(AI)来排列人员优先级,并根据多个限制和优先级分配时间插槽。
先决条件
- 已安装 OpenJDK 11 或更高版本。Red Hat build of Open JDK 位于红帽客户门户网站中的 Software Downloads 页面中(需要登录)。
- 已安装 Apache Maven 3.8 或更高版本。Maven 可从 Apache Maven 项目网站 获得。
- 提供了 IDE,如 IntelliJ IDEA、VSCode、Ecli 或 NetBeans。
- 您已创建了 Quakus OptaPlanner 项目,如 第 5 章 OptaPlanner 和 Quarkus 入门 所述。
10.1. OptaPlanner vaccination appointment 调度程序的工作方式 复制链接链接已复制到粘贴板!
调度点有两种主要方法:系统可以让人选择一个点插槽(user-selects),或者系统分配一个插槽,并告知人在什么地方进行参与(系统自动分配)。OptaPlanner vaccination appointment 调度程序使用 system-automaticly-assigns 方法。使用 OptaPlanner vaccination appointment 调度程序,您可以创建一个应用程序,其中人员向系统提供信息,系统分配一个点。
这个方法的特性:
- 点插槽会根据优先级分配。
- 系统根据预配置的规划限制分配最佳点时间和位置。
- 对于有限数量的故障点,系统不会受到大量用户负担。
这种方法通过使用规划限制为每个人创建分数,从而尽可能减少许多人。人员的分数决定何时获得缺陷。个人分数越大,他们可以获得较早的缺陷。
10.1.1. optaPlanner vaccination appointment scheduler 约束 复制链接链接已复制到粘贴板!
OptaPlanner vaccination appointment 调度程序约束是 hard、medium 或 soft :
硬限制不能被破坏。如果有任何硬约束无法正常工作,则计划不可激活,且无法执行:
- capacity :在任何位置没有任何时间不用尽书 vaccine 容量。
- Vaccine max age: 如果 vaccine 具有最长期限,请不要在第一次执行时将其管理的时间超过 vaccine 最长期限。确保为人获得适合其年龄的 vaccine 类型。例如,对于最长期限限制为 65 年,不要为旧人员分配 75 年。
- 所需的 vaccine 类型:使用所需的 vaccine 类型。例如,vaccine 的第二个操作必须与第一个操作相同。
- 就绪日期:管理指定日期或之后的 vaccine。例如,如果人收到第二个操作,请在特定 vaccine 类型的推荐最早的 vaccination 日期前不对其进行管理,例如 26 天。
- 到期日期:管理指定日期或之前的 vaccine。例如,如果人收到第二个操作,请在特定 vaccine 的推荐时间前对其进行管理,例如,第一次执行 3 个月。
- 限制最大旋转距离:将每个人分配给接近一组 vaccination 数据中心之一。这通常是三个数据中心之一。这个限制是通过旋转时间而不是距离来计算的,因此,一个人位于 urban 区域中的人通常具有比同一区域更低的距离。
中等约束决定在没有足够容量为任何人分配点时没有达到点的点。这通过受限规划调用:
- schedule second dose vaccinations:除非理想的日期不在规划窗口外,不要保留任何第二个 dose vaccination appointments unassigned。
- 根据优先级评级调度人员:每个人都有优先级评级。这通常是它们的年龄,但如果它们是健康的 worker,则可能要高得多。仅保留具有最低优先级评级的用户。它们将在下一次运行中考虑。这个约束比之前的约束是软的,因为第二个操作总是优先于优先级的评级。
软限制不应中断:
- 首选凭证 :如果人拥有首选凭证中心,为他们授予该中心上的点。
- 距离:最小化个人必须到达其分配的预测中心的距离。
- 理想的日期:管理 vaccine on 或尽可能接近指定日期。例如,如果人收到第二个操作,请在特定 vaccine 的理想日期上对其进行管理,例如 28 天。这个约束比距离约束相比,避免在国家内向人发送半次,成为他们理想的日期。
- 优先级评级:调度在计划窗口中之前具有较高的优先级评级的人员。这个约束比距离约束是软的,以避免在国家范围内发送半方。此约束也比理想的日期约束要软,因为第二个条件优先于优先级的评级。
硬限制会与其他硬限制进行权重。软限制会与其他软限制加权。但是,硬限制总是优先于中等和软限制。如果硬约束中断,则计划不可取。但是,如果没有硬约束,则考虑软和中等约束来确定优先级。由于用户通常比可用点插槽更多,所以您必须优先选择。最后,首先先分配第二个问题,以避免创建稍后系统的可能性。之后,用户会根据优先级评级进行分配。任何人都以优先级评级开始,这是其年龄。这比年龄较早的人优先选择旧的人员。之后,位于特定优先级组中的人员会收到几百个额外点。这因组的优先级而异。例如,nurses 可能会收到额外的 1000 点。这样,旧的 nurses 优先于 younger nurses,而您的 nurses 则优先于不是微不足的人员。下表演示了这个概念:
| 年龄 | 作业(job) | 优先级评级 |
|---|---|---|
| 60 | nurse | 1060 |
| 33 | nurse | 1033 |
| 71 | 弃用 | 71 |
| 52 | 办公室 worker | 52 |
10.1.2. OptaPlanner solver 复制链接链接已复制到粘贴板!
在 OptaPlanner 的核心上,使用问题数据集和覆盖规划限制和配置的引擎。问题数据集包含有关人员、vaccines 和 vaccination 数据中心的所有信息。该解析器通过各种数据组合进行,最终决定优化点计划,以及分配给特定中心上提示点的人员。以下图例显示了相应的调度:
10.1.3. 持续规划 复制链接链接已复制到粘贴板!
持续规划是同时管理一个或多个即将推出的规划周期的技术,并每月重复该过程,每周、每天、每天、每小时或更频繁地重复此过程。规划窗口以指定间隔递增。下面的图图显示了每天更新的两周规划窗口:
两个每周规划窗口被分为一半。第一个每周处于 published 状态,第二个每周处于草案状态。人员在计划窗口发布的和草案部分中被分配到点。但是,只有计划窗口发布部分的人员才会收到它们的点。其他点仍然可以在下次运行中轻松更改。这样做可让 OptaPlanner 在再次运行 solver 时更改草案部分中的故障点(如果需要)。例如,如果需要第二天做的人具有周一日期,并且一个理想的日期为周五,OptaPlanner 不必为他们授予周一的解点。
您可以确定规划窗口的大小,但只了解问题空间的大小。问题空间是创建计划的所有各种元素。您提前规划的时间越大,问题空间越大。
10.1.4. 固定计划实体 复制链接链接已复制到粘贴板!
如果您持续规划每天,在已经分配给人的两周内会存在点。为确保 appointments 没有双书,OptaPlanner 会将现有的点标记为分配,方法是固定它们。固定用于定位一个或多个特定的分配,并强制 OptaPlanner 调度这些固定分配。固定规划实体(如点点)在竞争过程中不会改变。
实体是否固定,还是由点状态决定。缩写可以有五个状态: Open、Invited、Accepted、Rejected 或 Rescheduled。
您实际上不会在快速启动演示代码中直接看到这些状态,因为 OptaPlanner 引擎只对点是否固定感兴趣。
您需要能够计划已经调度的点。固定 Invited 或 Accepted 状态的点。带有 Open、Reschedule 和 Rejected 状态的点不会被固定,并可用于调度。
在本例中,当解析器在发布和草案范围内在两个周计划窗口中运行时,它会搜索。除了未计划输入数据外,解析器还考虑任何未 固定实体,以及 Open、Reschedule 或 Rejected 状态。如果解析器每天运行,您会在运行 solver 前看到一个新的日期添加到计划中。
请注意,新一天的点已被分配,之前在规划窗口的草案部分调度的 Amy 和 Edna 会被调度到窗口的发布部分。这是因为 Gus 和 Hugo 请求重新调度。这不会造成任何混淆,因为 Amy 和 Edna 不会通知其草案日期。现在,由于他们在计划窗口的发布部分中有点,因此它们会收到通知并询问接受或拒绝它们的点,其点现已固定。
10.2. 下载并运行 OptaPlanner vaccination appointment 调度程序 复制链接链接已复制到粘贴板!
下载 OptaPlanner vaccination appointment 调度程序快速启动存档,以 Quarkus 开发模式启动它,并在浏览器中查看应用程序。Quarkus 开发模式允许您在其运行时进行更改和更新应用程序。
流程
导航到红帽客户门户网站中的 Software Downloads 页面(需要登录),然后从下拉菜单中选择产品和版本:
- 产品: 红帽构建的 OptaPlanner
- Version: 8.29
- 下载 Red Hat Build of OptaPlanner 8.29 Quick Starts。
提取
rhbop-8.29.0-red_hat_build_of_optaplanner-quickstarts-sources.zip文件。提取的
org.red_hat_build_of_optaplanner-red_hat_build_of_optaplanner-quickstarts-8.29.0.Final-redhat-00009/use-cases/vaccination-scheduling目录包含示例源代码。-
进入
org.red_hat_build_of_optaplanner-red_hat_build_of_optaplanner-quickstarts-8.29.0.Final-redhat-00009/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 scheduler,请点 Solve。
- 更改源代码,然后按 F5 键刷新浏览器。请注意,您所做的更改现已可用。
10.3. 软件包并运行 OptaPlanner vaccination appointment 调度程序 复制链接链接已复制到粘贴板!
当您完成 OptaPlanner vaccination appointment scheduler in quarkus:dev 模式的开发工作后,以传统的 jar 文件运行应用程序。
先决条件
- 您已下载了 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/quarkus-app/quarkus-run.jar
$ java -jar ./target/quarkus-app/quarkus-run.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
员工调度程序快速启动应用程序分配员工以在机构的不同位置上过渡。例如,您可以使用应用程序在 nurses 之间分发转换,保护任务在多个位置间移动,或者在 worker 间移动行。
最佳员工调度必须考虑多个变量。例如,在不同位置上移动需要不同的知识。另外,一些员工可能不适用于一些时间插槽,或者可能首选特定的时间插槽。此外,员工可以有一个合同,限制员工可在单一时间段内工作的小时数。
此启动程序应用的 OptaPlanner 规则使用硬和软限制。在优化过程中,计划器引擎可能无法违反硬限制,例如,如果员工不可用(sick),或者员工无法在单个迁移中工作两个点。Planner 引擎尝试遵循软限制,如员工的首选项无法进行特定的转换,但如果最佳解决方案需要,则可以违反它们。
先决条件
- 已安装 OpenJDK 11 或更高版本。Red Hat build of Open JDK 位于红帽客户门户网站中的 Software Downloads 页面中(需要登录)。
- 已安装 Apache Maven 3.8 或更高版本。Maven 可从 Apache Maven 项目网站 获得。
- 提供了 IDE,如 IntelliJ IDEA、VSCode、Ecli 或 NetBeans。
11.1. 下载并运行 OptaPlanner 员工调度程序 复制链接链接已复制到粘贴板!
下载 OptaPlanner 员工调度程序快速启动存档,以 Quarkus 开发模式启动它,并在浏览器中查看应用程序。Quarkus 开发模式允许您在其运行时进行更改和更新应用程序。
流程
导航到红帽客户门户网站中的 Software Downloads 页面(需要登录),然后从下拉菜单中选择产品和版本:
- 产品: 红帽构建的 OptaPlanner
- Version: 8.29
- 下载 Red Hat Build of OptaPlanner 8.29 Quick Starts。
-
提取
rhbop-8.29.0-red_hat_build_of_optaplanner-quickstarts-sources.zip文件。 -
进入
org.red_hat_build_of_optaplanner-red_hat_build_of_optaplanner-quickstarts-8.29.0.Final-redhat-00009/use-cases/employee-scheduling目录。 输入以下命令在开发模式中启动 OptaPlanner 员工调度程序:
mvn quarkus:dev
$ mvn quarkus:devCopy to Clipboard Copied! Toggle word wrap Toggle overflow 要查看 OptaPlanner 员工调度程序,请在 Web 浏览器中输入以下 URL。
http://localhost:8080/
http://localhost:8080/Copy to Clipboard Copied! Toggle word wrap Toggle overflow - 要运行 OptaPlanner 员工调度程序,请单击 Solve。
- 更改源代码,然后按 F5 键刷新浏览器。请注意,您所做的更改现已可用。
11.2. 软件包并运行 OptaPlanner 员工调度程序 复制链接链接已复制到粘贴板!
当您在 quarkus:dev 模式中对 OptaPlanner 员工调度程序完成开发工作后,以传统的 jar 文件运行应用程序。
先决条件
- 您已下载了 OptaPlanner 员工的调度快速启动。
流程
-
进入
/use-cases/vaccination-scheduling目录。 要编译 OptaPlanner 员工调度程序,请输入以下命令:
mvn package
$ mvn packageCopy to Clipboard Copied! Toggle word wrap Toggle overflow 要运行编译的 OptaPlanner 员工调度程序,请输入以下命令:
java -jar ./target/quarkus-app/quarkus-run.jar
$ java -jar ./target/quarkus-app/quarkus-run.jarCopy to Clipboard Copied! Toggle word wrap Toggle overflow 注意要在端口 8081 上运行应用程序,请在上一命令中添加
-Dquarkus.http.port=8081。要启动 OptaPlanner 员工调度程序,请在 Web 浏览器中输入以下 URL。
http://localhost:8080/
http://localhost:8080/Copy to Clipboard Copied! Toggle word wrap Toggle overflow
第 12 章 Spring Boot 上的 OptaPlanner:一个可写入时间的快速启动指南 复制链接链接已复制到粘贴板!
本指南指导您完成创建带有 OptaPlanner 约束(AI)的 Spring Boot 应用程序的过程。您将构建 REST 应用程序,该应用程序针对领导和专业者进行了优化。
您的服务将通过 AI 遵循以下硬和软 调度限制,自动将附件实例分配给 Timeslot 和 Room 实例:
- 空间最多可以同时有一个小时间。
- 一个架构师可以同时参与一个小时间。
- 每天最多可同时参与一个小时间。
- 交互式人员更喜欢在单个房间参与。
- 指导人员更喜欢参与较少的活动,并排解少量间的差距。
mathematly 认为是 NP 硬性问题。这意味着难以扩展。只需将所有可能的组合与括号一起进行迭代,可以在非平量数据集中使用数百万年,即使在超级计算器上也是如此。AI 约束解析器(如 OptaPlanner)有高级算法,可在合理的时间内提供最接近的解决方案。认为是合理的时间,这取决于您的问题的目标。
先决条件
- 已安装 OpenJDK 11 或更高版本。Red Hat build of Open JDK 位于红帽客户门户网站中的 Software Downloads 页面中(需要登录)。
- 已安装 Apache Maven 3.8 或更高版本。Maven 可从 Apache Maven 项目网站 获得。
- 提供了 IDE,如 IntelliJ IDEA、VSCode、Ecli 或 NetBeans。
12.1. 下载并构建 Spring Boot phone 时间的快速开始 复制链接链接已复制到粘贴板!
如果要看到带有 Spring Boot 产品的 OptaPlanner 的 SVVP timetable 项目的完整示例,请从红帽客户门户网站下载入门程序应用程序。
流程
导航到红帽客户门户网站中的 Software Downloads 页面(需要登录),然后从下拉菜单中选择产品和版本:
- 产品: 红帽构建的 OptaPlanner
- Version: 8.29
- 下载 Red Hat Build of OptaPlanner 8.29 Quick Starts。
提取
rhbop-8.29.0-red_hat_build_of_optaplanner-quickstarts-sources.zip文件。解压的
org.red_hat_build_of_optaplanner-red_hat_build_of_optaplanner-quickstarts-8.29.0.Final-redhat-00009/use-cases/DSL-timetabling目录包含示例源代码。-
进入
org.red_hat_build_of_optaplanner-red_hat_build_of_optaplanner-quickstarts-8.29.0.Final-redhat-00009/use-cases/ASP-timetabling目录。 -
下载 Red Hat Build of OptaPlanner 8.29.0 Maven Repositroy (
rhbop-8.29.0-red_hat_build_of_optaplanner-maven-repository.zip)。 -
提取
rhbop-8.29.0-red_hat_build_of_optaplanner-maven-repository.zip文件。 -
将
rhbop-8.29.0-red_hat_build_of_optaplanner/maven-repository子目录的内容复制到~/.m2/repository目录中。 -
进入
org.red_hat_build_of_optaplanner-red_hat_build_of_optaplanner-quickstarts-8.29.0.Final-redhat-00009/certification/java-spring-boot目录。 输入以下命令来构建 Spring Boot eXecut timetabling 项目:
mvn clean install -DskipTests
mvn clean install -DskipTestsCopy to Clipboard Copied! Toggle word wrap Toggle overflow 要构建 Spring Boot phone 时间建立项目,请输入以下命令:
mvn spring-boot:run -DskipTests
mvn spring-boot:run -DskipTestsCopy to Clipboard Copied! Toggle word wrap Toggle overflow 要查看项目,请在 Web 浏览器中输入以下 URL:
http://localhost:8080/
http://localhost:8080/Copy to Clipboard Copied! Toggle word wrap Toggle overflow
12.2. 对域对象建模 复制链接链接已复制到粘贴板!
OptaPlanner timetable 项目的目标是将每个小项分配到一个时间插槽和房间。要做到这一点,添加三个类: Timeslot、init on 和 Room,如下图所示:
timeslot
Timeslot 类代表当减少活动为过期的时间间隔时,例如,Monday 10:30 - 11:30 或 Tuesday 13:30 - 14:30。在这个示例中,所有时间段都有相同的持续时间,在 lunch 或其他中断过程中没有时间插槽。
一个时间插槽没有日期,因为每周计划只重复一次。不需要 持续规划。timeslot 被称为 问题事实,因为公司不会更改 Timeslot 实例。此类类不需要任何特定于 OptaPlanner 的注解。
房间
Room 类代表更小活动的位置,例如 Room A 或 Room B。在本例中,所有房间都没有容量限制,它们可以容纳所有定义。
空间 实例在竞争过程中不会改变,因此 Room 也是 问题事实。
lesson
在小时间中,由周一类代表,受一组参与者代表的规定,例如,A.Turing for 9th rating 或 Chemistry by M.Curie 代表第 10 等级。如果一个主题是按同一站组相同的每周的多次,则有多个仅可通过 id 区分的实例。例如,第 9 等级每周有六个数学。
在 OptaPlanner 期间,OptaPlanner 会更改周一类的 timeslot 和 room 字段,以将每个小时间分配给一个时间插槽和房间。因为 OptaPlanner 更改这些字段,因此 Apple on 是一个 规划实体 :
上图中的大部分字段都包含输入数据,但 orange 字段除外。在输入数据中取消分配较少的 timeslot 和 room 字段,并在输出数据中分配(而非 )。OptaPlanner 在参与过程中会更改这些字段。此类字段称为 规划变量。为了使 OptaPlanner 能够识别它们,nulltimeslot 和 room 字段都需要 @PlanningVariable 注释。它们包含类属性需要 @PlanningEntity 注释。
流程
创建
src/main/java/com/example/domain/Timeslot.java类:Copy to Clipboard Copied! Toggle word wrap Toggle overflow 请注意,
toString ()方法保留输出短,因此更易于阅读 OptaPlanner 的DEBUG或TRACE日志,如稍后所示。创建
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 bring
on类有一个@PlanningEntity注释,因此 OptaPlanner 知道该类在周五期间发生了变化,因为它包含一个或多个规划变量。timeslot字段有一个@PlanningVariable注释,因此 OptaPlanner 知道它可以更改其值。要查找分配给此字段的潜在Timeslot实例,OptaPlanner 使用valueRangeProviderRefs属性连接到提供List<Timeslot> 的值范围供应商。有关值范围供应商的详情,请查看 第 12.4 节 “在计划解决方案中收集域对象”。由于同样原因,
room字段也有一个@PlanningVariable注释。
12.3. 定义约束并计算分数 复制链接链接已复制到粘贴板!
当遇到问题时,分数 代表特定解决方案的质量。分数越大。OptaPlanner 查找最佳解决方案,这是可用时间中发现的最高分数的解决方案。它可能 是最佳解决方案。
因为 timetable 示例用例有硬和软限制,因此请使用 Hard SoftScore 类来代表分数:
- 硬限制不能被破坏。例如: 一个房间可以同时有一个小时间。
- 软限制不应中断。例如: 一个公司更喜欢在单个房间参与。
硬限制会与其他硬限制进行权重。软限制会与其他软限制加权。硬限制始终超过软限制,无论它们对应的权重是什么。
要计算分数,您可以实施 EasyScoreCalculator 类:
不幸的是,这个解决方案无法很好地扩展,因为它不可缩小:每次将小时间分配给不同的时区或空间时,所有定义都会被重新评估以计算新的分数。
更好的解决方案是创建一个 src/main/java/com/example/solver/TimeTableConstraintProvider.java 类来执行增量分数计算。这个类使用 OptaPlanner 的 ConstraintStream API,该 API 被 Java 8 Streams 和 SQL 增加。ConstraintProvider 扩展比 EasyScoreCalculator: O (n)而不是O(n)而不是 O(n)更好的 magnitude 顺序。
流程
创建以下 src/main/java/com/example/solver/TimeTableConstraintProvider.java 类:
12.4. 在计划解决方案中收集域对象 复制链接链接已复制到粘贴板!
TimeTable 实例将单个数据集的所有 Timeslot、 和 Appleon 实例嵌套。另外,由于它包含所有 lessons,每个都具有特定的计划变量状态,所以它是一个 规划解决方案,它具有分数:
Room
-
如果仅未被分配,则它是一个 未初始化的 解决方案,例如,分数为
-4init/0hard/0soft的解决方案。 -
如果它破坏硬限制,则是一个不可行的解决方案,例如,分数为
-2hard/-3soft的解决方案。 -
如果它遵循所有硬限制,那么它是一种可行的解决方案,例如,分数为
0hard/-7soft的解决方案。
TimeTable 类有一个 @PlanningSolution 注释,因此 OptaPlanner 知道这个类包含所有输入和输出数据。
具体来说,这个类是问题的输入:
带有所有时间插槽的
timeslotList字段- 这是问题事实列表,因为它们在竞争过程中不会改变。
包含所有空间的
roomList字段- 这是问题事实列表,因为它们在竞争过程中不会改变。
带有所有 lessons 的
lessonList字段- 这是规划实体的列表,因为它们在竞争期间发生了变化。
每个
周一:-
timeslot和room字段的值通常仍然为空,因此未分配。它们正在规划变量。 -
其他字段(如
subject、Mrr和slirpGroup)已填写。这些字段是问题属性。
-
但是,此类也是解决方案的输出:
-
一个
lessonList字段,其中每个每天实例在周五后都有非空timeslot和room字段 -
代表输出解决方案的质量的
score字段,例如0hard/-5soft
流程
创建 src/main/java/com/example/domain/TimeTable.java 类:
值范围供应商
timeslotList 字段是一个值范围供应商。它包含 OptaPlanner 可以从中分配 to to the timeslot 字段的 Timeslot 实例。timeslotList 字段有一个 @ValueRangeProvider 注释来连接这两个注解,方法是将 id 与大括号中的 @PlanningVariable 的 valueRangeProviderRefs 匹配。
遵循同样的逻辑,roomList 字段也具有 @ValueRangeProvider 注释。
问题事实和规划实体属性
此外,OptaPlanner 需要知道它可以更改哪些附件,以及如何检索用于 TimeTableConstraintProvider 的 Timeslot 和 Room 实例。
timeslotList 和 roomList 字段有一个 @ProblemFactCollectionProperty 注解,因此您的 TimeTableConstraintProvider 可以从这些实例中选择。
lessonList 有一个 @PlanningEntityCollectionProperty 注解,因此 OptaPlanner 可以在将来更改它们,而您的 TimeTableConstraintProvider 也可以从这些中选择。
12.5. 创建 Timetable 服务 复制链接链接已复制到粘贴板!
现在,您已准备好将所有内容放在一起,并创建一个 REST 服务。但是,在 REST 线程上计划问题会导致 HTTP 超时问题。因此,Spring Boot Start 注入 SolverManager,它会在一个单独的线程池中运行 solvers,并可并行解决多个数据集。
流程
创建 src/main/java/com/example/solver/TimeTableController.java 类:
在本例中,初始实施会等待 solver 完成,这仍然可能导致 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 项目后,将所有内容打包成单一可执行 JAR 文件中,由标准 Java main () 方法驱动。
先决条件
- 您有一个已完成的 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 日志显示 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).
12.7.2. 构建应用程序 复制链接链接已复制到粘贴板!
良好的应用程序包括测试覆盖。本例测试 Timetable OptaPlanner Spring Boot 应用程序。它使用 JUnit 测试来生成测试数据集,并将其发送到 TimeTableController 来解决。
流程
使用以下内容创建 src/test/java/com/example/solver/TimeTableControllerTest.java 类:
此测试会验证之后,所有较少记录都会被分配给一个时间插槽和房间。它还会验证它发现了一个可行的解决方案(没有硬约束中断)。
通常,解析器会在 200 毫秒内找到可行的解决方案。请注意,@SpringBootTest 注释 的属性 如何覆盖 solver 终止,以便在找到可行的解决方案(0hard soft)时立即终止。这可避免硬编码一个固定时间,因为单元测试可能会在任意硬件上运行。这种方法可确保测试运行足够长,以找到可行的解决方案,即使在较慢的系统上也是如此。但是,即使在快速的系统上,它不会运行毫秒的时间超过它。
12.7.3. 日志记录 复制链接链接已复制到粘贴板!
完成 OptaPlanner Spring Boot timetable 应用程序后,您可以使用日志信息来帮助微调 ConstraintProvider 中的限制。查看 info 日志文件中的分数计算速度,以评估对您的限制的影响。以调试模式运行应用程序,以显示应用程序采取的每个步骤,或使用 trace 日志记录来记录每个步骤和每次移动。
流程
- 在固定时间内运行 timetable 应用程序,例如五分钟。
查看日志文件中的分数计算速度,如下例所示:
... Solving ended: ..., score calculation speed (29455/sec), ...
... Solving ended: ..., score calculation speed (29455/sec), ...Copy to Clipboard Copied! Toggle word wrap Toggle overflow -
更改约束,在相同时间内再次运行 planning 应用程序,并查看日志文件中记录的分数计算速度。
以 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
-
要从命令行运行调试模式,请使用
-
使用
trace日志记录来显示每个步骤,以及每个步骤的每次移动。
12.8. 添加数据库和 UI 集成 复制链接链接已复制到粘贴板!
使用 Spring Boot 创建 OptaPlanner 应用程序示例后,添加数据库和 UI 集成。
前提条件
- 您已创建了 OptaPlanner Spring Boot timetable 示例。
流程
-
为
Timeslot、Room和 Pricingon 创建 Java Persistence API (DSL)存储库。有关创建 JPA 存储库的详情,请参考在 Spring 网站上 使用 JPA 访问数据。 - 通过 REST 公开 JPA 存储库。有关公开存储库的详情,请参考 Spring 网站 使用 REST 访问 JPA 数据。
-
构建
TimeTableRepositoryfacade,在单个事务中读取和写入TimeTable。 按照以下示例所示调整
TimeTableController:Copy to Clipboard Copied! Toggle word wrap Toggle overflow 为了简单起见,此代码仅处理一次
实例,但易于启用多租户并并行处理不同高站的多个时间表实例。TimeTablegetTimeTable ()方法返回数据库的最新时间表。它使用ScoreManager(自动注入)计算该时间的分数,以便 UI 能够显示分数。resolve
()方法启动一个作业,以解决当前时间集中,并将时间插槽和房间分配存储在数据库中。它使用SolverManager.solveAndListen ()方法侦听中间最佳解决方案并相应地更新数据库。这可让 UI 在后端仍在竞争时显示进度。现在,
solv ()方法会立即返回,调整TimeTableControllerTest,如下例所示:Copy to Clipboard Copied! Toggle word wrap Toggle overflow - 轮询最新的解决方案,直到解决方案完成。
- 要视觉化时间表,请在这些 REST 方法之上构建高级 Web UI。
OptaPlanner 通过 Micrometer (一个 Java 应用程序的指标检测库)公开指标。您可以将 Micrometer 与 Prometheus 搭配使用,来监控 IANA 时间应用程序中的 OptaPlanner solver。
先决条件
- 您已创建了 Spring Boot OptaPlanner eXecut timetable 应用程序。
- 已安装 Prometheus。有关安装 Prometheus 的详情,请查看 Prometheus 网站。
流程
-
进入 Technology
/java-spring-boot目录。 将 Micrometer Prometheus 依赖项添加到 IANA 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 启动 7000 timetable 应用程序:
mvn spring-boot:run
mvn spring-boot:runCopy to Clipboard Copied! Toggle word wrap Toggle overflow -
在网页浏览器中打开
http://localhost:8080/actuator/prometheus。
第 13 章 OptaPlanner 和 Java:一个可插入时间快速启动指南 复制链接链接已复制到粘贴板!
本指南指导您完成创建具有 OptaPlanner 约束(AI)的简单 Java 应用程序的过程。您将构建一个命令行应用程序,该应用程序针对领导和架构师进行了优化,以优化电池时间:
您的应用程序将通过 AI 遵循硬和软调度 限制,将自动将附件实例分配给 Timeslot 和 Room 实例,例如:
- 空间最多可以同时有一个小时间。
- 一个架构师可以同时参与一个小时间。
- 每天最多可同时参与一个小时间。
- 指导人员更喜欢在同一房间所有较少的活动。
- 指导人员更喜欢参与较少的活动,并排解少量间的差距。
- 同一主题上连续的 小项是 一 个 一 个。
mathematly 认为是 NP 硬性问题。这意味着难以扩展。简单地强制迭代所有可能的组合,对于非平量数据集也需要数百万年,即使在超级计算器上也是如此。AI 约束解析器(如 OptaPlanner)有高级算法,可在合理的时间内提供最接近的解决方案。
先决条件
- 已安装 OpenJDK (JDK) 11。Red Hat build of Open JDK 位于红帽客户门户网站中的 Software Downloads 页面中(需要登录)。
- 已安装 Apache Maven 3.6 或更高版本。Maven 可从 Apache Maven 项目网站 获得。
- IDE,如 IntelliJ IDEA、VSCode 或 Eclipse
13.1. 创建 Maven 或 Gradle 构建文件并添加依赖项 复制链接链接已复制到粘贴板!
您可以将 Maven 或 Gradle 用于 OptaPlanner Evolution 时间应用程序。创建构建文件后,添加以下依赖项:
-
OptaPlanner-core(复合范围)来解决 IANA 时间问题 -
optaPlanner-test(测试范围)测试 JUnit 计时限制 -
一个实现,如
logback-classic(runtime 范围)来查看 OptaPlanner 采取的步骤
流程
- 创建 Maven 或 Gradle 构建文件。
在您的构建文件中添加
和optaplanner-core、Optaplanner-testlogback-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 timetable 项目的目标是将每个小项分配到一个时间插槽和房间。要做到这一点,添加三个类: Timeslot、init on 和 Room,如下图所示:
timeslot
Timeslot 类代表当减少活动为过期的时间间隔时,例如,Monday 10:30 - 11:30 或 Tuesday 13:30 - 14:30。在这个示例中,所有时间段都有相同的持续时间,在 lunch 或其他中断过程中没有时间插槽。
一个时间插槽没有日期,因为每周计划只重复一次。不需要 持续规划。timeslot 被称为 问题事实,因为公司不会更改 Timeslot 实例。此类类不需要任何特定于 OptaPlanner 的注解。
房间
Room 类代表更小活动的位置,例如 Room A 或 Room B。在本例中,所有房间都没有容量限制,它们可以容纳所有定义。
空间 实例在竞争过程中不会改变,因此 Room 也是 问题事实。
lesson
在小时间中,由周一类代表,受一组参与者代表的规定,例如,A.Turing for 9th rating 或 Chemistry by M.Curie 代表第 10 等级。如果一个主题是按同一站组相同的每周的多次,则有多个仅可通过 id 区分的实例。例如,第 9 等级每周有六个数学。
在 OptaPlanner 期间,OptaPlanner 会更改周一类的 timeslot 和 room 字段,以将每个小时间分配给一个时间插槽和房间。因为 OptaPlanner 更改这些字段,因此 Apple on 是一个 规划实体 :
上图中的大部分字段都包含输入数据,但 orange 字段除外。在输入数据中取消分配较少的 timeslot 和 room 字段,并在输出数据中分配(而非 )。OptaPlanner 在参与过程中会更改这些字段。此类字段称为 规划变量。为了使 OptaPlanner 能够识别它们,nulltimeslot 和 room 字段都需要 @PlanningVariable 注释。它们包含类属性需要 @PlanningEntity 注释。
流程
创建
src/main/java/com/example/domain/Timeslot.java类:Copy to Clipboard Copied! Toggle word wrap Toggle overflow 请注意,
toString ()方法保留输出短,因此更易于阅读 OptaPlanner 的DEBUG或TRACE日志,如稍后所示。创建
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 bring
on类有一个@PlanningEntity注释,因此 OptaPlanner 知道该类在周五期间发生了变化,因为它包含一个或多个规划变量。timeslot字段有一个@PlanningVariable注释,因此 OptaPlanner 知道它可以更改其值。要查找分配给此字段的潜在Timeslot实例,OptaPlanner 使用valueRangeProviderRefs属性连接到提供List<Timeslot> 的值范围供应商。有关值范围供应商的详情,请查看 第 13.4 节 “在计划解决方案中收集域对象”。由于同样原因,
room字段也有一个@PlanningVariable注释。
13.3. 定义约束并计算分数 复制链接链接已复制到粘贴板!
当遇到问题时,分数 代表特定解决方案的质量。分数越大。OptaPlanner 查找最佳解决方案,这是可用时间中发现的最高分数的解决方案。它可能 是最佳解决方案。
因为 timetable 示例用例有硬和软限制,因此请使用 Hard SoftScore 类来代表分数:
- 硬限制不能被破坏。例如: 一个房间可以同时有一个小时间。
- 软限制不应中断。例如: 一个公司更喜欢在单个房间参与。
硬限制会与其他硬限制进行权重。软限制会与其他软限制加权。硬限制始终超过软限制,无论它们对应的权重是什么。
要计算分数,您可以实施 EasyScoreCalculator 类:
不幸的是,这个解决方案无法很好地扩展,因为它不可缩小:每次将小时间分配给不同的时区或空间时,所有定义都会被重新评估以计算新的分数。
更好的解决方案是创建一个 src/main/java/com/example/solver/TimeTableConstraintProvider.java 类来执行增量分数计算。这个类使用 OptaPlanner 的 ConstraintStream API,该 API 被 Java 8 Streams 和 SQL 增加。ConstraintProvider 扩展比 EasyScoreCalculator: O (n)而不是O(n)而不是 O(n)更好的 magnitude 顺序。
流程
创建以下 src/main/java/com/example/solver/TimeTableConstraintProvider.java 类:
13.4. 在计划解决方案中收集域对象 复制链接链接已复制到粘贴板!
TimeTable 实例将单个数据集的所有 Timeslot、 和 Appleon 实例嵌套。另外,由于它包含所有 lessons,每个都具有特定的计划变量状态,所以它是一个 规划解决方案,它具有分数:
Room
-
如果仅未被分配,则它是一个 未初始化的 解决方案,例如,分数为
-4init/0hard/0soft的解决方案。 -
如果它破坏硬限制,则是一个不可行的解决方案,例如,分数为
-2hard/-3soft的解决方案。 -
如果它遵循所有硬限制,那么它是一种可行的解决方案,例如,分数为
0hard/-7soft的解决方案。
TimeTable 类有一个 @PlanningSolution 注释,因此 OptaPlanner 知道这个类包含所有输入和输出数据。
具体来说,这个类是问题的输入:
带有所有时间插槽的
timeslotList字段- 这是问题事实列表,因为它们在竞争过程中不会改变。
包含所有空间的
roomList字段- 这是问题事实列表,因为它们在竞争过程中不会改变。
带有所有 lessons 的
lessonList字段- 这是规划实体的列表,因为它们在竞争期间发生了变化。
每个
周一:-
timeslot和room字段的值通常仍然为空,因此未分配。它们正在规划变量。 -
其他字段(如
subject、Mrr和slirpGroup)已填写。这些字段是问题属性。
-
但是,此类也是解决方案的输出:
-
一个
lessonList字段,其中每个每天实例在周五后都有非空timeslot和room字段 -
代表输出解决方案的质量的
score字段,例如0hard/-5soft
流程
创建 src/main/java/com/example/domain/TimeTable.java 类:
值范围供应商
timeslotList 字段是一个值范围供应商。它包含 OptaPlanner 可以从中分配 to to the timeslot 字段的 Timeslot 实例。timeslotList 字段有一个 @ValueRangeProvider 注释来连接这两个注解,方法是将 id 与大括号中的 @PlanningVariable 的 valueRangeProviderRefs 匹配。
遵循同样的逻辑,roomList 字段也具有 @ValueRangeProvider 注释。
问题事实和规划实体属性
此外,OptaPlanner 需要知道它可以更改哪些附件,以及如何检索用于 TimeTableConstraintProvider 的 Timeslot 和 Room 实例。
timeslotList 和 roomList 字段有一个 @ProblemFactCollectionProperty 注解,因此您的 TimeTableConstraintProvider 可以从这些实例中选择。
lessonList 有一个 @PlanningEntityCollectionProperty 注解,因此 OptaPlanner 可以在将来更改它们,而您的 TimeTableConstraintProvider 也可以从这些中选择。
13.5. TimeTableApp.java 类 复制链接链接已复制到粘贴板!
创建 IANA 时间应用程序的所有组件后,会将它们放在 TimeTableApp.java 类中。
main () 方法执行以下任务:
-
创建
SolverFactory以为每个数据集构建 Solver。 - 加载数据集。
-
使用 Solver
.solve ()来解决它。 - 视觉化该数据集的解决方案。
通常,应用程序只有一个 来为每个问题数据集构建一个新的 Solver 实例。Solver FactorySolverFactory 是 thread-safe,但 Solver 不是。对于 IANA 时间可时间化应用程序,只有一个数据集,因此只有一个 Solver 实例。
以下是完成的 TimeTableApp.java 类:
main () 方法首先创建 SolverFactory :
SolverFactory 创建注册 @PlanningSolution 类、@PlanningEntity 类和 ConstraintProvider 类(您之前创建的全部)。
如果没有终止设置或 terminationEarly () 事件,则 Addressr 会永久运行。为避免这种情况,解决方案会将计时限制为 5 秒。
5 秒后,main () 方法加载问题,解决这个问题,并打印解决方案:
solve () 方法不会立即返回。在返回最佳解决方案前,它会运行五秒。
OptaPlanner 返回可用终止时间中找到的最佳解决方案。由于 NP 硬问题的性质,最佳解决方案可能不是最佳的,特别是对于较大的数据集。增加终止时间以有可能找到更好的解决方案。
generateDemoData () 方法生成 IANA 时间化问题来解决。
printTimetable () 方法可模拟控制台的可写时间,因此可以轻松地确定是否是良好的调度。
13.6. 创建并运行 这个时间可时间化应用程序 复制链接链接已复制到粘贴板!
现在,您已完成了 IANA timetable Java 应用程序的所有组件,您可以将它们放在 TimeTableApp.java 类中,并运行它。
先决条件
- 您已创建了 IANA timetable 应用程序所需的所有组件。
流程
创建
src/main/java/org/acme/rhacmtimetabling/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. 测试应用程序 复制链接链接已复制到粘贴板!
良好的应用程序包括测试覆盖。测试限制以及您的时间表项目中的 solver。
13.7.1. 测试 prompt 时间可写入限制 复制链接链接已复制到粘贴板!
要在隔离中测试 timetable 项目的约束,请在单元测试中使用 ConstraintVerifier。这会测试每个约束的发生与其他测试隔离的情况,这可在添加正确测试覆盖的新约束时降低维护。
当给定同一空间的三个较少时,此测试会验证约束 TimeTableConstraintProvider:: roomConflict,其中两个较少项具有相同的 timeslot,以匹配权重为 1。因此,如果约束权重为 10hard,它会按 -10hard 减少分数。
流程
创建 src/test/java/org/acme/optaplanner/solver/TimeTableConstraintProviderTest.java 类:
请注意,ConstraintVerifier 在测试过程中如何忽略约束权重,即使这些约束权重在 ConstraintProvider 中硬编码。这是因为在进入生产环境前定期更改约束权重。这样,约束权重调整不会影响单元测试。
13.7.2. 测试 7000 timetable resolver 复制链接链接已复制到粘贴板!
本例在 Red Hat build of Quarkus 上测试 OptaPlanner Demo 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 文件在测试期间如何覆盖 solver 终止,以便在找到可行的解决方案 (0hard thesoft) 时立即终止。这可避免硬编码一个固定时间,因为单元测试可能会在任意硬件上运行。这种方法可确保测试运行足够长,以找到可行的解决方案,即使在较慢的系统上也是如此。但是,即使在快速的系统上,它不会运行毫秒的时间超过其严格要求。
13.8. 日志记录 复制链接链接已复制到粘贴板!
完成 OptaPlanner 修订时间项目后,您可以使用日志信息来帮助微调 ConstraintProvider 中的限制。查看 info 日志文件中的分数计算速度,以评估对您的限制的影响。以调试模式运行应用程序,以显示应用程序采取的每个步骤,或使用 trace 日志记录来记录每个步骤和每次移动。
流程
- 在固定时间内运行 7000 timetable 应用程序,例如五分钟。
查看日志文件中的分数计算速度,如下例所示:
... Solving ended: ..., score calculation speed (29455/sec), ...
... Solving ended: ..., score calculation speed (29455/sec), ...Copy to Clipboard Copied! Toggle word wrap Toggle overflow -
更改约束,在相同时间内再次运行 planning 应用程序,并查看日志文件中记录的分数计算速度。
以 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
-
要从命令行运行调试模式,请使用
-
使用
trace日志记录来显示每个步骤,以及每个步骤的每次移动。
OptaPlanner 通过 Micrometer (一个 Java 应用程序的指标检测库)公开指标。您可以将 Micrometer 与 Prometheus 搭配使用,来监控 IANA 时间应用程序中的 OptaPlanner solver。
先决条件
- 您已使用 Java 创建了 OptaPlanner Demo timetable 应用程序。
- 已安装 Prometheus。有关安装 Prometheus 的详情,请查看 Prometheus 网站。
流程
将 Micrometer Prometheus 依赖项添加到 IANA 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.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 - 启动 7000 timetable 应用程序。
-
在 Web 浏览器中打开
http://localhost:8080/prometheus,以在 Prometheus 中查看时间化应用程序。 打开您的监控系统,以查看您的 OptaPlanner 项目的指标。
公开以下指标:
-
optaplanner_solver_errors_total:自测量开始以来发生的错误总数。 -
optaplanner_solver_solve_duration_seconds_active_count: 当前地址器的数量。 -
optaplanner_solver_solve_duration_seconds_max: 运行 longest-running 当前活跃解析程序的时间。 -
optaplanner_solver_solve_duration_seconds_duration_sum: 每个活跃解析器解析持续时间的总和。例如,如果有两个活跃的解析器,一个会运行一个三分钟,另一个一分钟,总地址时间为 4 分钟。
-
部分 V. 部署并使用 OptaPlanner vehicle 路由规划启动程序应用程序 复制链接链接已复制到粘贴板!
作为开发人员,您可以使用 OptaWeb Vehicle Routing starter 应用程序来优化您的电池团队。
先决条件
- 已安装 OpenJDK (JDK) 11。Red Hat build of Open JDK 位于红帽客户门户网站中的 Software Downloads 页面中(需要登录)。
- 已安装 Apache Maven 3.6 或更高版本。Maven 可从 Apache Maven 项目网站 获得。
第 14 章 什么是 OptaWeb Vehicle Routing? 复制链接链接已复制到粘贴板!
许多行业的主要目的是传输各种类型的 cargo。这些企业的目标是将加载点从加载点发送到目的地,并以最有效的方式使用其电点。主要目标之一是最大程度降低以时间或距离来测量的批量成本。
这种类型的优化问题被称为 vehicle 路由问题(VRP),并且有很多变化。
OptaPlanner 可以解决这些 vehicle 路由变体,并提供解决方案示例。OptaPlanner 允许开发人员专注于对业务规则和要求建模,而不是学习 约束编程。optaweb Vehicle Routing 通过提供一个回答问题的入门应用程序来扩展 OptaPlanner 的 vehicle 路由功能,例如:
- 我在哪里获取距离和旋转时间?
- 如何在映射中视觉化解决方案?
- 如何构建在云中运行的应用程序?
optaweb Vehicle Routing 使用 OpenStreetMap (OSM)数据文件。有关 OpenStreetMap 的详情,请查看 OpenStreetMap 网站。
在使用 OptaWeb Vehicle 路由时使用以下定义:
区域 :由 OSM 文件表示的 Earth 映射的任意区域。区域可以是国家、城市、连续或一组通常一起使用的国家/地区。例如,DACH 区域包括西班牙语(DE)、Austria (AT)和 Sphone (CH)。
国家/ 地区代码 :由 ISO-3166 标准分配给国家/地区的双字母代码。您可以使用国家代码来过滤 geosearch 结果。由于您可以处理跨越多个国家/地区(如 DACH 区域)的区域,OptaWeb Vehicle Routing 接受一个国家代码列表,以便 geosearch 过滤可与这些区域一起使用。有关国家代码列表,请参阅 ISO 3166 国家代码
Geosearch :一个查询类型,您可以提供一个地址或区域的位置名称作为 search 关键字,并接收多个 GPS 位置。返回的位置数量取决于 search 关键字的唯一位置数量。由于大多数位置名称不是唯一的,因此仅过滤出非相关的结果,方法是仅包含位于工作地区或国家/国家/地区。
第 15 章 下载并构建 OptaWeb Vehicle Routing 部署文件 复制链接链接已复制到粘贴板!
您必须在构建和部署 OptaWeb Vehicle 路由前下载并准备部署文件。
流程
导航到红帽客户门户网站中的 Software Downloads 页面(需要登录),然后从下拉菜单中选择产品和版本:
- 产品: 红帽构建的 OptaPlanner
- Version: 8.29
- 下载 Red Hat Build of OptaPlanner 8.29 Quick Starts。
-
提取
rhbop-8.29.0-red_hat_build_of_optaplanner-quickstarts-sources.zip文件。 -
导航到
org.optaweb.optaweb-vehicle-routing-8.29.0.Final-redhat-00009目录。 -
下载 Red Hat Build of OptaPlanner 8.29.0 Maven Repositroy (
rhbop-8.29.0-red_hat_build_of_optaplanner-maven-repository.zip)。 -
提取
rhbop-8.29.0-red_hat_build_of_optaplanner-maven-repository.zip文件。 -
将
rhbop-8.29.0-red_hat_build_of_optaplanner/maven-repository子目录的内容复制到~/.m2/repository目录中。 输入以下命令来构建 OptaWeb Vehicle Routing:
mvn clean package -DskipTests
mvn clean package -DskipTestsCopy to Clipboard Copied! Toggle word wrap Toggle overflow
第 16 章 使用 runLocally.sh 脚本在本地运行 OptaWeb Vehicle Routing 复制链接链接已复制到粘贴板!
Linux 用户可以使用 runLocally.sh Bash 脚本来运行 OptaWeb Vehicle Routing。
runLocally.sh 脚本不会在 macOS 中运行。如果您无法使用 runLocally.sh 脚本,请参阅 第 17 章 手动配置和运行 OptaWeb Vehicle Routing。
runLocally.sh 脚本自动执行以下设置步骤,否则必须手动执行:
- 创建数据目录。
- 从 Geofabrik 下载所选的 OpenStreetMap (OSM)文件。
- 尝试自动将国家代码与每个下载的 OSM 文件关联。
- 如果独立 JAR 文件不存在,则构建项目。
- 通过使用一个 region 参数或以交互方式选择区域来启动 OptaWeb Vehicle 路由。
有关执行 local Locally.sh 脚本的说明,请参见以下小节:
16.1. 以快速启动模式运行 OptaWeb Vehicle Routing runLocally.sh 脚本 复制链接链接已复制到粘贴板!
使用 OptaWeb Vehicle Routing 启动的最简单方法是在没有任何参数的情况下运行 runLocally.sh 脚本。
先决条件
- optaweb Vehicle Routing 已成功使用 Maven 构建,如 第 15 章 下载并构建 OptaWeb Vehicle Routing 部署文件 所述。
- 可通过互联网访问。
流程
在
org.optaweb.optaweb-vehicle-routing-8.29.0.Final-redhat-00009目录中输入以下命令。./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 用户界面,请在网页浏览器中输入以下 URL:
http://localhost:8080
http://localhost:8080Copy to Clipboard Copied! Toggle word wrap Toggle overflow
您第一次运行脚本时,需要几分钟才能启动,因为 OSM 文件必须由 GraphHopper 导入,并存储为 road 网络图。下一次运行 run ly.sh 脚本时,负载时间会非常快。
16.2. 以互动模式运行 OptaWeb Vehicle Routing runLocally.sh 脚本 复制链接链接已复制到粘贴板!
使用交互模式查看下载的 OSM 文件和分配给每个区域的国家代码的列表。您可以使用交互模式从 Geofabrik 下载额外的 OSM 文件,而无需访问网站并选择下载目的地。
先决条件
- optaweb Vehicle Routing 已成功使用 Maven 构建,如 第 15 章 下载并构建 OptaWeb Vehicle Routing 部署文件 所述。
- 可通过互联网访问。
流程
-
将目录更改为
org.optaweb.optaweb-vehicle-routing-8.29.0.Final-redhat-00009。 输入以下命令以互动模式运行脚本:
./runLocally.sh -i
./runLocally.sh -iCopy to Clipboard Copied! Toggle word wrap Toggle overflow -
在您选择的提示符处,输入d以显示下载菜单。之前下载的区域列表会显示,后跟您可以下载的区域列表。 可选:从之前下载的区域列表中选择区域:
- 在下载的区域列表中,输入与区域关联的数字。
- 按 Enter 键。
可选:下载区域:
-
输入与您要下载的区域关联的数字。例如,若要选择 Europe 映射,请输入
5。 -
要下载映射,请输入
d,然后按 Enter 键。 要在映射中下载特定区域,请输入
e,然后输入与您要下载的区域关联的数字,然后按 Enter 键。使用大型 OSM 文件为获得最佳用户体验,请使用较小的区域,如个人美国或美国状态。使用大于 1 GB 的 OSM 文件需要大量 RAM 大小,并且需要大量时间(最多几小时)进行初始处理。
应用程序在下载 OSM 文件后启动。
-
输入与您要下载的区域关联的数字。例如,若要选择 Europe 映射,请输入
要打开 OptaWeb Vehicle Routing 用户界面,请在网页浏览器中输入以下 URL:
http://localhost:8080
http://localhost:8080Copy to Clipboard Copied! Toggle word wrap Toggle overflow
16.3. 在非互动模式下运行 OptaWeb Vehicle Routing runLocally.sh 脚本 复制链接链接已复制到粘贴板!
在非互动模式中使用 OptaWeb Vehicle Routing 启动 OptaWeb Vehicle Routing,并带有之前下载的 OSM 文件的单一命令。当您想快速切换区域或进行演示时,这非常有用。
先决条件
- optaweb Vehicle Routing 已成功使用 Maven 构建,如 第 15 章 下载并构建 OptaWeb Vehicle Routing 部署文件 所述。
- 您要使用的区域的 OSM 文件已被下载。有关下载 OSM 文件的详情,请参考 第 16.2 节 “以互动模式运行 OptaWeb Vehicle Routing runLocally.sh 脚本”。
- 可通过互联网访问。
流程
-
将目录更改为
org.optaweb.optaweb-vehicle-routing-8.29.0.Final-redhat-00009。 执行以下命令,其中 <
;OSM_FILE_NAME> 是之前下载的 OSM 文件:./runLocally.sh <OSM_FILE_NAME>
./runLocally.sh <OSM_FILE_NAME>Copy to Clipboard Copied! Toggle word wrap Toggle overflow
16.4. 更新数据目录 复制链接链接已复制到粘贴板!
如果要使用不同的数据目录,您可以更新 OptaWeb Vehicle Routing 使用的数据目录。默认数据目录为 $HOME/.optaweb-vehicle-routing。
先决条件
- optaweb Vehicle Routing 已成功使用 Maven 构建,如 第 15 章 下载并构建 OptaWeb Vehicle Routing 部署文件 所述。
流程
-
要使用不同的数据目录,请将目录的绝对路径添加到当前数据目录中的
.DATA_DIR_LAST文件。 要更改与地区关联的国家代码,请在当前数据目录中编辑
national_codes目录中的对应文件。例如,如果您为 Scotland 下载 OSM 文件,且脚本无法猜测国家代码,请将 national
_codes/scotland-latest的内容设置为 GB。-
要删除区域,请从 data 目录中的
openstreetmap目录中删除对应的 OSM 文件,并删除graphhopper目录中的区域目录。
第 17 章 手动配置和运行 OptaWeb Vehicle Routing 复制链接链接已复制到粘贴板!
运行 OptaWeb Vehicle Routing 的最简单方法是使用 runlocally.sh 脚本。但是,如果系统上没有 Bash,您可以手动完成 runlocally.sh 脚本执行的步骤。
先决条件
- optaweb Vehicle Routing 已成功使用 Maven 构建,如 第 15 章 下载并构建 OptaWeb Vehicle Routing 部署文件 所述。
- 可通过互联网访问。
流程
下载路由数据。
路由引擎需要地理数据来计算在位置间传输所需时间。在运行 OptaWeb Vehicle Routing 前,您必须将 OpenStreetMap (OSM)数据文件下载到本地文件系统中。
注意OSM 数据文件通常介于 100 MB 到 1 GB 之间,需要时间来下载文件,以便在构建或启动 OptaWeb Vehicle Routing 应用程序前下载文件。
-
在网页浏览器中打开
http://download.geofabrik.de/。 - 单击 Sub Region 列表中的区域,如 Europe。此时会打开 subregion 页面。
-
在 Sub Regions 表中,下载国家的 OSM 文件(
.osm.pbf),如 Belgium。
-
在网页浏览器中打开
创建数据结构。
optaweb Vehicle Routing 读取和写入文件系统中多种数据。它从
openstreetmap目录中读取 OSM (OpenStreetMap)文件,将 road 网络图写入图形跃点目录,并在名为db的目录中保留用户数据。创建一个用于存储所有数据的新目录,以便更轻松地升级到 OptaWeb Vehicle Routing 的较新版本,并继续使用之前创建的数据。-
创建
$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 应用程序首次运行时创建。之后,您的目录结构类似以下示例:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
-
创建
-
将目录改为
rhbop-8.29.0-kogito-and-optaplanner-quickstarts/optaweb-8.29.0.Final-redhat-00009/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 下载 Central 美国的 OSM 映射(
central-america-latest.osm.pbf),并在国家 Belize (BZ)和 Guatemala (GT)中搜索。Copy to Clipboard Copied! Toggle word wrap Toggle overflow
-
要打开 OptaWeb Vehicle Routing 用户界面,请在网页浏览器中输入以下 URL:
http://localhost:8080
http://localhost:8080Copy to Clipboard Copied! Toggle word wrap Toggle overflow
第 18 章 在 Red Hat OpenShift 上运行 OptaWeb Vehicle Routing 复制链接链接已复制到粘贴板!
Linux 用户可以使用 runOnOpenShift.sh Bash 脚本在 Red Hat OpenShift 上安装 OptaWeb Vehicle Routing。
runOnOpenShift.sh 脚本不会在 macOS 上运行。
先决条件
-
您可以访问 OpenShift 集群,并且已安装 OpenShift 命令行界面(
oc)。有关 Red Hat OpenShift 的详情,请参阅安装 OpenShift Container Platform。 - optaweb Vehicle Routing 已成功使用 Maven 构建,如 第 15 章 下载并构建 OptaWeb Vehicle Routing 部署文件 所述。
- 可通过互联网访问。
流程
- 登录或启动 Red Hat OpenShift 集群。
输入以下命令,其中 &
lt;PROJECT_NAME> 是新项目的名称:oc new-project <PROJECT_NAME>
oc new-project <PROJECT_NAME>Copy to Clipboard Copied! Toggle word wrap Toggle overflow -
如有必要,将目录改为
org.optaweb.optaweb-vehicle-routing-8.29.0.Final-redhat-00009。 输入以下命令来执行
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> :可从 OpenShift 访问的 PBF 格式的 OSM 数据文件的 URL。该文件将在后端启动过程中下载,并保存为/deployments/local/<OSM_FILE_NAME>。在以下示例中,OptaWeb Vehicle Routing 下载 Central 美国的 OSM 映射(
central-america-latest.osm.pbf),并在国家 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。
18.1. 使用本地更改更新部署的 OptaWeb Vehicle Routing 应用程序 复制链接链接已复制到粘贴板!
在 Red Hat OpenShift 上部署 OptaWeb Vehicle Routing 应用程序后,您可以更新后端和前端。
先决条件
- optaweb Vehicle Routing 已成功使用 Maven 构建并在 OpenShift 上部署。
流程
要更新后端,请执行以下步骤:
- 更改源代码,并使用 Maven 构建后端模块。
-
将目录更改为
org.optaweb.optaweb-vehicle-routing-8.29.0.Final-redhat-00009。 输入以下命令启动 OpenShift 构建:
oc start-build backend --from-dir=. --follow
oc start-build backend --from-dir=. --followCopy to Clipboard Copied! Toggle word wrap Toggle overflow
要更新前端,请执行以下步骤:
-
更改源代码,并使用
npm工具构建前端模块。 -
将目录更改为
sources/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
-
更改源代码,并使用
第 19 章 使用 OptaWeb Vehicle Routing 复制链接链接已复制到粘贴板!
在 OptaWeb Vehicle Routing 应用程序中,您可以标记映射中的多个位置。假设第一个位置为 depot。vehicles 必须从这一投票到您标记的所有其他位置提供很好的选择。
您可以设置 vehicle 的数量,以及每个载体的获取容量。但是,路由无法保证使用所有 vehicles。该应用程序根据需要对最佳路由使用尽可能多的 vehicle。
当前版本有一些限制:
- 每次发送到某个位置的工作都应该使用一个点载载容量。例如,一个容量为 10 的 vehicle 可以访问最多 10 个位置,然后再返回 depot。
- 不支持设置 vehicles 和 location 的自定义名称。
19.1. 创建路由 复制链接链接已复制到粘贴板!
要创建最佳路由,请使用 OptaWeb Vehicle Routing 用户界面的 Demo 选项卡。
先决条件
- optaweb Vehicle Routing 正在运行,您可以访问用户界面。
流程
- 在 OptaWeb Vehicle Routing 中,单击 Demo 以打开 Demo 选项卡。
- 使用映射上方的蓝色减号和加号按钮来设置载号。每个 vehicle 都有默认的容量 10。
根据需要,使用映射上的加号按钮到缩放。
注意不要双击 缩放。双击 也会创建一个位置。
- 点 depot 的位置。
- 点映射上的其他位置进行发送点。
如果要删除位置:
- 将鼠标光标悬停在位置上,以查看位置名称。
- 在屏幕左侧的列表中找到位置名称。
- 点名称旁边的 X 图标。
每次添加或删除位置或更改 vehicle 数量时,应用程序会创建并显示新的最佳路由。如果解决方案使用多个 vehicles,则应用程序以不同的颜色显示每个载体的路由。
19.2. 查看和设置其他详情 复制链接链接已复制到粘贴板!
您可以使用 OptaWeb Vehicle Routing 用户界面中的其他标签页来查看和设置其他详情。
先决条件
- optaweb Vehicle Routing 正在运行,您可以访问用户界面。
流程
- 点 Vehicles 选项卡查看、添加和删除 vehicles,以及设置每个 vehicle 的容量。
- 点 Visits 选项卡查看和删除位置。
- 点 Route 选项卡选择每个载体并查看所选载体的路由。
19.3. 使用 OptaWeb Vehicle 路由创建自定义数据集 复制链接链接已复制到粘贴板!
有由几个大型 Belgian 城市组成的内置演示数据集。如果要在 Load demo 菜单中有更多演示,您可以准备自己的数据集。
流程
- 在 OptaWeb Vehicle Routing 中,通过单击映射或使用 geosearch 添加 depot 和一个或多个访问。
点 Export,并将文件保存到数据集目录中。
注意data set 目录是
app.demo.data-set-dir属性中指定的目录。如果应用程序通过
runLocally.sh脚本运行,数据集目录被设置为$HOME/.optaweb-vehicle-routing/dataset。否则,属性会从
application.properties文件中获取,默认为rhbop-8.29.0-kogito-and-optaplanner-quickstarts/optaweb-8.29.0.Final-redhat-00009/optaweb-vehicle-routing/optaweb-vehicle-routing-standalone/target/local/dataset。您可以编辑
app.demo.data-set-dir属性来指定 diffent 数据目录。- 编辑 YAML 文件并为数据集选择唯一名称。
- 重启后端。
重启后端后,数据集目录中的文件会出现在 Load demo 菜单中。
19.4. 对 OptaWeb Vehicle 路由进行故障排除 复制链接链接已复制到粘贴板!
如果 OptaWeb Vehicle 路由行为意外,请按照以下步骤出现问题。
先决条件
- optaweb Vehicle Routing 正在运行,并意外地执行。
流程
- 要识别问题,请查看后端终端输出日志。
要解决这个问题,删除后端数据库:
- 通过在后端终端窗口中按 Ctrl+C 来停止后端。
-
删除
optaweb-vehicle-routing/optaweb-vehicle-routing-backend/local/db目录。 - 重启 OptaWeb Vehicle Routing。
第 20 章 optaweb Vehicle Routing 开发指南 复制链接链接已复制到粘贴板!
本节论述了如何在开发模式中配置和运行后端模块。
20.1. optaweb Vehicle Routing 项目结构 复制链接链接已复制到粘贴板!
OptaWeb Vehicle Routing 项目是一个多模块 Maven 项目。
图 20.1. 模块依赖关系树图
后端和前端模块位于模块树的底部。这些模块包含应用程序源代码。
standalone 模块是一种编译模块,用于将后端和前端合并到单个可执行 JAR 文件中。
distribution 模块代表最终装配步骤。它采用独立应用程序和文档,并将其嵌套在容易分发的存档中。
后端和前端是单独的项目,您可以单独构建和部署。实际上,它们以完全不同的语言编写,并使用不同的工具构建。这两个项目都可以提供现代开发人员体验,并在代码更改和正在运行的应用程序之间快速切换。
下一小节介绍了如何在开发模式下运行后端和前端项目。
20.2. OptaWeb Vehicle Routing 后端模块 复制链接链接已复制到粘贴板!
backend 模块包含一个服务器端应用程序,它使用 OptaPlanner 优化载体路由。优化是一个 CPU 密集型计算,必须避免任何 I/O 操作才能对其完整潜在的执行。由于其中一个目标是最大程度降低往返成本,无论是时间还是距离,OptaWeb Vehicle 路由会使传输成本信息保留在 RAM 内存中。OptaPlanner 需要了解用户输入的每一个位置之间的旋转成本。此信息存储在名为 距离列表的结构中。
当您输入新位置时,OptaWeb Vehicle Routing 会计算新位置和目前输入的所有其他位置之间的旋转成本,并将传输成本存储在距离列表中。旋转成本计算由 GraphHopper 路由引擎执行。
后端模块实现以下额外功能:
- Persistence
- 前端的 websocket 连接
- 数据集加载、导出和导入
要了解更多有关后端代码架构的信息,请参阅 第 21 章 optaweb Vehicle Routing 后端架构。
下一节介绍了如何在开发模式下配置和运行后端。
20.2.1. 运行 OptaWeb Vehicle Routing 后端模块 复制链接链接已复制到粘贴板!
您可以在 Quarkus 开发模式下运行后端模块。
先决条件
- 配置了 optaweb Vehicle Routing,如 第 17 章 手动配置和运行 OptaWeb Vehicle Routing 所述。
流程
-
将目录改为
rhbop-8.29.0-kogito-and-optaplanner-quickstarts/optaweb-8.29.0.Final-redhat-00009/optaweb-vehicle-routing/optaweb-vehicle-routing-backend。 要在开发模式下运行后端,请输入以下命令:
mvn compile quarkus:dev
mvn compile quarkus:devCopy to Clipboard Copied! Toggle word wrap Toggle overflow
您可以使用 IntelliJ IDEA Ulitmate 运行 OptaWeb Vehicle Routing 后端模块,以便更轻松地开发您的项目。IntelliJ IDEA Ultimate 包含一个 Quarkus 插件,它为使用 Quarkus 框架的模块自动创建运行配置。
流程
使用 optaweb-vehicle-routing-backend 运行配置来运行后端。
其他资源
如需更多信息,请参阅 运行 Quarkus 应用程序。
20.2.3. Quarkus 开发模式 复制链接链接已复制到粘贴板!
在开发模式中,如果更改了后端源代码或配置,并且您刷新前端运行的浏览器标签页,后端会自动重启。
了解有关 Quarkus 开发模式 的更多信息。
20.2.4. 更改 OptaWeb Vehicle Routing 后端模块系统属性值 复制链接链接已复制到粘贴板!
您可以临时或永久覆盖 OptaWeb Vehicle Routing 后端模块的默认系统属性值。
OptaWeb Vehicle Routing 后端模块系统属性存储在 /src/main/resources/application.properties 文件中。此文件在版本控制下。使用它永久存储默认配置属性值,并定义 Quarkus 配置集。
先决条件
- OptaWeb Vehicle Routing starter 应用已下载并提取。有关详情请参考 第 15 章 下载并构建 OptaWeb Vehicle Routing 部署文件。
流程
要临时覆盖默认系统属性值,在运行
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 配置属性的完整列表,请参阅 第 22 章 optaweb Vehicle Routing 后端配置属性。
20.2.5. optaweb Vehicle Routing 后端日志记录 复制链接链接已复制到粘贴板!
optaweb Vehicle Routing 使用 SLF4J API 和 Logback 作为日志记录框架。如需更多信息,请参阅 Quarkus - 配置日志记录。
20.3. 使用 OptaWeb Vehicle Routing 前端模块 复制链接链接已复制到粘贴板!
前端项目使用 Create React App 启动。创建 React App 提供了很多脚本和依赖项,可帮助开发并为生产环境构建应用程序。
先决条件
- OptaWeb Vehicle Routing starter 应用已下载并提取。有关详情请参考 第 15 章 下载并构建 OptaWeb Vehicle Routing 部署文件。
流程
在 Fedora 中,输入以下命令设置开发环境:
sudo dnf install npm
sudo dnf install npmCopy to Clipboard Copied! Toggle word wrap Toggle overflow -
将目录更改为
rhbop-8.29.0-kogito-and-optaplanner-quickstarts/optaweb-8.29.0.Final-redhat-00009/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 安装 时才执行。每当package.json中列出的依赖项更改时,例如,当您拉取到 master 分支更改时,您必须在运行开发服务器前执行npm 安装。输入以下命令来运行开发服务器:
npm start
npm startCopy to Clipboard Copied! Toggle word wrap Toggle overflow 如果没有自动打开,在 Web 浏览器中打开
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 更改
REACT_APP_BACKEND_URL环境变量的值,以指定在执行npm start或 npmrun 构建时由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
第 21 章 optaweb Vehicle Routing 后端架构 复制链接链接已复制到粘贴板!
域模型和用例对于应用程序至关重要。OptaWeb Vehicle Routing 域模型位于构架的中心,由嵌入用例的应用程序层组成。路由优化、距离计算、持久性和网络通信等功能被视为实施详细信息,并放置在架构的最顶层。
图 21.1. 应用程序层图
21.1. 代码机构 复制链接链接已复制到粘贴板!
后端代码组织在三个层中,如上图所示。
service 软件包包含实施用例的应用程序层。插件 软件包包含基础架构层。
每个层中的代码按照功能进一步组织。这意味着每个服务或插件都有自己的软件包。
21.2. 依赖项规则 复制链接链接已复制到粘贴板!
编译时依赖项只允许从外部层指向中心。遵循此规则有助于使域模型独立于底层框架和其他实施详情,并更精确建模业务实体的行为。随着正在推送到外文的演示和持久性,测试业务实体和用例的行为更为容易。
域没有依赖项。
服务仅依赖于域。如果服务需要发送结果(例如到数据库或客户端),它会使用输出边界接口。其实现由 上下文和依赖项注入 (CDI)容器注入。
插件以两种方式依赖于服务:首先,它们根据用户输入或来自优化引擎的事件调用服务。服务注入到插件中,该插件会将构建和依赖项解析的负担移到 IoC 容器。其次,插件实施服务输出边界接口来处理用例结果,例如对数据库保留更改或向 Web UI 发送响应。
21.3. 域软件包 复制链接链接已复制到粘贴板!
domain 软件包包含对此项目的域建模 的业务对象,如 Location、Vehicle、Route。这些对象严格于面向业务的,不得受到任何工具和框架的影响,如对象关系映射工具和 Web 服务框架。
21.4. 服务软件包 复制链接链接已复制到粘贴板!
service 软件包包含实施 用例 的类。使用案例描述了您要做的项,例如添加新位置、更改出口容量或查找地址协调。管理用例的新规则使用域对象表示。
服务通常需要与外部层中的插件交互,如持久性、Web 和优化。为了满足层之间的依赖项规则,服务和插件之间的交互以定义服务依赖项的接口表示。插件可以通过提供实现服务的边界接口的 bean 来满足服务的依赖项。CDI 容器会创建一个插件 bean 实例,并在运行时将其注入该服务。这是控制原则的 inversion 示例。
21.5. 插件软件包 复制链接链接已复制到粘贴板!
插件 软件包包含基础架构功能,如优化、持久性、路由和网络。
第 22 章 optaweb Vehicle Routing 后端配置属性 复制链接链接已复制到粘贴板!
您可以设置下表中列出的 OptaWeb Vehicle Routing 应用程序属性。
| 属性 | 类型 | 示例 | 描述 |
|---|---|---|---|
|
| 相对路径或绝对路径 |
|
自定义数据集从此目录加载。默认为 |
|
| 相对路径或绝对路径 |
|
H2 用来存储数据库文件的目录。默认为 |
|
|
|
| 限制 geosearch 结果。 |
|
| Enumeration |
|
路由引擎实施。默认为 |
|
| 相对路径或绝对路径 |
|
GraphHopper 用来存储 road 网络图形的目录。默认为 |
|
| 相对路径或绝对路径 |
|
包含 OSM 文件的目录。默认为 |
|
| 文件名 |
|
GraphHopper 要载入的 OSM 文件的名称。该文件必须放在 |
|
|
|
| 在位置更改后解析程序应运行的时间。 |
|
| IP 地址或主机名 |
| 将服务器绑定到的网络地址。 |
|
| 端口号 |
| 服务器 HTTP 端口。 |
附录 A. 版本信息 复制链接链接已复制到粘贴板!
文档最后在 2023 年 2 月 13 日更新。