001package org.cpsolver.instructor.model; 002 003import java.util.ArrayList; 004import java.util.Collection; 005import java.util.List; 006import java.util.Set; 007 008import org.cpsolver.coursett.Constants; 009import org.cpsolver.coursett.model.TimeLocation; 010import org.cpsolver.coursett.preference.PreferenceCombination; 011import org.cpsolver.coursett.preference.SumPreferenceCombination; 012import org.cpsolver.ifs.assignment.Assignment; 013import org.cpsolver.ifs.model.Variable; 014 015/** 016 * Teaching request. A set of sections of a course to be assigned to an instructor. 017 * Each teaching request has a teaching load. The maximal teaching load of an instructor 018 * cannot be breached. 019 * 020 * @version IFS 1.3 (Instructor Sectioning)<br> 021 * Copyright (C) 2016 Tomas Muller<br> 022 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 023 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 024 * <br> 025 * This library is free software; you can redistribute it and/or modify 026 * it under the terms of the GNU Lesser General Public License as 027 * published by the Free Software Foundation; either version 3 of the 028 * License, or (at your option) any later version. <br> 029 * <br> 030 * This library is distributed in the hope that it will be useful, but 031 * WITHOUT ANY WARRANTY; without even the implied warranty of 032 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 033 * Lesser General Public License for more details. <br> 034 * <br> 035 * You should have received a copy of the GNU Lesser General Public 036 * License along with this library; if not see 037 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 038 */ 039public class TeachingRequest extends Variable<TeachingRequest, TeachingAssignment> { 040 private long iRequestId; 041 private int iIndex; 042 private Course iCourse; 043 private float iLoad; 044 private List<Section> iSections = new ArrayList<Section>(); 045 private List<Preference<Attribute>> iAttributePreferences = new ArrayList<Preference<Attribute>>(); 046 private List<Preference<Instructor>> iInstructorPreferences = new ArrayList<Preference<Instructor>>(); 047 048 /** 049 * 050 * @param requestId teaching request id 051 * @param index instructor index (if a class can be taught by multiple instructors, the index identifies the particular request) 052 * @param course course 053 * @param load teaching load 054 * @param sections list of sections 055 */ 056 public TeachingRequest(long requestId, int index, Course course, float load, Collection<Section> sections) { 057 super(); 058 iId = (requestId << 8) + index; 059 iRequestId = requestId; 060 iIndex = index; 061 iCourse = course; 062 iLoad = load; 063 iSections.addAll(sections); 064 } 065 066 /** 067 * Teaching request id that was provided in the constructor 068 * @return request id 069 */ 070 public long getRequestId() { 071 return iRequestId; 072 } 073 074 /** 075 * Instructor index that was provided in the constructor 076 * @return instructor index 077 */ 078 public int getInstructorIndex() { 079 return iIndex; 080 } 081 082 @Override 083 public List<TeachingAssignment> values(Assignment<TeachingRequest, TeachingAssignment> assignment) { 084 List<TeachingAssignment> values = super.values(assignment); 085 if (values == null) { 086 values = new ArrayList<TeachingAssignment>(); 087 for (Instructor instructor: ((InstructorSchedulingModel)getModel()).getInstructors()) { 088 if (instructor.canTeach(this)) { 089 PreferenceCombination attributePref = getAttributePreference(instructor); 090 if (attributePref.isProhibited()) continue; 091 values.add(new TeachingAssignment(this, instructor, attributePref.getPreferenceInt())); 092 } 093 } 094 setValues(values); 095 } 096 return values; 097 } 098 099 /** 100 * Return attribute preferences for this request 101 * @return attribute preferences 102 */ 103 public List<Preference<Attribute>> getAttributePreferences() { return iAttributePreferences; } 104 105 /** 106 * Add attribute preference 107 * @param pref attribute preference 108 */ 109 public void addAttributePreference(Preference<Attribute> pref) { iAttributePreferences.add(pref); } 110 111 /** 112 * Compute attribute preference for the given instructor and attribute type 113 * @param instructor an instructor 114 * @param type an attribute type 115 * @return combined preference using {@link Attribute.Type#isConjunctive()} and {@link Attribute.Type#isRequired()} properties 116 */ 117 public int getAttributePreference(Instructor instructor, Attribute.Type type) { 118 Set<Attribute> attributes = instructor.getAttributes(type); 119 boolean hasReq = false, hasPref = false, needReq = false; 120 PreferenceCombination ret = new SumPreferenceCombination(); 121 for (Preference<Attribute> pref: iAttributePreferences) { 122 if (!type.equals(pref.getTarget().getType())) continue; 123 if (pref.isRequired()) needReq = true; 124 if (attributes.contains(pref.getTarget())) { 125 if (pref.isProhibited()) return Constants.sPreferenceLevelProhibited; 126 else if (pref.isRequired()) hasReq = true; 127 else ret.addPreferenceInt(pref.getPreference()); 128 hasPref = true; 129 } else { 130 if (pref.isRequired() && type.isConjunctive()) return Constants.sPreferenceLevelProhibited; 131 } 132 } 133 if (needReq && !hasReq) return Constants.sPreferenceLevelProhibited; 134 if (type.isRequired() && !hasPref) return Constants.sPreferenceLevelProhibited; 135 return ret.getPreferenceInt(); 136 } 137 138 /** 139 * Compute attribute preference for the given instructor 140 * @param instructor an instructor 141 * @return using {@link SumPreferenceCombination} for the preferences of each attribute type (using {@link TeachingRequest#getAttributePreference(Instructor, org.cpsolver.instructor.model.Attribute.Type)}) 142 */ 143 public PreferenceCombination getAttributePreference(Instructor instructor) { 144 PreferenceCombination preference = new SumPreferenceCombination(); 145 for (Attribute.Type type: ((InstructorSchedulingModel)getModel()).getAttributeTypes()) 146 preference.addPreferenceInt(getAttributePreference(instructor, type)); 147 return preference; 148 } 149 150 /** 151 * Return instructor preferences for this request 152 * @return instructor preferences 153 */ 154 public List<Preference<Instructor>> getInstructorPreferences() { return iInstructorPreferences; } 155 156 /** 157 * Add instructor preference 158 * @param pref instructor preference 159 */ 160 public void addInstructorPreference(Preference<Instructor> pref) { iInstructorPreferences.add(pref); } 161 162 /** 163 * Return instructor preference for the given instructor 164 * @param instructor an instructor 165 * @return instructor preference for the given instructor 166 */ 167 public Preference<Instructor> getInstructorPreference(Instructor instructor) { 168 boolean hasRequired = false; 169 for (Preference<Instructor> pref: iInstructorPreferences) 170 if (pref.isRequired()) { hasRequired = true; break; } 171 for (Preference<Instructor> pref: iInstructorPreferences) 172 if (pref.getTarget().equals(instructor)) { 173 if (hasRequired && !pref.isRequired()) continue; 174 return pref; 175 } 176 if (hasRequired) 177 return new Preference<Instructor>(instructor, Constants.sPreferenceLevelProhibited); 178 return new Preference<Instructor>(instructor, Constants.sPreferenceLevelNeutral); 179 } 180 181 /** 182 * Course of the request that was provided in the constructor 183 * @return course of the request 184 */ 185 public Course getCourse() { 186 return iCourse; 187 } 188 189 @Override 190 public String getName() { 191 return iCourse.getCourseName() + " " + getSections(); 192 } 193 194 /** 195 * Sections of the request that was provided in the constructor 196 * @return sections of the request 197 */ 198 public List<Section> getSections() { return iSections; } 199 200 /** 201 * Return teaching load of the request 202 * @return teaching load 203 */ 204 public float getLoad() { return iLoad; } 205 206 /** 207 * Set teaching load of the request 208 * @param load teaching load 209 */ 210 public void setLoad(float load) { iLoad = load; } 211 212 @Override 213 public String toString() { 214 return getName(); 215 } 216 217 /** 218 * Check if the given request fully share the common sections with this request 219 * @param request the other teaching request 220 * @return true, if all common sections of this request are also present in the other request 221 */ 222 public boolean sameCommon(TeachingRequest request) { 223 for (Section section: getSections()) 224 if (section.isCommon() && !request.getSections().contains(section)) 225 return false; 226 return true; 227 } 228 229 /** 230 * Count the number of common sections that the given request share with this request 231 * @param request the other teaching request 232 * @return the number of shared common sections 233 */ 234 public double nrSameLectures(TeachingRequest request) { 235 if (!sameCourse(request)) return 0.0; 236 double same = 0; int common = 0; 237 for (Section section: getSections()) 238 if (section.isCommon()) { 239 common ++; 240 if (request.getSections().contains(section)) same++; 241 } 242 return (common == 0 ? 0.0 : same / common); 243 } 244 245 /** 246 * Check if this request and the given request are of the same course 247 * @param request the other teaching request 248 * @return true, if the course of the given request is the same as the course of this request 249 */ 250 public boolean sameCourse(TeachingRequest request) { 251 return getCourse().equals(request.getCourse()); 252 } 253 254 /** 255 * Check if this request overlaps with the given one 256 * @param request the other teaching request 257 * @return true, if there are two sections that are overlapping in time (that are not allowed to overlap) 258 */ 259 public boolean overlaps(TeachingRequest request) { 260 for (Section section: getSections()) { 261 if (section.isAllowOverlap() || section.getTime() == null || request.getSections().contains(section)) continue; 262 for (Section other: request.getSections()) { 263 if (other.isAllowOverlap() || other.getTime() == null || getSections().contains(other)) continue; 264 if (section.getTime().hasIntersection(other.getTime())) return true; 265 } 266 } 267 return false; 268 } 269 270 /** 271 * Count the number of (allowed) overlapping time slots between this request and the given one 272 * @param request the other teaching request 273 * @return the number of overlapping time slots 274 */ 275 public int share(TeachingRequest request) { 276 int ret = 0; 277 for (Section section: getSections()) 278 ret += section.share(request.getSections()); 279 return ret; 280 } 281 282 /** 283 * Count the number of overlapping time slots between this request and the given time 284 * @param time a time 285 * @return the number of overlapping time slots 286 */ 287 public int share(TimeLocation time) { 288 int ret = 0; 289 for (Section section: getSections()) 290 ret += section.share(time); 291 return ret; 292 } 293 294 /** 295 * Average value of the back-to-backs between this request and the given one 296 * @param request the other teaching request 297 * @param diffRoomWeight different room penalty 298 * @param diffTypeWeight different instructional type penalty 299 * @return average value of {@link Section#countBackToBacks(Collection, double, double)} between the two, common sections are ignored 300 */ 301 public double countBackToBacks(TeachingRequest request, double diffRoomWeight, double diffTypeWeight) { 302 double b2b = 0.0; 303 int count = 0; 304 for (Section section: getSections()) { 305 if (!section.isCommon() || !sameCourse(request) || !request.getSections().contains(section)) { 306 b2b += section.countBackToBacks(request.getSections(), diffRoomWeight, diffTypeWeight); 307 count ++; 308 } 309 } 310 return (count == 0 ? 0.0 : b2b / count); 311 } 312 313 @Override 314 public int hashCode() { 315 return new Long(iRequestId << 8 + iIndex).hashCode(); 316 } 317 318 @Override 319 public boolean equals(Object o) { 320 if (o == null || !(o instanceof TeachingRequest)) return false; 321 TeachingRequest tr = (TeachingRequest)o; 322 return getRequestId() == tr.getRequestId() && getInstructorIndex() == tr.getInstructorIndex(); 323 } 324 325 @Override 326 public void variableAssigned(Assignment<TeachingRequest, TeachingAssignment> assignment, long iteration, TeachingAssignment ta) { 327 super.variableAssigned(assignment, iteration, ta); 328 ta.getInstructor().getContext(assignment).assigned(assignment, ta); 329 } 330 331 @Override 332 public void variableUnassigned(Assignment<TeachingRequest, TeachingAssignment> assignment, long iteration, TeachingAssignment ta) { 333 super.variableUnassigned(assignment, iteration, ta); 334 ta.getInstructor().getContext(assignment).unassigned(assignment, ta); 335 } 336}