001 package ifs.model;
002
003 import ifs.util.*;
004 import java.util.*;
005
006 /**
007 * Generic variable.
008 * <br><br>
009 * Besides a domain of values, a variable also contains information about assigned value,
010 * the value assigned in the best ever found solution and also the initial value (minimal
011 * perturbations problem). It also knows what constraints are associated with this variable and
012 * has a unique id.
013 *
014 * @see Value
015 * @see Model
016 * @see ifs.solver.Solver
017 *
018 * @author <a href="mailto:muller@ktiml.mff.cuni.cz">Tomáš Müller</a>
019 * @version 1.0
020 */
021 public class Variable implements Comparable {
022 private static org.apache.log4j.Logger sLogger = org.apache.log4j.Logger.getLogger(Variable.class);
023 private static IdGenerator iIdGenerator = new IdGenerator();
024
025 private long iId = -1;
026 private Model iModel = null;
027
028 private Value iInitialValue = null; // initial value
029 /** Assigned value */
030 protected Value iValue = null; // assigned value
031 private Value iBestValue = null; // best value
032 private long iBestAssignmentIteration = 0;
033 private Vector iValues = null;
034
035 private Value iRecentlyRemovedValue = null;
036
037 private long iAssignmentCounter = 0;
038 private long iLastAssignmentIteration = -1;
039 private long iLastUnassignmentIteration = -1;
040 private Object iExtra = null;
041
042 private Vector iConstraints = new FastVector();
043 private Vector iHardConstraints = new FastVector();
044 private Vector iSoftConstraints = new FastVector();
045 private Vector iVariableListeners = null;
046
047 private Hashtable iConstraintVariables = null;
048
049 /** Constructor */
050 public Variable() {
051 this(null);
052 }
053
054 /** Constructor
055 * @param initialValue initial value (minimal-perturbation problem)
056 */
057 public Variable(Value initialValue) {
058 iId = iIdGenerator.newId();
059 setInitialAssignment(initialValue);
060 }
061
062 /** Model, the variable belong to */
063 public Model getModel() { return iModel; }
064 /** Set the model to which the variable belongs to */
065 public void setModel(Model model) { iModel = model; }
066
067 /** Domain */
068 public Vector values() {
069 return iValues;
070 }
071 /** Sets the domain */
072 protected void setValues(Vector values) {
073 iValues = values;
074 }
075
076 /** Returns current assignment */
077 public Value getAssignment() { return iValue; }
078 /** Returns true if the variable is assigned */
079 public boolean hasAssignment() { return iValue!=null; }
080 /** Returns initial assignment */
081 public Value getInitialAssignment() { return iInitialValue; }
082 /** Sets initial assignment */
083 public void setInitialAssignment(Value initialValue) {
084 iInitialValue = initialValue;
085 if (iInitialValue!=null && iInitialValue.variable()==null) iInitialValue.setVariable(this);
086 }
087 /** Returns true if the variable has an initial assignment */
088 public boolean hasInitialAssignment() { return iInitialValue!=null; }
089
090 /** Assign value to this variable. If the variable has already assigned another value, it is
091 * unassigned first. Also, all conflicting values are unassigned before the given value is assigned
092 * to this variable.
093 * @param iteration current iteration
094 * @param value the value to be assigned
095 */
096 public void assign(long iteration, Value value) {
097 getModel().beforeAssigned(iteration,value);
098 iLastAssignmentIteration = iteration;
099 if (iValue!=null) unassign(iteration);
100 if (iRecentlyRemovedValue!=null && iRecentlyRemovedValue.equals(value)) {
101 iRecentlyRemovedValue = null;
102 return;
103 }
104 if (value==null) return;
105 iValue = value;
106 for (Enumeration e=iConstraints.elements(); e.hasMoreElements();) {
107 Constraint constraint = (Constraint)e.nextElement();
108 constraint.assigned(iteration, value);
109 }
110 iAssignmentCounter++;
111 value.assigned(iteration);
112 if (iVariableListeners!=null)
113 for (Enumeration e=iVariableListeners.elements(); e.hasMoreElements();)
114 ((VariableListener)e.nextElement()).variableAssigned(iteration, value);
115 getModel().afterAssigned(iteration,value);
116 }
117
118 /** Unassign value from this variable.
119 * @param iteration current iteration
120 */
121 public void unassign(long iteration) {
122 if (iValue==null) return;
123 getModel().beforeUnassigned(iteration,iValue);
124 iLastUnassignmentIteration = iteration;
125 Value oldValue = iValue;
126 iValue = null;
127 for (Enumeration e=iConstraints.elements(); e.hasMoreElements();) {
128 Constraint constraint = (Constraint)e.nextElement();
129 constraint.unassigned(iteration, oldValue);
130 }
131 oldValue.unassigned(iteration);
132 if (iVariableListeners!=null)
133 for (Enumeration e=iVariableListeners.elements(); e.hasMoreElements();)
134 ((VariableListener)e.nextElement()).variableUnassigned(iteration, oldValue);
135 getModel().afterUnassigned(iteration,oldValue);
136 }
137 /** Return how many times was this variable assigned in the past. */
138 public long countAssignments() { return iAssignmentCounter; }
139
140 /** Adds a constraint. Called automatically when the constraint is added to the model, i.e.,
141 * {@link Model#addConstraint(Constraint)} is called.
142 * @param constraint added constraint
143 */
144 public void addContstraint(Constraint constraint) {
145 iConstraints.addElement(constraint);
146 if (constraint.isHard()) {
147 iHardConstraints.addElement(constraint);
148 iConstraintVariables = null;
149 } else iSoftConstraints.addElement(constraint);
150 }
151
152 /** Removes a constraint. Called automatically when the constraint is removed from the model, i.e.,
153 * {@link Model#removeConstraint(Constraint)} is called.
154 * @param constraint added constraint
155 */
156 public void removeContstraint(Constraint constraint) {
157 iConstraints.removeElement(constraint);
158 if (iHardConstraints.contains(constraint)) {
159 iHardConstraints.removeElement(constraint);
160 iConstraintVariables = null;
161 } else iSoftConstraints.removeElement(constraint);
162 }
163
164 /** Return the list of constraints associated with this variable */
165 public Vector constraints() { return iConstraints; }
166 /** Return the list of hard constraints associated with this variable */
167 public Vector hardConstraints() { return iHardConstraints; }
168 /** Return the list of soft constraints associated with this variable */
169 public Vector softConstraints() { return iSoftConstraints; }
170
171 public String toString() {
172 return "Variable{name="+getName()+", initial="+getInitialAssignment()+", current="+getAssignment()+", values="+values().size()+", constraints="+iConstraints.size()+"}";
173 }
174
175 /** Unique id */
176 public long getId() { return iId;}
177
178 public int hashCode() { return (int)iId; }
179 /** Variable's name -- for printing purposes */
180 public String getName() { return String.valueOf(iId); }
181 /** Variable's description -- for printing purposes */
182 public String getDescription() { return null; }
183
184 /** Sets variable's value of the best ever found solution. Called when {@link Model#saveBest()} is called. */
185 public void setBestAssignment(Value value) { iBestValue = value; iBestAssignmentIteration = (value==null?0l:value.lastAssignmentIteration()); }
186 /** Returns the value from the best ever found soultion. */
187 public Value getBestAssignment() { return iBestValue; }
188 /** Returns the iteration when the best value was assigned */
189 public long getBestAssignmentIteration() { return iBestAssignmentIteration; }
190
191 /** Returns the iteration when the variable was assigned for the last time (-1 if never) */
192 public long lastAssignmentIteration() { return iLastAssignmentIteration; }
193 /** Returns the iteration when the variable was unassigned for the last time (-1 if never) */
194 public long lastUnassignmentIteration() { return iLastUnassignmentIteration; }
195
196 public int compareTo(Object o) {
197 if (o==null || !(o instanceof Variable)) return -1;
198 Variable v = (Variable)o;
199 return getName().compareTo(v.getName());
200 }
201
202 public boolean equals(Object o) {
203 try {
204 Variable v = (Variable)o;
205 return getId()==v.getId();
206 } catch (Exception e) {
207 return false;
208 }
209 }
210
211 /** Adds variable listener */
212 public void addVariableListener(VariableListener listener) {
213 if (iVariableListeners==null) iVariableListeners=new FastVector();
214 iVariableListeners.addElement(listener);
215 }
216 /** Removess variable listener */
217 public void removeVariableListener(VariableListener listener) {
218 if (iVariableListeners==null) iVariableListeners=new FastVector();
219 iVariableListeners.removeElement(listener);
220 }
221
222 /** Extra information to which can be used by an extension (see {@link ifs.extension.Extension}). */
223 public void setExtra(Object object) { iExtra = object; }
224 /** Extra information to which can be used by an extension (see {@link ifs.extension.Extension}). */
225 public Object getExtra() { return iExtra; }
226
227 /** Permanently remove a value from variables domain. */
228 public void removeValue(long iteration, Value value) {
229 if (iValue!=null && iValue.equals(value)) unassign(iteration);
230 if (iValues==null) return;
231 iValues.remove(value);
232 if (iInitialValue!=null && iInitialValue.equals(value)) iInitialValue=null;
233 if (iVariableListeners!=null)
234 for (Enumeration e=iVariableListeners.elements(); e.hasMoreElements();)
235 ((VariableListener)e.nextElement()).valueRemoved(iteration, value);
236 iRecentlyRemovedValue = value;
237 }
238
239 /** Returns a table of all variables linked with this variable by a constraint.
240 * @return table (variable, constraint)
241 */
242 public Hashtable constraintVariables() {
243 if (iConstraintVariables == null) {
244 iConstraintVariables = new Hashtable();
245 for (Enumeration e1=constraints().elements();e1.hasMoreElements();) {
246 Constraint constraint = (Constraint)e1.nextElement();
247 for (Enumeration e2=constraint.variables().elements();e2.hasMoreElements();) {
248 Variable variable = (Variable)e2.nextElement();
249 if (!variable.equals(this)) {
250 Vector constraints = (Vector)iConstraintVariables.get(variable);
251 if (constraints==null) {
252 constraints = new FastVector(1,10);
253 iConstraintVariables.put(variable, constraints);
254 }
255 constraints.add(constraint);
256 }
257 }
258 }
259 }
260 return iConstraintVariables;
261 }
262
263 /** Permanently remove the initial value from the variable's domain -- for testing MPP */
264 public void removeInitialValue() {
265 if (iInitialValue==null) return;
266 if (iValues==null) return;
267 if (getAssignment()!=null && getAssignment().equals(iInitialValue)) unassign(0);
268 iValues.remove(iInitialValue);
269 iInitialValue=null;
270 }
271 }