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