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    }