001 package net.sf.cpsolver.coursett;
002
003 import java.io.File;
004 import java.io.FileWriter;
005 import java.io.IOException;
006 import java.io.PrintWriter;
007 import java.text.DecimalFormat;
008 import java.util.ArrayList;
009 import java.util.Collection;
010 import java.util.Date;
011 import java.util.HashSet;
012 import java.util.HashMap;
013 import java.util.List;
014 import java.util.Locale;
015 import java.util.Map;
016 import java.util.TreeSet;
017
018 import net.sf.cpsolver.coursett.constraint.DepartmentSpreadConstraint;
019 import net.sf.cpsolver.coursett.constraint.GroupConstraint;
020 import net.sf.cpsolver.coursett.constraint.InstructorConstraint;
021 import net.sf.cpsolver.coursett.constraint.JenrlConstraint;
022 import net.sf.cpsolver.coursett.constraint.RoomConstraint;
023 import net.sf.cpsolver.coursett.constraint.SpreadConstraint;
024 import net.sf.cpsolver.coursett.criteria.BackToBackInstructorPreferences;
025 import net.sf.cpsolver.coursett.criteria.BrokenTimePatterns;
026 import net.sf.cpsolver.coursett.criteria.DepartmentBalancingPenalty;
027 import net.sf.cpsolver.coursett.criteria.DistributionPreferences;
028 import net.sf.cpsolver.coursett.criteria.Perturbations;
029 import net.sf.cpsolver.coursett.criteria.RoomPreferences;
030 import net.sf.cpsolver.coursett.criteria.SameSubpartBalancingPenalty;
031 import net.sf.cpsolver.coursett.criteria.StudentCommittedConflict;
032 import net.sf.cpsolver.coursett.criteria.StudentConflict;
033 import net.sf.cpsolver.coursett.criteria.StudentDistanceConflict;
034 import net.sf.cpsolver.coursett.criteria.StudentHardConflict;
035 import net.sf.cpsolver.coursett.criteria.TimePreferences;
036 import net.sf.cpsolver.coursett.criteria.TooBigRooms;
037 import net.sf.cpsolver.coursett.criteria.UselessHalfHours;
038 import net.sf.cpsolver.coursett.heuristics.UniversalPerturbationsCounter;
039 import net.sf.cpsolver.coursett.model.Lecture;
040 import net.sf.cpsolver.coursett.model.Placement;
041 import net.sf.cpsolver.coursett.model.RoomLocation;
042 import net.sf.cpsolver.coursett.model.Student;
043 import net.sf.cpsolver.coursett.model.TimeLocation;
044 import net.sf.cpsolver.coursett.model.TimetableModel;
045 import net.sf.cpsolver.ifs.extension.ConflictStatistics;
046 import net.sf.cpsolver.ifs.extension.Extension;
047 import net.sf.cpsolver.ifs.extension.MacPropagation;
048 import net.sf.cpsolver.ifs.model.Constraint;
049 import net.sf.cpsolver.ifs.solution.Solution;
050 import net.sf.cpsolver.ifs.solution.SolutionListener;
051 import net.sf.cpsolver.ifs.solver.Solver;
052 import net.sf.cpsolver.ifs.util.DataProperties;
053 import net.sf.cpsolver.ifs.util.Progress;
054 import net.sf.cpsolver.ifs.util.ProgressWriter;
055 import net.sf.cpsolver.ifs.util.ToolBox;
056
057 /**
058 * A main class for running of the solver from command line. <br>
059 * <br>
060 * Usage:<br>
061 * java -Xmx1024m -jar coursett1.1.jar config.properties [input_file]
062 * [output_folder]<br>
063 * <br>
064 * See http://www.unitime.org for example configuration files and banchmark data
065 * sets.<br>
066 * <br>
067 *
068 * The test does the following steps:
069 * <ul>
070 * <li>Provided property file is loaded (see {@link DataProperties}).
071 * <li>Output folder is created (General.Output property) and loggings is setup
072 * (using log4j).
073 * <li>Input data are loaded (calling {@link TimetableLoader#load()}).
074 * <li>Solver is executed (see {@link Solver}).
075 * <li>Resultant solution is saved (calling {@link TimetableSaver#save()}, when
076 * General.Save property is set to true.
077 * </ul>
078 * Also, a log and a CSV (comma separated text file) is created in the output
079 * folder.
080 *
081 * @version CourseTT 1.2 (University Course Timetabling)<br>
082 * Copyright (C) 2006 - 2010 Tomas Muller<br>
083 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
084 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
085 * <br>
086 * This library is free software; you can redistribute it and/or modify
087 * it under the terms of the GNU Lesser General Public License as
088 * published by the Free Software Foundation; either version 3 of the
089 * License, or (at your option) any later version. <br>
090 * <br>
091 * This library is distributed in the hope that it will be useful, but
092 * WITHOUT ANY WARRANTY; without even the implied warranty of
093 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
094 * Lesser General Public License for more details. <br>
095 * <br>
096 * You should have received a copy of the GNU Lesser General Public
097 * License along with this library; if not see
098 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
099 */
100
101 public class Test implements SolutionListener<Lecture, Placement> {
102 private static java.text.SimpleDateFormat sDateFormat = new java.text.SimpleDateFormat("yyMMdd_HHmmss",
103 java.util.Locale.US);
104 private static java.text.DecimalFormat sDoubleFormat = new java.text.DecimalFormat("0.000",
105 new java.text.DecimalFormatSymbols(Locale.US));
106 private static org.apache.log4j.Logger sLogger = org.apache.log4j.Logger.getLogger(Test.class);
107
108 private PrintWriter iCSVFile = null;
109
110 private MacPropagation<Lecture, Placement> iProp = null;
111 private ConflictStatistics<Lecture, Placement> iStat = null;
112 private int iLastNotified = -1;
113
114 private boolean initialized = false;
115 private Solver<Lecture, Placement> iSolver = null;
116
117 /** Current version */
118 public static String getVersionString() {
119 return "IFS Timetable Solver v" + Constants.getVersion() + " build" + Constants.getBuildNumber() + ", "
120 + Constants.getReleaseDate();
121 }
122
123 /** Solver initialization */
124 public void init(Solver<Lecture, Placement> solver) {
125 iSolver = solver;
126 solver.currentSolution().addSolutionListener(this);
127 }
128
129 /**
130 * Return name of the class that is used for loading the data. This class
131 * needs to extend class {@link TimetableLoader}. It can be also defined in
132 * configuration, using TimetableLoader property.
133 **/
134 private String getTimetableLoaderClass(DataProperties properties) {
135 String loader = properties.getProperty("TimetableLoader");
136 if (loader != null)
137 return loader;
138 if (properties.getPropertyInt("General.InputVersion", -1) >= 0)
139 return "org.unitime.timetable.solver.TimetableDatabaseLoader";
140 else
141 return "net.sf.cpsolver.coursett.TimetableXMLLoader";
142 }
143
144 /**
145 * Return name of the class that is used for loading the data. This class
146 * needs to extend class {@link TimetableSaver}. It can be also defined in
147 * configuration, using TimetableSaver property.
148 **/
149 private String getTimetableSaverClass(DataProperties properties) {
150 String saver = properties.getProperty("TimetableSaver");
151 if (saver != null)
152 return saver;
153 if (properties.getPropertyInt("General.InputVersion", -1) >= 0)
154 return "org.unitime.timetable.solver.TimetableDatabaseSaver";
155 else
156 return "net.sf.cpsolver.coursett.TimetableXMLSaver";
157 }
158
159 /**
160 * Solver Test
161 *
162 * @param args
163 * command line arguments
164 */
165 public Test(String[] args) {
166 try {
167 DataProperties properties = ToolBox.loadProperties(new java.io.File(args[0]));
168 properties.putAll(System.getProperties());
169 properties.setProperty("General.Output", properties.getProperty("General.Output", ".") + File.separator
170 + (sDateFormat.format(new Date())));
171 if (args.length > 1)
172 properties.setProperty("General.Input", args[1]);
173 if (args.length > 2)
174 properties.setProperty("General.Output", args[2] + File.separator + (sDateFormat.format(new Date())));
175 System.out.println("Output folder: " + properties.getProperty("General.Output"));
176 ToolBox.configureLogging(properties.getProperty("General.Output"), properties, false, false);
177
178 File outDir = new File(properties.getProperty("General.Output", "."));
179 outDir.mkdirs();
180
181 Solver<Lecture, Placement> solver = new TimetableSolver(properties);
182 TimetableModel model = new TimetableModel(properties);
183 Progress.getInstance(model).addProgressListener(new ProgressWriter(System.out));
184
185 TimetableLoader loader = (TimetableLoader) Class.forName(getTimetableLoaderClass(properties))
186 .getConstructor(new Class[] { TimetableModel.class }).newInstance(new Object[] { model });
187 loader.load();
188
189 solver.setInitalSolution(model);
190 init(solver);
191
192 iCSVFile = new PrintWriter(new FileWriter(outDir.toString() + File.separator + "stat.csv"));
193 String colSeparator = ";";
194 iCSVFile.println("Assigned"
195 + colSeparator
196 + "Assigned[%]"
197 + colSeparator
198 + "Time[min]"
199 + colSeparator
200 + "Iter"
201 + colSeparator
202 + "IterYield[%]"
203 + colSeparator
204 + "Speed[it/s]"
205 + colSeparator
206 + "AddedPert"
207 + colSeparator
208 + "AddedPert[%]"
209 + colSeparator
210 + "HardStudentConf"
211 + colSeparator
212 + "StudentConf"
213 + colSeparator
214 + "DistStudentConf"
215 + colSeparator
216 + "CommitStudentConf"
217 + colSeparator
218 + "TimePref"
219 + colSeparator
220 + "RoomPref"
221 + colSeparator
222 + "DistInstrPref"
223 + colSeparator
224 + "GrConstPref"
225 + colSeparator
226 + "UselessHalfHours"
227 + colSeparator
228 + "BrokenTimePat"
229 + colSeparator
230 + "TooBigRooms"
231 + (iProp != null ? colSeparator + "GoodVars" + colSeparator + "GoodVars[%]" + colSeparator
232 + "GoodVals" + colSeparator + "GoodVals[%]" : ""));
233 iCSVFile.flush();
234
235 solver.start();
236 solver.getSolverThread().join();
237
238 long lastIt = solver.lastSolution().getIteration();
239 double lastTime = solver.lastSolution().getTime();
240
241 if (solver.lastSolution().getBestInfo() != null) {
242 Solution<Lecture, Placement> bestSolution = solver.lastSolution();// .cloneBest();
243 sLogger.info("Last solution: " + ToolBox.dict2string(bestSolution.getInfo(), 1));
244 sLogger.info("Best solution (before restore): " + ToolBox.dict2string(bestSolution.getBestInfo(), 1));
245 bestSolution.restoreBest();
246 sLogger.info("Best solution: " + ToolBox.dict2string(bestSolution.getInfo(), 1));
247 if (properties.getPropertyBoolean("General.SwitchStudents", true))
248 ((TimetableModel) bestSolution.getModel()).switchStudents();
249 sLogger.info("Best solution: " + ToolBox.dict2string(bestSolution.getInfo(), 1));
250 saveOutputCSV(bestSolution, new File(outDir, "output.csv"));
251
252 printSomeStuff(bestSolution);
253
254 if (properties.getPropertyBoolean("General.Save", false)) {
255 TimetableSaver saver = (TimetableSaver) Class.forName(getTimetableSaverClass(properties))
256 .getConstructor(new Class[] { Solver.class }).newInstance(new Object[] { solver });
257 if ((saver instanceof TimetableXMLSaver) && properties.getProperty("General.SolutionFile") != null)
258 ((TimetableXMLSaver) saver).save(new File(properties.getProperty("General.SolutionFile")));
259 else
260 saver.save();
261 }
262 } else
263 sLogger.info("Last solution:" + ToolBox.dict2string(solver.lastSolution().getInfo(), 1));
264
265 iCSVFile.close();
266
267 sLogger.info("Total number of done iteration steps:" + lastIt);
268 sLogger.info("Achieved speed: " + sDoubleFormat.format(lastIt / lastTime) + " iterations/second");
269
270 PrintWriter out = new PrintWriter(new FileWriter(new File(outDir, "solver.html")));
271 out.println("<html><title>Save log</title><body>");
272 out.println(Progress.getInstance(model).getHtmlLog(Progress.MSGLEVEL_TRACE, true));
273 out.println("</html>");
274 out.flush();
275 out.close();
276 Progress.removeInstance(model);
277
278 if (iStat != null) {
279 PrintWriter cbs = new PrintWriter(new FileWriter(new File(outDir, "cbs.txt")));
280 cbs.println(iStat.toString());
281 cbs.flush(); cbs.close();
282 }
283
284 System.out.println("Unassigned variables: " + model.nrUnassignedVariables());
285 System.exit(model.nrUnassignedVariables());
286 } catch (Throwable t) {
287 sLogger.error("Test failed.", t);
288 }
289 }
290
291 public static void main(String[] args) {
292 new Test(args);
293 }
294
295 @Override
296 public void bestCleared(Solution<Lecture, Placement> solution) {
297 }
298
299 @Override
300 public void bestRestored(Solution<Lecture, Placement> solution) {
301 }
302
303 @Override
304 public void bestSaved(Solution<Lecture, Placement> solution) {
305 notify(solution);
306 }
307
308 @Override
309 public void getInfo(Solution<Lecture, Placement> solution, Map<String, String> info) {
310 }
311
312 @Override
313 public void getInfo(Solution<Lecture, Placement> solution, Map<String, String> info, Collection<Lecture> variables) {
314 }
315
316 @Override
317 public void solutionUpdated(Solution<Lecture, Placement> solution) {
318 if (!initialized) {
319 for (Extension<Lecture, Placement> extension : iSolver.getExtensions()) {
320 if (MacPropagation.class.isInstance(extension))
321 iProp = (MacPropagation<Lecture, Placement>) extension;
322 if (ConflictStatistics.class.isInstance(extension)) {
323 iStat = (ConflictStatistics<Lecture, Placement>) extension;
324 }
325 }
326 }
327 }
328
329 /** Add a line into the output CSV file when a enw best solution is found. */
330 public void notify(Solution<Lecture, Placement> solution) {
331 String colSeparator = ";";
332 if (!solution.getModel().unassignedVariables().isEmpty()
333 && iLastNotified == solution.getModel().assignedVariables().size())
334 return;
335 iLastNotified = solution.getModel().assignedVariables().size();
336 if (iCSVFile != null) {
337 TimetableModel model = (TimetableModel) solution.getModel();
338 iCSVFile.print(model.variables().size() - model.unassignedVariables().size());
339 iCSVFile.print(colSeparator);
340 iCSVFile.print(sDoubleFormat.format(100.0 * (model.variables().size() - model.unassignedVariables().size())
341 / model.variables().size()));
342 iCSVFile.print(colSeparator);
343 iCSVFile.print(sDoubleFormat.format((solution.getTime()) / 60.0));
344 iCSVFile.print(colSeparator);
345 iCSVFile.print(solution.getIteration());
346 iCSVFile.print(colSeparator);
347 iCSVFile.print(sDoubleFormat.format(100.0 * (model.variables().size() - model.unassignedVariables().size())
348 / solution.getIteration()));
349 iCSVFile.print(colSeparator);
350 iCSVFile.print(sDoubleFormat.format((solution.getIteration()) / solution.getTime()));
351 iCSVFile.print(colSeparator);
352 iCSVFile.print(model.perturbVariables().size());
353 iCSVFile.print(colSeparator);
354 iCSVFile.print(sDoubleFormat.format(100.0 * model.perturbVariables().size() / model.variables().size()));
355 iCSVFile.print(colSeparator);
356 iCSVFile.print(Math.round(solution.getModel().getCriterion(StudentHardConflict.class).getValue()));
357 iCSVFile.print(colSeparator);
358 iCSVFile.print(Math.round(solution.getModel().getCriterion(StudentConflict.class).getValue()));
359 iCSVFile.print(colSeparator);
360 iCSVFile.print(Math.round(solution.getModel().getCriterion(StudentDistanceConflict.class).getValue()));
361 iCSVFile.print(colSeparator);
362 iCSVFile.print(Math.round(solution.getModel().getCriterion(StudentCommittedConflict.class).getValue()));
363 iCSVFile.print(colSeparator);
364 iCSVFile.print(sDoubleFormat.format(solution.getModel().getCriterion(TimePreferences.class).getValue()));
365 iCSVFile.print(colSeparator);
366 iCSVFile.print(Math.round(solution.getModel().getCriterion(RoomPreferences.class).getValue()));
367 iCSVFile.print(colSeparator);
368 iCSVFile.print(Math.round(solution.getModel().getCriterion(BackToBackInstructorPreferences.class).getValue()));
369 iCSVFile.print(colSeparator);
370 iCSVFile.print(Math.round(solution.getModel().getCriterion(DistributionPreferences.class).getValue()));
371 iCSVFile.print(colSeparator);
372 iCSVFile.print(Math.round(solution.getModel().getCriterion(UselessHalfHours.class).getValue()));
373 iCSVFile.print(colSeparator);
374 iCSVFile.print(Math.round(solution.getModel().getCriterion(BrokenTimePatterns.class).getValue()));
375 iCSVFile.print(colSeparator);
376 iCSVFile.print(Math.round(solution.getModel().getCriterion(TooBigRooms.class).getValue()));
377 if (iProp != null) {
378 if (solution.getModel().unassignedVariables().size() > 0) {
379 int goodVariables = 0;
380 long goodValues = 0;
381 long allValues = 0;
382 for (Lecture variable : ((TimetableModel) solution.getModel()).unassignedVariables()) {
383 goodValues += iProp.goodValues(variable).size();
384 allValues += variable.values().size();
385 if (!iProp.goodValues(variable).isEmpty())
386 goodVariables++;
387 }
388 iCSVFile.print(colSeparator);
389 iCSVFile.print(goodVariables);
390 iCSVFile.print(colSeparator);
391 iCSVFile.print(sDoubleFormat.format(100.0 * goodVariables
392 / solution.getModel().unassignedVariables().size()));
393 iCSVFile.print(colSeparator);
394 iCSVFile.print(goodValues);
395 iCSVFile.print(colSeparator);
396 iCSVFile.print(sDoubleFormat.format(100.0 * goodValues / allValues));
397 } else {
398 iCSVFile.print(colSeparator);
399 iCSVFile.print(colSeparator);
400 iCSVFile.print(colSeparator);
401 iCSVFile.print(colSeparator);
402 }
403 }
404 iCSVFile.println();
405 iCSVFile.flush();
406 }
407 }
408
409 /** Print room utilization */
410 public static void printRoomInfo(PrintWriter pw, TimetableModel model) {
411 pw.println("Room info:");
412 pw.println("id, name, size, used_day, used_total");
413 for (RoomConstraint rc : model.getRoomConstraints()) {
414 int used_day = 0;
415 int used_total = 0;
416 for (int day = 0; day < Constants.NR_DAYS_WEEK; day++) {
417 for (int time = 0; time < Constants.SLOTS_PER_DAY_NO_EVENINGS; time++) {
418 if (!rc.getResource(day * Constants.SLOTS_PER_DAY + time + Constants.DAY_SLOTS_FIRST).isEmpty())
419 used_day++;
420 }
421 }
422 for (int day = 0; day < Constants.DAY_CODES.length; day++) {
423 for (int time = 0; time < Constants.SLOTS_PER_DAY; time++) {
424 if (!rc.getResource(day * Constants.SLOTS_PER_DAY + time).isEmpty())
425 used_total++;
426 }
427 }
428 pw.println(rc.getResourceId() + "," + rc.getName() + "," + rc.getCapacity() + "," + used_day + ","
429 + used_total);
430 }
431 }
432
433 /** Class information */
434 public static void printClassInfo(PrintWriter pw, TimetableModel model) {
435 pw.println("Class info:");
436 pw.println("id, name, min_class_limit, max_class_limit, room2limit_ratio, half_hours");
437 for (Lecture lecture : model.variables()) {
438 TimeLocation time = lecture.timeLocations().get(0);
439 pw.println(lecture.getClassId() + "," + lecture.getName() + "," + lecture.minClassLimit() + ","
440 + lecture.maxClassLimit() + "," + lecture.roomToLimitRatio() + ","
441 + (time.getNrSlotsPerMeeting() * time.getNrMeetings()));
442 }
443 }
444
445 /** Create info.txt with some more information about the problem */
446 public static void printSomeStuff(Solution<Lecture, Placement> solution) throws IOException {
447 TimetableModel model = (TimetableModel) solution.getModel();
448 File outDir = new File(model.getProperties().getProperty("General.Output", "."));
449 PrintWriter pw = new PrintWriter(new FileWriter(outDir.toString() + File.separator + "info.txt"));
450 PrintWriter pwi = new PrintWriter(new FileWriter(outDir.toString() + File.separator + "info.csv"));
451 String name = new File(model.getProperties().getProperty("General.Input")).getName();
452 pwi.println("Instance," + name.substring(0, name.lastIndexOf('.')));
453 pw.println("Solution info: " + ToolBox.dict2string(solution.getInfo(), 1));
454 pw.println("Bounds: " + ToolBox.dict2string(model.getBounds(), 1));
455 Map<String, String> info = solution.getInfo();
456 for (String key : new TreeSet<String>(info.keySet())) {
457 if (key.equals("Memory usage"))
458 continue;
459 if (key.equals("Iteration"))
460 continue;
461 if (key.equals("Time"))
462 continue;
463 String value = info.get(key);
464 if (value.indexOf(' ') > 0)
465 value = value.substring(0, value.indexOf(' '));
466 pwi.println(key + "," + value);
467 }
468 printRoomInfo(pw, model);
469 printClassInfo(pw, model);
470 long nrValues = 0;
471 long nrTimes = 0;
472 long nrRooms = 0;
473 double totalMaxNormTimePref = 0.0;
474 double totalMinNormTimePref = 0.0;
475 double totalNormTimePref = 0.0;
476 int totalMaxRoomPref = 0;
477 int totalMinRoomPref = 0;
478 int totalRoomPref = 0;
479 long nrStudentEnrls = 0;
480 long nrInevitableStudentConflicts = 0;
481 long nrJenrls = 0;
482 int nrHalfHours = 0;
483 int nrMeetings = 0;
484 int totalMinLimit = 0;
485 int totalMaxLimit = 0;
486 long nrReqRooms = 0;
487 int nrSingleValueVariables = 0;
488 int nrSingleTimeVariables = 0;
489 int nrSingleRoomVariables = 0;
490 long totalAvailableMinRoomSize = 0;
491 long totalAvailableMaxRoomSize = 0;
492 long totalRoomSize = 0;
493 long nrOneOrMoreRoomVariables = 0;
494 long nrOneRoomVariables = 0;
495 HashSet<Student> students = new HashSet<Student>();
496 HashSet<Long> offerings = new HashSet<Long>();
497 HashSet<Long> configs = new HashSet<Long>();
498 HashSet<Long> subparts = new HashSet<Long>();
499 int[] sizeLimits = new int[] { 0, 25, 50, 75, 100, 150, 200, 400 };
500 int[] nrRoomsOfSize = new int[sizeLimits.length];
501 int[] minRoomOfSize = new int[sizeLimits.length];
502 int[] maxRoomOfSize = new int[sizeLimits.length];
503 int[] totalUsedSlots = new int[sizeLimits.length];
504 int[] totalUsedSeats = new int[sizeLimits.length];
505 int[] totalUsedSeats2 = new int[sizeLimits.length];
506 for (Lecture lect : model.variables()) {
507 if (lect.getConfiguration() != null) {
508 offerings.add(lect.getConfiguration().getOfferingId());
509 configs.add(lect.getConfiguration().getConfigId());
510 }
511 subparts.add(lect.getSchedulingSubpartId());
512 nrStudentEnrls += (lect.students() == null ? 0 : lect.students().size());
513 students.addAll(lect.students());
514 nrValues += lect.values().size();
515 nrReqRooms += lect.getNrRooms();
516 for (RoomLocation room: lect.roomLocations())
517 if (room.getPreference() < Constants.sPreferenceLevelProhibited / 2)
518 nrRooms++;
519 for (TimeLocation time: lect.timeLocations())
520 if (time.getPreference() < Constants.sPreferenceLevelProhibited / 2)
521 nrTimes ++;
522 totalMinLimit += lect.minClassLimit();
523 totalMaxLimit += lect.maxClassLimit();
524 if (!lect.values().isEmpty()) {
525 Placement p = lect.values().get(0);
526 nrMeetings += p.getTimeLocation().getNrMeetings();
527 nrHalfHours += p.getTimeLocation().getNrMeetings() * p.getTimeLocation().getNrSlotsPerMeeting();
528 totalMaxNormTimePref += lect.getMinMaxTimePreference()[1];
529 totalMinNormTimePref += lect.getMinMaxTimePreference()[0];
530 totalNormTimePref += Math.abs(lect.getMinMaxTimePreference()[1] - lect.getMinMaxTimePreference()[0]);
531 totalMaxRoomPref += lect.getMinMaxRoomPreference()[1];
532 totalMinRoomPref += lect.getMinMaxRoomPreference()[0];
533 totalRoomPref += Math.abs(lect.getMinMaxRoomPreference()[1] - lect.getMinMaxRoomPreference()[0]);
534 TimeLocation time = p.getTimeLocation();
535 boolean hasRoomConstraint = false;
536 for (RoomLocation roomLocation : lect.roomLocations()) {
537 if (roomLocation.getRoomConstraint().getConstraint())
538 hasRoomConstraint = true;
539 }
540 if (hasRoomConstraint && lect.getNrRooms() > 0) {
541 for (int d = 0; d < Constants.NR_DAYS_WEEK; d++) {
542 if ((time.getDayCode() & Constants.DAY_CODES[d]) == 0)
543 continue;
544 for (int t = Math.max(time.getStartSlot(), Constants.DAY_SLOTS_FIRST); t <= Math.min(time
545 .getStartSlot()
546 + time.getLength() - 1, Constants.DAY_SLOTS_LAST); t++) {
547 for (int l = 0; l < sizeLimits.length; l++) {
548 if (sizeLimits[l] <= lect.minRoomSize()) {
549 totalUsedSlots[l] += lect.getNrRooms();
550 totalUsedSeats[l] += lect.classLimit();
551 totalUsedSeats2[l] += lect.minRoomSize() * lect.getNrRooms();
552 }
553 }
554 }
555 }
556 }
557 }
558 if (lect.values().size() == 1) {
559 nrSingleValueVariables++;
560 }
561 if (lect.timeLocations().size() == 1) {
562 nrSingleTimeVariables++;
563 }
564 if (lect.roomLocations().size() == 1) {
565 nrSingleRoomVariables++;
566 }
567 if (lect.getNrRooms() == 1) {
568 nrOneRoomVariables++;
569 }
570 if (lect.getNrRooms() > 0) {
571 nrOneOrMoreRoomVariables++;
572 }
573 if (!lect.roomLocations().isEmpty()) {
574 int minRoomSize = Integer.MAX_VALUE;
575 int maxRoomSize = Integer.MIN_VALUE;
576 for (RoomLocation rl : lect.roomLocations()) {
577 minRoomSize = Math.min(minRoomSize, rl.getRoomSize());
578 maxRoomSize = Math.max(maxRoomSize, rl.getRoomSize());
579 totalRoomSize += rl.getRoomSize();
580 }
581 totalAvailableMinRoomSize += minRoomSize;
582 totalAvailableMaxRoomSize += maxRoomSize;
583 }
584 }
585 for (JenrlConstraint jenrl : model.getJenrlConstraints()) {
586 nrJenrls += jenrl.getJenrl();
587 if ((jenrl.first()).timeLocations().size() == 1 && (jenrl.second()).timeLocations().size() == 1) {
588 TimeLocation t1 = jenrl.first().timeLocations().get(0);
589 TimeLocation t2 = jenrl.second().timeLocations().get(0);
590 if (t1.hasIntersection(t2)) {
591 nrInevitableStudentConflicts += jenrl.getJenrl();
592 pw.println("Inevitable " + jenrl.getJenrl() + " student conflicts between " + jenrl.first() + " "
593 + t1 + " and " + jenrl.second() + " " + t2);
594 } else if (jenrl.first().values().size() == 1 && jenrl.second().values().size() == 1) {
595 Placement p1 = jenrl.first().values().get(0);
596 Placement p2 = jenrl.second().values().get(0);
597 if (JenrlConstraint.isInConflict(p1, p2, ((TimetableModel)p1.variable().getModel()).getDistanceMetric())) {
598 nrInevitableStudentConflicts += jenrl.getJenrl();
599 pw.println("Inevitable " + jenrl.getJenrl()
600 + (p1.getTimeLocation().hasIntersection(p2.getTimeLocation()) ? "" : " distance")
601 + " student conflicts between " + p1 + " and " + p2);
602 }
603 }
604 }
605 }
606 int totalCommitedPlacements = 0;
607 for (Student student : students) {
608 if (student.getCommitedPlacements() != null)
609 totalCommitedPlacements += student.getCommitedPlacements().size();
610 }
611 pw.println("Total number of classes: " + model.variables().size());
612 pwi.println("Number of classes," + model.variables().size());
613 pw.println("Total number of instructional offerings: " + offerings.size() + " ("
614 + sDoubleFormat.format(100.0 * offerings.size() / model.variables().size()) + "%)");
615 // pwi.println("Number of instructional offerings,"+offerings.size());
616 pw.println("Total number of configurations: " + configs.size() + " ("
617 + sDoubleFormat.format(100.0 * configs.size() / model.variables().size()) + "%)");
618 pw.println("Total number of scheduling subparts: " + subparts.size() + " ("
619 + sDoubleFormat.format(100.0 * subparts.size() / model.variables().size()) + "%)");
620 // pwi.println("Number of scheduling subparts,"+subparts.size());
621 pw.println("Average number classes per subpart: "
622 + sDoubleFormat.format(1.0 * model.variables().size() / subparts.size()));
623 pwi.println("Avg. classes per instruction,"
624 + sDoubleFormat.format(1.0 * model.variables().size() / subparts.size()));
625 pw.println("Average number classes per config: "
626 + sDoubleFormat.format(1.0 * model.variables().size() / configs.size()));
627 pw.println("Average number classes per offering: "
628 + sDoubleFormat.format(1.0 * model.variables().size() / offerings.size()));
629 pw.println("Total number of classes with only one value: " + nrSingleValueVariables + " ("
630 + sDoubleFormat.format(100.0 * nrSingleValueVariables / model.variables().size()) + "%)");
631 pw.println("Total number of classes with only one time: " + nrSingleTimeVariables + " ("
632 + sDoubleFormat.format(100.0 * nrSingleTimeVariables / model.variables().size()) + "%)");
633 pw.println("Total number of classes with only one room: " + nrSingleRoomVariables + " ("
634 + sDoubleFormat.format(100.0 * nrSingleRoomVariables / model.variables().size()) + "%)");
635 pwi.println("Classes with single value," + nrSingleValueVariables);
636 // pwi.println("Classes with only one time/room,"+nrSingleTimeVariables+"/"+nrSingleRoomVariables);
637 pw.println("Total number of classes requesting no room: "
638 + (model.variables().size() - nrOneOrMoreRoomVariables)
639 + " ("
640 + sDoubleFormat.format(100.0 * (model.variables().size() - nrOneOrMoreRoomVariables)
641 / model.variables().size()) + "%)");
642 pw.println("Total number of classes requesting one room: " + nrOneRoomVariables + " ("
643 + sDoubleFormat.format(100.0 * nrOneRoomVariables / model.variables().size()) + "%)");
644 pw.println("Total number of classes requesting one or more rooms: " + nrOneOrMoreRoomVariables + " ("
645 + sDoubleFormat.format(100.0 * nrOneOrMoreRoomVariables / model.variables().size()) + "%)");
646 // pwi.println("% classes requesting no room,"+sDoubleFormat.format(100.0*(model.variables().size()-nrOneOrMoreRoomVariables)/model.variables().size())+"%");
647 // pwi.println("% classes requesting one room,"+sDoubleFormat.format(100.0*nrOneRoomVariables/model.variables().size())+"%");
648 // pwi.println("% classes requesting two or more rooms,"+sDoubleFormat.format(100.0*(nrOneOrMoreRoomVariables-nrOneRoomVariables)/model.variables().size())+"%");
649 pw.println("Average number of requested rooms: "
650 + sDoubleFormat.format(1.0 * nrReqRooms / model.variables().size()));
651 pw.println("Average minimal class limit: "
652 + sDoubleFormat.format(1.0 * totalMinLimit / model.variables().size()));
653 pw.println("Average maximal class limit: "
654 + sDoubleFormat.format(1.0 * totalMaxLimit / model.variables().size()));
655 // pwi.println("Average class limit,"+sDoubleFormat.format(1.0*(totalMinLimit+totalMaxLimit)/(2*model.variables().size())));
656 pw.println("Average number of placements: " + sDoubleFormat.format(1.0 * nrValues / model.variables().size()));
657 // pwi.println("Average domain size,"+sDoubleFormat.format(1.0*nrValues/model.variables().size()));
658 pwi.println("Avg. domain size," + sDoubleFormat.format(1.0 * nrValues / model.variables().size()));
659 pw.println("Average number of time locations: "
660 + sDoubleFormat.format(1.0 * nrTimes / model.variables().size()));
661 pwi.println("Avg. number of avail. times/rooms,"
662 + sDoubleFormat.format(1.0 * nrTimes / model.variables().size()) + "/"
663 + sDoubleFormat.format(1.0 * nrRooms / model.variables().size()));
664 pw.println("Average number of room locations: "
665 + sDoubleFormat.format(1.0 * nrRooms / model.variables().size()));
666 pw.println("Average minimal requested room size: "
667 + sDoubleFormat.format(1.0 * totalAvailableMinRoomSize / nrOneOrMoreRoomVariables));
668 pw.println("Average maximal requested room size: "
669 + sDoubleFormat.format(1.0 * totalAvailableMaxRoomSize / nrOneOrMoreRoomVariables));
670 pw.println("Average requested room sizes: " + sDoubleFormat.format(1.0 * totalRoomSize / nrRooms));
671 pwi.println("Average requested room size," + sDoubleFormat.format(1.0 * totalRoomSize / nrRooms));
672 pw.println("Average maximum normalized time preference: "
673 + sDoubleFormat.format(totalMaxNormTimePref / model.variables().size()));
674 pw.println("Average minimum normalized time preference: "
675 + sDoubleFormat.format(totalMinNormTimePref / model.variables().size()));
676 pw.println("Average normalized time preference,"
677 + sDoubleFormat.format(totalNormTimePref / model.variables().size()));
678 pw.println("Average maximum room preferences: "
679 + sDoubleFormat.format(1.0 * totalMaxRoomPref / nrOneOrMoreRoomVariables));
680 pw.println("Average minimum room preferences: "
681 + sDoubleFormat.format(1.0 * totalMinRoomPref / nrOneOrMoreRoomVariables));
682 pw.println("Average room preferences," + sDoubleFormat.format(1.0 * totalRoomPref / nrOneOrMoreRoomVariables));
683 pw.println("Total number of students:" + students.size());
684 pwi.println("Number of students," + students.size());
685 pwi.println("Number of inevitable student conflicts," + nrInevitableStudentConflicts);
686 pw.println("Total amount of student enrollments: " + nrStudentEnrls);
687 pwi.println("Number of student enrollments," + nrStudentEnrls);
688 pw.println("Total amount of joined enrollments: " + nrJenrls);
689 pwi.println("Number of joint student enrollments," + nrJenrls);
690 pw.println("Average number of students: "
691 + sDoubleFormat.format(1.0 * students.size() / model.variables().size()));
692 pw.println("Average number of enrollemnts (per student): "
693 + sDoubleFormat.format(1.0 * nrStudentEnrls / students.size()));
694 pwi.println("Avg. number of classes per student,"
695 + sDoubleFormat.format(1.0 * nrStudentEnrls / students.size()));
696 pwi.println("Avg. number of committed classes per student,"
697 + sDoubleFormat.format(1.0 * totalCommitedPlacements / students.size()));
698 pw.println("Total amount of inevitable student conflicts: " + nrInevitableStudentConflicts + " ("
699 + sDoubleFormat.format(100.0 * nrInevitableStudentConflicts / nrStudentEnrls) + "%)");
700 pw.println("Average number of meetings (per class): "
701 + sDoubleFormat.format(1.0 * nrMeetings / model.variables().size()));
702 pw.println("Average number of hours per class: "
703 + sDoubleFormat.format(1.0 * nrHalfHours / model.variables().size() / 12.0));
704 pwi.println("Avg. number of meetings per class,"
705 + sDoubleFormat.format(1.0 * nrMeetings / model.variables().size()));
706 pwi.println("Avg. number of hours per class,"
707 + sDoubleFormat.format(1.0 * nrHalfHours / model.variables().size() / 12.0));
708 int minRoomSize = Integer.MAX_VALUE;
709 int maxRoomSize = Integer.MIN_VALUE;
710 int nrDistancePairs = 0;
711 double maxRoomDistance = Double.MIN_VALUE;
712 double totalRoomDistance = 0.0;
713 int[] totalAvailableSlots = new int[sizeLimits.length];
714 int[] totalAvailableSeats = new int[sizeLimits.length];
715 int nrOfRooms = 0;
716 totalRoomSize = 0;
717 for (RoomConstraint rc : model.getRoomConstraints()) {
718 if (rc.variables().isEmpty()) continue;
719 nrOfRooms++;
720 minRoomSize = Math.min(minRoomSize, rc.getCapacity());
721 maxRoomSize = Math.max(maxRoomSize, rc.getCapacity());
722 for (int l = 0; l < sizeLimits.length; l++) {
723 if (sizeLimits[l] <= rc.getCapacity()
724 && (l + 1 == sizeLimits.length || rc.getCapacity() < sizeLimits[l + 1])) {
725 nrRoomsOfSize[l]++;
726 if (minRoomOfSize[l] == 0)
727 minRoomOfSize[l] = rc.getCapacity();
728 else
729 minRoomOfSize[l] = Math.min(minRoomOfSize[l], rc.getCapacity());
730 if (maxRoomOfSize[l] == 0)
731 maxRoomOfSize[l] = rc.getCapacity();
732 else
733 maxRoomOfSize[l] = Math.max(maxRoomOfSize[l], rc.getCapacity());
734 }
735 }
736 totalRoomSize += rc.getCapacity();
737 if (rc.getPosX() != null && rc.getPosY() != null) {
738 for (RoomConstraint rc2 : model.getRoomConstraints()) {
739 if (rc2.getResourceId().compareTo(rc.getResourceId()) > 0 && rc2.getPosX() != null && rc2.getPosY() != null) {
740 double distance = ((TimetableModel)solution.getModel()).getDistanceMetric().getDistanceInMinutes(rc.getId(), rc.getPosX(), rc.getPosY(), rc2.getId(), rc2.getPosX(), rc2.getPosY());
741 totalRoomDistance += distance;
742 nrDistancePairs++;
743 maxRoomDistance = Math.max(maxRoomDistance, distance);
744 }
745 }
746 }
747 for (int d = 0; d < Constants.NR_DAYS_WEEK; d++) {
748 for (int t = Constants.DAY_SLOTS_FIRST; t <= Constants.DAY_SLOTS_LAST; t++) {
749 if (rc.isAvailable(d * Constants.SLOTS_PER_DAY + t)) {
750 for (int l = 0; l < sizeLimits.length; l++) {
751 if (sizeLimits[l] <= rc.getCapacity()) {
752 totalAvailableSlots[l]++;
753 totalAvailableSeats[l] += rc.getCapacity();
754 }
755 }
756 }
757 }
758 }
759 }
760 pw.println("Total number of rooms: " + nrOfRooms);
761 pwi.println("Number of rooms," + nrOfRooms);
762 pw.println("Minimal room size: " + minRoomSize);
763 pw.println("Maximal room size: " + maxRoomSize);
764 pwi.println("Room size min/max," + minRoomSize + "/" + maxRoomSize);
765 pw.println("Average room size: "
766 + sDoubleFormat.format(1.0 * totalRoomSize / model.getRoomConstraints().size()));
767 pw.println("Maximal distance between two rooms: " + sDoubleFormat.format(maxRoomDistance));
768 pw.println("Average distance between two rooms: "
769 + sDoubleFormat.format(totalRoomDistance / nrDistancePairs));
770 pwi.println("Average distance between two rooms [min],"
771 + sDoubleFormat.format(totalRoomDistance / nrDistancePairs));
772 pwi.println("Maximal distance between two rooms [min]," + sDoubleFormat.format(maxRoomDistance));
773 for (int l = 0; l < sizeLimits.length; l++) {// sizeLimits.length;l++) {
774 pwi.println("\"Room frequency (size>=" + sizeLimits[l] + ", used/avaiable times)\","
775 + sDoubleFormat.format(100.0 * totalUsedSlots[l] / totalAvailableSlots[l]) + "%");
776 pwi.println("\"Room utilization (size>=" + sizeLimits[l] + ", used/available seats)\","
777 + sDoubleFormat.format(100.0 * totalUsedSeats[l] / totalAvailableSeats[l]) + "%");
778 pwi.println("\"Number of rooms (size>=" + sizeLimits[l] + ")\"," + nrRoomsOfSize[l]);
779 pwi.println("\"Min/max room size (size>=" + sizeLimits[l] + ")\"," + minRoomOfSize[l] + "-"
780 + maxRoomOfSize[l]);
781 // pwi.println("\"Room utilization (size>="+sizeLimits[l]+", minRoomSize)\","+sDoubleFormat.format(100.0*totalUsedSeats2[l]/totalAvailableSeats[l])+"%");
782 }
783 pw.println("Average hours available: "
784 + sDoubleFormat.format(1.0 * totalAvailableSlots[0] / nrOfRooms / 12.0));
785 int totalInstructedClasses = 0;
786 for (InstructorConstraint ic : model.getInstructorConstraints()) {
787 totalInstructedClasses += ic.variables().size();
788 }
789 pw.println("Total number of instructors: " + model.getInstructorConstraints().size());
790 pwi.println("Number of instructors," + model.getInstructorConstraints().size());
791 pw.println("Total class-instructor assignments: " + totalInstructedClasses + " ("
792 + sDoubleFormat.format(100.0 * totalInstructedClasses / model.variables().size()) + "%)");
793 pwi.println("Number of class-instructor assignments," + totalInstructedClasses);
794 pw.println("Average classes per instructor: "
795 + sDoubleFormat.format(1.0 * totalInstructedClasses / model.getInstructorConstraints().size()));
796 pwi.println("Average classes per instructor,"
797 + sDoubleFormat.format(1.0 * totalInstructedClasses / model.getInstructorConstraints().size()));
798 // pw.println("Average hours available: "+sDoubleFormat.format(1.0*totalAvailableSlots/model.getInstructorConstraints().size()/12.0));
799 // pwi.println("Instructor availability [h],"+sDoubleFormat.format(1.0*totalAvailableSlots/model.getInstructorConstraints().size()/12.0));
800 int nrGroupConstraints = model.getGroupConstraints().size() + model.getSpreadConstraints().size();
801 int nrHardGroupConstraints = 0;
802 int nrVarsInGroupConstraints = 0;
803 for (GroupConstraint gc : model.getGroupConstraints()) {
804 if (gc.isHard())
805 nrHardGroupConstraints++;
806 nrVarsInGroupConstraints += gc.variables().size();
807 }
808 for (SpreadConstraint sc : model.getSpreadConstraints()) {
809 nrVarsInGroupConstraints += sc.variables().size();
810 }
811 pw.println("Total number of group constraints: " + nrGroupConstraints + " ("
812 + sDoubleFormat.format(100.0 * nrGroupConstraints / model.variables().size()) + "%)");
813 // pwi.println("Number of group constraints,"+nrGroupConstraints);
814 pw.println("Total number of hard group constraints: " + nrHardGroupConstraints + " ("
815 + sDoubleFormat.format(100.0 * nrHardGroupConstraints / model.variables().size()) + "%)");
816 // pwi.println("Number of hard group constraints,"+nrHardGroupConstraints);
817 pw.println("Average classes per group constraint: "
818 + sDoubleFormat.format(1.0 * nrVarsInGroupConstraints / nrGroupConstraints));
819 // pwi.println("Average classes per group constraint,"+sDoubleFormat.format(1.0*nrVarsInGroupConstraints/nrGroupConstraints));
820 pwi.println("Avg. number distribution constraints per class,"
821 + sDoubleFormat.format(1.0 * nrVarsInGroupConstraints / model.variables().size()));
822 pwi.println("Joint enrollment constraints," + model.getJenrlConstraints().size());
823 pw.flush();
824 pw.close();
825 pwi.flush();
826 pwi.close();
827 }
828
829 public static void saveOutputCSV(Solution<Lecture, Placement> s, File file) {
830 try {
831 DecimalFormat dx = new DecimalFormat("000");
832 PrintWriter w = new PrintWriter(new FileWriter(file));
833 TimetableModel m = (TimetableModel) s.getModel();
834 int idx = 1;
835 w.println("000." + dx.format(idx++) + " Assigned variables," + m.assignedVariables().size());
836 w.println("000." + dx.format(idx++) + " Time [sec]," + sDoubleFormat.format(s.getBestTime()));
837 w.println("000." + dx.format(idx++) + " Hard student conflicts," + Math.round(m.getCriterion(StudentHardConflict.class).getValue()));
838 if (m.getProperties().getPropertyBoolean("General.UseDistanceConstraints", true))
839 w.println("000." + dx.format(idx++) + " Distance student conf.," + Math.round(m.getCriterion(StudentDistanceConflict.class).getValue()));
840 w.println("000." + dx.format(idx++) + " Student conflicts," + Math.round(m.getCriterion(StudentConflict.class).getValue()));
841 w.println("000." + dx.format(idx++) + " Committed student conflicts," + Math.round(m.getCriterion(StudentCommittedConflict.class).getValue()));
842 w.println("000." + dx.format(idx++) + " All Student conflicts,"
843 + Math.round(m.getCriterion(StudentConflict.class).getValue() + m.getCriterion(StudentCommittedConflict.class).getValue()));
844 w.println("000." + dx.format(idx++) + " Time preferences,"
845 + sDoubleFormat.format( m.getCriterion(TimePreferences.class).getValue()));
846 w.println("000." + dx.format(idx++) + " Room preferences," + Math.round(m.getCriterion(RoomPreferences.class).getValue()));
847 w.println("000." + dx.format(idx++) + " Useless half-hours," + Math.round(m.getCriterion(UselessHalfHours.class).getValue()));
848 w.println("000." + dx.format(idx++) + " Broken time patterns," + Math.round(m.getCriterion(BrokenTimePatterns.class).getValue()));
849 w.println("000." + dx.format(idx++) + " Too big room," + Math.round(m.getCriterion(TooBigRooms.class).getValue()));
850 w.println("000." + dx.format(idx++) + " Distribution preferences," + sDoubleFormat.format(m.getCriterion(DistributionPreferences.class).getValue()));
851 if (m.getProperties().getPropertyBoolean("General.UseDistanceConstraints", true))
852 w.println("000." + dx.format(idx++) + " Back-to-back instructor pref.," + Math.round(m.getCriterion(BackToBackInstructorPreferences.class).getValue()));
853 if (m.getProperties().getPropertyBoolean("General.DeptBalancing", true)) {
854 w.println("000." + dx.format(idx++) + " Dept. balancing penalty," + sDoubleFormat.format(m.getCriterion(DepartmentBalancingPenalty.class).getValue()));
855 }
856 w.println("000." + dx.format(idx++) + " Same subpart balancing penalty," + sDoubleFormat.format(m.getCriterion(SameSubpartBalancingPenalty.class).getValue()));
857 if (m.getProperties().getPropertyBoolean("General.MPP", false)) {
858 Map<String, Double> mppInfo = ((UniversalPerturbationsCounter)((Perturbations)m.getCriterion(Perturbations.class)).getPerturbationsCounter()).getCompactInfo(m, false, false);
859 int pidx = 51;
860 w.println("000." + dx.format(pidx++) + " Perturbation penalty," + sDoubleFormat.format(m.getCriterion(Perturbations.class).getValue()));
861 w.println("000." + dx.format(pidx++) + " Additional perturbations," + m.perturbVariables().size());
862 int nrPert = 0, nrStudentPert = 0;
863 for (Lecture lecture : m.variables()) {
864 if (lecture.getInitialAssignment() != null)
865 continue;
866 nrPert++;
867 nrStudentPert += lecture.classLimit();
868 }
869 w.println("000." + dx.format(pidx++) + " Given perturbations," + nrPert);
870 w.println("000." + dx.format(pidx++) + " Given student perturbations," + nrStudentPert);
871 for (String key : new TreeSet<String>(mppInfo.keySet())) {
872 Double value = mppInfo.get(key);
873 w.println("000." + dx.format(pidx++) + " " + key + "," + sDoubleFormat.format(value));
874 }
875 }
876 HashSet<Student> students = new HashSet<Student>();
877 int enrls = 0;
878 int minRoomPref = 0, maxRoomPref = 0;
879 int minGrPref = 0, maxGrPref = 0;
880 int minTimePref = 0, maxTimePref = 0;
881 int worstInstrPref = 0;
882 HashSet<Constraint<Lecture, Placement>> used = new HashSet<Constraint<Lecture, Placement>>();
883 for (Lecture lecture : m.variables()) {
884 enrls += (lecture.students() == null ? 0 : lecture.students().size());
885 students.addAll(lecture.students());
886
887 int[] minMaxRoomPref = lecture.getMinMaxRoomPreference();
888 minRoomPref += minMaxRoomPref[0];
889 maxRoomPref += minMaxRoomPref[1];
890
891 double[] minMaxTimePref = lecture.getMinMaxTimePreference();
892 minTimePref += minMaxTimePref[0];
893 maxTimePref += minMaxTimePref[1];
894 for (Constraint<Lecture, Placement> c : lecture.constraints()) {
895 if (!used.add(c))
896 continue;
897
898 if (c instanceof InstructorConstraint) {
899 InstructorConstraint ic = (InstructorConstraint) c;
900 worstInstrPref += ic.getWorstPreference();
901 }
902
903 if (c instanceof GroupConstraint) {
904 GroupConstraint gc = (GroupConstraint) c;
905 if (gc.isHard())
906 continue;
907 minGrPref -= Math.abs(gc.getPreference());
908 maxGrPref += 0;
909 // minGrPref += Math.min(gc.getPreference(), 0);
910 // maxGrPref += Math.max(gc.getPreference(), 0);
911 }
912 }
913 }
914 int totalCommitedPlacements = 0;
915 for (Student student : students) {
916 if (student.getCommitedPlacements() != null)
917 totalCommitedPlacements += student.getCommitedPlacements().size();
918 }
919 HashMap<Long, List<Lecture>> subs = new HashMap<Long, List<Lecture>>();
920 for (Lecture lecture : m.variables()) {
921 if (lecture.isCommitted() || lecture.getScheduler() == null)
922 continue;
923 List<Lecture> vars = subs.get(lecture.getScheduler());
924 if (vars == null) {
925 vars = new ArrayList<Lecture>();
926 subs.put(lecture.getScheduler(), vars);
927 }
928 vars.add(lecture);
929 }
930 int bidx = 101;
931 w.println("000." + dx.format(bidx++) + " Assigned variables max," + m.variables().size());
932 w.println("000." + dx.format(bidx++) + " Student enrollments," + enrls);
933 w.println("000." + dx.format(bidx++) + " Student commited enrollments," + totalCommitedPlacements);
934 w.println("000." + dx.format(bidx++) + " All student enrollments," + (enrls + totalCommitedPlacements));
935 w.println("000." + dx.format(bidx++) + " Time preferences min," + minTimePref);
936 w.println("000." + dx.format(bidx++) + " Time preferences max," + maxTimePref);
937 w.println("000." + dx.format(bidx++) + " Room preferences min," + minRoomPref);
938 w.println("000." + dx.format(bidx++) + " Room preferences max," + maxRoomPref);
939 w.println("000."
940 + dx.format(bidx++)
941 + " Useless half-hours max,"
942 + (Constants.sPreferenceLevelStronglyDiscouraged * m.getRoomConstraints().size()
943 * Constants.SLOTS_PER_DAY_NO_EVENINGS * Constants.NR_DAYS_WEEK));
944 w.println("000." + dx.format(bidx++) + " Too big room max,"
945 + (Constants.sPreferenceLevelStronglyDiscouraged * m.variables().size()));
946 w.println("000." + dx.format(bidx++) + " Distribution preferences min," + minGrPref);
947 w.println("000." + dx.format(bidx++) + " Distribution preferences max," + maxGrPref);
948 w.println("000." + dx.format(bidx++) + " Back-to-back instructor pref max," + worstInstrPref);
949 for (Long scheduler: new TreeSet<Long>(subs.keySet())) {
950 List<Lecture> vars = subs.get(scheduler);
951 idx = 001;
952 bidx = 101;
953 int nrAssg = 0;
954 enrls = 0;
955 int roomPref = 0;
956 minRoomPref = 0;
957 maxRoomPref = 0;
958 double timePref = 0;
959 minTimePref = 0;
960 maxTimePref = 0;
961 double grPref = 0;
962 minGrPref = 0;
963 maxGrPref = 0;
964 long allSC = 0, hardSC = 0, distSC = 0;
965 int instPref = 0;
966 worstInstrPref = 0;
967 int spreadPen = 0, deptSpreadPen = 0;
968 int tooBigRooms = 0;
969 int rcs = 0, uselessSlots = 0;
970 used = new HashSet<Constraint<Lecture, Placement>>();
971 for (Lecture lecture : vars) {
972 if (lecture.isCommitted())
973 continue;
974 enrls += lecture.students().size();
975 Placement placement = lecture.getAssignment();
976 if (placement != null) {
977 nrAssg++;
978 }
979
980 int[] minMaxRoomPref = lecture.getMinMaxRoomPreference();
981 minRoomPref += minMaxRoomPref[0];
982 maxRoomPref += minMaxRoomPref[1];
983
984 double[] minMaxTimePref = lecture.getMinMaxTimePreference();
985 minTimePref += minMaxTimePref[0];
986 maxTimePref += minMaxTimePref[1];
987
988 if (placement != null) {
989 roomPref += placement.getRoomPreference();
990 timePref += placement.getTimeLocation().getNormalizedPreference();
991 tooBigRooms += TooBigRooms.getTooBigRoomPreference(placement);
992 }
993
994 for (Constraint<Lecture, Placement> c : lecture.constraints()) {
995 if (!used.add(c))
996 continue;
997
998 if (c instanceof InstructorConstraint) {
999 InstructorConstraint ic = (InstructorConstraint) c;
1000 instPref += ic.getPreference();
1001 worstInstrPref += ic.getWorstPreference();
1002 }
1003
1004 if (c instanceof DepartmentSpreadConstraint) {
1005 DepartmentSpreadConstraint dsc = (DepartmentSpreadConstraint) c;
1006 deptSpreadPen += dsc.getPenalty();
1007 } else if (c instanceof SpreadConstraint) {
1008 SpreadConstraint sc = (SpreadConstraint) c;
1009 spreadPen += sc.getPenalty();
1010 }
1011
1012 if (c instanceof GroupConstraint) {
1013 GroupConstraint gc = (GroupConstraint) c;
1014 if (gc.isHard())
1015 continue;
1016 minGrPref -= Math.abs(gc.getPreference());
1017 maxGrPref += 0;
1018 grPref += Math.min(0, gc.getCurrentPreference());
1019 // minGrPref += Math.min(gc.getPreference(), 0);
1020 // maxGrPref += Math.max(gc.getPreference(), 0);
1021 // grPref += gc.getCurrentPreference();
1022 }
1023
1024 if (c instanceof JenrlConstraint) {
1025 JenrlConstraint jc = (JenrlConstraint) c;
1026 if (!jc.isInConflict() || !jc.isOfTheSameProblem())
1027 continue;
1028 Lecture l1 = jc.first();
1029 Lecture l2 = jc.second();
1030 allSC += jc.getJenrl();
1031 if (l1.areStudentConflictsHard(l2))
1032 hardSC += jc.getJenrl();
1033 Placement p1 = l1.getAssignment();
1034 Placement p2 = l2.getAssignment();
1035 if (!p1.getTimeLocation().hasIntersection(p2.getTimeLocation()))
1036 distSC += jc.getJenrl();
1037 }
1038
1039 if (c instanceof RoomConstraint) {
1040 RoomConstraint rc = (RoomConstraint) c;
1041 uselessSlots += UselessHalfHours.countUselessSlotsHalfHours(rc) + BrokenTimePatterns.countUselessSlotsBrokenTimePatterns(rc);
1042 rcs++;
1043 }
1044 }
1045 }
1046 w.println(dx.format(scheduler) + "." + dx.format(idx++) + " Assigned variables," + nrAssg);
1047 w.println(dx.format(scheduler) + "." + dx.format(bidx++) + " Assigned variables max," + vars.size());
1048 w.println(dx.format(scheduler) + "." + dx.format(idx++) + " Hard student conflicts," + hardSC);
1049 w.println(dx.format(scheduler) + "." + dx.format(bidx++) + " Student enrollments," + enrls);
1050 if (m.getProperties().getPropertyBoolean("General.UseDistanceConstraints", true))
1051 w.println(dx.format(scheduler) + "." + dx.format(idx++) + " Distance student conf.," + distSC);
1052 w.println(dx.format(scheduler) + "." + dx.format(idx++) + " Student conflicts," + allSC);
1053 w.println(dx.format(scheduler) + "." + dx.format(idx++) + " Time preferences," + timePref);
1054 w.println(dx.format(scheduler) + "." + dx.format(bidx++) + " Time preferences min," + minTimePref);
1055 w.println(dx.format(scheduler) + "." + dx.format(bidx++) + " Time preferences max," + maxTimePref);
1056 w.println(dx.format(scheduler) + "." + dx.format(idx++) + " Room preferences," + roomPref);
1057 w.println(dx.format(scheduler) + "." + dx.format(bidx++) + " Room preferences min," + minRoomPref);
1058 w.println(dx.format(scheduler) + "." + dx.format(bidx++) + " Room preferences max," + maxRoomPref);
1059 w.println(dx.format(scheduler) + "." + dx.format(idx++) + " Useless half-hours," + uselessSlots);
1060 w
1061 .println(dx.format(scheduler)
1062 + "."
1063 + dx.format(bidx++)
1064 + " Useless half-hours max,"
1065 + (Constants.sPreferenceLevelStronglyDiscouraged * rcs
1066 * Constants.SLOTS_PER_DAY_NO_EVENINGS * Constants.NR_DAYS_WEEK));
1067 w.println(dx.format(scheduler) + "." + dx.format(idx++) + " Too big room," + tooBigRooms);
1068 w.println(dx.format(scheduler) + "." + dx.format(bidx++) + " Too big room max,"
1069 + (Constants.sPreferenceLevelStronglyDiscouraged * vars.size()));
1070 w.println(dx.format(scheduler) + "." + dx.format(idx++) + " Distribution preferences," + grPref);
1071 w
1072 .println(dx.format(scheduler) + "." + dx.format(bidx++) + " Distribution preferences min,"
1073 + minGrPref);
1074 w
1075 .println(dx.format(scheduler) + "." + dx.format(bidx++) + " Distribution preferences max,"
1076 + maxGrPref);
1077 if (m.getProperties().getPropertyBoolean("General.UseDistanceConstraints", true))
1078 w.println(dx.format(scheduler) + "." + dx.format(idx++) + " Back-to-back instructor pref,"
1079 + instPref);
1080 w.println(dx.format(scheduler) + "." + dx.format(bidx++) + " Back-to-back instructor pref max,"
1081 + worstInstrPref);
1082 if (m.getProperties().getPropertyBoolean("General.DeptBalancing", true)) {
1083 w.println(dx.format(scheduler) + "." + dx.format(idx++) + " Department balancing penalty,"
1084 + sDoubleFormat.format((deptSpreadPen) / 12.0));
1085 }
1086 w.println(dx.format(scheduler) + "." + dx.format(idx++) + " Same subpart balancing penalty,"
1087 + sDoubleFormat.format((spreadPen) / 12.0));
1088 }
1089 w.flush();
1090 w.close();
1091 } catch (java.io.IOException io) {
1092 sLogger.error(io.getMessage(), io);
1093 }
1094 }
1095 }