APS开源源码解读: 排程工具 optaplanner
抽象层次非常好,广义优化工具。用于排产没有复杂的落地示例
- https://github.com/apache/incubator-kie-optaplanner/blob/main/optaplanner-examples/src/main/java/org/optaplanner/examples/projectjobscheduling/app/ProjectJobSchedulingApp.java
- https://github.com/eugenp/tutorials/tree/master/timefold-solver
- https://github.com/kisszhu/aps
安装
- java
- maven
配置
- xml配置
- solutionClass
- entityClass
- constraintProviderClass
- 约束设置
- termination: 5min
- constructionHeuristic: FIRST_FIT
- first fit
- localSearch:
也即是说,先定义对象“entityClass”, 转化为约束“constraintProviderClass”,然后运用 constructionHeuristic + localSearch的方式进行求解
其中,一个整体的任务叫做project, 资源有可再生,非可再生。
工序叫做Job,job跟着若干project。每个工序有自己的资源,ResourceRequirement. 执行模式Execution Mode. 分配allocation.
- resourceRequirement和allocation都要设置execution mode
- 每个工序JOb, 有自己的resourceRequirement, executation mode, allocation
基本概念
- PlanningSolution
- 定义Problem,以及解
- planning entity
- Allocation
- planing variable
- executionMode
- delay
- shadow variable
- predecessorsDoneDate
- https://www.optaplanner.org/docs/optaplanner/latest/shadow-variable/shadow-variable.html
- planning score
- 核心的优化算法
约束
比如排产中的工序依赖关系
import org.timefold.solver.core.api.score.buildin.hardsoft.HardSoftScore;
import org.timefold.solver.core.api.score.stream.ConstraintProvider;
import org.timefold.solver.core.api.score.stream.Constraint;
import org.timefold.solver.core.api.score.stream.ConstraintStream;
import org.timefold.solver.core.api.score.stream.Joiners;public class JobShopConstraintProvider implements ConstraintProvider {@Overridepublic Constraint[] defineConstraints(ConstraintFactory constraintFactory) {return new Constraint[] {// Ensure operations follow the sequence within each jobconstraintFactory.from(Operation.class).join(Operation.class, Joiners.filteringEach(otherOp -> otherOp.getJob().equals(op.getJob()) && otherOp.getSequence() == op.getSequence() + 1)).penalize("Operations must follow sequence",HardSoftScore.ONE_HARD,(op, otherOp) -> 1),// Ensure machine constraints are respectedconstraintFactory.from(Operation.class).join(Operation.class, Joiners.filteringEach((op1, op2) ->op1.getMachine().equals(op2.getMachine()) &&op1.getEndTime() > op2.getStartTime() &&op1.getStartTime() < op2.getEndTime() &&!op1.equals(op2))).penalize("Machine cannot process two operations at once",HardSoftScore.ONE_HARD,(op1, op2) -> 1)};}
}
官方示例
入口在APP的main
public static void main(String[] args) {prepareSwingEnvironment();new ProjectJobSchedulingApp().init();}
init
public void init() {init(null, true);}public void init(Component centerForComponent, boolean exitOnClose) {solutionBusiness = createSolutionBusiness();solverAndPersistenceFrame = new SolverAndPersistenceFrame<>(solutionBusiness, createSolutionPanel(),createExtraActions());solverAndPersistenceFrame.setDefaultCloseOperation(exitOnClose ? WindowConstants.EXIT_ON_CLOSE : WindowConstants.DISPOSE_ON_CLOSE);solverAndPersistenceFrame.init(centerForComponent);solverAndPersistenceFrame.setVisible(true);}
其中,solution business
- SolverFactory.createFromXmlResource建立了solver
public SolutionBusiness<Solution_, ?> createSolutionBusiness() {SolutionBusiness<Solution_, ?> solutionBusiness = new SolutionBusiness<>(this,SolverFactory.createFromXmlResource(solverConfigResource));solutionBusiness.setDataDir(determineDataDir(dataDirName));solutionBusiness.setSolutionFileIO(createSolutionFileIO());solutionBusiness.setImporters(createSolutionImporters());solutionBusiness.setExporters(createSolutionExporters());solutionBusiness.updateDataDirs();return solutionBusiness;}
在APP类继承的solution中,示例采用的是schedule,也就是planningsolution,作为问题和排产结果
package org.optaplanner.examples.projectjobscheduling.domain;import java.util.List;import org.optaplanner.core.api.domain.solution.PlanningEntityCollectionProperty;
import org.optaplanner.core.api.domain.solution.PlanningScore;
import org.optaplanner.core.api.domain.solution.PlanningSolution;
import org.optaplanner.core.api.domain.solution.ProblemFactCollectionProperty;
import org.optaplanner.core.api.score.buildin.hardmediumsoft.HardMediumSoftScore;
import org.optaplanner.examples.common.domain.AbstractPersistable;
import org.optaplanner.examples.projectjobscheduling.domain.resource.Resource;@PlanningSolution
public class Schedule extends AbstractPersistable {private List<Project> projectList;private List<Job> jobList;private List<ExecutionMode> executionModeList;private List<Resource> resourceList;private List<ResourceRequirement> resourceRequirementList;private List<Allocation> allocationList;private HardMediumSoftScore score;public Schedule() {}public Schedule(long id) {super(id);}@ProblemFactCollectionPropertypublic List<Project> getProjectList() {return projectList;}public void setProjectList(List<Project> projectList) {this.projectList = projectList;}@ProblemFactCollectionPropertypublic List<Job> getJobList() {return jobList;}public void setJobList(List<Job> jobList) {this.jobList = jobList;}@ProblemFactCollectionPropertypublic List<ExecutionMode> getExecutionModeList() {return executionModeList;}public void setExecutionModeList(List<ExecutionMode> executionModeList) {this.executionModeList = executionModeList;}@ProblemFactCollectionPropertypublic List<Resource> getResourceList() {return resourceList;}public void setResourceList(List<Resource> resourceList) {this.resourceList = resourceList;}@ProblemFactCollectionPropertypublic List<ResourceRequirement> getResourceRequirementList() {return resourceRequirementList;}public void setResourceRequirementList(List<ResourceRequirement> resourceRequirementList) {this.resourceRequirementList = resourceRequirementList;}@PlanningEntityCollectionPropertypublic List<Allocation> getAllocationList() {return allocationList;}public void setAllocationList(List<Allocation> allocationList) {this.allocationList = allocationList;}@PlanningScorepublic HardMediumSoftScore getScore() {return score;}public void setScore(HardMediumSoftScore score) {this.score = score;}// ************************************************************************// Complex methods// ************************************************************************}
Timefold 示例
Solver job接受到problem,开始run
@Deprecated(forRemoval = true, since = "1.6.0")default SolverJob<Solution_, ProblemId_> solve(ProblemId_ problemId,Solution_ problem, Consumer<? super Solution_> finalBestSolutionConsumer,BiConsumer<? super ProblemId_, ? super Throwable> exceptionHandler) {SolverJobBuilder<Solution_, ProblemId_> builder = solveBuilder().withProblemId(problemId).withProblem(problem);if (finalBestSolutionConsumer != null) {builder.withFinalBestSolutionConsumer(finalBestSolutionConsumer);}if (exceptionHandler != null) {builder.withExceptionHandler(exceptionHandler);}return builder.run();}
solverStatus = SolverStatus.SOLVING_ACTIVE;// Create the consumer thread pool only when this solver job is active.consumerSupport = new ConsumerSupport<>(getProblemId(), bestSolutionConsumer, finalBestSolutionConsumer,firstInitializedSolutionConsumer, exceptionHandler, bestSolutionHolder);Solution_ problem = problemFinder.apply(problemId);// add a phase lifecycle listener that unlock the solver status lock when solving startedsolver.addPhaseLifecycleListener(new UnlockLockPhaseLifecycleListener());// add a phase lifecycle listener that consumes the first initialized solutionsolver.addPhaseLifecycleListener(new FirstInitializedSolutionPhaseLifecycleListener(consumerSupport));solver.addEventListener(this::onBestSolutionChangedEvent);final Solution_ finalBestSolution = solver.solve(problem);consumerSupport.consumeFinalBestSolution(finalBestSolution);return finalBestSolution;
理解
-
https://www.optaplanner.org/docs/optaplanner/latest/shadow-variable/shadow-variable.html
-
build_solver/ default_solver_factory
public Solver<Solution_> buildSolver(SolverConfigOverride<Solution_> configOverride) {Objects.requireNonNull(configOverride, "Invalid configOverride (null) given to SolverFactory.");var isDaemon = Objects.requireNonNullElse(solverConfig.getDaemon(), false);var solverScope = new SolverScope<Solution_>();var monitoringConfig = solverConfig.determineMetricConfig();solverScope.setMonitoringTags(Tags.empty());var metricsRequiringConstraintMatchSet = Collections.<SolverMetric> emptyList();if (!monitoringConfig.getSolverMetricList().isEmpty()) {solverScope.setSolverMetricSet(EnumSet.copyOf(monitoringConfig.getSolverMetricList()));metricsRequiringConstraintMatchSet = solverScope.getSolverMetricSet().stream().filter(SolverMetric::isMetricConstraintMatchBased).filter(solverScope::isMetricEnabled).toList();} else {solverScope.setSolverMetricSet(EnumSet.noneOf(SolverMetric.class));}var environmentMode = solverConfig.determineEnvironmentMode();var constraintMatchEnabled = !metricsRequiringConstraintMatchSet.isEmpty() || environmentMode.isAsserted();if (constraintMatchEnabled && !environmentMode.isAsserted()) {LOGGER.info("Enabling constraint matching as required by the enabled metrics ({}). This will impact solver performance.",metricsRequiringConstraintMatchSet);}var innerScoreDirector = scoreDirectorFactory.buildScoreDirector(true, constraintMatchEnabled);solverScope.setScoreDirector(innerScoreDirector);solverScope.setProblemChangeDirector(new DefaultProblemChangeDirector<>(innerScoreDirector));var moveThreadCount = resolveMoveThreadCount(true);var bestSolutionRecaller = BestSolutionRecallerFactory.create().<Solution_> buildBestSolutionRecaller(environmentMode);var randomFactory = buildRandomFactory(environmentMode);var configPolicy = new HeuristicConfigPolicy.Builder<>(environmentMode,moveThreadCount,solverConfig.getMoveThreadBufferSize(),solverConfig.getThreadFactoryClass(),solverConfig.getNearbyDistanceMeterClass(),randomFactory.createRandom(),scoreDirectorFactory.getInitializingScoreTrend(),solutionDescriptor,ClassInstanceCache.create()).build();var basicPlumbingTermination = new BasicPlumbingTermination<Solution_>(isDaemon);var termination = buildTerminationConfig(basicPlumbingTermination, configPolicy, configOverride);var phaseList = buildPhaseList(configPolicy, bestSolutionRecaller, termination);return new DefaultSolver<>(environmentMode, randomFactory, bestSolutionRecaller, basicPlumbingTermination,termination, phaseList, solverScope,moveThreadCount == null ? SolverConfig.MOVE_THREAD_COUNT_NONE : Integer.toString(moveThreadCount));}
solver的主流程
@Overridepublic final Solution_ solve(Solution_ problem) {if (problem == null) {throw new IllegalArgumentException("The problem (" + problem + ") must not be null.");}// No tags for these metrics; they are globalLongTaskTimer solveLengthTimer = Metrics.more().longTaskTimer(SolverMetric.SOLVE_DURATION.getMeterId());Counter errorCounter = Metrics.counter(SolverMetric.ERROR_COUNT.getMeterId());solverScope.setBestSolution(problem);solverScope.setSolver(this);outerSolvingStarted(solverScope);boolean restartSolver = true;while (restartSolver) {LongTaskTimer.Sample sample = solveLengthTimer.start();try {// solvingStarted will call registerSolverSpecificMetrics(), since// the solverScope need to be fully initialized to calculate the// problem's scale metricssolvingStarted(solverScope);runPhases(solverScope);solvingEnded(solverScope);} catch (Exception e) {errorCounter.increment();solvingError(solverScope, e);throw e;} finally {sample.stop();unregisterSolverSpecificMetrics();}restartSolver = checkProblemFactChanges();}outerSolvingEnded(solverScope);return solverScope.getBestSolution();}
- run_phase /abstract_solver
protected void runPhases(SolverScope<Solution_> solverScope) {if (!solverScope.getSolutionDescriptor().hasMovableEntities(solverScope.getScoreDirector())) {logger.info("Skipped all phases ({}): out of {} planning entities, none are movable (non-pinned).",phaseList.size(), solverScope.getWorkingEntityCount());return;}Iterator<Phase<Solution_>> it = phaseList.iterator();while (!solverTermination.isSolverTerminated(solverScope) && it.hasNext()) {Phase<Solution_> phase = it.next();phase.solve(solverScope);// If there is a next phase, it starts from the best solution, which might differ from the working solution.// If there isn't, no need to planning clone the best solution to the working solution.if (it.hasNext()) {solverScope.setWorkingSolutionFromBestSolution();}}}
-
solver外面的phase, PhaseFactory
-
dostep
局部搜索在当前解上尝试多个移动,并选择最佳的被接受的移动作为这一步。A step is the winning Move。在每一步,它尝试所有选定的移动,除非是选定的step,否则它不会进一步研究那个解。这就是局部搜索具有很高可扩展性的原因之一。
private void doStep(CustomStepScope<Solution_> stepScope, CustomPhaseCommand<Solution_> customPhaseCommand) {InnerScoreDirector<Solution_, ?> scoreDirector = stepScope.getScoreDirector();customPhaseCommand.changeWorkingSolution(scoreDirector);calculateWorkingStepScore(stepScope, customPhaseCommand);solver.getBestSolutionRecaller().processWorkingSolutionDuringStep(stepScope);}
- 决定下一步
- A MoveSelector which selects the possible moves of the current solution. See the chapter move and neighborhood selection.
- An Acceptor which filters out unacceptable moves.
- A Forager which gathers accepted moves and picks the next step from them.
<localSearch><unionMoveSelector>...</unionMoveSelector><acceptor>...</acceptor><forager>...</forager></localSearch>
从底向上看,理解可能的move。如果是entity+value组合,或者是entity和entity进行新的组合。也许这就是叫做组合优化的原因?
相关文章:

APS开源源码解读: 排程工具 optaplanner
抽象层次非常好,广义优化工具。用于排产没有复杂的落地示例 https://github.com/apache/incubator-kie-optaplanner/blob/main/optaplanner-examples/src/main/java/org/optaplanner/examples/projectjobscheduling/app/ProjectJobSchedulingApp.javahttps://github…...

AMEYA360:村田量产用于汽车市场的高可靠性0603M铜电极负温度系数NTC热敏电阻
株式会社村田制作所开发了0603M尺寸(0.60.30.3mm)铜电极负温度系数(NTC)热敏电阻,型号分别是“NCU03XH103F6SRL”和“NCU03XH103F60RL”,该新品扩充了NCU系列的产品尺寸阵容,满足了汽车市场应用中电路板的高密度化和小型化、以及对电子部件的…...
代码随想录第十天|150.逆波兰表达式求值 239.滑动窗口的最大值 347.前K个高频元素
150.逆波兰表达式求解 思路:做过 使用stoi :字符串转数字 class Solution { public:int cal(int num1,int num2,char c){int res;if(c){resnum1num2;}if(c-){resnum2-num1;}if(c*){resnum1*num2;}if(c/){resnum2/num1;}return res;}int evalRPN(vector…...

[阅读笔记]《解读基金—我的投资观与实践》— 季凯帆
📢博客主页:https://loewen.blog.csdn.net📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!📢本文由 丶布布原创,首发于 CSDN,转载注明出处🙉📢现…...

2.3之前
1. 2. freertos基础时钟:freertos自动的采用systick定时器,作为freertos基础时钟,systick定时器只有定时中断功能,1ms中断一次。...

处理器基础知识——cache
本文节选自书籍《大话处理器:处理器基础知识读本》 PDF版本可以访问我的网盘通过网盘分享的文件:大话处理器:处理器基础知识读本.pdf 提取码: 1234 0 什么是 Cache 随处可见的Cache–技术来源于生活 使用电脑的人对 Cachc 并不会陌生&#…...
操作系统的运行环境
1.处理器的运行模式 目态 也叫用户态, 执行非特权指令:不能直接访问系统中的软硬件资源,仅仅可以访问用户地址空间。 用户应用程序运行在目态。 管态 也叫内核态, 可执行除访管指令外的任意指令,包括特权指令&…...

如何在 Selenium 中获取网络调用请求?
引言 捕获网络请求对于理解网站的工作方式以及传输的数据至关重要。Selenium 作为一种 Web 自动化工具,可以用于捕获网络请求。本文将讨论如何使用 Selenium 在 Java 中捕获网络请求并从网站检索数据。 我们可以使用浏览器开发者工具轻松捕获网络请求或日志。大多数现代 Web…...

IP学习——oneday
1.什么是网络?为什么需要网络? 空间,时间;传统的邮件传输要考虑到距离,网络解决了空间距离(太远)、解决了时间问题(旧音乐等) 云:面向客户的虚拟化服务 运营商公司主营…...
2024 年高教社杯全国大学生数学建模竞赛 C 题 农作物的种植策略(详细思路+matlab代码+python代码+论文范例)
持续更新中,2024年数学建模比赛思路代码论文都会发布到专栏内,只需订阅一次! 完整论文+代码+数据结果链接在文末! 一、第一问 问题描述:假定各种农作物未来的预期销售量、种植成本、亩产量和销售价格相对于 2023 年保持稳定,每季种植的农作物在当季销售。如果某种作物每…...

软件工程知识点总结(1):软件工程概述
1 什么是软件? 定义:计算机系统中的程序及其文档。 ——程序是计算机任务的处理对象和处理规模的描述; ——文档是为了便于了解程序所需要的阐明性资料。 2 软件的特点? 软件是无形的,不可见的逻辑实体 ——它的正确与…...

热烈庆祝中国科学技术大学建校六六周年
卡西莫多的诗文集2022-2024.9月6-校庆国庆专版 欢迎分享 通过网盘分享的文件:卡西莫多的诗文集2022-2024.9月6-A5-校庆国庆专版.pdf 链接: 百度网盘 请输入提取码 提取码: umpm...
iptables持久化命令:netfilter-persistent save
在Linux上,使用netfilter-persistent命令可以保存iptables防火墙规则,确保它们在系统重启后仍然有效。以下是如何使用netfilter-persistent来保存iptables规则的步骤: 打开终端:首先,你需要打开Linux系统的终端。保存…...

elementUI table 给表头添加气泡显示(鼠标悬浮显示注释)
elementUI table 给表头添加气泡显示(鼠标悬浮显示注释) 前言:文档显示:(使用插槽,我看看到底是怎么个事儿)文档代码:修改后的效果:页面效果: 前言: 公司出现…...

Web3社交新经济,与 SOEX 实现无缝交易的高级安全性
出于充分的理由,安全性是交易中至关重要的考虑因素。每个人都应该确保自己的资金在交易时是安全的。由于 SOEX 充当您与交易所的最佳连接,因此必须强调的是,该系统不会引发任何安全问题。 &a…...

Python和MATLAB(Java)及Arduino和Raspberry Pi(树莓派)点扩展函数导图
🎯要点 反卷积显微镜图像算法微珠图像获取显微镜分辨率基于像素、小形状、高斯混合等全视野建模基于探测器像素经验建模荧光成像算法模型傅里叶方法计算矢量点扩展函数模型天文空间成像重建二维高斯拟合天体图像伽马射线能量和视场中心偏移角标量矢量模型盲解卷积和…...
使用isolation: isolate声明隔离混合模式
在CSS中,isolation 属性与混合模式(如 mix-blend-mode 和 background-blend-mode)并不直接相关,但它确实可以影响元素如何与其他元素进行渲染,尤其是在涉及到堆叠上下文(stacking contexts)和复…...

93. UE5 GAS RPG 应用负面效果表现
在上一篇文章里,我们实现了添加负面效果GE,并且在添加GE时,也会给角色应用一个负面效果标签作为标识。在这一篇里,我们将通过负面效果标签标识,应用角色身上展现对应的负面效果的表现。 我们将在这篇文章里添加一个自定…...
TCP 和 UDP 区别
UDP UDP(用户数据报协议,User Datagram Protocol)是一种无连接的网络传输协议,提供了简单的消息传送服务。UDP位于传输层,允许应用程序向其他主机发送封装在IP数据报中的消息,而无需先建立连接。由于UDP不…...
免费2024柜台租赁经营合同范本模板下载分享
今天看到这个合同范本都拿来卖钱,我直接分享出来2024年最新的范本模板随便下,免费的 柜台租赁经营合同GF—2013—0603.docx: https://url51.ctfile.com/f/20096151-1353625109-4285d2?p1605 (访问密码: 1605) 柜台租赁经营合同GF—2013—0603.pdf: https://url51.ctfile.com/…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...
椭圆曲线密码学(ECC)
一、ECC算法概述 椭圆曲线密码学(Elliptic Curve Cryptography)是基于椭圆曲线数学理论的公钥密码系统,由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA,ECC在相同安全强度下密钥更短(256位ECC ≈ 3072位RSA…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

srs linux
下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935,SRS管理页面端口是8080,可…...
TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案
一、TRS收益互换的本质与业务逻辑 (一)概念解析 TRS(Total Return Swap)收益互换是一种金融衍生工具,指交易双方约定在未来一定期限内,基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...
【HTML-16】深入理解HTML中的块元素与行内元素
HTML元素根据其显示特性可以分为两大类:块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...

第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词
Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵,其中每行,每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid,其中有多少个 3 3 的 “幻方” 子矩阵&am…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制
在数字化浪潮席卷全球的今天,数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具,在大规模数据获取中发挥着关键作用。然而,传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时,常出现数据质…...