001 package net.sf.cpsolver.exam.criteria;
002
003 import java.util.Map;
004 import java.util.Set;
005
006 import net.sf.cpsolver.exam.model.Exam;
007 import net.sf.cpsolver.exam.model.ExamPeriod;
008 import net.sf.cpsolver.exam.model.ExamPlacement;
009 import net.sf.cpsolver.exam.model.ExamStudent;
010 import net.sf.cpsolver.ifs.solver.Solver;
011 import net.sf.cpsolver.ifs.util.DataProperties;
012
013 /**
014 * Number of back-to-back student conflicts. I.e., number of cases when
015 * an exam is attended by a student that attends some other exam at
016 * the previous {@link ExamPeriod#prev()} or following
017 * {@link ExamPeriod#next()} period. If
018 * {@link StudentBackToBackConflicts#isDayBreakBackToBack()} is false, back-to-back conflicts
019 * are only considered between consecutive periods that are of the same day.
020 * <br><br>
021 * Back-to-back student conflict weight can be set by problem property
022 * Exams.BackToBackConflictWeight, or in the input xml file,
023 * property backToBackConflictWeight.
024 *
025 *
026 * <br>
027 *
028 * @version ExamTT 1.2 (Examination Timetabling)<br>
029 * Copyright (C) 2008 - 2012 Tomas Muller<br>
030 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
031 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
032 * <br>
033 * This library is free software; you can redistribute it and/or modify
034 * it under the terms of the GNU Lesser General Public License as
035 * published by the Free Software Foundation; either version 3 of the
036 * License, or (at your option) any later version. <br>
037 * <br>
038 * This library is distributed in the hope that it will be useful, but
039 * WITHOUT ANY WARRANTY; without even the implied warranty of
040 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
041 * Lesser General Public License for more details. <br>
042 * <br>
043 * You should have received a copy of the GNU Lesser General Public
044 * License along with this library; if not see
045 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
046 */
047 public class StudentBackToBackConflicts extends ExamCriterion {
048 private boolean iDayBreakBackToBack = false;
049
050 @Override
051 public boolean init(Solver<Exam, ExamPlacement> solver) {
052 boolean ret = super.init(solver);
053 iDayBreakBackToBack = solver.getProperties().getPropertyBoolean("Exams.IsDayBreakBackToBack", iDayBreakBackToBack);
054 return ret;
055 }
056
057 @Override
058 public String getWeightName() {
059 return "Exams.BackToBackConflictWeight";
060 }
061
062 @Override
063 public String getXmlWeightName() {
064 return "backToBackConflictWeight";
065 }
066
067 @Override
068 public double getWeightDefault(DataProperties config) {
069 return 10.0;
070 }
071
072 /**
073 * True when back-to-back student conflict is to be encountered when a
074 * student is enrolled into an exam that is on the last period of one day
075 * and another exam that is on the first period of the consecutive day. It
076 * can be set by problem property Exams.IsDayBreakBackToBack, or in the
077 * input xml file, property isDayBreakBackToBack)
078 */
079 public boolean isDayBreakBackToBack() {
080 return iDayBreakBackToBack;
081 }
082
083 /**
084 * True when back-to-back student conflict is to be encountered when a
085 * student is enrolled into an exam that is on the last period of one day
086 * and another exam that is on the first period of the consecutive day. It
087 * can be set by problem property Exams.IsDayBreakBackToBack, or in the
088 * input xml file, property isDayBreakBackToBack)
089 *
090 */
091 public void setDayBreakBackToBack(boolean dayBreakBackToBack) {
092 iDayBreakBackToBack = dayBreakBackToBack;
093 }
094
095 @Override
096 public void getXmlParameters(Map<String, String> params) {
097 params.put(getXmlWeightName(), String.valueOf(getWeight()));
098 params.put("isDayBreakBackToBack", isDayBreakBackToBack() ? "true" : "false");
099 }
100
101 @Override
102 public void setXmlParameters(Map<String, String> params) {
103 try {
104 setWeight(Double.valueOf(params.get(getXmlWeightName())));
105 } catch (NumberFormatException e) {} catch (NullPointerException e) {}
106 try {
107 setDayBreakBackToBack("true".equals(params.get("isDayBreakBackToBack")));
108 } catch (NumberFormatException e) {} catch (NullPointerException e) {}
109 }
110
111 @Override
112 public double getValue(ExamPlacement value, Set<ExamPlacement> conflicts) {
113 Exam exam = value.variable();
114 int penalty = 0;
115 for (ExamStudent s : exam.getStudents()) {
116 if (value.getPeriod().prev() != null) {
117 if (isDayBreakBackToBack() || value.getPeriod().prev().getDay() == value.getPeriod().getDay()) {
118 Set<Exam> exams = s.getExams(value.getPeriod().prev());
119 int nrExams = exams.size() + (exams.contains(exam) ? -1 : 0);
120 penalty += nrExams;
121 }
122 }
123 if (value.getPeriod().next() != null) {
124 if (isDayBreakBackToBack() || value.getPeriod().next().getDay() == value.getPeriod().getDay()) {
125 Set<Exam> exams = s.getExams(value.getPeriod().next());
126 int nrExams = exams.size() + (exams.contains(exam) ? -1 : 0);
127 penalty += nrExams;
128 }
129 }
130 }
131 return penalty;
132 }
133
134 @Override
135 public String getName() {
136 return "Back-To-Back Conflicts";
137 }
138
139 @Override
140 public void getInfo(Map<String, String> info) {
141 if (getValue() != 0.0)
142 info.put(getName(), sDoubleFormat.format(getValue()));
143 }
144
145 @Override
146 public String toString() {
147 return "BTB:" + sDoubleFormat.format(getValue());
148 }
149 }