001 package net.sf.cpsolver.coursett; 002 003 import java.io.File; 004 import java.text.SimpleDateFormat; 005 import java.util.ArrayList; 006 import java.util.BitSet; 007 import java.util.Calendar; 008 import java.util.Date; 009 import java.util.HashSet; 010 import java.util.HashMap; 011 import java.util.Hashtable; 012 import java.util.Iterator; 013 import java.util.List; 014 import java.util.Locale; 015 import java.util.Map; 016 import java.util.Set; 017 018 import net.sf.cpsolver.coursett.constraint.ClassLimitConstraint; 019 import net.sf.cpsolver.coursett.constraint.DepartmentSpreadConstraint; 020 import net.sf.cpsolver.coursett.constraint.DiscouragedRoomConstraint; 021 import net.sf.cpsolver.coursett.constraint.FlexibleConstraint.FlexibleConstraintType; 022 import net.sf.cpsolver.coursett.constraint.GroupConstraint; 023 import net.sf.cpsolver.coursett.constraint.IgnoreStudentConflictsConstraint; 024 import net.sf.cpsolver.coursett.constraint.InstructorConstraint; 025 import net.sf.cpsolver.coursett.constraint.JenrlConstraint; 026 import net.sf.cpsolver.coursett.constraint.MinimizeNumberOfUsedGroupsOfTime; 027 import net.sf.cpsolver.coursett.constraint.MinimizeNumberOfUsedRoomsConstraint; 028 import net.sf.cpsolver.coursett.constraint.RoomConstraint; 029 import net.sf.cpsolver.coursett.constraint.SpreadConstraint; 030 import net.sf.cpsolver.coursett.model.Configuration; 031 import net.sf.cpsolver.coursett.model.InitialSectioning; 032 import net.sf.cpsolver.coursett.model.Lecture; 033 import net.sf.cpsolver.coursett.model.Placement; 034 import net.sf.cpsolver.coursett.model.RoomLocation; 035 import net.sf.cpsolver.coursett.model.RoomSharingModel; 036 import net.sf.cpsolver.coursett.model.Student; 037 import net.sf.cpsolver.coursett.model.TimeLocation; 038 import net.sf.cpsolver.coursett.model.TimetableModel; 039 import net.sf.cpsolver.ifs.model.Constraint; 040 import net.sf.cpsolver.ifs.solution.Solution; 041 import net.sf.cpsolver.ifs.solver.Solver; 042 import net.sf.cpsolver.ifs.util.Progress; 043 import net.sf.cpsolver.ifs.util.ToolBox; 044 045 import org.dom4j.Document; 046 import org.dom4j.Element; 047 import org.dom4j.io.SAXReader; 048 049 /** 050 * This class loads the input model from XML file. <br> 051 * <br> 052 * Parameters: 053 * <table border='1'> 054 * <tr> 055 * <th>Parameter</th> 056 * <th>Type</th> 057 * <th>Comment</th> 058 * </tr> 059 * <tr> 060 * <td>General.Input</td> 061 * <td>{@link String}</td> 062 * <td>Input XML file</td> 063 * </tr> 064 * <tr> 065 * <td>General.DeptBalancing</td> 066 * <td>{@link Boolean}</td> 067 * <td>Use {@link DepartmentSpreadConstraint}</td> 068 * </tr> 069 * <tr> 070 * <td>General.InteractiveMode</td> 071 * <td>{@link Boolean}</td> 072 * <td>Interactive mode (see {@link Lecture#purgeInvalidValues(boolean)})</td> 073 * </tr> 074 * <tr> 075 * <td>General.ForcedPerturbances</td> 076 * <td>{@link Integer}</td> 077 * <td>For testing of MPP: number of input perturbations, i.e., classes with 078 * prohibited intial assignment</td> 079 * </tr> 080 * <tr> 081 * <td>General.UseDistanceConstraints</td> 082 * <td>{@link Boolean}</td> 083 * <td>Consider distances between buildings</td> 084 * </tr> 085 * </table> 086 * 087 * @version CourseTT 1.2 (University Course Timetabling)<br> 088 * Copyright (C) 2006 - 2010 Tomas Muller<br> 089 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 090 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 091 * <br> 092 * This library is free software; you can redistribute it and/or modify 093 * it under the terms of the GNU Lesser General Public License as 094 * published by the Free Software Foundation; either version 3 of the 095 * License, or (at your option) any later version. <br> 096 * <br> 097 * This library is distributed in the hope that it will be useful, but 098 * WITHOUT ANY WARRANTY; without even the implied warranty of 099 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 100 * Lesser General Public License for more details. <br> 101 * <br> 102 * You should have received a copy of the GNU Lesser General Public 103 * License along with this library; if not see 104 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 105 */ 106 107 public class TimetableXMLLoader extends TimetableLoader { 108 private static org.apache.log4j.Logger sLogger = org.apache.log4j.Logger.getLogger(TimetableXMLLoader.class); 109 private static SimpleDateFormat sDF = new SimpleDateFormat("MM/dd"); 110 111 private boolean iDeptBalancing = true; 112 private int iForcedPerturbances = 0; 113 114 private boolean iInteractiveMode = false; 115 private File iInputFile; 116 117 private Progress iProgress = null; 118 119 public TimetableXMLLoader(TimetableModel model) { 120 super(model); 121 iProgress = Progress.getInstance(getModel()); 122 iInputFile = new File(getModel().getProperties().getProperty("General.Input", 123 "." + File.separator + "solution.xml")); 124 iForcedPerturbances = getModel().getProperties().getPropertyInt("General.ForcedPerturbances", 0); 125 iDeptBalancing = getModel().getProperties().getPropertyBoolean("General.DeptBalancing", true); 126 iInteractiveMode = getModel().getProperties().getPropertyBoolean("General.InteractiveMode", iInteractiveMode); 127 } 128 129 private Solver<Lecture, Placement> iSolver = null; 130 131 public void setSolver(Solver<Lecture, Placement> solver) { 132 iSolver = solver; 133 } 134 135 public Solver<Lecture, Placement> getSolver() { 136 return iSolver; 137 } 138 139 public void setInputFile(File inputFile) { 140 iInputFile = inputFile; 141 } 142 143 @Override 144 public void load() throws Exception { 145 load(null); 146 } 147 148 public void load(Solution<Lecture, Placement> currentSolution) throws Exception { 149 sLogger.debug("Reading XML data from " + iInputFile); 150 iProgress.setPhase("Reading " + iInputFile.getName() + " ..."); 151 152 Document document = (new SAXReader()).read(iInputFile); 153 Element root = document.getRootElement(); 154 sLogger.debug("Root element: " + root.getName()); 155 if (!"llrt".equals(root.getName()) && !"timetable".equals(root.getName())) { 156 sLogger.error("Given XML file is not large lecture room timetabling problem."); 157 return; 158 } 159 160 iProgress.load(root, true); 161 iProgress.message(Progress.MSGLEVEL_STAGE, "Restoring from backup ..."); 162 163 if (root.element("input") != null) 164 root = root.element("input"); 165 166 if (root.attributeValue("term") != null) 167 getModel().getProperties().setProperty("Data.Term", root.attributeValue("term")); 168 if (root.attributeValue("year") != null) 169 getModel().setYear(Integer.parseInt(root.attributeValue("year"))); 170 else if (root.attributeValue("term") != null) 171 getModel().setYear(Integer.parseInt(root.attributeValue("term").substring(0, 4))); 172 if (root.attributeValue("initiative") != null) 173 getModel().getProperties().setProperty("Data.Initiative", root.attributeValue("initiative")); 174 if (root.attributeValue("semester") != null && root.attributeValue("year") != null) 175 getModel().getProperties().setProperty("Data.Term", 176 root.attributeValue("semester") + root.attributeValue("year")); 177 if (root.attributeValue("session") != null) 178 getModel().getProperties().setProperty("General.SessionId", root.attributeValue("session")); 179 if (root.attributeValue("solverGroup") != null) 180 getModel().getProperties().setProperty("General.SolverGroupId", root.attributeValue("solverGroup")); 181 String version = root.attributeValue("version"); 182 183 // Student sectioning considers the whole course (including committed classes), since 2.5 184 boolean sectionWholeCourse = true; 185 186 if (version != null && version.indexOf('.') >= 0) { 187 int majorVersion = Integer.parseInt(version.substring(0, version.indexOf('.'))); 188 int minorVersion = Integer.parseInt(version.substring(1 + version.indexOf('.'))); 189 190 sectionWholeCourse = (majorVersion == 2 && minorVersion >= 5) || majorVersion > 2; 191 } 192 193 HashMap<Long, TimeLocation> perts = new HashMap<Long, TimeLocation>(); 194 if (getModel().getProperties().getPropertyInt("MPP.TimePert", 0) > 0) { 195 int nrChanges = getModel().getProperties().getPropertyInt("MPP.TimePert", 0); 196 int idx = 0; 197 for (Iterator<?> i = root.element("perturbations").elementIterator("class"); i.hasNext() && idx < nrChanges; idx++) { 198 Element pertEl = (Element) i.next(); 199 Long classId = Long.valueOf(pertEl.attributeValue("id")); 200 TimeLocation tl = new TimeLocation(Integer.parseInt(pertEl.attributeValue("days"), 2), Integer 201 .parseInt(pertEl.attributeValue("start")), Integer.parseInt(pertEl.attributeValue("length")), 202 0, 0.0, 0, null, null, null, 0); 203 perts.put(classId, tl); 204 } 205 } 206 207 iProgress.setPhase("Creating rooms ...", root.element("rooms").elements("room").size()); 208 HashMap<String, Element> roomElements = new HashMap<String, Element>(); 209 HashMap<String, RoomConstraint> roomConstraints = new HashMap<String, RoomConstraint>(); 210 HashMap<Long, List<Lecture>> sameLectures = new HashMap<Long, List<Lecture>>(); 211 for (Iterator<?> i = root.element("rooms").elementIterator("room"); i.hasNext();) { 212 Element roomEl = (Element) i.next(); 213 iProgress.incProgress(); 214 roomElements.put(roomEl.attributeValue("id"), roomEl); 215 if ("false".equals(roomEl.attributeValue("constraint"))) 216 continue; 217 RoomSharingModel sharingModel = null; 218 Element sharingEl = roomEl.element("sharing"); 219 if (sharingEl != null) { 220 Character freeForAllPrefChar = null; 221 Element freeForAllEl = sharingEl.element("freeForAll"); 222 if (freeForAllEl != null) 223 freeForAllPrefChar = freeForAllEl.attributeValue("value", "F").charAt(0); 224 Character notAvailablePrefChar = null; 225 Element notAvailableEl = sharingEl.element("notAvailable"); 226 if (notAvailableEl != null) 227 notAvailablePrefChar = notAvailableEl.attributeValue("value", "X").charAt(0); 228 String pattern = sharingEl.element("pattern").getText(); 229 int unit = Integer.parseInt(sharingEl.element("pattern").attributeValue("unit", "1")); 230 java.util.List<?> depts = sharingEl.elements("department"); 231 Long departmentIds[] = new Long[depts.size()]; 232 for (int j = 0; j < departmentIds.length; j++) 233 departmentIds[j] = Long.valueOf(((Element) depts.get(j)).attributeValue("id")); 234 sharingModel = new RoomSharingModel(unit, departmentIds, pattern, freeForAllPrefChar, notAvailablePrefChar); 235 } 236 boolean ignoreTooFar = false; 237 if ("true".equals(roomEl.attributeValue("ignoreTooFar"))) 238 ignoreTooFar = true; 239 boolean fake = false; 240 if ("true".equals(roomEl.attributeValue("fake"))) 241 fake = true; 242 Double posX = null, posY = null; 243 if (roomEl.attributeValue("location") != null) { 244 String loc = roomEl.attributeValue("location"); 245 posX = Double.valueOf(loc.substring(0, loc.indexOf(','))); 246 posY = Double.valueOf(loc.substring(loc.indexOf(',') + 1)); 247 } 248 boolean discouraged = "true".equals(roomEl.attributeValue("discouraged")); 249 RoomConstraint constraint = (discouraged ? new DiscouragedRoomConstraint( 250 getModel().getProperties(), 251 Long.valueOf(roomEl.attributeValue("id")), 252 (roomEl.attributeValue("name") != null ? roomEl.attributeValue("name") : "r" 253 + roomEl.attributeValue("id")), 254 (roomEl.attributeValue("building") == null ? null : Long.valueOf(roomEl.attributeValue("building"))), 255 Integer.parseInt(roomEl.attributeValue("capacity")), sharingModel, posX, posY, ignoreTooFar, !fake) 256 : new RoomConstraint(Long.valueOf(roomEl.attributeValue("id")), 257 (roomEl.attributeValue("name") != null ? roomEl.attributeValue("name") : "r" 258 + roomEl.attributeValue("id")), (roomEl.attributeValue("building") == null ? null 259 : Long.valueOf(roomEl.attributeValue("building"))), Integer.parseInt(roomEl 260 .attributeValue("capacity")), sharingModel, posX, posY, ignoreTooFar, !fake)); 261 if (roomEl.attributeValue("type") != null) 262 constraint.setType(Long.valueOf(roomEl.attributeValue("type"))); 263 getModel().addConstraint(constraint); 264 roomConstraints.put(roomEl.attributeValue("id"), constraint); 265 266 for (Iterator<?> j = roomEl.elementIterator("travel-time"); j.hasNext();) { 267 Element travelTimeEl = (Element)j.next(); 268 getModel().getDistanceMetric().addTravelTime(constraint.getResourceId(), 269 Long.valueOf(travelTimeEl.attributeValue("id")), 270 Integer.valueOf(travelTimeEl.attributeValue("minutes"))); 271 } 272 } 273 274 HashMap<String, InstructorConstraint> instructorConstraints = new HashMap<String, InstructorConstraint>(); 275 if (root.element("instructors") != null) { 276 for (Iterator<?> i = root.element("instructors").elementIterator("instructor"); i.hasNext();) { 277 Element instructorEl = (Element) i.next(); 278 InstructorConstraint instructorConstraint = new InstructorConstraint(Long.valueOf(instructorEl 279 .attributeValue("id")), instructorEl.attributeValue("puid"), (instructorEl 280 .attributeValue("name") != null ? instructorEl.attributeValue("name") : "i" 281 + instructorEl.attributeValue("id")), "true".equals(instructorEl.attributeValue("ignDist"))); 282 if (instructorEl.attributeValue("type") != null) 283 instructorConstraint.setType(Long.valueOf(instructorEl.attributeValue("type"))); 284 instructorConstraints.put(instructorEl.attributeValue("id"), instructorConstraint); 285 286 getModel().addConstraint(instructorConstraint); 287 } 288 } 289 HashMap<Long, String> depts = new HashMap<Long, String>(); 290 if (root.element("departments") != null) { 291 for (Iterator<?> i = root.element("departments").elementIterator("department"); i.hasNext();) { 292 Element deptEl = (Element) i.next(); 293 depts.put(Long.valueOf(deptEl.attributeValue("id")), (deptEl.attributeValue("name") != null ? deptEl 294 .attributeValue("name") : "d" + deptEl.attributeValue("id"))); 295 } 296 } 297 298 HashMap<Long, Configuration> configs = new HashMap<Long, Configuration>(); 299 HashMap<Long, List<Configuration>> alternativeConfigurations = new HashMap<Long, List<Configuration>>(); 300 if (root.element("configurations") != null) { 301 for (Iterator<?> i = root.element("configurations").elementIterator("config"); i.hasNext();) { 302 Element configEl = (Element) i.next(); 303 Long configId = Long.valueOf(configEl.attributeValue("id")); 304 int limit = Integer.parseInt(configEl.attributeValue("limit")); 305 Long offeringId = Long.valueOf(configEl.attributeValue("offering")); 306 Configuration config = new Configuration(offeringId, configId, limit); 307 configs.put(configId, config); 308 List<Configuration> altConfigs = alternativeConfigurations.get(offeringId); 309 if (altConfigs == null) { 310 altConfigs = new ArrayList<Configuration>(); 311 alternativeConfigurations.put(offeringId, altConfigs); 312 } 313 altConfigs.add(config); 314 config.setAltConfigurations(altConfigs); 315 } 316 } 317 318 iProgress.setPhase("Creating variables ...", root.element("classes").elements("class").size()); 319 320 HashMap<String, Element> classElements = new HashMap<String, Element>(); 321 HashMap<String, Lecture> lectures = new HashMap<String, Lecture>(); 322 HashMap<Lecture, Placement> assignedPlacements = new HashMap<Lecture, Placement>(); 323 HashMap<Lecture, String> parents = new HashMap<Lecture, String>(); 324 int ord = 0; 325 for (Iterator<?> i1 = root.element("classes").elementIterator("class"); i1.hasNext();) { 326 Element classEl = (Element) i1.next(); 327 328 Configuration config = null; 329 if (classEl.attributeValue("config") != null) { 330 config = configs.get(Long.valueOf(classEl.attributeValue("config"))); 331 } 332 if (config == null && classEl.attributeValue("offering") != null) { 333 Long offeringId = Long.valueOf(classEl.attributeValue("offering")); 334 Long configId = Long.valueOf(classEl.attributeValue("config")); 335 List<Configuration> altConfigs = alternativeConfigurations.get(offeringId); 336 if (altConfigs == null) { 337 altConfigs = new ArrayList<Configuration>(); 338 alternativeConfigurations.put(offeringId, altConfigs); 339 } 340 for (Configuration c : altConfigs) { 341 if (c.getConfigId().equals(configId)) { 342 config = c; 343 break; 344 } 345 } 346 if (config == null) { 347 config = new Configuration(offeringId, configId, -1); 348 altConfigs.add(config); 349 config.setAltConfigurations(altConfigs); 350 } 351 } 352 353 DatePattern defaultDatePattern = new DatePattern(); 354 if (classEl.attributeValue("dates") == null) { 355 int startDay = Integer.parseInt(classEl.attributeValue("startDay", "0")); 356 int endDay = Integer.parseInt(classEl.attributeValue("endDay", "1")); 357 defaultDatePattern.setPattern(startDay, endDay); 358 defaultDatePattern.setName(sDF.format(getDate(getModel().getYear(), startDay)) + "-" + sDF.format(getDate(getModel().getYear(), endDay))); 359 } else { 360 defaultDatePattern.setId(classEl.attributeValue("datePattern") == null ? null : Long.valueOf(classEl.attributeValue("datePattern"))); 361 defaultDatePattern.setName(classEl.attributeValue("datePatternName")); 362 defaultDatePattern.setPattern(classEl.attributeValue("dates")); 363 } 364 Hashtable<Long, DatePattern> datePatterns = new Hashtable<Long, TimetableXMLLoader.DatePattern>(); 365 for (Iterator<?> i2 = classEl.elementIterator("date"); i2.hasNext();) { 366 Element dateEl = (Element) i2.next(); 367 Long id = Long.valueOf(dateEl.attributeValue("id")); 368 datePatterns.put(id, new DatePattern( 369 id, 370 dateEl.attributeValue("name"), 371 dateEl.attributeValue("pattern"))); 372 } 373 classElements.put(classEl.attributeValue("id"), classEl); 374 List<InstructorConstraint> ics = new ArrayList<InstructorConstraint>(); 375 for (Iterator<?> i2 = classEl.elementIterator("instructor"); i2.hasNext();) { 376 Element instructorEl = (Element) i2.next(); 377 InstructorConstraint instructorConstraint = instructorConstraints 378 .get(instructorEl.attributeValue("id")); 379 if (instructorConstraint == null) { 380 instructorConstraint = new InstructorConstraint(Long.valueOf(instructorEl.attributeValue("id")), 381 instructorEl.attributeValue("puid"), 382 (instructorEl.attributeValue("name") != null ? instructorEl.attributeValue("name") : "i" 383 + instructorEl.attributeValue("id")), "true".equals(instructorEl 384 .attributeValue("ignDist"))); 385 instructorConstraints.put(instructorEl.attributeValue("id"), instructorConstraint); 386 getModel().addConstraint(instructorConstraint); 387 } 388 ics.add(instructorConstraint); 389 } 390 List<RoomLocation> roomLocations = new ArrayList<RoomLocation>(); 391 List<RoomConstraint> roomConstraintsThisClass = new ArrayList<RoomConstraint>(); 392 List<RoomLocation> initialRoomLocations = new ArrayList<RoomLocation>(); 393 List<RoomLocation> assignedRoomLocations = new ArrayList<RoomLocation>(); 394 List<RoomLocation> bestRoomLocations = new ArrayList<RoomLocation>(); 395 for (Iterator<?> i2 = classEl.elementIterator("room"); i2.hasNext();) { 396 Element roomLocationEl = (Element) i2.next(); 397 Element roomEl = roomElements.get(roomLocationEl.attributeValue("id")); 398 RoomConstraint roomConstraint = roomConstraints.get(roomLocationEl.attributeValue("id")); 399 400 Long roomId = null; 401 String roomName = null; 402 Long bldgId = null; 403 404 if (roomConstraint != null) { 405 roomConstraintsThisClass.add(roomConstraint); 406 roomId = roomConstraint.getResourceId(); 407 roomName = roomConstraint.getRoomName(); 408 bldgId = roomConstraint.getBuildingId(); 409 } else { 410 roomId = Long.valueOf(roomEl.attributeValue("id")); 411 roomName = (roomEl.attributeValue("name") != null ? roomEl.attributeValue("name") : "r" 412 + roomEl.attributeValue("id")); 413 bldgId = (roomEl.attributeValue("building") == null ? null : Long.valueOf(roomEl 414 .attributeValue("building"))); 415 } 416 417 boolean ignoreTooFar = false; 418 if ("true".equals(roomEl.attributeValue("ignoreTooFar"))) 419 ignoreTooFar = true; 420 Double posX = null, posY = null; 421 if (roomEl.attributeValue("location") != null) { 422 String loc = roomEl.attributeValue("location"); 423 posX = Double.valueOf(loc.substring(0, loc.indexOf(','))); 424 posY = Double.valueOf(loc.substring(loc.indexOf(',') + 1)); 425 } 426 RoomLocation rl = new RoomLocation(roomId, roomName, bldgId, Integer.parseInt(roomLocationEl 427 .attributeValue("pref")), Integer.parseInt(roomEl.attributeValue("capacity")), posX, posY, 428 ignoreTooFar, roomConstraint); 429 if ("true".equals(roomLocationEl.attributeValue("initial"))) 430 initialRoomLocations.add(rl); 431 if ("true".equals(roomLocationEl.attributeValue("solution"))) 432 assignedRoomLocations.add(rl); 433 if ("true".equals(roomLocationEl.attributeValue("best"))) 434 bestRoomLocations.add(rl); 435 roomLocations.add(rl); 436 } 437 List<TimeLocation> timeLocations = new ArrayList<TimeLocation>(); 438 TimeLocation initialTimeLocation = null; 439 TimeLocation assignedTimeLocation = null; 440 TimeLocation bestTimeLocation = null; 441 TimeLocation prohibitedTime = perts.get(Long.valueOf(classEl.attributeValue("id"))); 442 443 for (Iterator<?> i2 = classEl.elementIterator("time"); i2.hasNext();) { 444 Element timeLocationEl = (Element) i2.next(); 445 DatePattern dp = defaultDatePattern; 446 if (timeLocationEl.attributeValue("date") != null) 447 dp = datePatterns.get(Long.valueOf(timeLocationEl.attributeValue("date"))); 448 TimeLocation tl = new TimeLocation( 449 Integer.parseInt(timeLocationEl.attributeValue("days"), 2), 450 Integer.parseInt(timeLocationEl.attributeValue("start")), 451 Integer.parseInt(timeLocationEl.attributeValue("length")), 452 (int) Double.parseDouble(timeLocationEl.attributeValue("pref")), 453 Double.parseDouble(timeLocationEl.attributeValue("npref", timeLocationEl.attributeValue("pref"))), 454 Integer.parseInt(timeLocationEl.attributeValue("datePref", "0")), 455 dp.getId(), dp.getName(), dp.getPattern(), 456 Integer.parseInt(timeLocationEl.attributeValue("breakTime") == null ? "-1" : timeLocationEl.attributeValue("breakTime"))); 457 if (tl.getBreakTime() < 0) tl.setBreakTime(tl.getLength() == 18 ? 15 : 10); 458 if (timeLocationEl.attributeValue("pattern") != null) 459 tl.setTimePatternId(Long.valueOf(timeLocationEl.attributeValue("pattern"))); 460 /* 461 * if (timePatternTransform) tl = 462 * transformTimePattern(Long.valueOf 463 * (classEl.attributeValue("id")),tl); 464 */ 465 if (prohibitedTime != null && prohibitedTime.getDayCode() == tl.getDayCode() 466 && prohibitedTime.getStartSlot() == tl.getStartSlot() 467 && prohibitedTime.getLength() == tl.getLength()) { 468 sLogger.info("Time " + tl.getLongName() + " is prohibited for class " 469 + classEl.attributeValue("id")); 470 continue; 471 } 472 if ("true".equals(timeLocationEl.attributeValue("solution"))) 473 assignedTimeLocation = tl; 474 if ("true".equals(timeLocationEl.attributeValue("initial"))) 475 initialTimeLocation = tl; 476 if ("true".equals(timeLocationEl.attributeValue("best"))) 477 bestTimeLocation = tl; 478 timeLocations.add(tl); 479 } 480 if (timeLocations.isEmpty()) { 481 sLogger.error(" ERROR: No time."); 482 continue; 483 } 484 485 int minClassLimit = 0; 486 int maxClassLimit = 0; 487 float room2limitRatio = 1.0f; 488 if (!"true".equals(classEl.attributeValue("committed"))) { 489 if (classEl.attributeValue("expectedCapacity") != null) { 490 minClassLimit = maxClassLimit = Integer.parseInt(classEl.attributeValue("expectedCapacity")); 491 int roomCapacity = Integer.parseInt(classEl.attributeValue("roomCapacity", classEl 492 .attributeValue("expectedCapacity"))); 493 if (minClassLimit == 0) 494 minClassLimit = maxClassLimit = roomCapacity; 495 room2limitRatio = (minClassLimit == 0 ? 1.0f : ((float) roomCapacity) / minClassLimit); 496 } else { 497 if (classEl.attribute("classLimit") != null) { 498 minClassLimit = maxClassLimit = Integer.parseInt(classEl.attributeValue("classLimit")); 499 } else { 500 minClassLimit = Integer.parseInt(classEl.attributeValue("minClassLimit")); 501 maxClassLimit = Integer.parseInt(classEl.attributeValue("maxClassLimit")); 502 } 503 room2limitRatio = Float.parseFloat(classEl.attributeValue("roomToLimitRatio", "1.0")); 504 } 505 } 506 507 Lecture lecture = new Lecture(Long.valueOf(classEl.attributeValue("id")), 508 (classEl.attributeValue("solverGroup") != null ? Long 509 .valueOf(classEl.attributeValue("solverGroup")) : null), Long.valueOf(classEl 510 .attributeValue("subpart", classEl.attributeValue("course", "-1"))), (classEl 511 .attributeValue("name") != null ? classEl.attributeValue("name") : "c" 512 + classEl.attributeValue("id")), timeLocations, roomLocations, Integer.parseInt(classEl 513 .attributeValue("nrRooms", roomLocations.isEmpty() ? "0" : "1")), null, minClassLimit, maxClassLimit, room2limitRatio); 514 lecture.setNote(classEl.attributeValue("note")); 515 516 if ("true".equals(classEl.attributeValue("committed"))) 517 lecture.setCommitted(true); 518 519 if (!lecture.isCommitted() && classEl.attributeValue("ord") != null) 520 lecture.setOrd(Integer.parseInt(classEl.attributeValue("ord"))); 521 else 522 lecture.setOrd(ord++); 523 524 if (config != null) 525 lecture.setConfiguration(config); 526 527 if (initialTimeLocation != null && initialRoomLocations.size() == lecture.getNrRooms()) { 528 lecture.setInitialAssignment(new Placement(lecture, initialTimeLocation, initialRoomLocations)); 529 } 530 if (assignedTimeLocation != null && assignedRoomLocations.size() == lecture.getNrRooms()) { 531 assignedPlacements.put(lecture, new Placement(lecture, assignedTimeLocation, assignedRoomLocations)); 532 } else if (lecture.getInitialAssignment() != null) { 533 assignedPlacements.put(lecture, lecture.getInitialAssignment()); 534 } 535 if (bestTimeLocation != null && bestRoomLocations.size() == lecture.getNrRooms()) { 536 lecture.setBestAssignment(new Placement(lecture, bestTimeLocation, bestRoomLocations)); 537 } else if (assignedTimeLocation != null && assignedRoomLocations.size() == lecture.getNrRooms()) { 538 lecture.setBestAssignment(assignedPlacements.get(lecture)); 539 } 540 541 lectures.put(classEl.attributeValue("id"), lecture); 542 if (classEl.attributeValue("department") != null) 543 lecture.setDepartment(Long.valueOf(classEl.attributeValue("department"))); 544 if (classEl.attribute("scheduler") != null) 545 lecture.setScheduler(Long.valueOf(classEl.attributeValue("scheduler"))); 546 if ((sectionWholeCourse || !lecture.isCommitted()) && classEl.attributeValue("subpart", classEl.attributeValue("course")) != null) { 547 Long subpartId = Long.valueOf(classEl.attributeValue("subpart", classEl.attributeValue("course"))); 548 List<Lecture> sames = sameLectures.get(subpartId); 549 if (sames == null) { 550 sames = new ArrayList<Lecture>(); 551 sameLectures.put(subpartId, sames); 552 } 553 sames.add(lecture); 554 } 555 String parent = classEl.attributeValue("parent"); 556 if (parent != null) 557 parents.put(lecture, parent); 558 559 getModel().addVariable(lecture); 560 561 if (lecture.isCommitted()) { 562 Placement placement = assignedPlacements.get(lecture); 563 if (classEl.attribute("assignment") != null) 564 placement.setAssignmentId(Long.valueOf(classEl.attributeValue("assignment"))); 565 for (InstructorConstraint ic : ics) 566 ic.setNotAvailable(placement); 567 for (RoomConstraint rc : roomConstraintsThisClass) 568 rc.setNotAvailable(placement); 569 } else { 570 for (InstructorConstraint ic : ics) 571 ic.addVariable(lecture); 572 for (RoomConstraint rc : roomConstraintsThisClass) 573 rc.addVariable(lecture); 574 } 575 576 iProgress.incProgress(); 577 } 578 579 for (Map.Entry<Lecture, String> entry : parents.entrySet()) { 580 Lecture lecture = entry.getKey(); 581 Lecture parent = lectures.get(entry.getValue()); 582 if (parent == null) { 583 iProgress.warn("Parent class " + entry.getValue() + " does not exists."); 584 } else { 585 lecture.setParent(parent); 586 } 587 } 588 589 iProgress.setPhase("Creating constraints ...", root.element("groupConstraints").elements("constraint").size()); 590 HashMap<String, Element> grConstraintElements = new HashMap<String, Element>(); 591 HashMap<String, Constraint<Lecture, Placement>> groupConstraints = new HashMap<String, Constraint<Lecture, Placement>>(); 592 for (Iterator<?> i1 = root.element("groupConstraints").elementIterator("constraint"); i1.hasNext();) { 593 Element grConstraintEl = (Element) i1.next(); 594 Constraint<Lecture, Placement> c = null; 595 if ("SPREAD".equals(grConstraintEl.attributeValue("type"))) { 596 c = new SpreadConstraint(getModel().getProperties(), grConstraintEl.attributeValue("name", "spread")); 597 } else if ("MIN_ROOM_USE".equals(grConstraintEl.attributeValue("type"))) { 598 c = new MinimizeNumberOfUsedRoomsConstraint(getModel().getProperties()); 599 } else if ("CLASS_LIMIT".equals(grConstraintEl.attributeValue("type"))) { 600 if (grConstraintEl.element("parentClass") == null) { 601 c = new ClassLimitConstraint(Integer.parseInt(grConstraintEl.attributeValue("courseLimit")), 602 grConstraintEl.attributeValue("name", "class-limit")); 603 } else { 604 String classId = grConstraintEl.element("parentClass").attributeValue("id"); 605 c = new ClassLimitConstraint(lectures.get(classId), grConstraintEl.attributeValue("name", 606 "class-limit")); 607 } 608 if (grConstraintEl.attributeValue("delta") != null) 609 ((ClassLimitConstraint) c).setClassLimitDelta(Integer.parseInt(grConstraintEl 610 .attributeValue("delta"))); 611 } else if ("MIN_GRUSE(10x1h)".equals(grConstraintEl.attributeValue("type"))) { 612 c = new MinimizeNumberOfUsedGroupsOfTime(getModel().getProperties(), "10x1h", 613 MinimizeNumberOfUsedGroupsOfTime.sGroups10of1h); 614 } else if ("MIN_GRUSE(5x2h)".equals(grConstraintEl.attributeValue("type"))) { 615 c = new MinimizeNumberOfUsedGroupsOfTime(getModel().getProperties(), "5x2h", 616 MinimizeNumberOfUsedGroupsOfTime.sGroups5of2h); 617 } else if ("MIN_GRUSE(3x3h)".equals(grConstraintEl.attributeValue("type"))) { 618 c = new MinimizeNumberOfUsedGroupsOfTime(getModel().getProperties(), "3x3h", 619 MinimizeNumberOfUsedGroupsOfTime.sGroups3of3h); 620 } else if ("MIN_GRUSE(2x5h)".equals(grConstraintEl.attributeValue("type"))) { 621 c = new MinimizeNumberOfUsedGroupsOfTime(getModel().getProperties(), "2x5h", 622 MinimizeNumberOfUsedGroupsOfTime.sGroups2of5h); 623 } else if (IgnoreStudentConflictsConstraint.REFERENCE.equals(grConstraintEl.attributeValue("type"))) { 624 c = new IgnoreStudentConflictsConstraint(); 625 } else { 626 try { 627 FlexibleConstraintType f = FlexibleConstraintType.valueOf(grConstraintEl.attributeValue("type")); 628 try { 629 c = f.create( 630 Long.valueOf(grConstraintEl.attributeValue("id")), 631 grConstraintEl.attributeValue("owner"), 632 grConstraintEl.attributeValue("pref"), 633 grConstraintEl.attributeValue("reference")); 634 } catch (IllegalArgumentException e) { 635 iProgress.warn("Failed to create flexible constraint " + grConstraintEl.attributeValue("type") + ": " + e.getMessage(), e); 636 continue; 637 } 638 } catch (IllegalArgumentException e) { 639 // type did not match, continue with group constraint types 640 c = new GroupConstraint( 641 Long.valueOf(grConstraintEl.attributeValue("id")), 642 GroupConstraint.ConstraintType.get(grConstraintEl.attributeValue("type")), 643 grConstraintEl.attributeValue("pref")); 644 } 645 } 646 getModel().addConstraint(c); 647 for (Iterator<?> i2 = grConstraintEl.elementIterator("class"); i2.hasNext();) { 648 String classId = ((Element) i2.next()).attributeValue("id"); 649 Lecture other = lectures.get(classId); 650 if (other != null) 651 c.addVariable(other); 652 else 653 iProgress.warn("Class " + classId + " does not exists, but it is referred from group constraint " + c.getId() + " (" + c.getName() + ")"); 654 } 655 grConstraintElements.put(grConstraintEl.attributeValue("id"), grConstraintEl); 656 groupConstraints.put(grConstraintEl.attributeValue("id"), c); 657 iProgress.incProgress(); 658 } 659 660 iProgress.setPhase("Loading students ...", root.element("students").elements("student").size()); 661 boolean initialSectioning = true; 662 HashMap<Long, Student> students = new HashMap<Long, Student>(); 663 HashMap<Long, Set<Student>> offering2students = new HashMap<Long, Set<Student>>(); 664 for (Iterator<?> i1 = root.element("students").elementIterator("student"); i1.hasNext();) { 665 Element studentEl = (Element) i1.next(); 666 List<Lecture> lecturesThisStudent = new ArrayList<Lecture>(); 667 Long studentId = Long.valueOf(studentEl.attributeValue("id")); 668 Student student = students.get(studentId); 669 if (student == null) { 670 student = new Student(studentId); 671 students.put(studentId, student); 672 getModel().addStudent(student); 673 } 674 student.setAcademicArea(studentEl.attributeValue("area")); 675 student.setAcademicClassification(studentEl.attributeValue("classification")); 676 student.setMajor(studentEl.attributeValue("major")); 677 student.setCurriculum(studentEl.attributeValue("curriculum")); 678 for (Iterator<?> i2 = studentEl.elementIterator("offering"); i2.hasNext();) { 679 Element ofEl = (Element) i2.next(); 680 Long offeringId = Long.valueOf(ofEl.attributeValue("id")); 681 String priority = ofEl.attributeValue("priority"); 682 student.addOffering(offeringId, Double.parseDouble(ofEl.attributeValue("weight", "1.0")), priority == null ? null : Double.valueOf(priority)); 683 Set<Student> studentsThisOffering = offering2students.get(offeringId); 684 if (studentsThisOffering == null) { 685 studentsThisOffering = new HashSet<Student>(); 686 offering2students.put(offeringId, studentsThisOffering); 687 } 688 studentsThisOffering.add(student); 689 } 690 for (Iterator<?> i2 = studentEl.elementIterator("class"); i2.hasNext();) { 691 String classId = ((Element) i2.next()).attributeValue("id"); 692 Lecture lecture = lectures.get(classId); 693 if (lecture == null) { 694 iProgress.warn("Class " + classId + " does not exists, but it is referred from student " + student.getId()); 695 continue; 696 } 697 if (lecture.isCommitted()) { 698 if (sectionWholeCourse && (lecture.getParent() != null || lecture.getConfiguration() != null)) { 699 // committed, but with course structure -- sectioning can be used 700 student.addLecture(lecture); 701 lecture.addStudent(student); 702 lecturesThisStudent.add(lecture); 703 initialSectioning = false; 704 } else { 705 Placement placement = assignedPlacements.get(lecture); 706 student.addCommitedPlacement(placement); 707 } 708 } else { 709 student.addLecture(lecture); 710 lecture.addStudent(student); 711 lecturesThisStudent.add(lecture); 712 initialSectioning = false; 713 } 714 } 715 716 for (Iterator<?> i2 = studentEl.elementIterator("prohibited-class"); i2.hasNext();) { 717 String classId = ((Element) i2.next()).attributeValue("id"); 718 Lecture lecture = lectures.get(classId); 719 if (lecture != null) 720 student.addCanNotEnroll(lecture); 721 else 722 iProgress.warn("Class " + classId + " does not exists, but it is referred from student " + student.getId()); 723 } 724 725 if (studentEl.attributeValue("instructor") != null) 726 student.setInstructor(instructorConstraints.get(studentEl.attributeValue("instructor"))); 727 728 iProgress.incProgress(); 729 } 730 731 for (List<Lecture> sames: sameLectures.values()) { 732 for (Lecture lect : sames) { 733 lect.setSameSubpartLectures(sames); 734 } 735 } 736 737 if (initialSectioning) { 738 iProgress.setPhase("Initial sectioning ...", offering2students.size()); 739 for (Map.Entry<Long, Set<Student>> entry : offering2students.entrySet()) { 740 Long offeringId = entry.getKey(); 741 Set<Student> studentsThisOffering = entry.getValue(); 742 List<Configuration> altConfigs = alternativeConfigurations.get(offeringId); 743 InitialSectioning.initialSectioningCfg(iProgress, offeringId, String.valueOf(offeringId), 744 studentsThisOffering, altConfigs); 745 iProgress.incProgress(); 746 } 747 for (Student student: students.values()) { 748 student.clearDistanceCache(); 749 if (student.getInstructor() != null) 750 for (Lecture lecture: student.getInstructor().variables()) { 751 student.addLecture(lecture); 752 lecture.addStudent(student); 753 } 754 } 755 } 756 757 iProgress.setPhase("Computing jenrl ...", students.size()); 758 HashMap<Lecture, HashMap<Lecture, JenrlConstraint>> jenrls = new HashMap<Lecture, HashMap<Lecture, JenrlConstraint>>(); 759 for (Iterator<Student> i1 = students.values().iterator(); i1.hasNext();) { 760 Student st = i1.next(); 761 for (Iterator<Lecture> i2 = st.getLectures().iterator(); i2.hasNext();) { 762 Lecture l1 = i2.next(); 763 for (Iterator<Lecture> i3 = st.getLectures().iterator(); i3.hasNext();) { 764 Lecture l2 = i3.next(); 765 if (l1.getId() >= l2.getId()) 766 continue; 767 HashMap<Lecture, JenrlConstraint> x = jenrls.get(l1); 768 if (x == null) { 769 x = new HashMap<Lecture, JenrlConstraint>(); 770 jenrls.put(l1, x); 771 } 772 JenrlConstraint jenrl = x.get(l2); 773 if (jenrl == null) { 774 jenrl = new JenrlConstraint(); 775 jenrl.addVariable(l1); 776 jenrl.addVariable(l2); 777 getModel().addConstraint(jenrl); 778 x.put(l2, jenrl); 779 } 780 jenrl.incJenrl(st); 781 } 782 } 783 iProgress.incProgress(); 784 } 785 786 if (iDeptBalancing) { 787 iProgress.setPhase("Creating dept. spread constraints ...", getModel().variables().size()); 788 HashMap<Long, DepartmentSpreadConstraint> depSpreadConstraints = new HashMap<Long, DepartmentSpreadConstraint>(); 789 for (Lecture lecture : getModel().variables()) { 790 if (lecture.getDepartment() == null) 791 continue; 792 DepartmentSpreadConstraint deptConstr = depSpreadConstraints.get(lecture.getDepartment()); 793 if (deptConstr == null) { 794 String name = depts.get(lecture.getDepartment()); 795 deptConstr = new DepartmentSpreadConstraint(getModel().getProperties(), lecture.getDepartment(), 796 (name != null ? name : "d" + lecture.getDepartment())); 797 depSpreadConstraints.put(lecture.getDepartment(), deptConstr); 798 getModel().addConstraint(deptConstr); 799 } 800 deptConstr.addVariable(lecture); 801 iProgress.incProgress(); 802 } 803 } 804 805 if (getModel().getProperties().getPropertyBoolean("General.PurgeInvalidPlacements", true)) { 806 iProgress.setPhase("Purging invalid placements ...", getModel().variables().size()); 807 for (Lecture lecture : getModel().variables()) { 808 lecture.purgeInvalidValues(iInteractiveMode); 809 iProgress.incProgress(); 810 } 811 } 812 813 if (getModel().hasConstantVariables() && getModel().constantVariables().size() > 0) { 814 iProgress.setPhase("Assigning committed classes ...", assignedPlacements.size()); 815 for (Map.Entry<Lecture, Placement> entry : assignedPlacements.entrySet()) { 816 Lecture lecture = entry.getKey(); 817 Placement placement = entry.getValue(); 818 if (!lecture.isCommitted()) { iProgress.incProgress(); continue; } 819 getModel().weaken(placement); 820 Map<Constraint<Lecture, Placement>, Set<Placement>> conflictConstraints = getModel().conflictConstraints(placement); 821 if (conflictConstraints.isEmpty()) { 822 lecture.assign(0, placement); 823 } else { 824 sLogger.warn("WARNING: Unable to assign " + lecture.getName() + " := " + placement.getName()); 825 sLogger.debug(" Reason:"); 826 for (Constraint<Lecture, Placement> c : conflictConstraints.keySet()) { 827 Set<Placement> vals = conflictConstraints.get(c); 828 for (Placement v : vals) { 829 sLogger.debug(" " + v.variable().getName() + " = " + v.getName()); 830 } 831 sLogger.debug(" in constraint " + c); 832 } 833 } 834 iProgress.incProgress(); 835 } 836 } 837 838 if (currentSolution != null) { 839 iProgress.setPhase("Creating best assignment ...", 2 * getModel().variables().size()); 840 for (Lecture lecture : getModel().variables()) { 841 iProgress.incProgress(); 842 Placement placement = lecture.getBestAssignment(); 843 if (placement == null) continue; 844 getModel().weaken(placement); 845 lecture.assign(0, placement); 846 } 847 848 currentSolution.saveBest(); 849 for (Lecture lecture : getModel().variables()) { 850 iProgress.incProgress(); 851 if (lecture.getAssignment() != null) 852 lecture.unassign(0); 853 } 854 } 855 856 iProgress.setPhase("Creating initial assignment ...", assignedPlacements.size()); 857 for (Map.Entry<Lecture, Placement> entry : assignedPlacements.entrySet()) { 858 Lecture lecture = entry.getKey(); 859 Placement placement = entry.getValue(); 860 if (lecture.isCommitted()) { iProgress.incProgress(); continue; } 861 getModel().weaken(placement); 862 Map<Constraint<Lecture, Placement>, Set<Placement>> conflictConstraints = getModel().conflictConstraints(placement); 863 if (conflictConstraints.isEmpty()) { 864 if (!placement.isValid()) { 865 sLogger.warn("WARNING: Lecture " + lecture.getName() + " does not contain assignment " 866 + placement.getLongName() + " in its domain (" + placement.getNotValidReason() + ")."); 867 } else 868 lecture.assign(0, placement); 869 } else { 870 sLogger.warn("WARNING: Unable to assign " + lecture.getName() + " := " + placement.getName()); 871 sLogger.debug(" Reason:"); 872 for (Constraint<Lecture, Placement> c : conflictConstraints.keySet()) { 873 Set<Placement> vals = conflictConstraints.get(c); 874 for (Placement v : vals) { 875 sLogger.debug(" " + v.variable().getName() + " = " + v.getName()); 876 } 877 sLogger.debug(" in constraint " + c); 878 } 879 } 880 iProgress.incProgress(); 881 } 882 883 if (initialSectioning && !getModel().assignedVariables().isEmpty() 884 && !getModel().getProperties().getPropertyBoolean("Global.LoadStudentEnrlsFromSolution", false)) 885 getModel().switchStudents(); 886 887 if (iForcedPerturbances > 0) { 888 iProgress.setPhase("Forcing perturbances", iForcedPerturbances); 889 for (int i = 0; i < iForcedPerturbances; i++) { 890 iProgress.setProgress(i); 891 Lecture var = null; 892 do { 893 var = ToolBox.random(getModel().variables()); 894 } while (var.getInitialAssignment() == null || var.values().size() <= 1); 895 var.removeInitialValue(); 896 } 897 } 898 899 for (Constraint<Lecture, Placement> c : getModel().constraints()) { 900 if (c instanceof SpreadConstraint) 901 ((SpreadConstraint) c).init(); 902 if (c instanceof DiscouragedRoomConstraint) 903 ((DiscouragedRoomConstraint) c).setEnabled(true); 904 if (c instanceof MinimizeNumberOfUsedRoomsConstraint) 905 ((MinimizeNumberOfUsedRoomsConstraint) c).setEnabled(true); 906 if (c instanceof MinimizeNumberOfUsedGroupsOfTime) 907 ((MinimizeNumberOfUsedGroupsOfTime) c).setEnabled(true); 908 } 909 910 try { 911 getSolver().getClass().getMethod("load", new Class[] { Element.class }).invoke(getSolver(), 912 new Object[] { root }); 913 } catch (Exception e) { 914 } 915 916 iProgress.setPhase("Done", 1); 917 iProgress.incProgress(); 918 919 sLogger.debug("Model successfully loaded."); 920 iProgress.info("Model successfully loaded."); 921 } 922 923 public static Date getDate(int year, int dayOfYear) { 924 Calendar c = Calendar.getInstance(Locale.US); 925 c.set(year, 1, 1, 0, 0, 0); 926 c.set(Calendar.DAY_OF_YEAR, dayOfYear); 927 return c.getTime(); 928 } 929 930 public static class DatePattern { 931 Long iId; 932 String iName; 933 BitSet iPattern; 934 public DatePattern() {} 935 public DatePattern(Long id, String name, BitSet pattern) { 936 setId(id); setName(name); setPattern(pattern); 937 } 938 public DatePattern(Long id, String name, String pattern) { 939 setId(id); setName(name); setPattern(pattern); 940 } 941 public Long getId() { return iId; } 942 public void setId(Long id) { iId = id; } 943 public String getName() { return iName; } 944 public void setName(String name) { iName = name; } 945 public BitSet getPattern() { return iPattern; } 946 public void setPattern(BitSet pattern) { iPattern = pattern; } 947 public void setPattern(String pattern) { 948 iPattern = new BitSet(pattern.length()); 949 for (int i = 0; i < pattern.length(); i++) 950 if (pattern.charAt(i) == '1') 951 iPattern.set(i); 952 } 953 public void setPattern(int startDay, int endDay) { 954 iPattern = new BitSet(366); 955 for (int d = startDay; d <= endDay; d++) 956 iPattern.set(d); 957 } 958 } 959 }