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 double room2limitRatio = 1.0;
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.0 : ((double) 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 = Double.parseDouble(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 }