Introduction ============ OptaPlanner's public API classes are backwards compatible, but you might be using not-yet-public API classes (which are often documented in the reference manual too). This file describes how can you upgrade those from an earlier version to the latest, as well as easily apply general recommended changes. Every migration change has an indication 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. Important note for BRMS/BPMS customers: the BRMS/BPMS version does NOT match the optaplanner jar version. The sections below reference the optaplanner jar version. The mapping is as follows: - BRMS/BPMS 6.0 includes drools/optaplanner 6.0 - BRMS/BPMS 6.1 includes drools/optaplanner 6.2 So if you're upgrading to BRMS 6.1, follow the recipe to upgrade to 6.2.0.Final. From 5.* to 5.5.0.Final ----------------------- To see the upgrade path from an older version (5.0, 5.1, 5.2, 5.3 or 5.4) to 5.5.0.Final, see an older revision of this file: https://github.com/droolsjbpm/optaplanner/blob/6.1.0.Final/optaplanner-distribution/src/main/assembly/filtered-resources/UpgradeFromPreviousVersionRecipe.txt 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 From 6.1.0.Beta3 to 6.1.0.Beta4 ------------------------------- [MINOR] If you have a custom ScoreDefinition: the Score interface has an extra method negate(). [MINOR] If you combine Simulated Annealing with : The timeGradient (used only by Simulated Annealing) calculation has changed for BestScoreTermination. On average, this should be for the better. [MINOR] BestSolutionChangedEvent and SolverEventListener now have a generic parameter which you can optionally use. Before in *.java: solver.addEventListener(new SolverEventListener() { public void bestSolutionChanged(BestSolutionChangedEvent event) { CloudBalance solution = (CloudBalance) event.getNewBestSolution(); } }); After in *.java: solver.addEventListener(new SolverEventListener() { public void bestSolutionChanged(BestSolutionChangedEvent event) { CloudBalance solution = event.getNewBestSolution(); } }); [MINOR] BestSolutionChangedEvent now has the methods isNewBestSolutionInitialized() and isEveryProblemFactChangeProcessed(). In real-time planning, if you're only interested in processing valid solutions, you'll want to filter and check those. Note: in 6.0, filtering BestSolutionChangedEvents for only valid solutions was cumbersome. Note: often you're interested in invalid, uninitialized solutions too, to show to the user you've processed his problem fact changes. After in *.java: public void bestSolutionChanged(BestSolutionChangedEvent event) { // Ignore invalid solutions if (event.isEveryProblemFactChangeProcessed() && event.isNewBestSolutionInitialized() && event.getNewBestSolution().getScore().isFeasible()) { ... } } [MINOR] A benchmark configuration with writeOutputSolutionEnabled=true now writes the solution files in the single benchmark directory (instead of the problem benchmark directory) and no longer alters the filename. [MINOR] and 's s now default to a of 1 instead of 2. This is to enable to swap a subchain of size 1 and a subchain of at least size 2 too. [MINOR] 's now selects subpillars too by default. Normally, that's an improvement. Before in *SolverConfig.xml and *BenchmarkConfig.xml: After in *SolverConfig.xml and *BenchmarkConfig.xml (if you don't want this new behaviour): false Note: is not supported too [MINOR] SolverPhase has been renamed to Phase. SolverPhaseConfig has been renamed to PhaseConfig. Before in *.java: List solverPhaseConfigList = new ArrayList(); ConstructionHeuristicSolverPhaseConfig solverPhaseConfig = new ConstructionHeuristicSolverPhaseConfig(); ... solverPhaseConfigList.add(solverPhaseConfig); ... solverConfig.setPhaseConfigList(phaseConfigList); After in *.java: List phaseConfigList = new ArrayList(); ConstructionHeuristicPhaseConfig phaseConfig = new ConstructionHeuristicPhaseConfig(); ... phaseConfigList.add(phaseConfig); ... solverConfig.setPhaseConfigList(phaseConfigList); [MAJOR] The interface CustomSolverPhaseCommand has been renamed to CustomPhaseCommand. The element has been renamed to . The element has been renamed to . Before in *.java: public class ToOriginalMachineSolutionInitializer implements CustomSolverPhaseCommand { ... } After in *.java: public class ToOriginalMachineSolutionInitializer implements CustomPhaseCommand { ... } Before in *SolverConfig.xml and *BenchmarkConfig.xml: ...ToOriginalMachineSolutionInitializer After in *SolverConfig.xml and *BenchmarkConfig.xml: ....ToOriginalMachineSolutionInitializer From 6.1.0.Beta4 to 6.1.0.CR1 ----------------------------- [IMPL_DETAIL] The method ScoreDefinition.getLevelCount() has been renamed to ScoreDefinition.getLevelsSize(). [MAJOR] BendableScore: the configuration has changed: ...LevelCount has been renamed to ...LevelsSize Before in *SolverConfig.xml and *BenchmarkConfig.xml: BENDABLE 2 3 ... After in *SolverConfig.xml and *BenchmarkConfig.xml: BENDABLE 2 3 ... [MINOR] The interface SolverRankingWeightFactory has moved package (but almost nobody uses that). [MINOR] Configuration by Java (instead of XML): Enums for the Config classes have been moved into the config package and any inner classes of those enums have been moved to the top level. Before in *.java: import org.optaplanner.core.config.score.director.ScoreDirectorFactoryConfig.ScoreDefinitionType; import org.optaplanner.core.config.solver.termination.TerminationConfig.TerminationCompositionStyle; import org.optaplanner.core.impl.heuristic.selector.common.SelectionCacheType; import org.optaplanner.core.impl.heuristic.selector.common.decorator.SelectionSorterOrder; import org.optaplanner.core.config.constructionheuristic.ConstructionHeuristicPhaseConfig.ConstructionHeuristicType; import org.optaplanner.core.impl.constructionheuristic.decider.forager.ConstructionHeuristicPickEarlyType; import org.optaplanner.core.impl.localsearch.decider.forager.PickEarlyType; import org.optaplanner.core.config.localsearch.decider.acceptor.AcceptorConfig.AcceptorType; import org.optaplanner.benchmark.impl.statistic.ProblemStatisticType; import org.optaplanner.benchmark.api.ranking.SolverRankingType; After in *.java: import org.optaplanner.core.config.score.definition.ScoreDefinitionType; import org.optaplanner.core.config.solver.termination.TerminationCompositionStyle; import org.optaplanner.core.config.heuristic.selector.common.SelectionCacheType; import org.optaplanner.core.config.heuristic.selector.common.decorator.SelectionSorterOrder; import org.optaplanner.core.config.constructionheuristic.ConstructionHeuristicType; import org.optaplanner.core.config.constructionheuristic.decider.forager.ConstructionHeuristicPickEarlyType; import org.optaplanner.core.config.localsearch.decider.forager.LocalSearchPickEarlyType; import org.optaplanner.core.config.localsearch.decider.acceptor.AcceptorType; import org.optaplanner.benchmark.config.statistic.ProblemStatisticType; import org.optaplanner.benchmark.config.ranking.SolverRankingType; [IMPL_DETAIL] ForagerConfig has been renamed to LocalSearchForagerConfig [MAJOR] The interface Solution has been promoted to the public API. It has also moved package from impl.solution to api.domain.solution Before in *.java: import org.optaplanner.core.impl.solution.Solution; After in *.java: import org.optaplanner.core.api.domain.solution.Solution; [MAJOR] The @PlanningVariable property chained has been refactored to graphType. This is to allow support for other graph types (such as TREE) in the future. Before in *.java: @PlanningVariable(chained = true, ...) public Standstill getPreviousStandstill() { return previousStandstill; } After in *.java: @PlanningVariable(graphType = PlanningVariableGraphType.CHAINED, ...) public Standstill getPreviousStandstill() { return previousStandstill; } [MAJOR] The constructionHeuristicType BEST_FIT has been renamed into WEAKEST_FIT. The terminology "Best Fit" was not correct and did not allow for STRONGEST_FIT. Before in *SolverConfig.xml and *BenchmarkConfig.xml: BEST_FIT After in *SolverConfig.xml and *BenchmarkConfig.xml: WEAKEST_FIT [MAJOR] The constructionHeuristicType BEST_FIT_DECREASING has been renamed into WEAKEST_FIT_DECREASING. The terminology "Best Fit" was not correct and did not allow for STRONGEST_FIT_DECREASING. Before in *SolverConfig.xml and *BenchmarkConfig.xml: BEST_FIT_DECREASING After in *SolverConfig.xml and *BenchmarkConfig.xml: WEAKEST_FIT_DECREASING [MAJOR] For the shadow variable of a bi-directional relationship, the declaration has changed from @PlanningVariable(mappedBy) to @InverseRelationShadowVariable(sourceVariableName). Before in *.java: @PlanningVariable(mappedBy = "previousStandstill") Customer getNextCustomer(); void setNextCustomer(Customer nextCustomer); After in *.java: @InverseRelationShadowVariable(sourceVariableName = "previousStandstill") Customer getNextCustomer(); void setNextCustomer(Customer nextCustomer); [MAJOR] VariableListener: the VariableListener is now declared on the shadow side, instead of the @PlanningVariable side. This way, OptaPlanner recognizes the shadow variables, and all shadow variables are declared in a consistent matter. Furthermore, it allows a shadow variable to based on other shadow variable. Before in *.java: @PlanningVariable(valueRangeProviderRefs = {"vehicleRange", "customerRange"}, graphType = PlanningVariableGraphType.CHAINED, variableListenerClasses = {VehicleUpdatingVariableListener.class, ArrivalTimeUpdatingVariableListener.class}) public Standstill getPreviousStandstill() { return previousStandstill; } public Vehicle getVehicle() { return vehicle; } public Integer getArrivalTime() { return arrivalTime; } After in *.java: @PlanningVariable(...) public Standstill getPreviousStandstill() { return previousStandstill; } @CustomShadowVariable(variableListenerClass = VehicleUpdatingVariableListener.class, sources = {@CustomShadowVariable.Source(variableName = "previousStandstill")}) public Vehicle getVehicle() { return vehicle; } @CustomShadowVariable(variableListenerClass = ArrivalTimeUpdatingVariableListener.class, sources = {@CustomShadowVariable.Source(variableName = "previousStandstill")}) public Integer getArrivalTime() { return arrivalTime; } [MINOR] Even classes that only have shadow variables (and extend a planning entity class), now need to be explicitly registered as planning entities. Previously, it was only required for inverse relationship shadow variables. Now it's required for all shadow variables. Before in *.java: public class TimeWindowedCustomer extends Customer { After in *.java: @PlanningEntity public class TimeWindowedCustomer extends Customer { After in *SolverConfig.xml and *BenchmarkConfig.xml: org.optaplanner.examples.vehiclerouting.domain.timewindowed.TimeWindowedCustomer [MAJOR] Multiple elements now need to be ordered by superclasses (and superinterfaces) first, instead of superclasses (and superinterfaces) last. Before in *SolverConfig.xml and *BenchmarkConfig.xml: ...TimeWindowedCustomer ...Customer ...Standstill After in *SolverConfig.xml and *BenchmarkConfig.xml: ...Standstill ...Customer ...TimeWindowedCustomer [MAJOR] The element has been renamed to . Before in *SolverConfig.xml and *BenchmarkConfig.xml: org.optaplanner.examples.cloudbalancing.domain.CloudProcess After in *SolverConfig.xml and *BenchmarkConfig.xml: org.optaplanner.examples.cloudbalancing.domain.CloudProcess [MAJOR] XStreamScoreConverter and XStreamBendableScoreConverter have moved package. Before in *.java: import org.optaplanner.persistence.xstream.XStreamScoreConverter; After in *.java: import org.optaplanner.persistence.xstream.impl.score.XStreamScoreConverter; Before in *.java: import org.optaplanner.persistence.xstream.XStreamBendableScoreConverter; After in *.java: import org.optaplanner.persistence.xstream.impl.score.XStreamBendableScoreConverter; [MAJOR] ProblemIO has been renamed to SolutionFileIO and moved package (into the public API). Before in *.java: import org.optaplanner.core.impl.solution.ProblemIO; public class MachineReassignmentFileIO implements ProblemIO { ... } After in *.java: import org.optaplanner.persistence.common.api.domain.solution.SolutionFileIO; public class MachineReassignmentFileIO implements SolutionFileIO { ... } Before in *SolverConfig.xml and *BenchmarkConfig.xml: ...MachineReassignmentProblemIO ... After in *SolverConfig.xml and *BenchmarkConfig.xml: ...MachineReassignmentFileIO ... Before in *.java: import org.optaplanner.persistence.xstream.XStreamProblemIO; After in *.java: import org.optaplanner.persistence.xstream.impl.domain.solution.XStreamSolutionFileIO; [MAJOR] The method SolutionFileIO.getFileExtension() has been split up in getInputFileExtension() and getOutputFileExtension(); It's still highly recommended to use the same input and output file extension. Before in *.java: public String getFileExtension() { return FILE_EXTENSION; } After in *.java: public String getInputFileExtension() { return FILE_EXTENSION; } public String getOutputFileExtension() { return FILE_EXTENSION; } From 6.1.0.CR1 to 6.1.0.CR2 --------------------------- [IMPL_DETAIL] SolutionDescriptor.getEntityDescriptor(...) has been renamed to SolutionDescriptor.findEntityDescriptorOrFail(...) From 6.1.0.Final to 6.2.0.Beta1 ------------------------------- [MINOR] If you have a custom ScoreDefinition: the methods ScoreHolder.register*ConstraintMatch have been refactored. Before in *.java: public void addConstraintMatch(RuleContext kcontext, final int weight) { ... registerIntConstraintMatch(kcontext, 0, weight, new Runnable() { public void run() { ... } }); } After in *.java: public void addConstraintMatch(RuleContext kcontext, final int weight) { ... registerIntConstraintMatch(kcontext, 0, weight, new IntConstraintUndoListener() { public void undo() { ... } }); } [MAJOR] If you have a custom Move implementation, now extract AbstractMove Before in *.java: public class CloudComputerChangeMove implements Move {...} After in *.java: public class CloudComputerChangeMove extends AbstractMove {...} [MINOR] The interface Move has a new method getSimpleMoveTypeDescription(). Extend AbstractMove so to avoid having to implement it. [MINOR] If you have a @ValueRangeProvider on an entity class (instead of the Solution class), then it's now safe to use the and as is. It's no longer needed to filter out swaps or changes which could put a value in an entity's variable that's not in its value range. Before in *SolverConfig.xml and *BenchmarkConfig.xml: ...ValidChangesOnlyPillarChangeMoveFilter ...ValidSwapsOnlyPillarSwapMoveFilter After in *SolverConfig.xml and *BenchmarkConfig.xml: [MINOR] ConstraintMatchTotal are now naturally compared by scoreLevel, then constraintPackage, then constraintName (instead of by constraintPackage, then constraintName, then scoreLevel) for readability. [MINOR] The optional method IncrementalScoreCalculator.buildScoreCorruptionAnalysis(IncrementalScoreCalculator) has been removed. Instead, to get a pretty score corruption analysis, implement the new interface ConstraintMatchAwareIncrementalScoreCalculator. This also enable your GUI to explain the score with an IncrementalScoreCalculator. See the example code in MachineReassignmentIncrementalScoreCalculator.getConstraintMatchTotals(). From 6.2.0.Beta1 to 6.2.0.Beta2 ------------------------------- [MINOR] The element (which was deprecated, not documented, broken and clearly marked as not backwards compatible) has been removed. Instead, use strategic oscillation. Before in *SolverConfig.xml and *BenchmarkConfig.xml: ... ... ... After in *SolverConfig.xml and *BenchmarkConfig.xml: ... ... STRATEGIC_OSCILLATION [IMPL_DETAIL] The ScoreBounder methods calculateOptimisticBound() and calculatePessimisticBound() no longer have an uninitializedVariableCount parameter. Instead, if all the variables for a branch and bound algorithm are initialized, those methods are no called to determine the bounds (because the bound is the working score). If the uninitializedVariableCount is still needed for some reason, use the ScoreDirector to calculate it accurately. [MINOR] Before calling ScoreDirector.getConstraintMatchTotals(), it's no longer expected to call ScoreDirector.calculateScore() first. From 6.2.0.CR1 to 6.2.0.CR2 --------------------------- [MINOR] CompositeMove now uses a Move array instead of a List for performance reasons. Before in *.java: ... = CompositeMove.buildMove(Arrays.asList(moveA, moveB, ...)); After in *.java: ... = CompositeMove.buildMove(moveA, moveB, ...); Before in *.java: ... = new CompositeMove(moveList); // Not recommended After in *.java: ... = new CompositeMove(moves); // Not recommended [IMPL_DETAIL] InverseRelationShadowVariableListener renamed to SingletonInverseVariableListener. It and InverseRelationShadowVariableDescriptor moved to the package ...impl.domain.variable.inverserelation. From 6.2.0.CR3 to 6.2.0.CR4 --------------------------- [MAJOR] There is now out-of-the-box support for a shadow variable representing the anchor of a chained variable. For example, in a VRP each Customer (= entity) needs to know to which Vehicle (= anchor) it belongs. This declarative support allows build-in selectors to reuse that knowledge without duplicating the calculation. Before in *.java: @PlanningEntity public class Customer implements Standstill { @PlanningVariable(...) public Standstill getPreviousStandstill() {...} @CustomShadowVariable(variableListenerClass = VehicleUpdatingVariableListener.class, sources = {@CustomShadowVariable.Source(variableName = "previousStandstill")}) public Vehicle getVehicle() {...} } public class VehicleUpdatingVariableListener implements VariableListener { ... } After in *.java: @PlanningEntity public class Customer implements Standstill { @PlanningVariable(...) public Standstill getPreviousStandstill() {...} @AnchorShadowVariable(sourceVariableName = "previousStandstill") public Vehicle getVehicle() {...} } From 6.2.0.CR4 to 6.2.0.Final ----------------------------- [MINOR] The undocumented, experimental has been replaced by , which is documented. [README] To scale VRP cases, Nearby Selection is critical. It is now finally completely supported and documented. From 6.2.0.Final to 6.3.0.Beta1 ------------------------------- [MINOR] @InverseRelationShadowVariable is now also supported for non-chained planning variables, in which case it the inverse property must be Collection (Set or List). So it's no longer needed to use a CustomShadowVariable to implement the bi-directional relationship behaviour. Before in *.java: public class CloudComputer { ... @CustomShadowVariable(variableListenerClass = MyCustomInverseVariableListener.class, sources = {@CustomShadowVariable.Source(entityClass = CloudProcess.class, variableName = "computer")}) public List getProcessList() { return processList; } } After in *.java: @PlanningEntity // Shadow variable only public class CloudComputer { ... @InverseRelationShadowVariable(sourceVariableName = "computer") public List getProcessList() { return processList; } } [MAJOR] To adhere to Java best practices, which state that an Object.toString() should identify an instance and be short (instead of trying to verbalize it's entire state), the Move.toString() methods have been modified to mention the old value too (as well as the entity and the new value). Their notations have also been made consistent: ChangeMove: "a {v1 -> v2}" SwapMove: "a {v1} <-> b {v2}" PillarChangeMove: "[a, b] {v1 -> v2}" PillarSwapMove: "[a, b] {v1} <-> [c, d, e] {v2}" TailChainSwapMove: "a3 {a2} <-tailChainSwap-> b1 {b0}" SubChainChangeMove: "[a2..a5] {a1 -> b0}" Reversing: "[a2..a5] {a1 -reversing-> b0}" SubChainSwapMove: "[a2..a5] {a1} <-> [b1..b3] {b0}" Reversing: "[a2..a5] {a1} <-reversing-> [b1..b3] {b0}" This mainly affects the logging output. In the examples, the toString() method of planning entities and planning values has been modified accordingly to avoid mentioning the old value twice: Before in *.java: public class CloudProcess { ... public String toString() { return processName + "@" + computer.getName(); } } After in *.java: public class CloudProcess { ... public String toString() { return processName; } } [MINOR] Default parameter tweaked: a empty 's default changed from FIRST_FIT to ALLOCATE_ENTITY_FROM_QUEUE. If no entity difficulty comparison and no planning value strength comparison is defined, the behavior is exactly the same. Otherwise, the behaviour is FIRST_FIT_DECREASING or WEAKEST_FIT(_DECREASING). [MINOR] The solverBenchmarkBluePrintType ALL_CONSTRUCTION_HEURISTIC_TYPES has been renamed to EVERY_CONSTRUCTION_HEURISTIC_TYPE. The old name ALL_CONSTRUCTION_HEURISTIC_TYPES is deprecated and will be removed in 7.0. Before in *BenchmarkConfig.xml: ALL_CONSTRUCTION_HEURISTIC_TYPES After in *BenchmarkConfig.xml: EVERY_CONSTRUCTION_HEURISTIC_TYPE [MINOR] In real-time planning, calling addProblemFactChange() now resets the time spent terminations too. It did already reset all other terminations (despite that the docs claimed otherwise). The docs have also been fixed to reflect reality, which is also the desired behaviour by users. [MINOR] In real-time planning, addProblemFactChange() no longer causes the ScoreDirector to be replaced (so it no longer creates a new KieSession) upon solver restart. This might expose a hidden bug in your ProblemFactChange implementation. Enable environmentMode FULL_ASSERT and do a few addProblemFactChange() calls to validate that there are no such bugs. [MAJOR] If you use multiple planning variables, consider switching to the folded configuration. Before in *SolverConfig.xml and *BenchmarkConfig.xml: period room After in *SolverConfig.xml and *BenchmarkConfig.xml: [MAJOR] If you use multiple entity classes, consider switching to the folded configuration. Before in *SolverConfig.xml and *BenchmarkConfig.xml: ...CoachEntity ...ShuttleEntity ...CoachEntity ...ShuttleEntity After in *SolverConfig.xml and *BenchmarkConfig.xml: