org.drools.planner.examples.examination.solver.examinationScoreRules.drl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of drools-planner-examples Show documentation
Show all versions of drools-planner-examples Show documentation
Drools Planner optimizes automated planning by combining metaheuristic search algorithms with rule
engine powered score calculation. This is the drools-planner-examples module which contains examples on how to use
Drools Planner.
/*
* Copyright 2010 JBoss Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.drools.planner.examples.examination.solver;
dialect "java"
import org.drools.planner.core.score.buildin.hardandsoft.HardAndSoftScoreHolder;
import org.drools.planner.core.score.constraint.IntConstraintOccurrence;
import org.drools.planner.core.score.constraint.ConstraintType;
import org.drools.planner.examples.examination.domain.Exam;
import org.drools.planner.examples.examination.domain.Examination;
import org.drools.planner.examples.examination.domain.InstitutionalWeighting;
import org.drools.planner.examples.examination.domain.Period;
import org.drools.planner.examples.examination.domain.PeriodPenalty;
import org.drools.planner.examples.examination.domain.PeriodPenaltyType;
import org.drools.planner.examples.examination.domain.Room;
import org.drools.planner.examples.examination.domain.RoomPenalty;
import org.drools.planner.examples.examination.domain.RoomPenaltyType;
import org.drools.planner.examples.examination.domain.Student;
import org.drools.planner.examples.examination.domain.Topic;
import org.drools.planner.examples.examination.domain.solver.TopicConflict;
global HardAndSoftScoreHolder scoreHolder;
// ############################################################################
// Hard constraints
// ############################################################################
// Two exams in the same period which share students.
rule "conflictingExamsInSamePeriod"
when
$topicConflict : TopicConflict($leftTopic : leftTopic, $rightTopic : rightTopic)
$leftExam : Exam(topic == $leftTopic, $period : period)
$rightExam : Exam(topic == $rightTopic, period == $period)
then
insertLogical(new IntConstraintOccurrence("conflictingExamsInSamePeriod", ConstraintType.NEGATIVE_HARD,
$topicConflict.getStudentSize(),
$leftExam, $rightExam));
end
// More time required during a period than available in that period.
rule "periodDurationTooShort"
when
$exam : Exam(topicDuration > periodDuration)
then
insertLogical(new IntConstraintOccurrence("periodDurationTooShort", ConstraintType.NEGATIVE_HARD,
$exam.getTopicStudentSize(),
$exam));
end
// More seating required during a period in a room than available in that room.
rule "roomCapacityTooSmall" // TODO improve performance, as it takes 50% of the performance
when
$period : Period()
$room : Room($capacity : capacity)
$totalStudentSize : Number(intValue > $capacity) from accumulate(
Exam(period == $period, room == $room, $studentSize : topicStudentSize),
sum($studentSize) // Vote for http://jira.jboss.com/jira/browse/JBRULES-1075
)
then
insertLogical(new IntConstraintOccurrence("roomCapacityTooSmall", ConstraintType.NEGATIVE_HARD,
($totalStudentSize.intValue() - $capacity),
$period, $room));
end
// Period hard constraints
rule "periodPenaltyExamCoincidence"
when
$periodPenalty : PeriodPenalty(
periodPenaltyType == PeriodPenaltyType.EXAM_COINCIDENCE,
$leftSideTopic : leftSideTopic,
$rightSideTopic : rightSideTopic
)
$leftExam : Exam(topic == $leftSideTopic, $leftSidePeriod : period)
$rightExam : Exam(topic == $rightSideTopic, period != $leftSidePeriod)
// LeftSide and rightSide don't share a student because those are filtered out in the InputConvertor
then
insertLogical(new IntConstraintOccurrence("periodPenaltyExamCoincidence", ConstraintType.NEGATIVE_HARD,
($leftSideTopic.getStudentSize() + $rightSideTopic.getStudentSize()),
$leftExam, $rightExam));
end
rule "periodPenaltyExclusion"
when
$periodPenalty : PeriodPenalty(
periodPenaltyType == PeriodPenaltyType.EXCLUSION,
$leftSideTopic : leftSideTopic,
$rightSideTopic : rightSideTopic
)
$leftExam : Exam(topic == $leftSideTopic, $leftSidePeriod : period)
$rightExam : Exam(topic == $rightSideTopic, period == $leftSidePeriod)
then
insertLogical(new IntConstraintOccurrence("periodPenaltyExclusion", ConstraintType.NEGATIVE_HARD,
($leftSideTopic.getStudentSize() + $rightSideTopic.getStudentSize()),
$leftExam, $rightExam));
end
rule "periodPenaltyAfter"
when
$periodPenalty : PeriodPenalty(
periodPenaltyType == PeriodPenaltyType.AFTER,
$leftSideTopic : leftSideTopic,
$rightSideTopic : rightSideTopic
)
$leftExam : Exam(topic == $leftSideTopic, $afterSidePeriod : period)
$rightExam : Exam(topic == $rightSideTopic, eval($afterSidePeriod.getPeriodIndex() <= period.getPeriodIndex()),
$beforeSidePeriod : period
)
then
insertLogical(new IntConstraintOccurrence("periodPenaltyAfter", ConstraintType.NEGATIVE_HARD,
($leftSideTopic.getStudentSize() + $rightSideTopic.getStudentSize()),
$leftExam, $rightExam));
end
// Room hard constraints
rule "roomPenaltyExclusive"
when
$roomPenalty : RoomPenalty(
roomPenaltyType == RoomPenaltyType.ROOM_EXCLUSIVE,
$topic : topic
)
$leftExam : Exam(topic == $topic, $room : room)
$rightExam : Exam(room == $room, topic != $topic, $otherTopic : topic)
then
insertLogical(new IntConstraintOccurrence("roomPenaltyExclusive", ConstraintType.NEGATIVE_HARD,
($topic.getStudentSize() + $otherTopic.getStudentSize()),
$leftExam, $rightExam));
end
// ############################################################################
// Soft constraints
// ############################################################################
// Two exams in a row which share students
rule "twoExamsInARow"
when
$institutionalWeighting : InstitutionalWeighting(twoInARowPenalty != 0)
$topicConflict : TopicConflict($leftTopic : leftTopic, $rightTopic : rightTopic)
$leftExam : Exam(topic == $leftTopic, $leftPeriod : period)
$rightExam : Exam(topic == $rightTopic, $rightPeriod : period)
eval($leftPeriod.getDayIndex() == $rightPeriod.getDayIndex())
eval(Math.abs($leftPeriod.getPeriodIndex() - $rightPeriod.getPeriodIndex()) == 1)
then
insertLogical(new IntConstraintOccurrence("twoExamsInARow", ConstraintType.NEGATIVE_SOFT,
$topicConflict.getStudentSize() * $institutionalWeighting.getTwoInARowPenalty(),
$leftExam, $rightExam));
end
// TODO check if merging twoExamsInARow and twoExamsInADay can be a noticable performance boost
// Two exams in a day which share students
rule "twoExamsInADay"
when
$institutionalWeighting : InstitutionalWeighting(twoInADayPenalty != 0)
$topicConflict : TopicConflict($leftTopic : leftTopic, $rightTopic : rightTopic)
$leftExam : Exam(topic == $leftTopic, $leftPeriod : period)
$rightExam : Exam(topic == $rightTopic, $rightPeriod : period)
eval($leftPeriod.getDayIndex() == $rightPeriod.getDayIndex())
eval(Math.abs($leftPeriod.getPeriodIndex() - $rightPeriod.getPeriodIndex()) > 1)
then
insertLogical(new IntConstraintOccurrence("twoExamsInADay", ConstraintType.NEGATIVE_SOFT,
$topicConflict.getStudentSize() * $institutionalWeighting.getTwoInADayPenalty(),
$leftExam, $rightExam));
end
// Exams which share students have to few periods between them
rule "periodSpread"
when
$institutionalWeighting : InstitutionalWeighting(periodSpreadPenalty != 0)
$topicConflict : TopicConflict($leftTopic : leftTopic, $rightTopic : rightTopic)
$leftExam : Exam(topic == $leftTopic, $leftPeriod : period)
$rightExam : Exam(topic == $rightTopic, $rightPeriod : period)
eval(Math.abs($leftPeriod.getPeriodIndex() - $rightPeriod.getPeriodIndex())
< ($institutionalWeighting.getPeriodSpreadLength() + 1))
then
insertLogical(new IntConstraintOccurrence("periodSpread", ConstraintType.NEGATIVE_SOFT,
$topicConflict.getStudentSize() * $institutionalWeighting.getPeriodSpreadPenalty(),
$leftExam, $rightExam));
end
// Several exams in the same room and period have different durations
rule "mixedDurations"
when
$institutionalWeighting : InstitutionalWeighting(mixedDurationPenalty != 0)
$leftExam : Exam($leftId : id, $period : period, room != null, $room : room, $leftTopic : topic)
// 4 mixed durations of 100, 150, 200 and 200 should only result in 2 penalty's (for 100&150 and 100&200)
// leftExam has lowest id of the period+room combo
not Exam(period == $period, room == $room, id < $leftId)
// rightExam has a different duration
$rightExam : Exam(period == $period, room == $room, id > $leftId,
eval(topic.getDuration() != $leftTopic.getDuration()),
$rightId : id, $rightTopic : topic
)
// rightExam has the lowest id of the period+room+rightDuration combo
not Exam(period == $period, room == $room, id < $rightId,
eval(topic.getDuration() == $rightTopic.getDuration())
)
then
insertLogical(new IntConstraintOccurrence("mixedDurations", ConstraintType.NEGATIVE_SOFT,
$institutionalWeighting.getMixedDurationPenalty(),
$leftExam, $rightExam));
end
// Larger Exams towards the beginning of the examination session
rule "frontLoad"
when
$institutionalWeighting : InstitutionalWeighting(frontLoadPenalty != 0)
$topic : Topic(frontLoadLarge == true)
$period : Period(frontLoadLast == true)
$exam : Exam(topic == $topic, period == $period)
then
insertLogical(new IntConstraintOccurrence("frontLoad", ConstraintType.NEGATIVE_SOFT,
$institutionalWeighting.getFrontLoadPenalty(),
$exam));
end
// Period Penalty
rule "periodPenalty"
when
$period : Period(penalty != 0)
$exam: Exam(period == $period)
then
insertLogical(new IntConstraintOccurrence("periodPenalty", ConstraintType.NEGATIVE_SOFT,
$period.getPenalty(),
$exam));
end
// Room Penalty
rule "roomPenalty"
when
$room : Room(penalty != 0)
$exam: Exam(room == $room)
then
insertLogical(new IntConstraintOccurrence("roomPenalty", ConstraintType.NEGATIVE_SOFT,
$room.getPenalty(),
$exam));
end
// ############################################################################
// Calculate score
// ############################################################################
// Accumulate hard constraints
rule "hardConstraintsBroken"
salience -1 // Do the other rules first (optional, for performance)
when
$hardTotal : Number() from accumulate(
IntConstraintOccurrence(constraintType == ConstraintType.NEGATIVE_HARD, $weight : weight),
sum($weight) // Vote for http://jira.jboss.com/jira/browse/JBRULES-1075
)
then
scoreHolder.setHardConstraintsBroken($hardTotal.intValue());
end
// Accumulate soft constraints
rule "softConstraintsBroken"
salience -1 // Do the other rules first (optional, for performance)
when
$softTotal : Number() from accumulate(
IntConstraintOccurrence(constraintType == ConstraintType.NEGATIVE_SOFT, $weight : weight),
sum($weight) // Vote for http://jira.jboss.com/jira/browse/JBRULES-1075
)
then
scoreHolder.setSoftConstraintsBroken($softTotal.intValue());
end