001package org.cpsolver.coursett.model; 002 003import java.util.ArrayList; 004import java.util.Collection; 005import java.util.Collections; 006import java.util.Comparator; 007import java.util.Enumeration; 008import java.util.HashSet; 009import java.util.HashMap; 010import java.util.Iterator; 011import java.util.List; 012import java.util.Map; 013import java.util.Set; 014import java.util.concurrent.atomic.AtomicReference; 015import java.util.concurrent.locks.ReentrantReadWriteLock; 016 017import org.cpsolver.coursett.Constants; 018import org.cpsolver.coursett.constraint.ClassLimitConstraint; 019import org.cpsolver.coursett.constraint.DepartmentSpreadConstraint; 020import org.cpsolver.coursett.constraint.FlexibleConstraint; 021import org.cpsolver.coursett.constraint.GroupConstraint; 022import org.cpsolver.coursett.constraint.IgnoreStudentConflictsConstraint; 023import org.cpsolver.coursett.constraint.InstructorConstraint; 024import org.cpsolver.coursett.constraint.JenrlConstraint; 025import org.cpsolver.coursett.constraint.RoomConstraint; 026import org.cpsolver.coursett.constraint.SpreadConstraint; 027import org.cpsolver.coursett.criteria.StudentCommittedConflict; 028import org.cpsolver.coursett.criteria.StudentConflict; 029import org.cpsolver.ifs.assignment.Assignment; 030import org.cpsolver.ifs.assignment.context.AssignmentContext; 031import org.cpsolver.ifs.assignment.context.VariableWithContext; 032import org.cpsolver.ifs.constant.ConstantVariable; 033import org.cpsolver.ifs.model.Constraint; 034import org.cpsolver.ifs.model.GlobalConstraint; 035import org.cpsolver.ifs.model.WeakeningConstraint; 036import org.cpsolver.ifs.util.DistanceMetric; 037import org.cpsolver.ifs.util.ToolBox; 038 039 040/** 041 * Lecture (variable). 042 * 043 * @version CourseTT 1.3 (University Course Timetabling)<br> 044 * Copyright (C) 2006 - 2014 Tomas Muller<br> 045 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 046 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 047 * <br> 048 * This library is free software; you can redistribute it and/or modify 049 * it under the terms of the GNU Lesser General Public License as 050 * published by the Free Software Foundation; either version 3 of the 051 * License, or (at your option) any later version. <br> 052 * <br> 053 * This library is distributed in the hope that it will be useful, but 054 * WITHOUT ANY WARRANTY; without even the implied warranty of 055 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 056 * Lesser General Public License for more details. <br> 057 * <br> 058 * You should have received a copy of the GNU Lesser General Public 059 * License along with this library; if not see 060 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 061 */ 062 063public class Lecture extends VariableWithContext<Lecture, Placement, Lecture.LectureContext> implements ConstantVariable<Placement> { 064 private Long iClassId; 065 private Long iSolverGroupId; 066 private Long iSchedulingSubpartId; 067 private String iName; 068 private Long iDept; 069 private Long iScheduler; 070 private List<TimeLocation> iTimeLocations; 071 private List<RoomLocation> iRoomLocations; 072 private String iNote = null; 073 074 private int iMinClassLimit; 075 private int iMaxClassLimit; 076 private float iRoomToLimitRatio; 077 private int iNrRooms; 078 private int iOrd; 079 private double iWeight = 1.0; 080 081 private Set<Student> iStudents = new HashSet<Student>(); 082 private DepartmentSpreadConstraint iDeptSpreadConstraint = null; 083 private Set<SpreadConstraint> iSpreadConstraints = new HashSet<SpreadConstraint>(); 084 private Set<Constraint<Lecture, Placement>> iWeakeningConstraints = new HashSet<Constraint<Lecture, Placement>>(); 085 private List<InstructorConstraint> iInstructorConstraints = new ArrayList<InstructorConstraint>(); 086 private AtomicReference<Set<Long>> iIgnoreStudentConflictsWith = new AtomicReference<Set<Long>>(); 087 private ClassLimitConstraint iClassLimitConstraint = null; 088 089 private Lecture iParent = null; 090 private HashMap<Long, List<Lecture>> iChildren = null; 091 private java.util.List<Lecture> iSameSubpartLectures = null; 092 private Configuration iParentConfiguration = null; 093 094 private List<JenrlConstraint> iJenrlConstraints = new ArrayList<JenrlConstraint>(); 095 private HashMap<Lecture, JenrlConstraint> iJenrlConstraintsHash = new HashMap<Lecture, JenrlConstraint>(); 096 private HashMap<Placement, Integer> iCommitedConflicts = new HashMap<Placement, Integer>(); 097 private Set<GroupConstraint> iGroupConstraints = new HashSet<GroupConstraint>(); 098 private Set<GroupConstraint> iHardGroupSoftConstraints = new HashSet<GroupConstraint>(); 099 private Set<GroupConstraint> iCanShareRoomGroupConstraints = new HashSet<GroupConstraint>(); 100 private Set<FlexibleConstraint> iFlexibleGroupConstraints = new HashSet<FlexibleConstraint>(); 101 102 public boolean iCommitted = false; 103 104 public static boolean sSaveMemory = false; 105 public static boolean sAllowBreakHard = false; 106 private int iMaxRoomCombinations = -1; 107 108 private Integer iCacheMinRoomSize = null; 109 private Integer iCacheMaxRoomSize = null; 110 private Integer iCacheMaxAchievableClassLimit = null; 111 112 private final ReentrantReadWriteLock iLock = new ReentrantReadWriteLock(); 113 114 /** 115 * Constructor 116 * 117 * @param id class unique id 118 * @param solverGroupId solver group unique id 119 * @param schedulingSubpartId scheduling subpart unique id 120 * @param name class name 121 * @param timeLocations set of time locations 122 * @param roomLocations set of room location 123 * @param nrRooms number of rooms into which the class is to be assigned 124 * @param initialPlacement initial placement 125 * @param minClassLimit minimum class limit 126 * @param maxClassLimit maximum class limit 127 * @param room2limitRatio room ratio 128 */ 129 public Lecture(Long id, Long solverGroupId, Long schedulingSubpartId, String name, 130 java.util.List<TimeLocation> timeLocations, java.util.List<RoomLocation> roomLocations, int nrRooms, 131 Placement initialPlacement, int minClassLimit, int maxClassLimit, double room2limitRatio) { 132 super(initialPlacement); 133 iClassId = id; 134 iSchedulingSubpartId = schedulingSubpartId; 135 iTimeLocations = new ArrayList<TimeLocation>(timeLocations); 136 iRoomLocations = new ArrayList<RoomLocation>(roomLocations); 137 iName = name; 138 iMinClassLimit = minClassLimit; 139 iMaxClassLimit = maxClassLimit; 140 iRoomToLimitRatio = (float)room2limitRatio; 141 iNrRooms = nrRooms; 142 iSolverGroupId = solverGroupId; 143 } 144 145 public Lecture(Long id, Long solverGroupId, String name) { 146 super(null); 147 iClassId = id; 148 iSolverGroupId = solverGroupId; 149 iName = name; 150 } 151 152 public Long getSolverGroupId() { 153 return iSolverGroupId; 154 } 155 156 /** 157 * Add active jenrl constraint (active mean that there is at least one 158 * student between its classes) 159 * @param assignment current assignment 160 * @param constr an active jenrl constraint 161 */ 162 public void addActiveJenrl(Assignment<Lecture, Placement> assignment, JenrlConstraint constr) { 163 getContext(assignment).addActiveJenrl(constr); 164 } 165 166 /** 167 * Active jenrl constraints (active mean that there is at least one student 168 * between its classes) 169 * @param assignment current assignment 170 * @return set of active jenrl constraints 171 */ 172 public Set<JenrlConstraint> activeJenrls(Assignment<Lecture, Placement> assignment) { 173 return getContext(assignment).activeJenrls(); 174 } 175 176 /** 177 * Remove active jenrl constraint (active mean that there is at least one 178 * student between its classes) 179 * @param assignment current assignment 180 * @param constr an active jenrl constraint 181 */ 182 public void removeActiveJenrl(Assignment<Lecture, Placement> assignment, JenrlConstraint constr) { 183 getContext(assignment).removeActiveJenrl(constr); 184 } 185 186 /** Class id 187 * @return class unique id 188 **/ 189 public Long getClassId() { 190 return iClassId; 191 } 192 193 /** 194 * Scheduling subpart id 195 * @return scheduling subpart unique id 196 */ 197 public Long getSchedulingSubpartId() { 198 return iSchedulingSubpartId; 199 } 200 201 /** Class name */ 202 @Override 203 public String getName() { 204 return iName; 205 } 206 207 /** Class id */ 208 @Override 209 public long getId() { 210 return iClassId.longValue(); 211 } 212 213 /** Instructor name 214 * @return list of instructor names 215 **/ 216 public List<String> getInstructorNames() { 217 List<String> ret = new ArrayList<String>(); 218 for (InstructorConstraint ic : iInstructorConstraints) { 219 ret.add(ic.getName()); 220 } 221 return ret; 222 } 223 224 public String getInstructorName() { 225 StringBuffer sb = new StringBuffer(); 226 for (InstructorConstraint ic : iInstructorConstraints) { 227 if (sb.length() > 0) 228 sb.append(", "); 229 sb.append(ic.getName()); 230 } 231 return sb.toString(); 232 } 233 234 /** List of enrolled students 235 * @return list of enrolled students 236 **/ 237 public Set<Student> students() { 238 return iStudents; 239 } 240 241 /** 242 * Total weight of all enrolled students 243 * @return sum of {@link Student#getOfferingWeight(Configuration)} of each enrolled student 244 */ 245 public double nrWeightedStudents() { 246 double w = 0.0; 247 for (Student s : iStudents) { 248 w += s.getOfferingWeight(getConfiguration()); 249 } 250 return w; 251 } 252 253 /** Add an enrolled student 254 * @param assignment current assignment 255 * @param student a student to add 256 **/ 257 public void addStudent(Assignment<Lecture, Placement> assignment, Student student) { 258 if (!iStudents.add(student)) 259 return; 260 Placement value = (assignment == null ? null : assignment.getValue(this)); 261 if (value != null && getModel() != null) 262 getModel().getCriterion(StudentCommittedConflict.class).inc(assignment, student.countConflictPlacements(value)); 263 iLock.writeLock().lock(); 264 iCommitedConflicts.clear(); 265 iLock.writeLock().unlock(); 266 } 267 268 /** 269 * Remove an enrolled student 270 * @param assignment current assignment 271 * @param student a student to remove 272 */ 273 public void removeStudent(Assignment<Lecture, Placement> assignment, Student student) { 274 if (!iStudents.remove(student)) 275 return; 276 Placement value = (assignment == null ? null : assignment.getValue(this)); 277 if (value != null && getModel() != null) 278 getModel().getCriterion(StudentCommittedConflict.class).inc(assignment, -student.countConflictPlacements(value)); 279 iLock.writeLock().lock(); 280 iCommitedConflicts.clear(); 281 iLock.writeLock().unlock(); 282 } 283 284 /** Returns true if the given student is enrolled 285 * @param student a student 286 * @return true if the given student is enrolled in this class 287 **/ 288 public boolean hasStudent(Student student) { 289 return iStudents.contains(student); 290 } 291 292 /** Set of lectures of the same class (only section is different) 293 * @param sameSubpartLectures list of lectures of the same scheduling subpart 294 **/ 295 public void setSameSubpartLectures(List<Lecture> sameSubpartLectures) { 296 iSameSubpartLectures = sameSubpartLectures; 297 } 298 299 /** Set of lectures of the same class (only section is different) 300 * @return list of lectures of the same scheduling subpart 301 **/ 302 public List<Lecture> sameSubpartLectures() { 303 return iSameSubpartLectures; 304 } 305 306 /** List of students enrolled in this class as well as in the given class 307 * @param lecture a lecture 308 * @return a set of students that are enrolled in both lectures 309 **/ 310 public Set<Student> sameStudents(Lecture lecture) { 311 JenrlConstraint jenrl = jenrlConstraint(lecture); 312 return (jenrl == null ? new HashSet<Student>() : jenrl.getStudents()); 313 } 314 315 /** List of students of this class in conflict with the given assignment 316 * @param assignment current assignment 317 * @param value given placement 318 * @return list of student conflicts 319 **/ 320 public Set<Student> conflictStudents(Assignment<Lecture, Placement> assignment, Placement value) { 321 if (value == null) 322 return new HashSet<Student>(); 323 if (value.equals(assignment.getValue(this))) 324 return conflictStudents(assignment); 325 Set<Student> ret = new HashSet<Student>(); 326 for (JenrlConstraint jenrl : jenrlConstraints()) { 327 if (jenrl.jenrl(assignment, this, value) > 0) 328 ret.addAll(sameStudents(jenrl.another(this))); 329 } 330 return ret; 331 } 332 333 /** 334 * List of students of this class which are in conflict with any other 335 * assignment 336 * @param assignment current assignment 337 * @return list of student conflicts 338 */ 339 public Set<Student> conflictStudents(Assignment<Lecture, Placement> assignment) { 340 Set<Student> ret = new HashSet<Student>(); 341 Placement placement = assignment.getValue(this); 342 if (placement == null) 343 return ret; 344 for (JenrlConstraint jenrl : activeJenrls(assignment)) { 345 ret.addAll(sameStudents(jenrl.another(this))); 346 } 347 for (Student student : students()) { 348 if (student.countConflictPlacements(placement) > 0) 349 ret.add(student); 350 } 351 return ret; 352 } 353 354 /** 355 * Lectures different from this one, where it is student conflict of the 356 * given student between this and the lecture 357 * @param assignment current assignment 358 * @param student a student 359 * @return list of lectures with a student conflict 360 */ 361 public List<Lecture> conflictLectures(Assignment<Lecture, Placement> assignment, Student student) { 362 List<Lecture> ret = new ArrayList<Lecture>(); 363 if (assignment.getValue(this) == null) 364 return ret; 365 for (JenrlConstraint jenrl : activeJenrls(assignment)) { 366 Lecture lect = jenrl.another(this); 367 if (lect.students().contains(student)) 368 ret.add(lect); 369 } 370 return ret; 371 } 372 373 /** True if this lecture is in a student conflict with the given student 374 * @param assignment current assignment 375 * @param student a student 376 * @return number of other lectures with a student conflict 377 **/ 378 public int isInConflict(Assignment<Lecture, Placement> assignment, Student student) { 379 if (assignment.getValue(this) == null) 380 return 0; 381 int ret = 0; 382 for (JenrlConstraint jenrl : activeJenrls(assignment)) { 383 Lecture lect = jenrl.another(this); 384 if (lect.students().contains(student)) 385 ret++; 386 } 387 return ret; 388 } 389 390 private boolean isCacheDomain() { 391 return isCommitted() || (!sSaveMemory && (iNrRooms <= 1 || getMaxRoomCombinations() <= 0 || ToolBox.binomial(iRoomLocations.size(), iNrRooms) <= getMaxRoomCombinations())); 392 } 393 394 /** Domain -- all combinations of room and time locations 395 * @param assignment current assignment 396 * @param allowBreakHard breaking of hard constraints is allowed 397 * @return list of possible placements 398 **/ 399 public List<Placement> computeValues(Assignment<Lecture, Placement> assignment, boolean allowBreakHard) { 400 List<Placement> values = new ArrayList<Placement>(iRoomLocations.size() * iTimeLocations.size()); 401 for (TimeLocation timeLocation : iTimeLocations) { 402 if (!allowBreakHard && Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(timeLocation.getPreference()))) 403 continue; 404 if (timeLocation.getPreference() > 500) 405 continue; 406 boolean notAvailable = false; 407 for (InstructorConstraint ic : getInstructorConstraints()) { 408 if (!ic.isAvailable(this, timeLocation)) { 409 notAvailable = true; 410 break; 411 } 412 } 413 if (notAvailable) 414 continue; 415 if (iNrRooms == 0) { 416 Placement p = new Placement(this, timeLocation, (RoomLocation) null); 417 for (InstructorConstraint ic : getInstructorConstraints()) { 418 if (!ic.isAvailable(this, p)) { 419 notAvailable = true; 420 break; 421 } 422 } 423 if (notAvailable) 424 continue; 425 p.setVariable(this); 426 if (sSaveMemory && !isValid(p)) 427 continue; 428 if (getInitialAssignment() != null && p.equals(getInitialAssignment())) 429 setInitialAssignment(p); 430 // if (getAssignment() != null && getAssignment().equals(p)) iValue = getAssignment(); 431 if (getBestAssignment() != null && getBestAssignment().equals(p)) 432 setBestAssignment(p, getBestAssignmentIteration()); 433 values.add(p); 434 } else if (iNrRooms == 1) { 435 for (RoomLocation roomLocation : iRoomLocations) { 436 if (!allowBreakHard && Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(roomLocation.getPreference()))) continue; 437 if (roomLocation.getPreference() > 500) continue; 438 if (roomLocation.getRoomConstraint() != null && !roomLocation.getRoomConstraint().isAvailable(this, timeLocation, getScheduler())) continue; 439 Placement p = new Placement(this, timeLocation, roomLocation); 440 p.setVariable(this); 441 if (sSaveMemory && !isValid(p)) continue; 442 if (getInitialAssignment() != null && p.equals(getInitialAssignment())) setInitialAssignment(p); 443 if (getBestAssignment() != null && getBestAssignment().equals(p)) setBestAssignment(p, getBestAssignmentIteration()); 444 values.add(p); 445 } 446 } else { 447 if (getMaxRoomCombinations() > 0 && ToolBox.binomial(iRoomLocations.size(), iNrRooms) > getMaxRoomCombinations()) { 448 if (getInitialAssignment() != null && getInitialAssignment().getNrRooms() == getNrRooms()) { 449 Placement p = new Placement(this, timeLocation, new ArrayList<RoomLocation>(getInitialAssignment().getRoomLocations())); 450 p.setVariable(this); 451 if (p.equals(getInitialAssignment())) setInitialAssignment(p); 452 values.add(p); 453 } 454 List<RoomLocation> available = new ArrayList<RoomLocation>(iRoomLocations.size()); 455 List<RoomLocation> other = new ArrayList<RoomLocation>(iRoomLocations.size()); 456 for (RoomLocation room: iRoomLocations) { 457 if (room.getRoomConstraint() != null && !room.getRoomConstraint().isAvailable(this, timeLocation, getScheduler())) continue; 458 if (Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(room.getPreference()))) continue; 459 if (assignment != null && room.getRoomConstraint() != null && !room.getRoomConstraint().getContext(assignment).inConflict(this, timeLocation)) { 460 available.add(room); 461 } else { 462 other.add(room); 463 } 464 } 465 if (available.size() + other.size() < iNrRooms) continue; 466 for (Enumeration<Collection<RoomLocation>> e = ToolBox.sample(available, other, iNrRooms, getMaxRoomCombinations()); e.hasMoreElements(); ) { 467 Placement p = new Placement(this, timeLocation, new ArrayList<RoomLocation>(e.nextElement())); 468 if (getInitialAssignment() != null && p.sameRooms(getInitialAssignment())) continue; 469 p.setVariable(this); 470 values.add(p); 471 } 472 } else { 473 List<RoomLocation> rooms = new ArrayList<RoomLocation>(iRoomLocations.size()); 474 for (RoomLocation room: iRoomLocations) { 475 if (!allowBreakHard && Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(room.getPreference()))) continue; 476 if (room.getRoomConstraint() != null && !room.getRoomConstraint().isAvailable(this, timeLocation, getScheduler())) continue; 477 rooms.add(room); 478 } 479 if (rooms.size() < iNrRooms) continue; 480 for (Enumeration<Collection<RoomLocation>> e = ToolBox.permutations(rooms, iNrRooms); e.hasMoreElements(); ) { 481 Placement p = new Placement(this, timeLocation, new ArrayList<RoomLocation>(e.nextElement())); 482 p.setVariable(this); 483 if (sSaveMemory && !isValid(p)) continue; 484 if (getInitialAssignment() != null && p.equals(getInitialAssignment())) setInitialAssignment(p); 485 if (getBestAssignment() != null && getBestAssignment().equals(p)) setBestAssignment(p, getBestAssignmentIteration()); 486 values.add(p); 487 } 488 } 489 } 490 } 491 return values; 492 } 493 494 public void clearValueCache() { 495 super.setValues(null); 496 } 497 498 /** All values */ 499 @Override 500 public List<Placement> values(Assignment<Lecture, Placement> assignment) { 501 if (super.values(assignment) == null) { 502 if (getInitialAssignment() != null && iTimeLocations.size() == 1 && iRoomLocations.size() == getNrRooms()) { 503 List<Placement> values = new ArrayList<Placement>(1); 504 values.add(getInitialAssignment()); 505 setValues(values); 506 return values; 507 } else if (isCacheDomain()) { 508 List<Placement> values = computeValues(null, sAllowBreakHard); 509 setValues(values); 510 return values; 511 } else { 512 return computeValues(assignment, sAllowBreakHard); 513 } 514 } else { 515 return super.values(assignment); 516 } 517 } 518 519 @Override 520 public boolean equals(Object o) { 521 if (o == null || !(o instanceof Lecture)) 522 return false; 523 return getClassId().equals(((Lecture) o).getClassId()); 524 } 525 526 /** Best time preference of this lecture */ 527 private Double iBestTimePreferenceCache = null; 528 529 public double getBestTimePreference() { 530 if (iBestTimePreferenceCache == null) { 531 double ret = Double.MAX_VALUE; 532 for (TimeLocation time : iTimeLocations) { 533 ret = Math.min(ret, time.getNormalizedPreference()); 534 } 535 iBestTimePreferenceCache = new Double(ret); 536 } 537 return iBestTimePreferenceCache.doubleValue(); 538 } 539 540 /** Best room preference of this lecture 541 * @return best room preference 542 **/ 543 public int getBestRoomPreference() { 544 int ret = Integer.MAX_VALUE; 545 for (RoomLocation room : iRoomLocations) { 546 ret = Math.min(ret, room.getPreference()); 547 } 548 return ret; 549 } 550 551 /** 552 * Number of student conflicts caused by the given assignment of this 553 * lecture 554 * @param assignment current assignment 555 * @param value a placement 556 * @return number of student conflicts if assigned 557 */ 558 public int countStudentConflicts(Assignment<Lecture, Placement> assignment, Placement value) { 559 int studentConflictsSum = 0; 560 for (JenrlConstraint jenrl : jenrlConstraints()) { 561 studentConflictsSum += jenrl.jenrl(assignment, this, value); 562 } 563 return studentConflictsSum; 564 } 565 566 public int countStudentConflictsOfTheSameProblem(Assignment<Lecture, Placement> assignment, Placement value) { 567 int studentConflictsSum = 0; 568 for (JenrlConstraint jenrl : jenrlConstraints()) { 569 if (!jenrl.isOfTheSameProblem()) 570 continue; 571 studentConflictsSum += jenrl.jenrl(assignment, this, value); 572 } 573 return studentConflictsSum; 574 } 575 576 public int countHardStudentConflicts(Assignment<Lecture, Placement> assignment, Placement value) { 577 int studentConflictsSum = 0; 578 if (!isSingleSection()) 579 return 0; 580 for (JenrlConstraint jenrl : jenrlConstraints()) { 581 if (!jenrl.areStudentConflictsHard()) 582 continue; 583 studentConflictsSum += jenrl.jenrl(assignment, this, value); 584 } 585 return studentConflictsSum; 586 } 587 588 public int countCommittedStudentConflictsOfTheSameProblem(Assignment<Lecture, Placement> assignment, Placement value) { 589 int studentConflictsSum = 0; 590 for (JenrlConstraint jenrl : jenrlConstraints()) { 591 if (!jenrl.isOfTheSameProblem()) 592 continue; 593 if (!jenrl.areStudentConflictsCommitted()) 594 continue; 595 studentConflictsSum += jenrl.jenrl(assignment, this, value); 596 } 597 return studentConflictsSum; 598 } 599 600 public int countCommittedStudentConflicts(Assignment<Lecture, Placement> assignment, Placement value) { 601 int studentConflictsSum = 0; 602 for (JenrlConstraint jenrl : jenrlConstraints()) { 603 if (!jenrl.areStudentConflictsCommitted()) 604 continue; 605 studentConflictsSum += jenrl.jenrl(assignment, this, value); 606 } 607 return studentConflictsSum; 608 } 609 610 public int countHardStudentConflictsOfTheSameProblem(Assignment<Lecture, Placement> assignment, Placement value) { 611 int studentConflictsSum = 0; 612 for (JenrlConstraint jenrl : jenrlConstraints()) { 613 if (!jenrl.isOfTheSameProblem()) 614 continue; 615 if (!jenrl.areStudentConflictsHard()) 616 continue; 617 studentConflictsSum += jenrl.jenrl(assignment, this, value); 618 } 619 return studentConflictsSum; 620 } 621 622 public int countDistanceStudentConflicts(Assignment<Lecture, Placement> assignment, Placement value) { 623 int studentConflictsSum = 0; 624 for (JenrlConstraint jenrl : jenrlConstraints()) { 625 if (!jenrl.areStudentConflictsDistance(assignment, value)) 626 continue; 627 studentConflictsSum += jenrl.jenrl(assignment, this, value); 628 } 629 return studentConflictsSum; 630 } 631 632 public int countDistanceStudentConflictsOfTheSameProblem(Assignment<Lecture, Placement> assignment, Placement value) { 633 int studentConflictsSum = 0; 634 for (JenrlConstraint jenrl : jenrlConstraints()) { 635 if (!jenrl.isOfTheSameProblem()) 636 continue; 637 if (!jenrl.areStudentConflictsDistance(assignment, value)) 638 continue; 639 studentConflictsSum += jenrl.jenrl(assignment, this, value); 640 } 641 return studentConflictsSum; 642 } 643 644 private DistanceMetric getDistanceMetric() { 645 return ((TimetableModel)getModel()).getDistanceMetric(); 646 } 647 648 /** 649 * Number of student conflicts caused by the initial assignment of this 650 * lecture 651 * @return number of student conflicts with the initial assignment of this class 652 */ 653 public int countInitialStudentConflicts() { 654 Placement value = getInitialAssignment(); 655 if (value == null) 656 return 0; 657 int studentConflictsSum = 0; 658 for (JenrlConstraint jenrl : jenrlConstraints()) { 659 Lecture another = jenrl.another(this); 660 if (another.getInitialAssignment() != null) 661 if (JenrlConstraint.isInConflict(value, another.getInitialAssignment(), getDistanceMetric())) 662 studentConflictsSum += jenrl.getJenrl(); 663 } 664 return studentConflictsSum; 665 } 666 667 /** 668 * Table of student conflicts caused by the initial assignment of this 669 * lecture in format (another lecture, number) 670 * @return table of student conflicts with the initial assignment of this class 671 */ 672 public Map<Lecture, Long> getInitialStudentConflicts() { 673 Placement value = getInitialAssignment(); 674 if (value == null) 675 return null; 676 Map<Lecture, Long> ret = new HashMap<Lecture, Long>(); 677 for (JenrlConstraint jenrl : jenrlConstraints()) { 678 Lecture another = jenrl.another(this); 679 if (another.getInitialAssignment() != null) 680 if (JenrlConstraint.isInConflict(value, another.getInitialAssignment(), getDistanceMetric())) 681 ret.put(another, jenrl.getJenrl()); 682 } 683 return ret; 684 } 685 686 /** 687 * List of student conflicts caused by the initial assignment of this 688 * lecture 689 * @return a set of students in a conflict with the initial assignment of this class 690 */ 691 public Set<Student> initialStudentConflicts() { 692 Placement value = getInitialAssignment(); 693 if (value == null) 694 return null; 695 HashSet<Student> ret = new HashSet<Student>(); 696 for (JenrlConstraint jenrl : jenrlConstraints()) { 697 Lecture another = jenrl.another(this); 698 if (another.getInitialAssignment() != null) 699 if (JenrlConstraint.isInConflict(value, another.getInitialAssignment(), getDistanceMetric())) 700 ret.addAll(sameStudents(another)); 701 } 702 return ret; 703 } 704 705 @Override 706 public void addContstraint(Constraint<Lecture, Placement> constraint) { 707 super.addContstraint(constraint); 708 709 if (constraint instanceof WeakeningConstraint) 710 iWeakeningConstraints.add(constraint); 711 712 if (constraint instanceof FlexibleConstraint) 713 iFlexibleGroupConstraints.add((FlexibleConstraint) constraint); 714 715 if (constraint instanceof JenrlConstraint) { 716 JenrlConstraint jenrl = (JenrlConstraint) constraint; 717 Lecture another = jenrl.another(this); 718 if (another != null) { 719 iJenrlConstraints.add(jenrl); 720 another.iJenrlConstraints.add(jenrl); 721 iJenrlConstraintsHash.put(another, (JenrlConstraint) constraint); 722 another.iJenrlConstraintsHash.put(this, (JenrlConstraint) constraint); 723 } 724 } else if (constraint instanceof DepartmentSpreadConstraint) 725 iDeptSpreadConstraint = (DepartmentSpreadConstraint) constraint; 726 else if (constraint instanceof SpreadConstraint) 727 iSpreadConstraints.add((SpreadConstraint) constraint); 728 else if (constraint instanceof InstructorConstraint) { 729 InstructorConstraint ic = (InstructorConstraint) constraint; 730 if (ic.getResourceId() != null && ic.getResourceId().longValue() > 0) 731 iInstructorConstraints.add(ic); 732 } else if (constraint instanceof ClassLimitConstraint) 733 iClassLimitConstraint = (ClassLimitConstraint) constraint; 734 else if (constraint instanceof GroupConstraint) { 735 GroupConstraint gc = (GroupConstraint) constraint; 736 if (gc.canShareRoom()) { 737 iCanShareRoomGroupConstraints.add((GroupConstraint) constraint); 738 } else { 739 iGroupConstraints.add((GroupConstraint) constraint); 740 if (Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(gc.getPreference())) 741 || Constants.sPreferenceRequired.equals(Constants 742 .preferenceLevel2preference(gc.getPreference()))) 743 iHardGroupSoftConstraints.add((GroupConstraint) constraint); 744 } 745 } 746 } 747 748 @Override 749 public void removeContstraint(Constraint<Lecture, Placement> constraint) { 750 super.removeContstraint(constraint); 751 752 if (constraint instanceof WeakeningConstraint) 753 iWeakeningConstraints.remove(constraint); 754 755 if (constraint instanceof FlexibleConstraint) 756 iFlexibleGroupConstraints.remove(constraint); 757 758 if (constraint instanceof JenrlConstraint) { 759 JenrlConstraint jenrl = (JenrlConstraint) constraint; 760 Lecture another = jenrl.another(this); 761 if (another != null) { 762 iJenrlConstraints.remove(jenrl); 763 another.iJenrlConstraints.remove(jenrl); 764 iJenrlConstraintsHash.remove(another); 765 another.iJenrlConstraintsHash.remove(this); 766 } 767 } else if (constraint instanceof GroupConstraint) { 768 iCanShareRoomGroupConstraints.remove(constraint); 769 iHardGroupSoftConstraints.remove(constraint); 770 iGroupConstraints.remove(constraint); 771 } else if (constraint instanceof DepartmentSpreadConstraint) 772 iDeptSpreadConstraint = null; 773 else if (constraint instanceof SpreadConstraint) 774 iSpreadConstraints.remove(constraint); 775 else if (constraint instanceof InstructorConstraint) 776 iInstructorConstraints.remove(constraint); 777 else if (constraint instanceof ClassLimitConstraint) 778 iClassLimitConstraint = null; 779 } 780 781 /** All JENRL constraints of this lecture 782 * @param another another class 783 * @return a join enrollment constraint between this and the given class, if there is one 784 **/ 785 public JenrlConstraint jenrlConstraint(Lecture another) { 786 /* 787 * for (Enumeration e=iJenrlConstraints.elements();e.hasMoreElements();) 788 * { JenrlConstraint jenrl = (JenrlConstraint)e.nextElement(); if 789 * (jenrl.another(this).equals(another)) return jenrl; } return null; 790 */ 791 return iJenrlConstraintsHash.get(another); 792 } 793 794 /** All JENRL constraints of this lecture 795 * @return list of all join enrollment constraints in which this lecture is involved 796 **/ 797 public List<JenrlConstraint> jenrlConstraints() { 798 return iJenrlConstraints; 799 } 800 801 public int minClassLimit() { 802 return iMinClassLimit; 803 } 804 805 public int maxClassLimit() { 806 return iMaxClassLimit; 807 } 808 809 public int maxAchievableClassLimit() { 810 iLock.readLock().lock(); 811 try { 812 if (iCacheMaxAchievableClassLimit != null) return iCacheMaxAchievableClassLimit.intValue(); 813 } finally { 814 iLock.readLock().unlock(); 815 } 816 iLock.writeLock().lock(); 817 try { 818 if (iCacheMaxAchievableClassLimit != null) return iCacheMaxAchievableClassLimit.intValue(); 819 820 int maxAchievableClassLimit = Math.min(maxClassLimit(), (int) Math.floor(maxRoomSize() / roomToLimitRatio())); 821 822 if (hasAnyChildren()) { 823 824 for (Long subpartId: getChildrenSubpartIds()) { 825 int maxAchievableChildrenLimit = 0; 826 827 for (Lecture child : getChildren(subpartId)) { 828 maxAchievableChildrenLimit += child.maxAchievableClassLimit(); 829 } 830 831 maxAchievableClassLimit = Math.min(maxAchievableClassLimit, maxAchievableChildrenLimit); 832 } 833 } 834 835 maxAchievableClassLimit = Math.max(minClassLimit(), maxAchievableClassLimit); 836 iCacheMaxAchievableClassLimit = new Integer(maxAchievableClassLimit); 837 return maxAchievableClassLimit; 838 } finally { 839 iLock.writeLock().unlock(); 840 } 841 } 842 843 public int classLimit(Assignment<Lecture, Placement> assignment) { 844 if (minClassLimit() == maxClassLimit()) 845 return minClassLimit(); 846 return classLimit(assignment, null, null); 847 } 848 849 public int classLimit(Assignment<Lecture, Placement> assignment, Placement value, Set<Placement> conflicts) { 850 Placement a = (assignment == null ? null : assignment.getValue(this)); 851 if (value != null && value.variable().equals(this)) 852 a = value; 853 if (conflicts != null && a != null && conflicts.contains(a)) 854 a = null; 855 int classLimit = (a == null ? maxAchievableClassLimit() : Math.min(maxClassLimit(), (int) Math.floor(a.getRoomSize() / roomToLimitRatio()))); 856 857 if (!hasAnyChildren()) 858 return classLimit; 859 860 for (Long subpartId: getChildrenSubpartIds()) { 861 int childrenClassLimit = 0; 862 863 for (Lecture child : getChildren(subpartId)) { 864 childrenClassLimit += child.classLimit(assignment, value, conflicts); 865 } 866 867 classLimit = Math.min(classLimit, childrenClassLimit); 868 } 869 870 return Math.max(minClassLimit(), classLimit); 871 } 872 873 public double roomToLimitRatio() { 874 return iRoomToLimitRatio; 875 } 876 877 public int minRoomUse() { 878 return Math.round(iMinClassLimit * iRoomToLimitRatio); 879 } 880 881 public int maxRoomUse() { 882 return Math.round(iMaxClassLimit * iRoomToLimitRatio); 883 } 884 885 @Override 886 public String toString() { 887 return getName(); 888 } 889 890 /** Controlling Course Offering Department 891 * @return department unique id 892 **/ 893 public Long getDepartment() { 894 return iDept; 895 } 896 897 /** Controlling Course Offering Department 898 * @param dept department unique id 899 **/ 900 public void setDepartment(Long dept) { 901 iDept = dept; 902 } 903 904 /** Scheduler (Managing Department) 905 * @return solver group unique id 906 **/ 907 public Long getScheduler() { 908 return iScheduler; 909 } 910 911 /** Scheduler (Managing Department) 912 * @param scheduler solver group unique id 913 **/ 914 public void setScheduler(Long scheduler) { 915 iScheduler = scheduler; 916 } 917 918 /** Departmental spreading constraint 919 * @return department spread constraint of this class, if any 920 **/ 921 public DepartmentSpreadConstraint getDeptSpreadConstraint() { 922 return iDeptSpreadConstraint; 923 } 924 925 /** Instructor constraint 926 * @return instructors of this class 927 **/ 928 public List<InstructorConstraint> getInstructorConstraints() { 929 return iInstructorConstraints; 930 } 931 932 public ClassLimitConstraint getClassLimitConstraint() { 933 return iClassLimitConstraint; 934 } 935 936 public Set<SpreadConstraint> getSpreadConstraints() { 937 return iSpreadConstraints; 938 } 939 940 public Set<FlexibleConstraint> getFlexibleGroupConstraints() { 941 return iFlexibleGroupConstraints; 942 } 943 944 public Set<Constraint<Lecture, Placement>> getWeakeningConstraints() { 945 return iWeakeningConstraints; 946 } 947 948 /** All room locations 949 * @return possible rooms of this class 950 **/ 951 public List<RoomLocation> roomLocations() { 952 return iRoomLocations; 953 } 954 955 /** All time locations 956 * @return possible times of this class 957 **/ 958 public List<TimeLocation> timeLocations() { 959 return iTimeLocations; 960 } 961 962 public int nrTimeLocations() { 963 int ret = 0; 964 for (TimeLocation time : iTimeLocations) { 965 if (!Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(time.getPreference()))) 966 ret++; 967 } 968 return ret; 969 } 970 971 public int nrRoomLocations() { 972 int ret = 0; 973 for (RoomLocation room : iRoomLocations) { 974 if (!Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(room.getPreference()))) 975 ret++; 976 } 977 return ret; 978 } 979 980 public long nrValues() { 981 int nrTimes = 0; 982 for (TimeLocation time: timeLocations()) 983 if (!Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(time.getPreference()))) 984 nrTimes ++; 985 int nrRooms = 0; 986 for (RoomLocation room : iRoomLocations) 987 if (!Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(room.getPreference()))) 988 nrRooms ++; 989 long estNrValues = nrTimes; 990 if (getNrRooms() > 1 && getMaxRoomCombinations() > 0) 991 estNrValues *= Math.min(getMaxRoomCombinations(), ToolBox.binomial(nrRooms, getNrRooms())); 992 else 993 estNrValues *= ToolBox.binomial(nrRooms, getNrRooms()); 994 return estNrValues; 995 } 996 997 public int nrValues(TimeLocation time) { 998 int ret = 0; 999 for (RoomLocation room : iRoomLocations) { 1000 if (!Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(room.getPreference())) 1001 && (room.getRoomConstraint() == null || room.getRoomConstraint().isAvailable(this, time, getScheduler()))) 1002 ret++; 1003 } 1004 return ret; 1005 } 1006 1007 public int nrValues(RoomLocation room) { 1008 int ret = 0; 1009 for (TimeLocation time : iTimeLocations) { 1010 if (!Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(time.getPreference())) 1011 && (room.getRoomConstraint() == null || room.getRoomConstraint().isAvailable(this, time,getScheduler()))) 1012 ret++; 1013 } 1014 return ret; 1015 } 1016 1017 public int nrValues(List<RoomLocation> rooms) { 1018 int ret = 0; 1019 for (TimeLocation time : iTimeLocations) { 1020 boolean available = true; 1021 for (RoomLocation room : rooms) { 1022 if (Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(time.getPreference())) 1023 || (room.getRoomConstraint() != null && !room.getRoomConstraint().isAvailable(this, time, 1024 getScheduler()))) 1025 available = false; 1026 } 1027 if (available) 1028 ret++; 1029 } 1030 return ret; 1031 } 1032 1033 public boolean allowBreakHard() { 1034 return sAllowBreakHard; 1035 } 1036 1037 public int getNrRooms() { 1038 return iNrRooms; 1039 } 1040 1041 public Lecture getParent() { 1042 return iParent; 1043 } 1044 1045 public void setParent(Lecture parent) { 1046 iParent = parent; 1047 iParent.addChild(this); 1048 } 1049 1050 public boolean hasParent() { 1051 return (iParent != null); 1052 } 1053 1054 public boolean hasChildren(Long subpartId) { 1055 return (iChildren != null && iChildren.get(subpartId) != null && !iChildren.get(subpartId).isEmpty()); 1056 } 1057 1058 public boolean hasAnyChildren() { 1059 return (iChildren != null && !iChildren.isEmpty()); 1060 } 1061 1062 public List<Lecture> getChildren(Long subpartId) { 1063 return iChildren.get(subpartId); 1064 } 1065 1066 public Set<Long> getChildrenSubpartIds() { 1067 return (iChildren == null ? null : iChildren.keySet()); 1068 } 1069 1070 private void addChild(Lecture child) { 1071 if (iChildren == null) 1072 iChildren = new HashMap<Long, List<Lecture>>(); 1073 List<Lecture> childrenThisSubpart = iChildren.get(child.getSchedulingSubpartId()); 1074 if (childrenThisSubpart == null) { 1075 childrenThisSubpart = new ArrayList<Lecture>(); 1076 iChildren.put(child.getSchedulingSubpartId(), childrenThisSubpart); 1077 } 1078 childrenThisSubpart.add(child); 1079 } 1080 1081 public boolean isSingleSection() { 1082 return (iSameSubpartLectures == null || iSameSubpartLectures.size() <= 1); 1083 /* 1084 if (iParent == null) 1085 return (iSameSubpartLectures == null || iSameSubpartLectures.size() <= 1); 1086 return (iParent.getChildren(getSchedulingSubpartId()).size() <= 1); 1087 */ 1088 } 1089 1090 public java.util.List<Lecture> sameStudentsLectures() { 1091 return (hasParent() ? getParent().getChildren(getSchedulingSubpartId()) : sameSubpartLectures()); 1092 } 1093 1094 public Lecture getChild(Student student, Long subpartId) { 1095 if (!hasAnyChildren()) 1096 return null; 1097 List<Lecture> children = getChildren(subpartId); 1098 if (children == null) 1099 return null; 1100 for (Lecture child : children) { 1101 if (child.students().contains(student)) 1102 return child; 1103 } 1104 return null; 1105 } 1106 1107 public int getCommitedConflicts(Placement placement) { 1108 iLock.readLock().lock(); 1109 try { 1110 Integer ret = iCommitedConflicts.get(placement); 1111 if (ret != null) return ret; 1112 } finally { 1113 iLock.readLock().unlock(); 1114 } 1115 iLock.writeLock().lock(); 1116 try { 1117 int ret = placement.getCommitedConflicts(); 1118 iCommitedConflicts.put(placement, ret); 1119 return ret; 1120 } finally { 1121 iLock.writeLock().unlock(); 1122 } 1123 } 1124 1125 public Set<GroupConstraint> hardGroupSoftConstraints() { 1126 return iHardGroupSoftConstraints; 1127 } 1128 1129 public Set<GroupConstraint> groupConstraints() { 1130 return iGroupConstraints; 1131 } 1132 1133 public int minRoomSize() { 1134 iLock.readLock().lock(); 1135 try { 1136 if (iCacheMinRoomSize != null) return iCacheMinRoomSize.intValue(); 1137 } finally { 1138 iLock.readLock().unlock(); 1139 } 1140 iLock.writeLock().lock(); 1141 try { 1142 if (iCacheMinRoomSize != null) return iCacheMinRoomSize.intValue(); 1143 if (getNrRooms() <= 1) { 1144 int min = Integer.MAX_VALUE; 1145 for (RoomLocation r : roomLocations()) { 1146 if (r.getPreference() <= Constants.sPreferenceLevelProhibited / 2) 1147 min = Math.min(min, r.getRoomSize()); 1148 } 1149 iCacheMinRoomSize = new Integer(min); 1150 return min; 1151 } else { 1152 List<RoomLocation> rooms = new ArrayList<RoomLocation>(); 1153 for (RoomLocation r: roomLocations()) 1154 if (r.getPreference() <= Constants.sPreferenceLevelProhibited / 2) 1155 rooms.add(r); 1156 Collections.sort(rooms, new Comparator<RoomLocation>() { 1157 @Override 1158 public int compare(RoomLocation r1, RoomLocation r2) { 1159 if (r1.getRoomSize() < r2.getRoomSize()) return -1; 1160 if (r1.getRoomSize() > r2.getRoomSize()) return 1; 1161 return r1.compareTo(r2); 1162 } 1163 }); 1164 int min = rooms.isEmpty() ? 0 : rooms.get(Math.min(getNrRooms(), rooms.size()) - 1).getRoomSize(); 1165 iCacheMinRoomSize = new Integer(min); 1166 return min; 1167 } 1168 } finally { 1169 iLock.writeLock().unlock(); 1170 } 1171 } 1172 1173 public int maxRoomSize() { 1174 iLock.readLock().lock(); 1175 try { 1176 if (iCacheMaxRoomSize != null) return iCacheMaxRoomSize.intValue(); 1177 } finally { 1178 iLock.readLock().unlock(); 1179 } 1180 iLock.writeLock().lock(); 1181 try { 1182 if (iCacheMaxRoomSize != null) return iCacheMaxRoomSize.intValue(); 1183 if (getNrRooms() <= 1) { 1184 int max = Integer.MIN_VALUE; 1185 for (RoomLocation r : roomLocations()) { 1186 if (r.getPreference() <= Constants.sPreferenceLevelProhibited / 2) 1187 max = Math.max(max, r.getRoomSize()); 1188 } 1189 iCacheMaxRoomSize = new Integer(max); 1190 return max; 1191 } else { 1192 List<RoomLocation> rooms = new ArrayList<RoomLocation>(); 1193 for (RoomLocation r: roomLocations()) 1194 if (r.getPreference() <= Constants.sPreferenceLevelProhibited / 2) rooms.add(r); 1195 Collections.sort(rooms, new Comparator<RoomLocation>() { 1196 @Override 1197 public int compare(RoomLocation r1, RoomLocation r2) { 1198 if (r1.getRoomSize() > r2.getRoomSize()) return -1; 1199 if (r1.getRoomSize() < r2.getRoomSize()) return 1; 1200 return r1.compareTo(r2); 1201 } 1202 }); 1203 int max = rooms.isEmpty() ? 0 : rooms.get(Math.min(getNrRooms(), rooms.size()) - 1).getRoomSize(); 1204 iCacheMaxRoomSize = new Integer(max); 1205 return max; 1206 } 1207 } finally { 1208 iLock.writeLock().unlock(); 1209 } 1210 } 1211 1212 public boolean canShareRoom() { 1213 return (!iCanShareRoomGroupConstraints.isEmpty()); 1214 } 1215 1216 public boolean canShareRoom(Lecture other) { 1217 if (other.equals(this)) 1218 return true; 1219 for (GroupConstraint gc : iCanShareRoomGroupConstraints) { 1220 if (gc.variables().contains(other)) 1221 return true; 1222 } 1223 return false; 1224 } 1225 1226 public Set<GroupConstraint> canShareRoomConstraints() { 1227 return iCanShareRoomGroupConstraints; 1228 } 1229 1230 public boolean isSingleton() { 1231 return getNrRooms() == roomLocations().size() && timeLocations().size() == 1; 1232 } 1233 1234 public boolean isValid(Placement placement) { 1235 TimetableModel model = (TimetableModel) getModel(); 1236 if (model == null) 1237 return true; 1238 if (model.hasConstantVariables()) { 1239 for (Placement confPlacement : model.conflictValuesSkipWeakeningConstraints(model.getEmptyAssignment(), placement)) { 1240 Lecture lecture = confPlacement.variable(); 1241 if (lecture.isCommitted()) 1242 return false; 1243 if (confPlacement.equals(placement)) 1244 return false; 1245 } 1246 } else { 1247 Set<Placement> conflicts = new HashSet<Placement>(); 1248 for (Constraint<Lecture, Placement> constraint : hardConstraints()) { 1249 if (constraint instanceof WeakeningConstraint) continue; 1250 constraint.computeConflicts(model.getEmptyAssignment(), placement, conflicts); 1251 } 1252 for (GlobalConstraint<Lecture, Placement> constraint : model.globalConstraints()) { 1253 if (constraint instanceof WeakeningConstraint) continue; 1254 constraint.computeConflicts(model.getEmptyAssignment(), placement, conflicts); 1255 } 1256 if (conflicts.contains(placement)) 1257 return false; 1258 } 1259 return true; 1260 } 1261 1262 public String getNotValidReason(Assignment<Lecture, Placement> assignment, Placement placement, boolean useAmPm) { 1263 TimetableModel model = (TimetableModel) getModel(); 1264 if (model == null) 1265 return "no model for class " + getName(); 1266 Map<Constraint<Lecture, Placement>, Set<Placement>> conflictConstraints = model.conflictConstraints(assignment, placement); 1267 for (Map.Entry<Constraint<Lecture, Placement>, Set<Placement>> entry : conflictConstraints.entrySet()) { 1268 Constraint<Lecture, Placement> constraint = entry.getKey(); 1269 Set<Placement> conflicts = entry.getValue(); 1270 String cname = constraint.getName(); 1271 if (constraint instanceof RoomConstraint) { 1272 cname = "Room " + constraint.getName(); 1273 } else if (constraint instanceof InstructorConstraint) { 1274 cname = "Instructor " + constraint.getName(); 1275 } else if (constraint instanceof GroupConstraint) { 1276 cname = "Distribution " + constraint.getName(); 1277 } else if (constraint instanceof DepartmentSpreadConstraint) { 1278 cname = "Balancing of department " + constraint.getName(); 1279 } else if (constraint instanceof SpreadConstraint) { 1280 cname = "Same subpart spread " + constraint.getName(); 1281 } else if (constraint instanceof ClassLimitConstraint) { 1282 cname = "Class limit " + constraint.getName(); 1283 } 1284 for (Placement confPlacement : conflicts) { 1285 Lecture lecture = confPlacement.variable(); 1286 if (lecture.isCommitted()) { 1287 return placement.getLongName(useAmPm) + " conflicts with " + lecture.getName() + " " 1288 + confPlacement.getLongName(useAmPm) + " due to constraint " + cname; 1289 } 1290 if (confPlacement.equals(placement)) { 1291 return placement.getLongName(useAmPm) + " is not valid due to constraint " + cname; 1292 } 1293 } 1294 } 1295 return null; 1296 } 1297 1298 @Deprecated 1299 public String getNotValidReason(Assignment<Lecture, Placement> assignment, Placement placement) { 1300 return getNotValidReason(assignment, placement, true); 1301 } 1302 1303 public void purgeInvalidValues(boolean interactiveMode) { 1304 if (isCommitted() || sSaveMemory) return; 1305 TimetableModel model = (TimetableModel) getModel(); 1306 if (model == null) 1307 return; 1308 List<Placement> newValues = new ArrayList<Placement>(values(null).size()); 1309 for (Placement placement : values(null)) { 1310 if (placement.isValid()) 1311 newValues.add(placement); 1312 } 1313 if (!interactiveMode && newValues.size() != values(null).size()) { 1314 for (Iterator<TimeLocation> i = timeLocations().iterator(); i.hasNext();) { 1315 TimeLocation timeLocation = i.next(); 1316 boolean hasPlacement = false; 1317 for (Placement placement : newValues) { 1318 if (timeLocation.equals(placement.getTimeLocation())) { 1319 hasPlacement = true; 1320 break; 1321 } 1322 } 1323 if (!hasPlacement) 1324 i.remove(); 1325 } 1326 for (Iterator<RoomLocation> i = roomLocations().iterator(); i.hasNext();) { 1327 RoomLocation roomLocation = i.next(); 1328 boolean hasPlacement = false; 1329 for (Placement placement : newValues) { 1330 if (placement.isMultiRoom()) { 1331 if (placement.getRoomLocations().contains(roomLocation)) { 1332 hasPlacement = true; 1333 break; 1334 } 1335 } else { 1336 if (roomLocation.equals(placement.getRoomLocation())) { 1337 hasPlacement = true; 1338 break; 1339 } 1340 } 1341 } 1342 if (!hasPlacement) 1343 i.remove(); 1344 } 1345 } 1346 setValues(newValues); 1347 } 1348 1349 public void setCommitted(boolean committed) { 1350 iCommitted = committed; 1351 } 1352 1353 public boolean isCommitted() { 1354 return iCommitted; 1355 } 1356 1357 @Override 1358 public boolean isConstant() { 1359 return iCommitted; 1360 } 1361 1362 @Override 1363 public Placement getConstantValue() { 1364 return (isCommitted() ? getInitialAssignment() : null); 1365 } 1366 1367 public void setConstantValue(Placement value) { 1368 setInitialAssignment(value); 1369 } 1370 1371 public int getSpreadPenalty(Assignment<Lecture, Placement> assignment) { 1372 int spread = 0; 1373 for (SpreadConstraint sc : getSpreadConstraints()) { 1374 spread += sc.getPenalty(assignment); 1375 } 1376 return spread; 1377 } 1378 1379 @Override 1380 public int hashCode() { 1381 return getClassId().hashCode(); 1382 } 1383 1384 public Configuration getConfiguration() { 1385 Lecture lecture = this; 1386 while (lecture.getParent() != null) 1387 lecture = lecture.getParent(); 1388 return lecture.iParentConfiguration; 1389 } 1390 1391 public void setConfiguration(Configuration configuration) { 1392 Lecture lecture = this; 1393 while (lecture.getParent() != null) 1394 lecture = lecture.getParent(); 1395 lecture.iParentConfiguration = configuration; 1396 configuration.addTopLecture(lecture); 1397 } 1398 1399 private int[] iMinMaxRoomPreference = null; 1400 1401 public int[] getMinMaxRoomPreference() { 1402 iLock.readLock().lock(); 1403 try { 1404 if (iMinMaxRoomPreference != null) return iMinMaxRoomPreference; 1405 } finally { 1406 iLock.readLock().unlock(); 1407 } 1408 iLock.writeLock().lock(); 1409 try { 1410 if (iMinMaxRoomPreference != null) return iMinMaxRoomPreference; 1411 1412 if (getNrRooms() <= 0 || roomLocations().isEmpty()) { 1413 iMinMaxRoomPreference = new int[] { 0, 0 }; 1414 } else { 1415 Integer minRoomPref = null, maxRoomPref = null; 1416 for (RoomLocation r : roomLocations()) { 1417 int pref = r.getPreference(); 1418 if (pref >= Constants.sPreferenceLevelRequired / 2 && pref <= Constants.sPreferenceLevelProhibited / 2) { 1419 minRoomPref = (minRoomPref == null ? pref : Math.min(minRoomPref, pref)); 1420 maxRoomPref = (maxRoomPref == null ? pref : Math.max(maxRoomPref, pref)); 1421 } 1422 } 1423 iMinMaxRoomPreference = new int[] { minRoomPref == null ? 0 : minRoomPref, maxRoomPref == null ? 0 : maxRoomPref }; 1424 } 1425 1426 return iMinMaxRoomPreference; 1427 } finally { 1428 iLock.writeLock().unlock(); 1429 } 1430 } 1431 1432 private double[] iMinMaxTimePreference = null; 1433 1434 public double[] getMinMaxTimePreference() { 1435 iLock.readLock().lock(); 1436 try { 1437 if (iMinMaxTimePreference != null) return iMinMaxTimePreference; 1438 } finally { 1439 iLock.readLock().unlock(); 1440 } 1441 iLock.writeLock().lock(); 1442 try { 1443 if (iMinMaxTimePreference != null) return iMinMaxTimePreference; 1444 1445 Double minTimePref = null, maxTimePref = null; 1446 for (TimeLocation t : timeLocations()) { 1447 double npref = t.getNormalizedPreference(); 1448 int pref = t.getPreference(); 1449 if (pref >= Constants.sPreferenceLevelRequired / 2 && pref <= Constants.sPreferenceLevelProhibited / 2) { 1450 minTimePref = (minTimePref == null ? npref : Math.min(minTimePref, npref)); 1451 maxTimePref = (maxTimePref == null ? npref : Math.max(maxTimePref, npref)); 1452 } 1453 } 1454 iMinMaxTimePreference = new double[] { minTimePref == null ? 0.0 : minTimePref, maxTimePref == null ? 0.0 : maxTimePref }; 1455 1456 return iMinMaxTimePreference; 1457 } finally { 1458 iLock.writeLock().unlock(); 1459 } 1460 } 1461 1462 public void setOrd(int ord) { 1463 iOrd = ord; 1464 } 1465 1466 public int getOrd() { 1467 return iOrd; 1468 } 1469 1470 @Override 1471 public int compareTo(Lecture o) { 1472 int cmp = Double.compare(getOrd(), o.getOrd()); 1473 if (cmp != 0) 1474 return cmp; 1475 return super.compareTo(o); 1476 } 1477 1478 public String getNote() { 1479 return iNote; 1480 } 1481 1482 public void setNote(String note) { 1483 iNote = note; 1484 } 1485 1486 public boolean areStudentConflictsHard(Lecture other) { 1487 return StudentConflict.hard(this, other); 1488 } 1489 1490 public void clearIgnoreStudentConflictsWithCache() { 1491 iIgnoreStudentConflictsWith.set(null); 1492 } 1493 1494 /** 1495 * Returns true if there is {@link IgnoreStudentConflictsConstraint} between the two lectures. 1496 * @param other another class 1497 * @return true if student conflicts between this and the given calls are to be ignored 1498 */ 1499 public boolean isToIgnoreStudentConflictsWith(Lecture other) { 1500 Set<Long> cache = iIgnoreStudentConflictsWith.get(); 1501 if (cache != null) 1502 return cache.contains(other.getClassId()); 1503 cache = new HashSet<Long>(); 1504 for (Constraint<Lecture, Placement> constraint: constraints()) { 1505 if (constraint instanceof IgnoreStudentConflictsConstraint) 1506 for (Lecture x: constraint.variables()) { 1507 if (!x.equals(this)) cache.add(x.getClassId()); 1508 } 1509 } 1510 iIgnoreStudentConflictsWith.set(cache); 1511 return cache.contains(other); 1512 } 1513 1514 /** 1515 * Get class weight. This weight is used with the criteria. E.g., class that is not meeting all the 1516 * semester can have a lower weight. Defaults to 1.0 1517 * @return class weight 1518 */ 1519 public double getWeight() { return iWeight; } 1520 /** 1521 * Set class weight. This weight is used with the criteria. E.g., class that is not meeting all the 1522 * semester can have a lower weight. 1523 * @param weight class weight 1524 */ 1525 public void setWeight(double weight) { iWeight = weight; } 1526 1527 @Override 1528 public LectureContext createAssignmentContext(Assignment<Lecture, Placement> assignment) { 1529 return new LectureContext(); 1530 } 1531 1532 public class LectureContext implements AssignmentContext { 1533 private Set<JenrlConstraint> iActiveJenrls = new HashSet<JenrlConstraint>(); 1534 1535 /** 1536 * Add active jenrl constraint (active mean that there is at least one 1537 * student between its classes) 1538 * @param constr active join enrollment constraint 1539 */ 1540 public void addActiveJenrl(JenrlConstraint constr) { 1541 iActiveJenrls.add(constr); 1542 } 1543 1544 /** 1545 * Active jenrl constraints (active mean that there is at least one student 1546 * between its classes) 1547 * @return set of active join enrollment constraints 1548 */ 1549 public Set<JenrlConstraint> activeJenrls() { 1550 return iActiveJenrls; 1551 } 1552 1553 /** 1554 * Remove active jenrl constraint (active mean that there is at least one 1555 * student between its classes) 1556 * @param constr active join enrollment constraint 1557 */ 1558 public void removeActiveJenrl(JenrlConstraint constr) { 1559 iActiveJenrls.remove(constr); 1560 } 1561 } 1562 1563 public int getMaxRoomCombinations() { 1564 return iMaxRoomCombinations; 1565 } 1566 1567 public void setMaxRoomCombinations(int maxRoomCombinations) { 1568 iMaxRoomCombinations = maxRoomCombinations; 1569 } 1570}