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 }