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