001package net.sf.cpsolver.ifs.model; 002 003import java.util.ArrayList; 004import java.util.HashMap; 005import java.util.List; 006import java.util.Map; 007 008import net.sf.cpsolver.ifs.util.IdGenerator; 009 010/** 011 * Generic variable. <br> 012 * <br> 013 * Besides a domain of values, a variable also contains information about 014 * assigned value, the value assigned in the best ever found solution and also 015 * the initial value (minimal perturbations problem). It also knows what 016 * constraints are associated with this variable and has a unique id. 017 * 018 * @see Value 019 * @see Model 020 * @see net.sf.cpsolver.ifs.solver.Solver 021 * 022 * @version IFS 1.2 (Iterative Forward Search)<br> 023 * Copyright (C) 2006 - 2010 Tomáš Müller<br> 024 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 025 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 026 * <br> 027 * This library is free software; you can redistribute it and/or modify 028 * it under the terms of the GNU Lesser General Public License as 029 * published by the Free Software Foundation; either version 3 of the 030 * License, or (at your option) any later version. <br> 031 * <br> 032 * This library is distributed in the hope that it will be useful, but 033 * WITHOUT ANY WARRANTY; without even the implied warranty of 034 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 035 * Lesser General Public License for more details. <br> 036 * <br> 037 * You should have received a copy of the GNU Lesser General Public 038 * License along with this library; if not see 039 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 040 */ 041public class Variable<V extends Variable<V, T>, T extends Value<V, T>> implements Comparable<V> { 042 private static IdGenerator iIdGenerator = new IdGenerator(); 043 044 protected long iId = -1; 045 private Model<V, T> iModel = null; 046 047 private T iInitialValue = null; // initial value 048 /** Assigned value */ 049 protected T iValue = null; // assigned value 050 private T iBestValue = null; // best value 051 private long iBestAssignmentIteration = 0; 052 private List<T> iValues = null; 053 054 private T iRecentlyRemovedValue = null; 055 056 private long iAssignmentCounter = 0; 057 private long iLastAssignmentIteration = -1; 058 private long iLastUnassignmentIteration = -1; 059 private Object iExtra = null; 060 061 private List<Constraint<V, T>> iConstraints = new ArrayList<Constraint<V, T>>(); 062 private List<Constraint<V, T>> iHardConstraints = new ArrayList<Constraint<V, T>>(); 063 private List<Constraint<V, T>> iSoftConstraints = new ArrayList<Constraint<V, T>>(); 064 private List<VariableListener<T>> iVariableListeners = null; 065 066 private Map<V, List<Constraint<V, T>>> iConstraintVariables = null; 067 068 /** Constructor */ 069 public Variable() { 070 this(null); 071 } 072 073 /** 074 * Constructor 075 * 076 * @param initialValue 077 * initial value (minimal-perturbation problem) 078 */ 079 public Variable(T initialValue) { 080 iId = iIdGenerator.newId(); 081 setInitialAssignment(initialValue); 082 } 083 084 /** Model, the variable belong to */ 085 public Model<V, T> getModel() { 086 return iModel; 087 } 088 089 /** Set the model to which the variable belongs to */ 090 public void setModel(Model<V, T> model) { 091 iModel = model; 092 } 093 094 /** Domain */ 095 public List<T> values() { 096 return iValues; 097 } 098 099 /** Sets the domain */ 100 protected void setValues(List<T> values) { 101 iValues = values; 102 } 103 104 /** True, if the variable's domain is not empty */ 105 public boolean hasValues() { 106 return !values().isEmpty(); 107 } 108 109 /** Returns current assignment */ 110 public T getAssignment() { 111 return iValue; 112 } 113 114 /** Returns true if the variable is assigned */ 115 public boolean hasAssignment() { 116 return iValue != null; 117 } 118 119 /** Returns initial assignment */ 120 public T getInitialAssignment() { 121 return iInitialValue; 122 } 123 124 /** Sets initial assignment */ 125 public void setInitialAssignment(T initialValue) { 126 iInitialValue = initialValue; 127 if (iInitialValue != null && iInitialValue.variable() == null) 128 iInitialValue.setVariable(this); 129 if (iModel != null) 130 iModel.invalidateVariablesWithInitialValueCache(); 131 } 132 133 /** Returns true if the variable has an initial assignment */ 134 public boolean hasInitialAssignment() { 135 return iInitialValue != null; 136 } 137 138 /** 139 * Assign value to this variable. If the variable has already assigned 140 * another value, it is unassigned first. Also, all conflicting values are 141 * unassigned before the given value is assigned to this variable. 142 * 143 * @param iteration 144 * current iteration 145 * @param value 146 * the value to be assigned 147 */ 148 public void assign(long iteration, T value) { 149 if (getModel() != null) 150 getModel().beforeAssigned(iteration, value); 151 iLastAssignmentIteration = iteration; 152 if (iValue != null) 153 unassign(iteration); 154 if (iRecentlyRemovedValue != null && iRecentlyRemovedValue.equals(value)) { 155 iRecentlyRemovedValue = null; 156 return; 157 } 158 if (value == null) 159 return; 160 iValue = value; 161 for (Constraint<?, T> constraint : iConstraints) { 162 constraint.assigned(iteration, value); 163 } 164 if (getModel() != null) 165 for (GlobalConstraint<?, T> constraint : getModel().globalConstraints()) { 166 constraint.assigned(iteration, value); 167 } 168 iAssignmentCounter++; 169 value.assigned(iteration); 170 if (iVariableListeners != null) 171 for (VariableListener<T> listener : iVariableListeners) { 172 listener.variableAssigned(iteration, value); 173 } 174 if (getModel() != null) 175 getModel().afterAssigned(iteration, value); 176 } 177 178 /** 179 * Unassign value from this variable. 180 * 181 * @param iteration 182 * current iteration 183 */ 184 public void unassign(long iteration) { 185 if (iValue == null) 186 return; 187 if (getModel() != null) 188 getModel().beforeUnassigned(iteration, iValue); 189 iLastUnassignmentIteration = iteration; 190 T oldValue = iValue; 191 iValue = null; 192 for (Constraint<?, T> constraint : iConstraints) { 193 constraint.unassigned(iteration, oldValue); 194 } 195 if (getModel() != null) 196 for (GlobalConstraint<?, T> constraint : getModel().globalConstraints()) { 197 constraint.unassigned(iteration, oldValue); 198 } 199 oldValue.unassigned(iteration); 200 if (iVariableListeners != null) 201 for (VariableListener<T> listener : iVariableListeners) 202 listener.variableUnassigned(iteration, oldValue); 203 if (getModel() != null) 204 getModel().afterUnassigned(iteration, oldValue); 205 } 206 207 /** Return how many times was this variable assigned in the past. */ 208 public long countAssignments() { 209 return iAssignmentCounter; 210 } 211 212 /** 213 * Adds a constraint. Called automatically when the constraint is added to 214 * the model, i.e., {@link Model#addConstraint(Constraint)} is called. 215 * 216 * @param constraint 217 * added constraint 218 */ 219 public void addContstraint(Constraint<V, T> constraint) { 220 iConstraints.add(constraint); 221 if (constraint.isHard()) { 222 iHardConstraints.add(constraint); 223 iConstraintVariables = null; 224 } else 225 iSoftConstraints.add(constraint); 226 } 227 228 /** 229 * Removes a constraint. Called automatically when the constraint is removed 230 * from the model, i.e., {@link Model#removeConstraint(Constraint)} is 231 * called. 232 * 233 * @param constraint 234 * added constraint 235 */ 236 public void removeContstraint(Constraint<V, T> constraint) { 237 iConstraints.remove(constraint); 238 if (iHardConstraints.contains(constraint)) { 239 iHardConstraints.remove(constraint); 240 iConstraintVariables = null; 241 } else 242 iSoftConstraints.remove(constraint); 243 } 244 245 /** Return the list of constraints associated with this variable */ 246 public List<Constraint<V, T>> constraints() { 247 return iConstraints; 248 } 249 250 /** Return the list of hard constraints associated with this variable */ 251 public List<Constraint<V, T>> hardConstraints() { 252 return iHardConstraints; 253 } 254 255 /** Return the list of soft constraints associated with this variable */ 256 public List<Constraint<V, T>> softConstraints() { 257 return iSoftConstraints; 258 } 259 260 @Override 261 public String toString() { 262 return "Variable{name=" + getName() + ", initial=" + getInitialAssignment() + ", current=" + getAssignment() 263 + ", values=" + values().size() + ", constraints=" + iConstraints.size() + "}"; 264 } 265 266 /** Unique id */ 267 public long getId() { 268 return iId; 269 } 270 271 @Override 272 public int hashCode() { 273 return (int) iId; 274 } 275 276 /** Variable's name -- for printing purposes */ 277 public String getName() { 278 return String.valueOf(iId); 279 } 280 281 /** Variable's description -- for printing purposes */ 282 public String getDescription() { 283 return null; 284 } 285 286 /** 287 * Sets variable's value of the best ever found solution. Called when 288 * {@link Model#saveBest()} is called. 289 */ 290 public void setBestAssignment(T value) { 291 iBestValue = value; 292 iBestAssignmentIteration = (value == null ? 0l : value.lastAssignmentIteration()); 293 } 294 295 /** Returns the value from the best ever found soultion. */ 296 public T getBestAssignment() { 297 return iBestValue; 298 } 299 300 /** Returns the iteration when the best value was assigned */ 301 public long getBestAssignmentIteration() { 302 return iBestAssignmentIteration; 303 } 304 305 /** 306 * Returns the iteration when the variable was assigned for the last time 307 * (-1 if never) 308 */ 309 public long lastAssignmentIteration() { 310 return iLastAssignmentIteration; 311 } 312 313 /** 314 * Returns the iteration when the variable was unassigned for the last time 315 * (-1 if never) 316 */ 317 public long lastUnassignmentIteration() { 318 return iLastUnassignmentIteration; 319 } 320 321 @Override 322 public int compareTo(V variable) { 323 if (variable == null) 324 return -1; 325 int cmp = getName().compareTo(variable.getName()); 326 if (cmp != 0) 327 return cmp; 328 return Double.compare(getId(), variable.getId()); 329 } 330 331 @Override 332 public boolean equals(Object o) { 333 if (o == null || !(o instanceof Variable<?, ?>)) 334 return false; 335 return getId() == ((Variable<?, ?>) o).getId(); 336 } 337 338 /** Adds variable listener */ 339 public void addVariableListener(VariableListener<T> listener) { 340 if (iVariableListeners == null) 341 iVariableListeners = new ArrayList<VariableListener<T>>(); 342 iVariableListeners.add(listener); 343 } 344 345 /** Removes variable listener */ 346 public void removeVariableListener(VariableListener<T> listener) { 347 if (iVariableListeners != null) 348 iVariableListeners.remove(listener); 349 } 350 351 /** Return variable listeners */ 352 public List<VariableListener<T>> getVariableListeners() { 353 return iVariableListeners; 354 } 355 356 /** 357 * Extra information to which can be used by an extension (see 358 * {@link net.sf.cpsolver.ifs.extension.Extension}). 359 */ 360 public void setExtra(Object object) { 361 iExtra = object; 362 } 363 364 /** 365 * Extra information to which can be used by an extension (see 366 * {@link net.sf.cpsolver.ifs.extension.Extension}). 367 */ 368 public Object getExtra() { 369 return iExtra; 370 } 371 372 /** Permanently remove a value from variables domain. */ 373 public void removeValue(long iteration, T value) { 374 if (iValue != null && iValue.equals(value)) 375 unassign(iteration); 376 if (iValues == null) 377 return; 378 iValues.remove(value); 379 if (iInitialValue != null && iInitialValue.equals(value)) { 380 iInitialValue = null; 381 if (iModel != null) 382 iModel.invalidateVariablesWithInitialValueCache(); 383 } 384 if (iVariableListeners != null) 385 for (VariableListener<T> listener : iVariableListeners) 386 listener.valueRemoved(iteration, value); 387 iRecentlyRemovedValue = value; 388 } 389 390 /** 391 * Returns a table of all variables linked with this variable by a 392 * constraint. 393 * 394 * @return table (variable, constraint) 395 */ 396 public Map<V, List<Constraint<V, T>>> constraintVariables() { 397 if (iConstraintVariables == null) { 398 iConstraintVariables = new HashMap<V, List<Constraint<V, T>>>(); 399 for (Constraint<V, T> constraint : constraints()) { 400 for (V variable : constraint.variables()) { 401 if (!variable.equals(this)) { 402 List<Constraint<V, T>> constraints = iConstraintVariables.get(variable); 403 if (constraints == null) { 404 constraints = new ArrayList<Constraint<V, T>>(); 405 iConstraintVariables.put(variable, constraints); 406 } 407 constraints.add(constraint); 408 } 409 } 410 } 411 } 412 return iConstraintVariables; 413 } 414 415 /** 416 * Permanently remove the initial value from the variable's domain -- for 417 * testing MPP 418 */ 419 public void removeInitialValue() { 420 if (iInitialValue == null) 421 return; 422 if (iValues == null) 423 return; 424 if (getAssignment() != null && getAssignment().equals(iInitialValue)) 425 unassign(0); 426 iValues.remove(iInitialValue); 427 if (iModel != null) 428 iModel.invalidateVariablesWithInitialValueCache(); 429 iInitialValue = null; 430 } 431}