OptaPlanner笔记4

2.2.8. 创建应用程序

  1. 创建SolverFactory 来为每个数据集构建Solver
  2. 加载数据集
  3. 使用Solver.solve()进行求解
  4. 输出数据集的解决方案

通常一个应用包含一个SolverFactory 来为每个要求解的问题数据集构建新的Solver实例。SolverFactory是线程安全的,但Solver不是。

import org.optaplanner.core.api.solver.Solver;
import org.optaplanner.core.api.solver.SolverFactory;
import org.optaplanner.core.config.solver.SolverConfig;
public class TimeTableApp {
    
    
	...
	public static void main(String[] args) {
    
    
		// 创建求解器工厂实例
		SolverFactory<TimeTable> solverFactory = SolverFactory.create(new SolverConfig()
				// 注册规划方案类 @PlanningSolution
                .withSolutionClass(TimeTable.class)
                // 注册规划实体类 @PlanningEntity
                .withEntityClasses(Lesson.class)
                // 注册约束提供者类 ConstraintProvider
                .withConstraintProviderClass(TimeTableConstraintProvider.class)
                // 配置求解器执行时间限制,建议至少5分钟
                .withTerminationSpentLimit(Duration.ofSeconds(5)));
		// 加载问题数据集
        TimeTable problem = generateDemoData();
        // 求解问题
        Solver<TimeTable> solver = solverFactory.buildSolver();
        TimeTable solution = solver.solve(problem);
        // 输出解决方案
        printTimetable(solution);
	}
	...
}

注意:如果没有终止设置或者terminationEarly()事件,求解器将一直运行。

OptaPlanner返回在可用终止时间内找到的最优方案。 由于NP困难问题的性质(9.2),最优方案可能不是最佳的,尤其是对于较大的数据集。 增加终止时间以可能找到更好的方案。

2.2.9.2. 测试应用程序

2.2.9.2.1. 测试约束

可使用ConstraintVerifier对每一种约束条件进行单元测试

import org.junit.jupiter.api.Test;
import org.optaplanner.test.api.score.stream.ConstraintVerifier;

class TimeTableConstraintProviderTest {
    
    

    private static final Room ROOM1 = new Room("Room1");
    private static final Timeslot TIMESLOT1 = new Timeslot(DayOfWeek.MONDAY, LocalTime.NOON);
    private static final Timeslot TIMESLOT2 = new Timeslot(DayOfWeek.TUESDAY, LocalTime.NOON);
	
	// 构建约束校验器,入参:约束供应者实例,规划方案类,规划实体类
    ConstraintVerifier<TimeTableConstraintProvider, TimeTable> constraintVerifier = ConstraintVerifier.build(
            new TimeTableConstraintProvider(), TimeTable.class, Lesson.class);

    @Test
    void roomConflict() {
    
    
    	// 构建规划实体数据集合,验证约束是否正确的进行惩罚
        Lesson firstLesson = new Lesson(1, "Subject1", "Teacher1", "Group1", TIMESLOT1, ROOM1);
        Lesson conflictingLesson = new Lesson(2, "Subject2", "Teacher2", "Group2", TIMESLOT1, ROOM1);
        Lesson nonConflictingLesson = new Lesson(3, "Subject3", "Teacher3", "Group3", TIMESLOT2, ROOM1);
        constraintVerifier.verifyThat(TimeTableConstraintProvider::roomConflict)
                .given(firstLesson, conflictingLesson, nonConflictingLesson)
                .penalizesBy(1);
    }

}

注意:因为约束权重在投入生产运行前经常更改。ConstraintVerifier在测试中忽略约束权重,即使这些约束权重是在ConstraintProvider中硬编码的。这样,约束权重的调整就不会破坏单元测试。

如果测试失败将报错如:

java.lang.AssertionError: Broken expectation.
Constraint: example.domain/Room conflict
Expected penalty: 2 (class java.lang.Integer)
Actual penalty: 1 (class java.lang.Integer)
Explanation of score (-1hard/0soft):
Constraint match totals:
-1hard: constraint (Room conflict) has 1 matches:
-1hard: justifications ([Subject1(1), Subject2(2)])
Indictments:
-1hard: indicted object (Subject1(1)) has 1 matches:
-1hard: constraint (Room conflict)
-1hard: indicted object (Subject2(2)) has 1 matches:
-1hard: constraint (Room conflict)

参考Testing Constraint Streams

猜你喜欢

转载自blog.csdn.net/zhoudingding/article/details/132253633