001 package net.sf.cpsolver.ifs.model; 002 003 import java.util.*; 004 005 import net.sf.cpsolver.ifs.util.*; 006 007 /** 008 * Generic variable. 009 * <br><br> 010 * Besides a domain of values, a variable also contains information about assigned value, 011 * the value assigned in the best ever found solution and also the initial value (minimal 012 * perturbations problem). It also knows what constraints are associated with this variable and 013 * has a unique id. 014 * 015 * @see Value 016 * @see Model 017 * @see net.sf.cpsolver.ifs.solver.Solver 018 * 019 * @version 020 * IFS 1.1 (Iterative Forward Search)<br> 021 * Copyright (C) 2006 Tomáš Müller<br> 022 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 023 * Lazenska 391, 76314 Zlin, Czech Republic<br> 024 * <br> 025 * This library is free software; you can redistribute it and/or 026 * modify it under the terms of the GNU Lesser General Public 027 * License as published by the Free Software Foundation; either 028 * version 2.1 of the License, or (at your option) any later version. 029 * <br><br> 030 * This library is distributed in the hope that it will be useful, 031 * but WITHOUT ANY WARRANTY; without even the implied warranty of 032 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 033 * Lesser General Public License for more details. 034 * <br><br> 035 * You should have received a copy of the GNU Lesser General Public 036 * License along with this library; if not, write to the Free Software 037 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 038 */ 039 public class Variable implements Comparable { 040 private static IdGenerator iIdGenerator = new IdGenerator(); 041 042 protected long iId = -1; 043 private Model iModel = null; 044 045 private Value iInitialValue = null; // initial value 046 /** Assigned value */ 047 protected Value iValue = null; // assigned value 048 private Value iBestValue = null; // best value 049 private long iBestAssignmentIteration = 0; 050 private Vector iValues = null; 051 052 private Value iRecentlyRemovedValue = null; 053 054 private long iAssignmentCounter = 0; 055 private long iLastAssignmentIteration = -1; 056 private long iLastUnassignmentIteration = -1; 057 private Object iExtra = null; 058 059 private Vector iConstraints = new FastVector(); 060 private Vector iHardConstraints = new FastVector(); 061 private Vector iSoftConstraints = new FastVector(); 062 private Vector iVariableListeners = null; 063 064 private Hashtable iConstraintVariables = null; 065 066 /** Constructor */ 067 public Variable() { 068 this(null); 069 } 070 071 /** Constructor 072 * @param initialValue initial value (minimal-perturbation problem) 073 */ 074 public Variable(Value initialValue) { 075 iId = iIdGenerator.newId(); 076 setInitialAssignment(initialValue); 077 } 078 079 /** Model, the variable belong to */ 080 public Model getModel() { return iModel; } 081 /** Set the model to which the variable belongs to */ 082 public void setModel(Model model) { iModel = model; } 083 084 /** Domain */ 085 public Vector values() { 086 return iValues; 087 } 088 /** Sets the domain */ 089 protected void setValues(Vector values) { 090 iValues = values; 091 } 092 /** True, if the variable's domain is not empty */ 093 public boolean hasValues() { 094 return !values().isEmpty(); 095 } 096 097 /** Returns current assignment */ 098 public Value getAssignment() { return iValue; } 099 /** Returns true if the variable is assigned */ 100 public boolean hasAssignment() { return iValue!=null; } 101 /** Returns initial assignment */ 102 public Value getInitialAssignment() { return iInitialValue; } 103 /** Sets initial assignment */ 104 public void setInitialAssignment(Value initialValue) { 105 iInitialValue = initialValue; 106 if (iInitialValue!=null && iInitialValue.variable()==null) iInitialValue.setVariable(this); 107 if (iModel!=null) 108 iModel.invalidateVariablesWithInitialValueCache(); 109 } 110 /** Returns true if the variable has an initial assignment */ 111 public boolean hasInitialAssignment() { return iInitialValue!=null; } 112 113 /** Assign value to this variable. If the variable has already assigned another value, it is 114 * unassigned first. Also, all conflicting values are unassigned before the given value is assigned 115 * to this variable. 116 * @param iteration current iteration 117 * @param value the value to be assigned 118 */ 119 public void assign(long iteration, Value value) { 120 if (getModel()!=null) 121 getModel().beforeAssigned(iteration,value); 122 iLastAssignmentIteration = iteration; 123 if (iValue!=null) unassign(iteration); 124 if (iRecentlyRemovedValue!=null && iRecentlyRemovedValue.equals(value)) { 125 iRecentlyRemovedValue = null; 126 return; 127 } 128 if (value==null) return; 129 iValue = value; 130 for (Enumeration e=iConstraints.elements(); e.hasMoreElements();) { 131 Constraint constraint = (Constraint)e.nextElement(); 132 constraint.assigned(iteration, value); 133 } 134 if (getModel()!=null) 135 for (Enumeration e=getModel().globalConstraints().elements(); e.hasMoreElements();) { 136 Constraint constraint = (Constraint)e.nextElement(); 137 constraint.assigned(iteration, value); 138 } 139 iAssignmentCounter++; 140 value.assigned(iteration); 141 if (iVariableListeners!=null) 142 for (Enumeration e=iVariableListeners.elements(); e.hasMoreElements();) 143 ((VariableListener)e.nextElement()).variableAssigned(iteration, value); 144 if (getModel()!=null) 145 getModel().afterAssigned(iteration,value); 146 } 147 148 /** Unassign value from this variable. 149 * @param iteration current iteration 150 */ 151 public void unassign(long iteration) { 152 if (iValue==null) return; 153 if (getModel()!=null) 154 getModel().beforeUnassigned(iteration,iValue); 155 iLastUnassignmentIteration = iteration; 156 Value oldValue = iValue; 157 iValue = null; 158 for (Enumeration e=iConstraints.elements(); e.hasMoreElements();) { 159 Constraint constraint = (Constraint)e.nextElement(); 160 constraint.unassigned(iteration, oldValue); 161 } 162 if (getModel()!=null) 163 for (Enumeration e=getModel().globalConstraints().elements(); e.hasMoreElements();) { 164 Constraint constraint = (Constraint)e.nextElement(); 165 constraint.unassigned(iteration, oldValue); 166 } 167 oldValue.unassigned(iteration); 168 if (iVariableListeners!=null) 169 for (Enumeration e=iVariableListeners.elements(); e.hasMoreElements();) 170 ((VariableListener)e.nextElement()).variableUnassigned(iteration, oldValue); 171 if (getModel()!=null) 172 getModel().afterUnassigned(iteration,oldValue); 173 } 174 /** Return how many times was this variable assigned in the past. */ 175 public long countAssignments() { return iAssignmentCounter; } 176 177 /** Adds a constraint. Called automatically when the constraint is added to the model, i.e., 178 * {@link Model#addConstraint(Constraint)} is called. 179 * @param constraint added constraint 180 */ 181 public void addContstraint(Constraint constraint) { 182 iConstraints.addElement(constraint); 183 if (constraint.isHard()) { 184 iHardConstraints.addElement(constraint); 185 iConstraintVariables = null; 186 } else iSoftConstraints.addElement(constraint); 187 } 188 189 /** Removes a constraint. Called automatically when the constraint is removed from the model, i.e., 190 * {@link Model#removeConstraint(Constraint)} is called. 191 * @param constraint added constraint 192 */ 193 public void removeContstraint(Constraint constraint) { 194 iConstraints.removeElement(constraint); 195 if (iHardConstraints.contains(constraint)) { 196 iHardConstraints.removeElement(constraint); 197 iConstraintVariables = null; 198 } else iSoftConstraints.removeElement(constraint); 199 } 200 201 /** Return the list of constraints associated with this variable */ 202 public Vector constraints() { return iConstraints; } 203 /** Return the list of hard constraints associated with this variable */ 204 public Vector hardConstraints() { return iHardConstraints; } 205 /** Return the list of soft constraints associated with this variable */ 206 public Vector softConstraints() { return iSoftConstraints; } 207 208 public String toString() { 209 return "Variable{name="+getName()+", initial="+getInitialAssignment()+", current="+getAssignment()+", values="+values().size()+", constraints="+iConstraints.size()+"}"; 210 } 211 212 /** Unique id */ 213 public long getId() { return iId;} 214 215 public int hashCode() { return (int)iId; } 216 /** Variable's name -- for printing purposes */ 217 public String getName() { return String.valueOf(iId); } 218 /** Variable's description -- for printing purposes */ 219 public String getDescription() { return null; } 220 221 /** Sets variable's value of the best ever found solution. Called when {@link Model#saveBest()} is called. */ 222 public void setBestAssignment(Value value) { iBestValue = value; iBestAssignmentIteration = (value==null?0l:value.lastAssignmentIteration()); } 223 /** Returns the value from the best ever found soultion. */ 224 public Value getBestAssignment() { return iBestValue; } 225 /** Returns the iteration when the best value was assigned */ 226 public long getBestAssignmentIteration() { return iBestAssignmentIteration; } 227 228 /** Returns the iteration when the variable was assigned for the last time (-1 if never) */ 229 public long lastAssignmentIteration() { return iLastAssignmentIteration; } 230 /** Returns the iteration when the variable was unassigned for the last time (-1 if never) */ 231 public long lastUnassignmentIteration() { return iLastUnassignmentIteration; } 232 233 public int compareTo(Object o) { 234 if (o==null || !(o instanceof Variable)) return -1; 235 Variable v = (Variable)o; 236 return getName().compareTo(v.getName()); 237 } 238 239 public boolean equals(Object o) { 240 try { 241 if (o==null) return false; 242 Variable v = (Variable)o; 243 return getId()==v.getId(); 244 } catch (Exception e) { 245 return false; 246 } 247 } 248 249 /** Adds variable listener */ 250 public void addVariableListener(VariableListener listener) { 251 if (iVariableListeners==null) iVariableListeners=new FastVector(); 252 iVariableListeners.addElement(listener); 253 } 254 /** Removes variable listener */ 255 public void removeVariableListener(VariableListener listener) { 256 if (iVariableListeners==null) iVariableListeners=new FastVector(); 257 iVariableListeners.removeElement(listener); 258 } 259 /** Return variable listeners */ 260 public Vector getVariableListeners() { return iVariableListeners; } 261 262 /** Extra information to which can be used by an extension (see {@link net.sf.cpsolver.ifs.extension.Extension}). */ 263 public void setExtra(Object object) { iExtra = object; } 264 /** Extra information to which can be used by an extension (see {@link net.sf.cpsolver.ifs.extension.Extension}). */ 265 public Object getExtra() { return iExtra; } 266 267 /** Permanently remove a value from variables domain. */ 268 public void removeValue(long iteration, Value value) { 269 if (iValue!=null && iValue.equals(value)) unassign(iteration); 270 if (iValues==null) return; 271 iValues.remove(value); 272 if (iInitialValue!=null && iInitialValue.equals(value)) { 273 iInitialValue=null; 274 if (iModel!=null) iModel.invalidateVariablesWithInitialValueCache(); 275 } 276 if (iVariableListeners!=null) 277 for (Enumeration e=iVariableListeners.elements(); e.hasMoreElements();) 278 ((VariableListener)e.nextElement()).valueRemoved(iteration, value); 279 iRecentlyRemovedValue = value; 280 } 281 282 /** Returns a table of all variables linked with this variable by a constraint. 283 * @return table (variable, constraint) 284 */ 285 public Hashtable constraintVariables() { 286 if (iConstraintVariables == null) { 287 iConstraintVariables = new Hashtable(); 288 for (Enumeration e1=constraints().elements();e1.hasMoreElements();) { 289 Constraint constraint = (Constraint)e1.nextElement(); 290 for (Enumeration e2=constraint.variables().elements();e2.hasMoreElements();) { 291 Variable variable = (Variable)e2.nextElement(); 292 if (!variable.equals(this)) { 293 Vector constraints = (Vector)iConstraintVariables.get(variable); 294 if (constraints==null) { 295 constraints = new FastVector(1,10); 296 iConstraintVariables.put(variable, constraints); 297 } 298 constraints.add(constraint); 299 } 300 } 301 } 302 } 303 return iConstraintVariables; 304 } 305 306 /** Permanently remove the initial value from the variable's domain -- for testing MPP */ 307 public void removeInitialValue() { 308 if (iInitialValue==null) return; 309 if (iValues==null) return; 310 if (getAssignment()!=null && getAssignment().equals(iInitialValue)) unassign(0); 311 iValues.remove(iInitialValue); 312 if (iModel!=null) 313 iModel.invalidateVariablesWithInitialValueCache(); 314 iInitialValue=null; 315 } 316 }