Update optaplanner example (#12467)

* Update optaplanner example

Moved to 8.24.0.Final version
Removed deprecated drools calculation
Added constraints stream solver
Updated to solver config java api

* Update optaplanner/pom.xml

Co-authored-by: Jiří Locker <jiri.locker@gmail.com>

* PR feedback

* Move OptaPlanner module to jdk9-and-above
Moved to default-jdk9-and-above and integration-jdk9-and-above

Co-authored-by: Jiří Locker <jiri.locker@gmail.com>
This commit is contained in:
dupliaka 2022-08-18 06:32:25 +02:00 committed by GitHub
parent 4e18fd0aa9
commit 4e0ea2972f
12 changed files with 139 additions and 61 deletions

View File

@ -18,10 +18,16 @@
<artifactId>optaplanner-core</artifactId>
<version>${optaplanner-core.version}</version>
</dependency>
<dependency>
<groupId>org.optaplanner</groupId>
<artifactId>optaplanner-test</artifactId>
<version>${optaplanner-core.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<optaplanner-core.version>7.9.0.Final</optaplanner-core.version>
<optaplanner-core.version>8.24.0.Final</optaplanner-core.version>
</properties>
</project>

View File

@ -3,7 +3,7 @@ package com.baeldung.optaplanner;
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.drools.ProblemFactCollectionProperty;
import org.optaplanner.core.api.domain.solution.ProblemFactCollectionProperty;
import org.optaplanner.core.api.domain.valuerange.ValueRangeProvider;
import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore;
import org.slf4j.Logger;
@ -16,36 +16,36 @@ import java.util.List;
public class CourseSchedule {
Logger logger = LoggerFactory.getLogger("CourseSchedule");
@ValueRangeProvider(id = "availableRooms")
@ProblemFactCollectionProperty
private List<Integer> roomList;
@ValueRangeProvider(id = "availablePeriods")
@ProblemFactCollectionProperty
private List<Integer> periodList;
@PlanningEntityCollectionProperty
private List<Lecture> lectureList;
@PlanningScore
private HardSoftScore score;
public CourseSchedule(){
roomList = new ArrayList<>();
periodList = new ArrayList<>();
lectureList = new ArrayList<>();
}
@ValueRangeProvider(id = "availableRooms")
@ProblemFactCollectionProperty
public List<Integer> getRoomList() {
return roomList;
}
@ValueRangeProvider(id = "availablePeriods")
@ProblemFactCollectionProperty
public List<Integer> getPeriodList() {
return periodList;
}
@PlanningEntityCollectionProperty
public List<Lecture> getLectureList() {
return lectureList;
}
@PlanningScore
public HardSoftScore getScore() {
return score;
}
@ -54,6 +54,18 @@ public class CourseSchedule {
this.score = score;
}
public void setRoomList(List<Integer> roomList) {
this.roomList = roomList;
}
public void setPeriodList(List<Integer> periodList) {
this.periodList = periodList;
}
public void setLectureList(List<Lecture> lectureList) {
this.lectureList = lectureList;
}
public void printCourseSchedule() {
lectureList.stream()
.map(c -> "Lecture in Room " + c.getRoomNumber().toString() + " during Period " + c.getPeriod().toString())

View File

@ -0,0 +1,24 @@
package com.baeldung.optaplanner;
import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore;
import org.optaplanner.core.api.score.stream.Constraint;
import org.optaplanner.core.api.score.stream.ConstraintFactory;
import org.optaplanner.core.api.score.stream.ConstraintProvider;
import org.optaplanner.core.api.score.stream.Joiners;
public class CourseScheduleConstraintProvider implements ConstraintProvider {
@Override
public Constraint[] defineConstraints(ConstraintFactory constraintFactory) {
return new Constraint[]{
roomConflict(constraintFactory)
};
}
public Constraint roomConflict(ConstraintFactory constraintFactory){
return constraintFactory
.forEachUniquePair(Lecture.class,
Joiners.equal(Lecture::getRoomNumber), Joiners.equal(Lecture::getPeriod))
.penalize("Room conflict", HardSoftScore.ONE_HARD);
}
}

View File

@ -1,14 +1,24 @@
package com.baeldung.optaplanner;
import org.optaplanner.core.api.domain.entity.PlanningEntity;
import org.optaplanner.core.api.domain.lookup.PlanningId;
import org.optaplanner.core.api.domain.variable.PlanningVariable;
@PlanningEntity
public class Lecture {
@PlanningId
private Long id;
private Integer roomNumber;
private Integer period;
public Lecture(Long i) {
this.id = i;
}
public Lecture() {
}
@PlanningVariable(valueRangeProviderRefs = {"availablePeriods"})
public Integer getPeriod() {
return period;

View File

@ -1,15 +1,14 @@
package com.baeldung.optaplanner;
import org.optaplanner.core.api.score.Score;
import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore;
import org.optaplanner.core.impl.score.director.easy.EasyScoreCalculator;
import org.optaplanner.core.api.score.calculator.EasyScoreCalculator;
import java.util.HashSet;
public class ScoreCalculator implements EasyScoreCalculator<CourseSchedule> {
public class ScoreCalculator implements EasyScoreCalculator<CourseSchedule, HardSoftScore> {
@Override
public Score calculateScore(CourseSchedule courseSchedule) {
public HardSoftScore calculateScore(CourseSchedule courseSchedule) {
int hardScore = 0;
int softScore = 0;
@ -27,6 +26,6 @@ public class ScoreCalculator implements EasyScoreCalculator<CourseSchedule> {
}
}
return HardSoftScore.valueOf(hardScore, softScore);
return HardSoftScore.of(hardScore, softScore);
}
}

View File

@ -1,14 +0,0 @@
package com.baeldung.optaplanner
import com.baeldung.optaplanner.Lecture;
import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScoreHolder;
global HardSoftScoreHolder scoreHolder;
rule "noNullRoomPeriod"
when
Lecture( roomNumber == null );
Lecture( period == null );
then
scoreHolder.addHardConstraintMatch(kcontext, -1);
end

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<solver>
<scanAnnotatedClasses/>
<scoreDirectorFactory>
<scoreDrl>courseSchedule.drl</scoreDrl>
</scoreDirectorFactory>
<termination>
<secondsSpentLimit>10</secondsSpentLimit>
</termination>
</solver>

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<solver>
<scanAnnotatedClasses/>
<scoreDirectorFactory>
<easyScoreCalculatorClass>com.baeldung.optaplanner.ScoreCalculator</easyScoreCalculatorClass>
</scoreDirectorFactory>
<termination>
<secondsSpentLimit>10</secondsSpentLimit>
</termination>
</solver>

View File

@ -1,3 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%-12.12t] %-5p %m%n</pattern>
</encoder>
</appender>
<logger name="org.optaplanner" level="info"/>
</configuration>
<root level="info">
<appender-ref ref="consoleAppender" />
</root>
</configuration>

View File

@ -0,0 +1,28 @@
package com.baeldung.optaplanner.test;
import com.baeldung.optaplanner.CourseSchedule;
import com.baeldung.optaplanner.CourseScheduleConstraintProvider;
import com.baeldung.optaplanner.Lecture;
import org.junit.jupiter.api.Test;
import org.optaplanner.test.api.score.stream.ConstraintVerifier;
public class CourseScheduleConstraintProviderUnitTest {
private final ConstraintVerifier<CourseScheduleConstraintProvider, CourseSchedule> constraintVerifier =
ConstraintVerifier.build(new CourseScheduleConstraintProvider(), CourseSchedule.class, Lecture.class);
@Test
public void testCourseScheduleConstraint() {
Lecture lecture = new Lecture(0L);
lecture.setPeriod(1);
lecture.setRoomNumber(1);
Lecture conflictedLecture = new Lecture(1L);
conflictedLecture.setPeriod(1);
conflictedLecture.setRoomNumber(1);
constraintVerifier.verifyThat(CourseScheduleConstraintProvider::roomConflict)
.given(lecture, conflictedLecture)
.penalizesBy(1);
}
}

View File

@ -1,16 +1,26 @@
package com.baeldung.optaplanner.test;
import com.baeldung.optaplanner.CourseSchedule;
import com.baeldung.optaplanner.CourseScheduleConstraintProvider;
import com.baeldung.optaplanner.Lecture;
import com.baeldung.optaplanner.ScoreCalculator;
import org.junit.Assert;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.optaplanner.core.api.score.ScoreManager;
import org.optaplanner.core.api.solver.Solver;
import org.optaplanner.core.api.solver.SolverFactory;
import org.optaplanner.core.config.solver.SolverConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.time.Duration;
import java.util.Arrays;
public class OptaPlannerUnitTest {
Logger logger = LoggerFactory.getLogger("OptaPlannerUnitTest");
static CourseSchedule unsolvedCourseSchedule;
@BeforeAll
@ -18,8 +28,8 @@ public class OptaPlannerUnitTest {
unsolvedCourseSchedule = new CourseSchedule();
for(int i = 0; i < 10; i++){
unsolvedCourseSchedule.getLectureList().add(new Lecture());
for(long i = 0; i < 10; i++) {
unsolvedCourseSchedule.getLectureList().add(new Lecture(i));
}
unsolvedCourseSchedule.getPeriodList().addAll(Arrays.asList(new Integer[] { 1, 2, 3 }));
@ -29,7 +39,12 @@ public class OptaPlannerUnitTest {
@Test
public void test_whenCustomJavaSolver() {
SolverFactory<CourseSchedule> solverFactory = SolverFactory.createFromXmlResource("courseScheduleSolverConfiguration.xml");
SolverFactory<CourseSchedule> solverFactory = SolverFactory.create(new SolverConfig()
.withSolutionClass(CourseSchedule.class)
.withEntityClasses(Lecture.class)
.withEasyScoreCalculatorClass(ScoreCalculator.class)
.withTerminationSpentLimit(Duration.ofSeconds(1)));
Solver<CourseSchedule> solver = solverFactory.buildSolver();
CourseSchedule solvedCourseSchedule = solver.solve(unsolvedCourseSchedule);
@ -38,13 +53,22 @@ public class OptaPlannerUnitTest {
}
@Test
public void test_whenDroolsSolver() {
public void test_whenConstraintStreamSolver() {
SolverFactory<CourseSchedule> solverFactory = SolverFactory.create(new SolverConfig()
.withSolutionClass(CourseSchedule.class)
.withEntityClasses(Lecture.class)
.withConstraintProviderClass(CourseScheduleConstraintProvider.class)
.withTerminationSpentLimit(Duration.ofSeconds(1)));
SolverFactory<CourseSchedule> solverFactory = SolverFactory.createFromXmlResource("courseScheduleSolverConfigDrools.xml");
Solver<CourseSchedule> solver = solverFactory.buildSolver();
CourseSchedule solvedCourseSchedule = solver.solve(unsolvedCourseSchedule);
logger.info(ScoreManager.create(solverFactory).explainScore(solvedCourseSchedule).getSummary());
Assert.assertNotNull(solvedCourseSchedule.getScore());
Assert.assertEquals(0, solvedCourseSchedule.getScore().getHardScore());
Assert.assertEquals(-4, solvedCourseSchedule.getScore().getHardScore());
}
}

View File

@ -492,7 +492,6 @@
<module>open-liberty</module>
<module>oauth2-framework-impl</module>
<module>optaplanner</module>
<module>orika</module>
<module>osgi</module>
@ -926,7 +925,6 @@
<module>open-liberty</module>
<module>oauth2-framework-impl</module>
<module>optaplanner</module>
<module>orika</module>
<module>osgi</module>
@ -1248,6 +1246,7 @@
<module>apache-httpclient-2</module>
<module>libraries-concurrency</module>
<module>maven-modules/multimodulemavenproject</module>
<module>optaplanner</module>
<module>persistence-modules/sirix</module>
<module>persistence-modules/spring-data-cassandra-2</module>
<module>quarkus-modules/quarkus-vs-springboot</module>
@ -1317,6 +1316,7 @@
<module>apache-httpclient-2</module>
<module>libraries-concurrency</module>
<module>maven-modules/multimodulemavenproject</module>
<module>optaplanner</module>
<module>persistence-modules/sirix</module>
<module>persistence-modules/spring-data-cassandra-2</module>
<module>quarkus-modules/quarkus-vs-springboot</module>