001 package ttsolver.heuristics;
002
003 import ifs.solution.*;
004 import ifs.util.*;
005 import java.util.*;
006 import ttsolver.*;
007 import edu.purdue.smas.timetable.serverfwk.ParameterDefinition;
008
009 /**
010 * Timetable (solution) comparator.
011 * <br><br>
012 * The quality of a solution is expressed as a weighted sum combining soft time and classroom preferences, satisfied soft
013 * group constrains and the total number of student conflicts. This allows us to express the importance of different
014 * types of soft constraints.
015 * <br><br>
016 * The solution comparator prefers a more complete solution (with a smaller number of unassigned variables) and a solution
017 * with a smaller number of perturbations among solutions with the same number of unassigned variables. If both solutions
018 * have the same number of unassigned variables and perturbations, the solution of better quality is selected.
019 * <br><br>
020 * Parameters:
021 * <table border='1'><tr><th>Parameter</th><th>Type</th><th>Comment</th></tr>
022 * <tr><td>Comparator.HardStudentConflictWeight</td><td>{@link Double}</td><td>Weight of hard student conflict (conflict between single-section classes)</td></tr>
023 * <tr><td>Comparator.StudentConflictWeight</td><td>{@link Double}</td><td>Weight of student conflict</td></tr>
024 * <tr><td>Comparator.TimePreferenceWeight</td><td>{@link Double}</td><td>Time preferences weight</td></tr>
025 * <tr><td>Comparator.ContrPreferenceWeight</td><td>{@link Double}</td><td>Group constraint preferences weight</td></tr>
026 * <tr><td>Comparator.RoomPreferenceWeight</td><td>{@link Double}</td><td>Room preferences weight</td></tr>
027 * <tr><td>Comparator.UselessSlotWeight</td><td>{@link Double}</td><td>Useless slots weight</td></tr>
028 * <tr><td>Comparator.TooBigRoomWeight</td><td>{@link Double}</td><td>Too big room weight</td></tr>
029 * <tr><td>Comparator.DistanceInstructorPreferenceWeight</td><td>{@link Double}</td><td>Distance (of the rooms of the back-to-back classes) based instructor preferences weight</td></tr>
030 * <tr><td>Comparator.PerturbationPenaltyWeight</td><td>{@link Double}</td><td>Perturbation penalty (see {@link UniversalPerturbationsCounter})</td></tr>
031 * <tr><td>Comparator.DeptSpreadPenaltyWeight</td><td>{@link Double}</td><td>Department balancing penalty (see {@link ttsolver.constraint.DepartmentSpreadConstraint})</td></tr>
032 * </table>
033 *
034 *
035 * @author <a href="mailto:muller@ktiml.mff.cuni.cz">Tomáš Müller</a>
036 * @version 1.0
037 */
038
039 public class TimetableComparator implements SolutionComparator {
040 protected static org.apache.log4j.Logger sLogger = org.apache.log4j.Logger.getLogger(TimetableComparator.class);
041
042 private double iEmptySingleSlotWeight;
043 public static final String USELESS_SLOT_WEIGHT="Comparator.UselessSlotWeight";
044 private double iTimePreferencesWeight;
045 public static final String TIME_PREFERENCE_WEIGHT="Comparator.TimePreferenceWeight";
046 private double iStudentConflictWeight;
047 public static final String STUDENT_CONFLICT_WEIGHT="Comparator.StudentConflictWeight";
048 private double iRoomPreferencesWeight;
049 public static final String ROOM_PREFERENCE_WEIGHT="Comparator.RoomPreferenceWeight";
050 private double iConstrPreferencesWeight;
051 public static final String CONSTR_PREFERENCE_WEIGHT="Comparator.ContrPreferenceWeight";
052 private double iHardStudentConflictWeight;
053 public static final String HARD_STUDENT_CONFLICT_WEIGHT="Comparator.HardStudentConflictWeight";
054 private double iTooBigRoomWeight;
055 public static final String TOO_BIG_ROOM_WEIGHT = "Comparator.TooBigRoomWeight";
056 private boolean iCompareMpp;
057 public static final String DISTANCE_INSTRUCTOR_PREFERENCE_WEIGHT = "Comparator.DistanceInstructorPreferenceWeight";
058 private double iDistanceInstructorPreferenceWeight;
059 public static final String PERTURBATION_PENALTY_WEIGHT = "Comparator.PerturbationPenaltyWeight";
060 private double iPerturbationPenaltyWeight;
061 public static final String DEPT_SPREAD_PENALTY_WEIGHT = "Comparator.DeptSpreadPenaltyWeight";
062 private double iDeptSpreadPenaltyWeight;
063
064 public static Collection parameters() {
065 Vector ret = new FastVector();
066
067 ParameterDefinition.Dependency swDep = new ParameterDefinition.Dependency("General.SwitchStudents","true");
068 ParameterDefinition.Dependency deptSpreadDep = new ParameterDefinition.Dependency("General.UseDepartmentSpreadConstraints","false");
069
070 ret.addElement(new ParameterDefinition("Solution Comparator - Weights",USELESS_SLOT_WEIGHT, "Useless slots", ParameterDefinition.TYPE_DOUBLE, "0.0"));
071 ret.addElement(new ParameterDefinition("Solution Comparator - Weights",TIME_PREFERENCE_WEIGHT, "Time preferences", ParameterDefinition.TYPE_DOUBLE, "1.0"));
072 ret.addElement(new ParameterDefinition("Solution Comparator - Weights",ROOM_PREFERENCE_WEIGHT, "Room preferences", ParameterDefinition.TYPE_DOUBLE, "0.1"));
073 ret.addElement(new ParameterDefinition("Solution Comparator - Weights",STUDENT_CONFLICT_WEIGHT, "Student conflicts", ParameterDefinition.TYPE_DOUBLE, "0.2"));
074 ret.addElement(new ParameterDefinition("Solution Comparator - Weights",HARD_STUDENT_CONFLICT_WEIGHT, "Hard student conflicts", ParameterDefinition.TYPE_DOUBLE, "1.0").addDependency(swDep));
075 ret.addElement(new ParameterDefinition("Solution Comparator - Weights",CONSTR_PREFERENCE_WEIGHT, "Group constraint preferences", ParameterDefinition.TYPE_DOUBLE, "1.0"));
076 ret.addElement(new ParameterDefinition("Solution Comparator - Weights",TOO_BIG_ROOM_WEIGHT, "Too big rooms", ParameterDefinition.TYPE_DOUBLE, "0.0"));
077 ret.addElement(new ParameterDefinition("Solution Comparator - Weights",DISTANCE_INSTRUCTOR_PREFERENCE_WEIGHT, "Distance instructor preferences", ParameterDefinition.TYPE_DOUBLE, "1.0"));
078 ret.addElement(new ParameterDefinition("Solution Comparator - Weights",PERTURBATION_PENALTY_WEIGHT, "Perturbation penalty", ParameterDefinition.TYPE_DOUBLE, "1.0"));
079 ret.addElement(new ParameterDefinition("Solution Comparator - Weights",DEPT_SPREAD_PENALTY_WEIGHT, "Deparment balancing penalty", ParameterDefinition.TYPE_DOUBLE, "1.0").addDependency(deptSpreadDep));
080
081 return ret;
082 }
083
084 public TimetableComparator(DataProperties properties) {
085 iEmptySingleSlotWeight = properties.getPropertyDouble(USELESS_SLOT_WEIGHT,0.0);
086 iTimePreferencesWeight = properties.getPropertyDouble(TIME_PREFERENCE_WEIGHT,1.0);
087 iRoomPreferencesWeight = properties.getPropertyDouble(ROOM_PREFERENCE_WEIGHT,0.1);
088 iConstrPreferencesWeight = properties.getPropertyDouble(CONSTR_PREFERENCE_WEIGHT,1.0);
089 if (properties.getPropertyBoolean("General.SwitchStudents",true)) {
090 iHardStudentConflictWeight = properties.getPropertyDouble(HARD_STUDENT_CONFLICT_WEIGHT,1.0);
091 iStudentConflictWeight = properties.getPropertyDouble(STUDENT_CONFLICT_WEIGHT,0.2);
092 } else {
093 iHardStudentConflictWeight = 0.0;
094 iStudentConflictWeight = properties.getPropertyDouble(STUDENT_CONFLICT_WEIGHT,1.0);
095 }
096 iDistanceInstructorPreferenceWeight = properties.getPropertyDouble(DISTANCE_INSTRUCTOR_PREFERENCE_WEIGHT, 1.0);
097 iTooBigRoomWeight = properties.getPropertyDouble(TOO_BIG_ROOM_WEIGHT,0.0);
098 iCompareMpp = properties.getPropertyBoolean("General.MPP", false);
099 iPerturbationPenaltyWeight = properties.getPropertyDouble(PERTURBATION_PENALTY_WEIGHT,1.0);
100 iDeptSpreadPenaltyWeight = properties.getPropertyDouble(DEPT_SPREAD_PENALTY_WEIGHT,1.0);
101 }
102
103 public boolean isBetterThanBestSolution(Solution currentSolution) {
104 if (currentSolution.getBestInfo()==null) return true;
105 TimetableModel tm = (TimetableModel)currentSolution.getModel();
106 int unassigned = tm.unassignedVariables().size();
107 if (tm.getBestUnassignedVariables()!=unassigned)
108 return tm.getBestUnassignedVariables()>unassigned;
109
110 int tooBigBest=0, tooBigCurr=0;
111 if (iTooBigRoomWeight!=0.0) {
112 tooBigBest = tm.bestTooBigRooms();
113 tooBigCurr = tm.countTooBigRooms();
114 }
115 long uselessSlotsBest=0, uselessSlotsCur=0;
116 if (iEmptySingleSlotWeight!=0.0) {
117 uselessSlotsBest = tm.bestUselessSlots();
118 uselessSlotsCur = tm.getUselessSlots();
119 }
120 long hardSCBest = 0, hardSCCurr = 0;
121 if (iHardStudentConflictWeight!=0.0) {
122 hardSCBest = tm.bestHardStudentConflicts();
123 hardSCCurr = tm.getHardStudentConflicts();
124 }
125 double pertBest=0.0, pertCurr=0.0;
126 if (iCompareMpp && iPerturbationPenaltyWeight!=0.0) {
127 pertCurr = currentSolution.getPerturbationsCounter().getPerturbationPenalty(currentSolution);
128 pertBest = currentSolution.getBestPerturbationsPenalty();
129 }
130 double deptSpreadBest=0.0, deptSpread=0.0;
131 if (iDeptSpreadPenaltyWeight!=0.0) {
132 deptSpread = tm.getDepartmentSpreadPenalty();
133 deptSpreadBest = tm.bestDepartmentSpreadPenalty();
134 }
135
136 double prefBest = (iEmptySingleSlotWeight*uselessSlotsBest)+
137 (iTimePreferencesWeight*tm.bestGlobalTimePreference())+
138 (iRoomPreferencesWeight*tm.bestGlobalRoomPreference())+
139 (iConstrPreferencesWeight*tm.bestGlobalGroupConstraintPreference())+
140 (iStudentConflictWeight*tm.bestViolatedStudentConflicts())+
141 (iHardStudentConflictWeight*hardSCBest)+
142 (iTooBigRoomWeight*tooBigBest)+
143 (iDistanceInstructorPreferenceWeight*tm.bestInstructorDistancePreference())+
144 (iPerturbationPenaltyWeight*pertBest)+
145 (iDeptSpreadPenaltyWeight*deptSpreadBest)
146 ;
147 double prefCurr = (iEmptySingleSlotWeight*uselessSlotsCur)+
148 (iTimePreferencesWeight*tm.getGlobalTimePreference())+
149 (iRoomPreferencesWeight*tm.getGlobalRoomPreference())+
150 (iConstrPreferencesWeight*tm.getGlobalGroupConstraintPreference())+
151 (iStudentConflictWeight*tm.getViolatedStudentConflicts())+
152 (iHardStudentConflictWeight*hardSCCurr)+
153 (iTooBigRoomWeight*tooBigCurr)+
154 (iDistanceInstructorPreferenceWeight*tm.getInstructorDistancePreference())+
155 (iPerturbationPenaltyWeight*pertCurr)+
156 (iDeptSpreadPenaltyWeight*deptSpread)
157 ;
158 /*
159 if (currentSolution.getModel().unassignedVariables().isEmpty() && prefCurr>=prefBest) {
160 sLogger.info("Complete but worse (cur="+prefCurr+" vs. best="+prefBest+") solution "+currentSolution.getInfo()+" was found.");
161 }
162 */
163 return (prefCurr<prefBest);
164 }
165
166 }