001 package net.sf.cpsolver.ifs.extension; 002 003 004 import java.util.*; 005 006 import net.sf.cpsolver.ifs.model.*; 007 import net.sf.cpsolver.ifs.solver.*; 008 import net.sf.cpsolver.ifs.util.*; 009 010 /** 011 * Another implementation of MAC propagation. 012 * 013 * @see MacPropagation 014 * 015 * @version 016 * IFS 1.1 (Iterative Forward Search)<br> 017 * Copyright (C) 2006 Tomáš Müller<br> 018 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 019 * Lazenska 391, 76314 Zlin, Czech Republic<br> 020 * <br> 021 * This library is free software; you can redistribute it and/or 022 * modify it under the terms of the GNU Lesser General Public 023 * License as published by the Free Software Foundation; either 024 * version 2.1 of the License, or (at your option) any later version. 025 * <br><br> 026 * This library is distributed in the hope that it will be useful, 027 * but WITHOUT ANY WARRANTY; without even the implied warranty of 028 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 029 * Lesser General Public License for more details. 030 * <br><br> 031 * You should have received a copy of the GNU Lesser General Public 032 * License along with this library; if not, write to the Free Software 033 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 034 */ 035 036 public class MacRevised extends Extension { 037 private static org.apache.log4j.Logger sLogger = org.apache.log4j.Logger.getLogger(MacRevised.class); 038 private boolean iDbt = false; 039 private Progress iProgress; 040 041 /** List of constraints on which arc-consistency is to be maintained */ 042 protected Vector iConstraints = null; 043 /** Current iteration */ 044 protected long iIteration = 0; 045 046 /** Constructor */ 047 public MacRevised(Solver solver, DataProperties properties) { 048 super(solver, properties); 049 iDbt = properties.getPropertyBoolean("MacRevised.Dbt", false); 050 } 051 052 /** Adds a constraint on which arc-consistency is to be maintained */ 053 public void addConstraint(Constraint constraint) { 054 if (iConstraints == null) iConstraints = new FastVector(); 055 iConstraints.addElement(constraint); 056 } 057 058 /** Returns true, if arc-consistency is to be maintained on the given constraint */ 059 public boolean contains(Constraint constraint) { 060 if (iConstraints == null) return true; 061 return iConstraints.contains(constraint); 062 } 063 064 /** Before a value is unassigned: until the value is inconsistent with the current solution, 065 * an assignment from its explanation is picked and unassigned. 066 */ 067 public void beforeAssigned(long iteration, Value value) { 068 sLogger.debug("Before assign "+value.variable().getName() + " = " + value.getName()); 069 iIteration = iteration; 070 if (value == null) return; 071 while (!isGood(value) && !noGood(value).isEmpty()) { 072 if (iDbt) sLogger.warn("Going to assign a no-good value "+value+" (noGood:" + noGood(value) + ")."); 073 Value noGoodValue = (Value)noGood(value).iterator().next(); 074 noGoodValue.variable().unassign(iteration); 075 } 076 if (!isGood(value)) { 077 sLogger.warn("Going to assign a bad value "+value+" with empty no-good."); 078 } 079 } 080 081 /** After a value is assigned: explanations of other values of the value's variable are reset (to contain only the assigned value), 082 * propagation over the assigned variable takes place. 083 */ 084 public void afterAssigned(long iteration, Value value) { 085 sLogger.debug("After assign "+value.variable().getName() + " = " + value.getName()); 086 iIteration = iteration; 087 if (!isGood(value)) { 088 sLogger.warn(value.variable().getName() + " = " + value.getName() + " -- not good value assigned (noGood:" + noGood(value) + ")"); 089 setGood(value); 090 } 091 092 HashSet noGood = new HashSet(1); noGood.add(value); 093 Vector queue = new Vector(); 094 for (Enumeration i = value.variable().values().elements(); i.hasMoreElements(); ) { 095 Value anotherValue = (Value)i.nextElement(); 096 if (anotherValue.equals(value) || !isGood(anotherValue)) continue; 097 setNoGood(anotherValue, noGood); 098 queue.add(anotherValue); 099 } 100 propagate(queue); 101 } 102 103 /** After a value is unassigned: explanations of all values of unassigned variable are recomputed ({@link Value#conflicts()}), propagation 104 * undo over the unassigned variable takes place. 105 */ 106 public void afterUnassigned(long iteration, Value value) { 107 sLogger.debug("After unassign "+value.variable().getName() + " = " + value.getName()); 108 iIteration = iteration; 109 if (!isGood(value)) 110 sLogger.error(value.variable().getName() + " = " + value.getName() + " -- not good value unassigned (noGood:" + noGood(value) + ")"); 111 112 Vector back = new Vector(supportValues(value.variable())); 113 for (Enumeration e=back.elements();e.hasMoreElements();) { 114 Value aValue = (Value)e.nextElement(); 115 if (aValue.variable().getAssignment()!=null) { 116 HashSet noGood = new HashSet(1); noGood.add(aValue.variable().getAssignment()); 117 setNoGood(aValue, noGood); 118 } else 119 setGood(aValue); 120 } 121 122 Vector queue = new Vector(); 123 for (Enumeration e=back.elements();e.hasMoreElements();) { 124 Value aValue = (Value)e.nextElement(); 125 if (!isGood(aValue) || revise(aValue)) 126 queue.add(aValue); 127 } 128 129 propagate(queue); 130 } 131 132 public void propagate(Vector queue) { 133 int idx = 0; 134 while (queue.size()>idx) { 135 Value value = (Value)queue.elementAt(idx++); 136 sLogger.debug(" -- propagate "+value.variable().getName()+" = " + value.getName()+" (noGood:" + noGood(value) + ")"); 137 if (goodValues(value.variable()).isEmpty()) { 138 sLogger.info("Empty domain detected for variable "+value.variable().getName()); 139 continue; 140 } 141 for (Enumeration e=value.variable().hardConstraints().elements(); e.hasMoreElements(); ) { 142 Constraint constraint = (Constraint)e.nextElement(); 143 if (!contains(constraint)) continue; 144 propagate(constraint, value, queue); 145 } 146 } 147 } 148 149 public void propagate(Constraint constraint, Value noGoodValue, Vector queue) { 150 for (Enumeration e1 = constraint.variables().elements();e1.hasMoreElements();) { 151 Variable aVariable = (Variable)e1.nextElement(); 152 if (aVariable.equals(noGoodValue.variable())) continue; 153 for (Enumeration e2=aVariable.values().elements(); e2.hasMoreElements();) { 154 Value aValue = (Value)e2.nextElement(); 155 if (isGood(aValue) && constraint.isConsistent(noGoodValue,aValue) && !hasSupport(constraint, aValue, noGoodValue.variable())) { 156 setNoGood(aValue, explanation(constraint, aValue, noGoodValue.variable())); 157 queue.addElement(aValue); 158 } 159 } 160 } 161 } 162 163 public boolean revise(Value value) { 164 sLogger.debug(" -- revise "+value.variable().getName()+" = " + value.getName()); 165 for (Enumeration e=value.variable().hardConstraints().elements(); e.hasMoreElements(); ) { 166 Constraint constraint = (Constraint)e.nextElement(); 167 if (!contains(constraint)) continue; 168 if (revise(constraint, value)) return true; 169 } 170 return false; 171 } 172 173 public boolean revise(Constraint constraint, Value value) { 174 for (Enumeration e1 = constraint.variables().elements();e1.hasMoreElements();) { 175 Variable aVariable = (Variable)e1.nextElement(); 176 if (aVariable.equals(value.variable())) continue; 177 if (!hasSupport(constraint, value, aVariable)) { 178 setNoGood(value, explanation(constraint, value, aVariable)); 179 return true; 180 } 181 } 182 return false; 183 } 184 185 public HashSet explanation(Constraint constraint, Value value, Variable variable) { 186 HashSet expl = new HashSet(); 187 for (Enumeration e=variable.values().elements();e.hasMoreElements();) { 188 Value aValue = (Value)e.nextElement(); 189 if (constraint.isConsistent(aValue,value)) { 190 expl.addAll(noGood(aValue)); 191 } 192 } 193 return expl; 194 } 195 196 public HashSet supports(Constraint constraint, Value value, Variable variable) { 197 HashSet sup = new HashSet(); 198 for (Enumeration e=variable.values().elements();e.hasMoreElements();) { 199 Value aValue = (Value)e.nextElement(); 200 if (!isGood(aValue)) continue; 201 if (!constraint.isConsistent(aValue,value)) continue; 202 sup.add(aValue); 203 } 204 return sup; 205 } 206 207 public boolean hasSupport(Constraint constraint, Value value, Variable variable) { 208 for (Enumeration e=variable.values().elements();e.hasMoreElements();) { 209 Value aValue = (Value)e.nextElement(); 210 if (isGood(aValue) && constraint.isConsistent(aValue,value)) { 211 //sLogger.debug(" -- "+variable.getName()+" = "+aValue.getName()+" supports " + value.variable().getName()+" = "+value.getName()+" (constraint:"+constraint.getName()+")"); 212 return true; 213 } 214 } 215 //sLogger.debug(" -- value "+value.variable().getName()+" = " + value.getName()+" has no support from values of variable "+variable.getName()+" (constraint:"+constraint.getName()+")"); 216 /* 217 for (Enumeration e=variable.values().elements();e.hasMoreElements();) { 218 Value aValue = (Value)e.nextElement(); 219 if (constraint.isConsistent(aValue,value)) { 220 //sLogger.debug(" -- support "+aValue.getName()+" is not good: "+expl2str(noGood(aValue))); 221 } 222 } 223 */ 224 return false; 225 } 226 227 228 /** Initialization. Enforce arc-consistency over the current (initial) solution. AC3 algorithm is used. */ 229 public boolean init(Solver solver) { 230 boolean isOk = true; 231 iProgress = Progress.getInstance(getModel()); 232 for (Enumeration e=getModel().constraints().elements();e.hasMoreElements();) { 233 Constraint constraint = (Constraint)e.nextElement(); 234 sLogger.debug("Constraint "+constraint.getName()); 235 } 236 iProgress.save(); 237 iProgress.setPhase("Initializing propagation:", getModel().variables().size()); 238 for (Enumeration i1 = getModel().variables().elements(); i1.hasMoreElements(); ) { 239 Variable aVariable = (Variable)i1.nextElement(); 240 supportValues(aVariable).clear(); 241 goodValues(aVariable).clear(); 242 } 243 Vector queue = new Vector(); 244 for (Enumeration i1 = getModel().variables().elements(); i1.hasMoreElements(); ) { 245 Variable aVariable = (Variable)i1.nextElement(); 246 for (Enumeration i2 = aVariable.values().elements(); i2.hasMoreElements(); ) { 247 Value aValue = (Value)i2.nextElement(); 248 initNoGood(aValue, null); 249 goodValues(aVariable).add(aValue); 250 if (revise(aValue)) { 251 queue.add(aValue); 252 } else if (aVariable.getAssignment()!=null && !aValue.equals(aVariable.getAssignment())) { 253 HashSet noGood = new HashSet(); noGood.add(aVariable.getAssignment()); 254 setNoGood(aValue, noGood); 255 queue.add(noGood); 256 } 257 } 258 iProgress.incProgress(); 259 } 260 propagate(queue); 261 iProgress.restore(); 262 return isOk; 263 } 264 265 /** support values of a variable */ 266 private Set supportValues(Variable variable) { 267 Set[] ret = (Set[])variable.getExtra(); 268 if (ret == null) { 269 ret = new HashSet[] { new HashSet(1000), new HashSet()}; 270 variable.setExtra(ret); 271 } 272 return ret[0]; 273 } 274 275 /** good values of a variable (values not removed from variables domain)*/ 276 public Set goodValues(Variable variable) { 277 Set[] ret = (Set[])variable.getExtra(); 278 if (ret == null) { 279 ret = new HashSet[] { new HashSet(1000), new HashSet()}; 280 variable.setExtra(ret); 281 } 282 return ret[1]; 283 } 284 285 /** notification that a nogood value becomes good or vice versa */ 286 private void goodnessChanged(Value value) { 287 if (isGood(value)) { 288 goodValues(value.variable()).add(value); 289 } 290 else { 291 goodValues(value.variable()).remove(value); 292 } 293 } 294 /** removes support of a variable */ 295 private void removeSupport(Variable variable, Value value) { 296 supportValues(variable).remove(value); 297 } 298 /** adds support of a variable */ 299 private void addSupport(Variable variable, Value value) { 300 supportValues(variable).add(value); 301 } 302 303 /** variables explanation */ 304 public Set noGood(Value value) { 305 return (Set)value.getExtra(); 306 } 307 /** is variable good */ 308 public boolean isGood(Value value) { 309 return (value.getExtra() == null); 310 } 311 /** sets value to be good */ 312 protected void setGood(Value value) { 313 sLogger.debug(" -- set good "+value.variable().getName()+" = " + value.getName()); 314 Set noGood = noGood(value); 315 if (noGood != null) 316 for (Iterator i = noGood.iterator(); i.hasNext();) 317 removeSupport(((Value)i.next()).variable(), value); 318 value.setExtra(null); 319 goodnessChanged(value); 320 } 321 /** sets values explanation (initialization) */ 322 private void initNoGood(Value value, Set reason) { 323 value.setExtra(reason); 324 } 325 326 private static String expl2str(Set expl) { 327 StringBuffer sb = new StringBuffer("["); 328 for (Iterator i=expl.iterator();i.hasNext();) { 329 Value value = (Value)i.next(); 330 sb.append(value.variable().getName()+"=" + value.getName()); 331 if (i.hasNext()) sb.append(", "); 332 } 333 sb.append("]"); 334 return sb.toString(); 335 } 336 337 private static void checkExpl(Set expl) { 338 sLogger.debug(" -- checking explanation: "+expl2str(expl)); 339 for (Iterator i=expl.iterator();i.hasNext();) { 340 Value value = (Value)i.next(); 341 if (!value.equals(value.variable().getAssignment())) { 342 if (value.variable().getAssignment()==null) 343 sLogger.warn(" -- variable "+value.variable().getName()+" unassigned"); 344 else 345 sLogger.warn(" -- variable "+value.variable().getName()+" assigned to a different value "+value.variable().getAssignment().getName()); 346 } 347 } 348 } 349 350 private void printAssignments() { 351 sLogger.debug(" -- printing assignments: "); 352 for (Enumeration e=getModel().variables().elements();e.hasMoreElements();) { 353 Variable variable = (Variable)e.nextElement(); 354 if (variable.getAssignment()!=null) 355 sLogger.debug(" -- "+variable.getName()+" = "+variable.getAssignment().getName()); 356 } 357 } 358 359 /** sets value's explanation*/ 360 public void setNoGood(Value value, Set reason) { 361 sLogger.debug(" -- set nogood "+value.variable().getName()+" = " + value.getName()+"(expl:"+expl2str(reason)+")"); 362 if (value.equals(value.variable().getAssignment())) { 363 try { 364 throw new Exception("An assigned value "+value.variable().getName() + " = " + value.getName() + " become no good (noGood:" + reason + ")!!"); 365 } catch (Exception e) { 366 sLogger.warn(e.getMessage(),e); 367 } 368 checkExpl(reason); 369 printAssignments(); 370 } 371 Set noGood = noGood(value); 372 if (noGood != null) 373 for (Iterator i = noGood.iterator(); i.hasNext();) 374 removeSupport(((Value)i.next()).variable(), value); 375 value.setExtra(reason); 376 for (Iterator i = reason.iterator(); i.hasNext();) { 377 Value aValue = (Value)i.next(); 378 addSupport(aValue.variable(), value); 379 } 380 goodnessChanged(value); 381 } 382 }