001 package ifs.example.tt;
002
003 import ifs.model.*;
004 import ifs.solution.*;
005 import java.util.*;
006 import java.io.*;
007 import ifs.util.*;
008
009 import org.dom4j.*;
010 import org.dom4j.io.*;
011
012 /**
013 * Simple Timetabling Problem.
014 * <br><br>
015 * The problem is modelled in such a way that every lecture was represented by a variable, resource as a constraint
016 * and every possible location of an activity in the time and space was represented by a single value. It means that a
017 * value stands for a selection of the time (starting time slot), and one of the available rooms. Binary dependencies
018 * are of course represented as constraints as well.
019 *
020 * @author <a href="mailto:muller@ktiml.mff.cuni.cz">Tomáš Müller</a>
021 * @version 1.0
022 */
023 public class TimetableModel extends Model {
024 private static org.apache.log4j.Logger sLogger = org.apache.log4j.Logger.getLogger(TimetableModel.class);
025 private int iNrDays, iNrHours;
026
027 public TimetableModel(int nrDays, int nrHours) {
028 super();
029 iNrDays = nrDays; iNrHours = nrHours;
030 }
031
032 public int getNrDays() { return iNrDays; }
033 public int getNrHours() { return iNrHours; }
034
035 public static TimetableModel generate(DataProperties cfg) {
036 int nrDays = cfg.getPropertyInt("Generator.NrDays",5);
037 int nrHours = cfg.getPropertyInt("Generator.NrHours",20);
038 int nrSlots = nrDays * nrHours;
039 TimetableModel m = new TimetableModel(nrDays, nrHours);
040
041 int nrRooms = cfg.getPropertyInt("Generator.NrRooms",20);
042 int nrInstructors = cfg.getPropertyInt("Generator.NrInstructors",20);
043 int nrClasses = cfg.getPropertyInt("Generator.NrClasses",20);
044 int nrGroupsOfRooms = cfg.getPropertyInt("Generator.NrGroupsOfRooms", 20);
045 int nrRoomsInGroupMin = cfg.getPropertyInt("Generator.NrRoomsInGroupMin", 1);
046 int nrRoomsInGroupMax = cfg.getPropertyInt("Generator.NrRoomsInGroupMax", 10);
047 int nrRoomInGroupMin = cfg.getPropertyInt("Generator.NrRoomInGroupMin", 1);
048 double fillFactor = cfg.getPropertyDouble("Generator.FillFactor", 0.8);
049 int maxLength = cfg.getPropertyInt("Generator.ActivityLengthMax", 5);
050 double hardFreeResource = cfg.getPropertyDouble("Generator.HardFreeResource", 0.05);
051 double softFreeResource = cfg.getPropertyDouble("Generator.SoftFreeResource", 0.3);
052 double softUsedResource = cfg.getPropertyDouble("Generator.SoftUsedResource", 0.05);
053 double softUsedActivity = cfg.getPropertyDouble("Generator.SoftUsedActivity", 0.05);
054 double softFreeActivity = cfg.getPropertyDouble("Generator.SoftFreeActivity", 0.3);
055 double hardFreeActivity = cfg.getPropertyDouble("Generator.HardFreeActivity", 0.05);
056 int nrDependencies = cfg.getPropertyInt("Generator.NrDependencies", 50);
057
058 Resource rooms[] = new Resource[nrRooms];
059 Vector groupForRoom[] = new Vector[nrRooms];
060 for (int i=0;i<nrRooms;i++) {
061 rooms[i] = new Resource("r"+(i+1),Resource.TYPE_ROOM, "Room "+(i+1));
062 groupForRoom[i] = new Vector();
063 m.addConstraint(rooms[i]);
064 }
065 Vector groupOfRooms[] = new Vector[nrGroupsOfRooms];
066 for (int i=0;i<nrGroupsOfRooms;i++) {
067 groupOfRooms[i] = new FastVector();
068 for (int j=0;j<ToolBox.random(1+nrRoomsInGroupMax-nrRoomsInGroupMin)+nrRoomsInGroupMin;j++) {
069 int r=0;
070 do {
071 r = ToolBox.random(nrRooms);
072 } while (groupOfRooms[i].contains(rooms[r]));
073 groupOfRooms[i].add(rooms[r]);
074 groupForRoom[r].add(groupOfRooms[i]);
075 }
076 }
077 for (int i=0;i<nrRooms;i++) {
078 int cnt = 0;
079 for (int j=0;j<nrGroupsOfRooms;j++)
080 if (groupOfRooms[j].contains(rooms[i])) cnt++;
081 while (cnt < nrRoomInGroupMin) {
082 int r = 0;
083 do {
084 r = ToolBox.random(nrGroupsOfRooms);
085 } while (groupOfRooms[r].contains(rooms[i]));
086 groupOfRooms[r].add(rooms[i]);
087 groupForRoom[i].add(groupOfRooms[r]);
088 cnt ++;
089 }
090 }
091 Resource instructors[] = new Resource[nrInstructors];
092 for (int i=0;i<nrInstructors;i++) {
093 instructors[i] = new Resource("t"+(i+1), Resource.TYPE_INSTRUCTOR, "Teacher "+(i+1));
094 m.addConstraint(instructors[i]);
095 }
096 Resource classes[] = new Resource[nrClasses];
097 for (int i=0;i<nrClasses;i++) {
098 classes[i] = new Resource("c"+(i+1),Resource.TYPE_CLASS ,"Class "+(i+1));
099 m.addConstraint(classes[i]);
100 }
101
102 int[][] timetable4room = new int[nrRooms][nrSlots];
103 int[][] timetable4instr = new int[nrInstructors][nrSlots];
104 int[][] timetable4class = new int[nrClasses][nrSlots];
105 int act = 0;
106 for (int i=0;i<timetable4room.length;i++)
107 for (int j=0;j<timetable4room[i].length;j++)
108 timetable4room[i][j]=0;
109 for (int i=0;i<timetable4instr.length;i++)
110 for (int j=0;j<timetable4instr[i].length;j++)
111 timetable4instr[i][j]=0;
112 for (int i=0;i<timetable4class.length;i++)
113 for (int j=0;j<timetable4class[i].length;j++)
114 timetable4class[i][j]=0;
115
116 int totalSlots = nrRooms * nrSlots;
117 int usedSlots = 0;
118 Vector starts = new Vector();
119 Vector arooms = new Vector();
120 while ((((double)usedSlots/((double)totalSlots)))<fillFactor) {
121 int attempt=0;
122 int slot = ToolBox.random(nrSlots);
123 int room = ToolBox.random(nrRooms);
124 while (attempt<500 && timetable4room[room][slot]!=0) {
125 slot = ToolBox.random(nrSlots);
126 room = ToolBox.random(nrRooms);
127 }
128 if (attempt==500) {
129 int s = slot; int r = room;
130 while (timetable4room[r][s]!=0) {
131 r++;
132 if (r==nrRooms) r=0;
133 if (r==room) s++;
134 if (s==nrSlots) s=0;
135 }
136 slot = s; room = r;
137 }
138 int length = maxLength;//ToolBox.random(maxLength)+1;
139 int aclass = ToolBox.random(nrClasses);
140 int instr = ToolBox.random(nrInstructors);
141 attempt=0;
142 while (attempt<500 && (timetable4class[aclass][slot]!=0 || timetable4instr[instr][slot]!=0)) {
143 aclass = ToolBox.random(nrClasses);
144 instr = ToolBox.random(nrInstructors);
145 }
146 if (attempt==500) continue;
147 int len = 1;
148 while (len<length) {
149 if ((((slot+len)%nrHours)!=0) && timetable4room[room][slot+len]==0 && timetable4instr[instr][slot+len]==0 && timetable4class[aclass][slot+len]==0)
150 len++;
151 else break;
152 }
153 Vector roomGr = (Vector)ToolBox.random(groupForRoom[room]);
154 act++;
155 usedSlots+=len;
156 Activity a = new Activity(len,"a"+act, "Activity "+act);
157 a.addResourceGroup(roomGr);
158 a.addResourceGroup(instructors[instr]);
159 a.addResourceGroup(classes[aclass]);
160 m.addVariable(a);
161 starts.addElement(new Integer(slot));
162 arooms.addElement(new Integer(room));
163 for (int i=slot;i<slot+len;i++) {
164 timetable4room[room][i]=act;
165 timetable4instr[instr][i]=act;
166 timetable4class[aclass][i]=act;
167 }
168 }
169 int nrHardFreeRes = 0;
170 int nrSoftFreeRes = 0;
171 int nrSoftUsedRes = 0;
172 for (int slot = 0; slot < nrSlots; slot++) {
173 for (int room = 0; room < nrRooms; room++) {
174 if (timetable4room[room][slot]==0) {
175 if (ToolBox.random()<hardFreeResource) {
176 nrHardFreeRes++;
177 rooms[room].addProhibitedSlot(slot);
178 } else if (ToolBox.random()<softFreeResource/(1.0-hardFreeResource)) {
179 nrSoftFreeRes++;
180 rooms[room].addDiscouragedSlot(slot);
181 }
182 } else if (ToolBox.random()<softUsedResource) {
183 nrSoftUsedRes++;
184 rooms[room].addDiscouragedSlot(slot);
185 }
186 }
187 for (int instr = 0; instr < nrInstructors; instr++) {
188 if (timetable4instr[instr][slot]==0) {
189 if (ToolBox.random()<hardFreeResource) {
190 nrHardFreeRes++;
191 instructors[instr].addProhibitedSlot(slot);
192 } else if (ToolBox.random()<softFreeResource/(1.0-hardFreeResource)) {
193 nrSoftFreeRes++;
194 instructors[instr].addDiscouragedSlot(slot);
195 }
196 } else if (ToolBox.random()<softUsedResource) {
197 nrSoftUsedRes++;
198 instructors[instr].addDiscouragedSlot(slot);
199 }
200 }
201 for (int aclass = 0; aclass < nrClasses; aclass++) {
202 if (timetable4class[aclass][slot]==0) {
203 if (ToolBox.random()<hardFreeResource) {
204 nrHardFreeRes++;
205 classes[aclass].addProhibitedSlot(slot);
206 } else if (ToolBox.random()<softFreeResource/(1.0-hardFreeResource)) {
207 nrSoftFreeRes++;
208 classes[aclass].addDiscouragedSlot(slot);
209 }
210 } else if (ToolBox.random()<softUsedResource) {
211 nrSoftUsedRes++;
212 classes[aclass].addDiscouragedSlot(slot);
213 }
214 }
215 }
216 int nrSoftFreeAct = 0;
217 int nrSoftUsedAct = 0;
218 int nrHardFreeAct = 0;
219 for (int i=0;i<m.variables().size();i++) {
220 Activity activity = (Activity)m.variables().elementAt(i);
221 for (int slot = 0; slot < nrSlots; slot++) {
222 int start = ((Integer)starts.elementAt(i)).intValue();
223 if (slot<start || slot>=start+activity.getLength()) {
224 if (ToolBox.random()<hardFreeActivity) {
225 nrHardFreeAct++;
226 activity.addProhibitedSlot(slot);
227 } else if (ToolBox.random()<(softFreeActivity/(1.0-hardFreeActivity))) {
228 nrSoftFreeAct++;
229 activity.addDiscouragedSlot(slot);
230 }
231 } else {
232 if (ToolBox.random()<softUsedActivity) {
233 nrSoftUsedAct++;
234 activity.addDiscouragedSlot(slot);
235 }
236 }
237 }
238 activity.init();
239 }
240 for (int i=0;i<nrDependencies;) {
241 int ac1 = ToolBox.random(m.variables().size());
242 int ac2 = ToolBox.random(m.variables().size());
243 while (ac1==ac2) {
244 ac2 = ToolBox.random(m.variables().size());
245 }
246 int s1 = ((Integer)starts.elementAt(ac1)).intValue();
247 int s2 = ((Integer)starts.elementAt(ac2)).intValue();
248 Activity a1 = (Activity)m.variables().elementAt(ac1);
249 Activity a2 = (Activity)m.variables().elementAt(ac2);
250 Dependence dep = null;
251 if (s1<s2) {
252 if (s1+a1.getLength()==s2)
253 dep = new Dependence("d"+(i+1),Dependence.TYPE_CLOSELY_BEFORE);
254 else if (s1+a1.getLength()<s2)
255 dep = new Dependence("d"+(i+1),Dependence.TYPE_BEFORE);
256 } else {
257 if (s2==s1+a1.getLength())
258 dep = new Dependence("d"+(i+1),Dependence.TYPE_CLOSELY_AFTER);
259 else if (s2>s1+a1.getLength())
260 dep = new Dependence("d"+(i+1),Dependence.TYPE_AFTER);
261 }
262 if (dep!=null) {
263 dep.addVariable(a1);
264 dep.addVariable(a2);
265 m.addConstraint(dep);
266 i++;
267 }
268 }
269 for (int i=0;i<m.variables().size();i++) {
270 Activity activity = (Activity)m.variables().elementAt(i);
271 //sLogger.debug("-- processing activity "+activity.getName());
272 int start = ((Integer)starts.elementAt(i)).intValue();
273 int room = ((Integer)arooms.elementAt(i)).intValue();
274 Location location = null;
275 for (Enumeration e=activity.values().elements();e.hasMoreElements();) {
276 Location l = (Location)e.nextElement();
277 if (l.getSlot()==start && l.getResource(0).getResourceId().equals("r"+(room+1))) {
278 location = l; break;
279 }
280 }
281 if (location!=null) {
282 Set conflicts = m.conflictValues(location);
283 if (!conflicts.isEmpty()) {
284 sLogger.warn("Unable to assign "+location.getName()+" to "+activity.getName()+", reason:");
285 for (Enumeration e=activity.constraints().elements();e.hasMoreElements();) {
286 Constraint c = (Constraint)e.nextElement();
287 Set cc = new HashSet(); c.computeConflicts(location, cc);
288 if (!cc.isEmpty())
289 sLogger.warn(" -- Constraint "+c.getName()+" causes conflicts "+cc);
290 }
291 } else {
292 activity.assign(0, location);
293 activity.setInitialAssignment(location);
294 }
295 //sLogger.debug(" -- location "+location.getName()+" found");
296 activity.setInitialAssignment(location);
297 } else {
298 sLogger.warn("Unable to assign "+activity.getName()+" -- no location matching slot="+start+" room='R"+(room+1)+"'");
299 }
300 }
301 if (!cfg.getPropertyBoolean("General.InitialAssignment", true)) {
302 for (int i=0;i<m.variables().size();i++) {
303 Activity activity = (Activity)m.variables().elementAt(i);
304 activity.unassign(0);
305 }
306 }
307
308 int forcedPerturbances = cfg.getPropertyInt("General.ForcedPerturbances", 0);
309 if (forcedPerturbances>0) {
310 Vector initialVariables = new Vector();
311 for (Enumeration e=m.variables().elements();e.hasMoreElements();) {
312 Variable v = (Variable)e.nextElement();
313 if (v.getInitialAssignment()!=null)
314 initialVariables.addElement(v);
315 }
316 for (int i=0;i<forcedPerturbances;i++) {
317 if (initialVariables.isEmpty()) break;
318 Variable var = (Variable)ToolBox.random(initialVariables);
319 initialVariables.remove(var);
320 var.removeInitialValue();
321 }
322 }
323
324
325 sLogger.debug("-- Generator Info ---------------------------------------------------------");
326 sLogger.debug(" Total number of "+m.variables().size()+" activities generated.");
327 sLogger.debug(" Total number of "+usedSlots+" slots are filled ("+((100.0*usedSlots)/totalSlots)+"% filled).");
328 sLogger.debug(" Average length of an activity is "+(((double)usedSlots)/m.variables().size()));
329 sLogger.debug(" Total number of hard constraints posted on free slots on activities: "+nrHardFreeAct);
330 sLogger.debug(" Total number of soft constraints posted on free slots on activities: "+nrSoftFreeAct);
331 sLogger.debug(" Total number of soft constraints posted on used slots on activities: "+nrSoftUsedAct);
332 sLogger.debug(" Total number of hard constraints posted on free slots on resources: "+nrHardFreeRes);
333 sLogger.debug(" Total number of soft constraints posted on free slots on resources: "+nrSoftFreeRes);
334 sLogger.debug(" Total number of soft constraints posted on used slots on resources: "+nrSoftUsedRes);
335 sLogger.debug(" Total number of "+nrDependencies+" dependencies generated.");
336 sLogger.debug("---------------------------------------------------------------------------");
337
338 return m;
339 }
340
341 public static void main(String[] args) {
342 org.apache.log4j.BasicConfigurator.configure();
343 TimetableModel model = generate(new DataProperties());
344 System.out.println(model.getInfo());
345 }
346
347 public void saveAsXML(DataProperties cfg, boolean gen, Solution solution, File outFile) throws IOException {
348 outFile.getParentFile().mkdirs();
349 sLogger.debug("Writting XML data to:"+outFile);
350
351 Document document = DocumentHelper.createDocument();
352 document.addComment("Interactive Timetabling - University Timetable Generator (version 2.0)");
353
354 if (!assignedVariables().isEmpty()) {
355 StringBuffer comments = new StringBuffer("Solution Info:\n");
356 Dictionary solutionInfo=(solution==null?getInfo():solution.getInfo());
357 for (Enumeration e=ToolBox.sortEnumeration(solutionInfo.keys());e.hasMoreElements();) {
358 String key = (String)e.nextElement();
359 Object value = solutionInfo.get(key);
360 comments.append(" "+key+": "+value+"\n");
361 }
362 document.addComment(comments.toString());
363 }
364
365 Element root = document.addElement("Timetable");
366 if (gen) {
367 Element generator = root.addElement("Generator");
368 generator.addAttribute("version", "2.0");
369 generator.addElement("DaysPerWeek").setText(String.valueOf(iNrDays));
370 generator.addElement("SlotsPerDay").setText(String.valueOf(iNrHours));
371 generator.addElement("NrRooms").setText(cfg.getProperty("Generator.NrRooms","20"));
372 generator.addElement("NrInstructors").setText(cfg.getProperty("Generator.NrInstructors","20"));
373 generator.addElement("NrClasses").setText(cfg.getProperty("Generator.NrClasses","20"));
374 generator.addElement("FillFactor").setText(cfg.getProperty("Generator.FillFactor", "0.8"));
375 generator.addElement("ActivityLengthMax").setText(cfg.getProperty("Generator.ActivityLengthMax", "5"));
376 generator.addElement("NrGroupsOfRooms").setText(cfg.getProperty("Generator.NrGroupsOfRooms", "20"));
377 generator.addElement("NrRoomsInGroupMin").setText(cfg.getProperty("Generator.NrRoomsInGroupMin", "1"));
378 generator.addElement("NrRoomsInGroupMax").setText(cfg.getProperty("Generator.NrRoomsInGroupMax", "10"));
379 generator.addElement("NrRoomInGroupMin").setText(cfg.getProperty("Generator.NrRoomInGroupMin", "1"));
380 generator.addElement("HardFreeResource").setText(cfg.getProperty("Generator.HardFreeResource", "0.05"));
381 generator.addElement("SoftFreeResource").setText(cfg.getProperty("Generator.SoftFreeResource", "0.3"));
382 generator.addElement("SoftUsedResource").setText(cfg.getProperty("Generator.SoftUsedResource", "0.05"));
383 generator.addElement("SoftUsedActivity").setText(cfg.getProperty("Generator.SoftUsedActivity", "0.05"));
384 generator.addElement("SoftFreeActivity").setText(cfg.getProperty("Generator.SoftFreeActivity", "0.3"));
385 generator.addElement("HardFreeActivity").setText(cfg.getProperty("Generator.HardFreeActivity", "0.05"));
386 generator.addElement("NrDependencies").setText(cfg.getProperty("Generator.NrDependencies", "50"));
387 }
388
389 Vector rooms = new Vector();
390 Vector classes = new Vector();
391 Vector instructors = new Vector();
392 Vector specials = new Vector();
393 Vector dependencies = new Vector();
394
395 for (Enumeration e=constraints().elements();e.hasMoreElements();) {
396 Constraint c = (Constraint)e.nextElement();
397 if (c instanceof Resource) {
398 Resource r = (Resource)c;
399 switch (r.getType()) {
400 case Resource.TYPE_ROOM:
401 rooms.addElement(r);
402 break;
403 case Resource.TYPE_CLASS:
404 classes.addElement(r);
405 break;
406 case Resource.TYPE_INSTRUCTOR:
407 instructors.addElement(r);
408 break;
409 default:
410 specials.addElement(r);
411 }
412 } else if (c instanceof Dependence) {
413 dependencies.addElement(c);
414 }
415 }
416
417 Element problem = root.addElement("Problem");
418 problem.addAttribute("version","2.0");
419 Element problemGen = problem.addElement("General");
420 problemGen.addElement("DaysPerWeek").setText(String.valueOf(iNrDays));
421 problemGen.addElement("SlotsPerDay").setText(String.valueOf(iNrHours));
422 Element resourceGen = problemGen.addElement("Resources");
423 resourceGen.addElement("Classrooms").setText(String.valueOf(rooms.size()));
424 resourceGen.addElement("Teachers").setText(String.valueOf(instructors.size()));
425 resourceGen.addElement("Classes").setText(String.valueOf(classes.size()));
426 resourceGen.addElement("Special").setText(String.valueOf(specials.size()));
427 problemGen.addElement("Activities").setText(String.valueOf(variables().size()));
428 problemGen.addElement("Dependences").setText(String.valueOf(dependencies.size()));
429
430 Element resources = problem.addElement("Resources");
431
432 Element resEl = resources.addElement("Classrooms");
433 for (Enumeration e=rooms.elements();e.hasMoreElements();) {
434 Resource r = (Resource)e.nextElement();
435 Element el = resEl.addElement("Resource");
436 el.addAttribute("id", r.getResourceId());
437 el.addElement("Name").setText(r.getName());
438 Element pref = el.addElement("TimePreferences");
439 for (Enumeration i=ToolBox.sortEnumeration((new Vector(r.getDiscouragedSlots())).elements());i.hasMoreElements();)
440 pref.addElement("Soft").setText(i.nextElement().toString());
441 for (Enumeration i=ToolBox.sortEnumeration((new Vector(r.getProhibitedSlots())).elements());i.hasMoreElements();)
442 pref.addElement("Hard").setText(i.nextElement().toString());
443 }
444
445 resEl = resources.addElement("Teachers");
446 for (Enumeration e=instructors.elements();e.hasMoreElements();) {
447 Resource r = (Resource)e.nextElement();
448 Element el = resEl.addElement("Resource");
449 el.addAttribute("id", r.getResourceId());
450 el.addElement("Name").setText(r.getName());
451 Element pref = el.addElement("TimePreferences");
452 for (Enumeration i=ToolBox.sortEnumeration((new Vector(r.getDiscouragedSlots())).elements());i.hasMoreElements();)
453 pref.addElement("Soft").setText(i.nextElement().toString());
454 for (Enumeration i=ToolBox.sortEnumeration((new Vector(r.getProhibitedSlots())).elements());i.hasMoreElements();)
455 pref.addElement("Hard").setText(i.nextElement().toString());
456 }
457
458 resEl = resources.addElement("Classes");
459 for (Enumeration e=classes.elements();e.hasMoreElements();) {
460 Resource r = (Resource)e.nextElement();
461 Element el = resEl.addElement("Resource");
462 el.addAttribute("id", r.getResourceId());
463 el.addElement("Name").setText(r.getName());
464 Element pref = el.addElement("TimePreferences");
465 for (Enumeration i=ToolBox.sortEnumeration((new Vector(r.getDiscouragedSlots())).elements());i.hasMoreElements();)
466 pref.addElement("Soft").setText(i.nextElement().toString());
467 for (Enumeration i=ToolBox.sortEnumeration((new Vector(r.getProhibitedSlots())).elements());i.hasMoreElements();)
468 pref.addElement("Hard").setText(i.nextElement().toString());
469 }
470
471 resEl = resources.addElement("Special");
472 for (Enumeration e=specials.elements();e.hasMoreElements();) {
473 Resource r = (Resource)e.nextElement();
474 Element el = resEl.addElement("Resource");
475 el.addAttribute("id", r.getResourceId());
476 el.addElement("Name").setText(r.getName());
477 Element pref = el.addElement("TimePreferences");
478 for (Enumeration i=ToolBox.sortEnumeration((new Vector(r.getDiscouragedSlots())).elements());i.hasMoreElements();)
479 pref.addElement("Soft").setText(i.nextElement().toString());
480 for (Enumeration i=ToolBox.sortEnumeration((new Vector(r.getProhibitedSlots())).elements());i.hasMoreElements();)
481 pref.addElement("Hard").setText(i.nextElement().toString());
482 }
483
484 boolean hasSolution = false;
485 Element actEl = problem.addElement("Activities");
486 for (Enumeration e=variables().elements();e.hasMoreElements();) {
487 Activity a = (Activity)e.nextElement();
488 Element el = actEl.addElement("Activity");
489 el.addAttribute("id", a.getActivityId());
490 el.addElement("Name").setText(a.getName());
491 el.addElement("Length").setText(String.valueOf(a.getLength()));
492 if (a.getAssignment()!=null) hasSolution=true;
493 Element pref = el.addElement("TimePreferences");
494 for (Enumeration i=ToolBox.sortEnumeration((new Vector(a.getDiscouragedSlots())).elements());i.hasMoreElements();)
495 pref.addElement("Soft").setText(i.nextElement().toString());
496 for (Enumeration i=ToolBox.sortEnumeration((new Vector(a.getProhibitedSlots())).elements());i.hasMoreElements();)
497 pref.addElement("Hard").setText(i.nextElement().toString());
498 Element reqRes = el.addElement("RequiredResources");
499 for (Enumeration f=a.getResourceGroups().elements();f.hasMoreElements();) {
500 Vector gr = (Vector)f.nextElement();
501 if (gr.size()==1) {
502 reqRes.addElement("Resource").setText(((Resource)gr.firstElement()).getResourceId());
503 } else {
504 Element grEl = reqRes.addElement("Group").addAttribute("conjunctive","no");
505 for (Enumeration g=gr.elements();g.hasMoreElements();)
506 grEl.addElement("Resource").setText(((Resource)g.nextElement()).getResourceId());
507 }
508 }
509 }
510
511 Element depEl = problem.addElement("Dependences");
512 for (Enumeration e=dependencies.elements();e.hasMoreElements();) {
513 Dependence d = (Dependence)e.nextElement();
514 Element el = depEl.addElement("Dependence");
515 el.addAttribute("id", d.getResourceId());
516 el.addElement("FirstActivity").setText(((Activity)d.first()).getActivityId());
517 el.addElement("SecondActivity").setText(((Activity)d.second()).getActivityId());
518 switch (d.getType()) {
519 case Dependence.TYPE_AFTER :
520 el.addElement("Operator").setText("After");
521 break;
522 case Dependence.TYPE_BEFORE :
523 el.addElement("Operator").setText("Before");
524 break;
525 case Dependence.TYPE_CLOSELY_BEFORE :
526 el.addElement("Operator").setText("Closely before");
527 break;
528 case Dependence.TYPE_CLOSELY_AFTER :
529 el.addElement("Operator").setText("Closely after");
530 break;
531 case Dependence.TYPE_CONCURRENCY :
532 el.addElement("Operator").setText("Concurrently");
533 break;
534 default :
535 el.addElement("Operator").setText("Unknown");
536 }
537 }
538
539 if (hasSolution) {
540 Element solutionEl = root.addElement("Solution");
541 solutionEl.addAttribute("version","2.0");
542 for (Enumeration e=variables().elements();e.hasMoreElements();) {
543 Activity a = (Activity)e.nextElement();
544 Element el = solutionEl.addElement("Activity");
545 el.addAttribute("id", a.getActivityId());
546 if (a.getAssignment()!=null) {
547 Location location = (Location)a.getAssignment();
548 el.addElement("StartTime").setText(String.valueOf(location.getSlot()));
549 Element res = el.addElement("UsedResources");
550 for (int i=0;i<location.getResources().length;i++)
551 res.addElement("Resource").setText(location.getResources()[i].getResourceId());
552 }
553 }
554 }
555
556 FileOutputStream fos = new FileOutputStream(outFile);
557 (new XMLWriter(fos,OutputFormat.createPrettyPrint())).write(document);
558 fos.flush();fos.close();
559 }
560
561 public static TimetableModel loadFromXML(File inFile, boolean assign) throws IOException, DocumentException {
562 Document document = (new SAXReader()).read(inFile);
563 Element root = document.getRootElement();
564 if (!"Timetable".equals(root.getName())) {
565 sLogger.error("Given XML file is not interactive timetabling problem.");
566 return null;
567 }
568
569 Element problem = root.element("Problem");
570 Element problemGen = problem.element("General");
571 TimetableModel m = new TimetableModel(
572 Integer.parseInt(problemGen.elementText("DaysPerWeek")),
573 Integer.parseInt(problemGen.elementText("SlotsPerDay")));
574
575 Element resources = problem.element("Resources");
576
577 Hashtable resTab = new Hashtable();
578
579 Element resEl = resources.element("Classrooms");
580 for (Iterator i=resEl.elementIterator("Resource");i.hasNext();) {
581 Element el = (Element)i.next();
582 Resource r = new Resource(el.attributeValue("id"), Resource.TYPE_ROOM, el.elementText("Name"));
583 Element pref = el.element("TimePreferences");
584 for (Iterator j=pref.elementIterator("Soft");j.hasNext();)
585 r.addDiscouragedSlot(Integer.parseInt(((Element)j.next()).getText()));
586 for (Iterator j=pref.elementIterator("Hard");j.hasNext();)
587 r.addProhibitedSlot(Integer.parseInt(((Element)j.next()).getText()));
588 m.addConstraint(r);
589 resTab.put(r.getResourceId(),r);
590 }
591
592 resEl = resources.element("Teachers");
593 for (Iterator i=resEl.elementIterator("Resource");i.hasNext();) {
594 Element el = (Element)i.next();
595 Resource r = new Resource(el.attributeValue("id"), Resource.TYPE_INSTRUCTOR, el.elementText("Name"));
596 Element pref = el.element("TimePreferences");
597 for (Iterator j=pref.elementIterator("Soft");j.hasNext();)
598 r.addDiscouragedSlot(Integer.parseInt(((Element)j.next()).getText()));
599 for (Iterator j=pref.elementIterator("Hard");j.hasNext();)
600 r.addProhibitedSlot(Integer.parseInt(((Element)j.next()).getText()));
601 m.addConstraint(r);
602 resTab.put(r.getResourceId(),r);
603 }
604
605 resEl = resources.element("Classes");
606 for (Iterator i=resEl.elementIterator("Resource");i.hasNext();) {
607 Element el = (Element)i.next();
608 Resource r = new Resource(el.attributeValue("id"), Resource.TYPE_CLASS, el.elementText("Name"));
609 Element pref = el.element("TimePreferences");
610 for (Iterator j=pref.elementIterator("Soft");j.hasNext();)
611 r.addDiscouragedSlot(Integer.parseInt(((Element)j.next()).getText()));
612 for (Iterator j=pref.elementIterator("Hard");j.hasNext();)
613 r.addProhibitedSlot(Integer.parseInt(((Element)j.next()).getText()));
614 m.addConstraint(r);
615 resTab.put(r.getResourceId(),r);
616 }
617
618 resEl = resources.element("Special");
619 for (Iterator i=resEl.elementIterator("Resource");i.hasNext();) {
620 Element el = (Element)i.next();
621 Resource r = new Resource(el.attributeValue("id"), Resource.TYPE_OTHER, el.elementText("Name"));
622 Element pref = el.element("TimePreferences");
623 for (Iterator j=pref.elementIterator("Soft");j.hasNext();)
624 r.addDiscouragedSlot(Integer.parseInt(((Element)j.next()).getText()));
625 for (Iterator j=pref.elementIterator("Hard");j.hasNext();)
626 r.addProhibitedSlot(Integer.parseInt(((Element)j.next()).getText()));
627 m.addConstraint(r);
628 resTab.put(r.getResourceId(),r);
629 }
630
631 Element actEl = problem.element("Activities");
632 Hashtable actTab = new Hashtable();
633 for (Iterator i=actEl.elementIterator("Activity");i.hasNext();) {
634 Element el = (Element)i.next();
635 Activity a = new Activity(Integer.parseInt(el.elementText("Length")), el.attributeValue("id"), el.elementText("Name"));
636 Element pref = el.element("TimePreferences");
637 for (Iterator j=pref.elementIterator("Soft");j.hasNext();)
638 a.addDiscouragedSlot(Integer.parseInt(((Element)j.next()).getText()));
639 for (Iterator j=pref.elementIterator("Hard");j.hasNext();)
640 a.addProhibitedSlot(Integer.parseInt(((Element)j.next()).getText()));
641 Element req = el.element("RequiredResources");
642 for (Iterator j=req.elementIterator();j.hasNext();) {
643 Element rqEl = (Element)j.next();
644 if ("Resource".equals(rqEl.getName())) {
645 a.addResourceGroup((Resource)resTab.get(rqEl.getText()));
646 } else if ("Group".equals(rqEl.getName())) {
647 if ("no".equalsIgnoreCase(rqEl.attributeValue("conjunctive")) || "false".equalsIgnoreCase(rqEl.attributeValue("conjunctive"))) {
648 Vector gr = new Vector();
649 for (Iterator k=rqEl.elementIterator("Resource");k.hasNext();)
650 gr.addElement(resTab.get(((Element)k.next()).getText()));
651 a.addResourceGroup(gr);
652 } else {
653 for (Iterator k=rqEl.elementIterator("Resource");k.hasNext();)
654 a.addResourceGroup((Resource)resTab.get(((Element)k.next()).getText()));
655 }
656 }
657 }
658 m.addVariable(a);
659 a.init();
660 actTab.put(a.getActivityId(),a);
661 }
662
663 Element depEl = problem.element("Dependences");
664 for (Iterator i=depEl.elementIterator("Dependence");i.hasNext();) {
665 Element el = (Element)i.next();
666 int type = Dependence.TYPE_NO_DEPENDENCE;
667 String typeStr = el.elementText("Operator");
668 if ("After".equals(typeStr))
669 type = Dependence.TYPE_AFTER;
670 else if ("Before".equals(typeStr))
671 type = Dependence.TYPE_BEFORE;
672 else if ("After".equals(typeStr))
673 type = Dependence.TYPE_AFTER;
674 else if ("Closely before".equals(typeStr))
675 type = Dependence.TYPE_CLOSELY_BEFORE;
676 else if ("Closely after".equals(typeStr))
677 type = Dependence.TYPE_CLOSELY_AFTER;
678 else if ("Concurrently".equals(typeStr))
679 type = Dependence.TYPE_CONCURRENCY;
680 Dependence d = new Dependence(el.attributeValue("id"), type);
681 d.addVariable((Activity)actTab.get(el.elementText("FirstActivity")));
682 d.addVariable((Activity)actTab.get(el.elementText("SecondActivity")));
683 m.addConstraint(d);
684 }
685
686 Element solEl = root.element("Solution");
687 if (solEl!=null) {
688 for (Iterator i=solEl.elementIterator("Activity");i.hasNext();) {
689 Element el = (Element)i.next();
690 Activity a = (Activity)actTab.get(el.attributeValue("id"));
691 if (a==null) continue;
692 int slot = Integer.parseInt(el.elementText("StartTime"));
693 Element usResEl = el.element("UsedResources");
694 Vector res = new Vector();
695 for (Iterator j=usResEl.elementIterator("Resource");j.hasNext();)
696 res.addElement(resTab.get(((Element)j.next()).getText()));
697 for (Enumeration e=a.values().elements();e.hasMoreElements();) {
698 Location loc = (Location)e.nextElement();
699 if (loc.getSlot()!=slot || loc.getResources().length!=res.size()) continue;
700 boolean same = true;
701 for (int j=0;j<loc.getResources().length && same;j++)
702 if (!res.elementAt(j).equals(loc.getResources()[j])) same=false;
703 if (!same) continue;
704 a.setInitialAssignment(loc);
705 if (assign) a.assign(0,loc);
706 break;
707 }
708 }
709 }
710 return m;
711 }
712 }