001package org.cpsolver.studentsct.extension; 002 003import java.util.HashMap; 004import java.util.HashSet; 005import java.util.Iterator; 006import java.util.Map; 007import java.util.Set; 008 009import org.apache.logging.log4j.Logger; 010import org.cpsolver.coursett.Constants; 011import org.cpsolver.coursett.model.Placement; 012import org.cpsolver.coursett.model.RoomLocation; 013import org.cpsolver.coursett.model.TimeLocation; 014import org.cpsolver.ifs.assignment.Assignment; 015import org.cpsolver.ifs.assignment.context.AssignmentConstraintContext; 016import org.cpsolver.ifs.assignment.context.CanInheritContext; 017import org.cpsolver.ifs.assignment.context.ExtensionWithContext; 018import org.cpsolver.ifs.model.ModelListener; 019import org.cpsolver.ifs.solver.Solver; 020import org.cpsolver.ifs.util.DataProperties; 021import org.cpsolver.ifs.util.DistanceMetric; 022import org.cpsolver.studentsct.StudentSectioningModel; 023import org.cpsolver.studentsct.StudentSectioningModel.StudentSectioningModelContext; 024import org.cpsolver.studentsct.model.CourseRequest; 025import org.cpsolver.studentsct.model.Enrollment; 026import org.cpsolver.studentsct.model.Request; 027import org.cpsolver.studentsct.model.Section; 028import org.cpsolver.studentsct.model.Student; 029 030 031/** 032 * This extension computes student distant conflicts. Two sections that are 033 * attended by the same student are considered in a distance conflict if they 034 * are back-to-back taught in locations that are two far away. This means that 035 * the (walking) distance in minutes between the two classes are longer than 036 * the break time of the earlier class. See {@link DistanceMetric} for more details. 037 * 038 * @see TimeLocation 039 * @see Placement 040 * 041 * <br> 042 * <br> 043 * 044 * @author Tomáš Müller 045 * @version StudentSct 1.3 (Student Sectioning)<br> 046 * Copyright (C) 2007 - 2014 Tomáš Müller<br> 047 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 048 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 049 * <br> 050 * This library is free software; you can redistribute it and/or modify 051 * it under the terms of the GNU Lesser General Public License as 052 * published by the Free Software Foundation; either version 3 of the 053 * License, or (at your option) any later version. <br> 054 * <br> 055 * This library is distributed in the hope that it will be useful, but 056 * WITHOUT ANY WARRANTY; without even the implied warranty of 057 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 058 * Lesser General Public License for more details. <br> 059 * <br> 060 * You should have received a copy of the GNU Lesser General Public 061 * License along with this library; if not see 062 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 063 */ 064 065public class DistanceConflict extends ExtensionWithContext<Request, Enrollment, DistanceConflict.DistanceConflictContext> implements ModelListener<Request, Enrollment>, CanInheritContext<Request, Enrollment, DistanceConflict.DistanceConflictContext> { 066 private static Logger sLog = org.apache.logging.log4j.LogManager.getLogger(DistanceConflict.class); 067 /** Debug flag */ 068 public static boolean sDebug = false; 069 private DistanceMetric iDistanceMetric = null; 070 071 /** 072 * Constructor. Beside of other thigs, this constructor also uses 073 * {@link StudentSectioningModel#setDistanceConflict(DistanceConflict)} to 074 * set the this instance to the model. 075 * 076 * @param solver 077 * constraint solver 078 * @param properties 079 * configuration 080 */ 081 public DistanceConflict(Solver<Request, Enrollment> solver, DataProperties properties) { 082 super(solver, properties); 083 if (solver != null) { 084 StudentSectioningModel model = (StudentSectioningModel) solver.currentSolution().getModel(); 085 iDistanceMetric = model.getDistanceMetric(); 086 model.setDistanceConflict(this); 087 } 088 if (iDistanceMetric == null) 089 iDistanceMetric = new DistanceMetric(properties); 090 } 091 092 /** 093 * Alternative constructor (for online student sectioning) 094 * @param metrics distance metrics 095 * @param properties configuration 096 */ 097 public DistanceConflict(DistanceMetric metrics, DataProperties properties) { 098 super(null, properties); 099 iDistanceMetric = metrics; 100 } 101 102 @Override 103 public String toString() { 104 return "DistanceConstraint"; 105 } 106 107 public DistanceMetric getDistanceMetric() { 108 return iDistanceMetric; 109 } 110 111 private Map<Long, Map<Long, Integer>> iDistanceCache = new HashMap<Long, Map<Long,Integer>>(); 112 protected synchronized int getDistanceInMinutes(RoomLocation r1, RoomLocation r2) { 113 if (r1.getId().compareTo(r2.getId()) > 0) return getDistanceInMinutes(r2, r1); 114 if (r1.getId().equals(r2.getId()) || r1.getIgnoreTooFar() || r2.getIgnoreTooFar()) 115 return 0; 116 if (r1.getPosX() == null || r1.getPosY() == null || r2.getPosX() == null || r2.getPosY() == null) 117 return iDistanceMetric.getMaxTravelDistanceInMinutes(); 118 Map<Long, Integer> other2distance = iDistanceCache.get(r1.getId()); 119 if (other2distance == null) { 120 other2distance = new HashMap<Long, Integer>(); 121 iDistanceCache.put(r1.getId(), other2distance); 122 } 123 Integer distance = other2distance.get(r2.getId()); 124 if (distance == null) { 125 distance = iDistanceMetric.getDistanceInMinutes(r1.getId(), r1.getPosX(), r1.getPosY(), r2.getId(), r2.getPosX(), r2.getPosY()); 126 other2distance.put(r2.getId(), distance); 127 } 128 return distance; 129 } 130 131 protected int getDistanceInMinutes(Placement p1, Placement p2) { 132 if (p1.isMultiRoom()) { 133 if (p2.isMultiRoom()) { 134 int dist = 0; 135 for (RoomLocation r1 : p1.getRoomLocations()) { 136 for (RoomLocation r2 : p2.getRoomLocations()) { 137 dist = Math.max(dist, getDistanceInMinutes(r1, r2)); 138 } 139 } 140 return dist; 141 } else { 142 if (p2.getRoomLocation() == null) 143 return 0; 144 int dist = 0; 145 for (RoomLocation r1 : p1.getRoomLocations()) { 146 dist = Math.max(dist, getDistanceInMinutes(r1, p2.getRoomLocation())); 147 } 148 return dist; 149 } 150 } else if (p2.isMultiRoom()) { 151 if (p1.getRoomLocation() == null) 152 return 0; 153 int dist = 0; 154 for (RoomLocation r2 : p2.getRoomLocations()) { 155 dist = Math.max(dist, getDistanceInMinutes(p1.getRoomLocation(), r2)); 156 } 157 return dist; 158 } else { 159 if (p1.getRoomLocation() == null || p2.getRoomLocation() == null) 160 return 0; 161 return getDistanceInMinutes(p1.getRoomLocation(), p2.getRoomLocation()); 162 } 163 } 164 165 /** 166 * Return true if the given two sections are in distance conflict. This 167 * means that the sections are back-to-back and that they are placed in 168 * locations that are two far. 169 * 170 * @param student a student 171 * @param s1 172 * a section 173 * @param s2 174 * a section 175 * @return true, if the given sections are in a distance conflict 176 */ 177 public boolean inConflict(Student student, Section s1, Section s2) { 178 if (s1.getPlacement() == null || s2.getPlacement() == null) 179 return false; 180 TimeLocation t1 = s1.getTime(); 181 TimeLocation t2 = s2.getTime(); 182 if (!t1.shareDays(t2) || !t1.shareWeeks(t2)) 183 return false; 184 int a1 = t1.getStartSlot(), a2 = t2.getStartSlot(); 185 if (student.isNeedShortDistances()) { 186 if (getDistanceMetric().doComputeDistanceConflictsBetweenNonBTBClasses()) { 187 if (a1 + t1.getNrSlotsPerMeeting() <= a2) { 188 int dist = getDistanceInMinutes(s1.getPlacement(), s2.getPlacement()); 189 if (dist > Constants.SLOT_LENGTH_MIN * (a2 - a1 - t1.getLength())) 190 return true; 191 } else if (a2 + t2.getNrSlotsPerMeeting() <= a1) { 192 int dist = getDistanceInMinutes(s1.getPlacement(), s2.getPlacement()); 193 if (dist > Constants.SLOT_LENGTH_MIN * (a1 - a2 - t2.getLength())) 194 return true; 195 } 196 } else { 197 if (a1 + t1.getNrSlotsPerMeeting() == a2) { 198 int dist = getDistanceInMinutes(s1.getPlacement(), s2.getPlacement()); 199 if (dist > 0) return true; 200 } else if (a2 + t2.getNrSlotsPerMeeting() == a1) { 201 int dist = getDistanceInMinutes(s1.getPlacement(), s2.getPlacement()); 202 if (dist > 0) return true; 203 } 204 } 205 return false; 206 } 207 if (getDistanceMetric().doComputeDistanceConflictsBetweenNonBTBClasses()) { 208 if (a1 + t1.getNrSlotsPerMeeting() <= a2) { 209 int dist = getDistanceInMinutes(s1.getPlacement(), s2.getPlacement()); 210 if (dist > t1.getBreakTime() + Constants.SLOT_LENGTH_MIN * (a2 - a1 - t1.getLength())) 211 return true; 212 } else if (a2 + t2.getNrSlotsPerMeeting() <= a1) { 213 int dist = getDistanceInMinutes(s1.getPlacement(), s2.getPlacement()); 214 if (dist > t2.getBreakTime() + Constants.SLOT_LENGTH_MIN * (a1 - a2 - t2.getLength())) 215 return true; 216 } 217 } else { 218 if (a1 + t1.getNrSlotsPerMeeting() == a2) { 219 int dist = getDistanceInMinutes(s1.getPlacement(), s2.getPlacement()); 220 if (dist > t1.getBreakTime()) 221 return true; 222 } else if (a2 + t2.getNrSlotsPerMeeting() == a1) { 223 int dist = getDistanceInMinutes(s1.getPlacement(), s2.getPlacement()); 224 if (dist > t2.getBreakTime()) 225 return true; 226 } 227 } 228 return false; 229 } 230 231 /** 232 * Return number of distance conflict of a (course) enrollment. It is the 233 * number of pairs of assignments of the enrollment that are in a distance 234 * conflict. 235 * 236 * @param e1 237 * an enrollment 238 * @return number of distance conflicts 239 */ 240 public int nrConflicts(Enrollment e1) { 241 if (!e1.isCourseRequest()) 242 return 0; 243 int cnt = 0; 244 for (Section s1 : e1.getSections()) { 245 for (Section s2 : e1.getSections()) { 246 if (s1.getId() < s2.getId() && inConflict(e1.getStudent(), s1, s2)) 247 cnt ++; 248 } 249 } 250 return cnt; 251 } 252 253 /** 254 * Return number of distance conflicts that are between two enrollments. It 255 * is the number of pairs of assignments of these enrollments that are in a 256 * distance conflict. 257 * 258 * @param e1 259 * an enrollment 260 * @param e2 261 * an enrollment 262 * @return number of distance conflict between given enrollments 263 */ 264 public int nrConflicts(Enrollment e1, Enrollment e2) { 265 if (!e1.isCourseRequest() || !e2.isCourseRequest() || !e1.getStudent().equals(e2.getStudent())) 266 return 0; 267 int cnt = 0; 268 for (Section s1 : e1.getSections()) { 269 for (Section s2 : e2.getSections()) { 270 if (inConflict(e1.getStudent(), s1, s2)) 271 cnt ++; 272 } 273 } 274 return cnt; 275 } 276 277 /** 278 * Return a set of distance conflicts ({@link Conflict} objects) of a 279 * (course) enrollment. 280 * 281 * @param e1 282 * an enrollment 283 * @return list of distance conflicts that are between assignment of the 284 * given enrollment 285 */ 286 public Set<Conflict> conflicts(Enrollment e1) { 287 Set<Conflict> ret = new HashSet<Conflict>(); 288 if (!e1.isCourseRequest()) 289 return ret; 290 for (Section s1 : e1.getSections()) { 291 for (Section s2 : e1.getSections()) { 292 if (s1.getId() < s2.getId() && inConflict(e1.getStudent(), s1, s2)) 293 ret.add(new Conflict(e1.getStudent(), e1, s1, e1, s2)); 294 } 295 } 296 return ret; 297 } 298 299 /** 300 * Return a set of distance conflicts ({@link Conflict} objects) between 301 * given (course) enrollments. 302 * 303 * @param e1 304 * an enrollment 305 * @param e2 306 * an enrollment 307 * @return list of distance conflicts that are between assignment of the 308 * given enrollments 309 */ 310 public Set<Conflict> conflicts(Enrollment e1, Enrollment e2) { 311 Set<Conflict> ret = new HashSet<Conflict>(); 312 if (!e1.isCourseRequest() || !e2.isCourseRequest() || !e1.getStudent().equals(e2.getStudent())) 313 return ret; 314 for (Section s1 : e1.getSections()) { 315 for (Section s2 : e2.getSections()) { 316 if (inConflict(e1.getStudent(), s1, s2)) 317 ret.add(new Conflict(e1.getStudent(), e1, s1, e2, s2)); 318 } 319 } 320 return ret; 321 } 322 323 /** 324 * The set of all conflicts ({@link Conflict} objects) of the given 325 * enrollment and other enrollments that are assignmed to the same student. 326 * @param assignment current assignment 327 * @param enrollment given enrollment 328 * @return set of all conflicts 329 */ 330 public Set<Conflict> allConflicts(Assignment<Request, Enrollment> assignment, Enrollment enrollment) { 331 Set<Conflict> ret = conflicts(enrollment); 332 if (!enrollment.isCourseRequest()) 333 return ret; 334 for (Request request : enrollment.getStudent().getRequests()) { 335 if (request.equals(enrollment.getRequest()) || assignment.getValue(request) == null) 336 continue; 337 ret.addAll(conflicts(enrollment, assignment.getValue(request))); 338 } 339 return ret; 340 } 341 342 /** Checks the counter counting all conflicts 343 * @param assignment current assignment 344 */ 345 public void checkAllConflicts(Assignment<Request, Enrollment> assignment) { 346 getContext(assignment).checkAllConflicts(assignment); 347 } 348 349 /** Actual number of all distance conflicts 350 * @param assignment current assignment 351 * @return cached number of all distance conflicts 352 **/ 353 public int getTotalNrConflicts(Assignment<Request, Enrollment> assignment) { 354 return getContext(assignment).getTotalNrConflicts(); 355 } 356 357 /** Actual number of all distance conflicts of students that need short distances 358 * @param assignment current assignment 359 * @return cached number of all distance conflicts 360 **/ 361 public int getTotalNrShortConflicts(Assignment<Request, Enrollment> assignment) { 362 return getContext(assignment).getTotalNrShortConflicts(); 363 } 364 365 /** 366 * Compute the actual number of all distance conflicts. Should be equal to 367 * {@link DistanceConflict#getTotalNrConflicts(Assignment)}. 368 * @param assignment current assignment 369 * @return computed number of all distance conflicts 370 */ 371 public int countTotalNrConflicts(Assignment<Request, Enrollment> assignment) { 372 int total = 0; 373 for (Request r1 : getModel().variables()) { 374 if (assignment.getValue(r1) == null || !(r1 instanceof CourseRequest)) 375 continue; 376 Enrollment e1 = assignment.getValue(r1); 377 total += nrConflicts(e1); 378 for (Request r2 : r1.getStudent().getRequests()) { 379 Enrollment e2 = assignment.getValue(r2); 380 if (e2 == null || r1.getId() >= r2.getId() || !(r2 instanceof CourseRequest)) 381 continue; 382 total += nrConflicts(e1, e2); 383 } 384 } 385 return total; 386 } 387 388 /** 389 * Compute a set of all distance conflicts ({@link Conflict} objects). 390 * @param assignment current assignment 391 * @return computed set of all distance conflicts 392 */ 393 public Set<Conflict> computeAllConflicts(Assignment<Request, Enrollment> assignment) { 394 Set<Conflict> ret = new HashSet<Conflict>(); 395 for (Request r1 : getModel().variables()) { 396 Enrollment e1 = assignment.getValue(r1); 397 if (e1 == null || !(r1 instanceof CourseRequest)) 398 continue; 399 ret.addAll(conflicts(e1)); 400 for (Request r2 : r1.getStudent().getRequests()) { 401 Enrollment e2 = assignment.getValue(r2); 402 if (e2 == null || r1.getId() >= r2.getId() || !(r2 instanceof CourseRequest)) 403 continue; 404 ret.addAll(conflicts(e1, e2)); 405 } 406 } 407 return ret; 408 } 409 410 /** 411 * Return a set of all distance conflicts ({@link Conflict} objects). 412 * @param assignment current assignment 413 * @return cached set of all distance conflicts 414 */ 415 public Set<Conflict> getAllConflicts(Assignment<Request, Enrollment> assignment) { 416 return getContext(assignment).getAllConflicts(); 417 } 418 419 /** 420 * Called before a value is assigned to a variable. 421 */ 422 @Override 423 public void beforeAssigned(Assignment<Request, Enrollment> assignment, long iteration, Enrollment value) { 424 getContext(assignment).beforeAssigned(assignment, iteration, value); 425 } 426 427 /** 428 * Called after a value is assigned to a variable. 429 */ 430 @Override 431 public void afterAssigned(Assignment<Request, Enrollment> assignment, long iteration, Enrollment value) { 432 getContext(assignment).afterAssigned(assignment, iteration, value); 433 } 434 435 /** 436 * Called after a value is unassigned from a variable. 437 */ 438 @Override 439 public void afterUnassigned(Assignment<Request, Enrollment> assignment, long iteration, Enrollment value) { 440 getContext(assignment).afterUnassigned(assignment, iteration, value); 441 } 442 443 /** A representation of a distance conflict */ 444 public static class Conflict { 445 private Student iStudent; 446 private Section iS1, iS2; 447 private Enrollment iE1, iE2; 448 private int iHashCode; 449 450 /** 451 * Constructor 452 * 453 * @param student 454 * related student 455 * @param e1 first enrollment 456 * @param s1 457 * first conflicting section 458 * @param e2 second enrollment 459 * @param s2 460 * second conflicting section 461 */ 462 public Conflict(Student student, Enrollment e1, Section s1, Enrollment e2, Section s2) { 463 iStudent = student; 464 if (s1.getId() < s2.getId()) { 465 iS1 = s1; 466 iS2 = s2; 467 iE1 = e1; 468 iE2 = e2; 469 } else { 470 iS1 = s2; 471 iS2 = s1; 472 iE1 = e2; 473 iE2 = e1; 474 } 475 iHashCode = (iStudent.getId() + ":" + iS1.getId() + ":" + iS2.getId()).hashCode(); 476 } 477 478 /** Related student 479 * @return student 480 **/ 481 public Student getStudent() { 482 return iStudent; 483 } 484 485 /** First section 486 * @return first section 487 **/ 488 public Section getS1() { 489 return iS1; 490 } 491 492 /** Second section 493 * @return second section 494 **/ 495 public Section getS2() { 496 return iS2; 497 } 498 499 /** First request 500 * @return first request 501 **/ 502 public Request getR1() { 503 return iE1.getRequest(); 504 } 505 506 /** Second request 507 * @return second request 508 **/ 509 public Request getR2() { 510 return iE2.getRequest(); 511 } 512 513 /** First enrollment 514 * @return first enrollment 515 **/ 516 public Enrollment getE1() { 517 return iE1; 518 } 519 520 /** Second enrollment 521 * @return second enrollment 522 **/ 523 public Enrollment getE2() { 524 return iE2; 525 } 526 527 @Override 528 public int hashCode() { 529 return iHashCode; 530 } 531 532 /** The distance between conflicting sections 533 * @param dm distance metrics 534 * @return distance in meters between conflicting sections 535 **/ 536 public double getDistance(DistanceMetric dm) { 537 return Placement.getDistanceInMeters(dm, getS1().getPlacement(), getS2().getPlacement()); 538 } 539 540 @Override 541 public boolean equals(Object o) { 542 if (o == null || !(o instanceof Conflict)) return false; 543 Conflict c = (Conflict) o; 544 return getStudent().equals(c.getStudent()) && getS1().equals(c.getS1()) && getS2().equals(c.getS2()); 545 } 546 547 @Override 548 public String toString() { 549 return getStudent() + ": " + getS1() + " -- " + getS2(); 550 } 551 } 552 553 public class DistanceConflictContext implements AssignmentConstraintContext<Request, Enrollment> { 554 private Set<Conflict> iAllConflicts = new HashSet<Conflict>(); 555 private Request iOldVariable = null; 556 private Enrollment iUnassignedValue = null; 557 558 public DistanceConflictContext(Assignment<Request, Enrollment> assignment) { 559 iAllConflicts = computeAllConflicts(assignment); 560 StudentSectioningModelContext cx = ((StudentSectioningModel)getModel()).getContext(assignment); 561 for (Conflict c: iAllConflicts) 562 cx.add(assignment, c); 563 } 564 565 public DistanceConflictContext(DistanceConflictContext parent) { 566 iAllConflicts.addAll(parent.iAllConflicts); 567 iOldVariable = parent.iOldVariable; 568 iUnassignedValue = parent.iUnassignedValue; 569 } 570 571 /** 572 * Called before a value is assigned to a variable. 573 * @param assignment current assignment 574 * @param iteration current iteration 575 * @param value value to be assigned 576 */ 577 public void beforeAssigned(Assignment<Request, Enrollment> assignment, long iteration, Enrollment value) { 578 if (value != null) { 579 Enrollment old = assignment.getValue(value.variable()); 580 if (old != null) { 581 unassigned(assignment, old); 582 iUnassignedValue = old; 583 } 584 iOldVariable = value.variable(); 585 } 586 } 587 588 /** 589 * Called after a value is assigned to a variable. 590 * @param assignment current assignment 591 * @param iteration current iteration 592 * @param value value that was assigned 593 */ 594 public void afterAssigned(Assignment<Request, Enrollment> assignment, long iteration, Enrollment value) { 595 iOldVariable = null; 596 iUnassignedValue = null; 597 if (value != null) 598 assigned(assignment, value); 599 } 600 601 /** 602 * Called after a value is unassigned from a variable. 603 * @param assignment current assignment 604 * @param iteration current iteration 605 * @param value value to be unassigned 606 */ 607 public void afterUnassigned(Assignment<Request, Enrollment> assignment, long iteration, Enrollment value) { 608 if (value != null && !value.equals(iUnassignedValue)) 609 unassigned(assignment, value); 610 } 611 612 /** 613 * Called when a value is assigned to a variable. Internal number of 614 * distance conflicts is updated, see 615 * {@link DistanceConflict#getTotalNrConflicts(Assignment)}. 616 */ 617 @Override 618 public void assigned(Assignment<Request, Enrollment> assignment, Enrollment value) { 619 StudentSectioningModelContext cx = ((StudentSectioningModel)getModel()).getContext(assignment); 620 for (Conflict c: allConflicts(assignment, value)) { 621 if (iAllConflicts.add(c)) 622 cx.add(assignment, c); 623 } 624 if (sDebug) { 625 sLog.debug("A:" + value.variable() + " := " + value); 626 int inc = nrConflicts(value); 627 if (inc != 0) { 628 sLog.debug("-- DC+" + inc + " A: " + value.variable() + " := " + value); 629 for (Iterator<Conflict> i = allConflicts(assignment, value).iterator(); i.hasNext();) 630 sLog.debug(" -- " + i.next()); 631 } 632 } 633 } 634 635 /** 636 * Called when a value is unassigned from a variable. Internal number of 637 * distance conflicts is updated, see 638 * {@link DistanceConflict#getTotalNrConflicts(Assignment)}. 639 */ 640 @Override 641 public void unassigned(Assignment<Request, Enrollment> assignment, Enrollment value) { 642 if (value.variable().equals(iOldVariable)) 643 return; 644 StudentSectioningModelContext cx = ((StudentSectioningModel)getModel()).getContext(assignment); 645 for (Conflict c: allConflicts(assignment, value)) { 646 if (iAllConflicts.remove(c)) 647 cx.remove(assignment, c); 648 } 649 if (sDebug) { 650 sLog.debug("U:" + value.variable() + " := " + value); 651 int dec = nrAllConflicts(assignment, value); 652 if (dec != 0) { 653 sLog.debug("-- DC+" + dec + " U: " + value.variable() + " := " + value); 654 Set<Conflict> confs = allConflicts(assignment, value); 655 for (Iterator<Conflict> i = confs.iterator(); i.hasNext();) 656 sLog.debug(" -- " + i.next()); 657 } 658 } 659 } 660 661 /** Checks the counter counting all conflicts 662 * @param assignment current assignment 663 **/ 664 public void checkAllConflicts(Assignment<Request, Enrollment> assignment) { 665 Set<Conflict> allConfs = computeAllConflicts(assignment); 666 if (iAllConflicts.size() != allConfs.size()) { 667 sLog.error("Different number of conflicts " + iAllConflicts.size() + "!=" + allConfs.size()); 668 for (Iterator<Conflict> i = allConfs.iterator(); i.hasNext();) { 669 Conflict c = i.next(); 670 if (!iAllConflicts.contains(c)) 671 sLog.debug(" +add+ " + c); 672 } 673 for (Iterator<Conflict> i = iAllConflicts.iterator(); i.hasNext();) { 674 Conflict c = i.next(); 675 if (!allConfs.contains(c)) 676 sLog.debug(" -rem- " + c); 677 } 678 iAllConflicts = allConfs; 679 } 680 } 681 682 /** Actual number of all distance conflicts 683 * @return number of all distance conflicts 684 **/ 685 public int getTotalNrConflicts() { 686 return iAllConflicts.size(); 687 } 688 689 /** Actual number of all distance conflicts of students that need short distances 690 * @return number of all distance conflicts 691 **/ 692 public int getTotalNrShortConflicts() { 693 int ret = 0; 694 for (Conflict c: iAllConflicts) 695 if (c.getStudent().isNeedShortDistances()) ret ++; 696 return ret; 697 } 698 699 700 /** 701 * Return a set of all distance conflicts ({@link Conflict} objects). 702 * @return all distance conflicts 703 */ 704 public Set<Conflict> getAllConflicts() { 705 return iAllConflicts; 706 } 707 708 /** 709 * Total sum of all conflict of the given enrollment and other enrollments 710 * that are assigned to the same student. 711 * @param assignment current assignment 712 * @param enrollment given enrollment 713 * @return number of all conflict of the given enrollment 714 */ 715 public int nrAllConflicts(Assignment<Request, Enrollment> assignment, Enrollment enrollment) { 716 if (!enrollment.isCourseRequest()) 717 return 0; 718 int cnt = nrConflicts(enrollment); 719 Request old = iOldVariable; 720 for (Request request : enrollment.getStudent().getRequests()) { 721 if (request.equals(enrollment.getRequest()) || assignment.getValue(request) == null || request.equals(old)) 722 continue; 723 cnt += nrConflicts(enrollment, assignment.getValue(request)); 724 } 725 return cnt; 726 } 727 } 728 729 @Override 730 public DistanceConflictContext createAssignmentContext(Assignment<Request, Enrollment> assignment) { 731 return new DistanceConflictContext(assignment); 732 } 733 734 @Override 735 public DistanceConflictContext inheritAssignmentContext(Assignment<Request, Enrollment> assignment, DistanceConflictContext parentContext) { 736 return new DistanceConflictContext(parentContext); 737 } 738}