<html><body><div style="color:#000; background-color:#fff; font-family:tahoma, new york, times, serif;font-size:10pt">Hi, me once more.<br><br>I guess you figured out by now that I am quite a newbie to Drools (planning).<br>Problem is I need to write a POC in max 3 to 4 days...<br>I'm drifting, so please help...<br><br>I have written a basic solution for planning plane-maintenance.<br>I stripped it to it's very basics and it works... if I keep the number of planes, periods and technicians to a very minimum.<br>I removed all additional constraints like skill matching and availability during a period...<br><br>Planning maintenance for 3 planes with each 2 required technicians, 4 available periods and a list of 10 available technicians delivers a solution in about 3 seconds.<br>Simply increasing the number of technicians per plane from 2 to 3 results in an indefinite solving time (I did not wait longer than a few minutes).<br>Decreasing the numbers
obviously works, at an elapsed time slightly under 3 seconds.<br><br>I finally need to plan about 500 plane maintenance jobs (3 to 4 technicians per maintenance job) over 12 available periods with about 400 available technicians..<br>So there must be something wrong/incomplete in my solution.<br><br>What I did:<br><br>I have created a planningEntity M<span class="tab">aintenanceTask with two planning variables (technician and period) and a simple rule file.<br>(See all below)<br>Running this solution thus results in such poor solving time.<br><br>Can you please advice me in what I am doing wrong?<br><br>Many thanks!!<br><br><span style="font-weight: bold;">PlanningEntity (</span></span><span class="tab"><span style="font-weight: bold;"><span class="tab">MaintenanceTask .java</span>):</span><br><br>@PlanningEntity(difficultyWeightFactoryClass = TaskDifficultyWeightFactory.class)<br>public class MaintenanceTask {<br><br> private
Period period;<br> private Project project;<br> private Technician technician;<br><br> public </span><span class="tab"><span class="tab">MaintenanceTask </span>(Project aProject) {<br> this.project = aProject;<br> }<br><br> public String getId(){<br> return project.getId()+"-"+hashCode();<br> }<br> <br> @PlanningVariable<br> @ValueRange(type = ValueRangeType.FROM_SOLUTION_PROPERTY, solutionProperty = "technicians")<br> public </span><span class="tab"><span class="tab">Technician </span>getTechnician() {<br> return technician;<br> }<br> <br> public void setTechnician(Technician aTechnician) {<br>
this.technican= </span><span class="tab"><span class="tab">aTechnician</span>;<br> }<br><br> @PlanningVariable<br> @ValueRange(type = ValueRangeType.FROM_SOLUTION_PROPERTY, solutionProperty = "periods")<br> public Period getPeriod() {<br> return period;<br> }<br><br> public void setPeriod(Period aPeriod) {<br> this.period = </span><span class="tab"><span class="tab">aPeriod</span>;<br> }<br> <br> public String getPeriodId(){<br> return period == null ? null : period.getId();<br> }<br> <br> public String getProjectId(){<br> return project </span><span class="tab"><span
class="tab">== null ? null :</span> <span class="tab">project.getId();</span></span><br><span class="tab"> }<br><br> public </span><span class="tab"><span class="tab">MaintenanceTask </span>clone() {<br> </span><span class="tab"><span class="tab">MaintenanceTask </span>clone = new </span><span class="tab"><span class="tab">MaintenanceTask </span>(project);<br> clone.period = this.period;<br> clone.technician= this.</span><span class="tab"><span class="tab">technician</span>;<br> return clone;<br> }<br><br>}<br><br></span><div><span><span style="font-weight: bold;">Project.java</span></span></div><div style="color: rgb(0, 0, 0); font-size: 13.3333px; font-family: tahoma,new york,times,serif; background-color: transparent; font-style:
normal;"><br><span></span></div><div style="color: rgb(0, 0, 0); font-size: 13.3333px; font-family: tahoma,new york,times,serif; background-color: transparent; font-style: normal;"><span>public class Project {<br> private Plane plane;<br> <br> public Project(Plane aPlane) {<br> this.plane = aPlane;<br> }<br> <br> public Plane getPlane() {<br> return plane;<br> }<br><br> public String getId() {<br> return getPlane().getName();<br> }<br> <br> public int getTechnicianLoad(){<br> return getPlane().getTechnicianLoad();<br> }<br> <br> public
</span><span><span>List<MaintenanceTask></span> getTasks() {<br> List<MaintenanceTask> tasks = new ArrayList<MaintenanceTask>();<br> for (int i = 0; i < getTechnicianLoad(); i++) {<br> tasks.add(new MaintenanceTask(this));<br> }<br> return tasks;<br> }<br>}</span></div><div style="color: rgb(0, 0, 0); font-size: 13.3333px; font-family: tahoma,new york,times,serif; background-color: transparent; font-style: normal;"><br><span></span></div><div style="color: rgb(0, 0, 0); font-size: 13.3333px; font-family: tahoma,new york,times,serif; background-color: transparent; font-style: normal;"><span>Technician.java and Period.java are at this moment basically just classes having an getId and
getName method, nothing more.</span></div><div style="color: rgb(0, 0, 0); font-size: 13.3333px; font-family: tahoma,new york,times,serif; background-color: transparent; font-style: normal;"><span>Plane.java has </span>a method <span>getTechnicianLoad that returns the number of required technicians for the maintenance.</span></div><div style="color: rgb(0, 0, 0); font-size: 13.3333px; font-family: tahoma,new york,times,serif; background-color: transparent; font-style: normal;"><br><span></span></div><div style="color: rgb(0, 0, 0); font-size: 13.3333px; font-family: tahoma,new york,times,serif; background-color: transparent; font-style: normal;"><span><span style="font-weight: bold;">This is the config file</span> (basically copy from one of the examples)<br></span></div><div style="color: rgb(0, 0, 0); font-size: 13.3333px; font-family: tahoma,new york,times,serif; background-color: transparent; font-style: normal;"><br><span></span></div><div
style="color: rgb(0, 0, 0); font-size: 13.3333px; font-family: tahoma,new york,times,serif; background-color: transparent; font-style: normal;"><span><?xml version="1.0" encoding="UTF-8"?><br><solver><br> <!-- Domain model configuration --><br> <solutionClass>be.axi.planner.domain.MaintenanceSchedule</solutionClass><br> <planningEntityClass>be.axi.planner.domain.MaintenanceTask</planningEntityClass><br><br> <!-- Score configuration --><br> <scoreDirectorFactory><br> <scoreDefinitionType>SIMPLE</scoreDefinitionType><br> <scoreDrl>/maintenancePlannerRules.drl</scoreDrl><br> </scoreDirectorFactory><br><br> <termination><br> <maximumSecondsSpend>700</maximumSecondsSpend><br> </termination></span></div><div style="color: rgb(0, 0, 0); font-size:
13.3333px; font-family: tahoma,new york,times,serif; background-color: transparent; font-style: normal;"><span><br></span></div><div style="color: rgb(0, 0, 0); font-size: 13.3333px; font-family: tahoma,new york,times,serif; background-color: transparent; font-style: normal;"><span> <constructionHeuristic><br> <constructionHeuristicType>FIRST_FIT_DECREASING</constructionHeuristicType><br> <constructionHeuristicPickEarlyType>FIRST_LAST_STEP_SCORE_EQUAL_OR_IMPROVING</constructionHeuristicPickEarlyType><br> </constructionHeuristic><br> <localSearch><br> <selector><br> <moveFactoryClass>org.drools.planner.core.move.generic.GenericChangeMoveFactory</moveFactoryClass><br> <!-- GenericChangeMoveFactory is the generic form of RowChangeMoveFactory:
--><br> <!--<moveFactoryClass>org.drools.planner.examples.nqueens.solver.move.factory.RowChangeMoveFactory</moveFactoryClass>--><br> </selector><br> <acceptor><br> <solutionTabuSize>1000</solutionTabuSize><br> <planningEntityTabuSize>7</planningEntityTabuSize><br> </acceptor><br> <forager><br> <minimalAcceptedSelection>800</minimalAcceptedSelection><br> </forager><br> </localSearch><br></solver></span></div><div style="color: rgb(0, 0, 0); font-size: 13.3333px; font-family: tahoma,new york,times,serif; background-color: transparent; font-style: normal;"><br><span></span></div><div style="color: rgb(0, 0, 0); font-size: 13.3333px; font-family:
tahoma,new york,times,serif; background-color: transparent; font-style: normal;"><span><span style="font-weight: bold;">this is the rules file</span>:</span></div><div style="color: rgb(0, 0, 0); font-size: 13.3333px; font-family: tahoma,new york,times,serif; background-color: transparent; font-style: normal;"><br><span></span></div><div style="color: rgb(0, 0, 0); font-size: 13.3333px; font-family: tahoma,new york,times,serif; background-color: transparent; font-style: normal;"><span>package org.drools.planner.examples.pas.solver;<br> dialect "java"<br><br>import org.drools.planner.core.score.buildin.simple.SimpleScoreHolder;<br>import org.drools.planner.core.score.constraint.IntConstraintOccurrence;<br>import org.drools.planner.core.score.constraint.UnweightedConstraintOccurrence<br>import org.drools.planner.core.score.constraint.ConstraintType;<br><br>import be.axi.planner.domain.MaintenanceTask;<br><br>global SimpleScoreHolder
scoreHolder;<br><br>// ############################################################################<br>// Hard constraints<br>// ############################################################################<br><br>rule "noMultipleAssignmentsInOnePeriod"<br> when<br> $t1 : MaintenanceTask($id : id, $periodId : periodId, $technician : technician )<br> $t2 : MaintenanceTask(id != $id, periodId == $periodId, technician == $technician)<br> then<br> insertLogical(new UnweightedConstraintOccurrence("noMultipleAssignmentsInOnePeriod", $t1, $t2));<br>end<br><br>rule "tasksInSameProjectMustBeInSamePeriod"<br> when<br> $t1 : MaintenanceTask($id : id, $periodId : periodId, $projectId : projectId )<br>
$t2 : MaintenanceTask(id != $id, periodId != $periodId, projectId == $projectId)<br> then<br> insertLogical(new UnweightedConstraintOccurrence("tasksInSameProjectMustBeInSamePeriod", $t1, $t2));<br>end<br><br>// ############################################################################<br>// Calculate score<br>// ############################################################################<br><br>rule "hardConstraintsBroken"<br> when<br> $occurrenceCount : Number() from accumulate(<br> $unweightedConstraintOccurrence : UnweightedConstraintOccurrence(),<br> count($unweightedConstraintOccurrence)<br> )<br>
then<br> scoreHolder.setScore(- $occurrenceCount.intValue());<br>end<br></span></div><div style="color: rgb(0, 0, 0); font-size: 13.3333px; font-family: tahoma,new york,times,serif; background-color: transparent; font-style: normal;"><br></div><div style="color: rgb(0, 0, 0); font-size: 13.3333px; font-family: tahoma,new york,times,serif; background-color: transparent; font-style: normal;"><span style="font-weight: bold;">Solution file:</span></div><div style="color: rgb(0, 0, 0); font-size: 13.3333px; font-family: tahoma,new york,times,serif; background-color: transparent; font-style: normal;"><br></div><div style="color: rgb(0, 0, 0); font-size: 13.3333px; font-family: tahoma,new york,times,serif; background-color: transparent; font-style: normal;">package be.axi.planner.domain;<br><br>import java.util.ArrayList;<br>import java.util.Collection;<br>import java.util.List;<br><br>import
org.drools.planner.api.domain.solution.PlanningEntityCollectionProperty;<br>import org.drools.planner.core.score.buildin.simple.SimpleScore;<br>import org.drools.planner.core.solution.Solution;<br><br><br>public class MaintenanceSchedule implements Solution<SimpleScore>{<br><br> private SimpleScore score;<br> private List<MaintenanceTask> maintenanceTasks;<br> private List<Period> periods;<br> private List<Technician> technicians;<br> <br> public MaintenanceSchedule cloneSolution() {<br> MaintenanceSchedule clone = new MaintenanceSchedule();<br> clone.periods = this.periods;<br> clone.technicians = this.technicians;<br> List<MaintenanceTask> clonedTasks =
new ArrayList<MaintenanceTask>(<br> maintenanceTasks.size());<br> for (MaintenanceTask task : maintenanceTasks) {<br> MaintenanceTask clonedTask = task.clone();<br> clonedTasks.add(clonedTask);<br> }<br> clone.maintenanceTasks = clonedTasks;<br> clone.score = this.score;<br> <br> return clone;<br> }<br><br> public Collection<? extends Object> getProblemFacts() {<br> System.out.println(">>getting
problemfacts");<br> List<Object> facts = new ArrayList<Object>();<br> facts.addAll(periods);<br> facts.addAll(technicians);<br> // Do not add the planning entity's (maintenaceTasks) because that will be done automatically<br> return facts;<br> }<br><br> public SimpleScore getScore() {<br> return this.score;<br> }<br><br> public void setScore(SimpleScore aScore) {<br> this.score = aScore;<br> if (aScore != null){<br> System.out.println("Score set to " + aScore.getScore());<br>
}<br> }<br><br> @PlanningEntityCollectionProperty<br> public List<MaintenanceTask> getMaintenanceTasks() {<br> System.out.println("getting tasks: #" +maintenanceTasks.size());<br> return maintenanceTasks;<br> }<br> <br> public void setMaintenanceTasks(List<MaintenanceTask> aMaintenanceTasks) {<br> System.out.println("setting tasks");<br> this.maintenanceTasks = aMaintenanceTasks;<br> }<br><br> public void setTechnicians(List<Technician> aTechnicians) {<br> System.out.println("setting Technicians");<br> this.technicians = aTechnicians;<br>
}<br> <br> public List<Technician> getTechnicians() {<br> System.out.println("Getting Technicians: #"+technicians.size());<br> return technicians;<br> }<br> <br> public List<Period> getPeriods() {<br> System.out.println("getting periods");<br> return periods;<br> }<br> <br> public void setPeriods(List<Period> periods) {<br> this.periods = periods;<br> }<br><br>}<br><br><br><br></div><div style="color: rgb(0, 0, 0); font-size: 13.3333px; font-family: tahoma,new york,times,serif; background-color: transparent; font-style:
normal;"><br><span></span></div><div>-----------------<br>http://www.codessentials.com - Your essential software, for free!<br>Follow us at http://twitter.com/#!/Codessentials<br></div> <div style="font-family: tahoma, new york, times, serif; font-size: 10pt;"> <div style="font-family: times new roman, new york, times, serif; font-size: 12pt;"> <div dir="ltr"> <font face="Arial" size="2"> <hr size="1"> <b><span style="font-weight:bold;">From:</span></b> Geoffrey De Smet <ge0ffrey.spam@gmail.com><br> <b><span style="font-weight: bold;">To:</span></b> rules-users@lists.jboss.org <br> <b><span style="font-weight: bold;">Sent:</span></b> Friday, August 31, 2012 10:54 AM<br> <b><span style="font-weight: bold;">Subject:</span></b> Re: [rules-users] Best model for planning? technicians, airplanes and shifts<br> </font> </div> <br>
Op 31-08-12 09:46, Michiel Vermandel schreef:<br>> Thank you for the response.<br>> I think I forgot one level of complexity (?):<br>><br>> The moment of maintenance of a plane is not fixed.<br>><br>> We have 12 "periods" per year for maintenance.<br>> One period every month.<br>><br>> We need to make the planning in such way that the list of planes is<br>> planned somewhere in these 12 periods.<br>> But it is not important that a specific plane is maintained at a<br>> specific period.<br>> It has though a calendar of unavailability.<br>><br>> So the job (==Shift) of the Task (==ShiftAssignment) has no hard<br>> constraint on period.<br>><br>> We need to find that combination with the best match between required<br>> skills for the job and available technicians.<br>> It's better to plan the job on a later period if at that time a more<br>> skilled technician is available.<br>><br>> I
hope this is a bit clear.<br>><br>> So, given this addition, should I take as planning variables:<br>><br>> 1) Period (List of 12 periods)<br>> 2) Technician<br><br>Good idea<br><br>><br>> Then use period to check availability on both plane and technician<br>> (hard-constraint)<br>I see 3:<br>- plane availability<br>- technician conflicts (2 tasks at the same time)<br>- technician availability<br>> Then give a better weight on skill-match (soft-constrain)?<br><br>yes (and possible others, such as work load, technician desires, ...)<br><br>><br>> Thank you.<br>><br>> Michiel<br>><br>> -----------------<br>> http://www.codessentials.com - Your essential software, for free!<br>> Follow us at http://twitter.com/#!/Codessentials<br>> ------------------------------------------------------------------------<br>> *From:* Geoffrey De Smet <<a ymailto="mailto:ge0ffrey.spam@gmail.com"
href="mailto:ge0ffrey.spam@gmail.com">ge0ffrey.spam@gmail.com</a>><br>> *To:* <a ymailto="mailto:rules-users@lists.jboss.org" href="mailto:rules-users@lists.jboss.org">rules-users@lists.jboss.org</a><br>> *Sent:* Friday, August 31, 2012 9:08 AM<br>> *Subject:* Re: [rules-users] Best model for planning? technicians,<br>> airplanes and shifts<br>><br>> Op 30-08-12 18:02, mvermand schreef:<br>> > Hi,<br>> ><br>> > I'd like some advice on how to make my model.<br>> ><br>> > I need to plan maintenance-schedules for - let's say - airplanes.<br>> > Constraints are:<br>> > - planes are only at given moments available for maintenance (when not in<br>> > the air ;-)<br>> > - maintenance of a planes requires a number of techniciens (with specific<br>> > skills).<br>> > - techniciens have skills and also shifts and
vacation.<br>> ><br>><br>> Sounds like the nurse rostering example in Planner :)<br>> In nurse rostering, such a Shift has a ShiftDate and a ShiftType.<br>> The difference is, you need to assign jobs instead of shifts.<br>><br>> > Now, first I tought to make the maintenance of a plane the<br>> planningEntity.<br>> > But I did not know how to populate and match the techniciens.<br>><br>> I wouldn't make plane the planningEntity either, it's on the one side of<br>> the manyToOne relation.<br>><br>> ><br>> > So, second I tought to make a new Task-class the planningEntity.<br>> > A Task is a job that needs to be done by a technicien with specific<br>> skills.<br>> > Maintaining a plane requires then a number of tasks.<br>><br>> Good idea. In nurse rostering, a Task is called a ShiftAssignment.<br>> You can generate all Tasks in
advance based on the Planes and their jobs<br>> and the number of technicians needed by each job.<br>><br>> I recommend to make separate Task instances for multiple technicians<br>> that perform the same job together on the same plane. In nurse rostering<br>> there are also multiple ShiftAssignments (=tasks) per Shift (=job).<br>><br>> ><br>> > If I choose this model, I think I'll have to pass two Plannings<br>> variables to<br>> > the Task planningEntity:<br>> > 1) a Maintenance-fact (Plane + Period + list of required tasks) and<br>><br>> I don't think this is a planner variable on the planning entity Task.<br>> It really defines the Task: what job on what plane (and some of sort of<br>> requiredTechnicanCountIndex)<br>><br>> > 2) a Technicien-fact (?)<br>> This is a planning variable.<br>> You're assigning task to techinicians.<br>> Each task has
exactly 1 technician (because a job has multiple tasks,<br>> one per required technician).<br>> Each technician can work on multiple tasks, but the hard/soft<br>> constraints make sure that it's not at the same time.<br>><br>> ><br>> > Then I'll need to check if availabity of plane and availability of<br>> > technicien match and also skill-requirement of task and skills of<br>> > technicien.<br>><br>> Yep, with constraints<br>><br>> ><br>> > Do you think this second model is the right one?<br>> ><br>> > Will it perform well?<br>> > => ? matching all combinations of plane availabilty and<br>> > techniciens-availability and -skills.<br>><br>> It should, see the nurse rostering problem.<br>> In Planner 5.5.0.Beta1, I would add a filter on the swap move selector<br>> to prevent it from swapping 2
technicians assigned to different tasks on<br>> the same job.<br>><br>> <a href="https://github.com/droolsjbpm/drools-planner/blob/master/drools-planner-examples/src/main/java/org/drools/planner/examples/curriculumcourse/solver/move/DifferentCourseSwapMoveFilter.javatd" target="_blank">https://github.com/droolsjbpm/drools-planner/blob/master/drools-planner-examples/src/main/java/org/drools/planner/examples/curriculumcourse/solver/move/DifferentCourseSwapMoveFilter.javatd</a><br>><br>> ><br>> > Thanks in advance<br>> ><br>> ><br>> ><br>> > --<br>> > View this message in context:<br>> <a href="http://drools.46999.n3.nabble.com/Best-model-for-planning-tp4019464.html" target="_blank">http://drools.46999.n3.nabble.com/Best-model-for-planning-tp4019464.html</a><br>> > Sent from the Drools: User forum mailing list archive at Nabble.com<br>> <<a
href="http://nabble.com/" target="_blank">http://nabble.com/</a>>.<br>> > _______________________________________________<br>> > rules-users mailing list<br>> > <a ymailto="mailto:rules-users@lists.jboss.org" href="mailto:rules-users@lists.jboss.org">rules-users@lists.jboss.org</a> <mailto:<a ymailto="mailto:rules-users@lists.jboss.org" href="mailto:rules-users@lists.jboss.org">rules-users@lists.jboss.org</a>><br>> > <a href="https://lists.jboss.org/mailman/listinfo/rules-users" target="_blank">https://lists.jboss.org/mailman/listinfo/rules-users</a><br>> ><br>><br>><br>> _______________________________________________<br>> rules-users mailing list<br>> <a ymailto="mailto:rules-users@lists.jboss.org" href="mailto:rules-users@lists.jboss.org">rules-users@lists.jboss.org</a> <mailto:<a ymailto="mailto:rules-users@lists.jboss.org"
href="mailto:rules-users@lists.jboss.org">rules-users@lists.jboss.org</a>><br>> <a href="https://lists.jboss.org/mailman/listinfo/rules-users" target="_blank">https://lists.jboss.org/mailman/listinfo/rules-users</a><br>><br>><br>><br>><br>> _______________________________________________<br>> rules-users mailing list<br>> <a ymailto="mailto:rules-users@lists.jboss.org" href="mailto:rules-users@lists.jboss.org">rules-users@lists.jboss.org</a><br>> <a href="https://lists.jboss.org/mailman/listinfo/rules-users" target="_blank">https://lists.jboss.org/mailman/listinfo/rules-users</a><br>><br><br><br>_______________________________________________<br>rules-users mailing list<br><a ymailto="mailto:rules-users@lists.jboss.org" href="mailto:rules-users@lists.jboss.org">rules-users@lists.jboss.org</a><br><a href="https://lists.jboss.org/mailman/listinfo/rules-users"
target="_blank">https://lists.jboss.org/mailman/listinfo/rules-users</a><br><br><br> </div> </div> </div></body></html>