001package org.cpsolver.coursett; 002 003import org.cpsolver.coursett.heuristics.FixCompleteSolutionNeighbourSelection; 004import org.cpsolver.coursett.heuristics.NeighbourSelectionWithSuggestions; 005import org.cpsolver.coursett.model.Lecture; 006import org.cpsolver.coursett.model.Placement; 007import org.cpsolver.coursett.model.TimetableModel; 008import org.cpsolver.ifs.assignment.Assignment; 009import org.cpsolver.ifs.model.Neighbour; 010import org.cpsolver.ifs.solution.Solution; 011import org.cpsolver.ifs.solver.Solver; 012import org.cpsolver.ifs.util.DataProperties; 013import org.cpsolver.ifs.util.JProf; 014import org.cpsolver.ifs.util.Progress; 015 016/** 017 * University course timetabling solver. <br> 018 * <br> 019 * When a complete solution is found, it is improved by limited depth 020 * backtracking search. This way it is ensured that the fund solution is at 021 * least locally optimal.<br> 022 * <br> 023 * Deprecated: use {@link FixCompleteSolutionNeighbourSelection} instead. 024 * 025 * @author Tomáš Müller 026 * @version CourseTT 1.3 (University Course Timetabling)<br> 027 * Copyright (C) 2006 - 2014 Tomáš Müller<br> 028 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 029 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 030 * <br> 031 * This library is free software; you can redistribute it and/or modify 032 * it under the terms of the GNU Lesser General Public License as 033 * published by the Free Software Foundation; either version 3 of the 034 * License, or (at your option) any later version. <br> 035 * <br> 036 * This library is distributed in the hope that it will be useful, but 037 * WITHOUT ANY WARRANTY; without even the implied warranty of 038 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 039 * Lesser General Public License for more details. <br> 040 * <br> 041 * You should have received a copy of the GNU Lesser General Public 042 * License along with this library; if not see 043 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 044 */ 045 046@Deprecated 047public class TimetableSolver extends Solver<Lecture, Placement> { 048 private long iLastCompleteSolutionFixIteration = -1; 049 private long iLastIncompleteSolutionFixIteration = -1; 050 private long iCompleteSolutionFixInterval = 1; 051 private long iIncompleteSolutionFixInterval = 5000; 052 053 public TimetableSolver(DataProperties properties) { 054 super(properties); 055 } 056 057 @Override 058 public void initSolver() { 059 super.initSolver(); 060 iCompleteSolutionFixInterval = getProperties().getPropertyLong("General.CompleteSolutionFixInterval", iCompleteSolutionFixInterval); 061 iIncompleteSolutionFixInterval = getProperties().getPropertyLong("General.IncompleteSolutionFixInterval", iIncompleteSolutionFixInterval); 062 } 063 064 @Override 065 protected void onAssigned(double startTime, Solution<Lecture, Placement> solution) { 066 if (solution.getModel().nrUnassignedVariables(solution.getAssignment()) == 0) { 067 // complete solution was found 068 if (iCompleteSolutionFixInterval < 0) { 069 // feature disabled 070 return; 071 } else if (iCompleteSolutionFixInterval == 0) { 072 // only run first time a complete solution is found 073 if (iLastCompleteSolutionFixIteration >= 0) return; 074 } else { 075 // run first time and if not run for a given number of iterations 076 if (iLastCompleteSolutionFixIteration >= 0 && solution.getIteration() - iLastCompleteSolutionFixIteration < iCompleteSolutionFixInterval) return; 077 } 078 if (getSolutionComparator().isBetterThanBestSolution(solution)) { 079 fixCompleteSolution(solution, startTime); 080 iLastCompleteSolutionFixIteration = solution.getIteration(); 081 } 082 } else if (solution.getBestInfo() == null) { 083 // complete solution has not been found yet 084 if (iIncompleteSolutionFixInterval < 0) { 085 // feature disabled 086 return; 087 } else if (iIncompleteSolutionFixInterval == 0) { 088 // only run first time a complete solution is found 089 if (iLastIncompleteSolutionFixIteration >= 0) return; 090 } else { 091 // run first time and if not run for a given number of iterations 092 if (iLastIncompleteSolutionFixIteration >= 0 && solution.getIteration() - iLastIncompleteSolutionFixIteration < iIncompleteSolutionFixInterval) return; 093 } 094 if (getSolutionComparator().isBetterThanBestSolution(solution)) { 095 fixCompleteSolution(solution, startTime); 096 iLastIncompleteSolutionFixIteration = solution.getIteration(); 097 } 098 } 099 } 100 101 /** 102 * Try to improve existing solution by backtracking search of very limited 103 * depth. See {@link NeighbourSelectionWithSuggestions} for more details. 104 * @param solution current solution 105 * @param startTime start time 106 */ 107 protected void fixCompleteSolution(Solution<Lecture, Placement> solution, double startTime) { 108 Progress progress = Progress.getInstance(solution.getModel()); 109 110 TimetableModel model = (TimetableModel) solution.getModel(); 111 Assignment<Lecture, Placement> assignment = solution.getAssignment(); 112 solution.saveBest(); 113 progress.save(); 114 double solutionValue = 0.0, newSolutionValue = model.getTotalValue(assignment); 115 do { 116 solutionValue = newSolutionValue; 117 progress.setPhase("Fixing solution", model.variables().size()); 118 for (Lecture variable : model.variables()) { 119 Placement bestValue = null; 120 double bestVal = 0.0; 121 Placement currentValue = assignment.getValue(variable); 122 if (currentValue == null) 123 continue; 124 double currentVal = currentValue.toDouble(assignment); 125 for (Placement value : variable.values()) { 126 if (value.equals(currentValue)) 127 continue; 128 if (model.conflictValues(assignment, value).isEmpty()) { 129 double val = value.toDouble(assignment); 130 if (bestValue == null || val < bestVal) { 131 bestValue = value; 132 bestVal = val; 133 } 134 } 135 } 136 if (bestValue != null && bestVal < currentVal) 137 assignment.assign(0, bestValue); 138 solution.update(JProf.currentTimeSec() - startTime); 139 progress.incProgress(); 140 if (iStop) 141 break; 142 } 143 newSolutionValue = model.getTotalValue(assignment); 144 if (newSolutionValue < solutionValue) { 145 progress.debug("New solution value is " + newSolutionValue); 146 } 147 } while (!iStop && newSolutionValue < solutionValue && getTerminationCondition().canContinue(solution)); 148 progress.restore(); 149 150 if (!solution.getModel().unassignedVariables(assignment).isEmpty()) 151 return; 152 progress.save(); 153 try { 154 progress.setPhase("Fixing solution [2]", model.variables().size()); 155 NeighbourSelectionWithSuggestions ns = new NeighbourSelectionWithSuggestions(this); 156 for (Lecture lecture : model.variables()) { 157 Neighbour<Lecture, Placement> n = ns.selectNeighbourWithSuggestions(solution, lecture, 2); 158 if (n != null && n.value(assignment) <= 0.0) 159 n.assign(assignment, 0); 160 solution.update(JProf.currentTimeSec() - startTime); 161 progress.incProgress(); 162 if (iStop) 163 break; 164 } 165 } catch (Exception e) { 166 sLogger.debug(e.getMessage(), e); 167 } finally { 168 progress.restore(); 169 } 170 } 171}