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; 013 014/** 015 * Teaching request. A set of sections of a course to be assigned to an instructor. 016 * Each teaching request has a teaching load. The maximal teaching load of an instructor 017 * cannot be breached. 018 * 019 * @version IFS 1.3 (Instructor Sectioning)<br> 020 * Copyright (C) 2016 Tomas Muller<br> 021 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 022 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 023 * <br> 024 * This library is free software; you can redistribute it and/or modify 025 * it under the terms of the GNU Lesser General Public License as 026 * published by the Free Software Foundation; either version 3 of the 027 * License, or (at your option) any later version. <br> 028 * <br> 029 * This library is distributed in the hope that it will be useful, but 030 * WITHOUT ANY WARRANTY; without even the implied warranty of 031 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 032 * Lesser General Public License for more details. <br> 033 * <br> 034 * You should have received a copy of the GNU Lesser General Public 035 * License along with this library; if not see 036 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 037 */ 038public class TeachingRequest { 039 private long iRequestId; 040 private Course iCourse; 041 private float iLoad; 042 private List<Section> iSections = new ArrayList<Section>(); 043 private List<Preference<Attribute>> iAttributePreferences = new ArrayList<Preference<Attribute>>(); 044 private List<Preference<Instructor>> iInstructorPreferences = new ArrayList<Preference<Instructor>>(); 045 private Variable[] iVariables; 046 047 /** 048 * Constructor 049 * @param requestId teaching request id 050 * @param nrVariables number of instructors for this teaching request 051 * @param course course 052 * @param load teaching load 053 * @param sections list of sections 054 */ 055 public TeachingRequest(long requestId, int nrVariables, Course course, float load, Collection<Section> sections) { 056 super(); 057 iRequestId = requestId; 058 iCourse = course; 059 iLoad = load; 060 iSections.addAll(sections); 061 iVariables = new Variable[nrVariables]; 062 for (int i = 0; i < nrVariables; i++) 063 iVariables[i] = new Variable(i); 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 * Get single instructor assignment variables 076 * @return variables for this request 077 */ 078 public Variable[] getVariables() { 079 return iVariables; 080 } 081 082 /** 083 * Get single instructor assignment variable 084 * @param index index of the variable 085 * @return variable for the index-th instructor assignment 086 */ 087 public Variable getVariable(int index) { 088 return iVariables[index]; 089 } 090 091 /** 092 * Get number of instructors needed 093 * @return number of variables for this request 094 */ 095 public int getNrInstructors() { 096 return iVariables.length; 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)getVariables()[0].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 /** 190 * Sections of the request that was provided in the constructor 191 * @return sections of the request 192 */ 193 public List<Section> getSections() { return iSections; } 194 195 /** 196 * Return teaching load of the request 197 * @return teaching load 198 */ 199 public float getLoad() { return iLoad; } 200 201 /** 202 * Set teaching load of the request 203 * @param load teaching load 204 */ 205 public void setLoad(float load) { iLoad = load; } 206 207 @Override 208 public String toString() { 209 return iCourse.getCourseName() + " " + getSections(); 210 } 211 212 /** 213 * Check if the given request fully share the common sections with this request 214 * @param request the other teaching request 215 * @return true, if all common sections of this request are also present in the other request 216 */ 217 public boolean sameCommon(TeachingRequest request) { 218 for (Section section: getSections()) 219 if (section.isCommon() && !request.getSections().contains(section)) 220 return false; 221 return true; 222 } 223 224 /** 225 * Count the number of common sections that the given request share with this request 226 * @param request the other teaching request 227 * @return the number of shared common sections 228 */ 229 public double nrSameLectures(TeachingRequest request) { 230 if (!sameCourse(request)) return 0.0; 231 double same = 0; int common = 0; 232 for (Section section: getSections()) 233 if (section.isCommon()) { 234 common ++; 235 if (request.getSections().contains(section)) same++; 236 } 237 return (common == 0 ? 1.0 : same / common); 238 } 239 240 /** 241 * Check if this request and the given request are of the same course 242 * @param request the other teaching request 243 * @return true, if the course of the given request is the same as the course of this request 244 */ 245 public boolean sameCourse(TeachingRequest request) { 246 return getCourse().equals(request.getCourse()); 247 } 248 249 /** 250 * Check if this request overlaps with the given one 251 * @param request the other teaching request 252 * @return true, if there are two sections that are overlapping in time (that are not allowed to overlap) 253 */ 254 public boolean overlaps(TeachingRequest request) { 255 for (Section section: getSections()) { 256 if (section.isAllowOverlap() || section.getTime() == null || request.getSections().contains(section)) continue; 257 for (Section other: request.getSections()) { 258 if (other.isAllowOverlap() || other.getTime() == null || getSections().contains(other)) continue; 259 if (section.getTime().hasIntersection(other.getTime())) return true; 260 } 261 } 262 return false; 263 } 264 265 /** 266 * Count the number of (allowed) overlapping time slots between this request and the given one 267 * @param request the other teaching request 268 * @return the number of overlapping time slots 269 */ 270 public int share(TeachingRequest request) { 271 int ret = 0; 272 for (Section section: getSections()) 273 ret += section.share(request.getSections()); 274 return ret; 275 } 276 277 /** 278 * Count the number of overlapping time slots between this request and the given time 279 * @param time a time 280 * @return the number of overlapping time slots 281 */ 282 public int share(TimeLocation time) { 283 int ret = 0; 284 for (Section section: getSections()) 285 ret += section.share(time); 286 return ret; 287 } 288 289 /** 290 * Average value of the back-to-backs between this request and the given one 291 * @param request the other teaching request 292 * @param diffRoomWeight different room penalty 293 * @param diffTypeWeight different instructional type penalty 294 * @return average value of {@link Section#countBackToBacks(Collection, double, double)} between the two, common sections are ignored 295 */ 296 public double countBackToBacks(TeachingRequest request, double diffRoomWeight, double diffTypeWeight) { 297 double b2b = 0.0; 298 int count = 0; 299 for (Section section: getSections()) { 300 if (!section.isCommon() || !sameCourse(request) || !request.getSections().contains(section)) { 301 b2b += section.countBackToBacks(request.getSections(), diffRoomWeight, diffTypeWeight); 302 count ++; 303 } 304 } 305 return (count == 0 ? 0.0 : b2b / count); 306 } 307 308 @Override 309 public int hashCode() { 310 return (int)(iRequestId ^ (iRequestId >>> 32)); 311 } 312 313 @Override 314 public boolean equals(Object o) { 315 if (o == null || !(o instanceof TeachingRequest)) return false; 316 TeachingRequest tr = (TeachingRequest)o; 317 return getRequestId() == tr.getRequestId(); 318 } 319 320 /** 321 * Single instructor assignment to this teaching request 322 */ 323 public class Variable extends org.cpsolver.ifs.model.Variable<TeachingRequest.Variable, TeachingAssignment> { 324 private int iIndex; 325 326 /** 327 * Constructor 328 * @param index instructor index (if a class can be taught by multiple instructors, the index identifies the particular request) 329 */ 330 public Variable(int index) { 331 iId = (iRequestId << 8) + index; 332 iIndex = index; 333 } 334 335 /** 336 * Instructor index that was provided in the constructor 337 * @return instructor index 338 */ 339 public int getInstructorIndex() { 340 return iIndex; 341 } 342 343 /** 344 * Teaching request for this variable 345 * @return teaching request 346 */ 347 public TeachingRequest getRequest() { 348 return TeachingRequest.this; 349 } 350 351 /** 352 * Course of the request that was provided in the constructor 353 * @return course of the request 354 */ 355 public Course getCourse() { 356 return iCourse; 357 } 358 359 /** 360 * Sections of the request that was provided in the constructor 361 * @return sections of the request 362 */ 363 public List<Section> getSections() { return iSections; } 364 365 @Override 366 public List<TeachingAssignment> values(Assignment<TeachingRequest.Variable, TeachingAssignment> assignment) { 367 List<TeachingAssignment> values = super.values(assignment); 368 if (values == null) { 369 values = new ArrayList<TeachingAssignment>(); 370 for (Instructor instructor: ((InstructorSchedulingModel)getModel()).getInstructors()) { 371 if (instructor.canTeach(getRequest())) { 372 PreferenceCombination attributePref = getAttributePreference(instructor); 373 if (attributePref.isProhibited()) continue; 374 values.add(new TeachingAssignment(this, instructor, attributePref.getPreferenceInt())); 375 } 376 } 377 setValues(values); 378 } 379 return values; 380 } 381 382 @Override 383 public void variableAssigned(Assignment<TeachingRequest.Variable, TeachingAssignment> assignment, long iteration, TeachingAssignment ta) { 384 super.variableAssigned(assignment, iteration, ta); 385 ta.getInstructor().getContext(assignment).assigned(assignment, ta); 386 } 387 388 @Override 389 public void variableUnassigned(Assignment<TeachingRequest.Variable, TeachingAssignment> assignment, long iteration, TeachingAssignment ta) { 390 super.variableUnassigned(assignment, iteration, ta); 391 ta.getInstructor().getContext(assignment).unassigned(assignment, ta); 392 } 393 394 @Override 395 public int hashCode() { 396 return new Long(iRequestId << 8 + iIndex).hashCode(); 397 } 398 399 @Override 400 public boolean equals(Object o) { 401 if (o == null || !(o instanceof Variable)) return false; 402 Variable tr = (Variable)o; 403 return getRequest().getRequestId() == tr.getRequest().getRequestId() && getInstructorIndex() == tr.getInstructorIndex(); 404 } 405 406 @Override 407 public String getName() { 408 return iCourse.getCourseName() + (getNrInstructors() > 1 ? "[" + getInstructorIndex() + "]" : "") + " " + getSections(); 409 } 410 } 411}