
org.cpsolver.coursett.criteria.additional.InstructorLunchBreak Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cpsolver Show documentation
Show all versions of cpsolver Show documentation
The constraint solver library contains a local search based framework that allows
modeling of a problem using constraint programming primitives (variables, values, constraints).
The newest version!
package org.cpsolver.coursett.criteria.additional;
import java.util.BitSet;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.cpsolver.coursett.Constants;
import org.cpsolver.coursett.constraint.InstructorConstraint;
import org.cpsolver.coursett.constraint.InstructorConstraint.InstructorConstraintContext;
import org.cpsolver.coursett.criteria.TimetablingCriterion;
import org.cpsolver.coursett.model.Lecture;
import org.cpsolver.coursett.model.Placement;
import org.cpsolver.coursett.model.TimetableModel;
import org.cpsolver.ifs.assignment.Assignment;
import org.cpsolver.ifs.util.DataProperties;
/**
* The class represents various criteria concerning compact timetables of
* instructors. The criteria are checked and updated when a variable is
* (un)assigned.
*
* implemented criterion: lunch break
*
* @author Matej Lukac
* @version CourseTT 1.3 (University Course Timetabling)
* Copyright (C) 2012 Matej Lukac
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not see
* http://www.gnu.org/licenses/.
*/
public class InstructorLunchBreak extends TimetablingCriterion {
// lunch attributes
private double iMultiplier;
private int iLunchStart, iLunchEnd, iLunchLength;
private boolean iFullInfo;
private List iWeeks = null;
public InstructorLunchBreak() {
setValueUpdateType(ValueUpdateType.AfterUnassignedAfterAssigned);
}
@Override
public void configure(DataProperties properties) {
super.configure(properties);
iWeight = properties.getPropertyDouble("InstructorLunch.Weight", 0.3d);
// lunch parameters
iLunchStart = properties.getPropertyInt("InstructorLunch.StartSlot", (11 * 60) / 5);
iLunchEnd = properties.getPropertyInt("InstructorLunch.EndSlot", (13 * 60 + 30) / 5);
iLunchLength = properties.getPropertyInt("InstructorLunch.Length", 30 / 5);
iMultiplier = properties.getPropertyDouble("InstructorLunch.Multiplier", 1.2d);
iFullInfo = properties.getPropertyBoolean("InstructorLunch.InfoShowViolations", false);
}
/**
* The method creates date patterns (bitsets) which represent the weeks of a
* semester.
*
* @return a list of BitSets which represents the weeks of a semester.
*/
protected List getWeeks() {
if (iWeeks == null) {
TimetableModel model = (TimetableModel) getModel();
iWeeks = model.getWeeks();
}
return iWeeks;
}
private boolean isEmpty(InstructorConstraintContext ic, int slot, BitSet week, Placement p) {
if (p.getTimeLocation().getStartSlot() <= slot && slot < p.getTimeLocation().getStartSlot() + p.getTimeLocation().getLength() && p.getTimeLocation().shareWeeks(week))
return false;
List placements = ic.getPlacements(slot, week);
return placements.isEmpty() || (placements.size() == 1 && placements.get(0).variable().equals(p.variable()));
}
@Override
public double getValue(Assignment assignment, Placement value, Set conflicts) {
double ret = 0.0;
if (value.getTimeLocation().getStartSlot() <= iLunchEnd && value.getTimeLocation().getStartSlot() + value.getTimeLocation().getLength() > iLunchStart) {
InstructorLunchBreakContext context = (InstructorLunchBreakContext)getContext(assignment);
for (InstructorConstraint constraint: value.variable().getInstructorConstraints()) {
InstructorConstraintContext icx = constraint.getContext(assignment);
CompactInfo compactInfo = context.getCompactInfo(constraint);
for (int i = 0; i < Constants.NR_DAYS; i++) {
// checks only days affected by the placement
if ((value.getTimeLocation().getDayCode() & Constants.DAY_CODES[i]) != 0) {
int currentLunchStartSlot = Constants.SLOTS_PER_DAY * i + iLunchStart;
int currentLunchEndSlot = Constants.SLOTS_PER_DAY * i + iLunchEnd;
int semesterViolations = 0;
for (BitSet week : getWeeks()) {
int maxBreak = 0;
int currentBreak = 0;
for (int slot = currentLunchStartSlot; slot < currentLunchEndSlot; slot++) {
if (isEmpty(icx, slot, week, value)) {
currentBreak++;
if (maxBreak < currentBreak) {
maxBreak = currentBreak;
}
} else {
currentBreak = 0;
}
}
if (maxBreak < iLunchLength) {
semesterViolations++;
}
}
// add the difference to the result
ret += semesterViolations - compactInfo.getLunchDayViolations()[i];
}
}
}
}
return ret;
}
@Override
public double getValue(Assignment assignment, Collection variables) {
double lunchValue = 0.0d;
Set constraints = new HashSet();
for (Lecture lecture : variables) {
constraints.addAll(lecture.getInstructorConstraints());
}
for (InstructorConstraint instructor : constraints) {
lunchValue += ((InstructorLunchBreakContext)getContext(assignment)).getLunchPreference(assignment, instructor);
}
return lunchValue;
}
@Override
public void getInfo(Assignment assignment, Map info) {
Set violatedLunchBreaks = new TreeSet();
int lunchViolations = 0;
for (InstructorConstraint c : ((TimetableModel)getModel()).getInstructorConstraints()) {
String days = "";
CompactInfo compactInfo = ((InstructorLunchBreakContext)getContext(assignment)).getCompactInfo(c);
for (int i = 0; i < Constants.NR_DAYS; i++) {
if (compactInfo.getLunchDayViolations()[i] > 0) {
if (iFullInfo)
days += (days.isEmpty() ? "" : ", ") + compactInfo.getLunchDayViolations()[i] + " × " + Constants.DAY_NAMES_SHORT[i];
lunchViolations += compactInfo.getLunchDayViolations()[i];
}
}
if (iFullInfo && !days.isEmpty())
violatedLunchBreaks.add(c.getName() + ": " + days);
}
if (lunchViolations > 0) {
info.put("Lunch breaks", getPerc(lunchViolations, 0, ((TimetableModel)getModel()).getInstructorConstraints().size() * Constants.NR_DAYS * getWeeks().size()) + "% (" + lunchViolations + ")");
if (iFullInfo && !violatedLunchBreaks.isEmpty()) {
String message = "";
for (String s: violatedLunchBreaks)
message += (message.isEmpty() ? "" : "
") + s;
info.put("Lunch break violations", message);
}
}
}
@Override
public void getInfo(Assignment assignment, Map info, Collection variables) {
Set constraints = new HashSet();
for (Lecture lecture : variables) {
for (InstructorConstraint c : lecture.getInstructorConstraints()) {
constraints.add(c);
}
}
Set violatedLunchBreaks = new TreeSet();
int lunchViolations = 0;
for (InstructorConstraint c : constraints) {
String days = "";
CompactInfo compactInfo = ((InstructorLunchBreakContext)getContext(assignment)).getCompactInfo(c);
for (int i = 0; i < Constants.NR_DAYS; i++) {
if (compactInfo.getLunchDayViolations()[i] > 0) {
if (iFullInfo)
days += (days.isEmpty() ? "" : ", ") + compactInfo.getLunchDayViolations()[i] + " × " + Constants.DAY_NAMES_SHORT[i];
lunchViolations += compactInfo.getLunchDayViolations()[i];
}
}
if (iFullInfo && !days.isEmpty())
violatedLunchBreaks.add(c.getName() + ": " + days);
}
if (lunchViolations > 0) {
info.put("Lunch breaks", getPerc(lunchViolations, 0, constraints.size() * Constants.NR_DAYS * getWeeks().size()) + "% (" + lunchViolations + ")");
if (iFullInfo && !violatedLunchBreaks.isEmpty()) {
String message = "";
for (String s: violatedLunchBreaks)
message += (message.isEmpty() ? "" : "; ") + s;
info.put("Lunch break violations", message);
}
}
}
/**
* The class is used as a container of information concerning lunch break
* of instructors. It is designed as an attribute of an
* InstructorConstraint.
*/
public static class CompactInfo {
// lunch attributes
private int[] iLunchDayViolations = new int[Constants.NR_DAYS];
public CompactInfo() {
}
public int[] getLunchDayViolations() { return iLunchDayViolations; }
}
public class InstructorLunchBreakContext extends ValueContext {
private Map iCompactInfos = new HashMap();
protected InstructorLunchBreakContext(Assignment assignment) {
for (InstructorConstraint constraint: ((TimetableModel)getModel()).getInstructorConstraints())
iTotal += computeLunchPenalty(assignment, constraint);
}
@Override
protected void unassigned(Assignment assignment, Placement value) {
for (InstructorConstraint constraint: value.variable().getInstructorConstraints())
updateCriterion(assignment, constraint, value);
}
@Override
protected void assigned(Assignment assignment, Placement value) {
for (InstructorConstraint constraint: value.variable().getInstructorConstraints())
updateCriterion(assignment, constraint, value);
}
/**
* Method checks or sets the CompactInfo of an InstructorConstraint. It
* updates the preference of chosen criteria. The update consists of
* decrementing the criterion value by previous preference, finding the
* current preference and incrementing the criterion value by the current
* preference.
*
* @param assignment current assignment
* @param instructorConstraint
* the Instructor constraint of an instructor checked for
* criteria
* @param placement
* placement of a lecture currently (un)assigned
*/
public void updateCriterion(Assignment assignment, InstructorConstraint instructorConstraint, Placement placement) {
iTotal -= getLunchPreference(assignment, instructorConstraint);
updateLunchPenalty(assignment, instructorConstraint, placement);
iTotal += getLunchPreference(assignment, instructorConstraint);
}
/**
* Get compact info that is associated with an instructor constraint.
* Create a new one if none has been created yet.
* @param constraint instructor constraint
* @return compact info for the given constraint
*/
protected CompactInfo getCompactInfo(InstructorConstraint constraint) {
CompactInfo info = iCompactInfos.get(constraint);
if (info == null) {
info = new CompactInfo();
iCompactInfos.put(constraint, info);
}
return info;
}
/**
* Method updates number of violations in days (Mo, Tue, Wed,..) considering
* each week in the semester separately. The current number of violations
* for a day is stored in the CompactInfo.lunchDayViolations of the
* constraint, which must be set properly before the calling of the method.
*
* @param assignment current assignment
* @param constraint
* the Instructor constraint of an instructor checked for a lunch
* break
* @param p
* placement of a lecture currently (un)assigned
*/
public void updateLunchPenalty(Assignment assignment, InstructorConstraint constraint, Placement p) {
// checks only placements in the lunch time
if (p.getTimeLocation().getStartSlot() <= iLunchEnd && p.getTimeLocation().getStartSlot() + p.getTimeLocation().getLength() > iLunchStart) {
CompactInfo compactInfo = getCompactInfo(constraint);
for (int i = 0; i < Constants.NR_DAYS; i++) {
// checks only days affected by the placement
if ((p.getTimeLocation().getDayCode() & Constants.DAY_CODES[i]) != 0) {
int currentLunchStartSlot = Constants.SLOTS_PER_DAY * i + iLunchStart;
int currentLunchEndSlot = Constants.SLOTS_PER_DAY * i + iLunchEnd;
int semesterViolations = 0;
for (BitSet week : getWeeks()) {
int maxBreak = 0;
int currentBreak = 0;
for (int slot = currentLunchStartSlot; slot < currentLunchEndSlot; slot++) {
if (constraint.getContext(assignment).getPlacements(slot, week).isEmpty()) {
currentBreak++;
if (maxBreak < currentBreak) {
maxBreak = currentBreak;
}
} else {
currentBreak = 0;
}
}
if (maxBreak < iLunchLength) {
semesterViolations++;
}
}
// saving the result in the CompactInfo of the
// InstructorConstraint
compactInfo.getLunchDayViolations()[i] = semesterViolations;
}
}
}
}
/**
* Method computes number of violations in days (Mo, Tue, Wed,..) considering
* each week in the semester separately. Updates the compact infos accordingly.
* @param assignment current assignment
* @param constraint instructor constraint
* @return current penalty for the given instructor
*/
public double computeLunchPenalty(Assignment assignment, InstructorConstraint constraint) {
double violations = 0d;
CompactInfo compactInfo = getCompactInfo(constraint);
for (int i = 0; i < Constants.NR_DAYS; i++) {
int currentLunchStartSlot = Constants.SLOTS_PER_DAY * i + iLunchStart;
int currentLunchEndSlot = Constants.SLOTS_PER_DAY * i + iLunchEnd;
int semesterViolations = 0;
for (BitSet week : getWeeks()) {
int maxBreak = 0;
int currentBreak = 0;
for (int slot = currentLunchStartSlot; slot < currentLunchEndSlot; slot++) {
if (constraint.getContext(assignment).getPlacements(slot, week).isEmpty()) {
currentBreak++;
if (maxBreak < currentBreak) {
maxBreak = currentBreak;
}
} else {
currentBreak = 0;
}
}
if (maxBreak < iLunchLength) {
semesterViolations++;
}
}
// saving the result in the CompactInfo of the
// InstructorConstraint
compactInfo.getLunchDayViolations()[i] = semesterViolations;
violations += semesterViolations;
}
return Math.pow(violations, iMultiplier);
}
/**
* Method uses the CompactInfo of the InstructorConstraint and returns the
* lunch preference for this constraint. Calculation formula does not use
* linear function, the number of violations is multiplied by a power of
* iMultiplier.
*
* @param instructorConstraint
* the Instructor constraint of an instructor checked for a lunch
* break
* @return the lunch preference for this constraint
*/
private double getLunchPreference(Assignment assignment, InstructorConstraint instructorConstraint) {
double violations = 0d;
CompactInfo info = getCompactInfo(instructorConstraint);
for (int i = 0; i < Constants.NR_DAYS; i++)
violations += info.getLunchDayViolations()[i];
return Math.pow(violations, iMultiplier);
}
}
@Override
public ValueContext createAssignmentContext(Assignment assignment) {
return new InstructorLunchBreakContext(assignment);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy