001 package net.sf.cpsolver.ifs.heuristics; 002 003 import java.util.Collection; 004 import java.util.Enumeration; 005 import java.util.Iterator; 006 import java.util.Set; 007 import java.util.Vector; 008 009 import org.apache.log4j.Logger; 010 011 import net.sf.cpsolver.ifs.constant.ConstantVariable; 012 import net.sf.cpsolver.ifs.extension.ConflictStatistics; 013 import net.sf.cpsolver.ifs.extension.Extension; 014 import net.sf.cpsolver.ifs.model.Model; 015 import net.sf.cpsolver.ifs.model.Neighbour; 016 import net.sf.cpsolver.ifs.model.Value; 017 import net.sf.cpsolver.ifs.model.Variable; 018 import net.sf.cpsolver.ifs.solution.Solution; 019 import net.sf.cpsolver.ifs.solver.Solver; 020 import net.sf.cpsolver.ifs.util.DataProperties; 021 022 /** 023 * Backtracking-based neighbour selection. A best neighbour that is found by 024 * a backtracking-based algorithm within a limited depth from a selected variable 025 * is returned. 026 * <br><br> 027 * Parameters: 028 * <br> 029 * <table border='1'><tr><th>Parameter</th><th>Type</th><th>Comment</th></tr> 030 * <tr><td>Neighbour.BackTrackTimeout</td><td>{@link Integer}</td><td>Timeout for each neighbour selection (in milliseconds).</td></tr> 031 * <tr><td>Neighbour.BackTrackDepth</td><td>{@link Integer}</td><td>Limit of search depth.</td></tr> 032 * </table> 033 * 034 * <br><br> 035 * 036 * @version 037 * StudentSct 1.1 (Student Sectioning)<br> 038 * Copyright (C) 2007 Tomáš Müller<br> 039 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 040 * Lazenska 391, 76314 Zlin, Czech Republic<br> 041 * <br> 042 * This library is free software; you can redistribute it and/or 043 * modify it under the terms of the GNU Lesser General Public 044 * License as published by the Free Software Foundation; either 045 * version 2.1 of the License, or (at your option) any later version. 046 * <br><br> 047 * This library is distributed in the hope that it will be useful, 048 * but WITHOUT ANY WARRANTY; without even the implied warranty of 049 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 050 * Lesser General Public License for more details. 051 * <br><br> 052 * You should have received a copy of the GNU Lesser General Public 053 * License along with this library; if not, write to the Free Software 054 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 055 */ 056 057 public class BacktrackNeighbourSelection extends StandardNeighbourSelection { 058 private ConflictStatistics iStat = null; 059 private static Logger sLog = Logger.getLogger(BacktrackNeighbourSelection.class); 060 private int iTimeout = 5000; 061 private int iDepth = 4; 062 063 protected Solution iSolution = null; 064 protected BackTrackNeighbour iBackTrackNeighbour = null; 065 protected double iValue = 0; 066 private int iNrAssigned = 0; 067 private long iT0,iT1; 068 private boolean iTimeoutReached = false; 069 private int iMaxIters = -1, iNrIters = 0; 070 private boolean iMaxItersReached = false; 071 072 /** 073 * Constructor 074 * @param properties configuration 075 * @throws Exception 076 */ 077 public BacktrackNeighbourSelection(DataProperties properties) throws Exception { 078 super(properties); 079 iTimeout = properties.getPropertyInt("Neighbour.BackTrackTimeout", iTimeout); 080 iDepth = properties.getPropertyInt("Neighbour.BackTrackDepth", iDepth); 081 iMaxIters = properties.getPropertyInt("Neighbour.BackTrackMaxIters", iMaxIters); 082 } 083 084 /** Solver initialization */ 085 public void init(Solver solver) { 086 super.init(solver); 087 for (Enumeration i = solver.getExtensions().elements(); i.hasMoreElements();) { 088 Extension extension = (Extension)i.nextElement(); 089 if (extension instanceof ConflictStatistics) 090 iStat = (ConflictStatistics)extension; 091 } 092 } 093 094 /** 095 * Select neighbour. The standard variable selection 096 * (see {@link StandardNeighbourSelection#getVariableSelection()}) is used to select 097 * a variable. A backtracking of a limited depth is than employed from this variable. 098 * The best assignment found is returned (see {@link BackTrackNeighbour}). 099 **/ 100 public Neighbour selectNeighbour(Solution solution) { 101 return selectNeighbour(solution, getVariableSelection().selectVariable(solution)); 102 } 103 104 /** 105 * Select neighbour -- starts from the provided variable. A backtracking of a limited 106 * depth is employed from the given variable. 107 * The best assignment found is returned (see {@link BackTrackNeighbour}). 108 **/ 109 public synchronized Neighbour selectNeighbour(Solution solution, Variable variable) { 110 if (variable==null) return null; 111 112 iSolution = solution; 113 iBackTrackNeighbour = null; 114 iValue = solution.getModel().getTotalValue(); 115 iNrAssigned = solution.getModel().assignedVariables().size(); 116 iT0 = System.currentTimeMillis(); 117 iNrIters = 0; 118 iTimeoutReached = false; 119 iMaxItersReached = false; 120 121 synchronized (solution) { 122 Model model = solution.getModel(); 123 if (sLog.isDebugEnabled()) sLog.debug("-- before BT ("+variable.getName()+"): nrAssigned="+iSolution.getModel().assignedVariables().size()+", value="+iSolution.getModel().getTotalValue()); 124 125 Vector variables2resolve = new Vector(1); 126 variables2resolve.add(variable); 127 backtrack(variables2resolve, 0, iDepth); 128 129 if (sLog.isDebugEnabled()) sLog.debug("-- after BT ("+variable.getName()+"): nrAssigned="+iSolution.getModel().assignedVariables().size()+", value="+iSolution.getModel().getTotalValue()); 130 } 131 132 iT1 = System.currentTimeMillis(); 133 134 if (sLog.isDebugEnabled()) sLog.debug("-- selected neighbour: "+iBackTrackNeighbour); 135 return iBackTrackNeighbour; 136 } 137 138 /** Time needed to find a neighbour (last call of selectNeighbour method) */ 139 public long getTime() { return iT1 - iT0; } 140 141 /** True, if timeout was reached during the last call of selectNeighbour method */ 142 public boolean isTimeoutReached() { return iTimeoutReached; } 143 144 /** True, if the maximum number of iterations was reached by the last call of selectNeighbour method */ 145 public boolean isMaxItersReached() { return iMaxItersReached; } 146 147 private boolean containsConstantValues(Collection values) { 148 for (Iterator i=values.iterator();i.hasNext();) { 149 Value value = (Value)i.next(); 150 if (value.variable() instanceof ConstantVariable && ((ConstantVariable)value.variable()).isConstant()) 151 return true; 152 } 153 return false; 154 } 155 156 /** List of values of the given variable that will be considered */ 157 protected Enumeration values(Variable variable) { 158 return variable.values().elements(); 159 } 160 161 /** Check bound */ 162 protected boolean checkBound(Vector variables2resolve, int idx, int depth, Value value, Set conflicts) { 163 int nrUnassigned = variables2resolve.size()-idx; 164 if ((nrUnassigned+conflicts.size()>depth)) { 165 if (sLog.isDebugEnabled()) sLog.debug(" -- too deap"); 166 return false; 167 } 168 if (containsConstantValues(conflicts)) { 169 if (sLog.isDebugEnabled()) sLog.debug(" -- contains constants values"); 170 return false; 171 } 172 boolean containAssigned = false; 173 for (Iterator i=conflicts.iterator();!containAssigned && i.hasNext();) { 174 Value conflict = (Value)i.next(); 175 int confIdx = variables2resolve.indexOf(conflict.variable()); 176 if (confIdx>=0 && confIdx<=idx) { 177 if (sLog.isDebugEnabled()) sLog.debug(" -- contains resolved variable "+conflict.variable()); 178 containAssigned = true; 179 } 180 } 181 if (containAssigned) return false; 182 return true; 183 } 184 185 /** Check whether backtrack can continue */ 186 protected boolean canContinue(Vector variables2resolve, int idx, int depth) { 187 if (depth<=0) { 188 if (sLog.isDebugEnabled()) sLog.debug(" -- depth reached"); 189 return false; 190 } 191 if (iTimeoutReached) { 192 if (sLog.isDebugEnabled()) sLog.debug(" -- timeout reached"); 193 return false; 194 } 195 if (iMaxItersReached) { 196 if (sLog.isDebugEnabled()) sLog.debug(" -- max number of iterations reached"); 197 return false; 198 } 199 return true; 200 } 201 202 protected boolean canContinueEvaluation() { 203 return !iTimeoutReached && !iMaxItersReached; 204 } 205 206 /** Backtracking */ 207 protected void backtrack(Vector variables2resolve, int idx, int depth) { 208 if (sLog.isDebugEnabled()) sLog.debug(" -- bt["+depth+"]: "+idx+" of "+variables2resolve.size()+" "+variables2resolve); 209 if (!iTimeoutReached && iTimeout>0 && System.currentTimeMillis()-iT0>iTimeout) 210 iTimeoutReached = true; 211 if (!iMaxItersReached && iMaxIters>0 && iNrIters++>iMaxIters) 212 iMaxItersReached = true; 213 int nrUnassigned = variables2resolve.size()-idx; 214 if (nrUnassigned==0) { 215 if (sLog.isDebugEnabled()) sLog.debug(" -- all assigned"); 216 if (iSolution.getModel().assignedVariables().size()>iNrAssigned || (iSolution.getModel().assignedVariables().size()==iNrAssigned && iValue>iSolution.getModel().getTotalValue())) { 217 if (sLog.isDebugEnabled()) sLog.debug(" -- better than current"); 218 if (iBackTrackNeighbour==null || iBackTrackNeighbour.compareTo(iSolution)>=0) { 219 if (sLog.isDebugEnabled()) sLog.debug(" -- better than best"); 220 iBackTrackNeighbour=new BackTrackNeighbour(variables2resolve); 221 } 222 } 223 return; 224 } 225 if (!canContinue(variables2resolve, idx, depth)) return; 226 Variable variable = (Variable)variables2resolve.elementAt(idx); 227 if (sLog.isDebugEnabled()) sLog.debug(" -- variable "+variable); 228 for (Enumeration e=values(variable);canContinueEvaluation() && e.hasMoreElements();) { 229 Value value = (Value)e.nextElement(); 230 if (value.equals(variable.getAssignment())) continue; 231 if (sLog.isDebugEnabled()) sLog.debug(" -- value "+value); 232 Set conflicts = iSolution.getModel().conflictValues(value); 233 if (sLog.isDebugEnabled()) sLog.debug(" -- conflicts "+conflicts); 234 if (!checkBound(variables2resolve,idx,depth,value,conflicts)) continue; 235 Value current = variable.getAssignment(); 236 Vector newVariables2resolve = new Vector(variables2resolve); 237 for (Iterator i=conflicts.iterator();i.hasNext();) { 238 Value conflict = (Value)i.next(); 239 conflict.variable().unassign(0); 240 if (!newVariables2resolve.contains(conflict.variable())) 241 newVariables2resolve.addElement(conflict.variable()); 242 } 243 if (current!=null) current.variable().unassign(0); 244 value.variable().assign(0, value); 245 backtrack(newVariables2resolve, idx+1, depth-1); 246 if (current==null) 247 variable.unassign(0); 248 else 249 variable.assign(0, current); 250 for (Iterator i=conflicts.iterator();i.hasNext();) { 251 Value conflict = (Value)i.next(); 252 conflict.variable().assign(0, conflict); 253 } 254 } 255 } 256 257 258 /** Backtracking neighbour */ 259 public class BackTrackNeighbour extends Neighbour { 260 private double iTotalValue = 0; 261 private double iValue = 0; 262 private Vector iDifferentAssignments = null; 263 264 /** 265 * Constructor 266 * @param resolvedVariables variables that has been changed 267 */ 268 public BackTrackNeighbour(Vector resolvedVariables) { 269 iTotalValue = iSolution.getModel().getTotalValue(); 270 iValue = 0; 271 iDifferentAssignments = new Vector(); 272 for (Enumeration e=resolvedVariables.elements();e.hasMoreElements();) { 273 Variable variable = (Variable)e.nextElement(); 274 Value value = variable.getAssignment(); 275 iDifferentAssignments.add(value); 276 iValue += value.toDouble(); 277 } 278 } 279 280 /** Neighbour value (solution total value if the neighbour is applied). */ 281 public double getTotalValue() { 282 return iTotalValue; 283 } 284 285 /** Sum of values of variables from the neighbour that change their values */ 286 public double value() { 287 return iValue; 288 } 289 290 /** Neighbour assignments */ 291 public Vector getAssignments() { 292 return iDifferentAssignments; 293 } 294 295 /** 296 * Assign the neighbour 297 */ 298 public void assign(long iteration) { 299 if (sLog.isDebugEnabled()) sLog.debug("-- before assignment: nrAssigned="+iSolution.getModel().assignedVariables().size()+", value="+iSolution.getModel().getTotalValue()); 300 if (sLog.isDebugEnabled()) sLog.debug(" "+this); 301 int idx=0; 302 for (Enumeration e=iDifferentAssignments.elements();e.hasMoreElements();idx++) { 303 Value p = (Value)e.nextElement(); 304 if (p.variable().getAssignment()!=null) { 305 if (idx>0 && iStat!=null) 306 iStat.variableUnassigned(iteration, p.variable().getAssignment(), (Value)iDifferentAssignments.firstElement()); 307 p.variable().unassign(iteration); 308 } 309 } 310 for (Enumeration e=iDifferentAssignments.elements();e.hasMoreElements();) { 311 Value p = (Value)e.nextElement(); 312 p.variable().assign(iteration, p); 313 } 314 if (sLog.isDebugEnabled()) sLog.debug("-- after assignment: nrAssigned="+iSolution.getModel().assignedVariables().size()+", value="+iSolution.getModel().getTotalValue()); 315 } 316 317 /** 318 * Compare two neighbours 319 */ 320 public int compareTo(Solution solution) { 321 return Double.compare(iTotalValue, solution.getModel().getTotalValue()); 322 } 323 324 public String toString() { 325 StringBuffer sb = new StringBuffer("BT{value="+(iTotalValue-iSolution.getModel().getTotalValue())+": "); 326 for (Enumeration e=iDifferentAssignments.elements();e.hasMoreElements();) { 327 Value p = (Value)e.nextElement(); 328 sb.append("\n "+p.variable().getName()+" "+p.getName()+(e.hasMoreElements()?",":"")); 329 } 330 sb.append("}"); 331 return sb.toString(); 332 } 333 } 334 335 /** Return maximal depth */ 336 public int getDepth() { 337 return iDepth; 338 } 339 /** Set maximal depth */ 340 public void setDepth(int depth) { 341 iDepth = depth; 342 } 343 /** Return time limit */ 344 public int getTimeout() { 345 return iTimeout; 346 } 347 /** Set time limit */ 348 public void setTimeout(int timeout) { 349 iTimeout = timeout; 350 } 351 /** Return maximal number of iterations */ 352 public int getMaxIters() { 353 return iMaxIters; 354 } 355 /** Set maximal number of iterations */ 356 public void setMaxIters(int maxIters) { 357 iMaxIters = maxIters; 358 } 359 }