001    package ttsolver.heuristics;
002    
003    import ifs.perturbations.*;
004    import ifs.util.*;
005    import ifs.model.*;
006    import ifs.solution.*;
007    import ttsolver.model.*;
008    import ttsolver.constraint.*;
009    import java.util.*;
010    import edu.purdue.smas.timetable.serverfwk.ParameterDefinition;
011    
012    /**
013     * Perturbation penalty computation.
014     * <br><br>
015     * In practise, the strategy for computing perturbations needs to be extended. For example, a change in time is usually 
016     * much worse than a movement to a different classroom. The number of enrolled/involved students should also be taken 
017     * into account. Another factor is whether the solution has already been published or not.
018     * <br>
019     * The priorities for evaluating perturbations are as follows. Before publishing timetable:<ul>
020     * <li>minimize number of classes with time changes,
021     * <li>minimize number of student conflicts,
022     * <li>optimize satisfaction of problem soft constraints.
023     * </ul><br>
024     * After publishing the timetable (class time changes are not allowed):<ul>
025     * <li>minimize number of additional (new) student conflicts,
026     * <li>minimize number of students with time changes,
027     * <li>minimize number of classes with time changes,
028     * <li>optimize satisfaction of problem soft constraints.
029     * </ul>
030     * In both cases, the number of classes with room change is not significant at all. Before the timetable is published, 
031     * minimizing the number of classes with time changes is the most important criteria for the MPP as long as it does not 
032     * create too many additional student conflicts in the process. Therefore, as a compromise, the cost (in equivalent 
033     * conflicts) of changing the time assigned to a class equals a number like 5% of the students enrolled in that class. 
034     * Otherwise none of our other criteria would have any importance. 
035     * <br><br>
036     * Similar properties apply between other criteria as well. To fulfil all these needs we have crated a function (called 
037     * perturbations penalty) which can be computed over a partial solution. This is a weighted sum of various perturbations 
038     * criteria like the number of classes with time changes or the number of additional student conflicts. This 
039     * perturbation penalty is added as an extra optimization criterion to the solution comparator and to value selection 
040     * criterion, so we can also setup the weights between this perturbation penalty and other (initial) soft constraints.
041     * <br><br>
042     * Parameters:
043     * <table border='1'><tr><th>Parameter</th><th>Type</th><th>Comment</th></tr>
044     * <tr><td>Perturbations.DifferentPlacement</td><td>{@link Double}</td><td>Different value than initial is assigned</td></tr>
045     * <tr><td>Perturbations.AffectedStudentWeight</td><td>{@link Double}</td><td>Number of students which are enrolled in a class which is placed to a different location than initial (a student can be included twice or more)</td></tr>
046     * <tr><td>Perturbations.AffectedInstructorWeight</td><td>{@link Double}</td><td>Number of instructors which are assigned to classes which are placed to different locations than initial (an instructor can be included twice or more)</td></tr>
047     * <tr><td>Perturbations.DifferentRoomWeight</td><td>{@link Double}</td><td>Number of classes which are placed to a different room than initial</td></tr>
048     * <tr><td>Perturbations.DifferentBuildingWeight</td><td>{@link Double}</td><td>Number of classes which are placed to a different building than initial</td></tr>
049     * <tr><td>Perturbations.DifferentTimeWeight</td><td>{@link Double}</td><td>Number of classes which are placed in a different time than initial</td></tr>
050     * <tr><td>Perturbations.DifferentDayWeight</td><td>{@link Double}</td><td>Number of classes which are placed in a different days than initial</td></tr>
051     * <tr><td>Perturbations.DifferentHourWeight</td><td>{@link Double}</td><td>Number of classes which are placed in a different hours than initial</td></tr>
052     * <tr><td>Perturbations.DeltaStudentConflictsWeight</td><td>{@link Double}</td><td>Difference of student conflicts of classes assigned to current placements instead of initial placements. It is a difference between number of students conflicts which are in the initial solution and the current one. Student conflicts created by classes without initial placement are not taken into account</td></tr>
053     * <tr><td>Perturbations.NewStudentConflictsWeight</td><td>{@link Double}</td><td>New created student conflicts -- particular students are taken into account. Student conflicts created by classes without initial placement are not taken into account</td></tr>
054     * <tr><td>Perturbations.TooFarForInstructorsWeight</td><td>{@link Double}</td><td>New placement of a class is too far from the intial placement (instructor-wise). It is computed only when the class has an instructor assigned, moreover:<ul><li>0 < distance(currentPlacement,initialPlacement) <= 5  .. weight is taken once<li>5 < distance(currentPlacement,initialPlacement) <= 20 .. weight is taken twice<li>20 < distance(currentPlacement,initialPlacement)       .. weight is taken ten times</ul></td></tr>
055     * <tr><td>Perturbations.TooFarForStudentsWeight</td><td>{@link Double}</td><td>New placement of a class is too far from the intial placement (instructor-student). It is weighted by the number of students enrolled in the class when distance(currentPlacement,initialPlacement) > 67</td></tr>
056     * <tr><td>Perturbations.DeltaInstructorDistancePreferenceWeight</td><td>{@link Double}</td><td>Difference between number of instructor distance preferences of the initial (but maybe inconsistent) solution and the current solution. Instructor distance preferences of classes without initial placement are not taken into account</td></tr>
057     * <tr><td>Perturbations.DeltaRoomPreferenceWeight</td><td>{@link Double}</td><td>Difference between room preferences of the initial and the current solution. Room preferences of classes without initial placement are not taken into account</td></tr>
058     * <tr><td>Perturbations.DeltaTimePreferenceWeight</td><td>{@link Double}</td><td>Difference between time preferences of the initial and the current solution. Time preferences of classes without initial placement are not taken into account</td></tr>
059     * <tr><td>Perturbations.AffectedStudentByTimeWeight</td><td>{@link Double}</td><td>Number of students which are enrolled in a class which is placed to a different time than initial </td></tr>
060     * <tr><td>Perturbations.AffectedInstructorByTimeWeight</td><td>{@link Double}</td><td>Number of instructors which are assigned to classes which are placed to different time than initial</td></tr>
061     * <tr><td>Perturbations.AffectedStudentByRoomWeight</td><td>{@link Double}</td><td>Number of students which are enrolled in a class which is placed to a different room than initial </td></tr>
062     * <tr><td>Perturbations.AffectedInstructorByRoomWeight</td><td>{@link Double}</td><td>Number of instructors which are assigned to classes which are placed to different room than initial </td></tr>
063     * <tr><td>Perturbations.AffectedStudentByBldgWeight</td><td>{@link Double}</td><td>Number of students which are enrolled in a class which is placed to a different building than initial</td></tr>
064     * <tr><td>Perturbations.AffectedInstructorByBldgWeight</td><td>{@link Double}</td><td>Number of instructors which are assigned to classes which are placed to different building than initial </td></tr>
065     * </table>
066     *
067     * @author <a href="mailto:muller@ktiml.mff.cuni.cz">Tomáš Müller</a>
068     * @version 1.0
069     */
070    
071    public class UniversalPerturbationsCounter extends DefaultPerturbationsCounter {
072        private double iDifferentPlacement = 1.0;
073        private double iAffectedStudentWeight = 0.0;
074        private double iAffectedInstructorWeight = 0.0;
075        private double iAffectedStudentByTimeWeight = 0.0;
076        private double iAffectedInstructorByTimeWeight = 0.0;
077        private double iAffectedStudentByRoomWeight = 0.0;
078        private double iAffectedInstructorByRoomWeight = 0.0;
079        private double iAffectedStudentByBldgWeight = 0.0;
080        private double iAffectedInstructorByBldgWeight = 0.0;
081        private double iDifferentRoomWeight = 0.0;
082        private double iDifferentBuildingWeight = 0.0;
083        private double iDifferentTimeWeight = 0.0;
084        private double iDifferentDayWeight = 0.0;
085        private double iDifferentHourWeight = 0.0;
086        private double iNewStudentConflictsWeight = 0.0;
087        private double iDeltaStudentConflictsWeight = 0.0;
088        private double iTooFarForInstructorsWeight = 0.0;
089        private double iTooFarForStudentsWeight = 0.0;
090        private double iDeltaInstructorDistancePreferenceWeight = 0.0;
091        private double iDeltaRoomPreferenceWeight = 0.0;
092        private double iDeltaTimePreferenceWeight = 0.0;
093        private boolean iMPP = false;
094        
095        public UniversalPerturbationsCounter(DataProperties properties) {
096            super(properties);
097            iMPP = properties.getPropertyBoolean("General.MPP",false);
098            iDifferentPlacement = properties.getPropertyDouble("Perturbations.DifferentPlacement",iDifferentPlacement);
099            iAffectedStudentWeight = properties.getPropertyDouble("Perturbations.AffectedStudentWeight",iAffectedStudentWeight);
100            iAffectedInstructorWeight = properties.getPropertyDouble("Perturbations.AffectedInstructorWeight",iAffectedInstructorWeight);
101            iAffectedStudentByTimeWeight = properties.getPropertyDouble("Perturbations.AffectedStudentByTimeWeight",iAffectedStudentByTimeWeight);
102            iAffectedInstructorByTimeWeight = properties.getPropertyDouble("Perturbations.AffectedInstructorByTimeWeight",iAffectedInstructorByTimeWeight);
103            iAffectedStudentByRoomWeight = properties.getPropertyDouble("Perturbations.AffectedStudentByRoomWeight",iAffectedStudentByRoomWeight);
104            iAffectedInstructorByRoomWeight = properties.getPropertyDouble("Perturbations.AffectedInstructorByRoomWeight",iAffectedInstructorByRoomWeight);
105            iAffectedStudentByBldgWeight = properties.getPropertyDouble("Perturbations.AffectedStudentByBldgWeight",iAffectedStudentByBldgWeight);
106            iAffectedInstructorByBldgWeight = properties.getPropertyDouble("Perturbations.AffectedInstructorByBldgWeight",iAffectedInstructorByBldgWeight);
107            iDifferentRoomWeight = properties.getPropertyDouble("Perturbations.DifferentRoomWeight",iDifferentRoomWeight);
108            iDifferentBuildingWeight = properties.getPropertyDouble("Perturbations.DifferentBuildingWeight",iDifferentBuildingWeight);
109            iDifferentTimeWeight = properties.getPropertyDouble("Perturbations.DifferentTimeWeight",iDifferentTimeWeight);
110            iDifferentDayWeight = properties.getPropertyDouble("Perturbations.DifferentDayWeight",iDifferentDayWeight);
111            iDifferentHourWeight = properties.getPropertyDouble("Perturbations.DifferentHourWeight",iDifferentHourWeight);
112            iDeltaStudentConflictsWeight = properties.getPropertyDouble("Perturbations.DeltaStudentConflictsWeight",iDeltaStudentConflictsWeight);
113            iNewStudentConflictsWeight = properties.getPropertyDouble("Perturbations.NewStudentConflictsWeight",iNewStudentConflictsWeight);
114            iTooFarForInstructorsWeight = properties.getPropertyDouble("Perturbations.TooFarForInstructorsWeight",iTooFarForInstructorsWeight);
115            iTooFarForStudentsWeight = properties.getPropertyDouble("Perturbations.TooFarForStudentsWeight",iTooFarForStudentsWeight);
116            iDeltaInstructorDistancePreferenceWeight = properties.getPropertyDouble("Perturbations.DeltaInstructorDistancePreferenceWeight",iDeltaInstructorDistancePreferenceWeight);
117            iDeltaRoomPreferenceWeight = properties.getPropertyDouble("Perturbations.DeltaRoomPreferenceWeight",iDeltaRoomPreferenceWeight);
118            iDeltaTimePreferenceWeight = properties.getPropertyDouble("Perturbations.DeltaTimePreferenceWeight",iDeltaTimePreferenceWeight);
119        }
120    
121        public static Vector parameters() {
122            Vector ret = new FastVector();
123            
124            ParameterDefinition.Dependency mppDep = new ParameterDefinition.Dependency("General.MPP","true");
125            
126            ret.addElement(new ParameterDefinition("Perturbations - Weights","Perturbations.DifferentPlacement", "Different placement than initial", ParameterDefinition.TYPE_DOUBLE, "1.0").addDependency(mppDep));
127            ret.addElement(new ParameterDefinition("Perturbations - Weights","Perturbations.AffectedStudentWeight", "Number of students", ParameterDefinition.TYPE_DOUBLE, "0.0").addDependency(mppDep));
128            ret.addElement(new ParameterDefinition("Perturbations - Weights","Perturbations.AffectedInstructorWeight", "An instructor is assigned", ParameterDefinition.TYPE_DOUBLE, "0.0").addDependency(mppDep));
129            ret.addElement(new ParameterDefinition("Perturbations - Weights","Perturbations.AffectedStudentByTimeWeight", "Number of students (time change)", ParameterDefinition.TYPE_DOUBLE, "0.0").addDependency(mppDep));
130            ret.addElement(new ParameterDefinition("Perturbations - Weights","Perturbations.AffectedInstructorByTimeWeight", "An instructor is assigned (time change)", ParameterDefinition.TYPE_DOUBLE, "0.0").addDependency(mppDep));
131            ret.addElement(new ParameterDefinition("Perturbations - Weights","Perturbations.AffectedStudentByRoomWeight", "Number of students (room change)", ParameterDefinition.TYPE_DOUBLE, "0.0").addDependency(mppDep));
132            ret.addElement(new ParameterDefinition("Perturbations - Weights","Perturbations.AffectedInstructorByRoomWeight", "An instructor is assigned (room change)", ParameterDefinition.TYPE_DOUBLE, "0.0").addDependency(mppDep));
133            ret.addElement(new ParameterDefinition("Perturbations - Weights","Perturbations.AffectedStudentByBldgWeight", "Number of students (building change)", ParameterDefinition.TYPE_DOUBLE, "0.0").addDependency(mppDep));
134            ret.addElement(new ParameterDefinition("Perturbations - Weights","Perturbations.AffectedInstructorByBldgWeight", "An instructor is assigned (building change)", ParameterDefinition.TYPE_DOUBLE, "0.0").addDependency(mppDep));
135            ret.addElement(new ParameterDefinition("Perturbations - Weights","Perturbations.DifferentRoomWeight", "Different room used", ParameterDefinition.TYPE_DOUBLE, "0.0").addDependency(mppDep));
136            ret.addElement(new ParameterDefinition("Perturbations - Weights","Perturbations.DifferentBuildingWeight", "Different building used", ParameterDefinition.TYPE_DOUBLE, "0.0").addDependency(mppDep));
137            ret.addElement(new ParameterDefinition("Perturbations - Weights","Perturbations.DifferentTimeWeight", "Different time used", ParameterDefinition.TYPE_DOUBLE, "0.0").addDependency(mppDep));
138            ret.addElement(new ParameterDefinition("Perturbations - Weights","Perturbations.DifferentDayWeight", "Different day(s) used", ParameterDefinition.TYPE_DOUBLE, "0.0").addDependency(mppDep));
139            ret.addElement(new ParameterDefinition("Perturbations - Weights","Perturbations.DifferentHourWeight", "Different hour(s) used", ParameterDefinition.TYPE_DOUBLE, "0.0").addDependency(mppDep));
140            ret.addElement(new ParameterDefinition("Perturbations - Weights","Perturbations.DeltaStudentConflictsWeight", "Delta of student conflicts", ParameterDefinition.TYPE_DOUBLE, "0.0").addDependency(mppDep));
141            ret.addElement(new ParameterDefinition("Perturbations - Weights","Perturbations.NewStudentConflictsWeight", "New students in conflict", ParameterDefinition.TYPE_DOUBLE, "0.0").addDependency(mppDep));
142            ret.addElement(new ParameterDefinition("Perturbations - Weights","Perturbations.TooFarForInstructorsWeight", "New placement too far from initial (fot instructors)", ParameterDefinition.TYPE_DOUBLE, "0.0").addDependency(mppDep));
143            ret.addElement(new ParameterDefinition("Perturbations - Weights","Perturbations.TooFarForStudentsWeight", "New placement too far from initial (for students)", ParameterDefinition.TYPE_DOUBLE, "0.0").addDependency(mppDep));
144            ret.addElement(new ParameterDefinition("Perturbations - Weights","Perturbations.DeltaInstructorDistancePreferenceWeight", "Delta of instructor distance preferences", ParameterDefinition.TYPE_DOUBLE, "0.0").addDependency(mppDep));
145            ret.addElement(new ParameterDefinition("Perturbations - Weights","Perturbations.DeltaRoomPreferenceWeight", "Delta of room preferences", ParameterDefinition.TYPE_DOUBLE, "0.0").addDependency(mppDep));
146            ret.addElement(new ParameterDefinition("Perturbations - Weights","Perturbations.DeltaTimePreferenceWeight", "Delta of time preferences", ParameterDefinition.TYPE_DOUBLE, "0.0").addDependency(mppDep));
147    
148            return ret;
149        }
150        
151        protected double getPenalty(Value assignedValue, Value initialValue) {
152            // assigned and initial value of the same lecture
153            // assigned might be null
154            Lecture lecture = (Lecture)initialValue.variable();
155            Placement assignedPlacement = (assignedValue==null?null:(Placement)assignedValue);
156            Placement initialPlacement = (Placement)initialValue;
157            double penalty = 0.0;
158            if (iDifferentPlacement!=0.0)
159                penalty += iDifferentPlacement;
160            if (iAffectedStudentWeight!=0.0)
161                penalty += iAffectedStudentWeight*lecture.countStudents();
162            if (iAffectedInstructorWeight!=0.0 && initialPlacement.getInstructorId()!=null)
163                penalty += iAffectedInstructorWeight;
164            if (assignedValue!=null) {
165                if ((iDifferentRoomWeight!=0.0 || iAffectedInstructorByRoomWeight!=0.0 || iAffectedStudentByRoomWeight!=0.0) && !initialPlacement.getRoomId().equals(assignedPlacement.getRoomId())) {
166                    penalty += iDifferentRoomWeight;
167                    if (initialPlacement.getInstructorId()!=null) penalty += iAffectedInstructorByRoomWeight;
168                    penalty += iAffectedStudentByRoomWeight * lecture.countStudents();
169                } if ((iDifferentBuildingWeight!=0.0 || iAffectedInstructorByBldgWeight!=0.0 || iAffectedStudentByBldgWeight!=0.0) && !initialPlacement.getBuildingId().equals(assignedPlacement.getBuildingId())) {
170                    penalty += iDifferentBuildingWeight;
171                    if (initialPlacement.getInstructorId()!=null) penalty += iAffectedInstructorByBldgWeight;
172                    penalty += iAffectedStudentByBldgWeight * lecture.countStudents();
173                } if ((iDifferentTimeWeight!=0.0 || iAffectedInstructorByTimeWeight!=0.0 || iAffectedStudentByTimeWeight!=0.0) && !initialPlacement.getTimeLocation().equals(assignedPlacement.getTimeLocation())) {
174                    penalty += iDifferentTimeWeight;
175                    if (initialPlacement.getInstructorId()!=null) penalty += iAffectedInstructorByTimeWeight;
176                    penalty += iAffectedStudentByTimeWeight * lecture.countStudents();
177                } if (iDifferentDayWeight!=0.0 && initialPlacement.getTimeLocation().getDayCode()!=assignedPlacement.getTimeLocation().getDayCode())
178                    penalty += iDifferentDayWeight;
179                if (iDifferentHourWeight!=0.0 && initialPlacement.getTimeLocation().getTimeCode()!=assignedPlacement.getTimeLocation().getTimeCode())
180                    penalty += iDifferentHourWeight;
181                if ((iTooFarForInstructorsWeight!=0.0 || iTooFarForStudentsWeight!=0.0) && !initialPlacement.getTimeLocation().equals(assignedPlacement.getTimeLocation())) {
182                    double distance = Placement.getDistance(initialPlacement, assignedPlacement);
183                    if (initialPlacement.getInstructorId()!=null && iTooFarForInstructorsWeight!=0.0) {
184                        if (distance>0.0 && distance<=5.0) {
185                            penalty += iTooFarForInstructorsWeight;
186                        } else if (distance>5.0 && distance<=20.0) {
187                            penalty += 2*iTooFarForInstructorsWeight;
188                        } else if (distance>20.0) {
189                            penalty += 10*iTooFarForInstructorsWeight;
190                        }
191                    }
192                    if (iTooFarForStudentsWeight!=0.0 && distance > 67.0)
193                        penalty += iTooFarForStudentsWeight*lecture.countStudents();
194                }
195                if (iDeltaStudentConflictsWeight!=0.0) {
196                    int newStudentConflicts = lecture.countStudentConflicts(assignedValue);
197                    int oldStudentConflicts = lecture.countInitialStudentConflicts();
198                    penalty += iDeltaStudentConflictsWeight * (newStudentConflicts-oldStudentConflicts);
199                }
200                if (iNewStudentConflictsWeight!=0.0) {
201                    Vector newStudentConflicts = lecture.conflictStudents(assignedValue);
202                    Set initialStudentConflicts = lecture.initialStudentConflicts();
203                    for (Enumeration e=newStudentConflicts.elements();e.hasMoreElements();)
204                        if (!initialStudentConflicts.contains(e.nextElement())) penalty += iNewStudentConflictsWeight;
205                }
206                if (iDeltaTimePreferenceWeight!=0.0) {
207                    penalty += iDeltaTimePreferenceWeight * (assignedPlacement.getTimeLocation().getPreference() - initialPlacement.getTimeLocation().getPreference());
208                }
209                if (iDeltaRoomPreferenceWeight!=0.0) {
210                    penalty += iDeltaRoomPreferenceWeight * (assignedPlacement.getRoomLocation().getPreference() - initialPlacement.getRoomLocation().getPreference());
211                }
212                if (iDeltaInstructorDistancePreferenceWeight!=0.0) {
213                    InstructorConstraint ic = lecture.getInstructorConstraint();
214                    if (ic!=null) for (Enumeration e=ic.variables().elements();e.hasMoreElements();) {
215                        Lecture lect = (Lecture)e.nextElement();
216                        if (lect.equals(lecture)) continue;
217                        int initialPreference = (lect.getInitialAssignment()==null?0:InstructorConstraint.getDistancePreference(initialPlacement,(Placement)lect.getInitialAssignment()));
218                        int assignedPreference = (lect.getAssignment()==null?0:InstructorConstraint.getDistancePreference(assignedPlacement,(Placement)lect.getAssignment()));
219                        penalty += iDeltaInstructorDistancePreferenceWeight * (assignedPreference - initialPreference);
220                    }
221                }
222            }
223            return penalty;
224        }
225        
226        public void getInfo(Dictionary info, Solution solution) {
227            super.getInfo(info, solution);
228            if (!iMPP) return;
229            int perts = 0;
230            long affectedStudents=0;
231            int affectedInstructors=0;
232            long affectedStudentsByTime=0;
233            int affectedInstructorsByTime=0;
234            long affectedStudentsByRoom=0;
235            int affectedInstructorsByRoom=0;
236            long affectedStudentsByBldg=0;
237            int affectedInstructorsByBldg=0;
238            int differentRoom=0;
239            int differentBuilding=0;
240            int differentTime=0;
241            int differentDay=0;
242            int differentHour=0;
243            int tooFarForInstructors=0;
244            int tooFarForStudents=0;
245            int deltaStudentConflicts=0;
246            int newStudentConflicts=0;
247            int deltaTimePreferences=0;
248            int deltaRoomPreferences=0;
249            int deltaInstructorDistancePreferences=0;
250            for (Enumeration e=solution.getModel().perturbVariables().elements();e.hasMoreElements();) {
251                Variable variable = (Variable)e.nextElement();
252                if (variable.getAssignment()==null || variable.getInitialAssignment()==null || variable.getAssignment().equals(variable.getInitialAssignment())) continue;
253                perts++;
254                Lecture lecture = (Lecture)variable ;
255                Placement assignedPlacement = (Placement)variable.getAssignment();
256                Placement initialPlacement = (Placement)variable.getInitialAssignment();
257                affectedStudents += lecture.countStudents();
258                if (initialPlacement.getInstructorId()!=null) affectedInstructors++;
259                if (!initialPlacement.getRoomId().equals(assignedPlacement.getRoomId())) {
260                    differentRoom++;
261                    if (initialPlacement.getInstructorId()!=null) affectedInstructorsByRoom++;
262                    affectedStudentsByRoom += lecture.countStudents();
263                }
264                if (!initialPlacement.getBuildingId().equals(assignedPlacement.getBuildingId())) {
265                    differentBuilding++;
266                    if (initialPlacement.getInstructorId()!=null) affectedInstructorsByBldg++;
267                    affectedStudentsByBldg += lecture.countStudents();
268                }
269                if (!initialPlacement.getTimeLocation().equals(assignedPlacement.getTimeLocation())) {
270                    differentTime++;
271                    if (initialPlacement.getInstructorId()!=null) affectedInstructorsByTime++;
272                    affectedStudentsByTime += lecture.countStudents();
273                }
274                if (initialPlacement.getTimeLocation().getDayCode()!=assignedPlacement.getTimeLocation().getDayCode()) differentDay++;
275                if (initialPlacement.getTimeLocation().getTimeCode()!=assignedPlacement.getTimeLocation().getTimeCode()) differentHour++;
276                if (!initialPlacement.getTimeLocation().equals(assignedPlacement.getTimeLocation())) {
277                    double distance = Placement.getDistance(initialPlacement, assignedPlacement);
278                    if (initialPlacement.getInstructorId()!=null) {
279                        if (distance>0.0 && distance<=5.0) tooFarForInstructors+=1;
280                        else if (distance>5.0 && distance<=20.0) tooFarForInstructors+=2;
281                        else if (distance>20.0) tooFarForInstructors+=10;
282                    }
283                    if (distance > 67.0) tooFarForStudents+=lecture.countStudents();
284                }
285                deltaStudentConflicts += lecture.countStudentConflicts(assignedPlacement) - lecture.countInitialStudentConflicts();
286                Vector newStudentConflictsSet = lecture.conflictStudents(assignedPlacement);
287                Set initialStudentConflicts = lecture.initialStudentConflicts();
288                for (Enumeration e1=newStudentConflictsSet.elements();e1.hasMoreElements();)
289                    if (!initialStudentConflicts.contains(e1.nextElement())) newStudentConflicts++;
290                deltaTimePreferences += assignedPlacement.getTimeLocation().getPreference() - initialPlacement.getTimeLocation().getPreference();
291                deltaRoomPreferences += assignedPlacement.getRoomLocation().getPreference() - initialPlacement.getRoomLocation().getPreference();
292                InstructorConstraint ic = lecture.getInstructorConstraint();
293                if (ic!=null) for (Enumeration e1=ic.variables().elements();e1.hasMoreElements();) {
294                    Lecture lect = (Lecture)e1.nextElement();
295                    if (lect.equals(lecture)) continue;
296                    int initialPreference = (lect.getInitialAssignment()==null?0:InstructorConstraint.getDistancePreference(initialPlacement,(Placement)lect.getInitialAssignment()));
297                    int assignedPreference = (lect.getAssignment()==null?0:InstructorConstraint.getDistancePreference(assignedPlacement,(Placement)lect.getAssignment()));
298                    deltaInstructorDistancePreferences += assignedPreference - initialPreference;
299                }
300            }
301            info.put("Perturbations: Different placement", String.valueOf(perts)+" (weighted "+sDoubleFormat.format(iDifferentPlacement*perts)+")");
302            info.put("Perturbations: Number of affected students", String.valueOf(affectedStudents)+" (weighted "+sDoubleFormat.format(iAffectedStudentWeight*affectedStudents)+")");
303            info.put("Perturbations: Number of affected instructors", String.valueOf(affectedInstructors)+" (weighted "+sDoubleFormat.format(iAffectedInstructorWeight*affectedInstructors)+")");
304            info.put("Perturbations: Number of affected students (time change)", String.valueOf(affectedStudentsByTime)+" (weighted "+sDoubleFormat.format(iAffectedStudentByTimeWeight*affectedStudentsByTime)+")");
305            info.put("Perturbations: Number of affected instructors (time change)", String.valueOf(affectedInstructorsByTime)+" (weighted "+sDoubleFormat.format(iAffectedInstructorByTimeWeight*affectedInstructorsByTime)+")");
306            info.put("Perturbations: Number of affected students (room change)", String.valueOf(affectedStudentsByRoom)+" (weighted "+sDoubleFormat.format(iAffectedStudentByRoomWeight*affectedStudentsByRoom)+")");
307            info.put("Perturbations: Number of affected instructors (room change)", String.valueOf(affectedInstructorsByRoom)+" (weighted "+sDoubleFormat.format(iAffectedInstructorByRoomWeight*affectedInstructorsByRoom)+")");
308            info.put("Perturbations: Number of affected students (bldg change)", String.valueOf(affectedStudentsByBldg)+" (weighted "+sDoubleFormat.format(iAffectedStudentByBldgWeight*affectedStudentsByBldg)+")");
309            info.put("Perturbations: Number of affected instructors (bldg change)", String.valueOf(affectedInstructorsByBldg)+" (weighted "+sDoubleFormat.format(iAffectedInstructorByBldgWeight*affectedInstructorsByBldg)+")");
310            info.put("Perturbations: Different room", String.valueOf(differentRoom)+" (weighted "+sDoubleFormat.format(iDifferentRoomWeight*differentRoom)+")");
311            info.put("Perturbations: Different building", String.valueOf(differentBuilding)+" (weighted "+sDoubleFormat.format(iDifferentBuildingWeight*differentBuilding)+")");
312            info.put("Perturbations: Different time", String.valueOf(differentTime)+" (weighted "+sDoubleFormat.format(iDifferentTimeWeight*differentTime)+")");
313            info.put("Perturbations: Different day", String.valueOf(differentDay)+" (weighted "+sDoubleFormat.format(iDifferentDayWeight*differentDay)+")");
314            info.put("Perturbations: Different hour", String.valueOf(differentHour)+" (weighted "+sDoubleFormat.format(iDifferentHourWeight*differentHour)+")");
315            info.put("Perturbations: New placement too far from initial (for instructors)", String.valueOf(tooFarForInstructors)+" (weighted "+sDoubleFormat.format(iTooFarForInstructorsWeight*tooFarForInstructors)+")");
316            info.put("Perturbations: New placement too far from initial (for students)", String.valueOf(tooFarForStudents)+" (weighted "+sDoubleFormat.format(iTooFarForStudentsWeight*tooFarForStudents)+")");
317            info.put("Perturbations: Delta student conflicts", String.valueOf(deltaStudentConflicts)+" (weighted "+sDoubleFormat.format(iDeltaStudentConflictsWeight*deltaStudentConflicts)+")");
318            info.put("Perturbations: New student conflicts", String.valueOf(newStudentConflicts)+" (weighted "+sDoubleFormat.format(iNewStudentConflictsWeight*newStudentConflicts)+")");
319            info.put("Perturbations: Delta time preferences", String.valueOf(deltaTimePreferences)+" (weighted "+sDoubleFormat.format(iDeltaTimePreferenceWeight*deltaTimePreferences)+")");
320            info.put("Perturbations: Delta room preferences", String.valueOf(deltaRoomPreferences)+" (weighted "+sDoubleFormat.format(iDeltaRoomPreferenceWeight*deltaRoomPreferences)+")");
321            info.put("Perturbations: Delta instructor distance preferences", String.valueOf(deltaInstructorDistancePreferences)+" (weighted "+sDoubleFormat.format(iDeltaInstructorDistancePreferenceWeight*deltaInstructorDistancePreferences)+")");
322        }
323        
324        public Hashtable getCompactInfo(Solution solution, boolean includeZero, boolean weighted) {
325            Hashtable info = new Hashtable();
326            if (!iMPP) return info;
327            int perts = 0;
328            long affectedStudents=0;
329            int affectedInstructors=0;
330            long affectedStudentsByTime=0;
331            int affectedInstructorsByTime=0;
332            long affectedStudentsByRoom=0;
333            int affectedInstructorsByRoom=0;
334            long affectedStudentsByBldg=0;
335            int affectedInstructorsByBldg=0;
336            int differentRoom=0;
337            int differentBuilding=0;
338            int differentTime=0;
339            int differentDay=0;
340            int differentHour=0;
341            int tooFarForInstructors=0;
342            int tooFarForStudents=0;
343            int deltaStudentConflicts=0;
344            int newStudentConflicts=0;
345            int deltaTimePreferences=0;
346            int deltaRoomPreferences=0;
347            int deltaInstructorDistancePreferences=0;
348            for (Enumeration e=solution.getModel().perturbVariables().elements();e.hasMoreElements();) {
349                Variable variable = (Variable)e.nextElement();
350                if (variable.getAssignment()==null || variable.getInitialAssignment()==null || variable.getAssignment().equals(variable.getInitialAssignment())) continue;
351                perts++;
352                Lecture lecture = (Lecture)variable ;
353                Placement assignedPlacement = (Placement)variable.getAssignment();
354                Placement initialPlacement = (Placement)variable.getInitialAssignment();
355                affectedStudents += lecture.countStudents();
356                if (initialPlacement.getInstructorId()!=null) affectedInstructors++;
357                if (!initialPlacement.getRoomId().equals(assignedPlacement.getRoomId())) {
358                    differentRoom++;
359                    if (initialPlacement.getInstructorId()!=null) affectedInstructorsByRoom++;
360                    affectedStudentsByRoom += lecture.countStudents();
361                }
362                if (!initialPlacement.getBuildingId().equals(assignedPlacement.getBuildingId())) {
363                    differentBuilding++;
364                    if (initialPlacement.getInstructorId()!=null) affectedInstructorsByBldg++;
365                    affectedStudentsByBldg += lecture.countStudents();
366                }
367                if (!initialPlacement.getTimeLocation().equals(assignedPlacement.getTimeLocation())) {
368                    differentTime++;
369                    if (initialPlacement.getInstructorId()!=null) affectedInstructorsByTime++;
370                    affectedStudentsByTime += lecture.countStudents();
371                }
372                if (initialPlacement.getTimeLocation().getDayCode()!=assignedPlacement.getTimeLocation().getDayCode()) differentDay++;
373                if (initialPlacement.getTimeLocation().getTimeCode()!=assignedPlacement.getTimeLocation().getTimeCode()) differentHour++;
374                if (!initialPlacement.getTimeLocation().equals(assignedPlacement.getTimeLocation())) {
375                    double distance = Placement.getDistance(initialPlacement, assignedPlacement);
376                    if (initialPlacement.getInstructorId()!=null) {
377                        if (distance>0.0 && distance<=5.0) tooFarForInstructors+=1;
378                        else if (distance>5.0 && distance<=20.0) tooFarForInstructors+=2;
379                        else if (distance>20.0) tooFarForInstructors+=10;
380                    }
381                    if (distance > 67.0) tooFarForStudents+=lecture.countStudents();
382                }
383                deltaStudentConflicts += lecture.countStudentConflicts(assignedPlacement) - lecture.countInitialStudentConflicts();
384                Vector newStudentConflictsSet = lecture.conflictStudents(assignedPlacement);
385                Set initialStudentConflicts = lecture.initialStudentConflicts();
386                for (Enumeration e1=newStudentConflictsSet.elements();e1.hasMoreElements();)
387                    if (!initialStudentConflicts.contains(e1.nextElement())) newStudentConflicts++;
388                deltaTimePreferences += assignedPlacement.getTimeLocation().getPreference() - initialPlacement.getTimeLocation().getPreference();
389                deltaRoomPreferences += assignedPlacement.getRoomLocation().getPreference() - initialPlacement.getRoomLocation().getPreference();
390                InstructorConstraint ic = lecture.getInstructorConstraint();
391                if (ic!=null) for (Enumeration e1=ic.variables().elements();e1.hasMoreElements();) {
392                    Lecture lect = (Lecture)e1.nextElement();
393                    if (lect.equals(lecture)) continue;
394                    int initialPreference = (lect.getInitialAssignment()==null?0:InstructorConstraint.getDistancePreference(initialPlacement,(Placement)lect.getInitialAssignment()));
395                    int assignedPreference = (lect.getAssignment()==null?0:InstructorConstraint.getDistancePreference(assignedPlacement,(Placement)lect.getAssignment()));
396                    deltaInstructorDistancePreferences += assignedPreference - initialPreference;
397                }
398            }
399            if (includeZero || iDifferentPlacement!=0.0)
400                info.put("Different placement", new Double(weighted?iDifferentPlacement*perts:perts));
401            if (includeZero || iAffectedStudentWeight!=0.0)
402                info.put("Affected students", new Double(weighted?iAffectedStudentWeight*affectedStudents:affectedStudents));
403            if (includeZero || iAffectedInstructorWeight!=0.0)
404                info.put("Affected instructors", new Double(weighted?iAffectedInstructorWeight*affectedInstructors:affectedInstructors));
405            if (includeZero || iAffectedStudentByTimeWeight!=0.0)
406                info.put("Affected students [time]", new Double(weighted?iAffectedStudentByTimeWeight*affectedStudentsByTime:affectedStudentsByTime));
407            if (includeZero || iAffectedInstructorByTimeWeight!=0.0)
408                info.put("Affected instructors [time]", new Double(weighted?iAffectedInstructorByTimeWeight*affectedInstructorsByTime:affectedInstructorsByTime));
409            if (includeZero || iAffectedStudentByRoomWeight!=0.0)
410                info.put("Affected students [room]", new Double(weighted?iAffectedStudentByRoomWeight*affectedStudentsByRoom:affectedStudentsByRoom));
411            if (includeZero || iAffectedInstructorByRoomWeight!=0.0)
412                info.put("Affected instructors [room]", new Double(weighted?iAffectedInstructorByRoomWeight*affectedInstructorsByRoom:affectedInstructorsByRoom));
413            if (includeZero || iAffectedStudentByBldgWeight!=0.0)
414                info.put("Affected students [bldg]", new Double(weighted?iAffectedStudentByBldgWeight*affectedStudentsByBldg:affectedStudentsByBldg));
415            if (includeZero || iAffectedInstructorByBldgWeight!=0.0)
416                info.put("Affected instructors [bldg]", new Double(weighted?iAffectedInstructorByBldgWeight*affectedInstructorsByBldg:affectedInstructorsByBldg));
417            if (includeZero || iDifferentRoomWeight!=0.0)
418                info.put("Different room", new Double(weighted?iDifferentRoomWeight*differentRoom:differentRoom));
419            if (includeZero || iDifferentBuildingWeight!=0.0)
420                info.put("Different building", new Double(weighted?iDifferentBuildingWeight*differentBuilding:differentBuilding));
421            if (includeZero || iDifferentTimeWeight!=0.0)
422                info.put("Different time", new Double(weighted?iDifferentTimeWeight*differentTime:differentTime));
423            if (includeZero || iDifferentDayWeight!=0.0)
424                info.put("Different day", new Double(weighted?iDifferentDayWeight*differentDay:differentDay));
425            if (includeZero || iDifferentHourWeight!=0.0)
426                info.put("Different hour", new Double(weighted?iDifferentHourWeight*differentHour:differentHour));
427            if (includeZero || iTooFarForInstructorsWeight!=0.0)
428                info.put("New placement too far for initial [instructors]", new Double(weighted?iTooFarForInstructorsWeight*tooFarForInstructors:tooFarForInstructors));
429            if (includeZero || iTooFarForStudentsWeight!=0.0)
430                info.put("New placement too far for initial [students]", new Double(weighted?iTooFarForStudentsWeight*tooFarForStudents:tooFarForStudents));
431            if (includeZero || iDeltaStudentConflictsWeight!=0.0)
432                info.put("Delta student conflicts", new Double(weighted?iDeltaStudentConflictsWeight*deltaStudentConflicts:deltaStudentConflicts));
433            if (includeZero || iNewStudentConflictsWeight!=0.0)
434                info.put("New student conflicts", new Double(weighted?iNewStudentConflictsWeight*newStudentConflicts:newStudentConflicts));
435            if (includeZero || iDeltaTimePreferenceWeight!=0.0)
436                info.put("Delta time preferences", new Double(weighted?iDeltaTimePreferenceWeight*deltaTimePreferences:deltaTimePreferences));
437            if (includeZero || iDeltaRoomPreferenceWeight!=0.0)
438                info.put("Delta room preferences", new Double(weighted?iDeltaRoomPreferenceWeight*deltaRoomPreferences:deltaRoomPreferences));
439            if (includeZero || iDeltaInstructorDistancePreferenceWeight!=0.0)
440                info.put("Delta instructor distance preferences", new Double(weighted?iDeltaInstructorDistancePreferenceWeight*deltaInstructorDistancePreferences:deltaInstructorDistancePreferences));
441            return info;
442        }    
443    }