Skip to content

Instantly share code, notes, and snippets.

@oskopek
Last active September 30, 2015 09:03
Show Gist options
  • Save oskopek/cf1d649345db69537d7e to your computer and use it in GitHub Desktop.
Save oskopek/cf1d649345db69537d7e to your computer and use it in GitHub Desktop.
Converting OptaPlanner's UpgradeRecipe from .txt -> .adoc
#!/bin/bash
convertToAdoc () {
while read line; do
eval "echo ""$line"""
done < header
echo
awk '
!/^[ ]+/ {if (match(last_line, "^[ ]+")) {print "----"}}
/(NOTE)|(Note)|(IMPORTANT)|(Important)|(WARNING)|(Warning)/ {print ""}
/.*/ {last_line=$0}
# End of non-nexting commands
/^From [*0-9.FinalCRBetaAlpha]+ to [*0-9.FinalCRBetaAlpha]+/ {print "====\n"; print "== From " $2 " to " $4; print "===="; next} # Titles
/[-]{5,}/ {next} # Titles
/^[a-zA-Z ]+ in .*\.drl.*/ {print "\n" $0; print "[source, drl]"; print "----"; next}
/^[a-zA-Z ]+ in .*\.java.*/ {print "\n" $0; print "[source, java]"; print "----"; next}
/^[a-zA-Z ]+ in .*\.xml.*/ {print "\n" $0; print "[source, xml]"; print "----"; next}
/.*/ {print $0} # Unmatched lines (add next everywhere)
END { print "====" }
' | sed -E '#s/^([a-zA-Z].*) ([a-zA-Z]*\.*[a-zA-Z]+\(\))/\1 `\2`/g; # Methods
#s/^([a-zA-Z].*) ([0-9]+\.[0-9]+\.[*0-9]\.(Final|CR|Beta|Alpha)[0-9A-Za-z]*)/\1 `\2`/g; # Versions
#s/(<[^>]+>)/`\1`/g; # XML elements
s/\[MAJOR\]\s+/====\n\n=== icon:exclamation-circle[] Major\n\n====\n/g; # MAJOR
s/\[MINOR\]\s+/====\n\n=== icon:info-circle[] Minor\n\n[.hidden-section]\n====\n/g; # MINOR
s/\[IMPL_DETAIL\]\s+/====\n\n=== icon:user-secret[] Implementation detail\n\n[.hidden-section]\n====\n/g; # IMPL_DETAIL
s/\[RECOMMENDED\]\s+/====\n\n=== icon:thumbs-up[] Recommended\n\n====\n/g; # RECOMMENDED
s/\[README\]\s+/====\n\n=== icon:book[] Readme\n\n====\n/g; # README
s/^[ ]{4}//g; # Code examples default indentation
s/(^[a-zA-Z ]+ in )(.*\.[a-zA-Z]+)(.*)/\1`\2`\3/g; # Filenames highlighting in source examples
s/Note: /NOTE: /g; # Standardize
s/Warning: /WARNING: /g; # Standardize
s/Important: /IMPORTANT: /g; # Standardize
s/note: /NOTE: /g; # Standardize
s/warning: /WARNING: /g; # Standardize
s/important: /IMPORTANT: /g; # Standardize
s/NOTE: /Note: /g; # Dont create a block
s/WARNING: /Warning: /g; # Dont create a block
s/IMPORTANT: /Important: /g; # Dont create a block
' | sed -E '0,/====/{s/====//}' | awk '
/^====/ {if (last_line == "====") {next;}}
/^$/ {if (last_line == "====") {print last_line "\n" $0;} else {print $0}; last_line = $0; next}
/.*/ {print $0;}
' | awk 'BEGIN {RS = "====\n\n====\n"} # Remove this weird pattern that we substituted in
{print $0}
'
}
previous=""
version='6.0'
for file in `ls upgradeRecipe*.txt`; do
newfile=`echo "$file" | sed 's/\.txt/.adoc/g'`
previous="$version"
version=`echo "$file" | sed 's/upgradeRecipe//' | sed 's/\.txt//'`
cat "$file" | convertToAdoc > "$newfile"
# rm "$file"
done
upgradeRecipe6.0.adoc
* line 850
upgradeRecipe6.1.adoc
* line 19
upgradeRecipe6.2.adoc
* line 121
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] `<assertionScoreDirectorFactory>` no longer needs a `<scoreDefinition*>`: 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<Examination> {
...
public Examination planningClone() {
Examination clone = new Examination();
...
for (Exam exam : examList) {
Exam clonedExam = exam.planningClone();
...
}
...
return clone;
}
}
public class Exam implements PlanningCloneable<Exam> {
...
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:
<scoreDefinitionClass>...</scoreDefinitionClass>
After in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<scoreDefinitionType>HARD_MEDIUM_SOFT</scoreDefinitionType>
[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<NQueens, Queen> {
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<NQueens, Row> {
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:
<environmentMode>DEBUG</environmentMode>
After in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<environmentMode>FAST_ASSERT</environmentMode>
Before in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<environmentMode>TRACE</environmentMode>
After in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<environmentMode>FULL_ASSERT</environmentMode>
[MAJOR] Configuration property `entityFilterClass` has been renamed to `filterClass`.
Before in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<entitySelector>
<entityFilterClass>...</entityFilterClass>
</entitySelector>
After in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<entitySelector>
<filterClass>...</filterClass>
</entitySelector>
[MAJOR] Configuration property `moveFilterClass` has been renamed to `filterClass`.
Before in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<...MoveSelector>
<moveFilterClass>...</moveFilterClass>
</...MoveSelector>
After in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<...MoveSelector>
<filterClass>...</filterClass>
</...MoveSelector>
[MINOR] Configuration property `entityProbabilityWeightFactoryClass` has been renamed to `probabilityWeightFactoryClass`.
Before in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<entitySelector>
<entityProbabilityWeightFactoryClass>...</entityProbabilityWeightFactoryClass>
</entitySelector>
After in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<entitySelector>
<probabilityWeightFactoryClass>...</probabilityWeightFactoryClass>
</entitySelector>
[MINOR] Configuration property `valueProbabilityWeightFactoryClass` has been renamed to `probabilityWeightFactoryClass`.
Before in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<valueSelector>
<valueProbabilityWeightFactoryClass>...</valueProbabilityWeightFactoryClass>
</valueSelector>
After in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<valueSelector>
<valueProbabilityWeightFactoryClass>...</valueProbabilityWeightFactoryClass>
</valueSelector>
[MINOR] Configuration property `moveProbabilityWeightFactoryClass` has been renamed to `probabilityWeightFactoryClass`.
Before in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<...MoveSelector>
<moveProbabilityWeightFactoryClass>...</moveProbabilityWeightFactoryClass>
</...MoveSelector>
After in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<...MoveSelector>
<probabilityWeightFactoryClass>...</probabilityWeightFactoryClass>
</...MoveSelector>
[MINOR] For `entitySelector`, configuration property `planningEntityClass` has been renamed to `entityClass`.
Before in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<entitySelector>
<planningEntityClass>...Lecture</planningEntityClass>
</entitySelector>
After in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<entitySelector>
<entityClass>...Lecture</entityClass>
</entitySelector>
[MAJOR] Configuration property `planningVariableName` has been renamed to `variableName`.
Before in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<valueSelector>
<planningVariableName>period</planningVariableName>
</valueSelector>
After in *SolverConfig.xml and *BenchmarkConfig.xml:
<valueSelector>
<variableName>period</variableName>
</valueSelector>
[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:
<scoreDefinitionType>HARD_AND_SOFT</scoreDefinitionType>
After in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<scoreDefinitionType>HARD_SOFT</scoreDefinitionType>
Before in *.java:
import org.drools.planner.core.score.buildin.hardandsoft.HardAndSoftScore;
...
public class CloudBalance ... implements Solution<HardAndSoftScore> {
...
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<HardSoftScore> {
...
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]
----
<score>0hard/-130870soft</score>
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<SimpleScore> {
...
private SimpleScore score;
...
}
After in *.java:
public class NQueens ... implements Solution<SimpleScore> {
...
@XStreamConverter(value = XStreamScoreConverter.class, types = {SimpleScoreDefinition.class})
private SimpleScore score;
...
}
Before in *.java:
public class CloudBalance ... implements Solution<HardSoftScore> {
...
private HardSoftScore score;
...
}
After in *.java:
public class CloudBalance ... implements Solution<HardSoftScore> {
...
@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/<score class="[^"]*Score"/<score/g' $f > $f.modifiedMigration
sed ':a;N;$!ba;s/>\n *<hardScore>/>/g' $f.modifiedMigration | sed ':a;N;$!ba;s/<\/hardScore>\n *<softScore>/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<HardSoftScore> 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:
<dependency>
<groupId>org.drools.planner</groupId>
<artifactId>drools-planner-core</artifactId>
...
</dependency>
After in pom.xml:
<dependency>
<groupId>org.optaplanner</groupId>
<artifactId>optaplanner-core</artifactId>
...
</dependency>
Before in pom.xml:
<dependency>
<groupId>org.drools.planner</groupId>
<artifactId>drools-planner-benchmark</artifactId>
...
</dependency>
After in pom.xml:
<dependency>
<groupId>org.optaplanner</groupId>
<artifactId>optaplanner-benchmark</artifactId>
...
</dependency>
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:
<logger name="org.drools.planner" level="debug"/>
After in logback*.xml:
<logger name="org.optaplanner" level="debug"/>
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<ConstraintOccurrence> it = (Iterator<ConstraintOccurrence>) 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<ConstraintOccurrence> constraintOccurrences = (Collection<ConstraintOccurrence>)
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:
<!--<environmentMode>FULL_ASSERT</environmentMode>-->
...
<scoreDirectorFactory>
...
<scoreDrl>...ScoreRules.drl</scoreDrl>
<!--<assertionScoreDirectorFactory>-->
<!--<scoreDrl>...ScoreRulesOld.drl</scoreDrl>-->
<!--</assertionScoreDirectorFactory>-->
</scoreDirectorFactory>
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<ScoreDetail> getScoreDetailList() {
if (!(guiScoreDirector instanceof DroolsScoreDirector)) {
return null;
}
Map<String, ScoreDetail> scoreDetailMap = new HashMap<String, ScoreDetail>();
KieSession kieSession = ((DroolsScoreDirector) guiScoreDirector).getKieSession();
if (kieSession == null) {
return Collections.emptyList();
}
Collection<ConstraintOccurrence> constraintOccurrences = (Collection<ConstraintOccurrence>)
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<ScoreDetail> scoreDetailList = new ArrayList<ScoreDetail>(scoreDetailMap.values());
Collections.sort(scoreDetailList);
return scoreDetailList;
After in *.java:
public List<ConstraintMatchTotal> getConstraintMatchTotalList() {
List<ConstraintMatchTotal> constraintMatchTotalList = new ArrayList<ConstraintMatchTotal>(
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:
<forager>
<minimalAcceptedSelection>1000</minimalAcceptedSelection>
</forager>
After in *Config.xml:
<forager>
<acceptedCountLimit>1000</acceptedCountLimit>
</forager>
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 `<lateAcceptanceSize>` 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:
<acceptor>
<planningEntityTabuSize>...</planningEntityTabuSize>
<fadingPlanningEntityTabuSize>...</fadingPlanningEntityTabuSize>
<planningValueTabuSize>...</planningValueTabuSize>
<fadingPlanningValueTabuSize>...</fadingPlanningValueTabuSize>
</acceptor>
After in *Config.xml:
<acceptor>
<entityTabuSize>...</entityTabuSize>
<fadingEntityTabuSize>...</fadingEntityTabuSize>
<valueTabuSize>...</valueTabuSize>
<fadingValueTabuSize>...</fadingValueTabuSize>
</acceptor>
[MINOR] The implementation classes `PlanningEntityTabuAcceptor` and `PlanningValueTabuAcceptor` have been renamed
to `EntityTabuAcceptor` and `ValueTabuAcceptor`
[MINOR] `<subChainChangeMoveSelector>` and `<subChainSwapMoveSelector>` combined with a `<maximumSubChainSize>` 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 `<problemStatisticType>` `BEST_SOLUTION_CHANGED` has been renamed to `BEST_SCORE`
Before in *BenchmarkConfig.xml:
<problemBenchmarks>
...
<problemStatisticType>BEST_SOLUTION_CHANGED</problemStatisticType>
</problemBenchmarks>
After in *BenchmarkConfig.xml:
<problemBenchmarks>
...
<problemStatisticType>BEST_SCORE</problemStatisticType>
</problemBenchmarks>
[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 `<subChainChangeMoveSelector>` and `<subChainSwapMoveSelector>`
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<VrpVehicle> getVehicleList() {
return vehicleList;
}
public Collection<? extends Object> getProblemFacts() {
...
facts.addAll(vehicleList);
return facts;
}
After in VrpSchedule.java:
@PlanningEntityCollectionProperty
public List<VrpVehicle> getVehicleList() {
return vehicleList;
}
public Collection<? extends Object> getProblemFacts() {
...
return facts;
}
Before in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<solver>
...
<planningEntityClass>org.optaplanner.examples.vehiclerouting.domain.VrpCustomer</planningEntityClass>
...
</solver>
After in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<solver>
...
<planningEntityClass>org.optaplanner.examples.vehiclerouting.domain.VrpCustomer</planningEntityClass>
<planningEntityClass>org.optaplanner.examples.vehiclerouting.domain.VrpStandstill</planningEntityClass>
...
</solver>
[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 `<constructionHeuristicPickEarlyType>` has been renamed to `<pickEarlyType>`.
The value `FIRST_LAST_STEP_SCORE_EQUAL_OR_IMPROVING` has been renamed to `FIRST_NON_DETERIORATING_SCORE`.
The `<pickEarlyType>` has been nested into a `<forager>` element (similar like for Local Search).
Before in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<constructionHeuristic>
...
<constructionHeuristicPickEarlyType>FIRST_LAST_STEP_SCORE_EQUAL_OR_IMPROVING</constructionHeuristicPickEarlyType>
</constructionHeuristic>
After in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<constructionHeuristic>
...
<forager>
<pickEarlyType>FIRST_NON_DETERIORATING_SCORE</pickEarlyType>
</forager>
</constructionHeuristic>
[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]
----
<constructionHeuristic>
...
<queuedEntityPlacer>
<entitySelector id="placerEntitySelector">
<cacheType>PHASE</cacheType>
...
</entitySelector>
<cartesianProductMoveSelector>
<changeMoveSelector>
<entitySelector mimicSelectorRef="placerEntitySelector"/>
<valueSelector>
<variableName>secondVariable</variableName>
...
</valueSelector>
</changeMoveSelector>
<changeMoveSelector>
<entitySelector mimicSelectorRef="placerEntitySelector"/>
<valueSelector>
<variableName>firstVariable</variableName>
...
</valueSelector>
</changeMoveSelector>
</cartesianProductMoveSelector>
</queuedEntityPlacer>
</constructionHeuristic>
[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<CloudComputer> 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<CloudComputer> 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<Domicile> getDomicileList() {...}
@PlanningEntityCollectionProperty
public List<Visit> 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<Domicile> getDomicileList() {...}
@PlanningEntityCollectionProperty
@ValueRangeProvider(id = "visitRange")
public List<Visit> 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<ExecutionMode> getExecutionModeRange() {
return job.getExecutionModeList();
}
}
After in *.java:
@PlanningEntity
public class Allocation ... {
...
@PlanningVariable(valueRangeProviderRefs = {"executionModeRange"})
public ExecutionMode getExecutionMode() {
return executionMode;
}
@ValueRangeProvider(id = "executionModeRange")
public List<ExecutionMode> 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 `<acceptor>` 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:
<localSearch>
<acceptor/>
...
</localSearch>
After in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<localSearch>
<acceptor>
<entityTabuSize>7</entityTabuSize>
</acceptor>
...
</localSearch>
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<Move> createMoveList(Solution solution) {
NQueens nQueens = (NQueens) solution;
...
}
}
After in *.java:
public class RowChangeMoveFactory implements MoveListFactory<NQueens> {
public List<Move> 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<Integer>` or `List<BigDecimal>`),
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<Integer> getDelayRange() {
List<Integer> = new ArrayList<Integer>(5000);
for (int i = 0; i < 5000; i++) {
delayRange.add(i);
}
return delayRange;
}
After in *.java:
@ValueRangeProvider(id = "delayRange")
public CountableValueRange<Integer> 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:
<problemBenchmarks>
...
<problemStatisticType>IMPROVING_STEP_PERCENTAGE</problemStatisticType>
</problemBenchmarks>
[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<SolverBenchmark> solverBenchmarkList, SolverBenchmark solverBenchmark) {...}
After in *.java:
public Comparable createRankingWeight(List<SolverBenchmarkResult> solverBenchmarkResultList, SolverBenchmarkResult solverBenchmarkResult) {...}
[MINOR] If you explicitly specify Benchmarker rankings:
All `solverBenchmarkRanking*` properties have been renamed to `solverRanking*`.
Before in *BenchmarkConfig.xml:
<solverBenchmarkRankingType>TOTAL_SCORE</solverBenchmarkRankingType>
After in *BenchmarkConfig.xml:
<solverRankingType>TOTAL_SCORE</solverRankingType>
Before in *BenchmarkConfig.xml:
<solverBenchmarkRankingComparatorClass>...</solverBenchmarkRankingComparatorClass>
After in *BenchmarkConfig.xml:
<solverRankingComparatorClass>...</solverRankingComparatorClass>
Before in *BenchmarkConfig.xml:
<solverBenchmarkRankingWeightFactoryClass>...</solverBenchmarkRankingWeightFactoryClass>
After in *BenchmarkConfig.xml:
<solverRankingWeightFactoryClass>...</solverRankingWeightFactoryClass>
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 `<benchmarkReportLocale>`, `<solverRankingType>`, `<solverRankingComparatorClass>`
and `<solverRankingWeightFactoryClass>` have been moved inside the new element `<benchmarkReport>`.
Before in *BenchmarkConfig.xml:
<benchmarkReportLocale>...</benchmarkReportLocale>
After in *BenchmarkConfig.xml:
<benchmarkReport>
<locale>...</locale>
</benchmarkReport>
Before in *BenchmarkConfig.xml:
<solverRankingType>...</solverRankingType>
After in *BenchmarkConfig.xml:
<benchmarkReport>
<solverRankingType>...</solverRankingType>
</benchmarkReport>
[MINOR] If you explicitly configured all your `<entitySelector>` elements with `<cacheType>PHASE</cacheType>` 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 `<entitySelector>`.
Before in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<unionMoveSelector>
<changeMoveSelector>
<entitySelector>
<cacheType>PHASE</cacheType>
</entitySelector>
</changeMoveSelector>
<swapMoveSelector>
<entitySelector>
<cacheType>PHASE</cacheType>
</entitySelector>
</swapMoveSelector>
</unionMoveSelector>
After in *SolverConfig.xml and *BenchmarkConfig.xml:
<unionMoveSelector>
<changeMoveSelector/>
<swapMoveSelector/>
</unionMoveSelector>
[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 `<termination>` have been renamed:
The element `<maximumTimeMillisSpend>` has been renamed to `<millisecondsSpentLimit>`
The element `<maximumSecondsSpend>` has been renamed to `<secondsSpentLimit>`
The element `<maximumMinutesSpend>` has been renamed to `<minutesSpentLimit>`
The element `<maximumHoursSpend>` has been renamed to `<hoursSpentLimit>`
The element `<scoreAttained>` has been renamed to `<bestScoreLimit>`
The element `<maximumStepCount>` has been renamed to `<stepCountLimit>`
The element `<maximumUnimprovedStepCount>` has been renamed to `<unimprovedStepCountLimit>`
Before in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<termination>
<maximumTimeMillisSpend>...</maximumTimeMillisSpend>
<maximumSecondsSpend>...</maximumSecondsSpend>
<maximumMinutesSpend>...</maximumMinutesSpend>
<maximumHoursSpend>...</maximumHoursSpend>
<scoreAttained>...</scoreAttained>
<maximumStepCount>...</maximumStepCount>
<maximumUnimprovedStepCount>...</maximumUnimprovedStepCount>
</termination>
After in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<termination>
<millisecondsSpentLimit>...</millisecondsSpentLimit>
<secondsSpentLimit>...</secondsSpentLimit>
<minutesSpentLimit>...</minutesSpentLimit>
<hoursSpentLimit>...</hoursSpentLimit>
<bestScoreLimit>...</bestScoreLimit>
<stepCountLimit>...</stepCountLimit>
<unimprovedStepCountLimit>...</unimprovedStepCountLimit>
</termination>
[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 `<warmUp*>` elements have been renamed:
The element `<warmUpTimeMillisSpend>` has been renamed to `<warmUpMillisecondsSpentLimit>`
The element `<warmUpSecondsSpend>` has been renamed to `<warmUpSecondsSpentLimit>`
The element `<warmUpMinutesSpend>` has been renamed to `<warmUpMinutesSpentLimit>`
The element `<warmUpHoursSpend>` has been renamed to `<warmUpHoursSpentLimit>`
Before in *BenchmarkConfig.xml:
<plannerBenchmark>
<warmUpTimeMillisSpend>...</warmUpTimeMillisSpend>
<warmUpSecondsSpend>...</warmUpSecondsSpend>
<warmUpMinutesSpend>...</warmUpMinutesSpend>
<warmUpHoursSpend>...</warmUpHoursSpend>
...
<//plannerBenchmark>
After in *BenchmarkConfig.xml:
<plannerBenchmark>
<warmUpMillisecondsSpentLimit>...</warmUpMillisecondsSpentLimit>
<warmUpSecondsSpentLimit>...</warmUpSecondsSpentLimit>
<warmUpMinutesSpentLimit>...</warmUpMinutesSpentLimit>
<warmUpHoursSpentLimit>...</warmUpHoursSpentLimit>
...
<//plannerBenchmark>
[MINOR] Real time planning: `addProblemFactChange(...)` no longer causes solver `Termination`s 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 `<acceptorType>` 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:
<acceptorType>PLANNING_ENTITY_TABU</acceptorType>
After in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<acceptorType>ENTITY_TABU</acceptorType>
Before in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<acceptorType>PLANNING_VALUE_TABU</acceptorType>
After in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<acceptorType>VALUE_TABU</acceptorType>
[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<Customer> {
After in *.java:
public class VehicleUpdatingVariableListener implements VariableListener<Customer> {
[MAJOR] The class `AbstractPlanningVariableListener` has been removed.
Before in *.java:
public class VehicleUpdatingVariableListener extends AbstractPlanningVariableListener<Customer> {
After in *.java:
public class VehicleUpdatingVariableListener implements VariableListener<Customer> {
[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:
<greatDelugeWaterLevelUpperBoundRate>...</greatDelugeWaterLevelUpperBoundRate>
<greatDelugeWaterRisingRate>...</greatDelugeWaterRisingRate>
[RECOMMENDED] Specify an `<initializingScoreTrend>` in the `<scoreDirectorFactory>`,
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:
<scoreDirectorFactory>
<scoreDefinitionType>HARD_SOFT</scoreDefinitionType>
<scoreDrl>.../cloudBalancingScoreRules.drl</scoreDrl>
</scoreDirectorFactory>
After in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<scoreDirectorFactory>
<scoreDefinitionType>HARD_SOFT</scoreDefinitionType>
<scoreDrl>.../cloudBalancingScoreRules.drl</scoreDrl>
<initializingScoreTrend>ONLY_DOWN</initializingScoreTrend>
</scoreDirectorFactory>
[MAJOR] Replace `<pickEarlyType>` `FIRST_NON_DETERIORATING_SCORE` with `<initializingScoreTrend>` `ONLY_DOWN`.
If the `<initializingScoreTrend>` is specified,
the `<constructionHeuristic>` will automatically use the most appropriate `<pickEarlyType>`.
Before in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<scoreDirectorFactory>
...
</scoreDirectorFactory>
...
<constructionHeuristic>
<constructionHeuristicType>FIRST_FIT_DECREASING</constructionHeuristicType>
<forager>
<pickEarlyType>FIRST_NON_DETERIORATING_SCORE</pickEarlyType>
</forager>
</constructionHeuristic>
After in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<scoreDirectorFactory>
...
<initializingScoreTrend>ONLY_DOWN</initializingScoreTrend>
</scoreDirectorFactory>
...
<constructionHeuristic>
<constructionHeuristicType>FIRST_FIT_DECREASING</constructionHeuristicType>
</constructionHeuristic>
[MAJOR] The solver phase `<bruteForce>` has been replaced by `<exhaustiveSearch>` 's `BRUTE_FORCE` type.
Before in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<bruteForce/>
After in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<exhaustiveSearch>
<exhaustiveSearchType>BRUTE_FORCE</exhaustiveSearchType>
</exhaustiveSearch>
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 `<swapMoveSelector>` 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:
<swapMoveSelector>
<filterClass>...ValidSwapsOnlySwapMoveFilter</filterClass>
</swapMoveSelector>
After in *SolverConfig.xml and *BenchmarkConfig.xml:
<swapMoveSelector/>
[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 `<xstreamAnnotatedClass>` has been renamed to `<xStreamAnnotatedClass>`.
Before in *BenchmarkConfig.xml:
<problemBenchmarks>
<xstreamAnnotatedClass>org.optaplanner.examples.nqueens.domain.NQueens</xstreamAnnotatedClass>
...
</problemBenchmarks>
After in *BenchmarkConfig.xml:
<problemBenchmarks>
<xStreamAnnotatedClass>org.optaplanner.examples.nqueens.domain.NQueens</xStreamAnnotatedClass>
...
</problemBenchmarks>
[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 `<scoreDrl>` must lose their leading slash.
Before in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<scoreDrl>/org/optaplanner/examples/cloudbalancing/solver/cloudBalancingScoreRules.drl</scoreDrl>
After in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<scoreDrl>org/optaplanner/examples/cloudbalancing/solver/cloudBalancingScoreRules.drl</scoreDrl>
[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<CloudBalance> {
...
}
After in *.java:
import org.optaplanner.core.impl.score.director.easy.EasyScoreCalculator;
public class CloudBalancingEasyScoreCalculator implements EasyScoreCalculator<CloudBalance> {
...
}
Before in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<simpleScoreCalculatorClass>org.optaplanner.examples.cloudbalancing.solver.score.CloudBalancingEasyScoreCalculator<simpleScoreCalculatorClass>
After in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<easyScoreCalculatorClass>org.optaplanner.examples.cloudbalancing.solver.score.CloudBalancingEasyScoreCalculator<easyScoreCalculatorClass>
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 `<bestScoreLimit>`:
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<CloudBalance>() {
public void bestSolutionChanged(BestSolutionChangedEvent<CloudBalance> 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<CloudBalance> 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] `<subChainChangeMoveSelector>` and `<subChainSwapMoveSelector>`'s `<subChainSelector>`s now default
to a `<minimumSubChainSize>` of 1 instead of 2.
This is to enable `<subChainSwapMoveSelector>` to swap a subchain of size 1 and a subchain of at least size 2 too.
[MINOR] `<pillarSwapMoveSelector>'s `<pillarSelector>` now selects subpillars too by default.
Normally, that's an improvement.
Before in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<pillarSwapMoveSelector/>
After in *SolverConfig.xml` and `*BenchmarkConfig.xml (if you don't want this new behaviour):
<pillarSwapMoveSelector>
<pillarSelector>
<subPillarEnabled>false</subPillarEnabled>
</pillarSelector>
</pillarSwapMoveSelector>
Note: `<pillarChangeMoveSelector>` is not supported too
[MINOR] `SolverPhase` has been renamed to `Phase`. `SolverPhaseConfig` has been renamed to `PhaseConfig`.
Before in *.java:
List<SolverPhaseConfig> solverPhaseConfigList = new ArrayList<SolverPhaseConfig>();
ConstructionHeuristicSolverPhaseConfig solverPhaseConfig = new ConstructionHeuristicSolverPhaseConfig();
...
solverPhaseConfigList.add(solverPhaseConfig);
...
solverConfig.setPhaseConfigList(phaseConfigList);
After in *.java:
List<PhaseConfig> phaseConfigList = new ArrayList<PhaseConfig>();
ConstructionHeuristicPhaseConfig phaseConfig = new ConstructionHeuristicPhaseConfig();
...
phaseConfigList.add(phaseConfig);
...
solverConfig.setPhaseConfigList(phaseConfigList);
[MAJOR] The interface `CustomSolverPhaseCommand` has been renamed to `CustomPhaseCommand`.
The element `<customSolverPhase>` has been renamed to `<customPhase>`.
The element `<customSolverPhaseCommandClass>` has been renamed to `<customPhaseCommandClass>`.
Before in *.java:
public class ToOriginalMachineSolutionInitializer implements CustomSolverPhaseCommand {
...
}
After in *.java:
public class ToOriginalMachineSolutionInitializer implements CustomPhaseCommand {
...
}
Before in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<customSolverPhase>
<customSolverPhaseCommandClass>...ToOriginalMachineSolutionInitializer</customSolverPhaseCommandClass>
</customSolverPhase>
After in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<customPhase>
<customPhaseCommandClass>....ToOriginalMachineSolutionInitializer</customPhaseCommandClass>
</customPhase>
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:
<scoreDirectorFactory>
<scoreDefinitionType>BENDABLE</scoreDefinitionType>
<bendableHardLevelCount>2</bendableHardLevelCount>
<bendableSoftLevelCount>3</bendableSoftLevelCount>
...
</scoreDirectorFactory>
After in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<scoreDirectorFactory>
<scoreDefinitionType>BENDABLE</scoreDefinitionType>
<bendableHardLevelsSize>2</bendableHardLevelsSize>
<bendableSoftLevelsSize>3</bendableSoftLevelsSize>
...
</scoreDirectorFactory>
[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:
<constructionHeuristic>
<constructionHeuristicType>BEST_FIT</constructionHeuristicType>
</constructionHeuristic>
After in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<constructionHeuristic>
<constructionHeuristicType>WEAKEST_FIT</constructionHeuristicType>
</constructionHeuristic>
[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:
<constructionHeuristic>
<constructionHeuristicType>BEST_FIT_DECREASING</constructionHeuristicType>
</constructionHeuristic>
After in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<constructionHeuristic>
<constructionHeuristicType>WEAKEST_FIT_DECREASING</constructionHeuristicType>
</constructionHeuristic>
[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:
<planningEntityClass>org.optaplanner.examples.vehiclerouting.domain.timewindowed.TimeWindowedCustomer</planningEntityClass>
[MAJOR] Multiple `<planningEntityClass>` elements now need to be ordered by superclasses (and superinterfaces) first,
instead of superclasses (and superinterfaces) last.
Before in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<planningEntityClass>...TimeWindowedCustomer</planningEntityClass>
<planningEntityClass>...Customer</planningEntityClass>
<planningEntityClass>...Standstill</planningEntityClass>
After in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<planningEntityClass>...Standstill</planningEntityClass>
<planningEntityClass>...Customer</planningEntityClass>
<planningEntityClass>...TimeWindowedCustomer</planningEntityClass>
[MAJOR] The element `<planningEntityClass>` has been renamed to `<entityClass>`.
Before in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<planningEntityClass>org.optaplanner.examples.cloudbalancing.domain.CloudProcess</planningEntityClass>
After in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<entityClass>org.optaplanner.examples.cloudbalancing.domain.CloudProcess</entityClass>
[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:
<problemBenchmarks>
<problemIOClass>...MachineReassignmentProblemIO</problemIOClass>
...
</problemBenchmarks>
After in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<problemBenchmarks>
<solutionFileIOClass>...MachineReassignmentFileIO</solutionFileIOClass>
...
</problemBenchmarks>
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 `<pillarChangeMoveSelector>` and `<pillarSwapMoveSelector>` 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:
<pillarChangeMoveSelector>
<filterClass>...ValidChangesOnlyPillarChangeMoveFilter</filterClass>
</pillarChangeMoveSelector>
<pillarSwapMoveSelector>
<filterClass>...ValidSwapsOnlyPillarSwapMoveFilter</filterClass>
</pillarSwapMoveSelector>
After in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<pillarChangeMoveSelector/>
<pillarSwapMoveSelector/>
[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 `<deciderScoreComparatorFactory>` (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:
<localSearch>
...
<forager>
...
<deciderScoreComparatorFactory>
...
</deciderScoreComparatorFactory>
</forager>
</localSearch>
After in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<localSearch>
...
<forager>
...
<finalistPodiumType>STRATEGIC_OSCILLATION</finalistPodiumType>
</forager>
</localSearch>
[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<Move>` 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<Customer> {
...
}
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 `<twoOptMoveSelector>` has been replaced by `<tailChainSwapMoveSelector>`,
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<CloudProcess> getProcessList() {
return processList;
}
}
After in *.java:
@PlanningEntity // Shadow variable only
public class CloudComputer {
...
@InverseRelationShadowVariable(sourceVariableName = "computer")
public List<CloudProcess> 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: an empty `<constructionHeuristic>`'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:
<solverBenchmarkBluePrintType>ALL_CONSTRUCTION_HEURISTIC_TYPES</solverBenchmarkBluePrintType>
After in *BenchmarkConfig.xml:
<solverBenchmarkBluePrintType>EVERY_CONSTRUCTION_HEURISTIC_TYPE</solverBenchmarkBluePrintType>
[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.
From 6.3.0.Beta1 to 6.3.0.Beta2
-------------------------------
[MINOR] If you implemented a custom `ValueRange`, also implement the method `isEmpty()`.
Normally, you should not have any need for a custom `ValueRange`, because `ValueRangeFactory` supports all sensible ranges.
Before in *.java:
public class MyDoubleValueRange extends AbstractUncountableValueRange<Double> {
...
}
After in *.java:
public class MyDoubleValueRange extends AbstractUncountableValueRange<Double> {
...
@Override
public boolean isEmpty() {
return from == to;
}
}
[MAJOR] If you use multiple planning variables, consider switching to the folded configuration.
Before in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<changeMoveSelector>
<valueSelector>
<variableName>period</variableName>
</valueSelector>
</changeMoveSelector>
<changeMoveSelector>
<valueSelector>
<variableName>room</variableName>
</valueSelector>
</changeMoveSelector>
After in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<changeMoveSelector/>
[MAJOR] If you use multiple entity classes, consider switching to the folded configuration.
Before in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<changeMoveSelector>
<entitySelector>
<entityClass>...CoachEntity</entityClass>
</entitySelector>
</changeMoveSelector>
<changeMoveSelector>
<entitySelector>
<entityClass>...ShuttleEntity</entityClass>
</entitySelector>
</changeMoveSelector>
<swapMoveSelector>
<entitySelector>
<entityClass>...CoachEntity</entityClass>
</entitySelector>
</swapMoveSelector>
<swapMoveSelector>
<entitySelector>
<entityClass>...ShuttleEntity</entityClass>
</entitySelector>
</swapMoveSelector>
After in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<changeMoveSelector/>
<swapMoveSelector/>
[MINOR] If your planning solution has a superclass with planner annotations,
those will now be ignored (just like solution subclass annotations are ignored
and just like entity superclass or subclass annotations are ignored unless they are a declare planning entity class too).
Declare the `@PlanningSolution` on the superclass instead, the solver will handle subclass instances gracefully
(presuming there are no planner annotations in the subclass).
Before in *.java:
public abstract class ParentSolution {
@ValueRangeProvider(...)
public List<Computer> getComputers() {...}
}
@PlanningSolution
public class ChildSolution extends ParentSolution {...}
Before in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<solutionClass>...ChildSolution</solutionClass>
After in *.java:
@PlanningSolution
public abstract class ParentSolution {
@ValueRangeProvider(...)
public List<Computer> getComputers() {...}
}
public class ChildSolution extends ParentSolution {...}
After in *SolverConfig.xml` and `*BenchmarkConfig.xml:
<solutionClass>...ParentSolution</solutionClass>
From 6.3.0.Beta2 to 6.3.0.CR1
-----------------------------
[MINOR] If a custom `VariableListener` changes 2 shadow variables, use the new `variableListenerRef` property accordingly
to indicate that the `VariableListener` class of another shadow variable also updates this shadow variable:
Before in *.java:
@PlanningVariable(...)
public Standstill getPreviousStandstill() {
return previousStandstill;
}
@CustomShadowVariable(variableListenerClass = TransportTimeAndCapacityUpdatingVariableListener.class,
sources = {@CustomShadowVariable.Source(variableName = "previousStandstill")})
public Integer getTransportTime() {
return transportTime;
}
@CustomShadowVariable(variableListenerClass = DummyListener.class, sources = ...)
public Integer getCapacity() {
return capacity;
}
After in *.java:
@PlanningVariable(...)
public Standstill getPreviousStandstill() {
return previousStandstill;
}
@CustomShadowVariable(variableListenerClass = TransportTimeAndCapacityUpdatingVariableListener.class,
sources = {@CustomShadowVariable.Source(variableName = "previousStandstill")})
public Integer getTransportTime() {
return transportTime;
}
@CustomShadowVariable(variableListenerRef = @PlanningVariableReference(variableName = "transportTime"))
public Integer getCapacity() {
return capacity;
}
From 6.3.0.CR1 to 6.3.0.CR2
---------------------------
[README] `VariableListeners` no longer trigger chaotically. This applies to both out of the box shadow variables and custom shadow variables.
Planner now guarantees that the first `VariableListener` 's `after*()` method triggers *AFTER* the last genuine variable has been modified.
This means a `VariableListener` is no longer exposed to intermediate state.
The `before*()` methods still trigger immediately (otherwise they would not be able to capture the source variable's orginal state).
Furthermore, Planner guarantees this triggering in stages also for `VariableListener` for a shadow variable that depend on earlier shadow variables.
[MAJOR] If you have a custom `Move`, its `doMove()` method must now call `scoreDirector.triggerVariableListeners()` at the end.
In practice, you should have extended `AbstractMove` - which does the `triggerVariableListeners()` call for you -
but you'll need to rename your `doMove()` method to `doMoveOnGenuineVariables()`.
Before in *.java:
public class CloudComputerChangeMove extends AbstractMove {
...
public void doMove(ScoreDirector scoreDirector) {
CloudBalancingMoveHelper.moveCloudComputer(scoreDirector, cloudProcess, toCloudComputer);
}
}
After in *.java:
public class CloudComputerChangeMove extends AbstractMove {
...
protected void doMoveOnGenuineVariables(ScoreDirector scoreDirector) {
CloudBalancingMoveHelper.moveCloudComputer(scoreDirector, cloudProcess, toCloudComputer);
}
}
[MAJOR] If you have a `ProblemFactChange`, its `doChange()` method must now call `scoreDirector.triggerVariableListeners()`
after every set of changes (before calling `calculateScore()` or relying on shadow variables).
Before in *.java:
public void deleteComputer(final CloudComputer computer) {
doProblemFactChange(new ProblemFactChange() {
public void doChange(ScoreDirector scoreDirector) {
CloudBalance cloudBalance = (CloudBalance) scoreDirector.getWorkingSolution();
// First remove the problem fact from all planning entities that use it
for (CloudProcess process : cloudBalance.getProcessList()) {
if (ObjectUtils.equals(process.getComputer(), computer)) {
scoreDirector.beforeVariableChanged(process, "computer");
process.setComputer(null);
scoreDirector.afterVariableChanged(process, "computer");
}
}
...
}
});
}
After in *.java:
public void deleteComputer(final CloudComputer computer) {
doProblemFactChange(new ProblemFactChange() {
public void doChange(ScoreDirector scoreDirector) {
CloudBalance cloudBalance = (CloudBalance) scoreDirector.getWorkingSolution();
// First remove the problem fact from all planning entities that use it
for (CloudProcess process : cloudBalance.getProcessList()) {
if (ObjectUtils.equals(process.getComputer(), computer)) {
scoreDirector.beforeVariableChanged(process, "computer");
process.setComputer(null);
scoreDirector.afterVariableChanged(process, "computer");
}
}
scoreDirector.triggerVariableListeners();
...
}
});
}
[MAJOR] If you have a `CustomPhaseCommand`, its `changeWorkingSolution()` method must now call `scoreDirector.triggerVariableListeners()`
after every set of changes (before calling `calculateScore()` or relying on shadow variables).
Before in *.java:
public class MyCustomPhase implements CustomPhaseCommand {
public void changeWorkingSolution(ScoreDirector scoreDirector) {
scoreDirector.beforeVariableChanged(processAssignment, "machine");
processAssignment.setMachine(machine);
scoreDirector.afterVariableChanged(processAssignment, "machine");
Score score = scoreDirector.calculateScore();
}
}
After in *.java:
public class MyCustomPhase implements CustomPhaseCommand {
public void changeWorkingSolution(ScoreDirector scoreDirector) {
scoreDirector.beforeVariableChanged(processAssignment, "machine");
processAssignment.setMachine(machine);
scoreDirector.afterVariableChanged(processAssignment, "machine");
scoreDirector.triggerVariableListeners();
Score score = scoreDirector.calculateScore();
}
}
[MINOR] A custom `Move` must now read any shadow variables it needs before its first `beforeVariableChanged()` call.
It no longer needs to assign genuine variables to intermediate values to avoid errors in the `VariableListeners` that update shadow variables.
[IMPL_DETAIL] All built-in moves that affect chained variables have been greatly simplified due to the new `VariableListener` guarantee.
[IMPL_DETAIL] The constructor of `ChainedChangeMove`, `ChainedSwapMove`, `SubChainChangeMove` and `SubChainSwapMove` now require the `SingletonInverseVariableSupply` parameter.
Before in *.java:
return new ChainedChangeMove(entity, variableDescriptor, toValue);
After in *.java:
SingletonInverseVariableSupply inverseVariableSupply = ((InnerScoreDirector) scoreDirector).getSupplyManager()
.demand(new SingletonInverseVariableDemand(variableDescriptor));
return new ChainedChangeMove(entity, variableDescriptor, inverseVariableSupply, toValue);
[IMPL_DETAIL] `ChainedSwapMove` 's constructor now requires a `List` instead of `Collection` of `VariableDescriptor` s.
[IMPL_DETAIL] The method `InnerScoreDirector.getTrailingEntity()` has been removed. Use `SingletonInverseVariableSupply` instead.
[MINOR] One score rule can now change 2 score levels in its RHS
Before in *.drl:
rule "Costly and unfair: part 1"
when
// Complex pattern
then
scoreHolder.addMediumConstraintMatch(kcontext, -1); // Financial cost
end
rule "Costly and unfair: part 2"
when
// Complex pattern (duplication)
then
scoreHolder.addSoftConstraintMatch(kcontext, -1); // Employee happiness cost
end
After in *.drl:
rule "Costly and unfair"
when
// Complex pattern
then
scoreHolder.addMediumConstraintMatch(kcontext, -1); // Financial cost
scoreHolder.addSoftConstraintMatch(kcontext, -1); // Employee happiness cost
end
From 6.3.0.CR2 to 6.3.0.CR3
---------------------------
[MINOR] If you build a `Solver` entirely from API (not recommended - it's better to load it partially from XML),
use `SolverFactory.createEmpty()` and `solverFactory.getSolverConfig()` accordingly.
Before in *.java:
SolverConfig solverConfig = new SolverConfig();
...
return solverConfig.buildSolver();
After in *.java:
SolverFactory solverFactory = SolverFactory.createEmpty();
SolverConfig solverConfig = solverFactory.getSolverConfig();
...
return solverFactory.buildSolver();
From 6.3.0.Final to 6.4.0.Beta1
-------------------------------
[MINOR] If you have a custom `ScoreDefinition`: that interface has new method `getLevelLabels()`.
`AbstractScoreDefinition` and `AbstractFeasibilityScoreDefinition` now expect those `levelLabels` as a constructor parameter.
Before in *.java:
public class HardSoftScoreDefinition extends AbstractFeasibilityScoreDefinition<HardSoftScore> {
public HardSoftScoreDefinition() {}
public int getLevelsSize() {
return 2;
}
...
}
After in *.java:
public class HardSoftScoreDefinition extends AbstractFeasibilityScoreDefinition<HardSoftScore> {
public HardSoftScoreDefinition() {
super(new String[]{"hard score", "soft score"});
}
...
}
[MAJOR] If you're using `SolutionFileIO`: that class has been moved from the `optaplanner-benchmark` jar to `optaplanner-persistence-common` jar.
After in pom.xml:
<dependency>
<groupId>org.optaplanner</groupId>
<artifactId>optaplanner-persistence-common</artifactId>
</dependency>
[MINOR] If you're using `XStreamScoreConverter`: that class has been moved from the `optaplanner-benchmark` jar to `optaplanner-persistence-xstream` jar.
After in pom.xml:
<dependency>
<groupId>org.optaplanner</groupId>
<artifactId>optaplanner-persistence-xstream</artifactId>
</dependency>
[MINOR] If you're using `BendableScore` and XStream: `XStreamBendableScoreConverter` (which only supported `BendableScore`) has been deleted.
The generic `XStreamScoreConverter` now supports `BendableScore`, `BendableLongScore` and `BendableBigDecimalScore` too.
Before in *.java:
@XStreamConverter(value = XStreamBendableScoreConverter.class, ints = {1, 2})
private BendableScore score;
After in *.java:
@XStreamConverter(value = XStreamScoreConverter.class, types = {BendableScoreDefinition.class}, ints = {1, 2})
private BendableScore score;
[MINOR] If you have a custom `ScoreDefinition`: that interface has another new method `fromLevelNumbers()`.
It does the opposite of `Score.toLevelNumbers()`.
After in *.java:
public class HardSoftScoreDefinition extends AbstractFeasibilityScoreDefinition<HardSoftScore> {
...
@Override
public HardSoftScore fromLevelNumbers(Number[] levelNumbers) {
if (levelNumbers.length != getLevelsSize()) {
throw new IllegalStateException("The levelNumbers (" + Arrays.toString(levelNumbers)
+ ")'s length (" + levelNumbers.length + ") must equal the levelSize (" + getLevelsSize() + ").");
}
return HardSoftScore.valueOf((Integer) levelNumbers[0], (Integer) levelNumbers[1]);
}
}
[README] If you're using JPA-Hibernate with OptaPlanner, you'll want to take advantage of the new module `optaplanner-persistence-jpa`.
See docs chapter "Integration" for more information.
[MINOR] If you're using `SingleBenchmarkResult`: the functionality of the method `getScore()` and `getUninitializedVariableCount()` has moved to
`getAverageScore()` and `getAverageUninitializedVariableCount()` because of the addition of `SubSingleBenchmarkResult` (Statistical benchmarking).
Before in *.java:
singleBenchmarkResult.getScore();
singleBenchmarkResult.getUninitializedVariableCount();
After in *.java:
singleBenchmarkResult.getAverageScore();
singleBenchmarkResult.getAverageUninitializedVariableCount();
[IMPL_DETAIL] If you're using `SingleStatistic` or any of it's subclasses: all of them were renamed to include "Sub" as a part of
Statistical benchmarking. The package `org.optaplanner.benchmark.impl.statistic.single` was also renamed to
`org.optaplanner.benchmark.impl.statistic.subsingle`. `SingleStatisticType` has not been changed, therefore no changes
in the configuration are needed.
Before in *.java:
import org.optaplanner.benchmark.impl.statistic.single.*;
*SingleStatistic singleStatistic;
After in *.java:
import org.optaplanner.benchmark.impl.statistic.subsingle.*;
*SubSingleStatistic subSingleStatistic;
[MINOR] If you're configuring the `SolverFactory` dynamically for each user request with differ settings,
use `SolverFactory.cloneSolverFactory()` to avoid a race condition.
[MINOR] The method `TerminationConfig.clone()` has been removed.
The `inherit()` method now guarantees that afterwards changing the child or parent will not affect the other.
Before in *.java:
TerminationConfig clone = terminationConfig.clone();
After in *.java:
TerminationConfig clone = new TerminationConfig();
clone.inherit(terminationConfig);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment