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    }