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 private int getStudentWorkDayLimit() { 649 return ((TimetableModel)getModel()).getStudentWorkDayLimit(); 650 } 651 652 /** 653 * Number of student conflicts caused by the initial assignment of this 654 * lecture 655 * @return number of student conflicts with the initial assignment of this class 656 */ 657 public int countInitialStudentConflicts() { 658 Placement value = getInitialAssignment(); 659 if (value == null) 660 return 0; 661 int studentConflictsSum = 0; 662 for (JenrlConstraint jenrl : jenrlConstraints()) { 663 Lecture another = jenrl.another(this); 664 if (another.getInitialAssignment() != null) 665 if (JenrlConstraint.isInConflict(value, another.getInitialAssignment(), getDistanceMetric(), getStudentWorkDayLimit())) 666 studentConflictsSum += jenrl.getJenrl(); 667 } 668 return studentConflictsSum; 669 } 670 671 /** 672 * Table of student conflicts caused by the initial assignment of this 673 * lecture in format (another lecture, number) 674 * @return table of student conflicts with the initial assignment of this class 675 */ 676 public Map<Lecture, Long> getInitialStudentConflicts() { 677 Placement value = getInitialAssignment(); 678 if (value == null) 679 return null; 680 Map<Lecture, Long> ret = new HashMap<Lecture, Long>(); 681 for (JenrlConstraint jenrl : jenrlConstraints()) { 682 Lecture another = jenrl.another(this); 683 if (another.getInitialAssignment() != null) 684 if (JenrlConstraint.isInConflict(value, another.getInitialAssignment(), getDistanceMetric(), getStudentWorkDayLimit())) 685 ret.put(another, jenrl.getJenrl()); 686 } 687 return ret; 688 } 689 690 /** 691 * List of student conflicts caused by the initial assignment of this 692 * lecture 693 * @return a set of students in a conflict with the initial assignment of this class 694 */ 695 public Set<Student> initialStudentConflicts() { 696 Placement value = getInitialAssignment(); 697 if (value == null) 698 return null; 699 HashSet<Student> ret = new HashSet<Student>(); 700 for (JenrlConstraint jenrl : jenrlConstraints()) { 701 Lecture another = jenrl.another(this); 702 if (another.getInitialAssignment() != null) 703 if (JenrlConstraint.isInConflict(value, another.getInitialAssignment(), getDistanceMetric(), getStudentWorkDayLimit())) 704 ret.addAll(sameStudents(another)); 705 } 706 return ret; 707 } 708 709 @Override 710 public void addContstraint(Constraint<Lecture, Placement> constraint) { 711 super.addContstraint(constraint); 712 713 if (constraint instanceof WeakeningConstraint) 714 iWeakeningConstraints.add(constraint); 715 716 if (constraint instanceof FlexibleConstraint) 717 iFlexibleGroupConstraints.add((FlexibleConstraint) constraint); 718 719 if (constraint instanceof JenrlConstraint) { 720 JenrlConstraint jenrl = (JenrlConstraint) constraint; 721 Lecture another = jenrl.another(this); 722 if (another != null) { 723 iJenrlConstraints.add(jenrl); 724 another.iJenrlConstraints.add(jenrl); 725 iJenrlConstraintsHash.put(another, (JenrlConstraint) constraint); 726 another.iJenrlConstraintsHash.put(this, (JenrlConstraint) constraint); 727 } 728 } else if (constraint instanceof DepartmentSpreadConstraint) 729 iDeptSpreadConstraint = (DepartmentSpreadConstraint) constraint; 730 else if (constraint instanceof SpreadConstraint) 731 iSpreadConstraints.add((SpreadConstraint) constraint); 732 else if (constraint instanceof InstructorConstraint) { 733 InstructorConstraint ic = (InstructorConstraint) constraint; 734 if (ic.getResourceId() != null && ic.getResourceId().longValue() > 0) 735 iInstructorConstraints.add(ic); 736 } else if (constraint instanceof ClassLimitConstraint) 737 iClassLimitConstraint = (ClassLimitConstraint) constraint; 738 else if (constraint instanceof GroupConstraint) { 739 GroupConstraint gc = (GroupConstraint) constraint; 740 if (gc.canShareRoom()) { 741 iCanShareRoomGroupConstraints.add((GroupConstraint) constraint); 742 } else { 743 iGroupConstraints.add((GroupConstraint) constraint); 744 if (Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(gc.getPreference())) 745 || Constants.sPreferenceRequired.equals(Constants 746 .preferenceLevel2preference(gc.getPreference()))) 747 iHardGroupSoftConstraints.add((GroupConstraint) constraint); 748 } 749 } 750 } 751 752 @Override 753 public void removeContstraint(Constraint<Lecture, Placement> constraint) { 754 super.removeContstraint(constraint); 755 756 if (constraint instanceof WeakeningConstraint) 757 iWeakeningConstraints.remove(constraint); 758 759 if (constraint instanceof FlexibleConstraint) 760 iFlexibleGroupConstraints.remove(constraint); 761 762 if (constraint instanceof JenrlConstraint) { 763 JenrlConstraint jenrl = (JenrlConstraint) constraint; 764 Lecture another = jenrl.another(this); 765 if (another != null) { 766 iJenrlConstraints.remove(jenrl); 767 another.iJenrlConstraints.remove(jenrl); 768 iJenrlConstraintsHash.remove(another); 769 another.iJenrlConstraintsHash.remove(this); 770 } 771 } else if (constraint instanceof GroupConstraint) { 772 iCanShareRoomGroupConstraints.remove(constraint); 773 iHardGroupSoftConstraints.remove(constraint); 774 iGroupConstraints.remove(constraint); 775 } else if (constraint instanceof DepartmentSpreadConstraint) 776 iDeptSpreadConstraint = null; 777 else if (constraint instanceof SpreadConstraint) 778 iSpreadConstraints.remove(constraint); 779 else if (constraint instanceof InstructorConstraint) 780 iInstructorConstraints.remove(constraint); 781 else if (constraint instanceof ClassLimitConstraint) 782 iClassLimitConstraint = null; 783 } 784 785 /** All JENRL constraints of this lecture 786 * @param another another class 787 * @return a join enrollment constraint between this and the given class, if there is one 788 **/ 789 public JenrlConstraint jenrlConstraint(Lecture another) { 790 /* 791 * for (Enumeration e=iJenrlConstraints.elements();e.hasMoreElements();) 792 * { JenrlConstraint jenrl = (JenrlConstraint)e.nextElement(); if 793 * (jenrl.another(this).equals(another)) return jenrl; } return null; 794 */ 795 return iJenrlConstraintsHash.get(another); 796 } 797 798 /** All JENRL constraints of this lecture 799 * @return list of all join enrollment constraints in which this lecture is involved 800 **/ 801 public List<JenrlConstraint> jenrlConstraints() { 802 return iJenrlConstraints; 803 } 804 805 public int minClassLimit() { 806 return iMinClassLimit; 807 } 808 809 public int maxClassLimit() { 810 return iMaxClassLimit; 811 } 812 813 public int maxAchievableClassLimit() { 814 iLock.readLock().lock(); 815 try { 816 if (iCacheMaxAchievableClassLimit != null) return iCacheMaxAchievableClassLimit.intValue(); 817 } finally { 818 iLock.readLock().unlock(); 819 } 820 iLock.writeLock().lock(); 821 try { 822 if (iCacheMaxAchievableClassLimit != null) return iCacheMaxAchievableClassLimit.intValue(); 823 824 int maxAchievableClassLimit = Math.min(maxClassLimit(), (int) Math.floor(maxRoomSize() / roomToLimitRatio())); 825 826 if (hasAnyChildren()) { 827 828 for (Long subpartId: getChildrenSubpartIds()) { 829 int maxAchievableChildrenLimit = 0; 830 831 for (Lecture child : getChildren(subpartId)) { 832 maxAchievableChildrenLimit += child.maxAchievableClassLimit(); 833 } 834 835 maxAchievableClassLimit = Math.min(maxAchievableClassLimit, maxAchievableChildrenLimit); 836 } 837 } 838 839 maxAchievableClassLimit = Math.max(minClassLimit(), maxAchievableClassLimit); 840 iCacheMaxAchievableClassLimit = new Integer(maxAchievableClassLimit); 841 return maxAchievableClassLimit; 842 } finally { 843 iLock.writeLock().unlock(); 844 } 845 } 846 847 public int classLimit(Assignment<Lecture, Placement> assignment) { 848 if (minClassLimit() == maxClassLimit()) 849 return minClassLimit(); 850 return classLimit(assignment, null, null); 851 } 852 853 public int classLimit(Assignment<Lecture, Placement> assignment, Placement value, Set<Placement> conflicts) { 854 Placement a = (assignment == null ? null : assignment.getValue(this)); 855 if (value != null && value.variable().equals(this)) 856 a = value; 857 if (conflicts != null && a != null && conflicts.contains(a)) 858 a = null; 859 int classLimit = (a == null ? maxAchievableClassLimit() : Math.min(maxClassLimit(), (int) Math.floor(a.getRoomSize() / roomToLimitRatio()))); 860 861 if (!hasAnyChildren()) 862 return classLimit; 863 864 for (Long subpartId: getChildrenSubpartIds()) { 865 int childrenClassLimit = 0; 866 867 for (Lecture child : getChildren(subpartId)) { 868 childrenClassLimit += child.classLimit(assignment, value, conflicts); 869 } 870 871 classLimit = Math.min(classLimit, childrenClassLimit); 872 } 873 874 return Math.max(minClassLimit(), classLimit); 875 } 876 877 public double roomToLimitRatio() { 878 return iRoomToLimitRatio; 879 } 880 881 public int minRoomUse() { 882 return iNrRooms == 0 ? 0 : Math.round(iMinClassLimit * iRoomToLimitRatio); 883 } 884 885 public int maxRoomUse() { 886 return iNrRooms == 0 ? 0 : Math.round(iMaxClassLimit * iRoomToLimitRatio); 887 } 888 889 @Override 890 public String toString() { 891 return getName(); 892 } 893 894 /** Controlling Course Offering Department 895 * @return department unique id 896 **/ 897 public Long getDepartment() { 898 return iDept; 899 } 900 901 /** Controlling Course Offering Department 902 * @param dept department unique id 903 **/ 904 public void setDepartment(Long dept) { 905 iDept = dept; 906 } 907 908 /** Scheduler (Managing Department) 909 * @return solver group unique id 910 **/ 911 public Long getScheduler() { 912 return iScheduler; 913 } 914 915 /** Scheduler (Managing Department) 916 * @param scheduler solver group unique id 917 **/ 918 public void setScheduler(Long scheduler) { 919 iScheduler = scheduler; 920 } 921 922 /** Departmental spreading constraint 923 * @return department spread constraint of this class, if any 924 **/ 925 public DepartmentSpreadConstraint getDeptSpreadConstraint() { 926 return iDeptSpreadConstraint; 927 } 928 929 /** Instructor constraint 930 * @return instructors of this class 931 **/ 932 public List<InstructorConstraint> getInstructorConstraints() { 933 return iInstructorConstraints; 934 } 935 936 public ClassLimitConstraint getClassLimitConstraint() { 937 return iClassLimitConstraint; 938 } 939 940 public Set<SpreadConstraint> getSpreadConstraints() { 941 return iSpreadConstraints; 942 } 943 944 public Set<FlexibleConstraint> getFlexibleGroupConstraints() { 945 return iFlexibleGroupConstraints; 946 } 947 948 public Set<Constraint<Lecture, Placement>> getWeakeningConstraints() { 949 return iWeakeningConstraints; 950 } 951 952 /** All room locations 953 * @return possible rooms of this class 954 **/ 955 public List<RoomLocation> roomLocations() { 956 return iRoomLocations; 957 } 958 959 /** All time locations 960 * @return possible times of this class 961 **/ 962 public List<TimeLocation> timeLocations() { 963 return iTimeLocations; 964 } 965 966 public int nrTimeLocations() { 967 int ret = 0; 968 for (TimeLocation time : iTimeLocations) { 969 if (!Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(time.getPreference()))) 970 ret++; 971 } 972 return ret; 973 } 974 975 public int nrRoomLocations() { 976 int ret = 0; 977 for (RoomLocation room : iRoomLocations) { 978 if (!Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(room.getPreference()))) 979 ret++; 980 } 981 return ret; 982 } 983 984 public long nrValues() { 985 int nrTimes = 0; 986 for (TimeLocation time: timeLocations()) 987 if (!Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(time.getPreference()))) 988 nrTimes ++; 989 int nrRooms = 0; 990 for (RoomLocation room : iRoomLocations) 991 if (!Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(room.getPreference()))) 992 nrRooms ++; 993 long estNrValues = nrTimes; 994 if (getNrRooms() > 1 && getMaxRoomCombinations() > 0) 995 estNrValues *= Math.min(getMaxRoomCombinations(), ToolBox.binomial(nrRooms, getNrRooms())); 996 else 997 estNrValues *= ToolBox.binomial(nrRooms, getNrRooms()); 998 return estNrValues; 999 } 1000 1001 public int nrValues(TimeLocation time) { 1002 int ret = 0; 1003 for (RoomLocation room : iRoomLocations) { 1004 if (!Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(room.getPreference())) 1005 && (room.getRoomConstraint() == null || room.getRoomConstraint().isAvailable(this, time, getScheduler()))) 1006 ret++; 1007 } 1008 return ret; 1009 } 1010 1011 public int nrValues(RoomLocation room) { 1012 int ret = 0; 1013 for (TimeLocation time : iTimeLocations) { 1014 if (!Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(time.getPreference())) 1015 && (room.getRoomConstraint() == null || room.getRoomConstraint().isAvailable(this, time,getScheduler()))) 1016 ret++; 1017 } 1018 return ret; 1019 } 1020 1021 public int nrValues(List<RoomLocation> rooms) { 1022 int ret = 0; 1023 for (TimeLocation time : iTimeLocations) { 1024 boolean available = true; 1025 for (RoomLocation room : rooms) { 1026 if (Constants.sPreferenceProhibited.equals(Constants.preferenceLevel2preference(time.getPreference())) 1027 || (room.getRoomConstraint() != null && !room.getRoomConstraint().isAvailable(this, time, 1028 getScheduler()))) 1029 available = false; 1030 } 1031 if (available) 1032 ret++; 1033 } 1034 return ret; 1035 } 1036 1037 public boolean allowBreakHard() { 1038 return sAllowBreakHard; 1039 } 1040 1041 public int getNrRooms() { 1042 return iNrRooms; 1043 } 1044 1045 public Lecture getParent() { 1046 return iParent; 1047 } 1048 1049 public void setParent(Lecture parent) { 1050 iParent = parent; 1051 iParent.addChild(this); 1052 } 1053 1054 public boolean hasParent() { 1055 return (iParent != null); 1056 } 1057 1058 public boolean hasChildren(Long subpartId) { 1059 return (iChildren != null && iChildren.get(subpartId) != null && !iChildren.get(subpartId).isEmpty()); 1060 } 1061 1062 public boolean hasAnyChildren() { 1063 return (iChildren != null && !iChildren.isEmpty()); 1064 } 1065 1066 public List<Lecture> getChildren(Long subpartId) { 1067 return iChildren.get(subpartId); 1068 } 1069 1070 public Set<Long> getChildrenSubpartIds() { 1071 return (iChildren == null ? null : iChildren.keySet()); 1072 } 1073 1074 public Map<Long, List<Lecture>> getChildren() { 1075 return iChildren; 1076 } 1077 1078 private void addChild(Lecture child) { 1079 if (iChildren == null) 1080 iChildren = new HashMap<Long, List<Lecture>>(); 1081 List<Lecture> childrenThisSubpart = iChildren.get(child.getSchedulingSubpartId()); 1082 if (childrenThisSubpart == null) { 1083 childrenThisSubpart = new ArrayList<Lecture>(); 1084 iChildren.put(child.getSchedulingSubpartId(), childrenThisSubpart); 1085 } 1086 childrenThisSubpart.add(child); 1087 } 1088 1089 public boolean isSingleSection() { 1090 return (iSameSubpartLectures == null || iSameSubpartLectures.size() <= 1); 1091 /* 1092 if (iParent == null) 1093 return (iSameSubpartLectures == null || iSameSubpartLectures.size() <= 1); 1094 return (iParent.getChildren(getSchedulingSubpartId()).size() <= 1); 1095 */ 1096 } 1097 1098 public java.util.List<Lecture> sameStudentsLectures() { 1099 return (hasParent() ? getParent().getChildren(getSchedulingSubpartId()) : sameSubpartLectures()); 1100 } 1101 1102 public Lecture getChild(Student student, Long subpartId) { 1103 if (!hasAnyChildren()) 1104 return null; 1105 List<Lecture> children = getChildren(subpartId); 1106 if (children == null) 1107 return null; 1108 for (Lecture child : children) { 1109 if (child.students().contains(student)) 1110 return child; 1111 } 1112 return null; 1113 } 1114 1115 public int getCommitedConflicts(Placement placement) { 1116 iLock.readLock().lock(); 1117 try { 1118 Integer ret = iCommitedConflicts.get(placement); 1119 if (ret != null) return ret; 1120 } finally { 1121 iLock.readLock().unlock(); 1122 } 1123 iLock.writeLock().lock(); 1124 try { 1125 int ret = placement.getCommitedConflicts(); 1126 iCommitedConflicts.put(placement, ret); 1127 return ret; 1128 } finally { 1129 iLock.writeLock().unlock(); 1130 } 1131 } 1132 1133 public Set<GroupConstraint> hardGroupSoftConstraints() { 1134 return iHardGroupSoftConstraints; 1135 } 1136 1137 public Set<GroupConstraint> groupConstraints() { 1138 return iGroupConstraints; 1139 } 1140 1141 public int minRoomSize() { 1142 iLock.readLock().lock(); 1143 try { 1144 if (iCacheMinRoomSize != null) return iCacheMinRoomSize.intValue(); 1145 } finally { 1146 iLock.readLock().unlock(); 1147 } 1148 iLock.writeLock().lock(); 1149 try { 1150 if (iCacheMinRoomSize != null) return iCacheMinRoomSize.intValue(); 1151 if (getNrRooms() <= 1) { 1152 int min = Integer.MAX_VALUE; 1153 for (RoomLocation r : roomLocations()) { 1154 if (r.getPreference() <= Constants.sPreferenceLevelProhibited / 2) 1155 min = Math.min(min, r.getRoomSize()); 1156 } 1157 iCacheMinRoomSize = new Integer(min); 1158 return min; 1159 } else { 1160 List<RoomLocation> rooms = new ArrayList<RoomLocation>(); 1161 for (RoomLocation r: roomLocations()) 1162 if (r.getPreference() <= Constants.sPreferenceLevelProhibited / 2) 1163 rooms.add(r); 1164 Collections.sort(rooms, new Comparator<RoomLocation>() { 1165 @Override 1166 public int compare(RoomLocation r1, RoomLocation r2) { 1167 if (r1.getRoomSize() < r2.getRoomSize()) return -1; 1168 if (r1.getRoomSize() > r2.getRoomSize()) return 1; 1169 return r1.compareTo(r2); 1170 } 1171 }); 1172 int min = rooms.isEmpty() ? 0 : rooms.get(Math.min(getNrRooms(), rooms.size()) - 1).getRoomSize(); 1173 iCacheMinRoomSize = new Integer(min); 1174 return min; 1175 } 1176 } finally { 1177 iLock.writeLock().unlock(); 1178 } 1179 } 1180 1181 public int maxRoomSize() { 1182 iLock.readLock().lock(); 1183 try { 1184 if (iCacheMaxRoomSize != null) return iCacheMaxRoomSize.intValue(); 1185 } finally { 1186 iLock.readLock().unlock(); 1187 } 1188 iLock.writeLock().lock(); 1189 try { 1190 if (iCacheMaxRoomSize != null) return iCacheMaxRoomSize.intValue(); 1191 if (getNrRooms() <= 1) { 1192 int max = Integer.MIN_VALUE; 1193 for (RoomLocation r : roomLocations()) { 1194 if (r.getPreference() <= Constants.sPreferenceLevelProhibited / 2) 1195 max = Math.max(max, r.getRoomSize()); 1196 } 1197 iCacheMaxRoomSize = new Integer(max); 1198 return max; 1199 } else { 1200 List<RoomLocation> rooms = new ArrayList<RoomLocation>(); 1201 for (RoomLocation r: roomLocations()) 1202 if (r.getPreference() <= Constants.sPreferenceLevelProhibited / 2) rooms.add(r); 1203 Collections.sort(rooms, new Comparator<RoomLocation>() { 1204 @Override 1205 public int compare(RoomLocation r1, RoomLocation r2) { 1206 if (r1.getRoomSize() > r2.getRoomSize()) return -1; 1207 if (r1.getRoomSize() < r2.getRoomSize()) return 1; 1208 return r1.compareTo(r2); 1209 } 1210 }); 1211 int max = rooms.isEmpty() ? 0 : rooms.get(Math.min(getNrRooms(), rooms.size()) - 1).getRoomSize(); 1212 iCacheMaxRoomSize = new Integer(max); 1213 return max; 1214 } 1215 } finally { 1216 iLock.writeLock().unlock(); 1217 } 1218 } 1219 1220 public boolean canShareRoom() { 1221 return (!iCanShareRoomGroupConstraints.isEmpty()); 1222 } 1223 1224 public boolean canShareRoom(Lecture other) { 1225 if (other.equals(this)) 1226 return true; 1227 for (GroupConstraint gc : iCanShareRoomGroupConstraints) { 1228 if (gc.variables().contains(other)) 1229 return true; 1230 } 1231 return false; 1232 } 1233 1234 public Set<GroupConstraint> canShareRoomConstraints() { 1235 return iCanShareRoomGroupConstraints; 1236 } 1237 1238 public boolean isSingleton() { 1239 return getNrRooms() == roomLocations().size() && timeLocations().size() == 1; 1240 } 1241 1242 public boolean isValid(Placement placement) { 1243 TimetableModel model = (TimetableModel) getModel(); 1244 if (model == null) 1245 return true; 1246 if (model.hasConstantVariables()) { 1247 for (Placement confPlacement : model.conflictValuesSkipWeakeningConstraints(model.getEmptyAssignment(), placement)) { 1248 Lecture lecture = confPlacement.variable(); 1249 if (lecture.isCommitted()) 1250 return false; 1251 if (confPlacement.equals(placement)) 1252 return false; 1253 } 1254 } else { 1255 Set<Placement> conflicts = new HashSet<Placement>(); 1256 for (Constraint<Lecture, Placement> constraint : hardConstraints()) { 1257 if (constraint instanceof WeakeningConstraint) continue; 1258 constraint.computeConflicts(model.getEmptyAssignment(), placement, conflicts); 1259 } 1260 for (GlobalConstraint<Lecture, Placement> constraint : model.globalConstraints()) { 1261 if (constraint instanceof WeakeningConstraint) continue; 1262 constraint.computeConflicts(model.getEmptyAssignment(), placement, conflicts); 1263 } 1264 if (conflicts.contains(placement)) 1265 return false; 1266 } 1267 return true; 1268 } 1269 1270 public String getNotValidReason(Assignment<Lecture, Placement> assignment, Placement placement, boolean useAmPm) { 1271 TimetableModel model = (TimetableModel) getModel(); 1272 if (model == null) 1273 return "no model for class " + getName(); 1274 Map<Constraint<Lecture, Placement>, Set<Placement>> conflictConstraints = model.conflictConstraints(assignment, placement); 1275 for (Map.Entry<Constraint<Lecture, Placement>, Set<Placement>> entry : conflictConstraints.entrySet()) { 1276 Constraint<Lecture, Placement> constraint = entry.getKey(); 1277 Set<Placement> conflicts = entry.getValue(); 1278 String cname = constraint.getName(); 1279 if (constraint instanceof RoomConstraint) { 1280 cname = "Room " + constraint.getName(); 1281 } else if (constraint instanceof InstructorConstraint) { 1282 cname = "Instructor " + constraint.getName(); 1283 } else if (constraint instanceof GroupConstraint) { 1284 cname = "Distribution " + constraint.getName(); 1285 } else if (constraint instanceof DepartmentSpreadConstraint) { 1286 cname = "Balancing of department " + constraint.getName(); 1287 } else if (constraint instanceof SpreadConstraint) { 1288 cname = "Same subpart spread " + constraint.getName(); 1289 } else if (constraint instanceof ClassLimitConstraint) { 1290 cname = "Class limit " + constraint.getName(); 1291 } 1292 for (Placement confPlacement : conflicts) { 1293 Lecture lecture = confPlacement.variable(); 1294 if (lecture.isCommitted()) { 1295 return placement.getLongName(useAmPm) + " conflicts with " + lecture.getName() + " " 1296 + confPlacement.getLongName(useAmPm) + " due to constraint " + cname; 1297 } 1298 if (confPlacement.equals(placement)) { 1299 return placement.getLongName(useAmPm) + " is not valid due to constraint " + cname; 1300 } 1301 } 1302 } 1303 return null; 1304 } 1305 1306 @Deprecated 1307 public String getNotValidReason(Assignment<Lecture, Placement> assignment, Placement placement) { 1308 return getNotValidReason(assignment, placement, true); 1309 } 1310 1311 public void purgeInvalidValues(boolean interactiveMode) { 1312 if (isCommitted() || sSaveMemory) return; 1313 TimetableModel model = (TimetableModel) getModel(); 1314 if (model == null) 1315 return; 1316 List<Placement> newValues = new ArrayList<Placement>(values(null).size()); 1317 for (Placement placement : values(null)) { 1318 if (placement.isValid()) 1319 newValues.add(placement); 1320 } 1321 if (!interactiveMode && newValues.size() != values(null).size()) { 1322 for (Iterator<TimeLocation> i = timeLocations().iterator(); i.hasNext();) { 1323 TimeLocation timeLocation = i.next(); 1324 boolean hasPlacement = false; 1325 for (Placement placement : newValues) { 1326 if (timeLocation.equals(placement.getTimeLocation())) { 1327 hasPlacement = true; 1328 break; 1329 } 1330 } 1331 if (!hasPlacement) 1332 i.remove(); 1333 } 1334 for (Iterator<RoomLocation> i = roomLocations().iterator(); i.hasNext();) { 1335 RoomLocation roomLocation = i.next(); 1336 boolean hasPlacement = false; 1337 for (Placement placement : newValues) { 1338 if (placement.isMultiRoom()) { 1339 if (placement.getRoomLocations().contains(roomLocation)) { 1340 hasPlacement = true; 1341 break; 1342 } 1343 } else { 1344 if (roomLocation.equals(placement.getRoomLocation())) { 1345 hasPlacement = true; 1346 break; 1347 } 1348 } 1349 } 1350 if (!hasPlacement) 1351 i.remove(); 1352 } 1353 } 1354 setValues(newValues); 1355 } 1356 1357 public void setCommitted(boolean committed) { 1358 iCommitted = committed; 1359 } 1360 1361 public boolean isCommitted() { 1362 return iCommitted; 1363 } 1364 1365 @Override 1366 public boolean isConstant() { 1367 return iCommitted; 1368 } 1369 1370 @Override 1371 public Placement getConstantValue() { 1372 return (isCommitted() ? getInitialAssignment() : null); 1373 } 1374 1375 public void setConstantValue(Placement value) { 1376 setInitialAssignment(value); 1377 } 1378 1379 public int getSpreadPenalty(Assignment<Lecture, Placement> assignment) { 1380 int spread = 0; 1381 for (SpreadConstraint sc : getSpreadConstraints()) { 1382 spread += sc.getPenalty(assignment); 1383 } 1384 return spread; 1385 } 1386 1387 @Override 1388 public int hashCode() { 1389 return getClassId().hashCode(); 1390 } 1391 1392 public Configuration getConfiguration() { 1393 Lecture lecture = this; 1394 while (lecture.getParent() != null) 1395 lecture = lecture.getParent(); 1396 return lecture.iParentConfiguration; 1397 } 1398 1399 public void setConfiguration(Configuration configuration) { 1400 Lecture lecture = this; 1401 while (lecture.getParent() != null) 1402 lecture = lecture.getParent(); 1403 lecture.iParentConfiguration = configuration; 1404 configuration.addTopLecture(lecture); 1405 } 1406 1407 private int[] iMinMaxRoomPreference = null; 1408 1409 public int[] getMinMaxRoomPreference() { 1410 iLock.readLock().lock(); 1411 try { 1412 if (iMinMaxRoomPreference != null) return iMinMaxRoomPreference; 1413 } finally { 1414 iLock.readLock().unlock(); 1415 } 1416 iLock.writeLock().lock(); 1417 try { 1418 if (iMinMaxRoomPreference != null) return iMinMaxRoomPreference; 1419 1420 if (getNrRooms() <= 0 || roomLocations().isEmpty()) { 1421 iMinMaxRoomPreference = new int[] { 0, 0 }; 1422 } else { 1423 Integer minRoomPref = null, maxRoomPref = null; 1424 for (RoomLocation r : roomLocations()) { 1425 int pref = r.getPreference(); 1426 if (pref >= Constants.sPreferenceLevelRequired / 2 && pref <= Constants.sPreferenceLevelProhibited / 2) { 1427 minRoomPref = (minRoomPref == null ? pref : Math.min(minRoomPref, pref)); 1428 maxRoomPref = (maxRoomPref == null ? pref : Math.max(maxRoomPref, pref)); 1429 } 1430 } 1431 iMinMaxRoomPreference = new int[] { minRoomPref == null ? 0 : minRoomPref, maxRoomPref == null ? 0 : maxRoomPref }; 1432 } 1433 1434 return iMinMaxRoomPreference; 1435 } finally { 1436 iLock.writeLock().unlock(); 1437 } 1438 } 1439 1440 private double[] iMinMaxTimePreference = null; 1441 1442 public double[] getMinMaxTimePreference() { 1443 iLock.readLock().lock(); 1444 try { 1445 if (iMinMaxTimePreference != null) return iMinMaxTimePreference; 1446 } finally { 1447 iLock.readLock().unlock(); 1448 } 1449 iLock.writeLock().lock(); 1450 try { 1451 if (iMinMaxTimePreference != null) return iMinMaxTimePreference; 1452 1453 Double minTimePref = null, maxTimePref = null; 1454 for (TimeLocation t : timeLocations()) { 1455 double npref = t.getNormalizedPreference(); 1456 int pref = t.getPreference(); 1457 if (pref >= Constants.sPreferenceLevelRequired / 2 && pref <= Constants.sPreferenceLevelProhibited / 2) { 1458 minTimePref = (minTimePref == null ? npref : Math.min(minTimePref, npref)); 1459 maxTimePref = (maxTimePref == null ? npref : Math.max(maxTimePref, npref)); 1460 } 1461 } 1462 iMinMaxTimePreference = new double[] { minTimePref == null ? 0.0 : minTimePref, maxTimePref == null ? 0.0 : maxTimePref }; 1463 1464 return iMinMaxTimePreference; 1465 } finally { 1466 iLock.writeLock().unlock(); 1467 } 1468 } 1469 1470 public void setOrd(int ord) { 1471 iOrd = ord; 1472 } 1473 1474 public int getOrd() { 1475 return iOrd; 1476 } 1477 1478 @Override 1479 public int compareTo(Lecture o) { 1480 int cmp = Double.compare(getOrd(), o.getOrd()); 1481 if (cmp != 0) 1482 return cmp; 1483 return super.compareTo(o); 1484 } 1485 1486 public String getNote() { 1487 return iNote; 1488 } 1489 1490 public void setNote(String note) { 1491 iNote = note; 1492 } 1493 1494 public boolean areStudentConflictsHard(Lecture other) { 1495 return StudentConflict.hard(this, other); 1496 } 1497 1498 public void clearIgnoreStudentConflictsWithCache() { 1499 iIgnoreStudentConflictsWith.set(null); 1500 } 1501 1502 /** 1503 * Returns true if there is {@link IgnoreStudentConflictsConstraint} between the two lectures. 1504 * @param other another class 1505 * @return true if student conflicts between this and the given calls are to be ignored 1506 */ 1507 public boolean isToIgnoreStudentConflictsWith(Lecture other) { 1508 Set<Long> cache = iIgnoreStudentConflictsWith.get(); 1509 if (cache != null) 1510 return cache.contains(other.getClassId()); 1511 cache = new HashSet<Long>(); 1512 for (Constraint<Lecture, Placement> constraint: constraints()) { 1513 if (constraint instanceof IgnoreStudentConflictsConstraint) 1514 for (Lecture x: constraint.variables()) { 1515 if (!x.equals(this)) cache.add(x.getClassId()); 1516 } 1517 } 1518 iIgnoreStudentConflictsWith.set(cache); 1519 return cache.contains(other); 1520 } 1521 1522 /** 1523 * Get class weight. This weight is used with the criteria. E.g., class that is not meeting all the 1524 * semester can have a lower weight. Defaults to 1.0 1525 * @return class weight 1526 */ 1527 public double getWeight() { return iWeight; } 1528 /** 1529 * Set class weight. This weight is used with the criteria. E.g., class that is not meeting all the 1530 * semester can have a lower weight. 1531 * @param weight class weight 1532 */ 1533 public void setWeight(double weight) { iWeight = weight; } 1534 1535 @Override 1536 public LectureContext createAssignmentContext(Assignment<Lecture, Placement> assignment) { 1537 return new LectureContext(); 1538 } 1539 1540 public class LectureContext implements AssignmentContext { 1541 private Set<JenrlConstraint> iActiveJenrls = new HashSet<JenrlConstraint>(); 1542 1543 /** 1544 * Add active jenrl constraint (active mean that there is at least one 1545 * student between its classes) 1546 * @param constr active join enrollment constraint 1547 */ 1548 public void addActiveJenrl(JenrlConstraint constr) { 1549 iActiveJenrls.add(constr); 1550 } 1551 1552 /** 1553 * Active jenrl constraints (active mean that there is at least one student 1554 * between its classes) 1555 * @return set of active join enrollment constraints 1556 */ 1557 public Set<JenrlConstraint> activeJenrls() { 1558 return iActiveJenrls; 1559 } 1560 1561 /** 1562 * Remove active jenrl constraint (active mean that there is at least one 1563 * student between its classes) 1564 * @param constr active join enrollment constraint 1565 */ 1566 public void removeActiveJenrl(JenrlConstraint constr) { 1567 iActiveJenrls.remove(constr); 1568 } 1569 } 1570 1571 public int getMaxRoomCombinations() { 1572 return iMaxRoomCombinations; 1573 } 1574 1575 public void setMaxRoomCombinations(int maxRoomCombinations) { 1576 iMaxRoomCombinations = maxRoomCombinations; 1577 } 1578}