All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.drools.planner.examples.examination.solver.examinationScoreRules.drl Maven / Gradle / Ivy

/*
 * 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.calculator.HardAndSoftConstraintScoreCalculator;
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.PeriodHardConstraint;
import org.drools.planner.examples.examination.domain.PeriodHardConstraintType;
import org.drools.planner.examples.examination.domain.Room;
import org.drools.planner.examples.examination.domain.RoomHardConstraint;
import org.drools.planner.examples.examination.domain.RoomHardConstraintType;
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 HardAndSoftConstraintScoreCalculator scoreCalculator;

// ############################################################################
// 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 "periodHardConstraintExamCoincidence"
    when
        $periodHardConstraint : PeriodHardConstraint(
            periodHardConstraintType == PeriodHardConstraintType.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("periodHardConstraintExamCoincidence", ConstraintType.NEGATIVE_HARD,
                ($leftSideTopic.getStudentSize() + $rightSideTopic.getStudentSize()),
                $leftExam, $rightExam));
end
rule "periodHardConstraintExclusion"
    when
        $periodHardConstraint : PeriodHardConstraint(
            periodHardConstraintType == PeriodHardConstraintType.EXCLUSION,
            $leftSideTopic : leftSideTopic,
            $rightSideTopic : rightSideTopic
        )
        $leftExam : Exam(topic == $leftSideTopic, $leftSidePeriod : period)
        $rightExam : Exam(topic == $rightSideTopic, period == $leftSidePeriod)
    then
        insertLogical(new IntConstraintOccurrence("periodHardConstraintExclusion", ConstraintType.NEGATIVE_HARD,
                ($leftSideTopic.getStudentSize() + $rightSideTopic.getStudentSize()),
                $leftExam, $rightExam));
end
rule "periodHardConstraintAfter"
    when
        $periodHardConstraint : PeriodHardConstraint(
            periodHardConstraintType == PeriodHardConstraintType.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("periodHardConstraintAfter", ConstraintType.NEGATIVE_HARD,
                ($leftSideTopic.getStudentSize() + $rightSideTopic.getStudentSize()),
                $leftExam, $rightExam));
end

// Room hard constraints
rule "roomHardConstraintExclusive"
    when
        $roomHardConstraint : RoomHardConstraint(
            roomHardConstraintType == RoomHardConstraintType.ROOM_EXCLUSIVE,
            $topic : topic
        )
        $leftExam : Exam(topic == $topic, $room : room)
        $rightExam : Exam(room == $room, topic != $topic, $otherTopic : topic)
    then
        insertLogical(new IntConstraintOccurrence("roomHardConstraintExclusive", 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
        scoreCalculator.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
        scoreCalculator.setSoftConstraintsBroken($softTotal.intValue());
end




© 2015 - 2024 Weber Informatics LLC | Privacy Policy