APS开源源码解读: 排程工具 optaplanner

news2024/9/22 1:07:24

抽象层次非常好,广义优化工具。用于排产没有复杂的落地示例

  • 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 {

    @Override
    public Constraint[] defineConstraints(ConstraintFactory constraintFactory) {
        return new Constraint[] {
            // Ensure operations follow the sequence within each job
            constraintFactory.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 respected
            constraintFactory.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);
    }

    @ProblemFactCollectionProperty
    public List<Project> getProjectList() {
        return projectList;
    }

    public void setProjectList(List<Project> projectList) {
        this.projectList = projectList;
    }

    @ProblemFactCollectionProperty
    public List<Job> getJobList() {
        return jobList;
    }

    public void setJobList(List<Job> jobList) {
        this.jobList = jobList;
    }

    @ProblemFactCollectionProperty
    public List<ExecutionMode> getExecutionModeList() {
        return executionModeList;
    }

    public void setExecutionModeList(List<ExecutionMode> executionModeList) {
        this.executionModeList = executionModeList;
    }

    @ProblemFactCollectionProperty
    public List<Resource> getResourceList() {
        return resourceList;
    }

    public void setResourceList(List<Resource> resourceList) {
        this.resourceList = resourceList;
    }

    @ProblemFactCollectionProperty
    public List<ResourceRequirement> getResourceRequirementList() {
        return resourceRequirementList;
    }

    public void setResourceRequirementList(List<ResourceRequirement> resourceRequirementList) {
        this.resourceRequirementList = resourceRequirementList;
    }

    @PlanningEntityCollectionProperty
    public List<Allocation> getAllocationList() {
        return allocationList;
    }

    public void setAllocationList(List<Allocation> allocationList) {
        this.allocationList = allocationList;
    }

    @PlanningScore
    public 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 started
            solver.addPhaseLifecycleListener(new UnlockLockPhaseLifecycleListener());
            // add a phase lifecycle listener that consumes the first initialized solution
            solver.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的主流程

@Override
    public 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 global
        LongTaskTimer 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 metrics
                solvingStarted(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进行新的组合。也许这就是叫做组合优化的原因?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2111453.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

亿佰特-NT1/NT1-B串口转RJ45以太网模块

亿佰特-NT1/NT1-B串口转RJ45以太网模块 模块概要连接方式配置模块亿佰特网络配置助手配置模块网页版配置模块 串口以太网双向数据收发AT模式配置模块 模块概要 亿佰特官网 https://www.ebyte.com/ 模块概要&#xff1a; 接口方式&#xff1a;TTL串口RJ45工作电压&#xff1a…

黑神话悟空红孩儿怎么打 妖王红孩儿攻略

​在《黑神话悟空》中&#xff0c;红孩儿作为IP里的经典BOSS&#xff0c;是一位相当顽皮的BOSS。接下来小编给大家带来了黑神话悟空红孩儿怎么打的攻略&#xff0c;一起来看看吧。 一、BOSS位置 随着主线流程的推进自然解锁。 二、BOSS打法 1、优先强化【身外身法】与【变身…

算法训练营——day3长度最小子数组

1 长度最小子数组-力扣209&#xff08;中等&#xff09; 1.1 题目&#xff1a; 长度最小的子数组 给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其总和大于等于 target 的长度最小的 子数组 [numsl, numsl1, ..., numsr-1, numsr] &#xff0c;并返…

springboot+vue+mybatis计算机毕业设计积分商城管理系统+PPT+论文+讲解+售后

近年来互联网络的迅猛发展和电子终端设备的普及&#xff0c;赋予了各行业充足的发展空间。积分商城管理系统相比于传统信息技术&#xff0c;时效性是它最大的特色&#xff0c;已经在电子娱乐、经济等中发挥着举足轻重的作用。2019年疫情的爆发&#xff0c;更是短时间内迅速扩大…

Spring之Bean的生命周期 2024-9-6 19:47

目录 什么是Bean的生命周期为什么要知道Bean的生命周期Bean的生命周期之5步Bean生命周期之7步Bean生命周期之10步 声明&#xff1a;本章博客内容采自老杜2022spring6 语雀文档 什么是Bean的生命周期 Spring其实就是一个管理Bean对象的工厂。它负责对象的创建&#xff0c;对象的…

据说这是一个能让AI自动升级的超级提示词,我试一试效果咋样

本文背景 前阵子我在某个地方看到个超离谱的 Prompt&#xff0c;把我和几百万网友都给整懵了。 说真的&#xff0c;好久没见过这么抽象的 Prompt 了。 这玩意儿在那地方火得不行&#xff0c;才发没两天就有一百多万浏览量。 还有个只有两个 Markdown 文件的 GitHub 项目&#x…

python中 if __name__ == “__main__“的代码没被执行

运行pytest 和unittest时&#xff0c;if name “main”: 下的代码没有被执行&#xff0c;发现__name__等于模块名 一、“name” 的作用 1、 __name__是python的一个内置类属性&#xff0c;它天生就存在于一个 python 程序中。 2、直接运行python程序时&#xff0c;__name__的…

Elastic Stack--ES集群加密及Kibana的RBAC实战

前言&#xff1a;本博客仅作记录学习使用&#xff0c;部分图片出自网络&#xff0c;如有侵犯您的权益&#xff0c;请联系删除 学习B站博主教程笔记&#xff1a; 最新版适合自学的ElasticStack全套视频&#xff08;Elk零基础入门到精通教程&#xff09;Linux运维必备—Elastic…

基于Java+SpringBoot+Vue+MySQL的社区医疗服务管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 基于SpringBootVue的可视化社区医疗服务管理系统【附源码文…

二级菜单的两种思路(完成部分)

第一种 <el-form ref"formRef" :model"form" :rules"rules" label-width"120px"><el-form-item label"类型" prop"type"><el-select v-model"form.type" placeholder"请选择类型&q…

【Python · Pytorch】配置cuda环境 cuDNN库

【Python Pytorch】配置cuda环境 & cuDNN库 1. 查找对应版本1.1 查看Pytorch GPU目前支持版本1.1 查看Nvidia驱动版本1.2 查看支持cuda版本1.3 查看支持cuDNN版本1.3.1 cuDNN 9.0.0及以上版本1.3.2 cuDNN 9.0.0以下版本 1.4 安装版本确定 2. 安装cuda环境2.1 cuda简介2.1.…

时装爱好者的网页购物天堂:Spring Boot技术探索

第2章相关技术 2.1 B/S架构 B/S结构的特点也非常多&#xff0c;例如在很多浏览器中都可以做出信号请求。并且可以适当的减轻用户的工作量&#xff0c;通过对客户端安装或者是配置少量的运行软件就能够逐步减少用户的工作量&#xff0c;这些功能的操作主要是由服务器来进行控制的…

基于yolov8的西红柿检测系统python源码+onnx模型+评估指标曲线+精美GUI界面

【算法介绍】 基于YOLOv8的西红柿检测系统是一种利用深度学习技术的创新应用&#xff0c;旨在通过自动化和智能化手段提高西红柿成熟度检测的准确性和效率。该系统采用YOLOv8算法&#xff0c;该算法是深度学习领域中的先进目标检测模型&#xff0c;具备实时检测和多目标识别的…

基于vue框架的车辆理赔系统5vzcd(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;用户,员工,保险信息,保单信息,申请理赔,事故调查,赔偿金发放 开题报告内容 基于Vue框架的车辆理赔系统开题报告 一、项目背景与意义 随着汽车保有量的持续增长&#xff0c;车辆事故频发&#xff0c;车辆保险理赔成为保险公司和车主共…

机器学习和物联网驱动技术在加工过程中监测工具磨损:一项全面的综述

这篇论文的标题是《Machine-Learning and Internet-of-Things-Driven Techniques for Monitoring Tool Wear in Machining Process: A Comprehensive Review》&#xff0c;由 Sudhan Kasiviswanathan、Sakthivel Gnanasekaran、Mohanraj Thangamuthu 和 Jegadeeshwaran Rakkiya…

最好用的Python IDE,PyCharm保姆级安装教程

简介 由于Python语法简单容易入门&#xff0c;并且Python在办公自动化等领域的功能非常强大&#xff0c;所以现在越来越多非IT行业的人也开始学起了Python&#xff0c;要学习和使用一门编程语言&#xff0c;一个好用的IDE是必不可少的&#xff0c;而对于Python来说&#xff0c…

SpringMVC基于注解使用

01-拦截器介绍 首先在pom.xml里面加入springmvc的依赖 创建拦截类 在spring-mvc.xml配置拦截器配置 创建控制类测试 拦截器中处理方法之前的方法介绍 拦截器中处理方法之后&#xff0c;渲染之前的方法介绍 拦截器中处理方法之后&#xff0c;渲染之后的方法介绍 判断拦截器和过…

SAM2(Segment Anything Model 2)新一代分割一切大模型实战总结

Segment Anything Model 2&#xff08;SAM 2&#xff09;作为Meta公司发布的Segment Anything Model&#xff08;SAM&#xff09;的升级版本&#xff0c;在图像和视频分割领域展现出了显著的优点和特性。 论文连接&#xff1a;https://arxiv.org/pdf/2408.00714 Demo: https…

9月6号作业

1&#xff1a;.h文件 #ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QWidget> #include<QIcon> //图标类 #include<QLabel> //标签类 #include<QMovie> //动图类 #include<QLineEdit> //行编辑器类 …

黑马点评8——好友关注-SortedSet

文章目录 关注和取关共同关注Feed流实现方案分析推送到粉丝收件箱Feed流基于推模式实现关注推送功能 滚动分页查询收件箱的思路实现滚动分页查询 关注和取关 所以关注和取关就是简单的插入和删除数据库。 Overridepublic Result isFollow(Long followUserId) {// 1. 获取登录用…