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 ints, 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: [source, xml] ---- 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: [source, bash] ---- 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' 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.xml` and `*BenchmarkConfig.xml: ... org.optaplanner.examples.vehiclerouting.domain.VrpCustomer ... After in *SolverConfig.xml` and `*BenchmarkConfig.xml: ... 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: [source, java] ---- xStream = new XStream(new PureJavaReflectionProvider(new FieldDictionary(new NativeFieldKeySorter()))); xStream.setMode(XStream.ID_REFERENCES); Now it simply does: [source, java] ---- 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.xml` and `*BenchmarkConfig.xml: ... FIRST_LAST_STEP_SCORE_EQUAL_OR_IMPROVING After in *SolverConfig.xml` and `*BenchmarkConfig.xml: ... 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: [source, xml] ---- ... 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()`.