001 package ifs.model;
002
003 import ifs.util.*;
004 import java.util.*;
005 import ifs.solver.*;
006
007 /**
008 * Generic model (definition of a problem).
009 * <br><br>
010 * It consists of variables and constraints. It has also capability of memorizing the current
011 * and the best ever found assignment.
012 * <br><br>
013 * Example usage:<br><ul><code>
014 * MyModel model = new MyModel();<br>
015 * Variable a = new MyVariable("A");<br>
016 * model.addVariable(a);<br>
017 * Variable b = new MyVariable("B");<br>
018 * model.addVariable(b);<br>
019 * Variable c = new MyVariable("C");<br>
020 * model.addVariable(c);<br>
021 * Constraint constr = MyConstraint("all-different");<br>
022 * model.addConstraint(constr);<br>
023 * constr.addVariable(a);<br>
024 * constr.addVariable(b);<br>
025 * constr.addVariable(c);<br>
026 * solver.setInitialSolution(model);
027 * </code></ul>
028 *
029 * @see Variable
030 * @see Constraint
031 * @see ifs.solution.Solution
032 * @see ifs.solver.Solver
033 *
034 * @author <a href="mailto:muller@ktiml.mff.cuni.cz">Tomáš Müller</a>
035 * @version 1.0
036 */
037
038 public class Model {
039 private static org.apache.log4j.Logger sLogger = org.apache.log4j.Logger.getLogger(Model.class);
040 private static java.text.DecimalFormat sTimeFormat = new java.text.DecimalFormat("0.00",new java.text.DecimalFormatSymbols(Locale.US));
041
042 private Vector iVariables = new FastVector();
043 private Vector iConstraints = new FastVector();
044 private Vector iUnassignedVariables = new FastVector();
045 private Vector iAssignedVariables = new FastVector();
046
047 private Vector iPerturbVariables = null;
048 private Vector iConflictVariables = null;
049 private Vector iVariablesWithoutInitialValue = null;
050
051 private int iBestUnassignedVariables = -1;
052 private int iBestPerturbations = 0;
053
054 /** Constructor */
055 public Model() {
056 }
057
058 /** The list of variables in the model */
059 public Vector variables() { return iVariables; }
060 /** The number of variables in the model */
061 public int countVariables() { return iVariables.size(); }
062 /** Adds a variable to the model */
063 public void addVariable(Variable variable) {
064 variable.setModel(this);
065 iVariables.addElement(variable);
066 if (variable.getAssignment()==null) iUnassignedVariables.addElement(variable);
067 else iAssignedVariables.addElement(variable);
068 if (variable.getAssignment()!=null) variable.assign(0L,variable.getAssignment());
069 for (Enumeration e=iModelListeners.elements();e.hasMoreElements();)
070 ((ModelListener)e.nextElement()).variableAdded(variable);
071 }
072 /** Removes a variable from the model */
073 public void removeVariable(Variable variable) {
074 variable.setModel(null);
075 iVariables.removeElement(variable);
076 if (iUnassignedVariables.contains(variable)) iUnassignedVariables.removeElement(variable);
077 if (iAssignedVariables.contains(variable)) iAssignedVariables.removeElement(variable);
078 for (Enumeration e=iModelListeners.elements();e.hasMoreElements();)
079 ((ModelListener)e.nextElement()).variableRemoved(variable);
080 }
081
082 /** The list of constraints in the model */
083 public Vector constraints() { return iConstraints; }
084 /** The number of constraints in the model */
085 public int countConstraints() { return iConstraints.size(); }
086 /** Adds a constraint to the model */
087 public void addConstraint(Constraint constraint) {
088 constraint.setModel(this);
089 iConstraints.addElement(constraint);
090 for (Enumeration e=iModelListeners.elements();e.hasMoreElements();)
091 ((ModelListener)e.nextElement()).constraintAdded(constraint);
092 }
093 /** Removes a constraint from the model */
094 public void removeConstraint(Constraint constraint) {
095 constraint.setModel(null);
096 iConstraints.removeElement(constraint);
097 for (Enumeration e=iModelListeners.elements();e.hasMoreElements();)
098 ((ModelListener)e.nextElement()).constraintRemoved(constraint);
099 }
100 /** The list of unassigned variables in the model */
101 public Vector unassignedVariables() { return iUnassignedVariables; }
102 /** The list of assigned variables in the model */
103 public Vector assignedVariables() { return iAssignedVariables; }
104 /** The list of perturbation variables in the model, i.e., the variables which has an initial value but which are not
105 * assigned with this value.
106 */
107 public Vector perturbVariables() {
108 if (iPerturbVariables!=null) return iPerturbVariables;
109 Vector perturbances = new FastVector();
110 for (Enumeration e=variables().elements();e.hasMoreElements();) {
111 Variable variable = (Variable)e.nextElement();
112 if (variable.getInitialAssignment()!=null) {
113 if (variable.getAssignment()!=null) {
114 if (!variable.getInitialAssignment().equals(variable.getAssignment())) perturbances.addElement(variable);
115 } else {
116 boolean hasPerturbance = false;
117 for (Enumeration x=variable.hardConstraints().elements();!hasPerturbance && x.hasMoreElements();) {
118 Constraint constraint = (Constraint)x.nextElement();
119 if (constraint.variables().contains(variable) && constraint.inConflict(variable.getInitialAssignment()))
120 hasPerturbance=true;
121 }
122 if (hasPerturbance) perturbances.addElement(variable);
123 }
124 }
125 }
126 iPerturbVariables = perturbances;
127 return perturbances;
128 }
129
130 /** Returns the set of confliction variables with this value, if it is assigned to its variable */
131 public Set conflictValues(Value value) {
132 HashSet conflictValues = new HashSet();
133 for (Enumeration c=value.variable().hardConstraints().elements(); c.hasMoreElements();)
134 ((Constraint)c.nextElement()).computeConflicts(value, conflictValues);
135 return conflictValues;
136 }
137
138 /** The list of variales without initial value */
139 public Vector variablesWithoutInitialValue() {
140 if (iVariablesWithoutInitialValue!=null) return iVariablesWithoutInitialValue;
141 Vector variables = new FastVector();
142 for (Enumeration e=variables().elements();e.hasMoreElements();) {
143 Variable variable = (Variable)e.nextElement();
144 if (variable.getInitialAssignment()==null) variables.addElement(variable);
145 }
146 iVariablesWithoutInitialValue=variables;
147 return variables;
148 }
149
150 /** Called before a value is assigned to its variable */
151 public void beforeAssigned(long iteration, Value value) {
152 for (Enumeration e=iModelListeners.elements();e.hasMoreElements();)
153 ((ModelListener)e.nextElement()).beforeAssigned(iteration, value);
154 }
155
156 /** Called before a value is unassigned from its variable */
157 public void beforeUnassigned(long iteration, Value value) {
158 for (Enumeration e=iModelListeners.elements();e.hasMoreElements();)
159 ((ModelListener)e.nextElement()).beforeUnassigned(iteration, value);
160 }
161
162 /** Called after a value is assigned to its variable */
163 public void afterAssigned(long iteration, Value value) {
164 iUnassignedVariables.removeElement(value.variable());
165 iAssignedVariables.addElement(value.variable());
166 iPerturbVariables = null;
167 iConflictVariables = null;
168 for (Enumeration e=iModelListeners.elements();e.hasMoreElements();)
169 ((ModelListener)e.nextElement()).afterAssigned(iteration, value);
170 }
171
172 /** Called after a value is unassigned from its variable */
173 public void afterUnassigned(long iteration, Value value) {
174 iUnassignedVariables.addElement(value.variable());
175 iAssignedVariables.removeElement(value.variable());
176 iPerturbVariables = null;
177 iConflictVariables = null;
178 for (Enumeration e=iModelListeners.elements();e.hasMoreElements();)
179 ((ModelListener)e.nextElement()).afterUnassigned(iteration, value);
180 }
181
182 public String toString() {
183 Collections.sort(variables(), new Comparator() {
184 public int compare(Object o1, Object o2) {
185 Variable v1 = (Variable)o1;
186 Variable v2 = (Variable)o2;
187 return (v1!=null?v1.getName().compareTo(v2.getName()):(int)(v1.getId()-v2.getId()));
188 }
189 });
190 return "Model{\n variables="+ToolBox.col2string(variables(),2)+
191 ",\n constraints="+ToolBox.col2string(constraints(),2)+
192 ",\n #unassigned="+unassignedVariables().size()+
193 ",\n unassigned="+ToolBox.col2string(unassignedVariables(),2)+
194 ",\n #perturbations="+perturbVariables().size()+"+"+variablesWithoutInitialValue().size()+
195 ",\n perturbations="+ToolBox.col2string(perturbVariables(),2)+
196 ",\n info="+getInfo()+
197 (variablesWithoutInitialValue().size()<variables().size()?",\n withoutInitial="+ToolBox.col2string(variablesWithoutInitialValue(),2):"")+
198 "\n }";
199 }
200
201 /** Returns information about the current solution. Information from all model listeners and constraints is also included.
202 */
203 public java.util.Hashtable getInfo() {
204 java.util.Hashtable ret = new java.util.Hashtable();
205 ret.put("Unassigned variables", unassignedVariables().size()+" / "+variables().size());
206 ret.put("Perturbation variables (with + without initial value)", perturbVariables().size()+" + "+variablesWithoutInitialValue().size());
207 ret.put("Total value", new Integer(getTotalValue()));
208 for (Enumeration e=iModelListeners.elements();e.hasMoreElements();)
209 ((ModelListener)e.nextElement()).getInfo(ret);
210 for (Enumeration e=iConstraints.elements();e.hasMoreElements();)
211 ((Constraint)e.nextElement()).getInfo(ret);
212 return ret;
213 }
214
215 /** Returns the number of unassigned variables in the best ever found solution */
216 public int getBestUnassignedVariables() { return iBestUnassignedVariables; }
217 /** Returns the number of perturbation variables in the best ever found solution */
218 public int getBestPerturbations() { return iBestPerturbations; }
219 /** Save the current assignment as the best ever found assignment */
220 public void saveBest() {
221 iBestUnassignedVariables = unassignedVariables().size();
222 iBestPerturbations = perturbVariables().size();
223 for (Enumeration e=variables().elements();e.hasMoreElements();) {
224 Variable variable = (Variable)e.nextElement();
225 variable.setBestAssignment(variable.getAssignment());
226 }
227 }
228 /** Restore the best ever found assignment into the current assignment*/
229 public void restoreBest() {
230 for (Enumeration e=variables().elements();e.hasMoreElements();) {
231 Variable variable = (Variable)e.nextElement();
232 variable.unassign(0);
233 }
234 HashSet problems = new HashSet();
235 for (Enumeration e=ToolBox.sortEnumeration(variables().elements(), new BestAssignmentComparator());e.hasMoreElements();) {
236 Variable variable = (Variable)e.nextElement();
237 if (variable.getBestAssignment()!=null) {
238 Set confs = conflictValues(variable.getBestAssignment());
239 if (!confs.isEmpty()) {
240 sLogger.error("restore best problem: assignment "+variable.getName()+" = "+variable.getBestAssignment().getName());
241 for (Enumeration en=variable.hardConstraints().elements();en.hasMoreElements();) {
242 Constraint c=(Constraint)en.nextElement();
243 Set x = new HashSet();
244 c.computeConflicts(variable.getBestAssignment(),x);
245 if (!x.isEmpty()) {
246 sLogger.error(" constraint "+c.getName()+" causes the following conflicts "+x);
247 }
248 }
249 problems.add(variable.getBestAssignment());
250 } else variable.assign(0,variable.getBestAssignment());
251 }
252 }
253 int attempt = 0;
254 while (!problems.isEmpty() && attempt<=100) {
255 attempt++;
256 Value value = (Value)ToolBox.random(problems); problems.remove(value);
257 Variable variable = value.variable();
258 Set confs = conflictValues(value);
259 if (!confs.isEmpty()) {
260 sLogger.error("restore best problem (again, att="+attempt+"): assignment "+variable.getName()+" = "+variable.getBestAssignment().getName());
261 for (Enumeration en=variable.hardConstraints().elements();en.hasMoreElements();) {
262 Constraint c=(Constraint)en.nextElement();
263 Set x = new HashSet();
264 c.computeConflicts(value,x);
265 if (!x.isEmpty()) sLogger.error(" constraint "+c.getName()+" causes the following conflicts "+x);
266 }
267 problems.addAll(confs);
268 }
269 variable.assign(0,value);
270 }
271 }
272
273 /** The list of unassigned variables in the best ever found solution*/
274 public Vector bestUnassignedVariables() {
275 if (iBestUnassignedVariables<0) return unassignedVariables();
276 Vector ret = new FastVector(variables().size());
277 for (Enumeration e=variables().elements();e.hasMoreElements();) {
278 Variable variable = (Variable)e.nextElement();
279 if (variable.getBestAssignment()==null) ret.addElement(variable);
280 }
281 return ret;
282 }
283
284 /** Value of the current solution. It is the sum of all assigned values, i.e., {@link Value#toInt()}.*/
285 public int getTotalValue() {
286 int valCurrent = 0;
287 for (Enumeration e=assignedVariables().elements();e.hasMoreElements();)
288 valCurrent += ((Variable)e.nextElement()).getAssignment().toInt();
289 return valCurrent;
290 }
291
292 private Vector iModelListeners = new FastVector();
293 /** Adds a model listener */
294 public void addModelListener(ModelListener listener) {
295 iModelListeners.addElement(listener);
296 for (Enumeration e=iConstraints.elements();e.hasMoreElements();)
297 listener.constraintAdded((Constraint)e.nextElement());
298 for (Enumeration e=iVariables.elements();e.hasMoreElements();)
299 listener.variableAdded((Variable)e.nextElement());
300 }
301 /** Removes a model listener */
302 public void removeModelListener(ModelListener listener) {
303 for (Enumeration e=iVariables.elements();e.hasMoreElements();)
304 listener.variableRemoved((Variable)e.nextElement());
305 for (Enumeration e=iConstraints.elements();e.hasMoreElements();)
306 listener.constraintRemoved((Constraint)e.nextElement());
307 iModelListeners.removeElement(listener);
308 }
309
310 /** Model initialization */
311 public boolean init(Solver solver) {
312 boolean ok = true;
313 for (Enumeration e=iModelListeners.elements();ok && e.hasMoreElements();)
314 ok = ((ModelListener)e.nextElement()).init(solver);
315 return ok;
316 }
317 /** The list of model listeners */
318 public Vector getModelListeners() { return iModelListeners; }
319 /** The list of model listeners that are of the given class*/
320 public ModelListener modelListenerOfType(Class type) {
321 for (Enumeration e=iModelListeners.elements();e.hasMoreElements();) {
322 ModelListener listener = (ModelListener)e.nextElement();
323 if (listener.getClass() == type) return listener;
324 }
325 return null;
326 }
327 /** The list of constraints which are in a conflict with the given value if it is assigned to its variable.
328 * This means the constraints, which adds a value into the set of conflicting values in {@link Constraint#computeConflicts(Value, Set)}.
329 */
330 public Hashtable conflictConstraints(Value value) {
331 Hashtable conflictConstraints = new Hashtable();
332 for (Enumeration c=value.variable().hardConstraints().elements(); c.hasMoreElements();) {
333 Constraint constraint = (Constraint)c.nextElement();
334 HashSet conflicts = new HashSet();
335 constraint.computeConflicts(value, conflicts);
336 if (conflicts!=null && !conflicts.isEmpty()) {
337 conflictConstraints.put(constraint,conflicts);
338 }
339 }
340 return conflictConstraints;
341 }
342 /** The list of hard constraints which contain at least one variable that is not assigned. */
343 public Vector unassignedHardConstraints() {
344 Vector ret = new FastVector();
345 for (Enumeration c=constraints().elements();c.hasMoreElements();) {
346 Constraint constraint = (Constraint)c.nextElement();
347 if (!constraint.isHard()) continue;
348 boolean assigned = true;
349 for (Enumeration v=constraint.variables().elements();assigned && v.hasMoreElements();)
350 if (((Variable)v.nextElement()).getAssignment()==null) assigned=false;
351 if (!assigned)
352 ret.addElement(constraint);
353 }
354 return ret;
355 }
356
357 private class BestAssignmentComparator implements Comparator {
358 public int compare(Object o1, Object o2) {
359 Variable v1=(Variable)o1;
360 Variable v2=(Variable)o2;
361 return (int)(v1.getBestAssignmentIteration()-v2.getBestAssignmentIteration());
362 }
363 }
364 }