001package org.cpsolver.coursett.heuristics;
002
003import java.util.HashMap;
004import java.util.Iterator;
005import java.util.List;
006import java.util.Map;
007import java.util.Set;
008
009import org.cpsolver.coursett.Constants;
010import org.cpsolver.coursett.constraint.InstructorConstraint;
011import org.cpsolver.coursett.model.Lecture;
012import org.cpsolver.coursett.model.Placement;
013import org.cpsolver.coursett.model.Student;
014import org.cpsolver.coursett.model.TimetableModel;
015import org.cpsolver.ifs.assignment.Assignment;
016import org.cpsolver.ifs.perturbations.DefaultPerturbationsCounter;
017import org.cpsolver.ifs.util.DataProperties;
018import org.cpsolver.ifs.util.DistanceMetric;
019
020
021/**
022 * Perturbation penalty computation. <br>
023 * <br>
024 * In practice, the strategy for computing perturbations needs to be extended.
025 * For example, a change in time is usually much worse than a movement to a
026 * different classroom. The number of enrolled/involved students should also be
027 * taken into account. Another factor is whether the solution has already been
028 * published or not. <br>
029 * The priorities for evaluating perturbations are as follows. Before publishing
030 * timetable:
031 * <ul>
032 * <li>minimize number of classes with time changes,
033 * <li>minimize number of student conflicts,
034 * <li>optimize satisfaction of problem soft constraints.
035 * </ul>
036 * <br>
037 * After publishing the timetable (class time changes are not allowed):
038 * <ul>
039 * <li>minimize number of additional (new) student conflicts,
040 * <li>minimize number of students with time changes,
041 * <li>minimize number of classes with time changes,
042 * <li>optimize satisfaction of problem soft constraints.
043 * </ul>
044 * In both cases, the number of classes with room change is not significant at
045 * all. Before the timetable is published, minimizing the number of classes with
046 * time changes is the most important criteria for the MPP as long as it does
047 * not create too many additional student conflicts in the process. Therefore,
048 * as a compromise, the cost (in equivalent conflicts) of changing the time
049 * assigned to a class equals a number like 5% of the students enrolled in that
050 * class. Otherwise none of our other criteria would have any importance. <br>
051 * <br>
052 * Similar properties apply between other criteria as well. To fulfill all these
053 * needs we have created a function (called perturbations penalty) which can be
054 * computed over a partial solution. This is a weighted sum of various
055 * perturbations criteria like the number of classes with time changes or the
056 * number of additional student conflicts. This perturbation penalty is added as
057 * an extra optimization criterion to the solution comparator and to value
058 * selection criterion, so we can also setup the weights between this
059 * perturbation penalty and other (initial) soft constraints. <br>
060 * <br>
061 * Parameters:
062 * <table border='1'><caption>Related Solver Parameters</caption>
063 * <tr>
064 * <th>Parameter</th>
065 * <th>Type</th>
066 * <th>Comment</th>
067 * </tr>
068 * <tr>
069 * <td>Perturbations.DifferentPlacement</td>
070 * <td>{@link Double}</td>
071 * <td>Different value than initial is assigned</td>
072 * </tr>
073 * <tr>
074 * <td>Perturbations.AffectedStudentWeight</td>
075 * <td>{@link Double}</td>
076 * <td>Number of students which are enrolled in a class which is placed to a
077 * different location than initial (a student can be included twice or more)</td>
078 * </tr>
079 * <tr>
080 * <td>Perturbations.AffectedInstructorWeight</td>
081 * <td>{@link Double}</td>
082 * <td>Number of instructors which are assigned to classes which are placed to
083 * different locations than initial (an instructor can be included twice or
084 * more)</td>
085 * </tr>
086 * <tr>
087 * <td>Perturbations.DifferentRoomWeight</td>
088 * <td>{@link Double}</td>
089 * <td>Number of classes which are placed to a different room than initial</td>
090 * </tr>
091 * <tr>
092 * <td>Perturbations.DifferentBuildingWeight</td>
093 * <td>{@link Double}</td>
094 * <td>Number of classes which are placed to a different building than initial</td>
095 * </tr>
096 * <tr>
097 * <td>Perturbations.DifferentTimeWeight</td>
098 * <td>{@link Double}</td>
099 * <td>Number of classes which are placed in a different time than initial</td>
100 * </tr>
101 * <tr>
102 * <td>Perturbations.DifferentDayWeight</td>
103 * <td>{@link Double}</td>
104 * <td>Number of classes which are placed in a different days than initial</td>
105 * </tr>
106 * <tr>
107 * <td>Perturbations.DifferentHourWeight</td>
108 * <td>{@link Double}</td>
109 * <td>Number of classes which are placed in a different hours than initial</td>
110 * </tr>
111 * <tr>
112 * <td>Perturbations.DeltaStudentConflictsWeight</td>
113 * <td>{@link Double}</td>
114 * <td>Difference of student conflicts of classes assigned to current placements
115 * instead of initial placements. It is a difference between number of students
116 * conflicts which are in the initial solution and the current one. Student
117 * conflicts created by classes without initial placement are not taken into
118 * account</td>
119 * </tr>
120 * <tr>
121 * <td>Perturbations.NewStudentConflictsWeight</td>
122 * <td>{@link Double}</td>
123 * <td>New created student conflicts -- particular students are taken into
124 * account. Student conflicts created by classes without initial placement are
125 * not taken into account</td>
126 * </tr>
127 * <tr>
128 * <td>Perturbations.TooFarForInstructorsWeight</td>
129 * <td>{@link Double}</td>
130 * <td>New placement of a class is too far from the intial placement
131 * (instructor-wise). It is computed only when the class has an instructor
132 * assigned, moreover:
133 * <ul>
134 * <li>0 &lt; distance(currentPlacement,initialPlacement) &lt;= 5 .. weight is taken
135 * once
136 * <li>5 &lt; distance(currentPlacement,initialPlacement) &lt;= 20 .. weight is taken
137 * twice
138 * <li>20 &lt; distance(currentPlacement,initialPlacement) .. weight is taken ten
139 * times
140 * </ul>
141 * </td>
142 * </tr>
143 * <tr>
144 * <td>Perturbations.TooFarForStudentsWeight</td>
145 * <td>{@link Double}</td>
146 * <td>New placement of a class is too far from the intial placement
147 * (instructor-student). It is weighted by the number of students enrolled in
148 * the class when distance(currentPlacement,initialPlacement) &gt; 67</td>
149 * </tr>
150 * <tr>
151 * <td>Perturbations.DeltaInstructorDistancePreferenceWeight</td>
152 * <td>{@link Double}</td>
153 * <td>Difference between number of instructor distance preferences of the
154 * initial (but maybe inconsistent) solution and the current solution.
155 * Instructor distance preferences of classes without initial placement are not
156 * taken into account</td>
157 * </tr>
158 * <tr>
159 * <td>Perturbations.DeltaRoomPreferenceWeight</td>
160 * <td>{@link Double}</td>
161 * <td>Difference between room preferences of the initial and the current
162 * solution. Room preferences of classes without initial placement are not taken
163 * into account</td>
164 * </tr>
165 * <tr>
166 * <td>Perturbations.DeltaTimePreferenceWeight</td>
167 * <td>{@link Double}</td>
168 * <td>Difference between time preferences of the initial and the current
169 * solution. Time preferences of classes without initial placement are not taken
170 * into account</td>
171 * </tr>
172 * <tr>
173 * <td>Perturbations.AffectedStudentByTimeWeight</td>
174 * <td>{@link Double}</td>
175 * <td>Number of students which are enrolled in a class which is placed to a
176 * different time than initial</td>
177 * </tr>
178 * <tr>
179 * <td>Perturbations.AffectedInstructorByTimeWeight</td>
180 * <td>{@link Double}</td>
181 * <td>Number of instructors which are assigned to classes which are placed to
182 * different time than initial</td>
183 * </tr>
184 * <tr>
185 * <td>Perturbations.AffectedStudentByRoomWeight</td>
186 * <td>{@link Double}</td>
187 * <td>Number of students which are enrolled in a class which is placed to a
188 * different room than initial</td>
189 * </tr>
190 * <tr>
191 * <td>Perturbations.AffectedInstructorByRoomWeight</td>
192 * <td>{@link Double}</td>
193 * <td>Number of instructors which are assigned to classes which are placed to
194 * different room than initial</td>
195 * </tr>
196 * <tr>
197 * <td>Perturbations.AffectedStudentByBldgWeight</td>
198 * <td>{@link Double}</td>
199 * <td>Number of students which are enrolled in a class which is placed to a
200 * different building than initial</td>
201 * </tr>
202 * <tr>
203 * <td>Perturbations.AffectedInstructorByBldgWeight</td>
204 * <td>{@link Double}</td>
205 * <td>Number of instructors which are assigned to classes which are placed to
206 * different building than initial</td>
207 * </tr>
208 * </table>
209 * 
210 * @author  Tomáš Müller
211 * @version CourseTT 1.3 (University Course Timetabling)<br>
212 *          Copyright (C) 2006 - 2014 Tomáš Müller<br>
213 *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
214 *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
215 * <br>
216 *          This library is free software; you can redistribute it and/or modify
217 *          it under the terms of the GNU Lesser General Public License as
218 *          published by the Free Software Foundation; either version 3 of the
219 *          License, or (at your option) any later version. <br>
220 * <br>
221 *          This library is distributed in the hope that it will be useful, but
222 *          WITHOUT ANY WARRANTY; without even the implied warranty of
223 *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
224 *          Lesser General Public License for more details. <br>
225 * <br>
226 *          You should have received a copy of the GNU Lesser General Public
227 *          License along with this library; if not see
228 *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
229 */
230
231public class UniversalPerturbationsCounter extends DefaultPerturbationsCounter<Lecture, Placement> {
232    private double iDifferentPlacement = 1.0;
233    private double iAffectedStudentWeight = 0.0;
234    private double iAffectedInstructorWeight = 0.0;
235    private double iAffectedStudentByTimeWeight = 0.0;
236    private double iAffectedInstructorByTimeWeight = 0.0;
237    private double iAffectedStudentByRoomWeight = 0.0;
238    private double iAffectedInstructorByRoomWeight = 0.0;
239    private double iAffectedStudentByBldgWeight = 0.0;
240    private double iAffectedInstructorByBldgWeight = 0.0;
241    private double iDifferentRoomWeight = 0.0;
242    private double iDifferentBuildingWeight = 0.0;
243    private double iDifferentTimeWeight = 0.0;
244    private double iDifferentDayWeight = 0.0;
245    private double iDifferentHourWeight = 0.0;
246    private double iNewStudentConflictsWeight = 0.0;
247    private double iDeltaStudentConflictsWeight = 0.0;
248    private double iTooFarForInstructorsWeight = 0.0;
249    private double iTooFarForStudentsWeight = 0.0;
250    private double iDeltaInstructorDistancePreferenceWeight = 0.0;
251    private double iDeltaRoomPreferenceWeight = 0.0;
252    private double iDeltaTimePreferenceWeight = 0.0;
253    private boolean iMPP = false;
254    private DistanceMetric iDistanceMetric = null;
255
256    public UniversalPerturbationsCounter(DataProperties properties) {
257        super(properties);
258        iMPP = properties.getPropertyBoolean("General.MPP", false);
259        iDifferentPlacement = properties.getPropertyDouble("Perturbations.DifferentPlacement", iDifferentPlacement);
260        iAffectedStudentWeight = properties.getPropertyDouble("Perturbations.AffectedStudentWeight",
261                iAffectedStudentWeight);
262        iAffectedInstructorWeight = properties.getPropertyDouble("Perturbations.AffectedInstructorWeight",
263                iAffectedInstructorWeight);
264        iAffectedStudentByTimeWeight = properties.getPropertyDouble("Perturbations.AffectedStudentByTimeWeight",
265                iAffectedStudentByTimeWeight);
266        iAffectedInstructorByTimeWeight = properties.getPropertyDouble("Perturbations.AffectedInstructorByTimeWeight",
267                iAffectedInstructorByTimeWeight);
268        iAffectedStudentByRoomWeight = properties.getPropertyDouble("Perturbations.AffectedStudentByRoomWeight",
269                iAffectedStudentByRoomWeight);
270        iAffectedInstructorByRoomWeight = properties.getPropertyDouble("Perturbations.AffectedInstructorByRoomWeight",
271                iAffectedInstructorByRoomWeight);
272        iAffectedStudentByBldgWeight = properties.getPropertyDouble("Perturbations.AffectedStudentByBldgWeight",
273                iAffectedStudentByBldgWeight);
274        iAffectedInstructorByBldgWeight = properties.getPropertyDouble("Perturbations.AffectedInstructorByBldgWeight",
275                iAffectedInstructorByBldgWeight);
276        iDifferentRoomWeight = properties.getPropertyDouble("Perturbations.DifferentRoomWeight", iDifferentRoomWeight);
277        iDifferentBuildingWeight = properties.getPropertyDouble("Perturbations.DifferentBuildingWeight",
278                iDifferentBuildingWeight);
279        iDifferentTimeWeight = properties.getPropertyDouble("Perturbations.DifferentTimeWeight", iDifferentTimeWeight);
280        iDifferentDayWeight = properties.getPropertyDouble("Perturbations.DifferentDayWeight", iDifferentDayWeight);
281        iDifferentHourWeight = properties.getPropertyDouble("Perturbations.DifferentHourWeight", iDifferentHourWeight);
282        iDeltaStudentConflictsWeight = properties.getPropertyDouble("Perturbations.DeltaStudentConflictsWeight",
283                iDeltaStudentConflictsWeight);
284        iNewStudentConflictsWeight = properties.getPropertyDouble("Perturbations.NewStudentConflictsWeight",
285                iNewStudentConflictsWeight);
286        iTooFarForInstructorsWeight = properties.getPropertyDouble("Perturbations.TooFarForInstructorsWeight",
287                iTooFarForInstructorsWeight);
288        iTooFarForStudentsWeight = properties.getPropertyDouble("Perturbations.TooFarForStudentsWeight",
289                iTooFarForStudentsWeight);
290        iDeltaInstructorDistancePreferenceWeight = properties.getPropertyDouble(
291                "Perturbations.DeltaInstructorDistancePreferenceWeight", iDeltaInstructorDistancePreferenceWeight);
292        iDeltaRoomPreferenceWeight = properties.getPropertyDouble("Perturbations.DeltaRoomPreferenceWeight",
293                iDeltaRoomPreferenceWeight);
294        iDeltaTimePreferenceWeight = properties.getPropertyDouble("Perturbations.DeltaTimePreferenceWeight",
295                iDeltaTimePreferenceWeight);
296        iDistanceMetric = new DistanceMetric(properties);
297    }
298
299    @Override
300    protected double getPenalty(Assignment<Lecture, Placement> assignment, Placement assignedPlacement, Placement initialPlacement) {
301        // assigned and initial value of the same lecture
302        // assigned might be null
303        Lecture lecture = initialPlacement.variable();
304        double penalty = 0.0;
305        if (iDifferentPlacement != 0.0)
306            penalty += iDifferentPlacement;
307        if (iAffectedStudentWeight != 0.0)
308            penalty += iAffectedStudentWeight * lecture.classLimit(assignment);
309        if (iAffectedInstructorWeight != 0.0)
310            penalty += iAffectedInstructorWeight * lecture.getInstructorConstraints().size();
311        if (assignedPlacement != null) {
312            if ((iDifferentRoomWeight != 0.0 || iAffectedInstructorByRoomWeight != 0.0 || iAffectedStudentByRoomWeight != 0.0)) {
313                int nrDiff = initialPlacement.nrDifferentRooms(assignedPlacement);
314                penalty += nrDiff * iDifferentRoomWeight;
315                penalty += nrDiff * iAffectedInstructorByRoomWeight * lecture.getInstructorConstraints().size();
316                penalty += nrDiff * iAffectedStudentByRoomWeight * lecture.classLimit(assignment);
317            }
318            if ((iDifferentBuildingWeight != 0.0 || iAffectedInstructorByBldgWeight != 0.0 || iAffectedStudentByBldgWeight != 0.0)) {
319                int nrDiff = initialPlacement.nrDifferentBuildings(assignedPlacement);
320                penalty += nrDiff * iDifferentBuildingWeight;
321                penalty += nrDiff * iAffectedInstructorByBldgWeight * lecture.getInstructorConstraints().size();
322                penalty += nrDiff * iAffectedStudentByBldgWeight * lecture.classLimit(assignment);
323            }
324            if ((iDifferentTimeWeight != 0.0 || iAffectedInstructorByTimeWeight != 0.0 || iAffectedStudentByTimeWeight != 0.0)
325                    && !initialPlacement.getTimeLocation().equals(assignedPlacement.getTimeLocation())) {
326                penalty += iDifferentTimeWeight;
327                penalty += iAffectedInstructorByTimeWeight * lecture.getInstructorConstraints().size();
328                penalty += iAffectedStudentByTimeWeight * lecture.classLimit(assignment);
329            }
330            if (iDifferentDayWeight != 0.0
331                    && initialPlacement.getTimeLocation().getDayCode() != assignedPlacement.getTimeLocation()
332                            .getDayCode())
333                penalty += iDifferentDayWeight;
334            if (iDifferentHourWeight != 0.0
335                    && initialPlacement.getTimeLocation().getStartSlot() != assignedPlacement.getTimeLocation()
336                            .getStartSlot())
337                penalty += iDifferentHourWeight;
338            if ((iTooFarForInstructorsWeight != 0.0 || iTooFarForStudentsWeight != 0.0)
339                    && !initialPlacement.getTimeLocation().equals(assignedPlacement.getTimeLocation())) {
340                double distance = Placement.getDistanceInMeters(iDistanceMetric, initialPlacement, assignedPlacement);
341                if (!lecture.getInstructorConstraints().isEmpty() && iTooFarForInstructorsWeight != 0.0) {
342                    if (distance > iDistanceMetric.getInstructorNoPreferenceLimit() && distance <= iDistanceMetric.getInstructorDiscouragedLimit()) {
343                        penalty += Constants.sPreferenceLevelDiscouraged * iTooFarForInstructorsWeight
344                                * lecture.getInstructorConstraints().size();
345                    } else if (distance > iDistanceMetric.getInstructorDiscouragedLimit() && distance <= iDistanceMetric.getInstructorProhibitedLimit()) {
346                        penalty += Constants.sPreferenceLevelStronglyDiscouraged * iTooFarForInstructorsWeight
347                                * lecture.getInstructorConstraints().size();
348                    } else if (distance > iDistanceMetric.getInstructorProhibitedLimit()) {
349                        penalty += Constants.sPreferenceLevelProhibited * iTooFarForInstructorsWeight
350                                * lecture.getInstructorConstraints().size();
351                    }
352                }
353                if (iTooFarForStudentsWeight != 0.0
354                        && distance > iDistanceMetric.minutes2meters(10))
355                    penalty += iTooFarForStudentsWeight * lecture.classLimit(assignment);
356            }
357            if (iDeltaStudentConflictsWeight != 0.0) {
358                int newStudentConflicts = lecture.countStudentConflicts(assignment, assignedPlacement);
359                int oldStudentConflicts = lecture.countInitialStudentConflicts();
360                penalty += iDeltaStudentConflictsWeight * (newStudentConflicts - oldStudentConflicts);
361            }
362            if (iNewStudentConflictsWeight != 0.0) {
363                Set<Student> newStudentConflicts = lecture.conflictStudents(assignment, assignedPlacement);
364                Set<Student> initialStudentConflicts = lecture.initialStudentConflicts();
365                for (Iterator<Student> i = newStudentConflicts.iterator(); i.hasNext();)
366                    if (!initialStudentConflicts.contains(i.next()))
367                        penalty += iNewStudentConflictsWeight;
368            }
369            if (iDeltaTimePreferenceWeight != 0.0) {
370                penalty += iDeltaTimePreferenceWeight * (assignedPlacement.getTimeLocation().getNormalizedPreference() - initialPlacement.getTimeLocation().getNormalizedPreference());
371            }
372            if (iDeltaRoomPreferenceWeight != 0.0) {
373                penalty += iDeltaRoomPreferenceWeight * (assignedPlacement.sumRoomPreference() - initialPlacement.sumRoomPreference());
374            }
375            if (iDeltaInstructorDistancePreferenceWeight != 0.0) {
376                for (InstructorConstraint ic : lecture.getInstructorConstraints()) {
377                    for (Lecture lect : ic.variables()) {
378                        if (lect.equals(lecture))
379                            continue;
380                        int initialPreference = (lect.getInitialAssignment() == null ? Constants.sPreferenceLevelNeutral : ic.getDistancePreference(initialPlacement, lect.getInitialAssignment()));
381                        int assignedPreference = (assignment.getValue(lect) == null ? Constants.sPreferenceLevelNeutral : ic.getDistancePreference(assignedPlacement, assignment.getValue(lect)));
382                        penalty += iDeltaInstructorDistancePreferenceWeight * (assignedPreference - initialPreference);
383                    }
384                }
385            }
386        }
387        return penalty;
388    }
389
390    public void getInfo(Assignment<Lecture, Placement> assignment, TimetableModel model, Map<String, String> info) {
391        getInfo(assignment, model, info, null);
392    }
393
394    public void getInfo(Assignment<Lecture, Placement> assignment, TimetableModel model, Map<String, String> info, List<Lecture> variables) {
395        if (variables == null)
396            super.getInfo(assignment, model, info);
397        else
398            super.getInfo(assignment, model, info, variables);
399        if (!iMPP)
400            return;
401        int perts = 0;
402        long affectedStudents = 0;
403        int affectedInstructors = 0;
404        long affectedStudentsByTime = 0;
405        int affectedInstructorsByTime = 0;
406        long affectedStudentsByRoom = 0;
407        int affectedInstructorsByRoom = 0;
408        long affectedStudentsByBldg = 0;
409        int affectedInstructorsByBldg = 0;
410        int differentRoom = 0;
411        int differentBuilding = 0;
412        int differentTime = 0;
413        int differentDay = 0;
414        int differentHour = 0;
415        int tooFarForInstructors = 0;
416        int tooFarForStudents = 0;
417        int deltaStudentConflicts = 0;
418        int newStudentConflicts = 0;
419        double deltaTimePreferences = 0;
420        int deltaRoomPreferences = 0;
421        int deltaInstructorDistancePreferences = 0;
422        for (Lecture lecture : (variables == null ? model.perturbVariables(assignment, model.variablesWithInitialValue(), false) : model.perturbVariables(assignment, variables, false))) {
423            if (assignment.getValue(lecture) == null || lecture.getInitialAssignment() == null || assignment.getValue(lecture).equals(lecture.getInitialAssignment()))
424                continue;
425            perts++;
426            Placement assignedPlacement = assignment.getValue(lecture);
427            Placement initialPlacement = lecture.getInitialAssignment();
428            affectedStudents += lecture.classLimit(assignment);
429            affectedInstructors += lecture.getInstructorConstraints().size();
430
431            int nrDiff = initialPlacement.nrDifferentRooms(assignedPlacement);
432            differentRoom += nrDiff;
433            affectedInstructorsByRoom += nrDiff * lecture.getInstructorConstraints().size();
434            affectedStudentsByRoom += nrDiff * lecture.classLimit(assignment);
435
436            nrDiff = initialPlacement.nrDifferentBuildings(assignedPlacement);
437            differentBuilding += nrDiff;
438            affectedInstructorsByBldg += nrDiff * lecture.getInstructorConstraints().size();
439            affectedStudentsByBldg += nrDiff * lecture.classLimit(assignment);
440
441            deltaRoomPreferences += assignedPlacement.sumRoomPreference() - initialPlacement.sumRoomPreference();
442
443            if (!initialPlacement.getTimeLocation().equals(assignedPlacement.getTimeLocation())) {
444                differentTime++;
445                affectedInstructorsByTime += lecture.getInstructorConstraints().size();
446                affectedStudentsByTime += lecture.classLimit(assignment);
447            }
448            if (initialPlacement.getTimeLocation().getDayCode() != assignedPlacement.getTimeLocation().getDayCode())
449                differentDay++;
450            if (initialPlacement.getTimeLocation().getStartSlot() != assignedPlacement.getTimeLocation().getStartSlot())
451                differentHour++;
452            if (!initialPlacement.getTimeLocation().equals(assignedPlacement.getTimeLocation())) {
453                double distance = Placement.getDistanceInMeters(iDistanceMetric, initialPlacement, assignedPlacement);
454                if (!lecture.getInstructorConstraints().isEmpty()) {
455                    if (distance > iDistanceMetric.getInstructorNoPreferenceLimit() && distance <= iDistanceMetric.getInstructorDiscouragedLimit()) {
456                        tooFarForInstructors += Constants.sPreferenceLevelDiscouraged * lecture.getInstructorConstraints().size();
457                    } else if (distance > iDistanceMetric.getInstructorDiscouragedLimit() && distance <= iDistanceMetric.getInstructorProhibitedLimit()) {
458                        tooFarForInstructors += Constants.sPreferenceLevelStronglyDiscouraged * lecture.getInstructorConstraints().size();
459                    } else if (distance > iDistanceMetric.getInstructorProhibitedLimit()) {
460                        tooFarForInstructors += Constants.sPreferenceLevelProhibited * lecture.getInstructorConstraints().size();
461                    }
462                }
463                if (distance > iDistanceMetric.minutes2meters(10))
464                    tooFarForStudents += lecture.classLimit(assignment);
465            }
466            deltaStudentConflicts += lecture.countStudentConflicts(assignment, assignedPlacement) - lecture.countInitialStudentConflicts();
467            Set<Student> newStudentConflictsSet = lecture.conflictStudents(assignment, assignedPlacement);
468            Set<Student> initialStudentConflicts = lecture.initialStudentConflicts();
469            for (Iterator<Student> e1 = newStudentConflictsSet.iterator(); e1.hasNext();)
470                if (!initialStudentConflicts.contains(e1.next()))
471                    newStudentConflicts++;
472            deltaTimePreferences += assignedPlacement.getTimeLocation().getNormalizedPreference() - initialPlacement.getTimeLocation().getNormalizedPreference();
473            for (InstructorConstraint ic : lecture.getInstructorConstraints()) {
474                for (Lecture lect : ic.variables()) {
475                    if (lect.equals(lecture))
476                        continue;
477                    int initialPreference = (lect.getInitialAssignment() == null ? Constants.sPreferenceLevelNeutral
478                            : ic.getDistancePreference(initialPlacement, lect.getInitialAssignment()));
479                    int assignedPreference = (assignedPlacement == null ? Constants.sPreferenceLevelNeutral : ic.getDistancePreference(assignedPlacement, assignedPlacement));
480                    deltaInstructorDistancePreferences += assignedPreference - initialPreference;
481                }
482            }
483        }
484        if (perts != 0)
485            info.put("Perturbations: Different placement", String.valueOf(perts) + " (weighted " + sDoubleFormat.format(iDifferentPlacement * perts) + ")");
486        if (affectedStudents != 0)
487            info.put("Perturbations: Number of affected students", String.valueOf(affectedStudents) + " (weighted " + sDoubleFormat.format(iAffectedStudentWeight * affectedStudents) + ")");
488        if (affectedInstructors != 0)
489            info.put("Perturbations: Number of affected instructors", String.valueOf(affectedInstructors) + " (weighted " + sDoubleFormat.format(iAffectedInstructorWeight * affectedInstructors) + ")");
490        if (affectedStudentsByTime != 0)
491            info.put("Perturbations: Number of affected students [time]", String.valueOf(affectedStudentsByTime) +
492                    " (weighted " + sDoubleFormat.format(iAffectedStudentByTimeWeight * affectedStudentsByTime) + ")");
493        if (affectedInstructorsByTime != 0)
494            info.put("Perturbations: Number of affected instructors [time]", String.valueOf(affectedInstructorsByTime) +
495                    " (weighted " + sDoubleFormat.format(iAffectedInstructorByTimeWeight * affectedInstructorsByTime) + ")");
496        if (affectedStudentsByRoom != 0)
497            info.put("Perturbations: Number of affected students [room]", String.valueOf(affectedStudentsByRoom) + 
498                    " (weighted " + sDoubleFormat.format(iAffectedStudentByRoomWeight * affectedStudentsByRoom) + ")");
499        if (affectedInstructorsByRoom != 0)
500            info.put("Perturbations: Number of affected instructors [room]", String.valueOf(affectedInstructorsByRoom) +
501                    " (weighted " + sDoubleFormat.format(iAffectedInstructorByRoomWeight * affectedInstructorsByRoom) + ")");
502        if (affectedStudentsByBldg != 0)
503            info.put("Perturbations: Number of affected students [bldg]", String.valueOf(affectedStudentsByBldg) +
504                    " (weighted " + sDoubleFormat.format(iAffectedStudentByBldgWeight * affectedStudentsByBldg) + ")");
505        if (affectedInstructorsByBldg != 0)
506            info.put("Perturbations: Number of affected instructors [bldg]", String.valueOf(affectedInstructorsByBldg) +
507                    " (weighted " + sDoubleFormat.format(iAffectedInstructorByBldgWeight * affectedInstructorsByBldg) + ")");
508        if (differentRoom != 0)
509            info.put("Perturbations: Different room", String.valueOf(differentRoom) + " (weighted " + sDoubleFormat.format(iDifferentRoomWeight * differentRoom) + ")");
510        if (differentBuilding != 0)
511            info.put("Perturbations: Different building", String.valueOf(differentBuilding) + " (weighted " + sDoubleFormat.format(iDifferentBuildingWeight * differentBuilding) + ")");
512        if (differentTime != 0)
513            info.put("Perturbations: Different time", String.valueOf(differentTime) + " (weighted " + sDoubleFormat.format(iDifferentTimeWeight * differentTime) + ")");
514        if (differentDay != 0)
515            info.put("Perturbations: Different day", String.valueOf(differentDay) + " (weighted " + sDoubleFormat.format(iDifferentDayWeight * differentDay) + ")");
516        if (differentHour != 0)
517            info.put("Perturbations: Different hour", String.valueOf(differentHour) + " (weighted " + sDoubleFormat.format(iDifferentHourWeight * differentHour) + ")");
518        if (tooFarForInstructors != 0)
519            info.put("Perturbations: New placement too far from initial [instructors]", String.valueOf(tooFarForInstructors) + 
520                    " (weighted " + sDoubleFormat.format(iTooFarForInstructorsWeight * tooFarForInstructors) + ")");
521        if (tooFarForStudents != 0)
522            info.put("Perturbations: New placement too far from initial [students]", String.valueOf(tooFarForStudents) +
523                    " (weighted " + sDoubleFormat.format(iTooFarForStudentsWeight * tooFarForStudents) + ")");
524        if (deltaStudentConflicts != 0)
525            info.put("Perturbations: Delta student conflicts", String.valueOf(deltaStudentConflicts) + " (weighted " + sDoubleFormat.format(iDeltaStudentConflictsWeight * deltaStudentConflicts) + ")");
526        if (newStudentConflicts != 0)
527            info.put("Perturbations: New student conflicts", String.valueOf(newStudentConflicts) + " (weighted " + sDoubleFormat.format(iNewStudentConflictsWeight * newStudentConflicts) + ")");
528        if (deltaTimePreferences != 0)
529            info.put("Perturbations: Delta time preferences", String.valueOf(deltaTimePreferences) + " (weighted " + sDoubleFormat.format(iDeltaTimePreferenceWeight * deltaTimePreferences) + ")");
530        if (deltaRoomPreferences != 0)
531            info.put("Perturbations: Delta room preferences", String.valueOf(deltaRoomPreferences) + " (weighted " + sDoubleFormat.format(iDeltaRoomPreferenceWeight * deltaRoomPreferences) + ")");
532        if (deltaInstructorDistancePreferences != 0)
533            info.put("Perturbations: Delta instructor distance preferences", String.valueOf(deltaInstructorDistancePreferences) +
534                    " (weighted " + sDoubleFormat.format(iDeltaInstructorDistancePreferenceWeight * deltaInstructorDistancePreferences) + ")");
535    }
536
537    public Map<String, Double> getCompactInfo(Assignment<Lecture, Placement> assignment, TimetableModel model, boolean includeZero, boolean weighted) {
538        Map<String, Double> info = new HashMap<String, Double>();
539        if (!iMPP)
540            return info;
541        int perts = 0;
542        long affectedStudents = 0;
543        int affectedInstructors = 0;
544        long affectedStudentsByTime = 0;
545        int affectedInstructorsByTime = 0;
546        long affectedStudentsByRoom = 0;
547        int affectedInstructorsByRoom = 0;
548        long affectedStudentsByBldg = 0;
549        int affectedInstructorsByBldg = 0;
550        int differentRoom = 0;
551        int differentBuilding = 0;
552        int differentTime = 0;
553        int differentDay = 0;
554        int differentHour = 0;
555        int tooFarForInstructors = 0;
556        int tooFarForStudents = 0;
557        int deltaStudentConflicts = 0;
558        int newStudentConflicts = 0;
559        double deltaTimePreferences = 0;
560        int deltaRoomPreferences = 0;
561        int deltaInstructorDistancePreferences = 0;
562        for (Lecture lecture : model.perturbVariables(assignment)) {
563            if (assignment.getValue(lecture) == null || lecture.getInitialAssignment() == null || assignment.getValue(lecture).equals(lecture.getInitialAssignment()))
564                continue;
565            perts++;
566            Placement assignedPlacement = assignment.getValue(lecture);
567            Placement initialPlacement = lecture.getInitialAssignment();
568            affectedStudents += lecture.classLimit(assignment);
569            affectedInstructors += lecture.getInstructorConstraints().size();
570
571            int nrDiff = initialPlacement.nrDifferentRooms(assignedPlacement);
572            differentRoom += nrDiff;
573            affectedInstructorsByRoom += nrDiff * lecture.getInstructorConstraints().size();
574            affectedStudentsByRoom += nrDiff * lecture.classLimit(assignment);
575
576            nrDiff = initialPlacement.nrDifferentBuildings(initialPlacement);
577            differentBuilding += nrDiff;
578            affectedInstructorsByBldg += nrDiff * lecture.getInstructorConstraints().size();
579            affectedStudentsByBldg += nrDiff * lecture.classLimit(assignment);
580
581            deltaRoomPreferences += assignedPlacement.sumRoomPreference() - initialPlacement.sumRoomPreference();
582
583            if (!initialPlacement.getTimeLocation().equals(assignedPlacement.getTimeLocation())) {
584                differentTime++;
585                affectedInstructorsByTime += lecture.getInstructorConstraints().size();
586                affectedStudentsByTime += lecture.classLimit(assignment);
587            }
588            if (initialPlacement.getTimeLocation().getDayCode() != assignedPlacement.getTimeLocation().getDayCode())
589                differentDay++;
590            if (initialPlacement.getTimeLocation().getStartSlot() != assignedPlacement.getTimeLocation().getStartSlot())
591                differentHour++;
592            if (!initialPlacement.getTimeLocation().equals(assignedPlacement.getTimeLocation())) {
593                double distance = Placement.getDistanceInMeters(iDistanceMetric, initialPlacement, assignedPlacement);
594                if (!lecture.getInstructorConstraints().isEmpty()) {
595                    if (distance > iDistanceMetric.getInstructorNoPreferenceLimit() && distance <= iDistanceMetric.getInstructorDiscouragedLimit()) {
596                        tooFarForInstructors += Constants.sPreferenceLevelDiscouraged;
597                    } else if (distance > iDistanceMetric.getInstructorDiscouragedLimit() && distance <= iDistanceMetric.getInstructorProhibitedLimit()) {
598                        tooFarForInstructors += Constants.sPreferenceLevelStronglyDiscouraged;
599                    } else if (distance > iDistanceMetric.getInstructorProhibitedLimit()) {
600                        tooFarForInstructors += Constants.sPreferenceLevelProhibited;
601                    }
602                }
603                if (distance > iDistanceMetric.minutes2meters(10))
604                    tooFarForStudents += lecture.classLimit(assignment);
605            }
606            deltaStudentConflicts += lecture.countStudentConflicts(assignment, assignedPlacement) - lecture.countInitialStudentConflicts();
607            Set<Student> newStudentConflictsSet = lecture.conflictStudents(assignment, assignedPlacement);
608            Set<Student> initialStudentConflicts = lecture.initialStudentConflicts();
609            for (Iterator<Student> e1 = newStudentConflictsSet.iterator(); e1.hasNext();)
610                if (!initialStudentConflicts.contains(e1.next()))
611                    newStudentConflicts++;
612            deltaTimePreferences += assignedPlacement.getTimeLocation().getNormalizedPreference() - initialPlacement.getTimeLocation().getNormalizedPreference();
613            for (InstructorConstraint ic : lecture.getInstructorConstraints()) {
614                if (ic != null)
615                    for (Lecture lect : ic.variables()) {
616                        if (lect.equals(lecture))
617                            continue;
618                        int initialPreference = (lect.getInitialAssignment() == null ? Constants.sPreferenceLevelNeutral
619                                : ic.getDistancePreference(initialPlacement, lect.getInitialAssignment()));
620                        int assignedPreference = (assignedPlacement == null ? Constants.sPreferenceLevelNeutral : ic.getDistancePreference(assignedPlacement, assignedPlacement));
621                        deltaInstructorDistancePreferences += assignedPreference - initialPreference;
622                    }
623            }
624        }
625        if (includeZero || iDifferentPlacement != 0.0)
626            info.put("Different placement", Double.valueOf(weighted ? iDifferentPlacement * perts : perts));
627        if (includeZero || iAffectedStudentWeight != 0.0)
628            info.put("Affected students", Double.valueOf(weighted ? iAffectedStudentWeight * affectedStudents : affectedStudents));
629        if (includeZero || iAffectedInstructorWeight != 0.0)
630            info.put("Affected instructors", Double.valueOf(weighted ? iAffectedInstructorWeight * affectedInstructors : affectedInstructors));
631        if (includeZero || iAffectedStudentByTimeWeight != 0.0)
632            info.put("Affected students [time]", Double.valueOf(weighted ? iAffectedStudentByTimeWeight * affectedStudentsByTime : affectedStudentsByTime));
633        if (includeZero || iAffectedInstructorByTimeWeight != 0.0)
634            info.put("Affected instructors [time]", Double.valueOf(weighted ? iAffectedInstructorByTimeWeight * affectedInstructorsByTime : affectedInstructorsByTime));
635        if (includeZero || iAffectedStudentByRoomWeight != 0.0)
636            info.put("Affected students [room]", Double.valueOf(weighted ? iAffectedStudentByRoomWeight * affectedStudentsByRoom : affectedStudentsByRoom));
637        if (includeZero || iAffectedInstructorByRoomWeight != 0.0)
638            info.put("Affected instructors [room]", Double.valueOf(weighted ? iAffectedInstructorByRoomWeight * affectedInstructorsByRoom : affectedInstructorsByRoom));
639        if (includeZero || iAffectedStudentByBldgWeight != 0.0)
640            info.put("Affected students [bldg]", Double.valueOf(weighted ? iAffectedStudentByBldgWeight * affectedStudentsByBldg : affectedStudentsByBldg));
641        if (includeZero || iAffectedInstructorByBldgWeight != 0.0)
642            info.put("Affected instructors [bldg]", Double.valueOf(weighted ? iAffectedInstructorByBldgWeight * affectedInstructorsByBldg : affectedInstructorsByBldg));
643        if (includeZero || iDifferentRoomWeight != 0.0)
644            info.put("Different room", Double.valueOf(weighted ? iDifferentRoomWeight * differentRoom : differentRoom));
645        if (includeZero || iDifferentBuildingWeight != 0.0)
646            info.put("Different building", Double.valueOf(weighted ? iDifferentBuildingWeight * differentBuilding : differentBuilding));
647        if (includeZero || iDifferentTimeWeight != 0.0)
648            info.put("Different time", Double.valueOf(weighted ? iDifferentTimeWeight * differentTime : differentTime));
649        if (includeZero || iDifferentDayWeight != 0.0)
650            info.put("Different day", Double.valueOf(weighted ? iDifferentDayWeight * differentDay : differentDay));
651        if (includeZero || iDifferentHourWeight != 0.0)
652            info.put("Different hour", Double.valueOf(weighted ? iDifferentHourWeight * differentHour : differentHour));
653        if (includeZero || iTooFarForInstructorsWeight != 0.0)
654            info.put("New placement too far for initial [instructors]", Double.valueOf(weighted ? iTooFarForInstructorsWeight * tooFarForInstructors : tooFarForInstructors));
655        if (includeZero || iTooFarForStudentsWeight != 0.0)
656            info.put("New placement too far for initial [students]", Double.valueOf(weighted ? iTooFarForStudentsWeight * tooFarForStudents : tooFarForStudents));
657        if (includeZero || iDeltaStudentConflictsWeight != 0.0)
658            info.put("Delta student conflicts", Double.valueOf(weighted ? iDeltaStudentConflictsWeight * deltaStudentConflicts : deltaStudentConflicts));
659        if (includeZero || iNewStudentConflictsWeight != 0.0)
660            info.put("New student conflicts", Double.valueOf(weighted ? iNewStudentConflictsWeight * newStudentConflicts : newStudentConflicts));
661        if (includeZero || iDeltaTimePreferenceWeight != 0.0)
662            info.put("Delta time preferences", Double.valueOf(weighted ? iDeltaTimePreferenceWeight * deltaTimePreferences : deltaTimePreferences));
663        if (includeZero || iDeltaRoomPreferenceWeight != 0.0)
664            info.put("Delta room preferences", Double.valueOf(weighted ? iDeltaRoomPreferenceWeight * deltaRoomPreferences : deltaRoomPreferences));
665        if (includeZero || iDeltaInstructorDistancePreferenceWeight != 0.0)
666            info.put("Delta instructor distance preferences", Double.valueOf(weighted ? iDeltaInstructorDistancePreferenceWeight * deltaInstructorDistancePreferences : deltaInstructorDistancePreferences));
667        return info;
668    }
669
670    public Map<String, Double> getCompactInfo(Assignment<Lecture, Placement> assignment, TimetableModel model, Placement assignedPlacement, boolean includeZero,
671            boolean weighted) {
672        Map<String, Double> info = new HashMap<String, Double>();
673        if (!iMPP)
674            return info;
675        Lecture lecture = assignedPlacement.variable();
676        Placement initialPlacement = lecture.getInitialAssignment();
677        if (initialPlacement == null || initialPlacement.equals(assignedPlacement))
678            return info;
679        int perts = 1;
680        long affectedStudents = lecture.classLimit(assignment);
681        int affectedInstructors = lecture.getInstructorConstraints().size();
682        long affectedStudentsByTime = (initialPlacement.getTimeLocation().equals(assignedPlacement.getTimeLocation()) ? 0 : lecture.classLimit(assignment));
683        int affectedInstructorsByTime = (initialPlacement.getTimeLocation().equals(assignedPlacement.getTimeLocation()) ? 0 : lecture.getInstructorConstraints().size());
684
685        int differentRoom = initialPlacement.nrDifferentRooms(assignedPlacement);
686        int affectedInstructorsByRoom = differentRoom * lecture.getInstructorConstraints().size();
687        long affectedStudentsByRoom = differentRoom * lecture.classLimit(assignment);
688
689        int differentBuilding = initialPlacement.nrDifferentBuildings(initialPlacement);
690        int affectedInstructorsByBldg = differentBuilding * lecture.getInstructorConstraints().size();
691        long affectedStudentsByBldg = differentBuilding * lecture.classLimit(assignment);
692
693        int deltaRoomPreferences = assignedPlacement.sumRoomPreference() - initialPlacement.sumRoomPreference();
694
695        int differentTime = (initialPlacement.getTimeLocation().equals(assignedPlacement.getTimeLocation()) ? 0 : 1);
696        int differentDay = (initialPlacement.getTimeLocation().getDayCode() != assignedPlacement.getTimeLocation().getDayCode() ? 1 : 0);
697        int differentHour = (initialPlacement.getTimeLocation().getStartSlot() != assignedPlacement.getTimeLocation().getStartSlot() ? 1 : 0);
698        int tooFarForInstructors = 0;
699        int tooFarForStudents = 0;
700        int deltaStudentConflicts = lecture.countStudentConflicts(assignment, assignedPlacement) - lecture.countInitialStudentConflicts();
701        int newStudentConflicts = 0;
702        double deltaTimePreferences = (assignedPlacement.getTimeLocation().getNormalizedPreference() -
703                initialPlacement.getTimeLocation().getNormalizedPreference());
704        int deltaInstructorDistancePreferences = 0;
705
706        double distance = Placement.getDistanceInMeters(iDistanceMetric, initialPlacement, assignedPlacement);
707        if (!lecture.getInstructorConstraints().isEmpty()) {
708            if (distance > iDistanceMetric.getInstructorNoPreferenceLimit() && distance <= iDistanceMetric.getInstructorDiscouragedLimit()) {
709                tooFarForInstructors += lecture.getInstructorConstraints().size();
710            } else if (distance > iDistanceMetric.getInstructorDiscouragedLimit() && distance <= iDistanceMetric.getInstructorProhibitedLimit()) {
711                tooFarForInstructors += 2 * lecture.getInstructorConstraints().size();
712            } else if (distance > iDistanceMetric.getInstructorProhibitedLimit()) {
713                tooFarForInstructors += 10 * lecture.getInstructorConstraints().size();
714            }
715        }
716        if (distance > iDistanceMetric.minutes2meters(10))
717            tooFarForStudents = lecture.classLimit(assignment);
718
719        Set<Student> newStudentConflictsVect = lecture.conflictStudents(assignment, assignedPlacement);
720        Set<Student> initialStudentConflicts = lecture.initialStudentConflicts();
721        for (Iterator<Student> e = newStudentConflictsVect.iterator(); e.hasNext();)
722            if (!initialStudentConflicts.contains(e.next()))
723                newStudentConflicts++;
724
725        for (InstructorConstraint ic : lecture.getInstructorConstraints()) {
726            for (Lecture lect : ic.variables()) {
727                if (lect.equals(lecture))
728                    continue;
729                int initialPreference = (lect.getInitialAssignment() == null ? Constants.sPreferenceLevelNeutral : ic.getDistancePreference(initialPlacement, lect.getInitialAssignment()));
730                int assignedPreference = (assignment.getValue(lect) == null ? Constants.sPreferenceLevelNeutral : ic.getDistancePreference(assignedPlacement, assignment.getValue(lect)));
731                deltaInstructorDistancePreferences += (assignedPreference - initialPreference);
732            }
733        }
734
735        if (includeZero || iDifferentPlacement != 0.0)
736            info.put("Different placement", Double.valueOf(weighted ? iDifferentPlacement * perts : perts));
737        if (includeZero || iAffectedStudentWeight != 0.0)
738            info.put("Affected students", Double.valueOf(weighted ? iAffectedStudentWeight * affectedStudents : affectedStudents));
739        if (includeZero || iAffectedInstructorWeight != 0.0)
740            info.put("Affected instructors", Double.valueOf(weighted ? iAffectedInstructorWeight * affectedInstructors : affectedInstructors));
741        if (includeZero || iAffectedStudentByTimeWeight != 0.0)
742            info.put("Affected students [time]", Double.valueOf(weighted ? iAffectedStudentByTimeWeight * affectedStudentsByTime : affectedStudentsByTime));
743        if (includeZero || iAffectedInstructorByTimeWeight != 0.0)
744            info.put("Affected instructors [time]", Double.valueOf(weighted ? iAffectedInstructorByTimeWeight * affectedInstructorsByTime : affectedInstructorsByTime));
745        if (includeZero || iAffectedStudentByRoomWeight != 0.0)
746            info.put("Affected students [room]", Double.valueOf(weighted ? iAffectedStudentByRoomWeight * affectedStudentsByRoom : affectedStudentsByRoom));
747        if (includeZero || iAffectedInstructorByRoomWeight != 0.0)
748            info.put("Affected instructors [room]", Double.valueOf(weighted ? iAffectedInstructorByRoomWeight * affectedInstructorsByRoom : affectedInstructorsByRoom));
749        if (includeZero || iAffectedStudentByBldgWeight != 0.0)
750            info.put("Affected students [bldg]", Double.valueOf(weighted ? iAffectedStudentByBldgWeight * affectedStudentsByBldg : affectedStudentsByBldg));
751        if (includeZero || iAffectedInstructorByBldgWeight != 0.0)
752            info.put("Affected instructors [bldg]", Double.valueOf(weighted ? iAffectedInstructorByBldgWeight * affectedInstructorsByBldg : affectedInstructorsByBldg));
753        if (includeZero || iDifferentRoomWeight != 0.0)
754            info.put("Different room", Double.valueOf(weighted ? iDifferentRoomWeight * differentRoom : differentRoom));
755        if (includeZero || iDifferentBuildingWeight != 0.0)
756            info.put("Different building", Double.valueOf(weighted ? iDifferentBuildingWeight * differentBuilding : differentBuilding));
757        if (includeZero || iDifferentTimeWeight != 0.0)
758            info.put("Different time", Double.valueOf(weighted ? iDifferentTimeWeight * differentTime : differentTime));
759        if (includeZero || iDifferentDayWeight != 0.0)
760            info.put("Different day", Double.valueOf(weighted ? iDifferentDayWeight * differentDay : differentDay));
761        if (includeZero || iDifferentHourWeight != 0.0)
762            info.put("Different hour", Double.valueOf(weighted ? iDifferentHourWeight * differentHour : differentHour));
763        if (includeZero || iTooFarForInstructorsWeight != 0.0)
764            info.put("New placement too far for initial [instructors]", Double.valueOf(weighted ? iTooFarForInstructorsWeight * tooFarForInstructors : tooFarForInstructors));
765        if (includeZero || iTooFarForStudentsWeight != 0.0)
766            info.put("New placement too far for initial [students]", Double.valueOf(weighted ? iTooFarForStudentsWeight * tooFarForStudents : tooFarForStudents));
767        if (includeZero || iDeltaStudentConflictsWeight != 0.0)
768            info.put("Delta student conflicts", Double.valueOf(weighted ? iDeltaStudentConflictsWeight * deltaStudentConflicts : deltaStudentConflicts));
769        if (includeZero || iNewStudentConflictsWeight != 0.0)
770            info.put("New student conflicts", Double.valueOf(weighted ? iNewStudentConflictsWeight * newStudentConflicts : newStudentConflicts));
771        if (includeZero || iDeltaTimePreferenceWeight != 0.0)
772            info.put("Delta time preferences", Double.valueOf(weighted ? iDeltaTimePreferenceWeight * deltaTimePreferences : deltaTimePreferences));
773        if (includeZero || iDeltaRoomPreferenceWeight != 0.0)
774            info.put("Delta room preferences", Double.valueOf(weighted ? iDeltaRoomPreferenceWeight * deltaRoomPreferences : deltaRoomPreferences));
775        if (includeZero || iDeltaInstructorDistancePreferenceWeight != 0.0)
776            info.put("Delta instructor distance preferences", Double.valueOf(weighted ? iDeltaInstructorDistancePreferenceWeight * deltaInstructorDistancePreferences : deltaInstructorDistancePreferences));
777        return info;
778    }
779}