2018年10月11日星期四

Optaplanner规划引擎的工作原理及简单示例(1)

  在之前的文章中,老猿已介绍过APS及规划的相关内容,也对Optaplanner相关的概念和一些使用示例进行过介绍,接下来的文章中,我会自己做一个规划小程序 - 一个关于把任务分配到不同的机台上进行作来的小程序,并在这个小程序的基础上对Optaplanner中更多的概念,功能,及使用方法进行讲解。但在此之前,我需要先讲解一下Optaplanner在运行规则运算的原理。所以,本文是讲述一些关于寻找最优解的过程中的原理性的内容,作为后续通过示例深入讲解的基础。但这些原理知识不会涉及过分深奥的数学算法,毕竟我们的目标不是写一个新的规划引擎出来,只是理解一些概念,用于理解Optaplanner是依据什么找出一个相对优解的。好让在接下来的一系列文章中,可以快速无障碍地理解我所讲解的更细化的Optaplanner功能。
  好了,言归正传,本文主要是讲述Optaplanner是如何在用户定义的规则限制条件中,基于约束的限制,对被规划对象进行排列组合,再对比各个组合(称作解,或方案),并找出相对最优的解出来。在这个寻优过程中,Optaplanner会使用到一些相关算法,例如启发式算法(例如First Fit)和延迟接受法(例如禁忌搜索),从而提高寻找相对最优解的效率和防止嵌入局部最优解,从而可以在固定的时间内,找到尽可能优的方案。
  在理解Optapalnner是如何实现之前,我们先复习并展开一下上一篇提到的概念 - 约束。

约束(Constraint):

  也就是对事物的一种限制,规定事物的发展应该遵循什么规则,具体到Optaplanner里,就是用于表达出什么是对的,什么是错的,什么情况是最优,什么情况次优,什么情况较差。从而让引擎得到各个解的对比依据。
  在Optapalnner中的约束可以分为硬约束软约束两种,其实还有更多的约束类型 ,例如中间约束,甚至是无限层级的约束,但总结起来,其作用也就是把约束划分为不同层级,从而区分出不同的优等级而已,如果有软件开发经验的同学,可以理解不同层级的约束,分别是SQL语句里Order By子句后面的字段次序。在进行记录排序时,前面的字段排列的优先级,是从性质上优先于后面的字段的,大家理解了Order By子句,也就理解了不同层级约束的问题了。接下来我们以最简单的软硬约束,来分析一下约束的作用。

硬约束:

  硬约束是用来规定什么情况是对的,什么情况是错的;什么组合是好的,什么组合是不好的......也就是它通常是用来对所得的解进行一些定性的状态定义。例如一个计划是否可行,例如会不会同一个机台同一个时间分配了两个不同的任务(假设每个机台同时只能做同一个任务)。一个员工所排班次是否正确(例如一个员工是否被安排了三个连续的班次)。若出现上种情况,即表示违反了硬约束,这种方案称作不可行方案。以后的文章里,会提到Optaplanner里有一个明确的概念 - Feasable Solution(可行方案,或称可行解),就是表示这个方案是完全符合硬约束的。

软约束:

  软约束规定什么情况最优,什么情况次优,什么情况是差的;它是用来定义方案优劣的定量状态。例如:一个计划的成本是否足够低;一个排班表到底有多大程度上的合理性,例如一个人正常情况下是需要5天工作制的,但如果遇到特殊情况,也可以连续工作6天,但这种情况是特殊的,需要额外付加班费(成本上升)最好不要出现这种情况。那么在编制这个排班表的时候,如果有一个方案是需要有人员连续工作6天,但如果找到另一个方案,可以令所有人均不需要连续工作6天,那么,后面这个方案就比那些有人需要连续工作6天的方案更好了。体现在软约束上,就是后面的排产表,其软约束上会比前一个排班表更好,违反的软约束更少。
  上述讲述的是两种常见约束,那么这些约束在Optaplanner里是如何生效的呢?那说需要有一种评分机制了,也是我们在使用Optaplanner里,比较难准确把握的一个内容之一。
评分机制:评分是用分数来评价事物特性的一种方法。但如果我们细心观察总结一下,会发现评分是可以通过两种方向来评价的;分别是正评分(奖励性评分)和负评分(惩罚性评分)。
正评分:通过获得分数的多少,来体现事物的优劣。例如我们在学校考试过程中,成绩是通过一种正分数来体现的,即做对一题奖励相应的分数,分数越高成绩越好;完美状态是获得满分。
负评分:通过扣除分数的多少,来体现事物的优劣。例如我们的驾驶证记分制,每违章一次就扣除相应的分数,很明显这种评分体系中,分数越低越好,也就是扣得越少越好;完美状态是扣0分。
  在对实际问题进行约束规划时,是一种封闭性约束,也就是约定事物往指定的一个方向发现,使用负评分的方式,很显然更合理。也就是一个方案有哪些不好的,我们通过对它评定一些惩罚分数标准,告诉引擎这种组合出现了一些不太好的情况。如此类推,每找到一个更佳、扣分更少的方案,就离完美就更近一步。无论是使用正方向评分还是反方向评分(或称负方向评分),在Optaplanner里都是可以实现的,只不过按我们日常的逻辑,在定义方案时,通常我们只会根据业务定义出一些规则,方案是需要守这些规则,当一个方案出现有违反规则时,就作出相应的惩罚性扣分;这种方法比当出现好的情况就加分更合理。因为我们的现实世界里,"好"是可能无限好的,当问题足够复杂,数据量足够大,即问题规模够大时,描述一个方案如何个好法,其实很难是一个定数。比描述一个方案如何个差法更难,因为前者可以是无限的,而后者就只需要我们定义好什么是差的标准,一但问题范围确定,它的最差情况(也就是最差的扣分情况)就有一个字数了。所以,在Optaplanner的世界里,常见的做法是,定义一些约束,并设定相应的惩罚分数标准(即将约束量化),用来描述这个方案的制约因素,当这个约束被打破时,就作出惩罚性记分,那么到最后,扣分越少的方案就越好。这就是Optaplanner实现寻优的最基本原理,但其实现是非常复杂的,会将问题划分为很多种类,将寻优的过程划分为多个阶段,每个阶段利用不同种类的算法来提高找到更优方案的效率,每个阶段有很多个步骤,每个步骤又有多个移动(没错,Optaplanner里就有Step与Move的概念,以后会详解);在以后的深入文章中,我会详细把这个过程分析出来。
  上面描述了硬约束、软约束和评分机制。那么如何将这两种约束与这种评分机制关联起来,令评分机制可以实现软、硬约束呢?大家可能已想到,在Optaplanner给出了软分数,硬分数的概念。在评分机制中,当出现一个方案违反了某个硬约束时,就给这个方案扣除这个约束相应的分数;同样地,当该方案违反了一种软约束时,就对该方案扣除该软约束相应的分数。这两个分数是分开处理的。因为通过它们对应的约束类别就知道,它们分别代表的性质不一样,硬分数对应的硬约束,代表的是一种定性评价;即描述方案好不好,行不行,可不可取等,一旦被记扣硬分数,那就表示这个方案的性质就变了,由可行方案变成不可行方案。理想的方案是一个硬分都不能扣的,一旦扣了就是不可行方案了。有人问,那么定义硬分数的分值有什么用?直接给一个标识出来,将方案的可用性定义为True or False,分别代表是否有硬约束被违反不就行了吗,多简单呀,因为一旦为False就是不可用了,再去讨论它扣了多少分,又有何意义呢?硬约束、硬分数不就是为了给方案定性而设立的吗?何必还要记录它的扣分量,多此一举呢?
  如果这样想,就是一种不全面的想法了。因为大家需要明白,现实世界往往是很大程度是不完美的,但而对不完美,我们是放弃这个世界,还是在不完美中进行坚持,对这个不完美的世界,朝完美的方向进行改造呢?上面的说法就比较抽象比较虚了,举个大家容易理解的例子。例如:刑法是用来惩罚犯罪的,在正常的法治社会中,犯罪对于一个人说,就相当于违反了硬约束(刑事处罚记录是终身跟随的)。也就是对于一个人来说,一生中是否触犯过刑法,是一个定性的问题。那么既然是定性问题,我们在设立刑法的时候,其对应的惩罚是不是只有一种就足够了呢?例如凡是触犯刑法,全部判死刑,那不就简单得多啦?事实上人类社会是不可能这样的,因为就算是触犯了刑法(这个已经是定性问题),但罪行也有轻重之分的、对应了刑法的不同条款,有些罪名经过对罪犯的惩戒,是可以再给他一次机会的,也说就是说触犯的刑法,是有轻重之分的,但性质不会变,他在国家司法机关的档案里,永远留有普被刑事处理的记录。所以,这可以称该种情况为定性范围内的定量问题。就是一个人做错了就是错了,其性质已经定了,但犯的错误有多大,还得是一个定量问题。因此,硬约束对应的扣除硬的分数有多有少就不难理解了。就是我们的方案如果出现了违反硬约束、被扣除了硬分数的,它在Optaplanner上就是一个不可行方案了。但是在众多的不可行方案里,其实还要区分哪个是更不可行,哪些其实只是违反了一点点,还是“稍为可行的”。回到我们的实际排程问题中,有可能客观条件限制,我们所有排出来的方案(例如生产计划、排班表、车辆调试线路图)都是不可行的,例如:我们排生产计划的时候,将交货期延误作为一种硬约束,但是现实的生产活动中,确确实实有可能无论你怎么排,因为产能、资源限制等因素,你是不可能找到一个完完全全符合交期的生产计划的,那么这个时间我们就需要找出一个违反得最小的计划出来,作为可行计划,视情况进行相应的修改并执行了。也就是说两害相遇取其轻。
  对于硬约束,除了上述讲到,当出现有可能确实需要使用不可行方案作为执行计划的情况外,在Optaplanner进行规则的过程中,其实也起到非常大作用的。先不说optaplanner引来来排程;如果让你来排,对于各种硬约束,全都不给出一个分数,而是给一个定性的标识,就是一旦出现违反了,就报一个违反硬约束的消息出来,你会怎么样?你肯定会抱怨提示的信息太简陋了,只有一个标识,最多只是知道哪里违反了,再也没有更详细的信息供你参考了。那你接下来的排产活动,其实就是一个组合一个组合逐一地去碰彩了。因为各个方案之间是否有关联,你是无法得知的,所以你根本找不到什么好的办法去将各种情况下的方案进行归类、比较进行往指定的一个方向收敛。但如果在一个硬约束被违反时,会出现一些明确的信息,是哪个硬约束被违反了。违反和程度是多少,扣了多少分,是因为哪个被规则的对象,放在哪里,或与哪个对象相邻从而导致的硬约束被违反。这样就形成了一个很明确指导方向,对于人而言,通过归纳统计就知道某些情况肯定会出现,或极大可能会出现违反硬约束的情况,那我们就可以在排列新方案时,尽力去避免这种情况了;也就是有了参考方向 。对于Optaplanner引擎来说也是同理,尽管它不像人这么聪明(但从最近的消息来看,Optapalnner团队已经着手思考人工智能引入到引擎中,从而实现如上述人类一样对这类问题进行归纳思考),但也能够作为其寻找更佳方案的过程中的一些很重要的参考,从而为寻优算法所用,进而提高寻优效率。例如遗传算法。
  软分数对应的软约束,代表的是一种定量评价;即描述方案有多好、有多差,成本有多高、有多低。它是一种优化约束,即在定义它的时候,就已经知道它必然是被违反的(也有可能完全不违反,那当然是好的,但如果是这样的话,就脱离了软约束的初衷了)。所以,软件约束、软件分数的扣分值用途相对来说就容易理解得多了。
   综上所述,Optaplanner就是通过一种体现为分数的约束机制,进行寻找最优组合。当一个排产问题中,设定的软硬两种约束时,它会优先满足硬约束的要求,再满足软约束的要求,也就是说,软约束被扣为1万分,也不及硬约束被扣了1分重要,联系上面的SQL语句中的Order By子句的例子。
  Optaplanner其利用途径有以下两点:
1. 用分数来确定,一个方案是否可行,是优是劣;
2. 在决定每一步的时候,参考上一点的扣分情况,来确定下一次生成方法时,应该考虑哪些因素(想想遗传算法).
  这一篇我们先讲解一下原理,打一下基础,下一篇将用一个任务与机台的例子来说明一下这些原理在Optaplanner中是如何体现的。

如需了解更多关于Optaplanner的应用,请发电邮致:kentbill@gmail.com

Optaplanner - 从探究示例中的hello world,初步认识规划引擎的运行步骤。

  上一篇我们成功以把Opotaplanner规划引擎下载回来,并把它的示例运行起来,简单解析了一下它的Cloud balance示例。这一篇我们这些示例的源代码导入到Eclipse中,看看它在后台是怎么运行的。 

一、推荐使用Maven

  在上一篇,我们已经从Optaplanner的官网下载了它的压缩包,它里面几乎包含了Optaplanner的所有东西,基本上有了这个包,我们离线都可以做一个应用Optaplanner规划引擎程序出来了。但是如果我们直接使用里面的核心包来做Java Project是很不明智的;因为:1.这些包有很多在特殊的场景才会用到,并不是每个项目都会用到,引入太多浪费空间。2. 如果Optaplanner引擎有版本更新了,你又想使用的话,那只能重新下载、配置。所以,现在Optaplanner官网通常都是推荐通过Maven的方式来建议项目。关于Maven的用法,大家可以去看一下相关的文章,其实也不复杂的,就是有一些公共的库帮你管理好了这些你用到的包,你只需要在你的项目里配置好你需要使用的包,剩下的就是Maven自己把需要的包括下载到你本地,并自动匹配版本了,当有Optaplanner有版本更新的时候,你所使用的包也可以更新为最新版本,而无需人工下载。所以,在这里,我们都是以Maven项目的方式来建立Optaplanner的示例源码,在以后的Optaplanner相关的演示中(稍后会有一篇文章会编写一个最基本的Hello world程序,也会通过Maven项目实现). 

二、Optaplanner的Hello word

  这一篇里面我们就从Optaplanner所有示例程序中的“Hello word”开始,因为Optaplanner面对的是规则问题,所以并没办法像学习一门新语言的入门教程一下,以打印一个Hello world信息出来作为第一个程序,毕竟它是个规划引擎,是用来对一系列对像进行规划的。所以我们就从它的说明文档里最简单的一个示例Cloud Banacing开始。关于这个示例的说明,在上一篇文章里,我们把它的所有示例程序跑起来的时候,重点讲解过它,这里就概述一下,让大家对这个示例有个大概的了解。大家可以打开《OptaPlanner - 把example运行起来(运行并浅析Cloud balancing)》这篇文里看它在程序里的具体呈现方式。简而言之,Cloud banacing就是模拟在云端有很多任务,需要根据CPU, 内存及带宽的要求,分配到不同的计算机上去执行,在满足了每个任务的基础上,还需要实现最省计算机资源的原则。这就是典型的资源规则问题了,大家可以扩展到供应链各个环节中的场景,例如APS(Advanced Planning and Scheduling, 高级计划与排程)中,如何将任务按一定的要求分配到指定的车间、产线甚至机台、工位上,并实现成本最低,或效率最高,或资源平衡等要求。 

三、导入示例源码并试运行

  接下来我们就一步步把源代码都导进Eclipse里慢慢分析一下,如果要实现一个规则程序,至少需要用到Optaplanner哪里功能,需要建议哪些对象和规则。在一上篇里,我们已经下载了Optaplanner的发布包了,它里面包含了Optaplanner引擎的所有东西,包括可以直接使用的字节码程序,源代码,用户手册(包括所有API的Java Doc),所有示例程序和所有示例程序的源代码.这里,我们就以Mavin Project为基础,把这个发布包里的示例程序的源代码导进来,然后再从这些源代码里去看看它的基本运行步骤和所需的对象和规则。

1. 创建workspace

  创建一个文件夹作为这些试验的workspace.接下来我们的所有示例源码都放在这个文件中进行导入、运行、调度并修改。
                                

2. 解压示例源码

  把示例源代码解压到workspace文件夹中,以便下一步把它作为maven项目导入,注意,需要将optaplanner-distribution-7.6.0.Final\examples\sources整个文件夹解压到workspace文件夹中去,因为这个文件夹里包含了示例源代码,用示运行示例用的数据文件,还有一些资源文件。source文件夹下面有个pom,xml文件,表示它是一个maven项目。
              

3. 导入示例源代码

  在eclipse中,选择菜单File -> Import, 在弹出的Import对话框中,选择"Existing Maven Projects",(可以在Select an import wizard下面的文档框中输入maven来快速定位你们导入的项目,输入maven,就会过滤出maven相关的项目),选择“Existing Maven Projects”,点击"Next", 在"Import Maven Projects"对话框中,通过"Browser"按钮定位到刚才解压的sources文件夹去,Root Directory即会显示该位置,并在下面的Projects列表中,显示该文件夹下的pom.xml文件,选中该pom.xml文件,并选中“add projects(s) to working set”,点击Finish。eclipse即会把程序导入,并在sources文件夹(即与pom.xml文件同一个地方)中生成.project文件。即表示项目导入成功。
                             
                                                                                                                                                                                                    

4. 更新依赖包。

  项目导入后,通常eclipse会自己检测项目中依赖的包是否都存在,若不存在会自己下载。如果eclipse没有自动下载(通常几秒钟后会检查到并下载),就点选一下菜单File -> Refresh 刷新一下。你们的电脑如果是第一次导入Optaplanner的项目,将会有一个比较长的下载依赖包过程,视下载速率而定。通常会显示更新进度。完成依赖包下载后,eclipse还会原始的项目信息,为源创建好各种包。即恢复原来的包信息.
        
                              
      

  5.试运行

  我们先试一下,看看我们的导入的源代码是否都已经正确,所需的依赖包是否都已经完成下载并更新。找到整个示例的入口类 - OptaPlannerExamplesApp.java. 右击它,在弹出菜单中,选择Run As -> 2 Java Application. 稍等片刻,程序就会跑起来了,效果跟上一篇我们直接通过批处理文件运行起来的效果一样,那么就表示我们已经成功把Optaplanner的所有示例成功导进eclipse了。
                                

四、分析Hello world源码

  下面,我们着重分析一下它的Cloud Balancing示例,它的,在包org.optaplanner.examples.cloudbalancing.app下,有一个CloudBalancingHelloWorld.java类。这个就是Optaplanner最基本的入门示例了。我们直接看它的代码,可以看到要使用Optaplanner需要最基本的三个步骤,分别是创建Solver对象, 创建被规划的对象,启动solve()方法,solver方法的返回值就是一个已经规划好的方案了.代码如下: 

 public static void main(String[] args) {
        // Build the Solver
        SolverFactory<CloudBalance> solverFactory = SolverFactory.createFromXmlResource(
                "org/optaplanner/examples/cloudbalancing/solver/cloudBalancingSolverConfig.xml");
        Solver<CloudBalance> solver = solverFactory.buildSolver();

        // Load a problem with 400 computers and 1200 processes
        CloudBalance unsolvedCloudBalance = new CloudBalancingGenerator().createCloudBalance(400, 1200);

        // Solve the problem
        CloudBalance solvedCloudBalance = solver.solve(unsolvedCloudBalance);

        // Display the result
        System.out.println("\nSolved cloudBalance with 400 computers and 1200 processes:\n"
                + toDisplayString(solvedCloudBalance));
    }

  第一步:生成Solver对象,代码的第3行创建一个SolverFactory<CloudBanace>对象,其实也就是它使用了工厂模式,并使用了泛型了。其中CloudBalance是一个由我们定义的Planning Problem对象,被规则的对象都会作为Planning Problem对象的属性列表而传进引擎中,它是Opaplanner的几大基本对象之一,在这个示例中,第8得就是创建了一个Planning Problem对象,大家可以导航进去看到,创建它的时候,是否为它的两个列表(Computer和Process列表)初始化了一些对象。在关于这些基本对象的文章中,将会有详细的说明.在这一步主要是创建一个Solver对象出来,这个对象是指Optaplanner引擎将会使用什么算法,以什么参数,引用哪些规则对Planning Problem进行规划运算的,在规划运算过程中,基于什么原则进行退出等等设置。而这些设置全部可以写进一个XML文件中,也就是上面代码中的cloudBalancingSolverConfig.xml了。
  第二步:创建将要被规划的对象,就是上面提到的Planning Problem对象了,在代码中的第8行实现。
第三步:通过Solver对象的solve方法,对上面创建的Planning Problem进行规划。这个过程有可能需要一个很长的时间,也有可能是实时规划的,也可能7 * 24小时都在包(实时规划)。而对于前一种(非实进规划),当规划运算完成后(通常在cloudBalancingSolverConfig.xml文件中会设置规划的完成条件),会返回一个已经完成了规划的Planning Problem对象,读取这个对象里的规划实体列表(例如本例中的规划实体就是Process对象),就得到规划好的方案了。
  以下是这个示例在规划过程中的Log输出,它清楚以显示了每一个规划步骤,引擎对规划实体进行了什么操作。

20:00:47.447 [main        ] DEBUG     LS step (20378), time spent (14822), score (0hard/-519420soft),     best score (0hard/-518110soft), accepted/selected move count (1/2), picked move (CloudProcess-21 {CloudComputer-182 -> CloudComputer-74}).
20:00:47.447 [main        ] DEBUG     LS step (20379), time spent (14822), score (0hard/-519420soft),     best score (0hard/-518110soft), accepted/selected move count (1/1), picked move (CloudProcess-1191 {CloudComputer-164} <-> CloudProcess-674 {CloudComputer-375}).
20:00:47.447 [main        ] DEBUG     LS step (20380), time spent (14822), score (0hard/-519420soft),     best score (0hard/-518110soft), accepted/selected move count (1/3), picked move (CloudProcess-696 {CloudComputer-360} <-> CloudProcess-945 {CloudComputer-286}).
20:00:47.447 [main        ] DEBUG     LS step (20381), time spent (14822), score (0hard/-519420soft),     best score (0hard/-518110soft), accepted/selected move count (1/1), picked move (CloudProcess-490 {CloudComputer-298} <-> CloudProcess-1196 {CloudComputer-258}).
20:00:47.447 [main        ] DEBUG     LS step (20382), time spent (14822), score (0hard/-519420soft),     best score (0hard/-518110soft), accepted/selected move count (1/10), picked move (CloudProcess-204 {CloudComputer-375 -> CloudComputer-159}).
20:00:47.448 [main        ] DEBUG     LS step (20383), time spent (14823), score (0hard/-519420soft),     best score (0hard/-518110soft), accepted/selected move count (1/1), picked move (CloudProcess-465 {CloudComputer-136} <-> CloudProcess-621 {CloudComputer-0}).
20:00:47.448 [main        ] DEBUG     LS step (20384), time spent (14823), score (0hard/-519420soft),     best score (0hard/-518110soft), accepted/selected move count (1/1), picked move (CloudProcess-860 {CloudComputer-393} <-> CloudProcess-29 {CloudComputer-216}).
20:00:47.449 [main        ] DEBUG     LS step (20385), time spent (14824), score (0hard/-519420soft),     best score (0hard/-518110soft), accepted/selected move count (1/5), picked move (CloudProcess-57 {CloudComputer-323} <-> CloudProcess-768 {CloudComputer-36}).
20:00:47.449 [main        ] DEBUG     LS step (20386), time spent (14824), score (0hard/-519420soft),     best score (0hard/-518110soft), accepted/selected move count (1/3), picked move (CloudProcess-934 {CloudComputer-324 -> CloudComputer-246}).
20:00:47.449 [main        ] DEBUG     LS step (20387), time spent (14824), score (0hard/-519420soft),     best score (0hard/-518110soft), accepted/selected move count (1/2), picked move (CloudProcess-812 {CloudComputer-198} <-> CloudProcess-1085 {CloudComputer-112}).
20:00:47.449 [main        ] DEBUG     LS step (20388), time spent (14824), score (0hard/-519420soft),     best score (0hard/-518110soft), accepted/selected move count (1/1), picked move (CloudProcess-883 {CloudComputer-41} <-> CloudProcess-1180 {CloudComputer-237}).
20:00:47.450 [main        ] DEBUG     LS step (20389), time spent (14825), score (0hard/-519420soft),     best score (0hard/-518110soft), accepted/selected move count (1/6), picked move (CloudProcess-477 {CloudComputer-376} <-> CloudProcess-713 {CloudComputer-197}).
20:00:47.450 [main        ] DEBUG     LS step (20390), time spent (14825), score (0hard/-519420soft),     best score (0hard/-518110soft), accepted/selected move count (1/3), picked move (CloudProcess-693 {CloudComputer-311 -> CloudComputer-342}).
20:00:47.450 [main        ] DEBUG     LS step (20391), time spent (14825), score (0hard/-519420soft),     best score (0hard/-518110soft), accepted/selected move count (1/3), picked move (CloudProcess-328 {CloudComputer-186} <-> CloudProcess-520 {CloudComputer-59}).
20:00:47.453 [main        ] DEBUG     LS step (20392), time spent (14828), score (0hard/-519420soft),     best score (0hard/-518110soft), accepted/selected move count (1/3), picked move (CloudProcess-203 {CloudComputer-103} <-> CloudProcess-745 {CloudComputer-112}).
复制代码

  至此,我们已把Optaplanner的示例程序全部导入到eclipse并跑起来了,也简单地介绍过一下它的hello world示例,可能大家还是会有些疑问,到底它是怎么执行得的,它做了些什么,要理解这些问题,就真的需要从需求开始,再理解一下Optaplanner的规划模型,最后结合一些示例才能说得清楚了。在接下来的文章中,我将会以一个个自己想出来的简单示全,逐步对上述的问题进行讲述。过程不再一次过写太长的内容了,会在每篇文章里介绍几个相关的概念。好让大家更容易理解,更容易上手。
PS: 其实在导入并试运行过程中,使用7.6.0.Final版本的代码会出现一个异常的,刚好今天发现有7.7.0.Final发布了(好快喔),就下了最新的源码,那个异常消失了。大家可以注意一下,下载7.6.0.Final的示例源码不一定能跑成功喔,
原创不易,如果觉得文章对你有帮助,欢迎点赞、评论。文章有疏漏之处,欢迎批评指正。
如需了解更多关于Optaplanner的应用,请发电邮致:kentbill@gmail.com

OptaPlanner - 把example运行起来(运行并浅析Cloud balancing)

  经过上面篇长篇大论的理论之后,在开始讲解Optaplanner相关基本概念及用法之前,我们先把他们提供的示例运行起来,好先让大家看看它是如何工作的。OptaPlanner的优点不仅仅是提供详细丰富的文档 ,还为各种应用场景提供丰富的示例,它的文档里都是以几个简单经典的例子来说名各种功能特征和深层次概念的,例如Solver, Phase及Move等,以下我们就先把这些示例运行起来,先看看整体的情况,下一往篇我们再把示例的源码导进Eclipse,拿一个简单经典的示例,讲解一下Optaplanner规划引擎工作时需要哪些要素,它是如何工作的。

1.下载:
  首先得把示例下载回来,大家到Optaplanner的官网就可以看到一个绿色的按钮(见下图),点击它就可以下载了。它的版本更新非常快,我们就基于7.6.0Final进行讲解。  
                 
2. 解压:
  下载回来的压缩包“optaplanner-distribution-7.6.0.Final.zip”包含了Optaplanner的源码、各种包(引擎自己的核心包及其依赖包)、说明文件和示例及其源码。其中示例包括两个版本,一个是基础Swing的,也就是Java的Windows程序;另一个是基于Web的,以War包提供,需要自己部署Tomcat等App服务器来运行。我们着重讨论Swing版本的,因为它不需要我们部署App服务器。如果以后大家有需要,我可以另写一篇专门部署Web版本示例的文章详细讲解。打开压缩包,里面的文件夹结构如下图:
            
3. 试运行示例:
  因为压缩包中除了提供源码,还提供了已编译的包,只要在你系统中安装好Java环境,就可以运行起来,先看个究竟了。ps:java要1.8以上。
    3.1: 解压示例文件:
  你会看到一个包文件夹(binaries),一个源码文件夹(sources),一个windows批处理命令文件(runExamples.bat)和一个Linux下运行示例的Shell文件 (runExamples.sh). 因为我是在Windows环境下运行的,所以把binaries和runExamples.bat解压出来放在同一文件夹即可,examples子文件夹中的目录结构如下图。
            
    3.2 运行示例:
  如果windows下使用cmd不太熟悉的话,就按我下面的步骤操作.完成之后就可以看到它示例的真容了。示例程序是基于Swing做的,理论上通过里面的批处理文件就可以运行起来,其实里面就是一些运行jar包的命令,只不过它会有更多的功能,例如检查当前系统的JRE等等。不过中间有点小插曲,我使用7.6.0的示例运行的时候,它报了一个slf4j找不到的异常,应该是一个日志组件缺少了,我要看看它这个版本的更新记录,看是否有相关的提示,否则我得联系一下他们项目组的人才行。后来我用7.5.0Final的示例可以正常运行起来了。
          
            
            
  7.5.0版本提供了18个示例,已经 包含了几乎所有Optaplanner规划引擎具有的特性及应用模式。但其实在他们的Github中提供了更多的示例,有兴趣的同学可以关注一下Github上optaplanner项目的leader Geoffrey De Smit,他现在是Optaplanner项目的头儿,也是Optaplanner的作者,10多年前他开发了Optaplanner,前些年他把它贡献给了JBoss开源社区,任这个项目的头儿。我在使用Optaplanner做项目的时候,他们的讨论组上向他提过一些问题,他为人相当nice且有耐心,给我解答了不少问题。

3.3 运行示例:
  我们选择一个比较经典的Cloud balancing示例运行一下看看。
  先说明一下这个示例,这个示例是模拟在云端进行进程管理(或称进程调度,或称任务调度吧),也就是进程分配到不同的计算资源(也就是计算机)的方案,演示Optaplanner规划引擎是如何在保证每个进程都满足运行要求的情况下,以最节省成本的方式分配计算机资源的。
  示例中有两个主要实体概念 - 进程(Process,下面跟着官方文档称Process吧, 可以理解为我们的程序,或任务)和Computer(也就是我们理解的计算机、服务器了)。每个Process有CPU速度,内存大小和网络带宽三大要求。对应地,每台Computer也有一个固定的参数,表明该Computer可提供的CPU速度、内存大小和带宽;Computer另外还有一个属性就是成本。也就是这台电脑一但被使用了,就需要花费成本去维护。这个示例的目标是:给出一些Process和一些Computer,Optaplanner规划引擎在对这些实体进行对比运算,将所有Process分配到指定的一台Computer, 这个分配方案有两个要求:
  1.硬性要求: Process所分配到的Computer必然满足CPU,内存和带宽三大要求要求。ps:当多个Process被分配到同一个Computer时,它的CPU,内存和带宽资源占用是累加的,也就是说,当台Computer只有2G内存,若已经有一个内存需求是1G的Process被分配在它上面,那后面可以再分配给它的Process,其内存要求必然是1G以下的,因为这进修这台Computer还只剩下1G内存了,CPU和带宽也是同样的分配规则。
  2. 软性要求:任何一台Computer一旦有任务分配进去,即表示该Computer被占用,需计算这台Computer的成本。Optaplanner规划引擎需要找找出一个方案,在满足了第1点的硬性要求的前提下,令到这所有被占用的Computer的成本加起来尽量小(为什么不能说最小呢?因为这是一个NPC问题,不一定可以找到成本最小的,也就是 说不一定能找到最佳方案的,详情参考本系列文章中,关于规则问题与NP, NPC问题的篇章).
  下图是我进入这个示例后,选择了9个Processes分配到3台Computers上的示例。Optaplanner的示例程序都提供这些示例的相关数据,只要选择就可以了,所以还是比较贴心的,但我们自己做项目过程中,去生成、处理这些数据的工作量,就点了系统的不少比例了。

               
            

3.4. 运行并解读示例:
  点击顶端的Solve按钮,引擎就开始工作,它会不断尝试不同的组合方案(这是一个非常复杂的过程,涉及到中种搜索算法Tabu,模拟退火等),找到既满足Process对CPU、内存和带宽的要求,且所使用的所有Computer中,成本加起来尽量小。下面就是运行了一段时间之后,9个Process分配到了两个Computer的情况。所得的方案的好坏,是通过评分来实现的,关于评分,可以查看后面Optaplanner规划引擎关于分数方面的文章。
            

  好了,到目前为止我们已经初成功能运行起了它的示例,大家也可以尝试一下其它示例,各个示例的背景,可以到Optaplanner官网关于示例的章节中查看。我在后面的文章中,也会找几个具代表性的示例进行翻译。

  在下一篇,我们就要用这个示例的源码生成Eclipse中项目,好让大家可以更深入具体了解Optaplanner的实现。
谢谢。

另外,若对此文(或本系列任何内容)感兴趣,欢迎转载,但请尊重艰辛劳动,注明出处。为谢!
 End.
 如需了解更多关于Optaplanner的应用,请发电邮致:kentbill@gmail.com

Optaplanner逐步学习(0) : 基本概念 - Optaplanner,规划问题, 约束,方案

  之前的文章中,分别从APS,排产到规划引擎叙述了一些理论基础;并介绍了一些Optaplanner大概的情况;并一步步将Optaplanner的示例运行起来,将示例源码导进Eclipse分析了一下它的Hello world入门示例,从本篇开始,我们将分步学习它的一些概念及用法。 

什么是Optaplanner

  其实这个名称是作者将这个引擎贡献给了Jboss社区后,才使用的名,之前叫做Drools planner。没错,它就是结合Drools(一个开源规则引擎)一起应用的(也可以单独使用),Drools在这里的作用主要是用来作编写计分脚本,事实上完全可以抛开Drools,直接使用Optaplanner自己的API,通过Java代码自己来计分,但这个难度就大得多。详细情况计到相应的章节再细说。
  名称的前缀应该是Optimize的词根,或取近音吧,因为Optaplanner其实就是一个对待规划的方案组合进行优化的引擎。好了,关于它的名称就不花费太多的口水去深究,我们看看官方是怎么定义Optaplanner的。"OptaPlanner is a constraint solver. It optimizes business resource planning use cases, such as Vehicle Routing, Employee Rostering, Cloud Optimization, Task Assignment, Job Scheduling, Bin Packing and many more. " - Optaplanner 是一个约束解决器,它可以优化业务资源,规划各种案例,例如车间调度,职员排班,云优化,任务分配,工作排程,装箱等相关的问题,例如下图。
  
  而我对Optaplanner的理解,它是一个Planning Engine - 规划引擎,针对各行各业的业务需求,开发人员需要将一些业务规则翻译成约束,并对业务场景中的实体进行抽象建模,规划引擎根据上述约束和模型对象进行规划,找出一个相对最优化的方案出来返回给用户。其实如果需要规划的业务对象不多(种类和数量都不多),规则不太复杂,人类是可以通过自己的经验、推算和规则运行,得到一个可行方案的,甚至当问题规模足够小的时候,是可以找到一个最优方案的。关于规划问题,大家可以参考这个系统文章中的一篇入门介绍《Optaplanner - 入门介绍》,里面讲到,规划问题其实就是数学上的NP问题或NPC问题,目前数据世界对于这种问题,是没有可用算法直接实现的,当问题足够大的时候,只能够通过一些寻优算法(例如爬山算法,模拟退火及遗传算法等)提高找到问题相对优解的机率。而Optaplanner正是一个集成了这类算法,实现快速赶寻找相对最优方案的引擎。它是一个轻量级的,可嵌入的规划引擎,也就是说你可以在自己的程序中通过Jar包直接和相关的配置项来直接使用Optapalnner. 当然,当你需要一个独立的,具有良好扩展性的规划服务组件时,可以直接使用Optaplanner建立自己的规划服务器,通过Spring等框架,对外提供规划服务。
  Optaplanner是基于Apache Software License.协议的,你可以直接使用它作为商业用途。并且它是使用纯Java编写的,最低功能要求下,只需安装一个JVM即可以使用Optapalnner了。并且它所有的包都可以从Maven中央库中获得,即只需要建立一个Maven项目,简单配置好依赖项,就可以开始基于Optaplanner的开发了。
下面,就开始对Optaplanner中概念进行逐一讲解.

什么是规划问题(Planning Problem)

规划问题是 - 基于有限资源,及指定约束条件下达到优化目标(包括资源、排程安排等优化).,例如:
  • 最大化利润
  • 最小化对生态环境的影响
  • 提高员工及客户的满意度
  • ........
要实现这些目标,需要以下条件:
  • 人员
  • 时间
  • 预算(资金)
  • 物理资产(例如机台、汽车,电脑,建筑等等)
下图是Optaplanner官网对规划问题的定义:
  
  上面是对官网的一些翻译。通俗地讲,规划问题就是:
1. 存在一堆对象,例如:任务、人员、资源等,以后称作规划实体 - 官方称planning entity;
2. 还存在一些条件规则,例如:任务最迟需要什么时候完成,人员每天最多只能上班8小时,在指定的时间段内资源是有限的。以后称约束 - 官方称Constraint
3. 根据上述第2点的条件,对第1点所述的规划实体进行资源分配和时间安排,例如,哪个任务应该安排在哪个机台上,在什么时候开始作业;哪个人员安排在哪个车间的哪个班次;哪种资源(例如:机台、原料等)需要确保在哪个时间送到哪个车间等。
  上述第3点所做的工作就是一个规划的过程,也就是引擎会根据约束的限制和规划实体的特性,对这些规划实体进行时间或/和空间上的规划;这个就是规划过程。而我们面对的这些规划实体和这些约束的结合体,就称作规划问题。例如:排定下个学期每个年级的课程表,令每个课程的老师不会出现同一时候分配到不同的班级上课。现有一堆外卖,规划好各个骑手的取餐、送餐路线,令每个骑手都以尽量小的路程和时间成本送最多的单。这些都可以被视作规划问题。

规划实体与规划变量(Planning Entity & Planning Variable)

  我们知道,规划问题,就是对一些规划实体进规划预计分配。例如编造排班表,是一个规划问题,那么抽象出来,一个工人就是一个规划实体(Planning Entity)了,它是被规划的对象。而工人在指定的时间在哪个车间上班,就是这个规划实体的规划变量(Planning vaiable)了。所以,其实解决这个规划问题的过程,就是针对每一个规划实体,根据约束及每个规划实体的情况,来给它的规划变量设置适当的值,令到所有规划实体的所有规划变量的组合达到整体最优。即是设定每个工人(规划实体),在哪个时间,去哪个车间上班(上班时间和车间就是规划变量)。

问题事实(Problem Fact)

  问题事实是相对规则实体而言的,它也是一个业务实体,与规划实体不同的是,它只反映出业务情况,而在规划的过程中,不会被规划引擎进行修改。也就是说,问题事实只是用于提供资料,辅助规划引擎进行规划运算的。在整个规划过程,问题事实是只读的。例如规则班次计划的时间,其中的班次是在开始规则之前已经确定的,所以“班次”这个业务实体只会在规划过程中,提供每个班次具体的时间等信息,而不会改变的。那么“班次”这个业务实体,就是一个问题事实。

约束(硬约束与软约束)

  上而我们把业务规则定义为约束,其实目前针对排程方面的规划问题,主要是通过约束进行评分机制的寻优方法。约束就是根据业务规则抽象出来,针对规划变量,在求解规划问题时候的一种限制,或惩罚机制。也就是说,约束是用来制约引擎对规划变量的赋值行为的。例如一个人不可能有超过24个小时的可用时间。
  硬约束:硬约束是指那些不能违反的约束,违反了就会出现不符合常理,即业务可能出现绝不允许的情况出现。例如上面提高,一个人不可能有超过24小时的可用时间(常理);机台运行过程中,机修工不能进行维修工作(涉及安全生产问题,法律及业务有硬性要求。)。因此,硬约束可以被人视为是用于对规则行为进行定义的。
  软约束:软约束是相对硬约束而言的,它是可违反的。设立软约束之目的并不是不允许它违反,而是定量地制约规划结果(结果,即是下面讲到的解或方案)的发展方向,起到对规划结果的偏向作用,即让规则结果尽量向指定的一个方向偏衙。也就是说在满足了硬约束的前提下,再对软约束进行判断,如果软约束能不违反就最好,要是必须违反,违反得越少,所得的方案就越好。例如成本高低就是一种软约束,生产运营中不可能不产生成本,那么如果成本越低,那么方案肯定越好,当然是在满足了硬约束的前提下。

规划问题其实是NP问题或NP-Hard问题

  其实在《Optaplanner - 入门介绍》中已经有讲解过关于NP或NP-Hard(那讲到NPC问题),大家可以去参考一下那篇文章。这时概括地重述一下,NP或NP-Hard问题是问题以下条件的:
  • 对于一个给定的规划的结果(官网中称作solution, 即是解),很容易在合理的时间内对其进行验证是否可行。例如:课程表编排得正不正确,可以根据约束来核对一下就可以确定了,例如有没有出现同一个时间内,一个老师被分配到不同的班级上课。
  • 不存在一个可确定的方法,在合理的时间内找到一个最优解(这里指的是绝对最优解)。这个也不难理解,对于这种没有任何快捷方法找最优解的规划问题,我们唯一的办法就是把所有不同的组合情况全部排列出来,一个一个比较(即逐一枚举),那必然是可以找到最优解的。但是,因为这种方法其实是一种暴力穷举法,当问题非常复杂、且需要规划的实体数量非常多时,它的时间复杂度是随着组合情况的增加,逞指数式上升的,暴力穷举的方法是不可取的。

规划问题布在巨量搜索空间

  搜索空间:因为目前针对规划问题,只能通过搜索的方式去寻找相对最优解,因为相对一些直接通过算法操作得到的办法而言,规划问题只能将它的解一个一个地对比,逐步收敛逼近的办法来得到相对最优解。所以,你可以认为规划问题的相对最优解是搜索出来的,而且每一步搜索都需要对约束进行运算;从所有经历过的解中,找到相对最优一个。所以规划问题存在一个搜索空间的问题,即有多少种可能的解,就表示搜索空间有多大。例如将3个任务分配到两个机台上,存在多少种可能?大家可以自己去算,其实就是排列组合问题。
  而对实际问题时,稍复杂的约束,稍多一点的规划实体,最后得出的可能解的数量都是非常巨大的,很多问题其搜索空间轻易就是一个天文数字。所以,如果对于所有规则问题,都是使用这些暴力枚举的办法,以现有世界上的计算机的算力,很多问题是没办法找到最优解的。
  规划问题的规模,即是规划实体及每个实体的规划变量的组合,例如时间、空间,及影响因素,及这些因素的所有情况组合。例如,如果上述所有实体,规划的变量和所有因素,展开后的数量是M,而一个解是对其中的N个变量进行规划,那么有多少个解呢?其实就是M到N的排列P(M->N).当遇到实际问题的,这些组合的数量就是天文数字了。

可能解,可行解,相对最优解与绝对最优解

  在规则问题中,需要清楚解的概念,在Optaplanner里称作solution, 即方案。在本系列文章中,解与方案是相同的意义,请注意。本猿只是根据中文表达的习惯,在不同的场合以最顺口的方式,视情况确定到底应用用“解”,还是“方案”来表述。
在接下来的一系列文章中,我在讲解这些方案的过程中,会用到以下概念:
  可能解:一个规划问题的任意一个解都称为可能解,也就是所有规则实体的所有规则变量,任意一个组合,都称作一个可能解。例如分配工人A,在1月20日晚班,到1号车间;分配工人A在1月20日晚班到2号车间;分别是两个不同的可能解,尽管它们的差别只是分配到不同的车间.而每个工人的每个班次的工作车间,正好是规划变量。所以任意一个规划变量的不同,都会产生不同的可能解。现在知道为什么规划问题存在巨量搜索空间了吧?
  可行解:可行解就是那些完全符合硬约束的解。即若存在一个解,它对任何一个硬约束都是符合的,则称这个解为可行解。可行解是可能解的一个子集。可行解是可验证的,只要根据目前所有的硬约束,对解中的每一个规划实体中的每个规划变量,逐一核对,看是否符合所有硬约束,如果符合,那就表示这个解是可行解。
  相对最优解:上面已经提,规划问题的搜索空间非常巨量,大多数情况下是不可能计算并比较所有解的值,再取得最佳方案(这个解就是绝对最优解)的。那以在我们固定的时间内,Optaplanner引擎帮我们找到的最优方案,就是称作相对最优解了。大家来思考一下,相对最优解必然是可行解吗?
  绝对最优解:同样的上面提到,就是所有可能解中最优的那个解,目前是没有直接确定的算法,通过运算在合理的时间内去找到一个问题的绝对最优解的,所以要得到绝对最优解,只有一个办法,就是将所有可能解都遍历一次才能找到。当问题规模不算大的时候,以目前的CPU速度还是能实现的。但如果问题稍复杂一点,规划实体和规划变量稍多一点,那么可能解的数量就是一个天文数字了,这种情况下是没办法完全遍历的。所以,在我们现实中,我们是无法得到绝对最优解的。其实更贴切地说,我们所得到的相对最优解,我们不知道它是不是绝对最优解。因为现在数学上还没有办法(除了遍历)证明一个相对最优解是否绝对最优。

  本篇先介绍一下上述两个概念,下一篇将我们再具体介绍其它概念。

如需了解更多关于Optaplanner的应用,请发电邮致:kentbill@gmail.com

关于APS在企业生产计划上的应用

  本人本身是一个码农,已经服务了共和国各项事业(好像是说得有点漂,没办法段子看多了)大约一半工作时候了(按60岁退休的话),从一线的小码农,到现在成了老农,出产了不少或优或劣的各种码,几乎啥都做过。近几年慢慢沉淀到制造业信息化方面,主要是APS在生产计划方面的应用,APS - Advance Planning and Scheduling. 高级计划与排程;其实也就是做计划,只不过使用了一些优化算法,另计划的质量更高一些。从最开始被调去做ERP数据适配APS项目实施,到现在自己在为公司开发排产引擎(当然规划引擎用的是开源的,我可不是数学方面的专家)。从中也接触过不少排程产品,掉过不少坑,身上算是留下了点APS的战斗痕迹吧。下面先讲一下我在这方面的一些看法。等我有时间了,我再把这一年来,为解决APS系统的引擎问题使用optaplanner规划引擎的一些小积累分享一下,但这个时间嘛,还真的不容易挤呀,这一年来基本上每天晚上9点30前没离开过办公室,11点后跟晚班工人一起下班是常事(没错,我在一个制造企业上班,对APS有一个天然的实战环境,这是公司给我的最大条件优势)。

1. 排产的现状

  关于制造业排产的系统,目前人们关注得更多的是MPS(主生产计划系统)的排期,即是公司甚至整个集团层面,根据产品的产工艺参数,结合订单的数量与交期要求,生成以生产订单为基本单位的生产计划,通常称作主生产计划(Master Planning).所谓的排期,或称排计划,更多的是对这些生产工单进行编排。例如根据这些工单的工艺要求分配到不同的生产单位(分厂、车间或承包商),并根据各个工序的生产时间需求,定出一个要求的完成日期,而这个日期其实是有水份的(下面会有解释)。但这些工单去到具体的生产单位后(特别是公司自己的车间作为生产单位时),其具体的生产计划就较少涉及了。原因有二.
  a. 复杂度与可变性太高。到了车间这一层,再下一层就是产线甚至机台了,即车间的生产控制部门获得上级下发来的生产要求之后,会结合在制品、资源与工单的具体要求,向上级单作出一个反馈,即回复是否可按计划的要求完,双方讨价还价确定了一个新的计划版本之后,车间生产控制部门就会制定一个适合本车间的生产计划,再把该计划下发给生产调度部门进行生产。生产调度部门再会根据具体情况,按生产计划进行生产。无论是车间生产计划部门,还是调度部门,他们面临的都是一些涉及一些非常复杂的细节规则,例如生产工单的工艺要求,投放到车间哪个产线,例如哪种甚至哪台机器进行生产,生产过程中需要注意的具体细节等。都需要生产计划部门有所考虑,当然到了调度部门有可能还会有一些更细节的实际情况及约束进行考虑,从而在生产过程中作出临时调整。无论是车间的生产计划部门还是生产调度部门,需要处理的逻辑细节都是很复杂繁多的。而作为人类面对种类繁多,复杂且多变的规则,各种业务制约与各种要求,是无法滴水不漏地顾及的。更多的是通过经验积累给出一些大概的,基于估量的安排。所以,车间各级部门给出的这个计划其不确定性是非常高的,甚至有些情况下,在经验老道的生产计划人员及调度人员排出来的生产计划,如果有足够多的时候去推敲,即使是按目前的生产情况不变,到最后也是不可行,或者说计划的质量要求(例如对成本、交期、产能利用率等要求)是非常低的。但往往在制定出来的初始阶段基本上是没人能推断出来的,更不用说计划推动了一段时间后,随着过程中的各种条件变更发生,越往后就越偏离原来的初充了。所以,要能最大程度上做出一个好的计划,是非常困难的,更多的是以经验生成一个初始计划,在生产过程中根据实际出现的情况,及在初始阶段未能考虑的问题慢慢明确,再持续地作出调整。所以给别人的印象就是,车间的生产计划毫无章法,质量太差,甚至有公司高层认为,车间根本无计划可言。但这是人思维的局限性,而对远超过其处理能力问题时,必然会出来的情况。因此车间层面的生产计划会面临一个严重的复杂度与可变性太高的问题。
  b. 车间生产计划被视作操作细节,被人为未达到战略层次,未得到足够的重视与认识。因为作为公司级别的主生产计划,它是直接作为公司供应链的一环而存在的,这个环节的目标达成率高低是需要下面各个更细层次生产计划的支持的。但作为公司层面,往往要求的是,只要公司的主生产计划保持在一定的达成率,那么就可以满足供应链其它环境的要求了。但事实上这个达成率是需要有冗余的,也就是主生产计划给制定车间生产计划的时间,已经预计到一定的不可确定性存在,因此往往会留下一定的缓冲期。但这个缓冲期长短,是否合理,往往都是通过以往经验得出。而同样道理整个供应链对主生产计划也会留有缓冲期,那么可以想像,为了能满足要求,往往这些缓冲期加起来就会很长,往往比实际执行制造生产的CT还要长。这样就会造成极大的效率低下,及产能资源浪费。但就是因为越往明细的生产计划,不可控、不确定性越大。因此,公司通常都只能够弃小保大。久而久之,大家的焦点都只关注在MPS的层面上了。
  综上所述,目前我们所说的排产,或说生产计划,更多的还只是停留在主生产计划这个层次较高,较虚泛的范畴。而真真正正到了生产控制层次的,往往关注的是MES(生产执行系统)了,而计划因为车间、产线层面的生产调度计划存在太多的难点,及很多方面技术上尚未成熟,令各大企业信息化产品对此较小涉及。大家也会留意到,无论多大、多出名的ERP系统,它关注的都是公司供应链层面的资源调配,而不会涉及具体生产环境,或者具体到库存物流,或具体的订单执行层面的内容。而目前这方面通常引入APS作为支撑慢慢有些成功可用的方案在市场上推广了。
  下面就来看看APS(Advance Planning and Scheduling - 高级计划与排程)技术,在生产制造业的一些应用.

2. 什么是APS.

  上面说了,APS就是一种高级计划与排程技术,那么为什么叫做高级呢?我的理解是,它是相对于以往的MPS的,它除了满足一些生产制造过程中关于工艺、交期等等的硬性要求,还要在满足这此硬性要求的基础上,根据既定的一些设定、或称策略进行不断优化,从而得出最接近策略目标的计划方案。这样说可能比较虚,下面举个例子说明一下。一个主生产计划下发到车间,当前正处于工厂的和产旺季,那么生产策略通常会被整理为"保证交期"(可能谈季的时候,因为资源相对充足,交期的保证不再是难事,策略往往会是"降低成本"。),那么车间生产计划部门收到计划,细分为各车间、产线甚至机台的计生计划时,就会把生产计划的策略大体上区分为两种,一种是保证硬性的要求不违反,例如产品质量要求、生产安全要求等。在此基础上,就会设法安排这些生产订,令其可以更快完成生产,从而可以保证产品所在订单的交期,又可以为后面更多的生产单尽早腾出资源,目标就是提高效率。对于前面的质量、安全的要求,是一些硬性的定性要求;而对于第二种效率的要求,是一种软性的定量要求。对于定性的要求,那么就会有好坏,或说能达到多好的程度评价。在人的角度上来讲,经验越丰富,他排出来的计划效率越高,越符合这个软性要求。这个就是APS的威力所在的,当然大家关注人工智能中的深度学习的信息,可能会发现,这个有一点人工智能的味道,确实是的,但目前还没有听说过这方面的研究。APS技术目前使用的还不是人工智能,而是基于有限资源、固定条件约束下的最优方案分搜寻技术。它的原理就如上面的例子,会把人们对计划的要求划会为硬性约束与软性约束。通过寻优算法(禁忌搜索、遗传算法、模拟退火等)在浩瀚的组合方案中,在有限的时间内,找出的方案,需要在满足硬性要求的前提下,最大程度上满足软性要求的方案。寻找这些方案的一些原理、算法,就涉及一些数学上的概念,例如NC问题,NPC问题等,在此就不再熬述了。如果有机会我另写一些相关的文章讲解一下。
总而言之,APS就是通过一些数学算法,在计算机的强大运算能力支持下,找出一些可能比人类排产老师傅更佳的生产调度计划。\

3. APS的适用场景.

  正如上面提到,现在制造业更多的关注于主生产计划,而具体明细的车间产线层面的生产计划、调度计划,还是处于放养式的存在。而主生产计划由于有足够的关注,往往有更多的投入对其进行研究,而且它面对的问题更宏观;综合来讲,相对车间层面的生产调度计划就没那么多繁杂的制约因素了。所以,目前市上各种APS产品和技术,主要还是针对车间、产线甚至机台的生产调度计划,希望在这个层面的生产计划有一些开创性的成果。但其实我们可以想象,APS可以处理车间、产线层面的生产计划,那么面对制约因素少得多,或宽松得多的主生产计划,是完全卓卓有余的。所以,虽然各大商家都把自己的APS产品瞄准车间、产线层面的生产计划,其实如果在主生产计划上面所需求,它也是可以对现有的主生产计划作出些非常大的改善的。因为尽管主生产计划比较宏观,但还是由公司计划部门的人来制定的,那么就必然有一定的局限性。例如上面提到的计划质量、缓冲期是否合理等等,APS在这方面可以作出很大的提升。当然,把APS应用于主生产计划,其实还是需要下面层面的车间、产线生产计划的支持的,毕竟在主生产计划中,对各种资源与时间的预判,都要由车间、产线层面的生产计划进行实现。而不是毫无根据地猜一个资源可用量,或完成时间的。
  当然,目前在制造业里面临的最大问题还是车间、产线面层的生产调度计划,目前各个APS产品与技术,都是号称可以解决这类问题,都是冲着车间、产线这个层面去的。所以目前见得最多的APS适用场面,还是在车间、产线甚至机台层面,针对已分配的工单,对各个车间、产线甚至机台,在已有的可用资源条件下,基于具体的业务制约因素,将生产任务适当分配到合理的生产单位(车间、产线甚至机台,工位),并根据计划中各任务的关后关联关系,确定每个生产任务的具体开始与结束时间。这个也是APS的核心价值所在。因为目前在车间调度工作中,对于资源的把控也许会相对准确一些,毕竟有条件的工厂,在自身产能不足,但订单要求有硬性规定的时候,可以通过引入外发加工来解决资源不足的问题。而生产时间的安排就没那么容易了。因为这是一个运算量非常大,考虑各种综合因素,考虑工序的前后关系,还要考虑工厂实现的班次等因素,综合起来的计算结果。人类是无法快速运算、毕竟种个方案的。这样的话,APS系统就可以基于自己内核所使用的各种最优解搜寻算法,基于各种约束;再利用计算的高速运算,快速地计算出各个方案的优劣,从而在短时间内对海量组合方案进行计算对比,从而往往能找出比人类更优的生产调度计划,甚至是对于生产任务的开始结束时间,甚至是精确到分钟的。因此,针对人类这方面的不足,通过大运算量,去生成的生产调度计划,是目前APS的主要应用场景。

4. APS产品及引擎的选用

  目前世界上可用的APS产品其实还是不多的,毕竟这是一个数学上都还在不断探索的问题,目前APS产品或技术,主要有偏重于MRP方面的,例如英国FastRact, 还有一些是结合规划引擎与实际排程经验的产例如日本的Asprova. 还有国内也有一些新秀产品,而这些接触不多。另外还有一种不算是产品,而是基于一些规划引擎,结合企业自身的业务场景,自身以项目形式开发的APS系统。目前我所在的企业正是处于这种APS发展状态。我们是基于Optaplanner + Drools作为规划与规则核心引擎,结合自身业务规则,将业务场景中的各类实体抽象,并将呼类繁多的业务规则抽象总结翻译为硬约束与软件约束。再通过程序使用Optaplanner中的适当模式进行生产计划的自动生成。目前我接触过Asprova与Fastract(这个只是接触过他们的顾问提供的信息,不没有进行过项目实施).觉得Asprova确定是相对比较成熟的产品,虽然它的技术已经非常老旧,但基核心价值是引擎可以根据实际的排产经验作出运算优化。如果觉得自己公司的业务相对比较复杂、奇葩,且自己公司具有一定的技术开发实力,建议还是使用Optaplanner进行定制吧。但还是要注意,在作技术选型时,还要充分了解自己业务上的情况,例如排产规则,自己业务跟各个引擎常用的模式有多大差异。这样才能选择一个真正适用的产品或技术。

上述都是自己这些年在APS上遇到各种坑后的总结,不一定对,欢迎大家拍砖。
谢谢。
End.

如需了解更多关于Optaplanner的应用,请发电邮致:kentbill@gmail.com

Optaplanner - 入门介绍

  在上一篇里喷了不少水,这一篇准备放点干货;其实也没办法完全干,因为很多预备知道在交待一下。好了,说一下关于OptaPlanner的背景、应用兼容性及其原理。
这一篇先说一下OptaPlanner是何方神圣,再看看它适用于哪种平台(.NET能用吗?老旧系统能用吗?),再从原理上探究一下,它是如何帮我们把一个看上去几乎不可能实现的工作,努力做到比经验丰富的老师傅更好的。下一篇我将会讲解OptaPlanner相关的基本概念,并教大家它的examples(示例)运行起来(这些examples可是好东西喔,并且非常丰富)。
  顾名思义,叫什么planner的,它肯定是用来plan东西的东东,就是把一堆东西(数据)扔进去,再教它一些规则(Drools脚本或Java写的算分程序),然后它就运用它的数学头脑,把这些东东按要求把它初始化好,并努力找到一个相对最优的方案;如果数据不是太多,那它就能找到一个绝对最优方案了,因为它以把所有情况都篇历。例如:你有一堆任务需要确定分配到哪些机台,需要计算每个任务什么时候开始处理(也就是明细的生产计划了);用OptaPlanner跑完之后,就会给出一个方案,这个方案包含了每个任务应该放在哪个机台,应该在什么时候开始。又例如:在医院等单位进行医护人员排班时,把各个医护人员的专长,每个人员的作息信息,每个科室需要的专业技能等信息放进去,OptaPlanner就能给你找出一个排班方案出来,可以满足各科室对特殊专业人员的需求,也可以满足各人员尽量不超时工作的方案。
  那么问题来了,OptaPlanner到底是一个什么鬼东西?它有这么牛?真能这样,我们做排产的老师傅不是要失业了吗?其实不然,它只是按我们设计的规则来找尽量好的方案,而这些规则好不好直接影响到方案的优劣,所以如果OptaPlanner成功应用了,并不是替代了老师傅们,而是把老师傅解放出来,让他们去重新思考并制定更佳的规则,并通过OptaPlanner来验证并实现这些方案。
  E文好的同学可以直接进它的官网(http://www.optaplanner.org), 学成了记得分享呀。先说一下这OptaPlanner的来头,它本来是一个名叫Geoffrey De Smet的大牛自己写的,后来他就把它贡献给了JBoss基金会(这里省去了N年的曲折离奇, N >= 10),并成为KIE项目组中OptaPlanner项目的负责人。所以OptaPlanner是基于Apache2.0开源协议的,对商业友好,就是说你想用就尽管用,有问题还可以在他们的讨论组上求助。关于这位超级大牛的个信息及OptaPlanner的详情,可从以下链接看到,其实这位大牛给OptaPlanner录制了很多讲解OptaPlanner的视频,只不过它只放在Youtube上,大家要看的自己想办法上去搜OptaPlanner了,提醒一下各位,Gerffrey这牛不是英美或其它以英语为母语国家的人(好像是比利时还是荷兰人),它的口语乡单比较重,听起来挺吃力的,还没字幕。而且讲的都是各个示例和一些比较高级的应用,去看视频之前最好还是打一下基础,要不然基本上看不懂(没基础就算讲中文也听不懂吧?)。

  Geoffrey De Smet在GitHub上的主页:https://github.com/ge0ffrey(还有StackOverflow上也有他老人家不少对OptaPlanner问题的解答,大家可以搜搜)
OptaPlanner的背景介绍:http://www.oschina.net/news/75942/a-decade-of-optaplanner (这是开源中国社区翻译Geoffrey老人家的文章,原著E文版在这:http://www.optaplanner.org/blog/2016/08/07/ADecadeOfOptaPlanner.html

  OptaPlanner其实是一个很好的排程引擎(更贴切地说它是一个规划引擎,下面就称规划引擎吧,因为它不光用于排产上),耐何在国内使用的人十分少,所以中文资料几乎没有,国内有几个比较出名的APS产品,不知道其排程核心用的是什么,不过如果是自主开发APS系统的话,OptaPlanner是一个好的引擎,毕竟并不是所有企业都能找到一堆数学专家对组合优化问题进行研究的。而OptaPlanner资料非常丰富,它的项目组还能提供很好的技术支持(免费的仅限于讨论组答疑,付费的就没试过了),而且使用起来也方便、容易。但目前我观察的情况来看,还只有比较多国外的同行们及相关的技术网站在研究讨论。我也是奉公司之命开发生产排程方面的系统,才硬着头皮去啃它的。又耐何我的体育老师不给力,教的E文也不怎么样,虽然是把基本的东西看懂了,但很多更深层的东西其实还没有完全摸透的(到目前为止我还遇到一个Score Corruption的问题还在研究)。所以有赖大家一起学习之后的分享了。在应用OptaPlanner的过程中,我也遇到一些问题,一开始有些小白问题,后来又遇到一些跟系统实情相关的难题,我也曾经在讨论组上向Geoffrey他老人家请教,老实说,他还是一个比较有耐心,非常nice的人,一点都不嫌我这类小白烦,从原理开始给我讲解出错的原因,应该如何改,这个要猛赞一下。
  接下来我就发挥程序狗的看家本领Ctrl + C -> Ctrl + V, 中间还去逛了一次百度翻译(没办法,体育老师呀).
OptaPlanner是一个约束求解器。它优化了企业资源计划的使用情况,如车辆调度、员工排班、云优化、任务分配、任务调度、Bin Packing等等。每个组织都面临这样的调度难题:分配一组有限的受限资源(员工、资产、时间和金钱)来提供产品或服务。OptaPlanner提供了更有效的计划,提高服务质量并降低成本。OptaPlanner是一个轻量级的、可嵌入的规划引擎。它令普通的java程序员有效地解决优化问题。它还与其他JVM语言兼容(如 Kotlin 与 Scala)。约束适用普通的域对象,可以重用现有代码。没有必要把它们作为数学方程来输入。在引擎盖之下,OptaPlanner结合先进的优化的启发式和共通启发式演算法(如禁忌搜索、模拟退火和延迟接受),非常高效地进行分数计算。OptaPlanner是开放源代码的软件,Apache软件许可下发布。它是用100%的纯java™,运行在任何JVM在Maven的中央存储库也可用。
  好了按上述的官方描述我们可以大概知道,它就是一个用来解一些规划问题的引擎,而规划问题几乎都可以被视作NPC问题,关于什么是NPC问题呢?这里还喷点水,让大家对NPC问题有个大概的概念,如果不是研究数据的,了解一下就可以了。大家可以看一下这位牛人写的关于NPC问题的文章(http://www.matrix67.com/blog/archives/105),概括来说,就是一些没有办法使用确定性算法来得到结果的问题,而对于这类问题,又分为NP问题和NPC问题,但都只能通过遍历的办法才能找到。对于NP问题和NPC问题,我有以下理解,也不知道对不对,大牛看到不对的帮忙指正一下:
  NP问题:一种无法通过确定性算法直接获得解,但对获得的解是可验证的,例如:结合上一篇文章提到的生产排程问题,如果老板只要求做出一个可行的生产计划,也就是只需要一个可以执行的生产排程就可以了。成本、效率什么的都不管;那么这就是一个NP问题。因为要做出这个计划,你也是没有直接的、确定的方法或算法来做的;更多的是靠经验、对实际情况的有限掌握、对来情况的预判和感觉。但是做出来的计划是可以验证的。也就是说车间拿着这个计划是真的可能执行的,而不会出现物料不到位、产品分配到了错误的机台上等违反硬约束问题的, 那么只要不违反这些硬性约束,就认为这是一个可行的计划。所以做一个可行计划,可以被视作是NP问题。
  NPC问题:则是那种不旦无法通过确定性算法获得解,对所得的解,也没有一个确定的办法去验证的问题。还是上面的生产排程问题,如果老板要求做一个所有情况下除了可行,还要成本最低、效率最高的计划。那么:1. 计划员也只是靠经验、预判、对数据有有限掌握做出一个计划来,计划是否可行是可能验证的(也就是NP问题),但这个计划是否成本最低、效率最高,那就没办法验证了,除非你把所有可能的计划都列出来(这个就不是确定性算法了,因为并不是所有情况你都能把所有情况都列出来)。事实上,现实世界遇到的问题,光靠人类,即便通过超级计算机,也是不太可能把所有情况都遍历完的,例如一个计划有1000个任务,就算忽略任务的所有其它考虑因素,就是1000个任务无任务要求,随便自由地排列,也就是1000个数的排列问题了,有多少种情况?是1000的阶乘!(有兴趣的同学自己回顾一下高中的排列公式)再考虑每个任务的各种属性,及每个属性的可能取值范围,那么组合下来,通常是天文数字了。
  所以,OptaPlanner在排程领域的作用就是帮人们对问题的可能性进行“遍历”,为什么我把遍历引起来呢?因为如果仅仅是无序地遍历,对所有情况一个一个试,那OptaPlanner就没啥作用了,我们可以通过自己编写程序,就能设计出遍历所有组合情况的代码来(能不能跑完那是另外一回事)。OptaPlanner强大之处在于,他是有方法地去遍历的,它引入了禁忌搜索,模拟退火等算法,力求在固定的时间内,找到比傻傻地遍历更好的组合方案出来。事实上也证明它这些算法是有效的。
OptaPlanner的作用、构架和应用兼容性
  关于OptaPlanner开源包,大家可以上官网看看,我在这里也只做个大概的介绍,毕竟我也是新手呀。其实OptaPlanner现在已经加入KIE Project Group,作为KIE的一个子项目,关于KIE可以看看Redhat的一个项目群,包括了OptaPlanner(就是本系列文章的主角),Drools(规则引擎,国内已有很多相关的资料,我就不再熬述了,OptaPlanner是需要结合Drools来使用的,所以这个系列的文章里也会有些内容涉及Drools,但不会太深入),另外一个就是jBMP了,是一个流程定制的平台。
作用
  OptaPlanner用官方的描述就是可以帮你规划出一个用更少的资源做更多更好事情的规划引擎。如下图列出它可以做的工作领域(这仅仅是OptaPalnner的Example里有的示例,其实所有关于规则的问题,属于NPC的问题,只要你能把它抽象并建模成OptaPlanner可识别的模型,你就可以用OptaPlanner来解决):车辆调度、工作排程、设备排程,Bin Packing(就是用袋子装石头那个问题啦)及员工排班。
  
构架和应用兼容性
  那么应用OptaPlanner需要什么条件呢?其实作为一个轻量的、可嵌入的规则引擎,兼容性肯定是人家设计时的考虑重点之一,所以它完全是一个纯Java环境的软件,只要你的系统有Java8以上的运行环境(7.6版本要求的是Java8),遵循 Apache Software License 2.0就可以使用了。在我的工作中,我把它运行于Windows, 云端的Unbuntu.凡是一般Java程序能运行的环境,只需你一个jar命令,就可以运行你内嵌了OptaPlanner的程序了。这里有一个官方关于OptaPlanner兼容性的图:
    
  那么有人就问了,现在很多企业用的都是Microsoft平台或其它老旧平台技术(有什么办法呢.NET就是多人才),是不是OptaPlanner与我的项目就无缘了?其实不然,因为OptaPlanner本身是一个引擎,是基于Java技术的,你通过它来实现你自己的规划引擎程序的时候,必然也是需要Java写的。但这个规划程序是一个服务程序,它不像普通的Web程序,需要频繁跟用户交互,事实上它的所有运行过程中涉及的数据都是需要基于内存的,在此过程是不能进行IO的(并不是说OptaPlanner引擎不允许这么做,而是我们设计的时候就不应该这么做),至于为什么,是码农都懂,一个对CPU高度依赖的程序,你还要它去做I/O,是不是有点那个?所以通常情况下,它是一次性把需要规则及数据都装入内存,完成后再输出。基于上述原则我们就可以把写好的规划引擎程序(Java包)放在一台相对独立的服务器上去运行,再以服务的形成为其它客户端系统提供规划服务。那你的客户端系统是用Web还是 C++来写,是你自己的事了。
原理
  那么OptaPlanner是通过什么方法,高效地帮我们在尽量短的时间内,找到更佳的方案呢?还记得上一任篇老农提到,我们做排程的时候,通常有两种约束条件,分别是不可违反的硬约束,如果一个计划违反了硬约束,那这个计划就是不可行的,例如:生产计划中,把产品固定工序的加工次序调乱了,又或者把产品分配到错误的机台上生产(这些约束条件都是业务上你们自己定义的),那么OptaPlanner就把它定义为违反了硬约束。另一类就是软约束,就是那种可以违反,但违反得越多,就会影响越大(影响包括成本、效率、质量等),所得结果方案的质量越差;违反这种约束,OptaPlanner就把它定义为违反了软约束。OptaPlanner就是对这两类约束进行打分,硬约束对应的是硬分数,软约束对应的是软分数。那么得分越高,就表示对应方案的质量越高。在计算这些约束分数的过程中,OptaPlanner会保持优先优化硬分数、然后在硬分数最优的基础上,再去优化软分数的原则,来寻找最佳方案。例如:两个方案A、B对比,方案A的硬分数比方案B的硬分数高1分,方案B的软分数比方案A的软高出10万分。那么OptaPlanner最后还是认为方案A更佳。也就相当于我们写SQL脚本时,order by子句中前后两个字段的关系了,靠前的字段排序比靠后的字段更优先。
思考题:
  既然硬约束是不能违反的,那OptaPlanner当然要保证找出来的方案绝对是不违反硬约束的,这个大家觉得在所有情况下都成立吗?就是OptaPlanner必然给你找到一个绝对不违反硬约束的方案吗? - 显示不是,大家自己思考一下。

这一篇我们先介绍一下OptaPlanner的背景、使用情景和原理。下一篇我们就开始实质的了解它的应用。

如需了解更多关于Optaplanner的应用,请发电邮致:kentbill@gmail.com

OptaPlanner 7.32.0.Final版本彩蛋 - SolverManager之批量求解

上一篇介绍了OptaPlanner 7.32.0.Final版本中的SolverManager接口可以实现异步求解功能。本篇将继续介绍SolverManager的另一大特性 - 批量求解。 适用场景 在日常的规划系统中,求解一个问题,绝大多数情况下,容许运行的时间较有限...