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