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:
parent
4e18fd0aa9
commit
4e0ea2972f
|
@ -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>
|
|
@ -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())
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
4
pom.xml
4
pom.xml
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue