001 package net.sf.cpsolver.coursett.constraint; 002 003 import java.util.Set; 004 005 import net.sf.cpsolver.coursett.model.Lecture; 006 import net.sf.cpsolver.coursett.model.Placement; 007 import net.sf.cpsolver.coursett.model.Student; 008 import net.sf.cpsolver.coursett.model.TimeLocation; 009 import net.sf.cpsolver.coursett.model.TimetableModel; 010 import net.sf.cpsolver.ifs.model.BinaryConstraint; 011 import net.sf.cpsolver.ifs.model.Value; 012 import net.sf.cpsolver.ifs.model.Variable; 013 import net.sf.cpsolver.ifs.util.ToolBox; 014 015 /** 016 * Join student enrollment constraint. 017 * <br> 018 * This constraint is placed between all pairs of classes where there is at least one student attending both classes. 019 * It represents a number of student conflicts (number of joined enrollments), if the given two classes overlap in time. 020 * <br> 021 * Also, it dynamically maintains the counter of all student conflicts. It is a soft constraint. 022 * 023 * 024 * @version 025 * CourseTT 1.1 (University Course Timetabling)<br> 026 * Copyright (C) 2006 Tomáš Müller<br> 027 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 028 * Lazenska 391, 76314 Zlin, Czech Republic<br> 029 * <br> 030 * This library is free software; you can redistribute it and/or 031 * modify it under the terms of the GNU Lesser General Public 032 * License as published by the Free Software Foundation; either 033 * version 2.1 of the License, or (at your option) any later version. 034 * <br><br> 035 * This library is distributed in the hope that it will be useful, 036 * but WITHOUT ANY WARRANTY; without even the implied warranty of 037 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 038 * Lesser General Public License for more details. 039 * <br><br> 040 * You should have received a copy of the GNU Lesser General Public 041 * License along with this library; if not, write to the Free Software 042 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 043 */ 044 045 public class JenrlConstraint extends BinaryConstraint { 046 private static org.apache.log4j.Logger sLogger = org.apache.log4j.Logger.getLogger(JenrlConstraint.class); 047 private double iJenrl = 0.0; 048 private int iNrStrudents = 0; 049 private boolean iAdded = false; 050 private boolean iAddedDistance = false; 051 052 /** Constructor 053 */ 054 public JenrlConstraint() { 055 super(); 056 } 057 058 public void computeConflicts(Value value, Set conflicts) { 059 } 060 061 public boolean inConflict(Value value) { 062 return false; 063 } 064 065 public boolean isConsistent(Value value1, Value value2) { 066 return true; 067 } 068 069 public void unassigned(long iteration, Value value) { 070 super.unassigned(iteration, value); 071 if (iAdded) { 072 ((TimetableModel)getModel()).getViolatedStudentConflictsCounter().dec((long)Math.ceil(iJenrl)); 073 if (areStudentConflictsHard()) 074 ((TimetableModel)getModel()).getViolatedHardStudentConflictsCounter().dec((long)Math.ceil(iJenrl)); 075 iAdded=false; 076 ((Lecture)first()).removeActiveJenrl(this); 077 ((Lecture)second()).removeActiveJenrl(this); 078 } 079 if (iAddedDistance) { 080 ((TimetableModel)getModel()).getViolatedDistanceStudentConflictsCounter().dec((long)Math.ceil(iJenrl)); 081 iAddedDistance = false; 082 } 083 } 084 085 /** Returns true if the given placements are overlapping or they are back-to-back and too far for students.*/ 086 public static boolean isInConflict(Placement p1, Placement p2) { 087 return isInConflict(p1, p2, true); 088 } 089 090 public static boolean isInConflict(Placement p1, Placement p2, boolean useDistances) { 091 if (p1==null || p2==null) return false; 092 TimeLocation t1=p1.getTimeLocation(), t2=p2.getTimeLocation(); 093 if (!t1.shareDays(t2)) return false; 094 if (!t1.shareWeeks(t2)) return false; 095 if (t1.shareHours(t2)) return true; 096 if (!useDistances) return false; 097 int s1 = t1.getStartSlot(), s2 = t2.getStartSlot(); 098 if (s1+t1.getNrSlotsPerMeeting()!=s2 && 099 s2+t2.getNrSlotsPerMeeting()!=s1) return false; 100 double distance = Placement.getDistance(p1,p2); 101 TimetableModel m = (TimetableModel)p1.variable().getModel(); 102 if (m==null) { 103 if (distance <= 67.0) return false; 104 if (distance <= 100.0 && ( 105 (t1.getLength()==18 && s1+t1.getLength()==s2) || 106 (t2.getLength()==18 && s2+t2.getLength()==s1))) 107 return false; 108 return true; 109 } else { 110 if (distance <= m.getStudentDistanceLimit()) return false; 111 if (distance <= m.getStudentDistanceLimit75min() && ( 112 (t1.getLength()==18 && s1+t1.getLength()==s2) || 113 (t2.getLength()==18 && s2+t2.getLength()==s1))) 114 return false; 115 return true; 116 } 117 } 118 119 public void assigned(long iteration, Value value) { 120 super.assigned(iteration, value); 121 if (second()==null || first().getAssignment()==null || second().getAssignment()==null) return; 122 //if (v1.getInitialAssignment()!=null && v2.getInitialAssignment()!=null && v1.getAssignment().equals(v1.getInitialAssignment()) && v2.getAssignment().equals(v2.getInitialAssignment())) return; 123 if (isInConflict((Placement)first().getAssignment(),(Placement)second().getAssignment())) { 124 iAdded=true; 125 ((TimetableModel)getModel()).getViolatedStudentConflictsCounter().inc((long)Math.ceil(iJenrl)); 126 if (areStudentConflictsHard()) 127 ((TimetableModel)getModel()).getViolatedHardStudentConflictsCounter().inc((long)Math.ceil(iJenrl)); 128 if (areStudentConflictsDistance()) { 129 ((TimetableModel)getModel()).getViolatedDistanceStudentConflictsCounter().inc((long)Math.ceil(iJenrl)); 130 iAddedDistance = true; 131 } 132 ((Lecture)first()).addActiveJenrl(this); 133 ((Lecture)second()).addActiveJenrl(this); 134 } 135 } 136 137 /** Number of joined enrollments if the given value is assigned to the given variable */ 138 public long jenrl(Variable variable, Value value) { 139 Lecture anotherLecture = (Lecture)(first().equals(variable)?second():first()); 140 if (anotherLecture.getAssignment()==null) return 0; 141 Lecture lecture = (Lecture) variable; 142 return (isInConflict((Placement)anotherLecture.getAssignment(),(Placement)value)?(long)Math.ceil(iJenrl):0); 143 } 144 145 /** True if the given two lectures overlap in time */ 146 public boolean isInConflict() { 147 return iAdded; 148 } 149 150 /** True if the given two lectures overlap in time */ 151 public boolean isInConflictPrecise() { 152 return isInConflict((Placement)first().getAssignment(),(Placement)second().getAssignment()); 153 } 154 155 /** Increment the number of joined enrollments (during student final sectioning) */ 156 public void incJenrl(Student student ) { 157 if (iAdded) { 158 ((TimetableModel)getModel()).getViolatedStudentConflictsCounter().dec((long)Math.ceil(iJenrl)); 159 if (areStudentConflictsHard()) 160 ((TimetableModel)getModel()).getViolatedHardStudentConflictsCounter().dec((long)Math.ceil(iJenrl)); 161 if (iAddedDistance) 162 ((TimetableModel)getModel()).getViolatedDistanceStudentConflictsCounter().dec((long)Math.ceil(iJenrl)); 163 } 164 iJenrl+=student.getJenrlWeight((Lecture)first(),(Lecture)second()); 165 iNrStrudents++; 166 if (iAdded) { 167 ((TimetableModel)getModel()).getViolatedStudentConflictsCounter().inc((long)Math.ceil(iJenrl)); 168 if (areStudentConflictsHard()) 169 ((TimetableModel)getModel()).getViolatedHardStudentConflictsCounter().inc((long)Math.ceil(iJenrl)); 170 if (iAddedDistance) 171 ((TimetableModel)getModel()).getViolatedDistanceStudentConflictsCounter().inc((long)Math.ceil(iJenrl)); 172 } 173 } 174 175 public double getJenrlWeight(Student student) { 176 return student.getJenrlWeight((Lecture)first(),(Lecture)second()); 177 } 178 179 /** Decrement the number of joined enrollments (during student final sectioning) */ 180 public void decJenrl(Student student) { 181 if (iAdded) { 182 ((TimetableModel)getModel()).getViolatedStudentConflictsCounter().dec((long)Math.ceil(iJenrl)); 183 if (areStudentConflictsHard()) 184 ((TimetableModel)getModel()).getViolatedHardStudentConflictsCounter().dec((long)Math.ceil(iJenrl)); 185 if (iAddedDistance) 186 ((TimetableModel)getModel()).getViolatedDistanceStudentConflictsCounter().dec((long)Math.ceil(iJenrl)); 187 } 188 iJenrl-=student.getJenrlWeight((Lecture)first(),(Lecture)second()); 189 iNrStrudents--; 190 if (iAdded) { 191 ((TimetableModel)getModel()).getViolatedStudentConflictsCounter().inc((long)Math.ceil(iJenrl)); 192 if (areStudentConflictsHard()) 193 ((TimetableModel)getModel()).getViolatedHardStudentConflictsCounter().inc((long)Math.ceil(iJenrl)); 194 if (iAddedDistance) 195 ((TimetableModel)getModel()).getViolatedDistanceStudentConflictsCounter().inc((long)Math.ceil(iJenrl)); 196 } 197 } 198 199 /** Number of joined enrollments (during student final sectioning) */ 200 public long getJenrl() { return (long)Math.ceil(iJenrl); } 201 public int getNrStudents() { return iNrStrudents; } 202 public boolean isHard() { return false; } 203 204 public String toString() { 205 return "Joint Enrollment between "+first().getName()+" and "+second().getName(); 206 } 207 208 public boolean areStudentConflictsHard() { 209 return ((Lecture)first()).areStudentConflictsHard((Lecture)second()); 210 } 211 212 public boolean areStudentConflictsDistance() { 213 return !((Placement)first().getAssignment()).getTimeLocation().hasIntersection(((Placement)second().getAssignment()).getTimeLocation()); 214 } 215 216 public boolean areStudentConflictsDistance(Value value) { 217 Placement first = (Placement)(first().equals(value.variable())?value:first().getAssignment()); 218 Placement second = (Placement)(second().equals(value.variable())?value:second().getAssignment()); 219 if (first==null || second==null) return false; 220 return !first.getTimeLocation().hasIntersection(second.getTimeLocation()); 221 } 222 223 public boolean isOfTheSameProblem() { 224 Lecture first = (Lecture)first(); 225 Lecture second = (Lecture)second(); 226 return ToolBox.equals(first.getSolverGroupId(),second.getSolverGroupId()); 227 } 228 229 }