<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body text="#000000" bgcolor="#ffffff">
Hi guys<br>
<br>
For Drools Planner 5.3.0 (not the upcoming release)<br>
I am working on a separate branch [1] to allow Planner to understand
your domain model better.<br>
<b>This will force some big changes upon you as a user</b>, but it
will also allow many new features.<br>
Reading and replying to this mail is your chance to steer those
changes, and verify that they are a good thing for your
implementation too.<br>
I'll push those changes to master in a day or 2 ... unless someone
finds a good reason not too.<br>
<br>
Any feed-back, especially on concept names, is welcome.<br>
<br>
<b>The Bad News</b><br>
<br>
You'd have to do some serious upgrading changes. Although I am
confident this can be done in an hour or 2.<br>
Here is the upgrading recipe as in github. Please go through to this
list to understand the impact of these changes.<br>
Once the changes are on master, I 'll update the reference manual.<br>
<br>
<br>
[MAJOR] You need to define your solution class in the configuration
now:<br>
Before in *SolverConfig.xml and *BenchmarkConfig.xml:<br>
<localSearchSolver><br>
<scoreDrl>...</scoreDrl><br>
After in *SolverConfig.xml and *BenchmarkConfig.xml:<br>
<localSearchSolver><br>
<solutionClass>org.drools.planner.examples.curriculumcourse.domain.CurriculumCourseSchedule</solutionClass><br>
<scoreDrl>...</scoreDrl><br>
<br>
[RECOMMENDED] Understand the concept of a "planning entity" class.<br>
The class (or classes) that change during planning (and do not
implement Solution) are a planning entity.<br>
For example: ShiftAssignment, BedDesignation, Queen,
CloudAssignment, ...<br>
The other domain classes are considered normal planning facts,<br>
for example Shift, Employee, Bed, Room, Department, ...<br>
They do not change during planning (at least not without pausing the
solver).<br>
Read the manual to understand the "planning entity" concept better.<br>
<br>
[MAJOR] You need to define your planning entity class(es) in the
configuration now:<br>
Before in *SolverConfig.xml and *BenchmarkConfig.xml:<br>
<localSearchSolver><br>
<solutionClass>....</solutionClass><br>
<scoreDrl>...</scoreDrl><br>
After in *SolverConfig.xml and *BenchmarkConfig.xml:<br>
<localSearchSolver><br>
<solutionClass>....</solutionClass><br>
<planningEntityClass>org.drools.planner.examples.curriculumcourse.domain.Lecture</planningEntityClass><br>
<scoreDrl>...</scoreDrl><br>
<br>
[MAJOR] You need to annotate your planning entity class(es) with the
@PlanningEntity annotation<br>
Before in *.java:<br>
public class Lecture ... {<br>
...<br>
}<br>
After in *.java:<br>
@PlanningEntity<br>
public class Lecture ... {<br>
...<br>
}<br>
<br>
[RECOMMENDED] Understand the concept of a "planning variable"
property.<br>
The property (or properties) on a planning entity class that are
changed (through their setter) during planning<br>
are planning variables.<br>
For example: ShiftAssignment.getEmployee(), BedDesignation.getBed(),
Queen.getY(), ...<br>
Note that most planning entities have 1 property which defines the
planning entity<br>
and that property is NOT a planning variable.<br>
For example: ShiftAssignment.getShift(),
BedDesignation.getAdmissionPart(), Queen.getX(), ...<br>
Read the manual to understand the "planning variable" concept
better.<br>
<br>
[MAJOR] You need to annotate your planning variable property(ies)
with the @PlanningVariable annotation.<br>
Furthermore, you need to annotate a @ValueRange* annotation on to
define the allowed values.<br>
Commonly, you 'll use @ValueRangeFromSolutionProperty which
specifies a property name on the solution<br>
which returns a collection of the allowed values for that variable.<br>
Before in *.java:<br>
@PlanningEntity<br>
public class Lecture ... {<br>
<br>
private Course course;<br>
private int lectureIndexInCourse;<br>
<br>
// Changed by moves, between score calculations.<br>
private Period period;<br>
private Room room;<br>
<br>
public Course getCourse() {...}<br>
public void setCourse(Course course) {...}<br>
<br>
public int getLectureIndexInCourse() {...}<br>
public void setLectureIndexInCourse(int
lectureIndexInCourse) {...}<br>
<br>
public Period getPeriod() {...}<br>
public void setPeriod(Period period) {...}<br>
<br>
public Room getRoom() {...}<br>
public void setRoom(Room room) {...}<br>
<br>
...<br>
<br>
public int getStudentSize() {<br>
return course.getStudentSize();<br>
}<br>
<br>
public Day getDay() {<br>
return period.getDay();<br>
}<br>
<br>
}<br>
After in *.java:<br>
@PlanningEntity<br>
public class Lecture ... {<br>
<br>
private Course course;<br>
private int lectureIndexInCourse;<br>
<br>
// Changed by moves, between score calculations.<br>
private Period period;<br>
private Room room;<br>
<br>
// This is not a PlanningVariable: it defines the planning
entity<br>
public Course getCourse() {...}<br>
public void setCourse(Course course) {...}<br>
<br>
// This is not a PlanningVariable: it defines the planning
entity<br>
public int getLectureIndexInCourse() {...}<br>
public void setLectureIndexInCourse(int
lectureIndexInCourse) {...}<br>
<br>
@PlanningVariable<br>
@ValueRangeFromSolutionProperty(propertyName = "periodList")<br>
public Period getPeriod() {...}<br>
public void setPeriod(Period period) {...}<br>
<br>
@PlanningVariable<br>
@ValueRangeFromSolutionProperty(propertyName = "roomList")<br>
public Room getRoom() {...}<br>
public void setRoom(Room room) {...}<br>
<br>
...<br>
<br>
// This is not a PlanningVariable: no setter<br>
public int getStudentSize() {<br>
return course.getStudentSize();<br>
}<br>
<br>
// This is not a PlanningVariable: no setter<br>
public Day getDay() {<br>
return period.getDay();<br>
}<br>
<br>
}<br>
<br>
[MAJOR] Annotate every property on your Solution that returns a
collection of planning entities<br>
with @PlanningEntityCollectionProperty.<br>
Before in *.java:<br>
public class CurriculumCourseSchedule ... implements
Solution<...> {<br>
<br>
private List<Lecture> lectureList;<br>
<br>
...<br>
<br>
public List<Lecture> getLectureList() {...}<br>
public void setLectureList(List<Lecture> lectureList)
{...}<br>
<br>
}<br>
After in *.java:<br>
public class CurriculumCourseSchedule ... implements
Solution<...> {<br>
<br>
private List<Lecture> lectureList;<br>
<br>
...<br>
<br>
@PlanningEntityCollectionProperty<br>
public List<Lecture> getLectureList() {...}<br>
public void setLectureList(List<Lecture> lectureList)
{...}<br>
<br>
}<br>
<br>
[MAJOR] The method getFacts() has been removed from the Solution
interface.<br>
Annotate every property that returns a fact or fact collection with
the @PlanningFactProperty<br>
or @PlanningFactCollectionProperty annotation respectively,<br>
except those already annotated with
@PlanningEntityCollectionProperty.<br>
Properties annotated with these annotations are inserted into the
working memory as facts:<br>
- @PlanningFactProperty<br>
- @PlanningFactCollectionProperty: each element in the collection<br>
- @PlanningEntityCollectionProperty: each planning entity in the
collection that is initialized<br>
Remove the getFacts() method.<br>
Before in *.java:<br>
public class ... implements Solution<...> {<br>
<br>
private InstitutionalWeighting institutionalWeighting;<br>
<br>
private List<Teacher> teacherList;<br>
private List<Curriculum> curriculumList;<br>
...<br>
private List<UnavailablePeriodConstraint>
unavailablePeriodConstraintList;<br>
<br>
private List<Lecture> lectureList;<br>
<br>
private HardAndSoftScore score;<br>
<br>
...<br>
<br>
public String getName() {...}<br>
<br>
public InstitutionalWeighting getInstitutionalWeighting()
{...}<br>
<br>
public List<Teacher> getTeacherList() {...}<br>
<br>
public List<Curriculum> getCurriculumList() {...}<br>
<br>
...<br>
<br>
public List<UnavailablePeriodConstraint>
getUnavailablePeriodConstraintList() {...}<br>
<br>
@PlanningEntityCollectionProperty<br>
public List<Lecture> getLectureList() {...}<br>
<br>
public ...Score getScore() {...}<br>
<br>
public Collection<? extends Object> getFacts() {<br>
List<Object> facts = new
ArrayList<Object>();<br>
facts.addAll(teacherList);<br>
facts.addAll(curriculumList);<br>
...<br>
facts.addAll(unavailablePeriodConstraintList);<br>
if (isInitialized()) {<br>
facts.addAll(lectureList);<br>
}<br>
facts.addAll(calculateTopicConflictList());<br>
return facts;<br>
}<br>
<br>
public List<TopicConflict>
calculateTopicConflictList() {...}<br>
<br>
}<br>
After in *.java:<br>
public class ... implements Solution<...> {<br>
<br>
private InstitutionalWeighting institutionalWeighting;<br>
<br>
private List<Teacher> teacherList;<br>
private List<Curriculum> curriculumList;<br>
...<br>
private List<UnavailablePeriodConstraint>
unavailablePeriodConstraintList;<br>
<br>
private List<Lecture> lectureList;<br>
<br>
private HardAndSoftScore score;<br>
<br>
...<br>
<br>
// This is not a PlanningFactProperty: the name is inserted
into the working memory<br>
public String getName() {...}<br>
<br>
@PlanningFactProperty<br>
public InstitutionalWeighting getInstitutionalWeighting()
{...}<br>
<br>
@PlanningFactCollectionProperty<br>
public List<Teacher> getTeacherList() {...}<br>
<br>
@PlanningFactCollectionProperty<br>
public List<Curriculum> getCurriculumList() {...}<br>
<br>
...<br>
<br>
@PlanningFactCollectionProperty<br>
public List<UnavailablePeriodConstraint>
getUnavailablePeriodConstraintList() {...}<br>
<br>
// This is not a PlanningFactCollectionProperty: it is a
PlanningEntityCollectionProperty<br>
@PlanningEntityCollectionProperty<br>
public List<Lecture> getLectureList() {...}<br>
<br>
// This is not a PlanningFactProperty: the score is inserted
into the working memory<br>
public ...Score getScore() {...}<br>
<br>
// renamed from calculateTopicConflictList because these are
also facts needed in the working memory<br>
@PlanningFactCollectionProperty<br>
public List<TopicConflict> getTopicConflictList()
{...}<br>
<br>
}<br>
<br>
[RECOMMEND] A planning entity is considered uninitialized if one if
at least on of its planning variables is null.<br>
Therefor it's now possible to start from a partially initialized
solution,<br>
for example during real-time re-planning as new facts events come
in.<br>
<br>
[MAJOR] The StartingSolutionInitializer no longer has a
isSolutionInitialized(AbstractSolverScope) method<br>
Before in *StartingSolutionInitializer.java:<br>
public class ...StartingSolutionInitializer extends
AbstractStartingSolutionInitializer {<br>
<br>
@Override<br>
public boolean isSolutionInitialized(AbstractSolverScope
abstractSolverScope) {<br>
...<br>
}<br>
<br>
...<br>
<br>
}<br>
After in *StartingSolutionInitializer.java:<br>
public class ...StartingSolutionInitializer extends
AbstractStartingSolutionInitializer {<br>
<br>
...<br>
<br>
}<br>
<br>
[MAJOR] The planning entity collection in the Solution can never be
null,<br>
but some (or all) of its planning entity's can be uninitialized.<br>
So create them before setting the starting solution, instead of in
your StartingSolutionInitializer.<br>
Before in *.java:<br>
public class ... {<br>
<br>
public void ...() {<br>
CurriculumCourseSchedule schedule = new
CurriculumCourseSchedule();<br>
schedule.setTeacherList(teacherList);<br>
schedule.setCourseList(courseList);<br>
...<br>
solver.setStartingSolution(schedule);<br>
}<br>
<br>
}<br>
After in *.java:<br>
public class ... {<br>
<br>
public void ...() {<br>
CurriculumCourseSchedule schedule = new
CurriculumCourseSchedule();<br>
schedule.setTeacherList(teacherList);<br>
schedule.setCourseList(courseList);<br>
...<br>
schedule.setLectureList(createLectureList(schedule.getCourseList()));<br>
solver.setStartingSolution(schedule);<br>
}<br>
<br>
private List<Lecture>
createLectureList(List<Course> courseList) {<br>
List<Lecture> lectureList = new
ArrayList<Lecture>(courseList.size());<br>
long id = 0L;<br>
for (Course course : courseList) {<br>
for (int i = 0; i < course.getLectureSize(); i++)
{<br>
Lecture lecture = new Lecture();<br>
lecture.setId((long) id);<br>
id++;<br>
lecture.setCourse(course);<br>
// Make sure to set all non PlanningVariable
properties<br>
lecture.setLectureIndexInCourse(i);<br>
// Notice that we lave the PlanningVariable
properties on null<br>
lectureList.add(lecture);<br>
}<br>
}<br>
return lectureList;<br>
}<br>
<br>
}<br>
<br>
[RECOMMENDED] Remove the isInitialized() from Solution if you copied
that from the examples.<br>
Before in *.java:<br>
public class ... implements Solution<...> {<br>
<br>
public boolean isInitialized() {<br>
return (lectureList != null);<br>
}<br>
<br>
...<br>
<br>
}<br>
After in *.java:<br>
public class ... implements Solution<...> {<br>
<br>
...<br>
<br>
}<br>
<br>
<b><br>
The Good News</b><br>
<br>
I already have written a brute force solver (only useful for very
very small toy problems of course).<br>
In time, I 'll write a branch and bound solver (only useful for very
small toy problems of course).<br>
<br>
I 'll write generic, high-quality StartingSolutionInitializers that
work on any program,<br>
such as First Fit Decreasing and Cheapest Insertion.<br>
A good StartingSolutionInitializer is really important to get a good
result from Planner.<br>
Currently writing a StartingSolutionInitializer was a bit of a black
art.<br>
Some users use a highly under optimized version or - even worse -
none at all.<br>
<br>
Phasing. This will be fun.<br>
You 'll be able to do configure this really easily:<br>
Phase 1 = First Fit Decreasing (for initialization)<br>
Phase 2 = Simulated Annealing (after initialization)<br>
Phase 3 = Tabu search (when things get really hard)<br>
Notice that a StartingSolutionInitializer is just a phase.<br>
<br>
[1] <a class="moz-txt-link-freetext"
href="https://github.com/droolsjbpm/drools-planner/pull/2/files">https://github.com/droolsjbpm/drools-planner/pull/2/files</a>
<pre class="moz-signature" cols="72">--
With kind regards,
Geoffrey De Smet</pre>
</body>
</html>