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' summary='Related Solver Parameters'>
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 * @version CourseTT 1.3 (University Course Timetabling)<br>
211 *          Copyright (C) 2006 - 2014 Tomáš Müller<br>
212 *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
213 *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
214 * <br>
215 *          This library is free software; you can redistribute it and/or modify
216 *          it under the terms of the GNU Lesser General Public License as
217 *          published by the Free Software Foundation; either version 3 of the
218 *          License, or (at your option) any later version. <br>
219 * <br>
220 *          This library is distributed in the hope that it will be useful, but
221 *          WITHOUT ANY WARRANTY; without even the implied warranty of
222 *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
223 *          Lesser General Public License for more details. <br>
224 * <br>
225 *          You should have received a copy of the GNU Lesser General Public
226 *          License along with this library; if not see
227 *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
228 */
229
230public class UniversalPerturbationsCounter extends DefaultPerturbationsCounter<Lecture, Placement> {
231    private double iDifferentPlacement = 1.0;
232    private double iAffectedStudentWeight = 0.0;
233    private double iAffectedInstructorWeight = 0.0;
234    private double iAffectedStudentByTimeWeight = 0.0;
235    private double iAffectedInstructorByTimeWeight = 0.0;
236    private double iAffectedStudentByRoomWeight = 0.0;
237    private double iAffectedInstructorByRoomWeight = 0.0;
238    private double iAffectedStudentByBldgWeight = 0.0;
239    private double iAffectedInstructorByBldgWeight = 0.0;
240    private double iDifferentRoomWeight = 0.0;
241    private double iDifferentBuildingWeight = 0.0;
242    private double iDifferentTimeWeight = 0.0;
243    private double iDifferentDayWeight = 0.0;
244    private double iDifferentHourWeight = 0.0;
245    private double iNewStudentConflictsWeight = 0.0;
246    private double iDeltaStudentConflictsWeight = 0.0;
247    private double iTooFarForInstructorsWeight = 0.0;
248    private double iTooFarForStudentsWeight = 0.0;
249    private double iDeltaInstructorDistancePreferenceWeight = 0.0;
250    private double iDeltaRoomPreferenceWeight = 0.0;
251    private double iDeltaTimePreferenceWeight = 0.0;
252    private boolean iMPP = false;
253    private DistanceMetric iDistanceMetric = null;
254
255    public UniversalPerturbationsCounter(DataProperties properties) {
256        super(properties);
257        iMPP = properties.getPropertyBoolean("General.MPP", false);
258        iDifferentPlacement = properties.getPropertyDouble("Perturbations.DifferentPlacement", iDifferentPlacement);
259        iAffectedStudentWeight = properties.getPropertyDouble("Perturbations.AffectedStudentWeight",
260                iAffectedStudentWeight);
261        iAffectedInstructorWeight = properties.getPropertyDouble("Perturbations.AffectedInstructorWeight",
262                iAffectedInstructorWeight);
263        iAffectedStudentByTimeWeight = properties.getPropertyDouble("Perturbations.AffectedStudentByTimeWeight",
264                iAffectedStudentByTimeWeight);
265        iAffectedInstructorByTimeWeight = properties.getPropertyDouble("Perturbations.AffectedInstructorByTimeWeight",
266                iAffectedInstructorByTimeWeight);
267        iAffectedStudentByRoomWeight = properties.getPropertyDouble("Perturbations.AffectedStudentByRoomWeight",
268                iAffectedStudentByRoomWeight);
269        iAffectedInstructorByRoomWeight = properties.getPropertyDouble("Perturbations.AffectedInstructorByRoomWeight",
270                iAffectedInstructorByRoomWeight);
271        iAffectedStudentByBldgWeight = properties.getPropertyDouble("Perturbations.AffectedStudentByBldgWeight",
272                iAffectedStudentByBldgWeight);
273        iAffectedInstructorByBldgWeight = properties.getPropertyDouble("Perturbations.AffectedInstructorByBldgWeight",
274                iAffectedInstructorByBldgWeight);
275        iDifferentRoomWeight = properties.getPropertyDouble("Perturbations.DifferentRoomWeight", iDifferentRoomWeight);
276        iDifferentBuildingWeight = properties.getPropertyDouble("Perturbations.DifferentBuildingWeight",
277                iDifferentBuildingWeight);
278        iDifferentTimeWeight = properties.getPropertyDouble("Perturbations.DifferentTimeWeight", iDifferentTimeWeight);
279        iDifferentDayWeight = properties.getPropertyDouble("Perturbations.DifferentDayWeight", iDifferentDayWeight);
280        iDifferentHourWeight = properties.getPropertyDouble("Perturbations.DifferentHourWeight", iDifferentHourWeight);
281        iDeltaStudentConflictsWeight = properties.getPropertyDouble("Perturbations.DeltaStudentConflictsWeight",
282                iDeltaStudentConflictsWeight);
283        iNewStudentConflictsWeight = properties.getPropertyDouble("Perturbations.NewStudentConflictsWeight",
284                iNewStudentConflictsWeight);
285        iTooFarForInstructorsWeight = properties.getPropertyDouble("Perturbations.TooFarForInstructorsWeight",
286                iTooFarForInstructorsWeight);
287        iTooFarForStudentsWeight = properties.getPropertyDouble("Perturbations.TooFarForStudentsWeight",
288                iTooFarForStudentsWeight);
289        iDeltaInstructorDistancePreferenceWeight = properties.getPropertyDouble(
290                "Perturbations.DeltaInstructorDistancePreferenceWeight", iDeltaInstructorDistancePreferenceWeight);
291        iDeltaRoomPreferenceWeight = properties.getPropertyDouble("Perturbations.DeltaRoomPreferenceWeight",
292                iDeltaRoomPreferenceWeight);
293        iDeltaTimePreferenceWeight = properties.getPropertyDouble("Perturbations.DeltaTimePreferenceWeight",
294                iDeltaTimePreferenceWeight);
295        iDistanceMetric = new DistanceMetric(properties);
296    }
297
298    @Override
299    protected double getPenalty(Assignment<Lecture, Placement> assignment, Placement assignedPlacement, Placement initialPlacement) {
300        // assigned and initial value of the same lecture
301        // assigned might be null
302        Lecture lecture = initialPlacement.variable();
303        double penalty = 0.0;
304        if (iDifferentPlacement != 0.0)
305            penalty += iDifferentPlacement;
306        if (iAffectedStudentWeight != 0.0)
307            penalty += iAffectedStudentWeight * lecture.classLimit(assignment);
308        if (iAffectedInstructorWeight != 0.0)
309            penalty += iAffectedInstructorWeight * lecture.getInstructorConstraints().size();
310        if (assignedPlacement != null) {
311            if ((iDifferentRoomWeight != 0.0 || iAffectedInstructorByRoomWeight != 0.0 || iAffectedStudentByRoomWeight != 0.0)) {
312                int nrDiff = initialPlacement.nrDifferentRooms(assignedPlacement);
313                penalty += nrDiff * iDifferentRoomWeight;
314                penalty += nrDiff * iAffectedInstructorByRoomWeight * lecture.getInstructorConstraints().size();
315                penalty += nrDiff * iAffectedStudentByRoomWeight * lecture.classLimit(assignment);
316            }
317            if ((iDifferentBuildingWeight != 0.0 || iAffectedInstructorByBldgWeight != 0.0 || iAffectedStudentByBldgWeight != 0.0)) {
318                int nrDiff = initialPlacement.nrDifferentBuildings(assignedPlacement);
319                penalty += nrDiff * iDifferentBuildingWeight;
320                penalty += nrDiff * iAffectedInstructorByBldgWeight * lecture.getInstructorConstraints().size();
321                penalty += nrDiff * iAffectedStudentByBldgWeight * lecture.classLimit(assignment);
322            }
323            if ((iDifferentTimeWeight != 0.0 || iAffectedInstructorByTimeWeight != 0.0 || iAffectedStudentByTimeWeight != 0.0)
324                    && !initialPlacement.getTimeLocation().equals(assignedPlacement.getTimeLocation())) {
325                penalty += iDifferentTimeWeight;
326                penalty += iAffectedInstructorByTimeWeight * lecture.getInstructorConstraints().size();
327                penalty += iAffectedStudentByTimeWeight * lecture.classLimit(assignment);
328            }
329            if (iDifferentDayWeight != 0.0
330                    && initialPlacement.getTimeLocation().getDayCode() != assignedPlacement.getTimeLocation()
331                            .getDayCode())
332                penalty += iDifferentDayWeight;
333            if (iDifferentHourWeight != 0.0
334                    && initialPlacement.getTimeLocation().getStartSlot() != assignedPlacement.getTimeLocation()
335                            .getStartSlot())
336                penalty += iDifferentHourWeight;
337            if ((iTooFarForInstructorsWeight != 0.0 || iTooFarForStudentsWeight != 0.0)
338                    && !initialPlacement.getTimeLocation().equals(assignedPlacement.getTimeLocation())) {
339                double distance = Placement.getDistanceInMeters(iDistanceMetric, initialPlacement, assignedPlacement);
340                if (!lecture.getInstructorConstraints().isEmpty() && iTooFarForInstructorsWeight != 0.0) {
341                    if (distance > iDistanceMetric.getInstructorNoPreferenceLimit() && distance <= iDistanceMetric.getInstructorDiscouragedLimit()) {
342                        penalty += Constants.sPreferenceLevelDiscouraged * iTooFarForInstructorsWeight
343                                * lecture.getInstructorConstraints().size();
344                    } else if (distance > iDistanceMetric.getInstructorDiscouragedLimit() && distance <= iDistanceMetric.getInstructorProhibitedLimit()) {
345                        penalty += Constants.sPreferenceLevelStronglyDiscouraged * iTooFarForInstructorsWeight
346                                * lecture.getInstructorConstraints().size();
347                    } else if (distance > iDistanceMetric.getInstructorProhibitedLimit()) {
348                        penalty += Constants.sPreferenceLevelProhibited * iTooFarForInstructorsWeight
349                                * lecture.getInstructorConstraints().size();
350                    }
351                }
352                if (iTooFarForStudentsWeight != 0.0
353                        && distance > iDistanceMetric.minutes2meters(10))
354                    penalty += iTooFarForStudentsWeight * lecture.classLimit(assignment);
355            }
356            if (iDeltaStudentConflictsWeight != 0.0) {
357                int newStudentConflicts = lecture.countStudentConflicts(assignment, assignedPlacement);
358                int oldStudentConflicts = lecture.countInitialStudentConflicts();
359                penalty += iDeltaStudentConflictsWeight * (newStudentConflicts - oldStudentConflicts);
360            }
361            if (iNewStudentConflictsWeight != 0.0) {
362                Set<Student> newStudentConflicts = lecture.conflictStudents(assignment, assignedPlacement);
363                Set<Student> initialStudentConflicts = lecture.initialStudentConflicts();
364                for (Iterator<Student> i = newStudentConflicts.iterator(); i.hasNext();)
365                    if (!initialStudentConflicts.contains(i.next()))
366                        penalty += iNewStudentConflictsWeight;
367            }
368            if (iDeltaTimePreferenceWeight != 0.0) {
369                penalty += iDeltaTimePreferenceWeight * (assignedPlacement.getTimeLocation().getNormalizedPreference() - initialPlacement.getTimeLocation().getNormalizedPreference());
370            }
371            if (iDeltaRoomPreferenceWeight != 0.0) {
372                penalty += iDeltaRoomPreferenceWeight * (assignedPlacement.sumRoomPreference() - initialPlacement.sumRoomPreference());
373            }
374            if (iDeltaInstructorDistancePreferenceWeight != 0.0) {
375                for (InstructorConstraint ic : lecture.getInstructorConstraints()) {
376                    for (Lecture lect : ic.variables()) {
377                        if (lect.equals(lecture))
378                            continue;
379                        int initialPreference = (lect.getInitialAssignment() == null ? Constants.sPreferenceLevelNeutral : ic.getDistancePreference(initialPlacement, lect.getInitialAssignment()));
380                        int assignedPreference = (assignment.getValue(lect) == null ? Constants.sPreferenceLevelNeutral : ic.getDistancePreference(assignedPlacement, assignment.getValue(lect)));
381                        penalty += iDeltaInstructorDistancePreferenceWeight * (assignedPreference - initialPreference);
382                    }
383                }
384            }
385        }
386        return penalty;
387    }
388
389    public void getInfo(Assignment<Lecture, Placement> assignment, TimetableModel model, Map<String, String> info) {
390        getInfo(assignment, model, info, null);
391    }
392
393    public void getInfo(Assignment<Lecture, Placement> assignment, TimetableModel model, Map<String, String> info, List<Lecture> variables) {
394        if (variables == null)
395            super.getInfo(assignment, model, info);
396        else
397            super.getInfo(assignment, model, info, variables);
398        if (!iMPP)
399            return;
400        int perts = 0;
401        long affectedStudents = 0;
402        int affectedInstructors = 0;
403        long affectedStudentsByTime = 0;
404        int affectedInstructorsByTime = 0;
405        long affectedStudentsByRoom = 0;
406        int affectedInstructorsByRoom = 0;
407        long affectedStudentsByBldg = 0;
408        int affectedInstructorsByBldg = 0;
409        int differentRoom = 0;
410        int differentBuilding = 0;
411        int differentTime = 0;
412        int differentDay = 0;
413        int differentHour = 0;
414        int tooFarForInstructors = 0;
415        int tooFarForStudents = 0;
416        int deltaStudentConflicts = 0;
417        int newStudentConflicts = 0;
418        double deltaTimePreferences = 0;
419        int deltaRoomPreferences = 0;
420        int deltaInstructorDistancePreferences = 0;
421        for (Lecture lecture : (variables == null ? model.perturbVariables(assignment) : model.perturbVariables(assignment, variables))) {
422            if (assignment.getValue(lecture) == null || lecture.getInitialAssignment() == null || assignment.getValue(lecture).equals(lecture.getInitialAssignment()))
423                continue;
424            perts++;
425            Placement assignedPlacement = assignment.getValue(lecture);
426            Placement initialPlacement = lecture.getInitialAssignment();
427            affectedStudents += lecture.classLimit(assignment);
428            affectedInstructors += lecture.getInstructorConstraints().size();
429
430            int nrDiff = initialPlacement.nrDifferentRooms(assignedPlacement);
431            differentRoom += nrDiff;
432            affectedInstructorsByRoom += nrDiff * lecture.getInstructorConstraints().size();
433            affectedStudentsByRoom += nrDiff * lecture.classLimit(assignment);
434
435            nrDiff = initialPlacement.nrDifferentBuildings(assignedPlacement);
436            differentBuilding += nrDiff;
437            affectedInstructorsByBldg += nrDiff * lecture.getInstructorConstraints().size();
438            affectedStudentsByBldg += nrDiff * lecture.classLimit(assignment);
439
440            deltaRoomPreferences += assignedPlacement.sumRoomPreference() - initialPlacement.sumRoomPreference();
441
442            if (!initialPlacement.getTimeLocation().equals(assignedPlacement.getTimeLocation())) {
443                differentTime++;
444                affectedInstructorsByTime += lecture.getInstructorConstraints().size();
445                affectedStudentsByTime += lecture.classLimit(assignment);
446            }
447            if (initialPlacement.getTimeLocation().getDayCode() != assignedPlacement.getTimeLocation().getDayCode())
448                differentDay++;
449            if (initialPlacement.getTimeLocation().getStartSlot() != assignedPlacement.getTimeLocation().getStartSlot())
450                differentHour++;
451            if (!initialPlacement.getTimeLocation().equals(assignedPlacement.getTimeLocation())) {
452                double distance = Placement.getDistanceInMeters(iDistanceMetric, initialPlacement, assignedPlacement);
453                if (!lecture.getInstructorConstraints().isEmpty()) {
454                    if (distance > iDistanceMetric.getInstructorNoPreferenceLimit() && distance <= iDistanceMetric.getInstructorDiscouragedLimit()) {
455                        tooFarForInstructors += Constants.sPreferenceLevelDiscouraged * lecture.getInstructorConstraints().size();
456                    } else if (distance > iDistanceMetric.getInstructorDiscouragedLimit() && distance <= iDistanceMetric.getInstructorProhibitedLimit()) {
457                        tooFarForInstructors += Constants.sPreferenceLevelStronglyDiscouraged * lecture.getInstructorConstraints().size();
458                    } else if (distance > iDistanceMetric.getInstructorProhibitedLimit()) {
459                        tooFarForInstructors += Constants.sPreferenceLevelProhibited * lecture.getInstructorConstraints().size();
460                    }
461                }
462                if (distance > iDistanceMetric.minutes2meters(10))
463                    tooFarForStudents += lecture.classLimit(assignment);
464            }
465            deltaStudentConflicts += lecture.countStudentConflicts(assignment, assignedPlacement) - lecture.countInitialStudentConflicts();
466            Set<Student> newStudentConflictsSet = lecture.conflictStudents(assignment, assignedPlacement);
467            Set<Student> initialStudentConflicts = lecture.initialStudentConflicts();
468            for (Iterator<Student> e1 = newStudentConflictsSet.iterator(); e1.hasNext();)
469                if (!initialStudentConflicts.contains(e1.next()))
470                    newStudentConflicts++;
471            deltaTimePreferences += assignedPlacement.getTimeLocation().getNormalizedPreference() - initialPlacement.getTimeLocation().getNormalizedPreference();
472            for (InstructorConstraint ic : lecture.getInstructorConstraints()) {
473                for (Lecture lect : ic.variables()) {
474                    if (lect.equals(lecture))
475                        continue;
476                    int initialPreference = (lect.getInitialAssignment() == null ? Constants.sPreferenceLevelNeutral
477                            : ic.getDistancePreference(initialPlacement, lect.getInitialAssignment()));
478                    int assignedPreference = (assignedPlacement == null ? Constants.sPreferenceLevelNeutral : ic.getDistancePreference(assignedPlacement, assignedPlacement));
479                    deltaInstructorDistancePreferences += assignedPreference - initialPreference;
480                }
481            }
482        }
483        if (perts != 0)
484            info.put("Perturbations: Different placement", String.valueOf(perts) + " (weighted " + sDoubleFormat.format(iDifferentPlacement * perts) + ")");
485        if (affectedStudents != 0)
486            info.put("Perturbations: Number of affected students", String.valueOf(affectedStudents) + " (weighted " + sDoubleFormat.format(iAffectedStudentWeight * affectedStudents) + ")");
487        if (affectedInstructors != 0)
488            info.put("Perturbations: Number of affected instructors", String.valueOf(affectedInstructors) + " (weighted " + sDoubleFormat.format(iAffectedInstructorWeight * affectedInstructors) + ")");
489        if (affectedStudentsByTime != 0)
490            info.put("Perturbations: Number of affected students [time]", String.valueOf(affectedStudentsByTime) +
491                    " (weighted " + sDoubleFormat.format(iAffectedStudentByTimeWeight * affectedStudentsByTime) + ")");
492        if (affectedInstructorsByTime != 0)
493            info.put("Perturbations: Number of affected instructors [time]", String.valueOf(affectedInstructorsByTime) +
494                    " (weighted " + sDoubleFormat.format(iAffectedInstructorByTimeWeight * affectedInstructorsByTime) + ")");
495        if (affectedStudentsByRoom != 0)
496            info.put("Perturbations: Number of affected students [room]", String.valueOf(affectedStudentsByRoom) + 
497                    " (weighted " + sDoubleFormat.format(iAffectedStudentByRoomWeight * affectedStudentsByRoom) + ")");
498        if (affectedInstructorsByRoom != 0)
499            info.put("Perturbations: Number of affected instructors [room]", String.valueOf(affectedInstructorsByRoom) +
500                    " (weighted " + sDoubleFormat.format(iAffectedInstructorByRoomWeight * affectedInstructorsByRoom) + ")");
501        if (affectedStudentsByBldg != 0)
502            info.put("Perturbations: Number of affected students [bldg]", String.valueOf(affectedStudentsByBldg) +
503                    " (weighted " + sDoubleFormat.format(iAffectedStudentByBldgWeight * affectedStudentsByBldg) + ")");
504        if (affectedInstructorsByBldg != 0)
505            info.put("Perturbations: Number of affected instructors [bldg]", String.valueOf(affectedInstructorsByBldg) +
506                    " (weighted " + sDoubleFormat.format(iAffectedInstructorByBldgWeight * affectedInstructorsByBldg) + ")");
507        if (differentRoom != 0)
508            info.put("Perturbations: Different room", String.valueOf(differentRoom) + " (weighted " + sDoubleFormat.format(iDifferentRoomWeight * differentRoom) + ")");
509        if (differentBuilding != 0)
510            info.put("Perturbations: Different building", String.valueOf(differentBuilding) + " (weighted " + sDoubleFormat.format(iDifferentBuildingWeight * differentBuilding) + ")");
511        if (differentTime != 0)
512            info.put("Perturbations: Different time", String.valueOf(differentTime) + " (weighted " + sDoubleFormat.format(iDifferentTimeWeight * differentTime) + ")");
513        if (differentDay != 0)
514            info.put("Perturbations: Different day", String.valueOf(differentDay) + " (weighted " + sDoubleFormat.format(iDifferentDayWeight * differentDay) + ")");
515        if (differentHour != 0)
516            info.put("Perturbations: Different hour", String.valueOf(differentHour) + " (weighted " + sDoubleFormat.format(iDifferentHourWeight * differentHour) + ")");
517        if (tooFarForInstructors != 0)
518            info.put("Perturbations: New placement too far from initial [instructors]", String.valueOf(tooFarForInstructors) + 
519                    " (weighted " + sDoubleFormat.format(iTooFarForInstructorsWeight * tooFarForInstructors) + ")");
520        if (tooFarForStudents != 0)
521            info.put("Perturbations: New placement too far from initial [students]", String.valueOf(tooFarForStudents) +
522                    " (weighted " + sDoubleFormat.format(iTooFarForStudentsWeight * tooFarForStudents) + ")");
523        if (deltaStudentConflicts != 0)
524            info.put("Perturbations: Delta student conflicts", String.valueOf(deltaStudentConflicts) + " (weighted " + sDoubleFormat.format(iDeltaStudentConflictsWeight * deltaStudentConflicts) + ")");
525        if (newStudentConflicts != 0)
526            info.put("Perturbations: New student conflicts", String.valueOf(newStudentConflicts) + " (weighted " + sDoubleFormat.format(iNewStudentConflictsWeight * newStudentConflicts) + ")");
527        if (deltaTimePreferences != 0)
528            info.put("Perturbations: Delta time preferences", String.valueOf(deltaTimePreferences) + " (weighted " + sDoubleFormat.format(iDeltaTimePreferenceWeight * deltaTimePreferences) + ")");
529        if (deltaRoomPreferences != 0)
530            info.put("Perturbations: Delta room preferences", String.valueOf(deltaRoomPreferences) + " (weighted " + sDoubleFormat.format(iDeltaRoomPreferenceWeight * deltaRoomPreferences) + ")");
531        if (deltaInstructorDistancePreferences != 0)
532            info.put("Perturbations: Delta instructor distance preferences", String.valueOf(deltaInstructorDistancePreferences) +
533                    " (weighted " + sDoubleFormat.format(iDeltaInstructorDistancePreferenceWeight * deltaInstructorDistancePreferences) + ")");
534    }
535
536    public Map<String, Double> getCompactInfo(Assignment<Lecture, Placement> assignment, TimetableModel model, boolean includeZero, boolean weighted) {
537        Map<String, Double> info = new HashMap<String, Double>();
538        if (!iMPP)
539            return info;
540        int perts = 0;
541        long affectedStudents = 0;
542        int affectedInstructors = 0;
543        long affectedStudentsByTime = 0;
544        int affectedInstructorsByTime = 0;
545        long affectedStudentsByRoom = 0;
546        int affectedInstructorsByRoom = 0;
547        long affectedStudentsByBldg = 0;
548        int affectedInstructorsByBldg = 0;
549        int differentRoom = 0;
550        int differentBuilding = 0;
551        int differentTime = 0;
552        int differentDay = 0;
553        int differentHour = 0;
554        int tooFarForInstructors = 0;
555        int tooFarForStudents = 0;
556        int deltaStudentConflicts = 0;
557        int newStudentConflicts = 0;
558        double deltaTimePreferences = 0;
559        int deltaRoomPreferences = 0;
560        int deltaInstructorDistancePreferences = 0;
561        for (Lecture lecture : model.perturbVariables(assignment)) {
562            if (assignment.getValue(lecture) == null || lecture.getInitialAssignment() == null || assignment.getValue(lecture).equals(lecture.getInitialAssignment()))
563                continue;
564            perts++;
565            Placement assignedPlacement = assignment.getValue(lecture);
566            Placement initialPlacement = lecture.getInitialAssignment();
567            affectedStudents += lecture.classLimit(assignment);
568            affectedInstructors += lecture.getInstructorConstraints().size();
569
570            int nrDiff = initialPlacement.nrDifferentRooms(assignedPlacement);
571            differentRoom += nrDiff;
572            affectedInstructorsByRoom += nrDiff * lecture.getInstructorConstraints().size();
573            affectedStudentsByRoom += nrDiff * lecture.classLimit(assignment);
574
575            nrDiff = initialPlacement.nrDifferentBuildings(initialPlacement);
576            differentBuilding += nrDiff;
577            affectedInstructorsByBldg += nrDiff * lecture.getInstructorConstraints().size();
578            affectedStudentsByBldg += nrDiff * lecture.classLimit(assignment);
579
580            deltaRoomPreferences += assignedPlacement.sumRoomPreference() - initialPlacement.sumRoomPreference();
581
582            if (!initialPlacement.getTimeLocation().equals(assignedPlacement.getTimeLocation())) {
583                differentTime++;
584                affectedInstructorsByTime += lecture.getInstructorConstraints().size();
585                affectedStudentsByTime += lecture.classLimit(assignment);
586            }
587            if (initialPlacement.getTimeLocation().getDayCode() != assignedPlacement.getTimeLocation().getDayCode())
588                differentDay++;
589            if (initialPlacement.getTimeLocation().getStartSlot() != assignedPlacement.getTimeLocation().getStartSlot())
590                differentHour++;
591            if (!initialPlacement.getTimeLocation().equals(assignedPlacement.getTimeLocation())) {
592                double distance = Placement.getDistanceInMeters(iDistanceMetric, initialPlacement, assignedPlacement);
593                if (!lecture.getInstructorConstraints().isEmpty()) {
594                    if (distance > iDistanceMetric.getInstructorNoPreferenceLimit() && distance <= iDistanceMetric.getInstructorDiscouragedLimit()) {
595                        tooFarForInstructors += Constants.sPreferenceLevelDiscouraged;
596                    } else if (distance > iDistanceMetric.getInstructorDiscouragedLimit() && distance <= iDistanceMetric.getInstructorProhibitedLimit()) {
597                        tooFarForInstructors += Constants.sPreferenceLevelStronglyDiscouraged;
598                    } else if (distance > iDistanceMetric.getInstructorProhibitedLimit()) {
599                        tooFarForInstructors += Constants.sPreferenceLevelProhibited;
600                    }
601                }
602                if (distance > iDistanceMetric.minutes2meters(10))
603                    tooFarForStudents += lecture.classLimit(assignment);
604            }
605            deltaStudentConflicts += lecture.countStudentConflicts(assignment, assignedPlacement) - lecture.countInitialStudentConflicts();
606            Set<Student> newStudentConflictsSet = lecture.conflictStudents(assignment, assignedPlacement);
607            Set<Student> initialStudentConflicts = lecture.initialStudentConflicts();
608            for (Iterator<Student> e1 = newStudentConflictsSet.iterator(); e1.hasNext();)
609                if (!initialStudentConflicts.contains(e1.next()))
610                    newStudentConflicts++;
611            deltaTimePreferences += assignedPlacement.getTimeLocation().getNormalizedPreference() - initialPlacement.getTimeLocation().getNormalizedPreference();
612            for (InstructorConstraint ic : lecture.getInstructorConstraints()) {
613                if (ic != null)
614                    for (Lecture lect : ic.variables()) {
615                        if (lect.equals(lecture))
616                            continue;
617                        int initialPreference = (lect.getInitialAssignment() == null ? Constants.sPreferenceLevelNeutral
618                                : ic.getDistancePreference(initialPlacement, lect.getInitialAssignment()));
619                        int assignedPreference = (assignedPlacement == null ? Constants.sPreferenceLevelNeutral : ic.getDistancePreference(assignedPlacement, assignedPlacement));
620                        deltaInstructorDistancePreferences += assignedPreference - initialPreference;
621                    }
622            }
623        }
624        if (includeZero || iDifferentPlacement != 0.0)
625            info.put("Different placement", new Double(weighted ? iDifferentPlacement * perts : perts));
626        if (includeZero || iAffectedStudentWeight != 0.0)
627            info.put("Affected students", new Double(weighted ? iAffectedStudentWeight * affectedStudents : affectedStudents));
628        if (includeZero || iAffectedInstructorWeight != 0.0)
629            info.put("Affected instructors", new Double(weighted ? iAffectedInstructorWeight * affectedInstructors : affectedInstructors));
630        if (includeZero || iAffectedStudentByTimeWeight != 0.0)
631            info.put("Affected students [time]", new Double(weighted ? iAffectedStudentByTimeWeight * affectedStudentsByTime : affectedStudentsByTime));
632        if (includeZero || iAffectedInstructorByTimeWeight != 0.0)
633            info.put("Affected instructors [time]", new Double(weighted ? iAffectedInstructorByTimeWeight * affectedInstructorsByTime : affectedInstructorsByTime));
634        if (includeZero || iAffectedStudentByRoomWeight != 0.0)
635            info.put("Affected students [room]", new Double(weighted ? iAffectedStudentByRoomWeight * affectedStudentsByRoom : affectedStudentsByRoom));
636        if (includeZero || iAffectedInstructorByRoomWeight != 0.0)
637            info.put("Affected instructors [room]", new Double(weighted ? iAffectedInstructorByRoomWeight * affectedInstructorsByRoom : affectedInstructorsByRoom));
638        if (includeZero || iAffectedStudentByBldgWeight != 0.0)
639            info.put("Affected students [bldg]", new Double(weighted ? iAffectedStudentByBldgWeight * affectedStudentsByBldg : affectedStudentsByBldg));
640        if (includeZero || iAffectedInstructorByBldgWeight != 0.0)
641            info.put("Affected instructors [bldg]", new Double(weighted ? iAffectedInstructorByBldgWeight * affectedInstructorsByBldg : affectedInstructorsByBldg));
642        if (includeZero || iDifferentRoomWeight != 0.0)
643            info.put("Different room", new Double(weighted ? iDifferentRoomWeight * differentRoom : differentRoom));
644        if (includeZero || iDifferentBuildingWeight != 0.0)
645            info.put("Different building", new Double(weighted ? iDifferentBuildingWeight * differentBuilding : differentBuilding));
646        if (includeZero || iDifferentTimeWeight != 0.0)
647            info.put("Different time", new Double(weighted ? iDifferentTimeWeight * differentTime : differentTime));
648        if (includeZero || iDifferentDayWeight != 0.0)
649            info.put("Different day", new Double(weighted ? iDifferentDayWeight * differentDay : differentDay));
650        if (includeZero || iDifferentHourWeight != 0.0)
651            info.put("Different hour", new Double(weighted ? iDifferentHourWeight * differentHour : differentHour));
652        if (includeZero || iTooFarForInstructorsWeight != 0.0)
653            info.put("New placement too far for initial [instructors]", new Double(weighted ? iTooFarForInstructorsWeight * tooFarForInstructors : tooFarForInstructors));
654        if (includeZero || iTooFarForStudentsWeight != 0.0)
655            info.put("New placement too far for initial [students]", new Double(weighted ? iTooFarForStudentsWeight * tooFarForStudents : tooFarForStudents));
656        if (includeZero || iDeltaStudentConflictsWeight != 0.0)
657            info.put("Delta student conflicts", new Double(weighted ? iDeltaStudentConflictsWeight * deltaStudentConflicts : deltaStudentConflicts));
658        if (includeZero || iNewStudentConflictsWeight != 0.0)
659            info.put("New student conflicts", new Double(weighted ? iNewStudentConflictsWeight * newStudentConflicts : newStudentConflicts));
660        if (includeZero || iDeltaTimePreferenceWeight != 0.0)
661            info.put("Delta time preferences", new Double(weighted ? iDeltaTimePreferenceWeight * deltaTimePreferences : deltaTimePreferences));
662        if (includeZero || iDeltaRoomPreferenceWeight != 0.0)
663            info.put("Delta room preferences", new Double(weighted ? iDeltaRoomPreferenceWeight * deltaRoomPreferences : deltaRoomPreferences));
664        if (includeZero || iDeltaInstructorDistancePreferenceWeight != 0.0)
665            info.put("Delta instructor distance preferences", new Double(weighted ? iDeltaInstructorDistancePreferenceWeight * deltaInstructorDistancePreferences : deltaInstructorDistancePreferences));
666        return info;
667    }
668
669    public Map<String, Double> getCompactInfo(Assignment<Lecture, Placement> assignment, TimetableModel model, Placement assignedPlacement, boolean includeZero,
670            boolean weighted) {
671        Map<String, Double> info = new HashMap<String, Double>();
672        if (!iMPP)
673            return info;
674        Lecture lecture = assignedPlacement.variable();
675        Placement initialPlacement = lecture.getInitialAssignment();
676        if (initialPlacement == null || initialPlacement.equals(assignedPlacement))
677            return info;
678        int perts = 1;
679        long affectedStudents = lecture.classLimit(assignment);
680        int affectedInstructors = lecture.getInstructorConstraints().size();
681        long affectedStudentsByTime = (initialPlacement.getTimeLocation().equals(assignedPlacement.getTimeLocation()) ? 0 : lecture.classLimit(assignment));
682        int affectedInstructorsByTime = (initialPlacement.getTimeLocation().equals(assignedPlacement.getTimeLocation()) ? 0 : lecture.getInstructorConstraints().size());
683
684        int differentRoom = initialPlacement.nrDifferentRooms(assignedPlacement);
685        int affectedInstructorsByRoom = differentRoom * lecture.getInstructorConstraints().size();
686        long affectedStudentsByRoom = differentRoom * lecture.classLimit(assignment);
687
688        int differentBuilding = initialPlacement.nrDifferentBuildings(initialPlacement);
689        int affectedInstructorsByBldg = differentBuilding * lecture.getInstructorConstraints().size();
690        long affectedStudentsByBldg = differentBuilding * lecture.classLimit(assignment);
691
692        int deltaRoomPreferences = assignedPlacement.sumRoomPreference() - initialPlacement.sumRoomPreference();
693
694        int differentTime = (initialPlacement.getTimeLocation().equals(assignedPlacement.getTimeLocation()) ? 0 : 1);
695        int differentDay = (initialPlacement.getTimeLocation().getDayCode() != assignedPlacement.getTimeLocation().getDayCode() ? 1 : 0);
696        int differentHour = (initialPlacement.getTimeLocation().getStartSlot() != assignedPlacement.getTimeLocation().getStartSlot() ? 1 : 0);
697        int tooFarForInstructors = 0;
698        int tooFarForStudents = 0;
699        int deltaStudentConflicts = lecture.countStudentConflicts(assignment, assignedPlacement) - lecture.countInitialStudentConflicts();
700        int newStudentConflicts = 0;
701        double deltaTimePreferences = (assignedPlacement.getTimeLocation().getNormalizedPreference() -
702                initialPlacement.getTimeLocation().getNormalizedPreference());
703        int deltaInstructorDistancePreferences = 0;
704
705        double distance = Placement.getDistanceInMeters(iDistanceMetric, initialPlacement, assignedPlacement);
706        if (!lecture.getInstructorConstraints().isEmpty()) {
707            if (distance > iDistanceMetric.getInstructorNoPreferenceLimit() && distance <= iDistanceMetric.getInstructorDiscouragedLimit()) {
708                tooFarForInstructors += lecture.getInstructorConstraints().size();
709            } else if (distance > iDistanceMetric.getInstructorDiscouragedLimit() && distance <= iDistanceMetric.getInstructorProhibitedLimit()) {
710                tooFarForInstructors += 2 * lecture.getInstructorConstraints().size();
711            } else if (distance > iDistanceMetric.getInstructorProhibitedLimit()) {
712                tooFarForInstructors += 10 * lecture.getInstructorConstraints().size();
713            }
714        }
715        if (distance > iDistanceMetric.minutes2meters(10))
716            tooFarForStudents = lecture.classLimit(assignment);
717
718        Set<Student> newStudentConflictsVect = lecture.conflictStudents(assignment, assignedPlacement);
719        Set<Student> initialStudentConflicts = lecture.initialStudentConflicts();
720        for (Iterator<Student> e = newStudentConflictsVect.iterator(); e.hasNext();)
721            if (!initialStudentConflicts.contains(e.next()))
722                newStudentConflicts++;
723
724        for (InstructorConstraint ic : lecture.getInstructorConstraints()) {
725            for (Lecture lect : ic.variables()) {
726                if (lect.equals(lecture))
727                    continue;
728                int initialPreference = (lect.getInitialAssignment() == null ? Constants.sPreferenceLevelNeutral : ic.getDistancePreference(initialPlacement, lect.getInitialAssignment()));
729                int assignedPreference = (assignment.getValue(lect) == null ? Constants.sPreferenceLevelNeutral : ic.getDistancePreference(assignedPlacement, assignment.getValue(lect)));
730                deltaInstructorDistancePreferences += (assignedPreference - initialPreference);
731            }
732        }
733
734        if (includeZero || iDifferentPlacement != 0.0)
735            info.put("Different placement", new Double(weighted ? iDifferentPlacement * perts : perts));
736        if (includeZero || iAffectedStudentWeight != 0.0)
737            info.put("Affected students", new Double(weighted ? iAffectedStudentWeight * affectedStudents : affectedStudents));
738        if (includeZero || iAffectedInstructorWeight != 0.0)
739            info.put("Affected instructors", new Double(weighted ? iAffectedInstructorWeight * affectedInstructors : affectedInstructors));
740        if (includeZero || iAffectedStudentByTimeWeight != 0.0)
741            info.put("Affected students [time]", new Double(weighted ? iAffectedStudentByTimeWeight * affectedStudentsByTime : affectedStudentsByTime));
742        if (includeZero || iAffectedInstructorByTimeWeight != 0.0)
743            info.put("Affected instructors [time]", new Double(weighted ? iAffectedInstructorByTimeWeight * affectedInstructorsByTime : affectedInstructorsByTime));
744        if (includeZero || iAffectedStudentByRoomWeight != 0.0)
745            info.put("Affected students [room]", new Double(weighted ? iAffectedStudentByRoomWeight * affectedStudentsByRoom : affectedStudentsByRoom));
746        if (includeZero || iAffectedInstructorByRoomWeight != 0.0)
747            info.put("Affected instructors [room]", new Double(weighted ? iAffectedInstructorByRoomWeight * affectedInstructorsByRoom : affectedInstructorsByRoom));
748        if (includeZero || iAffectedStudentByBldgWeight != 0.0)
749            info.put("Affected students [bldg]", new Double(weighted ? iAffectedStudentByBldgWeight * affectedStudentsByBldg : affectedStudentsByBldg));
750        if (includeZero || iAffectedInstructorByBldgWeight != 0.0)
751            info.put("Affected instructors [bldg]", new Double(weighted ? iAffectedInstructorByBldgWeight * affectedInstructorsByBldg : affectedInstructorsByBldg));
752        if (includeZero || iDifferentRoomWeight != 0.0)
753            info.put("Different room", new Double(weighted ? iDifferentRoomWeight * differentRoom : differentRoom));
754        if (includeZero || iDifferentBuildingWeight != 0.0)
755            info.put("Different building", new Double(weighted ? iDifferentBuildingWeight * differentBuilding : differentBuilding));
756        if (includeZero || iDifferentTimeWeight != 0.0)
757            info.put("Different time", new Double(weighted ? iDifferentTimeWeight * differentTime : differentTime));
758        if (includeZero || iDifferentDayWeight != 0.0)
759            info.put("Different day", new Double(weighted ? iDifferentDayWeight * differentDay : differentDay));
760        if (includeZero || iDifferentHourWeight != 0.0)
761            info.put("Different hour", new Double(weighted ? iDifferentHourWeight * differentHour : differentHour));
762        if (includeZero || iTooFarForInstructorsWeight != 0.0)
763            info.put("New placement too far for initial [instructors]", new Double(weighted ? iTooFarForInstructorsWeight * tooFarForInstructors : tooFarForInstructors));
764        if (includeZero || iTooFarForStudentsWeight != 0.0)
765            info.put("New placement too far for initial [students]", new Double(weighted ? iTooFarForStudentsWeight * tooFarForStudents : tooFarForStudents));
766        if (includeZero || iDeltaStudentConflictsWeight != 0.0)
767            info.put("Delta student conflicts", new Double(weighted ? iDeltaStudentConflictsWeight * deltaStudentConflicts : deltaStudentConflicts));
768        if (includeZero || iNewStudentConflictsWeight != 0.0)
769            info.put("New student conflicts", new Double(weighted ? iNewStudentConflictsWeight * newStudentConflicts : newStudentConflicts));
770        if (includeZero || iDeltaTimePreferenceWeight != 0.0)
771            info.put("Delta time preferences", new Double(weighted ? iDeltaTimePreferenceWeight * deltaTimePreferences : deltaTimePreferences));
772        if (includeZero || iDeltaRoomPreferenceWeight != 0.0)
773            info.put("Delta room preferences", new Double(weighted ? iDeltaRoomPreferenceWeight * deltaRoomPreferences : deltaRoomPreferences));
774        if (includeZero || iDeltaInstructorDistancePreferenceWeight != 0.0)
775            info.put("Delta instructor distance preferences", new Double(weighted ? iDeltaInstructorDistancePreferenceWeight * deltaInstructorDistancePreferences : deltaInstructorDistancePreferences));
776        return info;
777    }
778}