001 package net.sf.cpsolver.coursett.heuristics; 002 003 import java.util.Collection; 004 import java.util.Enumeration; 005 import java.util.Hashtable; 006 import java.util.Iterator; 007 import java.util.Set; 008 import java.util.Vector; 009 010 import net.sf.cpsolver.coursett.model.Lecture; 011 import net.sf.cpsolver.coursett.model.Placement; 012 import net.sf.cpsolver.coursett.model.TimetableModel; 013 import net.sf.cpsolver.ifs.heuristics.StandardNeighbourSelection; 014 import net.sf.cpsolver.ifs.model.Model; 015 import net.sf.cpsolver.ifs.model.Neighbour; 016 import net.sf.cpsolver.ifs.solution.Solution; 017 import net.sf.cpsolver.ifs.solver.Solver; 018 import net.sf.cpsolver.ifs.util.DataProperties; 019 import net.sf.cpsolver.ifs.util.ToolBox; 020 021 /** 022 * Neighbour selection which does the standard time neighbour selection most of the time, 023 * however, the very best neighbour is selected time to time (using backtracking based search). 024 * 025 * @see StandardNeighbourSelection 026 * @version 027 * CourseTT 1.1 (University Course Timetabling)<br> 028 * Copyright (C) 2006 Tomáš Müller<br> 029 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 030 * Lazenska 391, 76314 Zlin, Czech Republic<br> 031 * <br> 032 * This library is free software; you can redistribute it and/or 033 * modify it under the terms of the GNU Lesser General Public 034 * License as published by the Free Software Foundation; either 035 * version 2.1 of the License, or (at your option) any later version. 036 * <br><br> 037 * This library is distributed in the hope that it will be useful, 038 * but WITHOUT ANY WARRANTY; without even the implied warranty of 039 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 040 * Lesser General Public License for more details. 041 * <br><br> 042 * You should have received a copy of the GNU Lesser General Public 043 * License along with this library; if not, write to the Free Software 044 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 045 */ 046 047 public class NeighbourSelectionWithSuggestions extends StandardNeighbourSelection { 048 private double iSuggestionProbability = 0.1; 049 private double iSuggestionProbabilityAllAssigned = 0.5; 050 private int iSuggestionTimeout = 500; 051 private int iSuggestionDepth = 4; 052 053 private Solution iSolution = null; 054 private SuggestionNeighbour iSuggestionNeighbour = null; 055 private TimetableComparator iCmp = null; 056 private double iValue = 0; 057 private int iNrAssigned = 0; 058 059 public NeighbourSelectionWithSuggestions(DataProperties properties) throws Exception { 060 super(properties); 061 iSuggestionProbability = properties.getPropertyDouble("Neighbour.SuggestionProbability", iSuggestionProbability); 062 iSuggestionProbabilityAllAssigned = properties.getPropertyDouble("Neighbour.SuggestionProbabilityAllAssigned", iSuggestionProbabilityAllAssigned); 063 iSuggestionTimeout = properties.getPropertyInt("Neighbour.SuggestionTimeout", iSuggestionTimeout); 064 iSuggestionDepth = properties.getPropertyInt("Neighbour.SuggestionDepth", iSuggestionDepth); 065 } 066 067 public NeighbourSelectionWithSuggestions(Solver solver) throws Exception { 068 this(solver.getProperties()); 069 init(solver); 070 } 071 072 public void init(Solver solver) { 073 super.init(solver); 074 iCmp = (TimetableComparator)solver.getSolutionComparator(); 075 } 076 077 public Neighbour selectNeighbour(Solution solution) { 078 Neighbour neighbour = null; 079 if (solution.getModel().unassignedVariables().isEmpty()) { 080 for (int d=iSuggestionDepth;d>1;d--) { 081 if (ToolBox.random()<Math.pow(iSuggestionProbabilityAllAssigned,d-1)) { 082 neighbour = selectNeighbourWithSuggestions(solution,(Lecture)selectVariable(solution),d); 083 break; 084 } 085 } 086 } else { 087 for (int d=iSuggestionDepth;d>1;d--) { 088 if (ToolBox.random()<Math.pow(iSuggestionProbability,d-1)) { 089 neighbour = selectNeighbourWithSuggestions(solution,(Lecture)selectVariable(solution),d); 090 break; 091 } 092 } 093 } 094 return (neighbour!=null?neighbour:super.selectNeighbour(solution)); 095 } 096 097 public synchronized Neighbour selectNeighbourWithSuggestions(Solution solution, Lecture lecture, int depth) { 098 if (lecture==null) return null; 099 100 iSolution = solution; 101 iSuggestionNeighbour = null; 102 iValue = iCmp.currentValue(solution); 103 iNrAssigned = solution.getModel().assignedVariables().size(); 104 105 synchronized (solution) { 106 Model model = solution.getModel(); 107 //System.out.println("BEFORE BT ("+lecture.getName()+"): nrAssigned="+iSolution.getModel().assignedVariables().size()+", value="+iCmp.currentValue(iSolution)); 108 109 Vector initialLectures = new Vector(1); 110 initialLectures.add(lecture); 111 backtrack(System.currentTimeMillis(), initialLectures, new Vector(), new Hashtable(), depth); 112 113 //System.out.println("AFTER BT ("+lecture.getName()+"): nrAssigned="+iSolution.getModel().assignedVariables().size()+", value="+iCmp.currentValue(iSolution)); 114 } 115 116 return iSuggestionNeighbour; 117 } 118 119 private boolean containsCommited(Collection values) { 120 if (((TimetableModel)iSolution.getModel()).hasConstantVariables()) { 121 for (Iterator i=values.iterator();i.hasNext();) { 122 Placement placement = (Placement)i.next(); 123 Lecture lecture = (Lecture)placement.variable(); 124 if (lecture.isCommitted()) return true; 125 } 126 } 127 return false; 128 } 129 130 private void backtrack(long startTime, Vector initialLectures, Vector resolvedLectures, Hashtable conflictsToResolve, int depth) { 131 int nrUnassigned = conflictsToResolve.size(); 132 if ((initialLectures==null || initialLectures.isEmpty()) && nrUnassigned==0) { 133 if (iSolution.getModel().assignedVariables().size()>iNrAssigned || (iSolution.getModel().assignedVariables().size()==iNrAssigned && iValue>iCmp.currentValue(iSolution))) { 134 if (iSuggestionNeighbour==null || iSuggestionNeighbour.compareTo(iSolution)>=0) 135 iSuggestionNeighbour=new SuggestionNeighbour(resolvedLectures); 136 } 137 return; 138 } 139 if (depth<=0) return; 140 if (iSuggestionTimeout>0 && System.currentTimeMillis()-startTime>iSuggestionTimeout) { 141 return; 142 } 143 for (Enumeration e1=(initialLectures!=null && !initialLectures.isEmpty()?initialLectures.elements():conflictsToResolve.keys());e1.hasMoreElements();) { 144 Lecture lecture = (Lecture)e1.nextElement(); 145 if (resolvedLectures.contains(lecture)) continue; 146 resolvedLectures.add(lecture); 147 for (Enumeration e2=lecture.values().elements();e2.hasMoreElements();) { 148 Placement placement = (Placement)e2.nextElement(); 149 if (placement.equals(lecture.getAssignment())) continue; 150 if (placement.isHard()) continue; 151 Set conflicts = iSolution.getModel().conflictValues(placement); 152 if (conflicts!=null && (nrUnassigned+conflicts.size()>depth)) continue; 153 if (conflicts!=null && conflicts.contains(placement)) continue; 154 if (containsCommited(conflicts)) continue; 155 boolean containException = false; 156 if (conflicts!=null) { 157 for (Iterator i=conflicts.iterator();!containException && i.hasNext();) { 158 Placement c = (Placement)i.next(); 159 if (resolvedLectures.contains(((Lecture)c.variable()).getClassId())) containException = true; 160 } 161 } 162 if (containException) continue; 163 Placement cur = (Placement)lecture.getAssignment(); 164 if (conflicts!=null) { 165 for (Iterator i=conflicts.iterator();!containException && i.hasNext();) { 166 Placement c = (Placement)i.next(); 167 c.variable().unassign(0); 168 } 169 } 170 if (cur!=null) cur.variable().unassign(0); 171 Vector un = new Vector(lecture.getModel().unassignedVariables()); 172 for (Iterator i=conflicts.iterator();!containException && i.hasNext();) { 173 Placement c = (Placement)i.next(); 174 conflictsToResolve.put(c.variable(),c); 175 } 176 Placement resolvedConf = (Placement)conflictsToResolve.remove(lecture); 177 backtrack(startTime, null, resolvedLectures, conflictsToResolve, depth-1); 178 if (cur==null) 179 lecture.unassign(0); 180 else 181 lecture.assign(0, cur); 182 if (conflicts!=null) { 183 for (Iterator i=conflicts.iterator();i.hasNext();) { 184 Placement p = (Placement)i.next(); 185 p.variable().assign(0, p); 186 conflictsToResolve.remove(p.variable()); 187 } 188 } 189 if (resolvedConf!=null) 190 conflictsToResolve.put(lecture, resolvedConf); 191 } 192 resolvedLectures.remove(lecture); 193 } 194 } 195 196 197 public class SuggestionNeighbour extends Neighbour { 198 private double iValue = 0; 199 private Vector iDifferentAssignments = null; 200 201 public SuggestionNeighbour(Vector resolvedLectures) { 202 iValue = iCmp.currentValue(iSolution); 203 iDifferentAssignments = new Vector(); 204 for (Enumeration e=resolvedLectures.elements();e.hasMoreElements();) { 205 Lecture lecture = (Lecture)e.nextElement(); 206 Placement p = (Placement)lecture.getAssignment(); 207 iDifferentAssignments.add(p); 208 } 209 } 210 211 public double value() { 212 return iValue; 213 } 214 215 public void assign(long iteration) { 216 //System.out.println("START ASSIGN: nrAssigned="+iSolution.getModel().assignedVariables().size()+", value="+iCmp.currentValue(iSolution)); 217 //System.out.println(" "+this); 218 for (Enumeration e=iDifferentAssignments.elements();e.hasMoreElements();) { 219 Placement p = (Placement)e.nextElement(); 220 if (p.variable().getAssignment()!=null) 221 p.variable().unassign(iteration); 222 } 223 for (Enumeration e=iDifferentAssignments.elements();e.hasMoreElements();) { 224 Placement p = (Placement)e.nextElement(); 225 p.variable().assign(iteration, p); 226 } 227 //System.out.println("END ASSIGN: nrAssigned="+iSolution.getModel().assignedVariables().size()+", value="+iCmp.currentValue(iSolution)); 228 } 229 230 public int compareTo(Solution solution) { 231 return Double.compare(iValue, iCmp.currentValue(solution)); 232 } 233 234 public String toString() { 235 StringBuffer sb = new StringBuffer("Suggestion{value="+(iValue-iCmp.currentValue(iSolution))+": "); 236 for (Enumeration e=iDifferentAssignments.elements();e.hasMoreElements();) { 237 Placement p = (Placement)e.nextElement(); 238 sb.append("\n "+p.variable().getName()+" "+p.getName()+(e.hasMoreElements()?",":"")); 239 } 240 sb.append("}"); 241 return sb.toString(); 242 } 243 } 244 }