Introduction ============ OptaPlanner isn't always backwards compatible at this time. This file describes how can you upgrade from earlier versions to the latest. Starting from 5.1.0, every migration change has an indication on how likely your code is going to be affected by this change: - [MAJOR] Very likely affects your code. - [MINOR] Probably does not affect your code (especially if you followed the examples), unless you have hacks. - [IMPL_DETAIL] Should not affect your code, unless you have very deep hacks. - [RECOMMENDED] Not a backward incompatible change, but you probably want to do this. - [README] Read this to better understand why the following MAJOR change was made. From 5.0.0-m2 to 5.0.0 ---------------------- No backwards incompatible changes. From 5.0.0 to 5.0.1 ------------------- A score is no longer a double, now it's a Score instance, because a SimpleScore is now just an int and a HardAndSoftScore is now 2 separated ints. In the Solver interface, the return type of the getBestScore() method changed: Before: double score = solver.getBestScore(); After: Score score = solver.getBestScore(); Simple scores are written "-999", and hard and soft scores are written "-999hard/-999soft". You will see this in the examples GUI. In the *ScoreConfig.xml, configuring a feasableScore also reflects this. Before with simple score: -123456789.0 After: -123456789 Before with hard and soft score (Note that the hard part started from 1000000): -123456789.0 After: -123hard/-456789soft--> In the *ScoreConfig.xml, you no longer configure a ScoreCalculator, but now you configure a ScoreDefinition instead. Before: SIMPLE After: SIMPLE Before: HARD_AND_SOFT_CONSTRAINTS After: HARD_AND_SOFT DYNAMIC_HARD_AND_SOFT_CONSTRAINTS was bugged and has been removed, but will be replaced by shiftingPenalty support. Before: DYNAMIC_HARD_AND_SOFT_CONSTRAINTS After: HARD_AND_SOFT From 5.0.1 to 5.1.0.M1 ---------------------- In 5.1.0.m2, Drools Solver has been renamed to Drools Planner, and it's best to do that first. See "From 5.1.0.m1 to 5.1.0.m2". The method Solver.cancel() has been renamed to Solver.terminateEarly() because it does not undo the solving but terminates it early and there's a best solution. Before: solver.cancel(); After: solver.terminateEarly(); Likewise, Solver.isCancelled() has been renamed to Solver.isTerminatedEarly(). Before: solver.isCancelled(); After: solver.isTerminatedEarly(); The interface Finish has been renamed to Termination because Termination seems the defacto standard name for it in the literature. Before: ... extends Finish After: ... extends Termination Before in solver config xml's and benchmarker config xml's : ... After: ... Likewise FinishCompositionStyle has been renamed to TerminationCompositionStyle. Before in solver config xml's and benchmarker config xml's : ... After: ... The Finish.isFinished(...) has been renamed to Termination.isTerminated(). Before: .isFinished(...) After: .isTerminated(...) The class FeasableScoreTermination has been renamed to ScoreAttainedTermination. Its property feasableScore has been renamed to scoreAttained. Before in solver config xml's and benchmarker config xml's : ... After: ... The interface Solution now has a Score property: Solution.getScore() and Solution.getScore() Before: public class ... implements Solution { ... } After if you use a SimpleScore: public class ... implements Solution { ... private SimpleScore score; ... public SimpleScore getScore() { return score; } public void setScore(Score score) { this.score = (SimpleScore) score; } ... } After if you use a HardAndSoftScore: public class ... implements Solution { ... private HardAndSoftScore score; ... public HardAndSoftScore getScore() { return score; } public void setScore(Score score) { this.score = (HardAndSoftScore) score; } ... } The score property of a Solution needs to be cloned: Before: public X cloneSolution() { X clone = new X(); ... return clone; } After: public X cloneSolution() { X clone = new X(); ... clone.score = score; return clone; } The Solver interface no longer has a method Solver.getBestScore(). Before: solver.getBestScore() After: solver.getBestSolution().getScore() From 5.1.0.M1 to 5.1.0.M2 ------------------------- Drools Solver has been renamed to Drools Planner. The maven dependency has been renamed to drools-planner-core. Before in pom.xml: org.drools.solver drools-solver-core ... After in pom.xml: org.drools.planner drools-planner-core ... And resync your IDE (IntelliJ, Eclipse, NetBeans) from the pom.xml files. If you're still using ANT, replace drools-solver-core-?.jar with drools-planner-core-?.jar and adjust your ANT script and your IDE's classpath accordingly. The package has been renamed to org.drools.planner Before in *.java, *.drl: import org.drools.solver... After in *.java, *.drl: import org.drools.planner... Note that the Solver interface and several related classes have NOT been renamed to Planner (yet). TerminationConfig's maximumHouresSpend renamed to maximumHoursSpend Before: 2 After: 2 Accepter has been renamed to Acceptor Before: ... After: ... The method DefaultDecider.setAssertUndoMoveIsUncorrupted() has been renamed. Use the EnvironmentMode debug instead. Before in *.java: (DefaultDecider ((DefaultLocalSearchSolver) solver).getDecider()).setVerifyUndoMoveIsUncorrupted(true); Before in *.xml: ... After in *.xml: DEBUG ... True modify has been implemented. This is a great performance boost. Before in *.java: workingMemory.modifyRetract(xHandle); // before changes are made x.set...; workingMemory.modifyInsert(xHandle, x); // after changes are made After in *.java: x.set...; workingMemory.update(queenHandle, queen); // after changes are made Environment mode DEBUG now checks more and therefore it is slower than before. JBRULES-1804 has been fixed. The HACK to fix wierd truth maintenance behavior is now obsolete. Delete any instances of that hack to make your rules easier to read and faster. Before in *.drl: // HACK to fix wierd truth maintenance behavior in drools // because making weight part of the equals/hashcode doesn't cut it // Vote for https://jira.jboss.org/jira/browse/JBRULES-1804 not IntConstraintOccurrence( ruleId == "...", constraintType == ConstraintType...., causes contains $..., causes contains $..., eval(weight != (...)) ); then ... After in *.drl: then ... A custom ConstraintOccurrence implementation should now use the weight in its equals/hashcode methods. IntConstraintOccurrence and DoubleConstraintOccurrence have been changed as needed. From 5.1.0.M2 to 5.1.0.CR1 -------------------------- Custom ScoreDefinition implementations needs to implement the Double translateScoreToGraphValue(S score) method. After in *ScoreDefinition.java: public Double translateScoreToGraphValue(HardAndSoftScore score) { if (score.getHardScore() == 0) { return Double.valueOf(score.getSoftScore()); } else { return null; } } A benchmarker config no longer supports solvedSolutionVerbosity (only ALL was supported anyway). Before in *BenchmarkConfig.xml: ALL A benchmarker config now needs a benchmarkDirectory directory and solvedSolutionFilesDirectory and solverStatisticFilesDirectory are no longer required. Before in *BenchmarkConfig.xml: local/data/nurserostering/solved ... local/data/nurserostering/statistic ... After in *BenchmarkConfig.xml: local/data/nurserostering ... Benchmarker: the class MaxScoreSolverBenchmarkComparator has been renamed to WorstScoreSolverBenchmarkComparator. A benchmarker no longer uses WorstScoreSolverBenchmarkComparator by default. It now uses TotalScoreSolverBenchmarkComparator by default. The benchmarker method writeResults(resultFile) has been removed. The result is now always written at the end of the benchmark() method in the benchmarkDirectory directory in a file called benchmarkResult.xml. Before in *.java: solverBenchmarker.writeResults(...); The method Score.substract(subtrahend) has been renamed to Score.subtract Before in *Score.java: public ...Score substract(...Score subtrahend) { After in *Score.java: public ...Score subtract(...Score subtrahend) { Custom Score implementation: When rounding is needed, it should now be floored (as defined by Math.floor(double)). A selector no longer supports absoluteSelection and relativeSelection, because it was inefficient. Instead, the forager now supports minimalAcceptedSelection, which only counts doable, accepted moves (instead of all the selected moves). As a guideline, set your minimalAcceptedSelection to the sum of all your absoluteSelection and subtract 10%. Before in *Config.xml: ...ChangeMoveFactory 500 ...SwitchMoveFactory 500 ... MAX_SCORE_OF_ALL After in *Config.xml: ...ChangeMoveFactory ...SwitchMoveFactory ... MAX_SCORE_OF_ALL 900 If you weighted some MoveFactory heavier than another, vote for https://jira.jboss.org/browse/JBRULES-2553. If you use a custom selector: the Selector interface method List selectMoveList(StepScope stepScope) has been replaced by the more efficient method Iterator moveIterator(StepScope stepScope) Before in *Selector.java: public List selectMoveList(StepScope stepScope) { ... return moveList; } After in *Selector.java: public Iterator moveIterator(StepScope stepScope) { ... return moveList.iterator(); } There's now a decent simulated annealing implementation. In many use cases it clearly beats the tabu search implementation. See the reference manual and the nurse rostering example for more info. The ForagerType has been renamed to the PickEarlyType. The ForagerType MAX_SCORE_OF_ALL is now the PickEarlyType NEVER. It is also the default. Before in *Config.xml: MAX_SCORE_OF_ALL ... After in *Config.xml: ... The ForagerType FIRST_BEST_SCORE_IMPROVING is the PickEarlyType FIRST_BEST_SCORE_IMPROVING. Before in *Config.xml: FIRST_BEST_SCORE_IMPROVING ... After in *Config.xml: FIRST_BEST_SCORE_IMPROVING ... The ForagerType FIRST_LAST_STEP_SCORE_IMPROVING is now the PickEarlyType FIRST_LAST_STEP_SCORE_IMPROVING. Before in *Config.xml: FIRST_LAST_STEP_SCORE_IMPROVING ... After in *Config.xml: FIRST_LAST_STEP_SCORE_IMPROVING ... The ForagerType FIRST_RANDOMLY_ACCEPTED has been removed, use the PickEarlyType NEVER with minimalAcceptedSelection 1. Before in *Config.xml: FIRST_RANDOMLY_ACCEPTED After in *Config.xml: NEVER 1 The benchmarker no longer sorts the solvers, because that was confusing to read. Instead it outputs a table with a ranking column. You can still set a custom solverBenchmarkComparator to influence that ranking. Before in *BenchmarkConfig.xml: true From 5.1.0 to 5.2.0.M1 ---------------------- [RECOMMENDED] The example DRL's no longer use the optional character ';' at the end of lines in the LHS. If you copied that practice from the examples, it's recommended to remove them from your DRL's too. From 5.2.0.M1 to 5.2.0.M2 ------------------------- [MINOR] XmlSolverConfigurer.buildSolver() no longer returns a LocalSearchSolver, now it returns just a Solver. Since LocalSearchSolver is just an empty interface which extends the Solver interface, the impact should be small. Before in *.java: LocalSearchSolver solver = xmlSolverConfigurer.buildSolver(); After in *.java: Solver solver = xmlSolverConfigurer.buildSolver(); [MAJOR] EnvironmentMode has been moved to another package. Before in *.java: import org.drools.planner.config.localsearch.EnvironmentMode; After in *.java: import org.drools.planner.config.EnvironmentMode; [MINOR] XmlSolverConfigurer's getConfig() method now returns an AbstractSolverConfig instead of a LocalSearchSolverConfig. If you used anything LocalSearchSolverConfig specific, such as a TerminationConfig, then you have to cast it... Before in *.java: configurer.getConfig().getTerminationConfig().setMaximumSecondsSpend(maximumSecondsSpend); After in *.java: ((LocalSearchSolverConfig) configurer.getConfig()).getTerminationConfig() .setMaximumSecondsSpend(maximumSecondsSpend); [MAJOR] The StartingSolutionInitializer methods no longer have a LocalSearchSolverScope argument, but now have a AbstractSolverScope argument. Before in *.java: public boolean isSolutionInitialized(LocalSearchSolverScope localSearchSolverScope) { ... } public void initializeSolution(LocalSearchSolverScope localSearchSolverScope) { ... } After in *.java: public boolean isSolutionInitialized(AbstractSolverScope abstractSolverScope) { ... } public void initializeSolution(AbstractSolverScope abstractSolverScope) { ... } [MINOR] StepScope has been renamed to LocalSearchStepScope. This only impacts you if you use a custom Selector. Before in *Selector.java: @Override public void ...(StepScope stepScope) { ... } After in *Selector.java: @Override public void ...(LocalSearchStepScope localSearchStepScope) { ... } [MINOR] The Score has a new method: double[] toDoubleArray() If you use a custom Score implementation, you need to implement it: After in *Score.java: public double[] toDoubleArray() { return new double[]{hardScore, softScore, ...}; } [MINOR] SelectorConfig, AcceptorConfig and ForagerConfig's build method now have a ScoreDefinition argument. If you use any custom implementation for any of them, you need to adjust your method signature. Before in *SelectorConfig.java: public Selector buildSelector() { After in *SelectorConfig.java: public Selector buildSelector(ScoreDefinition scoreDefinition) { Before in *AcceptorConfig.java: public Acceptor buildAcceptor() { After in *AcceptorConfig.java: public Acceptor buildAcceptor(ScoreDefinition scoreDefinition) { Before in *ForagerConfig.java: public Forager buildForager() { After in *ForagerConfig.java: public Forager buildForager(ScoreDefinition scoreDefinition) { [MAJOR] Simulated annealing now supports working with hard constraints too (JBRULES-2911) The starting temperature (usually close the maximum score delta) is now a Score instead of a double. Before in *Config.xml: 20.0 After in *Config.xml (if you're using HardAndSoftScore): 0hard/20soft After in *Config.xml (if you're using SimpleScore): 20 [MAJOR] Solution now has a generic type of Score: Solution (JBRULES-2924) Your Solution implementation itself should not be generic, but it should define it's Score type through implements. Before in *.java if you use a SimpleScore: public class ... implements Solution { ... public void setScore(Score score) { this.score = (SimpleScore) score; } ... } After in *.java if you use a SimpleScore: public class ... implements Solution { ... public void setScore(SimpleScore score) { this.score = score; } ... } Before in *.java if you use a HardAndSoftScore: public class ... implements Solution { ... public void setScore(Score score) { this.score = (HardAndSoftScore) score; } ... } After in *.java if you use a HardAndSoftScore: public class ... implements Solution { ... public void setScore(HardAndSoftScore score) { this.score = score; } ... } [RECOMMENDED] If you've followed the examples (which is normally a good thing), check if your solutionEquals() and solutionHashCode() methods respects the hashcode/equals contract, because some of the examples did not. Before in *.java: public boolean solutionEquals(Object o) { ... return new EqualsBuilder() .append(id, other.id) .append(guest, other.guest) .append(seat, other.seat) .isEquals(); ... } public int solutionHashCode() { return new HashCodeBuilder() .append(seat) .toHashCode(); } After in *.java: public boolean solutionEquals(Object o) { ... return new EqualsBuilder() .append(id, other.id) .append(guest, other.guest) .append(seat, other.seat) .isEquals(); ... } public int solutionHashCode() { return new HashCodeBuilder() .append(id) .append(guest) .append(seat) .toHashCode(); } [MINOR] Your SolverEventListener will now a BestSolutionChangedEvent when the Solver starts too. So when the StartingSolutionInitializer initializes, the best solution changed event won't be missed. From 5.2.0.M2 to 5.2.0.CR1 -------------------------- [MAJOR] The Benchmarker configuration XML schema has changed. The entities inheritedUnsolvedSolutionFile and inheritedLocalSearchSolver are now wrapped in a inheritedSolverBenchmark entity and renamed to unsolvedSolutionFile and localSearchSolver. Before in *BenchmarkConfig.xml: ... ... ... ... ... After in *BenchmarkConfig.xml: ... ... ... ... ... From 5.2.0 to 5.3.0.Beta1 ------------------------- [MINOR] If you implemented a custom Acceptor, the buildAcceptor method's signature has changed. Before in *AcceptorConfig.java: public Acceptor buildAcceptor(ScoreDefinition scoreDefinition) { After in *AcceptorConfig.java: public Acceptor buildAcceptor(EnvironmentMode environmentMode, ScoreDefinition scoreDefinition) { [MAJOR] You need to define your solution class in the configuration now: Before in *SolverConfig.xml and *BenchmarkConfig.xml: ... After in *SolverConfig.xml and *BenchmarkConfig.xml: org.drools.planner.examples.curriculumcourse.domain.CurriculumCourseSchedule ... [README] Understand the concept of a "planning entity" class. The class (or classes) that change during planning (and do not implement Solution) are a planning entity. For example: ShiftAssignment, BedDesignation, Queen, CloudAssignment, ... The other domain classes are considered normal planning facts, for example Shift, Employee, Bed, Room, Department, ... They do not change during planning (at least not without pausing the solver). Read the manual to understand the "planning entity" concept better. [MAJOR] You need to define your planning entity class(es) in the configuration now: Before in *SolverConfig.xml and *BenchmarkConfig.xml: ... ... After in *SolverConfig.xml and *BenchmarkConfig.xml: ... org.drools.planner.examples.curriculumcourse.domain.Lecture ... [MAJOR] You need to annotate your planning entity class(es) with the @PlanningEntity annotation Before in *.java: public class Lecture ... { ... } After in *.java: @PlanningEntity public class Lecture ... { ... } [README] Understand the concept of a "planning variable" property. The property (or properties) on a planning entity class that are changed (through their setter) during planning are planning variables. For example: ShiftAssignment.getEmployee(), BedDesignation.getBed(), Queen.getY(), ... Note that most planning entities have 1 property which defines the planning entity and that property is NOT a planning variable. For example: ShiftAssignment.getShift(), BedDesignation.getAdmissionPart(), Queen.getX(), ... Read the manual to understand the "planning variable" concept better. [MAJOR] You need to annotate your planning variable property(ies) with the @PlanningVariable annotation. Furthermore, you need to annotate a @ValueRange* annotation on to define the allowed values. Commonly, you 'll use @ValueRangeFromSolutionProperty (recommended) which specifies a property name on the solution which returns a collection of the allowed values for that variable. If you can't use @ValueRangeFromSolutionProperty, use @ValueRangeUndefined instead (not recommended), but that will exclude you from using construction heuristics. Before in *.java: @PlanningEntity public class Lecture ... { private Course course; private int lectureIndexInCourse; // Changed by moves, between score calculations. private Period period; private Room room; public Course getCourse() {...} public void setCourse(Course course) {...} public int getLectureIndexInCourse() {...} public void setLectureIndexInCourse(int lectureIndexInCourse) {...} public Period getPeriod() {...} public void setPeriod(Period period) {...} public Room getRoom() {...} public void setRoom(Room room) {...} ... public int getStudentSize() { return course.getStudentSize(); } public Day getDay() { return period.getDay(); } } After in *.java: @PlanningEntity public class Lecture ... { private Course course; private int lectureIndexInCourse; // Planning variables: changes during planning, between score calculations. private Period period; private Room room; // This is not a PlanningVariable: it defines the planning entity public Course getCourse() {...} public void setCourse(Course course) {...} // This is not a PlanningVariable: it defines the planning entity public int getLectureIndexInCourse() {...} public void setLectureIndexInCourse(int lectureIndexInCourse) {...} @PlanningVariable @ValueRangeFromSolutionProperty(propertyName = "periodList") public Period getPeriod() {...} public void setPeriod(Period period) {...} @PlanningVariable @ValueRangeFromSolutionProperty(propertyName = "roomList") public Room getRoom() {...} public void setRoom(Room room) {...} ... // This is not a PlanningVariable: no setter public int getStudentSize() { return course.getStudentSize(); } // This is not a PlanningVariable: no setter public Day getDay() { return period.getDay(); } } [MAJOR] Annotate every property on your Solution that returns a collection of planning entities with @PlanningEntityCollectionProperty. Before in *.java: public class CurriculumCourseSchedule ... implements Solution<...> { private List lectureList; ... public List getLectureList() {...} public void setLectureList(List lectureList) {...} } After in *.java: public class CurriculumCourseSchedule ... implements Solution<...> { private List lectureList; ... @PlanningEntityCollectionProperty public List getLectureList() {...} public void setLectureList(List lectureList) {...} } If you have a property that returns a single planning entity (instead of a collection), use @PlanningEntityProperty. [MAJOR] The method getFacts() is renamed to getProblemFacts() and should no longer include planning entities. Every planning entity is now automatically inserted into the working memory if and only if it is initialized. When it's initialized later, it's also automatically inserted. Remove the adding of the planning entities in the getFacts() method. Before in *.java: public class ... implements Solution<...> { public Collection getFacts() { List facts = new ArrayList(); facts.addAll(teacherList); ... facts.addAll(calculateTopicConflictList()); if (isInitialized()) { facts.addAll(lectureList); } return facts; } } After in *.java: public class ... implements Solution<...> { public Collection getProblemFacts() { List facts = new ArrayList(); facts.addAll(teacherList); ... facts.addAll(calculateTopicConflictList()); // Do not add the planning entity's (lectureList) because that will be done automatically return facts; } } [README] A planning entity is considered uninitialized if one if at least on of its planning variables is null. Therefore it's now possible to start planning from a partially initialized starting solution, for example during real-time re-planning after facts change. [MAJOR] The StartingSolutionInitializer no longer has a isSolutionInitialized(AbstractSolverScope) method. The StartingSolutionInitializer should now be smart enough to do nothing if the solution is partially initialized Before in *StartingSolutionInitializer.java: public class ...StartingSolutionInitializer extends AbstractStartingSolutionInitializer { @Override public boolean isSolutionInitialized(AbstractSolverScope abstractSolverScope) { ... } ... } After in *StartingSolutionInitializer.java: public class ...StartingSolutionInitializer extends AbstractStartingSolutionInitializer { ... } Note that you'll probably want to remove the StartingSolutionInitializer altogether and replace it by a construction heuristic (see below). [MAJOR] The planning entity collection in the Solution can never be null, but some (or all) of its planning entity's can be uninitialized. So create them before setting the starting solution, instead of in your StartingSolutionInitializer. Before in *.java: public class ... { public void ...() { CurriculumCourseSchedule schedule = new CurriculumCourseSchedule(); schedule.setTeacherList(teacherList); schedule.setCourseList(courseList); ... solver.setStartingSolution(schedule); } } After in *.java: public class ... { public void ...() { CurriculumCourseSchedule schedule = new CurriculumCourseSchedule(); schedule.setTeacherList(teacherList); schedule.setCourseList(courseList); ... createLectureList(schedule); solver.setStartingSolution(schedule); } private void createLectureList(CurriculumCourseSchedule schedule) { List courseList = schedule.getCourseList(); List lectureList = new ArrayList(courseList.size()); long id = 0L; for (Course course : courseList) { for (int i = 0; i < course.getLectureSize(); i++) { Lecture lecture = new Lecture(); lecture.setId(id); id++; lecture.setCourse(course); // Make sure to set all non PlanningVariable properties lecture.setLectureIndexInCourse(i); // Notice that we leave the PlanningVariable properties on null lectureList.add(lecture); } } schedule.setLectureList(lectureList); } } [RECOMMENDED] Remove the isInitialized() from Solution if you copied that from the examples. Before in *.java: public class ... implements Solution<...> { public boolean isInitialized() { return (lectureList != null); } ... } After in *.java: public class ... implements Solution<...> { ... } [README] Phasing: a Solver can now have multiple phases. Each phase runs an algorithm, such as local search (tabu search, simulated annealing, ...), construction heuristic (first fit, first fit decreasing, best fit decreasing, ...), ... The phasing algorithms are executed sequentially. For example, it's now easy to do 5 minutes of simulated annealing, followed by 5 minutes of tabu search. [MAJOR] The local search solver is now a local search solver phase. Before in *SolverConfig.xml and *SolverBenchmarkConfig.xml: ... ... ... ... ... After in *SolverConfig.xml and *SolverBenchmarkConfig.xml: ... ... ... ... ... Note: it's also possible to set a termination per phase. Warning: some terminations are not supported on the solver level, only on the phase level. For example maximumStepCount: 100 100 ... Warning for *SolverBenchmarkConfig.xml: If the contains a , or , that can no longer be inherited individually. Copy the , or to every . Because a benchmark specific will not overwrite the inheriting , but instead add an extra localSearch phase, which will result in 2 separate, sequential localSearch phases. [README] The StartingSolutionInitializer interface has been removed. Instead, there's now out-of-the-box support for several construction heuristics. These implementations are fast, scalable and configurable. They require little or no domain specific code. They also support features like terminateEarly, logging, partially initialized starting solutions, ... Alternatively, you can also refactor it to a CustomSolverPhaseCommand, which has the same freedom (to write lots of custom code) as a StartingSolutionInitializer had. [MAJOR] Replacing the StartingSolutionInitializer by a construction heuristics (recommended) Before in *SolverConfig.xml and *SolverBenchmarkConfig.xml: org.drools.planner.examples.examination.solver.solution.initializer.ExaminationStartingSolutionInitializer ... After in *SolverConfig.xml and *SolverBenchmarkConfig.xml: ... FIRST_FIT But it's better to use the constructionHeuristicType FIRST_FIT_DECREASING instead, however you'll need to implement create a difficultyComparatorClass or difficultyWeightFactoryClass and configure that on your planning entity class: @PlanningEntity(difficultyComparatorClass = CloudAssignmentDifficultyComparator.class) public class CloudAssignment ... @PlanningEntity(difficultyWeightFactoryClass = LectureDifficultyWeightFactory.class) public class Lecture ... [MINOR] Replacing the StartingSolutionInitializer by a CustomSolverPhaseCommand (not recommended) Before in *SolverConfig.xml and *SolverBenchmarkConfig.xml: org.drools.planner.examples.examination.solver.solution.initializer.ExaminationStartingSolutionInitializer ... After in *SolverConfig.xml and *SolverBenchmarkConfig.xml: ... org.drools.planner.examples.examination.solver.solution.initializer.ExaminationStartingSolutionInitializer Before in *StartingSolutionInitializer.java: public class ...StartingSolutionInitializer extends AbstractStartingSolutionInitializer { public void initializeSolution(DefaultSolverScope solverScope) { // Initialize the solution ... solverScope.getWorkingMemory() ... ... solverScope.calculateScoreFromWorkingMemory() ... } ... } After in *StartingSolutionInitializer.java: public class ...StartingSolutionInitializer implements CustomSolverPhaseCommand { protected final transient Logger logger = LoggerFactory.getLogger(getClass()); public void changeWorkingSolution(SolutionDirector solutionDirector) { // Initialize the solution ... solutionDirector.getWorkingMemory() ... ... solutionDirector.calculateScoreFromWorkingMemory() ... } ... } Warning: A CustomSolverPhaseCommand can be called with a (partially or complete) initialized solution, in which case it probably doesn't want to reset the already initialized planning entities. [MINOR] The AbstractMoveFactory lifecycle methods have changed signature. If you overwrote them (which is unlikely as most implements use CachedMoveFactory), you'll need to adjust them. Before in *MoveFactory.java: public class ...MoveFactory extends AbstractMoveFactory { @Override public void solvingStarted(LocalSearchSolverScope localSearchSolverScope) { ... } ... } After in *MoveFactory.java: public class ... extends AbstractMoveFactory { @Override public void phaseStarted(LocalSearchSolverPhaseScope localSearchSolverPhaseScope) { ... } ... } Similar for solvingEnded(LocalSearchSolverScope), which is now phaseEnded(LocalSearchSolverPhaseScope) [MINOR] The Selector, Acceptor and Forager method have changed signature. If you implemented a custom Selector, Acceptor or Forager, you 'll need to adjust them. Before in *Selector.java, *Acceptor.java or *Forager.java: public class ...Selector extends AbstractSelector { @Override public void solvingStarted(LocalSearchSolverScope localSearchSolverScope) { ... } ... } After in *Selector.java, *Acceptor.java or *Forager.java: public class ...Selector extends AbstractSelector { @Override public void phaseStarted(LocalSearchSolverPhaseScope localSearchSolverPhaseScope) { ... } ... } Similar for solvingEnded(LocalSearchSolverScope), which is now phaseEnded(LocalSearchSolverPhaseScope) [MINOR] TerminationConfig and Termination moved to another package. Before in *.java: import org.drools.planner.config.localsearch.termination.TerminationConfig; import org.drools.planner.core.localsearch.termination.Termination; After in *.java: import org.drools.planner.config.termination.TerminationConfig; import org.drools.planner.core.termination.Termination; If you implemented a custom Termination: the interface has been changed to separate solver and phase termination. [MINOR] SolverConfig has been moved to another package. Before in *.java: import org.drools.planner.config.SolverConfig; After in *.java: import org.drools.planner.config.solver.SolverConfig; [MINOR] Solver.isTerminatedEarly() has been renamed to Solver.isTerminateEarly() Before in *.java: ... solver.isTerminatedEarly() ... After in *.java: ... solver.isTerminateEarly() ... [MINOR] HardPenaltyDeciderScoreComparator has been replaced by FlatteningHardAndSoftScoreComparator Before in *.java: ... HardPenaltyDeciderScoreComparator ... After in *.java: ... FlatteningHardAndSoftScoreComparator ... From 5.3.0.Beta1 to 5.3.0.CR1 ----------------------------- [MAJOR] PlanningFactChange has been renamed to ProblemFactChange (to be in sync with Solution.getProblemFacts()) Similarly, Solver.addPlanningFactChange has been renamed to Solver.addProblemFactChange. Before in *.java: solver.addPlanningFactChange(new PlanningFactChange() { public void doChange(SolutionDirector solutionDirector) { ... }); After in *.java: solver.addProblemFactChange(new ProblemFactChange() { public void doChange(SolutionDirector solutionDirector) { ... } }); [MAJOR] The XmlSolverConfigurer method getConfig() has been renamed to getSolverConfig() Before in *.java: ... configurer.getConfig() ... After in *.java: ... configurer.getSolverConfig() ... [MAJOR] The Solver method setStartingSolution(Solution) has been renamed to setPlanningProblem(Solution) because using it to set an uninitialized Solution is more likely than to set an initialized starting Solution. Before in *.java: ... solver.setStartingSolution(...) ... After in *.java: ... solver.setPlanningProblem(...) ... [MAJOR] Logging level changes: most debug statements have been changed to trace, most info statements have been changed to debug. If you were using info logging, you probably want to use debug now. Before in log4j*.xml: After in log4j*.xml: From 5.3.0.Final to 5.4.0.Beta1 ------------------------------- [MAJOR] Local search configuration: the complete*TabuSize configuration properties have been renamed to *TabuSize. Before in *SolverConfig.xml and *SolverBenchmarkConfig.xml: ... After in *SolverConfig.xml and *SolverBenchmarkConfig.xml: ... Before in *SolverConfig.xml and *SolverBenchmarkConfig.xml: ... After in *SolverConfig.xml and *SolverBenchmarkConfig.xml: ... Before in *SolverConfig.xml and *SolverBenchmarkConfig.xml: ... After in *SolverConfig.xml and *SolverBenchmarkConfig.xml: ... Before in *SolverConfig.xml and *SolverBenchmarkConfig.xml: ... After in *SolverConfig.xml and *SolverBenchmarkConfig.xml: ... [MAJOR] HardAndSoftConstraintScoreCalculator has been renamed to HardAndSoftScoreCalculator. Before in *ScoreRules.drl: import org.drools.planner.core.score.calculator.HardAndSoftConstraintScoreCalculator; global HardAndSoftConstraintScoreCalculator scoreCalculator; After in *ScoreRules.drl: import org.drools.planner.core.score.calculator.HardAndSoftScoreCalculator; global HardAndSoftScoreCalculator scoreCalculator; [MAJOR] The build-in Score implementations (including their ScoreCalculator and ScoreDefinition) have been moved into a package per implementation functionality. Before in *.java: import org.drools.planner.core.score.SimpleScore; import org.drools.planner.core.score.DefaultSimpleScore; import org.drools.planner.core.score.HardAndSoftScore; import org.drools.planner.core.score.DefaultHardAndSoftScore; After in *.java: import org.drools.planner.core.score.buildin.simple.SimpleScore; import org.drools.planner.core.score.buildin.simple.DefaultSimpleScore; import org.drools.planner.core.score.buildin.hardandsoft.HardAndSoftScore; import org.drools.planner.core.score.buildin.hardandsoft.DefaultHardAndSoftScore; Before in *ScoreRules.drl: import org.drools.planner.core.score.calculator.SimpleScoreCalculator; import org.drools.planner.core.score.calculator.HardAndSoftScoreCalculator; After in *ScoreRules.drl: import org.drools.planner.core.score.buildin.simple.SimpleScoreCalculator; import org.drools.planner.core.score.buildin.hardandsoft.HardAndSoftScoreCalculator; Before in xstreamed solution files: After in xstreamed solution files: [MINOR] If you implemented a custom score definition: DefaultHardAndSoftScoreCalculator has been replaced by HardAndSoftScoreCalculator Before in *ScoreDefinition.java: public ScoreCalculator buildScoreCalculator() { return new DefaultHardAndSoftScoreCalculator(); } After in *ScoreDefinition.java: public ScoreCalculator buildScoreCalculator() { return new HardAndSoftScoreCalculator(); } [MINOR] If you implemented a custom Acceptor or Forager: double acceptanceChance has been replaced by boolean accepted. [MINOR] The examples switched from log4j to logback. This does not affect you. Drools Planner still uses slf4j, so log4j is still supported too. [MAJOR] If you use the Benchmarker: JFreeChart has been upgraded and it's GAV changed. Upgrade the JFreeChart's groupId in your poms. After in pom.xml: jfree jfreechart ... After in pom.xml: org.jfree jfreechart ... From 5.4.0.Beta1 to 5.4.0.Beta2 ------------------------------- [MAJOR] The class XmlSolverConfigurer has been renamed to XmlSolverFactory Before in *.java: import org.drools.planner.config.XmlSolverConfigurer; ... protected Solver createSolver() { XmlSolverConfigurer configurer = new XmlSolverConfigurer(); configurer.configure(SOLVER_CONFIG); return configurer.buildSolver(); } After in *.java: import org.drools.planner.config.XmlSolverFactory; ... protected Solver createSolver() { XmlSolverFactory solverFactory = new XmlSolverFactory(); solverFactory.configure(SOLVER_CONFIG); return solverFactory.buildSolver(); } [MINOR] XmlSolverFactory.addXstreamAlias() has been renamed to addXstreamAnnotations() Before in *.java: solverFactory.addXstreamAlias(...); After in *.java: solverFactory.addXstreamAnnotations(...); [MAJOR] The class XmlSolverBenchmarker has moved and been renamed to XmlPlannerBenchmarkFactory. Before in *.java: import org.drools.planner.benchmark.XmlSolverBenchmarker; ... XmlSolverBenchmarker solverBenchmarker = new XmlSolverBenchmarker().configure(benchmarkConfig); After in *.java: import org.drools.planner.benchmark.config.XmlPlannerBenchmarkFactory; ... XmlPlannerBenchmarkFactory plannerBenchmarkFactory = new XmlPlannerBenchmarkFactory().configure(benchmarkConfig); [MAJOR] Benchmarker: the XmlPlannerBenchmarkFactory can now only build a PlannerBenchmark, not run it. To run a benchmark, call benchmark() on that PlannerBenchmark. Before in *.java: plannerBenchmarkFactory.benchmark(); After in *.java: PlannerBenchmark plannerBenchmark = plannerBenchmarkFactory.buildPlannerBenchmark(); plannerBenchmark.benchmark(); [MINOR] All the classes in the benchmark package have been split-up into a config and a core package. Look for "org.drools.planner.benchmark" in all your files. Normally you won't have any besides the XmlSolverBenchmarker. One rare exception is the use of a build-in solverBenchmarkComparator: Before in *BenchmarkConfig.xml: org.drools.planner.benchmark.WorstScoreSolverBenchmarkComparator After in *BenchmarkConfig.xml: org.drools.planner.benchmark.core.comparator.WorstScoreSolverBenchmarkComparator Before in *BenchmarkConfig.xml: org.drools.planner.benchmark.TotalScoreSolverBenchmarkComparator After in *BenchmarkConfig.xml: org.drools.planner.benchmark.core.comparator.TotalScoreSolverBenchmarkComparator [MAJOR] SolverBenchmarkSuite has been renamed to PlannerBenchmark. Before in *BenchmarkConfig.xml: ... After in *BenchmarkConfig.xml: ... [MAJOR] Benchmarker: has been renamed to and moved under Before in *BenchmarkConfig.xml: data/nqueens/unsolved/unsolvedNQueens32.xml data/nqueens/unsolved/unsolvedNQueens64.xml ... After in *BenchmarkConfig.xml: data/nqueens/unsolved/unsolvedNQueens32.xml data/nqueens/unsolved/unsolvedNQueens64.xml ... [MAJOR] Benchmarker: The XStream to parse the benchmark configuration is now separate from the one to parse the solution files. The xstreamAnnotatedClass is now set in the problemBenchmarks. Note: if you prefer to use a different solution file format, you can now easily hook in one with (see manual). Before in *BenchmarkApp.java: ... plannerBenchmarkFactory.addXstreamAnnotations(NQueens.class); ... After in *BenchmarkApp.java: ... Before in *BenchmarkConfig.xml: data/nqueens/unsolved/unsolvedNQueens32.xml ... After in *BenchmarkConfig.xml: org.drools.planner.examples.nqueens.domain.NQueens data/nqueens/unsolved/unsolvedNQueens32.xml ... [MAJOR] Benchmarker: has been renamed to and moved under (in or ) Before in *BenchmarkConfig.xml: ... BEST_SOLUTION_CHANGED ... ... data/nqueens/unsolved/unsolvedNQueens64.xml BEST_SOLUTION_CHANGED ... ... After in *BenchmarkConfig.xml: ... ... data/nqueens/unsolved/unsolvedNQueens64.xml BEST_SOLUTION_CHANGED ... ... [MINOR] Benchmarker: the solved solution files no longer contain the score the time spend in their filename. See the statistic/index.html file for those numbers. [MINOR] Benchmarker: has been renamed to But it's better not to specify it at all and use the default. Before in *BenchmarkConfig.xml: ... local/data/nurserostering/statistic ... After in *BenchmarkConfig.xml: ... local/data/nurserostering/statistic ... [RECOMMENDED] Rename *SolverBenchmarkConfig.xml to *BenchmarkConfig.xml. This is to avoid confusion with the more fine-grained SolverBenchmark and SolverBenchmarkConfig classes. The examples wrongly named their Benchmark configs for example nqueensSolverBenchmarkConfig.xml instead of nqueensBenchmarkConfig.xml. Before in *BenchmarkApp.java: public static final String DEFAULT_SOLVER_BENCHMARK_CONFIG = "/org/drools/planner/examples/nqueens/benchmark/nqueensSolverBenchmarkConfig.xml"; After in *BenchmarkApp.java: public static final String DEFAULT_BENCHMARK_CONFIG = "/org/drools/planner/examples/nqueens/benchmark/nqueensBenchmarkConfig.xml"; [MINOR] The DeciderScoreComparatorFactory has been moved into the Forager Before in *SolverConfig.xml: NATURAL ... ... After in *SolverConfig.xml: ... NATURAL ... From 5.4.0.Beta2 to 5.4.0.CR1 ----------------------------- [MAJOR] An uninitialized planning entity should be allowed to be put into the working memory without causing a NullPointerException because a planning variable is still null. Before in *.java: @PlanningEntity(...) public class Lecture ... { @PlanningVariable(...) public Period getPeriod() { return period; } ... public Day getDay() { return period.getDay(); } public int getTimeslotIndex() { return period.getTimeslot().getTimeslotIndex(); } ... } After in *.java: @PlanningEntity(...) public class Lecture ... { @PlanningVariable(...) public Period getPeriod() { return period; } ... public Day getDay() { if (period == null) { return null; } return period.getDay(); } public int getTimeslotIndex() { if (period == null) { return Integer.MIN_VALUE; } return period.getTimeslot().getTimeslotIndex(); } ... } [MINOR] AcceptorConfig and ForagerConfig no longer support directly assigning an Acceptor or Forager instance. This breaks using the SolverFactory to build multiple Solver instances and it also breaks XML file serialization of a Solver configuration. Before in *.java: acceptorConfig.setAcceptor(myAcceptor); Before in *.java: foragerConfig.setForager(myForager); After in *.java: Follow the instructions in the manual section "Using a custom Selector, Acceptor, Forager or Termination". You will need to extend the *Config class and the Abtract* class as stipulated in the manual. [MINOR] AcceptorConfig now accepts multiple acceptorClass instances. Solver configuration XML is unchanged. Before in *.java: acceptorConfig.setAcceptorClass(myAcceptorClass); After in *.java: acceptorConfig.setAcceptorClassList(Collections.singletonList(myAcceptorClass)); [MAJOR] All @ValueRange* annotations have been folded into a single @ValueRange annotation. Also, propertyName has been renamed to solutionProperty or planningEntityProperty accordingly. This was needed to support @ValueRanges, a plural annotation of @ValueRange and get around Java's annotation limits. Before in *.java: @PlanningVariable(...) @ValueRangeFromSolutionProperty(propertyName = "roomList") public Room getRoom() {...} After in *.java: @PlanningVariable(...) @ValueRange(type = ValueRangeType.FROM_SOLUTION_PROPERTY, solutionProperty = "roomList") public Room getRoom() {...} Before in *.java: @PlanningVariable(...) @ValueRangeFromPlanningEntityProperty(propertyName = "roomList") public Room getRoom() {...} After in *.java: @PlanningVariable(...) @ValueRange(type = ValueRangeType.FROM_PLANNING_ENTITY_PROPERTY, planningEntityProperty = "roomList") public Room getRoom() {...} Before in *.java: @PlanningVariable(...) @ValueRangeUndefined() public Room getRoom() {...} After in *.java: @PlanningVariable(...) @ValueRange(type = ValueRangeType.UNDEFINED) public Room getRoom() {...} [MAJOR] If you copied the examples GUI's HACKs with DefaultSolverScope, transform them to use the less worse SolutionDirector HACK. But it's still a hack: avoid if you can. An important change is that the GUI and the Solver should never share the same workingSolution instance any more. Also, the Solver implementation has been adjusted so that the workingSolution is a clone of setPlanningProblem(), instead of the bestSolution being the clone of it. Before in *.java: public void setSolver(Solver solver) { this.solver = solver; // TODO HACK Planner internal API: don't do this this.solverScope = ((DefaultSolver) solver).getSolverScope(); } ... public void openSolution(File file) { Solution solution = solutionDao.readSolution(file); solver.setPlanningProblem(solution); } ... public void solve() { solver.solve(); } After in *.java: public void setSolver(Solver solver) { this.solver = solver; // TODO HACK Planner internal API: don't do this this.guiSolutionDirector = ((DefaultSolver) solver).getSolverScope() .getSolutionDirector().cloneWithoutWorkingSolution(); } ... public void openSolution(File file) { Solution solution = solutionDao.readSolution(file); guiSolutionDirector.setWorkingSolution(solution); } ... public void solve() { solver.setPlanningProblem(guiSolutionDirector.getWorkingSolution()); solver.solve(); Solution bestSolution = solver.getBestSolution(); guiSolutionDirector.setWorkingSolution(bestSolution); } Important note: scroll below, this has been further improved during the ScoreDirector refactor. [MAJOR] The interface Move has gotten 2 new methods: getPlanningEntities() and getPlanningValues() The TabuPropertyEnabled interface has been removed: Normally, the method getTabuProperties() is renamed to getPlanningEntities(). Before in *.java: public class CloudComputerChangeMove implements Move, TabuPropertyEnabled { public Collection getTabuProperties() { return Collections.singletonList(cloudProcess); } } After in *.java: public class CloudComputerChangeMove implements Move { public Collection getPlanningEntities() { return Collections.singletonList(cloudProcess); } public Collection getPlanningValues() { return Collections.singletonList(toCloudComputer); } } Before in *.java: public class CloudProcessSwapMove implements Move, TabuPropertyEnabled { public Collection getTabuProperties() { return Arrays.asList(leftCloudProcess, rightCloudProcess); } } After in *.java: public class CloudProcessSwapMove implements Move { public Collection getPlanningEntities() { return Arrays.asList(leftCloudProcess, rightCloudProcess); } public Collection getPlanningValues() { return Arrays.asList(leftCloudProcess.getComputer(), rightCloudProcess.getComputer()); } } [MAJOR] propertyTabu has been renamed to planningEntityTabu (or in some cases planningValueTabu) Before in *SolverConfig.xml: 7 After in *SolverConfig.xml: 7 [MINOR] If you use planningEntityTabuSize and in the method getPlanningEntities() return a collection with a size >= 2, then you might need to retweak your planningEntityTabuSize to a lower number. More info in https://issues.jboss.org/browse/JBRULES-3007 Before in *SolverConfig.xml: 9 After in *SolverConfig.xml: 7 [MINOR] The interface HardAndSoftScore and HardAndSoftLongScore have a new method isFeasible(). If you use a custom score implementation that implements either interface, implement it: After in *HardAndSoftScore.java: public boolean isFeasible() { return getHardScore() >= 0; } [MINOR] SolverPhaseLifecycleListener.stepDecided() has been completely removed. Use stepTaken(...) instead. Before in *.java: public void stepDecided(...) { ... } [MINOR] If you have a custom ScoreCalculator: the clone() method has been removed Before in *ScoreCalculator.java: public ... clone() { ... } [MAJOR] ScoreCalculator has been renamed to ScoreHolder and the DRL global variable scoreCalculator has been renamed to scoreHolder. Before in *.drl: import org.drools.planner.core.score.buildin.hardandsoft.HardAndSoftScoreCalculator; ... global HardAndSoftScoreCalculator scoreCalculator; rule "hardConstraintsBroken" ... when ... then scoreCalculator.setHardConstraintsBroken($hardTotal.intValue()); end rule "softConstraintsBroken" ... when ... then scoreCalculator.setSoftConstraintsBroken($softTotal.intValue()); end After in *.drl: import org.drools.planner.core.score.buildin.hardandsoft.HardAndSoftScoreHolder; ... global HardAndSoftScoreHolder scoreHolder; rule "hardConstraintsBroken" ... when ... then scoreHolder.setHardConstraintsBroken($hardTotal.intValue()); end rule "softConstraintsBroken" ... when ... then scoreHolder.setSoftConstraintsBroken($softTotal.intValue()); end Similarly for SimpleScoreHolder, SimpleDoubleScoreHolder and HardAndSoftLongScoreHolder. [MINOR] If you have a custom ScoreDefinition: The method ScoreDefinition.buildScoreCalculator() has been renamed to buildScoreHolder(). The package org.drools.planner.core.score.calculator has been renamed to org.drools.planner.core.score.holder The method ScoreHolder.calculateScore() has been renamed to extractScore(). Before in *ScoreDefinition.java: import org.drools.planner.core.score.calculator... ... public ScoreHolder buildScoreCalculator() { ... } After in *ScoreDefinition.java: import org.drools.planner.core.score.holder... ... public ScoreHolder buildScoreHolder() { ... } Before in *ScoreHolder.java: public Score calculateScore() {...} After in *ScoreHolder.java: public Score extractScore() {...} [MINOR] SolutionDirector has been renamed to ScoreDirector. [MINOR] ScoreDirectorFactory has been extracted from ScoreDirector. [README] Planner now supports score calculation with plain Java code, as an alternative to using Drools Expert. As a result of this, the Drools Expert specific code has been centralized around the class DroolsScoreDirector and does no longer leak into the rest of Planner's code or into your code. If at any point, you still want to have direct access to WorkingMemory, simply do: ((DroolsScoreDirector) scoreDirector).getWorkingMemory() [MAJOR] Instead of notifying the WorkingMemory directly, changes must notified the ScoreDirector, before and after the entity or problem fact has been added, changed or removed. Migration table: workingMemory.insert(entity) -> beforeEntityAdded(entity) + afterEntityAdded(entity) workingMemory.update(entityHandle, entity) -> beforeVariableChanged(entity, "variableName") + afterVariableChanged(entity, "variableName") or beforeAllVariablesChanged(entity) + afterAllVariablesChanged(entity) workingMemory.retract(entityHandle) -> beforeEntityRemoved(entity) + afterEntityRemoved(entity) workingMemory.insert(problemFact) -> beforeProblemFactAdded(problemFact) + afterProblemFactAdded(problemFact) workingMemory.update(problemFactHandle, problemFact) -> beforeProblemFactChanged(problemFact) + afterProblemFactChanged(problemFact) workingMemory.retract(problemFactHandle) -> beforeProblemFactRemoved(problemFact) + afterProblemFactRemoved(problemFact) Hint: Look for all usages of org.drools.WorkingMemory in your planner related code to find all pieces of code that might be affected by these changes, which are described in detail below. [MAJOR] If you're using custom Move implementations (and don't use the generic move implementations): The class Move's methods with parameter WorkingMemory has been replaced by a parameter ScoreDirector. Before in *Move.java or *MoveHelper.java: public class PeriodChangeMove implements Move { public boolean isMoveDoable(WorkingMemory workingMemory) {...} public Move createUndoMove(WorkingMemory workingMemory) {...} public void doMove(WorkingMemory workingMemory) { FactHandle factHandle = workingMemory.getFactHandle(exam); exam.setPeriod(toPeriod); workingMemory.update(factHandle, exam); } } After in *Move.java or *MoveHelper.java: public class PeriodChangeMove implements Move { public boolean isMoveDoable(ScoreDirector scoreDirector) {...} public Move createUndoMove(ScoreDirector scoreDirector) {...} public void doMove(ScoreDirector scoreDirector) { scoreDirector.beforeVariableChanged(exam, "period"); exam.setPeriod(toPeriod); scoreDirector.afterVariableChanged(exam, "period"); } } Note: if multiple variables are changed at the same time, you might want to use before/afterAllVariablesChanged(): public void doMove(ScoreDirector scoreDirector) { scoreDirector.beforeAllVariablesChanged(exam); exam.setPeriod(toPeriod); exam.setRoom(toRoom); scoreDirector.afterAllVariablesChanged(exam); } [MAJOR] If you use a CustomSolverPhaseCommand or still have a custom SolutionInitializer (and haven't switched to the construction heuristics yet): Before in *SolutionInitializer.java or *.java: public class MrOriginalMachineSolutionInitializer implements CustomSolverPhaseCommand { public void changeWorkingSolution(SolutionDirector solutionDirector) { ... processAssignment.setMachine(processAssignment.getOriginalMachine()); workingMemory.insert(processAssignment); ... } } After in *SolutionInitializer.java or *.java: public class MrOriginalMachineSolutionInitializer implements CustomSolverPhaseCommand { public void changeWorkingSolution(ScoreDirector scoreDirector) { ... scoreDirector.beforeEntityAdded(processAssignment); processAssignment.setMachine(processAssignment.getOriginalMachine()); scoreDirector.afterEntityAdded(processAssignment); ... } } [MAJOR] If you're using real-time planning with ProblemFactChange, the method doProblemFactChange's parameter changed from SolutionDirector to ScoreDirector. Before in *.java: solutionBusiness.doProblemFactChange(new ProblemFactChange() { public void doChange(SolutionDirector solutionDirector) { WorkingMemory workingMemory = solutionDirector.getWorkingMemory(); ... FactHandle computerHandle = workingMemory.getFactHandle(workingComputer); workingMemory.retract(computerHandle); it.remove(); // remove from list ... } }); After in *.java: solutionBusiness.doProblemFactChange(new ProblemFactChange() { public void doChange(ScoreDirector scoreDirector) { ... scoreDirector.beforeProblemFactRemoved(workingComputer); it.remove(); // remove from list scoreDirector.beforeProblemFactRemoved(workingComputer); ... } }); [MAJOR] If you copied the examples GUI's HACKs with DefaultSolverScope, the SolutionDirector.cloneWithoutWorkingSolution() has been removed already, use the ScoreDirectorFactory.buildScoreDirector() method instead. Before in *.java: guiSolutionDirector = ((DefaultSolver) solver).getSolverScope() .getSolutionDirector().cloneWithoutWorkingSolution(); After in *.java: ScoreDirectorFactory scoreDirectorFactory = solver.getScoreDirectorFactory(); guiScoreDirector = scoreDirectorFactory.buildScoreDirector(); // if (guiScoreDirector instanceof DroolsScoreDirector) { // ... = ((DroolsScoreDirector) guiScoreDirector).getWorkingMemory(); // } [MINOR] The method Solver.getScoreDirectorFactory() has been removed. Before in *.java: ... = solver.getScoreDefinition(); After in *.java: ... = solver.getScoreDirectorFactory().getScoreDefinition(); [MAJOR] has been replaced by and has been moved under it. Before in *SolverConfig.xml and *BenchmarkConfig.xml: ... /org/drools/planner/examples/cloudbalancing/solver/cloudBalancingScoreRules.drl HARD_AND_SOFT ... After in *SolverConfig.xml and *BenchmarkConfig.xml: ... HARD_AND_SOFT /org/drools/planner/examples/cloudbalancing/solver/cloudBalancingScoreRules.drl ... If instead, you use API configuration (less likely): Before in *.java: solverConfig.setScoreDrlList( Arrays.asList("/org/drools/planner/examples/nqueens/solver/nQueensScoreRules.drl")); ScoreDefinitionConfig scoreDefinitionConfig = solverConfig.getScoreDefinitionConfig(); scoreDefinitionConfig.setScoreDefinitionType( ScoreDefinitionConfig.ScoreDefinitionType.SIMPLE); After in *.java: ScoreDirectorFactoryConfig scoreDirectorFactoryConfig = solverConfig.getScoreDirectorFactoryConfig(); scoreDirectorFactoryConfig.setScoreDefinitionType(ScoreDirectorFactoryConfig.ScoreDefinitionType.SIMPLE); scoreDirectorFactoryConfig.setScoreDrlList( Arrays.asList("/org/drools/planner/examples/nqueens/solver/nQueensScoreRules.drl")); From 5.4.0.Final to 5.5.0.Beta1 ------------------------------- [MINOR] Benchmarker: each *SolverBenchmarkComparator has been renamed to *SolverBenchmarkRankingComparator Before in *BenchmarkConfig.xml: org.drools.planner.benchmark.core.comparator.TotalScoreSolverBenchmarkComparator After in *BenchmarkConfig.xml: org.drools.planner.benchmark.core.comparator.TotalScoreSolverBenchmarkRankingComparator However, if you use any of the build-in ranking comparisons, it's better to use this in *BenchmarkConfig.xml: TOTAL_SCORE [MINOR] Benchmarker: package org.drools.planner.benchmark.core.comparator has been renamed to ranking Before in *BenchmarkConfig.xml: org.drools.planner.benchmark.core.comparator.TotalScoreSolverBenchmarkRankingComparator After in *BenchmarkConfig.xml: org.drools.planner.benchmark.core.ranking.TotalScoreSolverBenchmarkRankingComparator [MINOR] Benchmarker: PlannerBenchmarkResult has been renamed to SingleBenchmark This should not impact your code. [MAJOR] The benchmarker has moved out of the drools-planner-core jar into the a new drools-planner-benchmark jar. That new maven module drools-planner-benchmark now has a non-optional dependency on jfreechart, so it's no longer needed to explicitly depend on jfreechart in your pom. If you don't use the benchmarker, you don't need to change anything. Before in pom.xml: org.drools.planner drools-planner-core ... org.jfree jfreechart ... After in pom.xml: org.drools.planner drools-planner-core ... org.drools.planner drools-planner-benchmark ... And resync your IDE (IntelliJ, Eclipse, NetBeans) from the pom.xml files. If you're still using ANT, add the drools-planner-benchmark-?.jar and adjust your ANT script and your IDE's classpath accordingly. [MINOR] The benchmarker configuration no longer supports benchmarkInstanceDirectory, outputSolutionFilesDirectory or statisticDirectory. If you were using any of these usefully, let us know how by filing a JIRA issue. The statistics subdirectory has been removed. The index.html file is now available in the benchmarkReportDirectory. The entire benchmarkReportDirectory has been restructured into a nicer form. Before in *BenchmarkConfig.xml: ... Before in *BenchmarkConfig.xml: ... Before in *BenchmarkConfig.xml: ... [MINOR] If you've implemented a custom MoveFactory, Termination, Selector, Acceptor or Forager and if you have overridden lifecycle methods (which is unlikely): 2 lifecycle methods have been renamed. The method beforeDeciding is renamed to stepStarted and the method stepTaken is renamed to stepEnded. Before in *.java: public void beforeDeciding(...) { ... } After in *.java: public void stepStarted(...) { ... } Before in *.java: public void stepTaken(...) { ... } After in *.java: public void stepEnded(...) { ... } [MINOR] If you've implemented a custom BestSolutionRecaller, the method extractBestSolution is renamed to stepEnded. Before in *SolutionRecaller.java: public void extractBestSolution(AbstractStepScope stepScope) {...} Before in *SolutionRecaller.java: public void stepEnded(AbstractStepScope stepScope) {...} [MINOR] ProblemIO has moved from the package org.drools.planner.benchmark.api to the package org.drools.planner.core.solution. Before in *.java: import org.drools.planner.benchmark.api.ProblemIO; After in *.java: import org.drools.planner.core.solution.ProblemIO; [MINOR] Interface IncrementalScoreCalculator has gotten a new method buildScoreCorruptionAnalysis(...) It has a dummy implementation in AbstractIncrementalScoreCalculator, so no changes are needed normally. See manual to optionally implement this method to help you analyze score corruption during environmentMode TRACE. [README] A complete new Selector architecture has been written from scratch. It replaces LocalSearch's Selector (which selected moves) and it will also replace ConstructionHeuristics's Selector. The new Selector architecture supports Just In Time selection, caching (step, phase, solver), filtering, probability selection, selector composition (union, cartesian product) and more. Read about it in the manual. [README] A selector now has a cacheType: JUST_IN_TIME: No caching, generate the moves just before its evaluated. This did not exist before. This is the new default except for MoveListFactory's. STEP: Cache at the beginning of every step. This is the old AbstractMoveFactory. PHASE: Cache at the beginning of every phase. This is the old CachedMoveFactory. SOLVER: Cache at the beginning of the solver run. This did not exist before. All new Selector configurations default to the lowest possible cacheType, which is usually JUST_IN_TIME. JUST_IN_TIME is the preferred cacheType for very big problems, but not for medium or small problems. During the migration, it's recommended to keep the caching STEP or PHASE as before (don't switch to JUST_IN_TIME just yet), but after the migration, it's recommended to play with these settings aggressively in the Benchmarker because it can really improve your results. [README] A selector now has a selectionOrder: ORIGINAL: Select the moves in the default order, not random. This did not really exist before. RANDOM: Select the moves in random order without shuffling. This did not exist before. This is the new default. SHUFFLED: Select the moves in random order with shuffling. This is the old default. During the migration, it's recommended to keep the selectionOrder SHUFFLED as before (don't switch to RANDOM just yet), [MAJOR] The interface MoveFactory has been replaced by the interface MoveListFactory and the configuration has changed. Depending on whether your implementation extended CachedMoveFactory or AbstractMoveFactory, the migration differs. 1) If your implementation extended CachedMoveFactory, you were actually using PHASE caching. Before in *.java: import org.drools.planner.core.move.factory.CachedMoveFactory; public class LectureSwapMoveFactory extends CachedMoveFactory { public List createCachedMoveList(Solution solution) { ... } } After in *.java: import org.drools.planner.core.heuristic.selector.move.factory.MoveListFactory; public class LectureSwapMoveFactory implements MoveListFactory { public List createMoveList(Solution solution) { ... } } Before in *SolverConfig.xml and *BenchmarkConfig.xml: org.drools.planner.examples.curriculumcourse.solver.move.factory.LectureSwapMoveFactory After in *SolverConfig.xml and *BenchmarkConfig.xml: PHASE SHUFFLED org.drools.planner.examples.curriculumcourse.solver.move.factory.LectureSwapMoveFactory 2) If your implementation extended AbstractMoveFactory, you were actually using STEP caching. Before in *.java: import org.drools.planner.core.move.factory.AbstractMoveFactory; public class BedDesignationPillarPartSwapMoveFactory extends AbstractMoveFactory { public List createMoveList(Solution solution) { } } After in *.java: import org.drools.planner.core.heuristic.selector.move.factory.MoveListFactory; public class BedDesignationPillarPartSwapMoveFactory implements MoveListFactory { public List createMoveList(Solution solution) { ... } } Before in *SolverConfig.xml and *BenchmarkConfig.xml: org.drools.planner.examples.pas.solver.move.factory.BedDesignationPillarPartSwapMoveFactory After in *SolverConfig.xml and *BenchmarkConfig.xml: STEP SHUFFLED org.drools.planner.examples.pas.solver.move.factory.BedDesignationPillarPartSwapMoveFactory Note that MoveListFactory cannot really do JUST_IN_TIME caching because by design they create the entire List at the beginning of every step. If you want to use JUST_IN_TIME caching for your custom moves, take a look at MoveIteratorFactory instead (see manual). [MAJOR] The element to group a composition of selectors has been renamed from to . Before in *SolverConfig.xml and *BenchmarkConfig.xml: ...BedDesignationPillarPartSwapMoveFactory ...GenericChangeMoveFactory ... ... After in *SolverConfig.xml and *BenchmarkConfig.xml: PHASE SHUFFLED ...BedDesignationPillarPartSwapMoveFactory PHASE SHUFFLED ... ... [MAJOR] The generic MoveFactory classes have been replaced by generic MoveSelector implementations. The generic MoveFactories used to use PHASE or STEP caching (depending on the implementation), but now the generic MoveSelector implementations default to cacheType JUST_IN_TIME and selectionOrder RANDOM. To make no additional changes during the migration, overwrite to cacheType PHASE (or STEP) and selectionOrder SHUFFLED. If you have multiple planning entities or multiple planning variables, you will often need to create separate selectors and union them. Before in *SolverConfig.xml and *BenchmarkConfig.xml: org.drools.planner.core.move.generic.GenericChangeMoveFactory After in *SolverConfig.xml and *BenchmarkConfig.xml: PHASE SHUFFLED After in *SolverConfig.xml and *BenchmarkConfig.xml if you have multiple planning variables: PHASE SHUFFLED period room After in *SolverConfig.xml and *BenchmarkConfig.xml if you have multiple planning entities and multiple planning variables: PHASE SHUFFLED ...Cat ...Dog kennel ...Dog toy Before in *SolverConfig.xml and *BenchmarkConfig.xml: org.drools.planner.core.move.generic.GenericSwapMoveFactory After in *SolverConfig.xml and *BenchmarkConfig.xml: PHASE SHUFFLED Before in *SolverConfig.xml and *BenchmarkConfig.xml: org.drools.planner.core.move.generic.GenericSwapPillarMoveFactory After in *SolverConfig.xml and *BenchmarkConfig.xml: STEP SHUFFLED Before in *SolverConfig.xml and *BenchmarkConfig.xml: org.drools.planner.core.move.generic.GenericChainedChangeMoveFactory After in *SolverConfig.xml and *BenchmarkConfig.xml: PHASE SHUFFLED Before in *SolverConfig.xml and *BenchmarkConfig.xml: org.drools.planner.core.move.generic.GenericChainedSwapMoveFactory After in *SolverConfig.xml and *BenchmarkConfig.xml: PHASE SHUFFLED Before in *SolverConfig.xml and *BenchmarkConfig.xml: org.drools.planner.core.move.generic.GenericChainedChangePartMoveFactory After in *SolverConfig.xml and *BenchmarkConfig.xml: STEP SHUFFLED [MINOR] topSize is not longer supported If you used this undocumented feature anyway, vote for https://issues.jboss.org/browse/JBRULES-3598 Before in *SolverConfig.xml and *BenchmarkConfig.xml: ... [MINOR] If you were using a custom, old style Selector, you 'll have to rewrite it in the new Selector architecture. For more information, ask on the user mailing list. [MINOR] partialTabu has been renamed to fadingTabu Before in *SolverConfig.xml and *BenchmarkConfig.xml: ... After in *SolverConfig.xml and *BenchmarkConfig.xml: ... Before in *SolverConfig.xml and *BenchmarkConfig.xml: ... After in *SolverConfig.xml and *BenchmarkConfig.xml: ... Before in *SolverConfig.xml and *BenchmarkConfig.xml: ... After in *SolverConfig.xml and *BenchmarkConfig.xml: ... Before in *SolverConfig.xml and *BenchmarkConfig.xml: ... After in *SolverConfig.xml and *BenchmarkConfig.xml: ... Before in *SolverConfig.xml and *BenchmarkConfig.xml: ... After in *SolverConfig.xml and *BenchmarkConfig.xml: ... [MINOR] If you have a custom Score implementation, the toDoubleArray() method has been renamed to toDoubleLevels(). Before in *Score.java: public double[] toDoubleArray() { ... } After in *Score.java: public double[] toDoubleLevels() { ... } [MINOR] If you have a custom ScoreDefinition implementation, the method translateScoreToGraphValue(Score) has been removed. The benchmarker report now shows a graph for each score level. Before in *ScoreDefinition.java: public Double translateScoreToGraphValue(SimpleScore score) { return Double.valueOf(score.getScore()); } From 5.5.0.Beta1 to 5.5.0.CR1 ----------------------------- [MINOR] There are several changes to the scopes. None of these should impact your code. - MoveScope has been renamed to LocalSearchMoveScope - All *Scope classes have been moved into a subpackage scope - The methods get(LocalSearch|GreedyFit|BruteForce|Custom)*Scope() have been renamed to get*Scope() [MAJOR] By default, the benchmarker no longer writes the outputSolution of the benchmarker runs to disk. Those solution files are usually bloat in practice. To write them anyway: After in *BenchmarkConfig.xml: ... true ... From 5.5.0.Final to 6.0.0.Alpha7 -------------------------------- [README] Starting from 6.1.0.Final for 6.2, .6.3, ..., planner will have a backwards compatible API. As preparation for that, every part of the API is being reviewed. [RECOMMENDED] Don't directly upgrade the jars to 6.0.0.Final. In 6.0.0.Beta1, Drools Planner has been renamed to OptaPlanner and the java package structure has been changed severely. Instead, upgrade the jars to 6.0.0.Alpha9 first. Once that compiles, immediately upgrade the jars to your desired version and continue following the upgrade recipe. The 6.0.0.Alpha9 jars are available in the maven repository and in this zip: http://download.jboss.org/drools/release/6.0.0.Alpha9/drools-planner-distribution-6.0.0.Alpha9.zip [README] ValueRangeType.FROM_PLANNING_ENTITY_PROPERTY now works. The regression in 5.5 from 5.4 has been fixed. [RECOMMENDED] Planning variables can now have a null value when initialized. If you have any custom hacks for that, take a look at the reference manual how to replace them with a nullable variable. [MINOR] no longer needs a : it inherits it from its parent. [MAJOR] If you are using Drools and HardAndSoft*Score: ConstraintType no longer has NEGATIVE_HARD, NEGATIVE_SOFT and POSITIVE. It only has HARD and SOFT now. As a result, all negative weights are now a negative number. All positive weights are now a positive number. HardAndSoft*ScoreHolder replaced setHardConstraintsBroken() and setSoftConstraintsBroken() with setHardScore() and setSoftScore(). Before in *ScoreRules.drl: insertLogical(new IntConstraintOccurrence(..., ConstraintType.NEGATIVE_HARD, $x, ...)); After in *ScoreRules.drl: insertLogical(new IntConstraintOccurrence(..., ConstraintType.HARD, - $x, ...)); Before in *ScoreRules.drl: insertLogical(new IntConstraintOccurrence(..., ConstraintType.NEGATIVE_SOFT, $x, ...)); After in *ScoreRules.drl: insertLogical(new IntConstraintOccurrence(..., ConstraintType.SOFT, - $x, ...)); Before in *ScoreRules.drl: insertLogical(new IntConstraintOccurrence(..., ConstraintType.POSITIVE, - $x, ...)); After in *ScoreRules.drl: insertLogical(new IntConstraintOccurrence(..., ConstraintType.SOFT, $x, ...)); Before in *ScoreRules.drl: rule "hardConstraintsBroken" salience -1 when $hardTotal : Number() from accumulate( IntConstraintOccurrence(constraintType == ConstraintType.NEGATIVE_HARD, $weight : weight), sum($weight) ) then scoreHolder.setHardConstraintsBroken($hardTotal.intValue()); end rule "softConstraintsBroken" salience -1 when $softTotal : Number() from accumulate( IntConstraintOccurrence(constraintType == ConstraintType.NEGATIVE_SOFT, $weight : weight), sum($weight) ) then scoreHolder.setSoftConstraintsBroken($softTotal.intValue()); end After in *ScoreRules.drl: rule "accumulateHardScore" salience -1 when $hardTotal : Number() from accumulate( IntConstraintOccurrence(constraintType == ConstraintType.HARD, $weight : weight), sum($weight) ) then scoreHolder.setHardScore($hardTotal.intValue()); end rule "accumulateSoftScore" salience -1 when $softTotal : Number() from accumulate( IntConstraintOccurrence(constraintType == ConstraintType.SOFT, $weight : weight), sum($weight) ) then scoreHolder.setSoftScore($softTotal.intValue()); end Refactor similarly for LongConstraintOccurrence and longValue(). [MAJOR] Your Solution implementation class now needs a @PlanningSolution annotation Before in *.java: public class ... implements Solution<...Score> {...} After in *.java: @PlanningSolution public class ... implements Solution<...Score> {...} [MAJOR] A Solution no longer need to implement the method cloneSolution(Solution) Before in *.java: @PlanningSolution public class Examination implements Solution<...> { ... public Examination cloneSolution() { Examination clone = new Examination(); ... for (Exam exam : examList) { Exam clonedExam = exam.clone(); ... } ... return clone; } } public class Exam { ... public Exam clone() { Exam clone = new Exam(); ... return clone; } } After in *.java, option 1: if you want to use the automatic cloning system: @PlanningSolution public class Examination implements Solution<...> { ... } public class Exam { ... } After in *.java, option 2: if you want keep your code: @PlanningSolution public class Examination implements Solution<...>, PlanningCloneable { ... public Examination planningClone() { Examination clone = new Examination(); ... for (Exam exam : examList) { Exam clonedExam = exam.planningClone(); ... } ... return clone; } } public class Exam implements PlanningCloneable { ... public Exam planningClone() { Exam clone = new Exam(); ... return clone; } } [MINOR] If you've defined a custom ScoreDefinition to be able to use 3 score levels of int's, consider using the new HardMediumSoftScoreDefinition instead. Before in *SolverConfig.xml and *BenchmarkConfig.xml: ... After in *SolverConfig.xml and *BenchmarkConfig.xml: HARD_MEDIUM_SOFT [MAJOR] The construction heuristics now immediately add all uninitialized entities into the ScoreDirector (so into the WorkingMemory), instead of adding each entity just before initializing it. This change improves consistency and compatibility of construction heuristics with multiple variables, because they can now initialize 1 variable at a time instead of all variables. Also, this change improves support for nullable variables. Unfortunately, this means that in your score rules you might need to add a few or a lot of null checks, which can be annoying if you have a lot of score rules. If you don't use the constructions heuristics, no changes are required, but they are still highly recommended. Note: even though your DRL code already needed to be planning variable null-safe (since 5.5), these changes are different and still needed. No changes needed in *ScoreRules.drl: when $computer : CloudComputer(...) // $computer is never null ... : Number(...) from accumulate( CloudProcess(computer == $computer, ...), // no null check needed on computer sum(...) ) then No changes needed in *ScoreRules.drl: when $room : Room(...) // $room is never null $lecture : Lecture(room == $room, ...) // no null check needed on room + period is not (in)directly used then Before in *ScoreRules.drl: when ... $bedDesignation : BedDesignation(..., $room : room) // room uses bed indirectly: room is null if bed is null ... then After in *ScoreRules.drl: when ... $bedDesignation : BedDesignation(..., $room : room, bed != null) ... then Before in *ScoreRules.drl: when $leftLecture : Lecture(..., $period : period, $room : room) // null check needed on period and room $rightLecture : Lecture(period == $period, room == $room, ...) then After in *ScoreRules.drl: when $leftLecture : Lecture(..., period != null, $period : period, room != null, $room : room) $rightLecture : Lecture(period == $period, room == $room, ...) // no null check needed on period and room then [MINOR] @PlanningVariable's property uninitializedEntityFilter has been renamed to reinitializeVariableEntityFilter Before in *.java: @PlanningVariable(uninitializedEntityFilter = ...) After in *.java: @PlanningVariable(reinitializeVariableEntityFilter = ...) [MINOR] The Default*Score class have become more encapsulated. In the unlikely case that you've used any of them directly, do these changes: Before in *.java: ... = DefaultHardAndSoftScore.valueOf(-100); After in *.java: ... = DefaultHardAndSoftScore.valueOf(-100, Integer.MIN_VALUE); Before in *.java: ... = new DefaultHardAndSoftScore(-100, -20); After in *.java: ... = DefaultHardAndSoftScore.valueOf(-100, -20); [MAJOR] The interface PlanningEntityDifficultyWeightFactory has been replaced by SelectionSorterWeightFactory. The method createDifficultyWeight has been replaced by the method createSorterWeight. Sorting direction (difficulty ascending) has not changed. Before in *.java: public class QueenDifficultyWeightFactory implements PlanningEntityDifficultyWeightFactory { public Comparable createDifficultyWeight(Solution solution, Object planningEntity) { NQueens nQueens = (NQueens) solution; Queen queen = (Queen) planningEntity; ... return ...; } ... } After in *.java: public class QueenDifficultyWeightFactory implements SelectionSorterWeightFactory { public Comparable createSorterWeight(NQueens nQueens, Queen queen) { ... return ...; } ... } [MAJOR] The interface PlanningValueStrengthWeightFactory has been replaced by SelectionSorterWeightFactory. The method createStrengthWeight has been replaced by the method createSorterWeight. Sorting direction (strength ascending) has not changed. Before in *.java: public class RowStrengthWeightFactory implements PlanningValueStrengthWeightFactory { public Comparable createStrengthWeight(Solution solution, Object planningValue) { NQueens nQueens = (NQueens) solution; Row row = (Queen) planningValue; ... return ...; } ... } After in *.java: public class RowStrengthWeightFactory implements SelectionSorterWeightFactory { public Comparable createSorterWeight(NQueens nQueens, Row row) { ... return ...; } ... } [MAJOR] The EnvironmentMode DEBUG and TRACE have been renamed to FAST_ASSERT and FULL_ASSERT to avoid confusion with the logging levels debug and trace. Before in *SolverConfig.xml and *BenchmarkConfig.xml: DEBUG After in *SolverConfig.xml and *BenchmarkConfig.xml: FAST_ASSERT Before in *SolverConfig.xml and *BenchmarkConfig.xml: TRACE After in *SolverConfig.xml and *BenchmarkConfig.xml: FULL_ASSERT [MAJOR] Configuration property entityFilterClass has been renamed to filterClass. Before in *SolverConfig.xml and *BenchmarkConfig.xml: ... After in *SolverConfig.xml and *BenchmarkConfig.xml: ... [MAJOR] Configuration property moveFilterClass has been renamed to filterClass. Before in *SolverConfig.xml and *BenchmarkConfig.xml: <...MoveSelector> ... After in *SolverConfig.xml and *BenchmarkConfig.xml: <...MoveSelector> ... [MINOR] Configuration property entityProbabilityWeightFactoryClass has been renamed to probabilityWeightFactoryClass. Before in *SolverConfig.xml and *BenchmarkConfig.xml: ... After in *SolverConfig.xml and *BenchmarkConfig.xml: ... [MINOR] Configuration property valueProbabilityWeightFactoryClass has been renamed to probabilityWeightFactoryClass. Before in *SolverConfig.xml and *BenchmarkConfig.xml: ... After in *SolverConfig.xml and *BenchmarkConfig.xml: ... [MINOR] Configuration property moveProbabilityWeightFactoryClass has been renamed to probabilityWeightFactoryClass. Before in *SolverConfig.xml and *BenchmarkConfig.xml: <...MoveSelector> ... After in *SolverConfig.xml and *BenchmarkConfig.xml: <...MoveSelector> ... [MINOR] For entitySelector, configuration property planningEntityClass has been renamed to entityClass. Before in *SolverConfig.xml and *BenchmarkConfig.xml: ...Lecture After in *SolverConfig.xml and *BenchmarkConfig.xml: ...Lecture [MAJOR] Configuration property planningVariableName has been renamed to variableName. Before in *SolverConfig.xml and *BenchmarkConfig.xml: period After in *SolverConfig.xml and *BenchmarkConfig.xml: period [MAJOR] HardAndSoftScore has been renamed to HardSoftScore. Similarly, HardAndSoftLongScore has been renamed to HardSoftLongScore. The package, ScoreDefinitionType, *ScoreDefinition and *ScoreDefinition have been renamed accordingly. Before in *SolverConfig.xml and *BenchmarkConfig.xml: HARD_AND_SOFT After in *SolverConfig.xml and *BenchmarkConfig.xml: HARD_SOFT Before in *.java: import org.drools.planner.core.score.buildin.hardandsoft.HardAndSoftScore; ... public class CloudBalance ... implements Solution { ... private HardAndSoftScore score; public HardAndSoftScore getScore() { return score; } public void setScore(HardAndSoftScore score) { this.score = score; } } After in *.java: import org.drools.planner.core.score.buildin.hardsoft.HardSoftScore; ... public class CloudBalance ... implements Solution { ... private HardSoftScore score; public HardSoftScore getScore() { return score; } public void setScore(HardSoftScore score) { this.score = score; } } Before in *.drl: import org.drools.planner.core.score.buildin.hardandsoft.HardAndSoftScoreHolder; global HardAndSoftScoreHolder scoreHolder; After in *.drl: import org.drools.planner.core.score.buildin.hardsoft.HardSoftScoreHolder; global HardSoftScoreHolder scoreHolder; Before in *ScoreCalculator.java: public HardAndSoftScore calculateScore() { return DefaultHardAndSoftScore.valueOf(hardScore, softScore); } After in *ScoreCalculator.java: public HardSoftScore calculateScore() { return DefaultHardSoftScore.valueOf(hardScore, softScore); } [MAJOR] The Default*Score classes have been removed: they have been merged into their *Score interface. Before in *.java: ... DefaultSimpleScore.valueOf(...) ... DefaultSimpleDoubleScore.valueOf(...) ... DefaultHardSoftScore.valueOf(...) ... DefaultHardSoftLongScore.valueOf(...) ... DefaultHardMediumSoftScore.valueOf(...) After in *.java: ... SimpleScore.valueOf(...) ... SimpleDoubleScore.valueOf(...) ... HardSoftScore.valueOf(...) ... HardSoftLongScore.valueOf(...) ... HardMediumSoftScore.valueOf(...) [RECOMMENDED] If you store your solutions as XML and reused the example's XStream serialization technique, you probably want to have a score serialized as such: 0hard/-130870soft instead of the current way (which writes the full classname of the Score implementation in the XML). Before in *.java: public class CloudBalance ... implements Solution { ... private SimpleScore score; ... } After in *.java: public class NQueens ... implements Solution { ... @XStreamConverter(value = XStreamScoreConverter.class, types = {SimpleScoreDefinition.class}) private SimpleScore score; ... } Before in *.java: public class CloudBalance ... implements Solution { ... private HardSoftScore score; ... } After in *.java: public class CloudBalance ... implements Solution { ... @XStreamConverter(value = XStreamScoreConverter.class, types = {HardSoftScoreDefinition.class}) private HardSoftScore score; ... } [MAJOR] If you store your solutions as XML and reused the example's XStream serialization technique, then you'll also need to change all those xml files which mention the full score class name. Here's a bash script to automate that change to the new @XStreamConverter way in the recommended change above: for f in *.xml do sed 's/ $f.modifiedMigration sed ':a;N;$!ba;s/>\n */>/g' $f.modifiedMigration | sed ':a;N;$!ba;s/<\/hardScore>\n */hard\//g' | sed ':a;N;$!ba;s/<\/softScore>\n *<\/score>/soft<\/score>/g' > $f rm -f $f.modifiedMigration done [MINOR] Interface ScoreDefinition has a new method formatScore(Score). It's implemented by default by AbstractScoreDefinition to use Score.toString(). [MINOR] XStreamProblemIO has moved package Before in *.java: import org.drools.planner.benchmark.core.XStreamProblemIO; After in *.java: import org.drools.planner.persistence.xstream.XStreamProblemIO; [MAJOR] ValueRange and ValueRangeType have moved package Before in *.java: import org.drools.planner.api.domain.variable.ValueRange; import org.drools.planner.api.domain.variable.ValueRangeType; After in *.java: import org.drools.planner.api.domain.value.ValueRange; import org.drools.planner.api.domain.value.ValueRangeType; [MAJOR] Interface ScoreDefinition has a new method getScoreClass(). After in *ScoreDefinition.java: public Class getScoreClass() { return HardSoftScore.class; } From 6.0.0.Alpha7 to 6.0.0.Alpha8 --------------------------------- [MINOR] If you're using solver configuration by API (instead of XML): the Config properties are now null by default. Before in *.java: TerminationConfig terminationConfig = solverConfig.getTerminationConfig(); After in *.java: TerminationConfig terminationConfig = new TerminationConfig(); solverConfig.setTerminationConfig(terminationConfig); Generally this applies to ScoreDirectorFactoryConfig, AcceptorConfig, ForagerConfig, EntitySelectorConfig, ValueSelectorConfig, ...: Before in *.java: FooConfig fooConfig = ...Config.getFooConfig(); After in *.java: FooConfig fooConfig = new FooConfig(); ...Config.setFooConfig(fooConfig); [MINOR] XML solver configuration: ScoreDirectorFactoryConfig no longer supports setScoreDefinition(...). Everyone used setScoreDefinitionClass(...) instead. Before in *ScoreDefinition.java: scoreDirectorFactoryConfig.setScoreDefinition(...); [MINOR] XML solver configuration: ScoreDirectorFactoryConfig no longer supports setSimpleScoreCalculator(...). Everyone used setSimpleScoreCalculatorClass(...) instead, except maybe for score weighting parametrization, which can be done through the Solution (which enables real-time tweaking), see the InstitutionParametrization in the examples. Before in *ScoreDefinition.java: scoreDirectorFactoryConfig.setSimpleScoreCalculator(...); From 6.0.0.Alpha9 to 6.0.0.Beta1 -------------------------------- [README] Drools Planner has been renamed to OptaPlanner. See the official announcement here: http://www.optaplanner.org/community/droolsPlannerRenamed.html [MAJOR] The maven dependencies their groupId and artifactId's have been renamed. Before in pom.xml: org.drools.planner drools-planner-core ... After in pom.xml: org.optaplanner optaplanner-core ... Before in pom.xml: org.drools.planner drools-planner-benchmark ... After in pom.xml: org.optaplanner optaplanner-benchmark ... And resync your IDE (IntelliJ, Eclipse, NetBeans) from the pom.xml files. If you're still using ANT, replace drools-planner-*.jar with optaplanner-*.jar and adjust your ANT script and your IDE's classpath accordingly. Note: because of package name changes (see below), you 'll get a lot of compile errors at this point. [MAJOR] The logging category org.drools.planner has been renamed to org.optaplanner Before in logback*.xml: After in logback*.xml: Similar for log4j files. [MINOR] The github repository has been renamed. Before: https://github.com/droolsjbpm/drools-planner After: https://github.com/droolsjbpm/optaplanner [MAJOR] The package org.drools.planner has been renamed to org.optaplanner Before in *.java, *.drl: import org.drools.planner... After in *.java, *.drl: import org.optaplanner... Before in *.java: "/org/drools/planner/..." After in *.java: "/org/optaplanner/..." Before in *.xml: <...>org.drools.planner... After in *.xml: <...>org.optaplanner... Note: because of other package name changes (see below), you 'll get a lot of compile errors after these changes. [README] The packages now make a clear distinction between api, config and implementation classes. Starting from 6.1 for future versions (6.2, 6.3, ...): The api namespaced classes will be backwards compatible. The config namespaced classes will be backwards compatible on an XML level only. The impl namespaced classes will NOT be backwards compatible. Also, each artifact now has a unique package namespace. For example: optaplanner-core*.jar: org.optaplanner.core: this package contains all classes from this jar .api .config .impl optaplanner-benchmark*.jar: org.optaplanner.benchmark: this package contains all classes from this jar .api .config .impl [MAJOR] The package org.optaplanner.core has been renamed to org.optaplanner.core.impl Before in *.java, *.drl: import org.optaplanner.core... After in *.java, *.drl: import org.optaplanner.core.impl... Before in *.java: "/org/optaplanner/core/..." After in *.java: "/org/optaplanner/core/impl/..." Before in *.xml: <...>org.optaplanner.core... After in *.xml: <...>org.optaplanner.core.impl... [MAJOR] The package org.optaplanner.api has been renamed to org.optaplanner.core.api Before in *.java, *.drl: import org.optaplanner.api... After in *.java, *.drl: import org.optaplanner.core.api... Before in *.java: "/org/optaplanner/api/..." After in *.java: "/org/optaplanner/core/api/..." Before in *.xml: <...>org.optaplanner.api... After in *.xml: <...>org.optaplanner.core.api... [MAJOR] The package org.optaplanner.config has been renamed to org.optaplanner.core.config Before in *.java, *.drl: import org.optaplanner.config... After in *.java, *.drl: import org.optaplanner.core.config... Before in *.java: "/org/optaplanner/config/..." After in *.java: "/org/optaplanner/core/config/..." Before in *.xml: <...>org.optaplanner.config... After in *.xml: <...>org.optaplanner.core.config... [MINOR] The package org.optaplanner.benchmark.core has been renamed to org.optaplanner.benchmark.impl Before in *.java, *.drl: import org.optaplanner.benchmark.core... After in *.java, *.drl: import org.optaplanner.benchmark.impl... [MAJOR] The interface Solver has been moved from the package ...core.impl to ...core.api.solver Before in *.java: import org.optaplanner.core.impl.Solver; After in *.java: import org.optaplanner.core.api.solver.Solver; [MAJOR] The interface SolverFactory has been moved from the package ...core.config to ...core.api.solver Before in *.java: import org.optaplanner.core.config.SolverFactory; After in *.java: import org.optaplanner.core.api.solver.SolverFactory; [MAJOR] The classes EnvironmentMode and XmlSolverFactory has been moved from the package ...core.config to ...core.config.solver Before in *.java: import org.optaplanner.core.config.EnvironmentMode; import org.optaplanner.core.config.XmlSolverFactory; After in *.java: import org.optaplanner.core.config.solver.EnvironmentMode; import org.optaplanner.core.config.solver.XmlSolverFactory; [RECOMMENDED] Use the interface PlannerBenchmarkFactory in favor of XmlPlannerBenchmarkFactory Before in *.java: XmlPlannerBenchmarkFactory plannerBenchmarkFactory = new XmlPlannerBenchmarkFactory("...BenchmarkConfig.xml"); After in *.java: PlannerBenchmarkFactory plannerBenchmarkFactory = new XmlPlannerBenchmarkFactory("...BenchmarkConfig.xml"); [MAJOR] The interfaces Score and ScoreHolder and their subclasses have been promoted from the impl to the api package. Before in *.java and *.drl: import org.optaplanner.core.impl.score...Score; import org.optaplanner.core.impl.score...SimpleScore; import org.optaplanner.core.impl.score...HardAndSoftScore; ... After in *.java and *.drl: import org.optaplanner.core.api.score...Score; import org.optaplanner.core.api.score...SimpleScore; import org.optaplanner.core.api.score...HardAndSoftScore; ... Before in *.java and *.drl: import org.optaplanner.core.impl.score...ScoreHolder; import org.optaplanner.core.impl.score...SimpleScoreHolder; import org.optaplanner.core.impl.score...HardAndSoftScoreHolder; ... After in *.java and *.drl: import org.optaplanner.core.api.score...ScoreHolder; import org.optaplanner.core.api.score...SimpleScoreHolder; import org.optaplanner.core.api.score...HardAndSoftScoreHolder; ... Note: ScoreDefinition has not been promoted (yet), even though you might use that in @XStreamConverter. Note: ConstraintOccurrence hasn't been promoted yet, even though you use it in the drl files. [README] Planner has upgraded from the drools 4 RuleBase API to the drools 6 KieBase API. It has skipped the Drools 5 KnowledgeBase API. [MAJOR] ScoreDirectorFactoryConfig's method setRuleBase() has been replaced by setKieBase() Before in *.java: RuleBase ruleBase = ...; solverFactory.getSolverConfig().getScoreDirectorFactoryConfig.setRuleBase(ruleBase); After in *.java: KieBase kieBase = ...; solverFactory.getSolverConfig().getScoreDirectorFactoryConfig.setKieBase(kieBase); [MINOR] If you used the hack from the examples to extract the ConstraintOccurrences from the guiScoreDirectory. Before in *.java: import org.drools.core.ClassObjectFilter; import org.drools.core.WorkingMemory; ... WorkingMemory workingMemory = ((DroolsScoreDirector) guiScoreDirector).getWorkingMemory(); if (workingMemory == null) { return Collections.emptyList(); } Iterator it = (Iterator) workingMemory.iterateObjects( new ClassObjectFilter(ConstraintOccurrence.class)); while (it.hasNext()) { ConstraintOccurrence constraintOccurrence = it.next(); ... } After in *.java: import org.kie.api.runtime.ClassObjectFilter; import org.kie.api.runtime.KieSession; ... KieSession kieSession = ((DroolsScoreDirector) guiScoreDirector).getKieSession(); if (kieSession == null) { return Collections.emptyList(); } Collection constraintOccurrences = (Collection) kieSession.getObjects(new ClassObjectFilter(ConstraintOccurrence.class)); for (ConstraintOccurrence constraintOccurrence : constraintOccurrences) { ... } [README] In score DRL's, the insertLogical ConstraintOccurrence technique has been replaced with the ConstraintMatch technique. That new technique is much faster (see blog for details), easier to use and far less error-prone. Unlike ConstraintOccurrence, ConstraintMatch doesn't care about the equals/hashcode implementations of your classes. Also, the ConstraintMatch and ConstraintMatchTotal instances are designed to be reused outside Planner. [MAJOR] Change your scoreDrl's to use the ConstraintMatch technique. Instead of doing an insertLogical of a ConstraintOccurrence, they now call scoreHolder.add*ConstraintMatch() and no longer need to supply the infamous causes parameter. In the DRL, the LHS (= when parts) remain unchanged: only the RHS (= then parts) change and the accumulate*Score rules are removed. First, backup the old DRL, so you can easily verify that the new DRL works exactly the same as the old one: cp cloudBalancingScoreRules.drl cloudBalancingScoreRulesOld.drl Before in *ScoreRules.drl, to delete: import org.optaplanner.core.impl.score.constraint.IntConstraintOccurrence; import org.optaplanner.core.impl.score.constraint.ConstraintType; ... rule "accumulateHardScore" salience -1 when $hardTotal : Number() from accumulate( IntConstraintOccurrence(constraintType == ConstraintType.HARD, $weight : weight), sum($weight) ) then scoreHolder.setHardScore($hardTotal.intValue()); end rule "accumulateSoftScore" ... end Before in *ScoreRules.drl (hard constraints): rule "conflictingLecturesSameCourseInSamePeriod" when ... then insertLogical(new IntConstraintOccurrence("conflictingLecturesSameCourseInSamePeriod", ConstraintType.HARD, -1, $leftLecture, $rightLecture)); end After in *ScoreRules.drl: rule "conflictingLecturesSameCourseInSamePeriod" when ... then scoreHolder.addHardConstraintMatch(kcontext, -1); end Before in *ScoreRules.drl (soft constraints): rule "computerCost" when ... then insertLogical(new IntConstraintOccurrence("computerCost", ConstraintType.SOFT, - $cost, $computer)); end After in *ScoreRules.drl: rule "computerCost" when ... then scoreHolder.addSoftConstraintMatch(kcontext, - $cost); end Before in *ScoreRules.drl (SimpleScore): rule "multipleQueensHorizontal" when ... then insertLogical(new UnweightedConstraintOccurrence("multipleQueensHorizontal", $q1, $q2)); end After in *ScoreRules.drl: rule "multipleQueensHorizontal" when ... then scoreHolder.addConstraintMatch(kcontext, -1); end Note: kcontext is a magic variable name automatically made available by Drools Expert in the RHS (= then part). Note: The causes array parameter (for example $leftLecture, $rightLecture, $computer) is gone, because it is automatically deduced from kcontext. WARNING: Because ConstraintMatch doesn't do an insertLogical, nor does it depends on the equals/hashcode of your objects, there is a small chance that the ConstraintOccurrence counted a lower score (often unintentional by the author). To detect this uncomment this code in *SolverConfig.xml to verify that the new DRL works exactly the same as the old one: ... ... ...ScoreRules.drl In 6.0 (not in 6.1), the score corruption analysis helps to identify the rule which behaves differently. If it fails, it is because in the old way, 2 or more different fire events of a score rule inserted equal ConstraintOccurrences. In the new way, every fire event of a score rule adds a unique ConstraintMatch (there's a 1 to 1 relationship - which is expected by most users anyway). IMPORTANT: The class ConstraintOccurrence will be removed in 6.1.0.Final, so switch to ConstraintMatch now. The only reason why ConstraintOccurrence has not been removed already, is to make the migration easier: so you can easily verify that after migration to ConstraintMatch, it gives the exact same scores, but faster. NOTE: The examples still include their old drl variant too until 6.0.0.Beta4, if you want to use it for comparison. [MINOR] If you use ConstraintOccurrence outside of Planner itself, in the gui or your middleware, for example, to show the user the constraint matches, switch to using ConstraintMatch and ConstraintMatchTotal instead. Before in *.java: public List getScoreDetailList() { if (!(guiScoreDirector instanceof DroolsScoreDirector)) { return null; } Map scoreDetailMap = new HashMap(); KieSession kieSession = ((DroolsScoreDirector) guiScoreDirector).getKieSession(); if (kieSession == null) { return Collections.emptyList(); } Collection constraintOccurrences = (Collection) kieSession.getObjects(new ClassObjectFilter(ConstraintOccurrence.class)); for (ConstraintOccurrence constraintOccurrence : constraintOccurrences) { ScoreDetail scoreDetail = scoreDetailMap.get(constraintOccurrence.getRuleId()); if (scoreDetail == null) { scoreDetail = new ScoreDetail(constraintOccurrence.getRuleId(), constraintOccurrence.getConstraintType()); scoreDetailMap.put(constraintOccurrence.getRuleId(), scoreDetail); } scoreDetail.addConstraintOccurrence(constraintOccurrence); } List scoreDetailList = new ArrayList(scoreDetailMap.values()); Collections.sort(scoreDetailList); return scoreDetailList; After in *.java: public List getConstraintMatchTotalList() { List constraintMatchTotalList = new ArrayList( guiScoreDirector.getConstraintMatchTotals()); Collections.sort(constraintMatchTotalList); return constraintMatchTotalList; } Before in *.java: ... constraintOccurrence.getCauses() After in *.java: ... constraintMatch.getJustificationList() Note: the justificationList might have more or different elements than the causes, but it should be possible to extract the same information. [MINOR] If you use a DRL query to extract the ConstraintOccurrence, use ConstraintMatch and ConstraintMatchTotal instead. Before in *.drl: query "selectAllBrokenRules" $brokenRule : IntConstraintOccurrence(constraintType == ConstraintType.HARD) end After in *.java: guiScoreDirector.getConstraintMatchTotals() [MINOR] If you have a custom ScoreDefinition implementation: the method buildScoreHolder() has changed signature. Before in *ScoreDefinition.java: public ScoreHolder buildScoreHolder() { return new HardSoftScoreHolder(); } After in *ScoreDefinition.java: public ScoreHolder buildScoreHolder(boolean constraintMatchEnabled) { return new HardSoftScoreHolder(constraintMatchEnabled); } 6.0 supports a bunch more score types: it's easier (as well as recommended) to switch to a build-in one if you can. From 6.0.0.Beta1 to 6.0.0.Beta2 ------------------------------- [MINOR] optaplanner-examples: The class XStreamSolutionDaoImpl has been renamed to XStreamSolutionDao. This should not affect you because you should not be depending on optaplanner-examples. Before in *.java: public class ...DaoImpl extends XStreamSolutionDaoImpl {...} After in *.java: public class ...Dao extends XStreamSolutionDao {...} [MAJOR] The API to configure a Benchmarker from a Freemarker Template has moved to a separate calls and the methods have been renamed. Before in *.java: PlannerBenchmarkFactory plannerBenchmarkFactory = new XmlPlannerBenchmarkFactory() .configureFromTemplate(benchmarkConfigTemplate); After in *.java: PlannerBenchmarkFactory plannerBenchmarkFactory = new FreemarkerXmlPlannerBenchmarkFactory( benchmarkConfigTemplate); [MAJOR] The forager property minimalAcceptedSelection has been renamed to acceptedCountLimit. Before in *Config.xml: 1000 After in *Config.xml: 1000 From 6.0.0.Beta2 to 6.0.0.Beta3 ------------------------------- [MINOR] If you have a custom ScoreDefinition implementation: the interface Score has a new method power(double). After in *Score.java: public HardSoftScore power(double exponent) { return new HardSoftScore((int) Math.floor(Math.pow(hardScore, exponent)), (int) Math.floor(Math.pow(softScore, exponent))); } [MINOR] If you have a custom Score implementation: Score's method toDoubleLevels() has been renamed to toLevelNumbers(). Now it returns an array of Number instead of an array of doubles. Before in *Score.java: public double[] toDoubleLevels() { return new double[]{hardScore, softScore}; } After in *Score.java: public Number[] toLevelNumbers() { return new Number[]{hardScore, softScore}; } [MINOR] The LateAcceptanceAcceptor now also accepts any move that improves the current solution. If you use in your config, this will impact your results (normally in a good way). [MAJOR] The tabu search acceptor properties planningEntityTabuSize and planningValueTabuSize have been renamed to entityTabuSize and valueTabuSize. Before in *Config.xml: ... ... ... ... After in *Config.xml: ... ... ... ... [MINOR] The implementation classes PlanningEntityTabuAcceptor and PlanningValueTabuAcceptor have been renamed to EntityTabuAcceptor and ValueTabuAcceptor [MINOR] and combined with a value did not select all possible moves previously. If you use this, you might want to rerun benchmarks. From 6.0.0.Beta3 to 6.0.0.Beta4 ------------------------------- [MINOR] *Descriptor classes's methods have dropped the Planning prefix verbosity: Methods like getPlanningEntityDescriptor() have been renamed to getEntityDescriptor() Methods like getPlanningVariableDescriptor() have been renamed to getVariableDescriptor() Normally, your code should not be using those classes/methods. [MAJOR] Benchmarker: the BEST_SOLUTION_CHANGED has been renamed to BEST_SCORE Before in *BenchmarkConfig.xml: ... BEST_SOLUTION_CHANGED After in *BenchmarkConfig.xml: ... BEST_SCORE [MAJOR] The methods beforeAllVariablesChanged() and afterAllVariablesChanged() have been removed from IncrementalScoreCalculator and ScoreDirector. This was needed to make planning variable listeners work efficiently. Before in *IncrementalScoreCalculator.java: public void beforeAllVariablesChanged(Object entity) { ... } public void afterAllVariablesChanged(Object entity) { ... } Before in *Move.java: public void doMove(ScoreDirector scoreDirector) { scoreDirector.beforeAllVariablesChanged(lecture); lecture.setPeriod(period); lecture.setRoom(room); scoreDirector.afterAllVariablesChanged(lecture); } After in *Move.java: public void doMove(ScoreDirector scoreDirector) { scoreDirector.beforeVariableChanged(lecture, "period"); // because setPeriod() will be called scoreDirector.beforeVariableChanged(lecture, "room"); // because setRoom() will be called lecture.setPeriod(period); lecture.setRoom(room); scoreDirector.afterVariableChanged(lecture, "period"); scoreDirector.afterVariableChanged(lecture, "room"); } [README] The VehicleRouting example has been rewritten to take advantage of Variable Listeners. This makes it easier to implement time windowed vehicle routing. A variable listener is triggered when the variable (previousStandstill) changes and updates a shadow variable (vehicle, arrivalTime) accordingly. [MAJOR] The generic moves and no longer signal Drools/IncrementalScoreCalculator that an entity has changed when the anchor of an entity changes if none of the actual planning variables of the entity changed. So if you have shadow variable representing the anchor (for example the vehicle in VRP) this might cause score corruption. Instead, add a variable listener to update the shadow variable and signal the ScoreDirector accordingly. Before in *.java: public class VrpCustomer ... { // Planning variables: changes during planning, between score calculations. protected VrpStandstill previousStandstill; @PlanningVariable(chained = true) ... public VrpStandstill getPreviousStandstill() { return previousStandstill; } public VrpVehicle getVehicle() { // HACK VrpStandstill firstStandstill = getPreviousStandstill(); while (firstStandstill instanceof VrpCustomer) { if (firstStandstill == this) { throw new IllegalStateException("Impossible state"); // fail fast during infinite loop } firstStandstill = ((VrpCustomer) firstStandstill).getPreviousStandstill(); } return (VrpVehicle) firstStandstill; } ... } After in *.java: public class VrpCustomer ... { // Planning variables: changes during planning, between score calculations. protected VrpStandstill previousStandstill; // Shadow variable protected VrpVehicle vehicle; @PlanningVariable(chained = true, variableListenerClasses = {VehicleUpdatingVariableListener.class}) ... public VrpStandstill getPreviousStandstill() { return previousStandstill; } public VrpVehicle getVehicle() { return vehicle; } ... } To make it easier to implement that listener, a bi-directional relationship was introduced on VrpStandstill, which made VrpStandstill a @PlanningEntity (which effectively makes VrpVehicle a planning entity too) and Solution.getVehicleList()'s method annotated with @PlanningEntityCollectionProperty: Before in VrpStandstill.java: public interface VrpStandstill { ... } After in VrpStandstill.java: @PlanningEntity public interface VrpStandstill { ... @PlanningVariable(mappedBy = "previousStandstill") // Bi-directional relationship. This is the shadow side VrpCustomer getNextCustomer(); void setNextCustomer(VrpCustomer nextCustomer); } Before in VrpSchedule.java: public List getVehicleList() { return vehicleList; } public Collection getProblemFacts() { ... facts.addAll(vehicleList); return facts; } After in VrpSchedule.java: @PlanningEntityCollectionProperty public List getVehicleList() { return vehicleList; } public Collection getProblemFacts() { ... return facts; } Before in *SolverConfig.java and *BenchmarkConfig.java: ... org.optaplanner.examples.vehiclerouting.domain.VrpCustomer After in *SolverConfig.java and *BenchmarkConfig.java: ... org.optaplanner.examples.vehiclerouting.domain.VrpCustomer org.optaplanner.examples.vehiclerouting.domain.VrpStandstill [MINOR] SolverConfig's method setPlanningEntityClassSet() has changed into setPlanningEntityClassList() because the order is important. Before in *.java: ... = solverConfig.getPlanningEntityClassSet() solverConfig.setPlanningEntityClassSet(...) After in *.java: ... = solverConfig.getPlanningEntityClassList() solverConfig.setPlanningEntityClassList(...) From 6.0.0.Beta4 to 6.0.0.Beta5 ------------------------------- [MINOR] If you extended a Config class: the build*() method's parameters have been wrapped into a HeuristicConfigPolicy instance. Before in *Config.java: public ... build...(EnvironmentMode environmentMode, ScoreDefinition scoreDefinition) { After in *Config.java: public ... build...(HeuristicConfigPolicy configPolicy) { From 6.0.0.Beta5 to 6.0.0.CR1 ----------------------------- [MINOR] XStreamProblemIO has been modified to use a vanilla XStream instance (but still with ID_REFERENCES) instead of a complex construction to avoid an issue that has been fixed meanwhile. Before it did: xStream = new XStream(new PureJavaReflectionProvider(new FieldDictionary(new NativeFieldKeySorter()))); xStream.setMode(XStream.ID_REFERENCES); Now it simply does: xStream = new XStream(); xStream.setMode(XStream.ID_REFERENCES); Normally this should have no relevant impact on your XML dataset files or your code. [README] The construction heuristics have been rewritten from scratch to take advantage of the selector architecture. The basic configuration hasn't changed much, but power users can now optionally do advanced configuration too. These advanced options allow you to use construction heuristics with multiple entity classes, a higher number of variables, nullable variables, ... [MAJOR] The construction heuristic property has been renamed to . The value FIRST_LAST_STEP_SCORE_EQUAL_OR_IMPROVING has been renamed to FIRST_NON_DETERIORATING_SCORE. The has been nested into a element (similar like for Local Search). Before in *SolverConfig.java and *BenchmarkConfig.java: ... FIRST_LAST_STEP_SCORE_EQUAL_OR_IMPROVING After in *SolverConfig.java and *BenchmarkConfig.java: ... FIRST_NON_DETERIORATING_SCORE [MINOR] The class ConstructionHeuristicPickEarlyType has moved to another package. [MINOR] If you have multiple variables, the result of the construction heuristic is likely to differ. Even though the default still does a cartesian production over the variables for the FIT algorithm you choose, it will order the combinations in the original order (instead of reverse order as the old implementation did by accident), which might cause it to take a totally different path. Note: Through advanced configuration it's possible to make the new implementation behave exactly the same as the old, but this is NOT recommended: ... PHASE ... secondVariable ... firstVariable ... [MAJOR] The @ValueRange property excludeUninitializedPlanningEntity has been removed. Planner now does the right thing automatically for a chained variable. Before in *.java: @PlanningVariable(chained = true, ...) @ValueRanges({..., @ValueRange(type = ValueRangeType.FROM_SOLUTION_PROPERTY, solutionProperty = "customerList", excludeUninitializedPlanningEntity = true)}) public VrpStandstill getPreviousStandstill() {...} After in *.java: @PlanningVariable(chained = true, ...) @ValueRanges({..., @ValueRange(type = ValueRangeType.FROM_SOLUTION_PROPERTY, solutionProperty = "customerList")}) public VrpStandstill getPreviousStandstill() {...} If you used it on a non-chained variable (which is highly unlikely), look into ValueSelector filtering and let us know that such use cases exist (by creating a jira in our issue tracker). [MAJOR] The annotation @ValueRange on a planning variable has been replaced by @ValueRangeProvider on the providing method itself and @PlanningVariable(valueRangeProviderRefs) on planning variable. ValueRangeType.FROM_SOLUTION_PROPERTY has been removed: Before in *.java: @PlanningEntity public class CloudProcess ... { ... @PlanningVariable() @ValueRange(type = ValueRangeType.FROM_SOLUTION_PROPERTY, solutionProperty = "computerList") public CloudComputer getComputer() { return computer; } } @PlanningSolution public class CloudBalance ... { ... public List getComputerList() { return computerList; } } After in *.java: @PlanningEntity public class CloudProcess ... { ... @PlanningVariable(valueRangeProviderRefs = {"computerRange"}) public CloudComputer getComputer() { return computer; } } @PlanningSolution public class CloudBalance ... { ... @ValueRangeProvider(id = "computerRange") public List getComputerList() { return computerList; } } Consequently, the annotation @ValueRanges has been removed. Before in *.java: @PlanningEntity public class Visit ... { ... @PlanningVariable(chained = true) @ValueRanges({ @ValueRange(type = ValueRangeType.FROM_SOLUTION_PROPERTY, solutionProperty = "domicileList"), @ValueRange(type = ValueRangeType.FROM_SOLUTION_PROPERTY, solutionProperty = "visitList")}) public Standstill getPreviousStandstill() {...} } @PlanningSolution public class TravelingSalesmanTour ... { ... public List getDomicileList() {...} @PlanningEntityCollectionProperty public List getVisitList() {...} } After in *.java: @PlanningEntity public class Visit ... { ... @PlanningVariable(chained = true, valueRangeProviderRefs = {"domicileRange", "visitRange"}) public Standstill getPreviousStandstill() {...} } @PlanningSolution public class TravelingSalesmanTour ... { ... @ValueRangeProvider(id = "domicileRange") public List getDomicileList() {...} @PlanningEntityCollectionProperty @ValueRangeProvider(id = "visitRange") public List getVisitList() {...} } ValueRangeType.FROM_ENTITY_PROPERTY has been removed too: Before in *.java: @PlanningEntity public class Allocation ... { ... @PlanningVariable() @ValueRange(type = ValueRangeType.FROM_PLANNING_ENTITY_PROPERTY, planningEntityProperty = "executionModeRange") public ExecutionMode getExecutionMode() { return executionMode; } public List getExecutionModeRange() { return job.getExecutionModeList(); } } After in *.java: @PlanningEntity public class Allocation ... { ... @PlanningVariable(valueRangeProviderRefs = {"executionModeRange"}) public ExecutionMode getExecutionMode() { return executionMode; } @ValueRangeProvider(id = "executionModeRange") public List getExecutionModeRange() { return job.getExecutionModeList(); } } [MINOR] The ValueRangeType.UNDEFINED has been removed: it is no longer supported. [IMPL_DETAIL] DefaultDecider has been renamed to LocalSearchDecider. From 6.0.0.CR1 to 6.0.0.CR2 --------------------------- From 6.0.0.CR2 to 6.0.0.CR3 --------------------------- [MINOR] If you combine a @PlanningVariable(mappedBy = ...) with a Java IncrementalScoreCalculator: it now calls before/afterVariableChanged methods correctly. From 6.0.0.CR3 to 6.0.0.CR4 --------------------------- [MINOR] The examples data dir "input" has been renamed to "import" and "output" has been renamed to "export". [MINOR] The VRP example's now calculates the distance and time more accurately. To avoid floating-point arithmetic with HardSoftDoubleScore (which causes rounding errors and score corruption) and to avoid a performance loss with HardSoftBigDecimalScore, all distances and times have been multiplied by 1000 so it can continue to use HardSoftScore (which uses ints). From 6.0.0.CR4 to 6.0.0.CR5 --------------------------- [MINOR] If the element has no configuration, it now fail-fast. It used to default to entityTabuSize 7. To continue that behaviour, configure it explicitly. For good advice on what to configure, see the docs section "Which optimization algorithms should I use?". Before in *SolverConfig.xml and *BenchmarkConfig.xml: ... After in *SolverConfig.xml and *BenchmarkConfig.xml: 7 ... From 6.0.0.CR5 to 6.0.0.Final ----------------------------- [MINOR] The hospital bed planning (PAS) example's score function has severally changed. It now has more hard constraints and demonstrates overconstrained planning (with a nullable variable). [MAJOR] The interface MoveListFactory now has a generic type (which extends Solution) that is used as the parameter in the createMoveList() method. Before in *.java: public class RowChangeMoveFactory implements MoveListFactory { public List createMoveList(Solution solution) { NQueens nQueens = (NQueens) solution; ... } } After in *.java: public class RowChangeMoveFactory implements MoveListFactory { public List createMoveList(NQueens nQueens) { ... } } [MINOR] ConstraintMatch no longer has a getConstraintMatchTotal() method because a ConstraintMatch can survive many Local Search steps, but its Total (which holds a Set) should not survive. Instead it has getConstraintPackage(), getConstraintName() and getScoreLevel(). From 6.0.0.Final to 6.1.0.Beta1 ------------------------------- [README] As promised, starting from OptaPlanner 6.1.0.Final, there will be a public API, which will be backwards compatible in later versions (such as 6.2, 6.3, etc). The public API are all classes in the package org.optaplanner.core.api (or subpackages thereof). For more information, see the documentation, chapter "Status of OptaPlanner". Bear with us: this should be the last time we require you to do a large number of migrations during upgrading. [IMPL_DETAIL] Simulated Annealing now uses the time gradient of the current step instead of the time gradient of the last step. The impact of this change is negligible. [IMPL_DETAIL] On AbstractScore, the methods parseLevelStrings(...) and buildScorePattern(...) have been changed from public to protected. It's highly unlikely that this affects your code. [RECOMMENDED] If you have @ValueRangeProvider that returns a collection of numbers (for example List or List), then you probably want to switch to a ValueRange, which uses less memory and offers additional opportunities. Before in *.java: @ValueRangeProvider(id = "delayRange") public List getDelayRange() { List = new ArrayList(5000); for (int i = 0; i < 5000; i++) { delayRange.add(i); } return delayRange; } After in *.java: @ValueRangeProvider(id = "delayRange") public CountableValueRange getDelayRange() { return ValueRangeFactory.createIntValueRange(0, 5000); } [MAJOR] The annotation @ValueRangeProvider has been moved into another package Before in *.java: import org.optaplanner.core.api.domain.value.ValueRangeProvider; After in *.java: import org.optaplanner.core.api.domain.valuerange.ValueRangeProvider; [IMPL_DETAIL] The *Descriptor classes have been moved into a descriptor package. [MINOR] Selector.isContinuous() has been renamed to isCountable and its boolean return value has been inverted. It's highly unlikely that your code uses isContinuous() because uncountable value ranges weren't supported yet. [IMPL_DETAIL] SolutionDescriptor.isInitialized(Solution) now requires a ScoreDirector parameter too [MINOR] The deprecated statisticType IMPROVING_STEP_PERCENTAGE has been removed. Before in *BenchmarkConfig.xml: ... IMPROVING_STEP_PERCENTAGE [MINOR] If you have custom Benchmarker ranking: SolverBenchmark has been renamed to SolverBenchmarkResult. ProblemBenchmark has been renamed to ProblemBenchmarkResult. SingleBenchmark has been renamed to SingleBenchmarkResult. They also moved into the package result. Before in *.java: public int compare(SolverBenchmark a, SolverBenchmark b) {...} After in *.java: public int compare(SolverBenchmarkResult a, SolverBenchmarkResult b) {...} Before in *.java: public Comparable createRankingWeight(List solverBenchmarkList, SolverBenchmark solverBenchmark) {...} After in *.java: public Comparable createRankingWeight(List solverBenchmarkResultList, SolverBenchmarkResult solverBenchmarkResult) {...} [MINOR] If you explicitly specify Benchmarker rankings: All solverBenchmarkRanking* properties have been renamed to solverRanking*. Before in *BenchmarkConfig.xml: TOTAL_SCORE After in *BenchmarkConfig.xml: TOTAL_SCORE Before in *BenchmarkConfig.xml: ... After in *BenchmarkConfig.xml: ... Before in *BenchmarkConfig.xml: ... After in *BenchmarkConfig.xml: ... Before in *.java: ... implements SolverRankingWeightFactory After in *.java: ... implements SolverBenchmarkRankingWeightFactory [IMPL_DETAIL] The internals of optaplanner-benchmark have been deeply refactored to support the new aggregator functionality. [MINOR] The benchmark output report directory structure is slightly different: the CSV files have been renamed. [MINOR] If you explicitly specify Benchmarker rankings or report locale: The elements , , and have been moved inside the new element . Before in *BenchmarkConfig.xml: ... After in *BenchmarkConfig.xml: ... Before in *BenchmarkConfig.xml: ... After in *BenchmarkConfig.xml: ... [MINOR] If you explicitly configured all your elements with PHASE for performance, you know no longer have to do that, as OptaPlanner does the fast thing out of the box, if and only if no other properties (such as filtering) were specified on that . Before in *SolverConfig.xml and *BenchmarkConfig.xml: PHASE PHASE After in *SolverConfig.xml and *BenchmarkConfig.xml: [README] The phrase "time spend" has been renamed to "time spent". This includes the log output and the benchmark report. [MAJOR] All child elements of have been renamed: The element has been renamed to The element has been renamed to The element has been renamed to The element has been renamed to The element has been renamed to The element has been renamed to The element has been renamed to Before in *SolverConfig.xml and *BenchmarkConfig.xml: ... ... ... ... ... ... ... After in *SolverConfig.xml and *BenchmarkConfig.xml: ... ... ... ... ... ... ... [MAJOR] In Solver and BestSolutionChangedEvent, the method getTimeMillisSpend() has been renamed to getTimeMillisSpent(). Before in *.java: ... = solver.getTimeMillisSpend(); After in *.java: ... = solver.getTimeMillisSpent(); Before in *.java: public void bestSolutionChanged(BestSolutionChangedEvent event) { ... = event.getTimeMillisSpend(); } After in *.java: public void bestSolutionChanged(BestSolutionChangedEvent event) { ... = event.getTimeMillisSpent(); } [MAJOR] Benchmarker: the elements have been renamed: The element has been renamed to The element has been renamed to The element has been renamed to The element has been renamed to Before in *BenchmarkConfig.xml: ... ... ... ... ... After in *BenchmarkConfig.xml: ... ... ... ... ... [MINOR] Real time planning: addProblemFactChange(...) no longer causes solver Terminations to reset (but it still causes phase terminations to reset). [MAJOR] Classes BestSolutionChangedEvent and SolverEventListener moved from package impl.event to api.solver.event. They are now part of the public api. Before in *.java: import org.optaplanner.core.impl.event.BestSolutionChangedEvent; import org.optaplanner.core.impl.event.SolverEventListener; After in *.java: import org.optaplanner.core.api.solver.event.BestSolutionChangedEvent; import org.optaplanner.core.api.solver.event.SolverEventListener; [MINOR] The package config.termination has been moved to config.solver.termination. Similarly, the package impl.termination has been moved to impl.solver.termination. Before in *.java: import org.optaplanner.core.config.termination....; import org.optaplanner.core.impl.termination....; After in *.java: import org.optaplanner.core.config.solver.termination....; import org.optaplanner.core.impl.solver.termination....; [IMPL_DETAIL] RandomUtils moved from package impl.util to impl.solver.random. [IMPL_DETAIL] AbstractSolverPhaseScope and AbstractStepScope moved to package impl.phase.scope. [MAJOR] All classes in the package impl.move have been moved to impl.heuristic.move. None of them are future-proof enough at this time to be added the public API. Prefer generic moves whenever possible. Before in *.java: import org.optaplanner.core.impl.move.Move; import org.optaplanner.core.impl.move.CompositeMove; import org.optaplanner.core.impl.move.NoChangeMove; After in *.java: import org.optaplanner.core.impl.heuristic.move.Move; import org.optaplanner.core.impl.heuristic.move.CompositeMove; import org.optaplanner.core.impl.heuristic.move.NoChangeMove; [MINOR] The values PLANNING_ENTITY_TABU and PLANNING_VALUE_TABU are renamed to ENTITY_TABU and VALUE_TABU. It's very unlikely that you're using either, because neither specifies the tabu size. Before in *SolverConfig.xml and *BenchmarkConfig.xml: PLANNING_ENTITY_TABU After in *SolverConfig.xml and *BenchmarkConfig.xml: ENTITY_TABU Before in *SolverConfig.xml and *BenchmarkConfig.xml: PLANNING_VALUE_TABU After in *SolverConfig.xml and *BenchmarkConfig.xml: VALUE_TABU [IMPL_DETAIL] BestSolutionRecaller moved from package impl.bestsolution to impl.solver.recaller. Before in *.java: import org.optaplanner.core.impl.bestsolution.BestSolutionRecaller; After in *.java: import org.optaplanner.core.impl.solver.recaller.BestSolutionRecaller; From 6.1.0.Beta1 to 6.1.0.Beta2 ------------------------------- [IMPL_DETAIL] PlanningEntityDescriptor has been renamed to EntityDescriptor. [IMPL_DETAIL] PlanningVariableDescriptor has been renamed to GenuineVariableDescriptor. [MAJOR] The interface PlanningVariableListener has been renamed to VariableListener. Before in *.java: public class VehicleUpdatingVariableListener implements PlanningVariableListener { After in *.java: public class VehicleUpdatingVariableListener implements VariableListener { [MAJOR] The class AbstractPlanningVariableListener has been removed. Before in *.java: public class VehicleUpdatingVariableListener extends AbstractPlanningVariableListener { After in *.java: public class VehicleUpdatingVariableListener implements VariableListener { [MINOR] If you've copied the solutionEquals() and solutionHashCode() from the examples, it's safe to remove it if you're not using solutionTabu (which is often pointless to use). Also remove the equals() and hashCode() method on your Solution implementation. [MINOR] In DEBUG logging, each step now mentions it's phase type first: CH is Construction Heuristic, LS is Local Search, EH is Exhaustive Search. [MINOR] GreatDelugeAcceptor, an experimental implementation, has been removed. Use Late Acceptance instead. Before in *SolverConfig.xml and *BenchmarkConfig.xml: ... ... [RECOMMENDED] Specify an in the , to increase performance of some algorithms (Construction Heuristics and Exhaustive Search). See the documentation section on InitializingScoreTrend when to use ANY, ONLY_UP or ONLY_DOWN. Before in *SolverConfig.xml and *BenchmarkConfig.xml: HARD_SOFT .../cloudBalancingScoreRules.drl After in *SolverConfig.xml and *BenchmarkConfig.xml: HARD_SOFT .../cloudBalancingScoreRules.drl ONLY_DOWN [MAJOR] Replace FIRST_NON_DETERIORATING_SCORE with ONLY_DOWN. If the is specified, the will automatically use the most appropriate . Before in *SolverConfig.xml and *BenchmarkConfig.xml: ... ... FIRST_FIT_DECREASING FIRST_NON_DETERIORATING_SCORE After in *SolverConfig.xml and *BenchmarkConfig.xml: ... ONLY_DOWN ... FIRST_FIT_DECREASING [MAJOR] The solver phase has been replaced by 's BRUTE_FORCE type. Before in *SolverConfig.xml and *BenchmarkConfig.xml: After in *SolverConfig.xml and *BenchmarkConfig.xml: BRUTE_FORCE There is now a better alternative to Brute Force: Branch And Bound, see docs for more information. [MAJOR] The ConstraintOccurrence classes (which were deprecated) have been removed. If you hadn't switch them to the ConstraintMatch system yet, scroll up to the section "From 6.0.0.Alpha9 to 6.0.0.Beta1" for instructions. [MAJOR] Solver interface: the methods setPlanningProblem(Solution) and solve() have been merged as the method solve(Solution). Before in *.java: solver.setPlanningProblem(planningProblem); solver.solve(); After in *.java: solver.solve(planningProblem); Note: you still need to use solver.getBestSolution() to retrieve the best solution. That is intentional due to real-time planning and to support pareto optimization in the future. From 6.1.0.Beta2 to 6.1.0.Beta3 ------------------------------- [MINOR] If you have a @ValueRangeProvider on an entity class (instead of the Solution class), then it's now safe to use the as is. It's no longer needed to filter out swaps which could put a value in an entity's variable that's not in its value range. Before in *SolverConfig.xml and *BenchmarkConfig.xml: ...ValidSwapsOnlySwapMoveFilter After in *SolverConfig.xml and *BenchmarkConfig.xml: [MINOR] The interface ScoreDirector has been split up in ScoreDirector and InnerScoreDirector. ScoreDirector now only has methods which might make the public API in a future version. InnerScoreDirector extends ScoreDirector and it holds all methods which will never be made part of the public API. Similarly, ScoreDirectorFactory has been split up in ScoreDirectorFactory and InnerScoreDirectorFactory. [MINOR] If you're doing programmatic configuration (instead of by XML), Config methods that accepted a property wrapped in a singleton list (due to XStream limitations), now no longer need the property wrapped in a list. Before in *.java: localSearchSolverPhaseConfig.setMoveSelectorConfigList(Collections.singletonList(moveSelectorConfig)); After in *.java: localSearchSolverPhaseConfig.setMoveSelectorConfig(moveSelectorConfig); [MAJOR] The class XmlSolverFactory (which was not part of the public API) has been removed and replaced by static methods on SolverFactory (which are part of the public API). Before in *.java: SolverFactory solverFactory = new XmlSolverFactory("...solverConfig.xml"); After in *.java: SolverFactory solverFactory = SolverFactory.createFromXmlResource("...solverConfig.xml"); Before in *.java: SolverFactory solverFactory = new XmlSolverFactory().configure(inputStream); After in *.java: SolverFactory solverFactory = SolverFactory.createFromXmlInputStream(inputStream); Before in *.java: SolverFactory solverFactory = new XmlSolverFactory().configure(reader); After in *.java: SolverFactory solverFactory = SolverFactory.createFromXmlReader(reader); Note: If you used the method addXstreamAnnotations(), take a look at the non-public API class XStreamXmlSolverFactory. [MAJOR] Benchmarker: The class XmlPlannerBenchmarkFactory has been removed and replaced by static methods on PlannerBenchmarkFactory. Before in *.java: PlannerBenchmarkFactory plannerBenchmarkFactory = new XmlPlannerBenchmarkFactory(...); After in *.java: PlannerBenchmarkFactory plannerBenchmarkFactory = PlannerBenchmarkFactory.createFromXmlResource(...); Note: If you used the method addXstreamAnnotations(), take a look at the non-public API class XStreamXmlPlannerBenchmarkFactory. [MINOR] Benchmarker: The class FreemarkerXmlPlannerBenchmarkFactory has been removed and replaced by static methods on PlannerBenchmarkFactory. Before in *.java: PlannerBenchmarkFactory plannerBenchmarkFactory = new FreemarkerXmlPlannerBenchmarkFactory(...); After in *.java: PlannerBenchmarkFactory plannerBenchmarkFactory = PlannerBenchmarkFactory.createFromFreemarkerXmlResource(...); [MAJOR] Benchmarker configuration: the element has been renamed to . Before in *BenchmarkConfig.xml: org.optaplanner.examples.nqueens.domain.NQueens ... After in *BenchmarkConfig.xml: org.optaplanner.examples.nqueens.domain.NQueens ... [README] All classpath resources must lose their leading slash, because OptaPlanner now expects them to adhere to ClassLoader.getResource(String) instead of Class.getResource(String). [MAJOR] The SolverFactory.createFromXmlResource(String) parameter must lose its leading slash. Before in *.java: ... = SolverFactory.createFromXmlResource( "/org/optaplanner/examples/cloudbalancing/solver/cloudBalancingSolverConfig.xml"); After in *.java: ... = SolverFactory.createFromXmlResource( "org/optaplanner/examples/cloudbalancing/solver/cloudBalancingSolverConfig.xml"); [MAJOR] All elements must lose their leading slash. Before in *SolverConfig.xml and *BenchmarkConfig.xml: /org/optaplanner/examples/cloudbalancing/solver/cloudBalancingScoreRules.drl After in *SolverConfig.xml and *BenchmarkConfig.xml: org/optaplanner/examples/cloudbalancing/solver/cloudBalancingScoreRules.drl [MAJOR] The PlannerBenchmarkFactory.createFromXmlResource(String) parameter must lose its leading slash. Before in *.java: ... = PlannerBenchmarkFactory.createFromXmlResource( "/org/optaplanner/examples/cloudbalancing/benchmark/cloudBalancingBenchmarkConfig.xml"); After in *.java: ... = PlannerBenchmarkFactory.createFromXmlResource( "org/optaplanner/examples/cloudbalancing/benchmark/cloudBalancingBenchmarkConfig.xml"); [MAJOR] The PlannerBenchmarkFactory.createFromFreemarkerXmlResource(String) parameter must lose its leading slash. Before in *.java: ... = PlannerBenchmarkFactory.createFromFreemarkerXmlResource( "/org/optaplanner/examples/cloudbalancing/benchmark/cloudBalancingBenchmarkConfigTemplate.xml.ftl"); After in *.java: ... = PlannerBenchmarkFactory.createFromFreemarkerXmlResource( "org/optaplanner/examples/cloudbalancing/benchmark/cloudBalancingBenchmarkConfigTemplate.xml.ftl"); [MAJOR] The interface SimpleScoreCalculator has been renamed to EasyScoreCalculator to avoid confusion with SimpleScore and SimpleScore: it can return other Score types too. The package name has changed too. Before in *.java: import org.optaplanner.core.impl.score.director.simple.SimpleScoreCalculator; public class CloudBalancingEasyScoreCalculator implements SimpleScoreCalculator { ... } After in *.java: import org.optaplanner.core.impl.score.director.easy.EasyScoreCalculator; public class CloudBalancingEasyScoreCalculator implements EasyScoreCalculator { ... } Before in *SolverConfig.xml and *BenchmarkConfig.xml: org.optaplanner.examples.cloudbalancing.solver.score.CloudBalancingEasyScoreCalculator After in *SolverConfig.xml and *BenchmarkConfig.xml: org.optaplanner.examples.cloudbalancing.solver.score.CloudBalancingEasyScoreCalculator