2.1 OptPlanner求解步骤及配置

1,469 阅读4分钟

这是我参与更文挑战的第4天,活动详情查看: 更文挑战

内容概要

前面两个篇章,我们学习了两个例子,高中课程表以及云资源优化的问题。相信大家对OptaPlanner是什么,能解决什么有一个直观的感受。那在初步了解了这些之后,我们将来学习一下OptaPlanner求解时的步骤,它都做了什么。

概述

使用OptaPlanner解决一个Planning Problem规划问题包括以下步骤:

  1. 问题建模:将你的规划问题建模为一个带有@PlanningSolution注解的类,例如CloudBalance类。
  2. 配置求解器:配置一个求解器,例如任何CloudBalance实例的First Fit和Tabu Search求解器(不同的求解器对不同的规划问题,其性能和结果有着很大的差别,后续再讲解如何选择适合的求解器)。
  3. 组装问题数据:从数据层加载一个问题数据集,例如一个CloudBalance实例。这就是规划问题。
  4. 调用求解器:用Solver.solve(problem)来解决这个问题,返回找到的最佳解决方案。

image.png

求解器配置

求解器的配置OptaPlanner支持xml形式和API形式的配置。

XML配置

SolverFactory构建一个Solver实例。用一个作为classpath资源(由ClassLoader.getResource()定义)提供的Solver配置XML文件配置SolverFactory

   SolverFactory<NQueens> solverFactory = SolverFactory.createFromXmlResource(
               "org/optaplanner/examples/nqueens/solver/nqueensSolverConfig.xml");
       Solver<NQueens> solver = solverFactory.buildSolver();

在一个的项目中(遵循Maven目录结构),solverConfig XML文件位于$PROJECT_DIR/src/main/resources/org/optaplanner/examples/nqueens/solver/nqueensSolverConfig.xml
另外,也可以用SolverFactory.createFromXmlFile()从文件中创建一个SolverFactory。但是出于可移植性的考虑,推荐使用classpath资源。
SolverSolverFactory都有一个叫做Solution_的通用类型,它是代表规划问题和解决方案的类,例如CloudBalance类。
一个求解器配置的XML文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<solver xmlns="https://www.optaplanner.org/xsd/solver" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="https://www.optaplanner.org/xsd/solver https://www.optaplanner.org/xsd/solver/solver.xsd">
  <!-- 定义模型 -->
  <solutionClass>org.optaplanner.examples.nqueens.domain.NQueens</solutionClass>
  <entityClass>org.optaplanner.examples.nqueens.domain.Queen</entityClass>

  <!-- 定义约束规则文件 -->
  <scoreDirectorFactory>
    <scoreDrl>org/optaplanner/examples/nqueens/solver/nQueensConstraints.drl</scoreDrl>
  </scoreDirectorFactory>

  <!-- 配置不同的参数和优化算法 -->
  <termination>
    ...
  </termination>
  <constructionHeuristic>
    ...
  </constructionHeuristic>
  <localSearch>
    ...
  </localSearch>
</solver>

注意其中的三个部分:

  • 定义模型
  • 定义约束规则或者分数计算类
  • 可选择配置优化算法 后续会进一步解释配置的这些不同部分。

通过API配置

求解器配置也可以通过SolverConfig API进行配置。这对于在运行时动态地改变一些值特别有用。例如,在构建求解器之前,根据系统属性来改变运行时间。

        SolverConfig solverConfig = SolverConfig.createFromXmlResource(
                "org/optaplanner/examples/nqueens/solver/nqueensSolverConfig.xml");
        solverConfig.withTerminationConfig(new TerminationConfig()
                        .withMinutesSpentLimit(userInput));

        SolverFactory<NQueens> solverFactory = SolverFactory.create(solverConfig);
        Solver<NQueens> solver = solverFactory.buildSolver();

求解器配置XML中的每个元素都可以作为一个Config类或包命名空间org.optaplanner.core.config中的Config类的属性。这些*Config类是XML格式的Java表示。它们构建了运行时组件(属于包命名空间org.optaplanner.core.impl),并将它们组装成一个高效的Solver。

例如,我们在学习CloudBalance例子中就是通过API的方式进行配置的。

// Build the Solver
        SolverFactory<CloudBalance> solverFactory = SolverFactory.create(new SolverConfig()
                .withSolutionClass(CloudBalance.class)
                .withEntityClasses(CloudProcess.class)
                .withConstraintProviderClass(CloudBalancingConstraintProvider.class)
//                .withTerminationSpentLimit(Duration.ofMinutes(2)));
                .withTerminationSpentLimit(Duration.ofSeconds(30)));
        Solver<CloudBalance> solver = solverFactory.buildSolver();

OptPlanner注解

OptaPlanner需要知道业务模型中哪些类是PlanningEntity规划实体,哪些属性是PlanningVariable规划变量等等。有几种方法:

  1. 在业务模型上添加类注解和JavaBean属性注解(推荐)。属性注解必须是在getter方法上,而不是在setter方法上,这样的话getter不需要是公共的。
  2. 在业务模型上添加类注解和字段注解,这样的话字段也不需要是公共的。
  3. 使用XML:在一个XML文件中配置。但是部分属性还未支持。 OptaPlanner使用过程中重点使用第一种方式,例如:
@PlanningSolution
@XStreamAlias("CloudBalance")
public class CloudBalance extends AbstractPersistable {

    private List<CloudComputer> computerList;

    private List<CloudProcess> processList;

    @XStreamConverter(HardSoftScoreXStreamConverter.class)
    private HardSoftScore score;

    public CloudBalance() {
    }

    public CloudBalance(long id, List<CloudComputer> computerList, List<CloudProcess> processList) {
        super(id);
        this.computerList = computerList;
        this.processList = processList;
    }

    @ValueRangeProvider(id = "computerRange")
    @ProblemFactCollectionProperty
    public List<CloudComputer> getComputerList() {
        return computerList;
    }

    public void setComputerList(List<CloudComputer> computerList) {
        this.computerList = computerList;
    }

    @PlanningEntityCollectionProperty
    public List<CloudProcess> getProcessList() {
        return processList;
    }

    public void setProcessList(List<CloudProcess> processList) {
        this.processList = processList;
    }

    @PlanningScore
    public HardSoftScore getScore() {
        return score;
    }

    public void setScore(HardSoftScore score) {
        this.score = score;
    }

}

自定义属性配置

Solver配置,支持自定义属性。自定义属性对于通过Benchmarker调整动态值非常有用。例如:假设EasyScoreCalculator有大量的计算(这些计算是缓存的),你想在一个基准中增加缓存的大小。

  <scoreDirectorFactory>
    <easyScoreCalculatorClass>...MyEasyScoreCalculator</easyScoreCalculatorClass>
    <easyScoreCalculatorCustomProperties>
      <property name="myCacheSize" value="1000"/><!-- Override value -->
    </easyScoreCalculatorCustomProperties>
  </scoreDirectorFactory>

每个自定义属性添加一个public的setter方法,在构建Solver时调用。

public class MyEasyScoreCalculator extends EasyScoreCalculator<MySolution, SimpleScore> {

        private int myCacheSize = 500; // Default value

        @SuppressWarnings("unused")
        public void setMyCacheSize(int myCacheSize) {
            this.myCacheSize = myCacheSize;
        }
}

自定义属性支持大多数数值类型,包括boolean、int、double、BigDecimal、String和enums)。

总结

这一篇章主要学习了OptaPlanner的求解步骤,以及求解器的主要配置和方法,这对我们学习OptaPlanner求解过程是很有帮助的。

作业

在之前的两个例子当中,我们使用的都是API的方式,大家可以换成xml的方式来进行学习,其实xml中的元素都有对应的API方法,只不过xml可读性比较合适,但是使用的时候建议使用API形式。

结束语

下一篇章,我们会着重讲解业务模型中的概念,ProblemFact、ProblemEntity、ProblemSolution等。

创作不易,禁止未授权的转载。如果我的文章对您有帮助,就请点赞/收藏/关注鼓励支持一下吧❤❤❤❤❤❤