001    package net.sf.cpsolver.ifs.model;
002    
003    import java.util.*;
004    
005    import net.sf.cpsolver.ifs.solver.*;
006    import net.sf.cpsolver.ifs.util.*;
007    
008    /**
009     * Generic model (definition of a problem).
010     * <br><br>
011     * It consists of variables and constraints. It has also capability of memorizing the current
012     * and the best ever found assignment.
013     * <br><br>
014     * Example usage:<br><ul><code>
015     * MyModel model = new MyModel();<br>
016     * Variable a = new MyVariable("A");<br>
017     * model.addVariable(a);<br>
018     * Variable b = new MyVariable("B");<br>
019     * model.addVariable(b);<br>
020     * Variable c = new MyVariable("C");<br>
021     * model.addVariable(c);<br>
022     * Constraint constr = MyConstraint("all-different");<br>
023     * model.addConstraint(constr);<br>
024     * constr.addVariable(a);<br>
025     * constr.addVariable(b);<br>
026     * constr.addVariable(c);<br>
027     * solver.setInitialSolution(model);
028     * </code></ul>
029     * 
030     * @see Variable
031     * @see Constraint
032     * @see net.sf.cpsolver.ifs.solution.Solution
033     * @see net.sf.cpsolver.ifs.solver.Solver
034     *
035     * @version
036     * IFS 1.1 (Iterative Forward Search)<br>
037     * Copyright (C) 2006 Tomáš Müller<br>
038     * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
039     * Lazenska 391, 76314 Zlin, Czech Republic<br>
040     * <br>
041     * This library is free software; you can redistribute it and/or
042     * modify it under the terms of the GNU Lesser General Public
043     * License as published by the Free Software Foundation; either
044     * version 2.1 of the License, or (at your option) any later version.
045     * <br><br>
046     * This library is distributed in the hope that it will be useful,
047     * but WITHOUT ANY WARRANTY; without even the implied warranty of
048     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
049     * Lesser General Public License for more details.
050     * <br><br>
051     * You should have received a copy of the GNU Lesser General Public
052     * License along with this library; if not, write to the Free Software
053     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
054     */
055    
056    public class Model {
057        private static org.apache.log4j.Logger sLogger = org.apache.log4j.Logger.getLogger(Model.class);
058        protected static java.text.DecimalFormat sTimeFormat = new java.text.DecimalFormat("0.00",new java.text.DecimalFormatSymbols(Locale.US));
059        protected static java.text.DecimalFormat sDoubleFormat = new java.text.DecimalFormat("0.00",new java.text.DecimalFormatSymbols(Locale.US));
060        protected static java.text.DecimalFormat sPercentageFormat = new java.text.DecimalFormat("0.00",new java.text.DecimalFormatSymbols(Locale.US));
061        
062        private Vector iVariables = new FastVector();
063        private Vector iConstraints = new FastVector();
064        private Vector iGlobalConstraints = new FastVector();
065        protected EnumerableCollection iUnassignedVariables = new FastVector();
066        protected EnumerableCollection iAssignedVariables = new FastVector();
067        private Vector iInfoProviders = new FastVector();
068        private Vector iVariablesWithInitialValueCache = null;
069        
070        protected EnumerableCollection iPerturbVariables = null;
071        
072        private int iBestUnassignedVariables = -1;
073        private int iBestPerturbations = 0;
074        private int iNrAssignedVariables = 0;
075        
076        /** Constructor */
077        public Model() {
078        }
079        
080        /** The list of variables in the model */
081        public Vector variables() { return iVariables; }
082        /** The number of variables in the model */
083        public int countVariables() { return iVariables.size(); }
084        /** Adds a variable to the model */
085        public void addVariable(Variable variable) {
086            variable.setModel(this);
087            iVariables.addElement(variable);
088            if (variable instanceof InfoProvider)
089                    iInfoProviders.addElement(variable);
090            if (variable.getAssignment()==null) {
091                if (iUnassignedVariables!=null) iUnassignedVariables.addElement(variable);
092            } else {
093                if (iAssignedVariables!=null) iAssignedVariables.addElement(variable);
094                iNrAssignedVariables++;
095            }
096            if (variable.getAssignment()!=null) variable.assign(0L,variable.getAssignment());
097            for (Enumeration e=iModelListeners.elements();e.hasMoreElements();)
098                ((ModelListener)e.nextElement()).variableAdded(variable);
099            invalidateVariablesWithInitialValueCache();
100        }
101        /** Removes a variable from the model */
102        public void removeVariable(Variable variable) {
103            variable.setModel(null);
104            iVariables.removeElement(variable);
105            if (variable instanceof InfoProvider)
106                    iInfoProviders.removeElement(variable);
107            if (iUnassignedVariables!=null && iUnassignedVariables.contains(variable)) iUnassignedVariables.removeElement(variable);
108            if (iAssignedVariables!=null && iAssignedVariables.contains(variable)) iAssignedVariables.removeElement(variable);
109            if (variable.getAssignment()!=null) iNrAssignedVariables--;
110            for (Enumeration e=iModelListeners.elements();e.hasMoreElements();)
111                ((ModelListener)e.nextElement()).variableRemoved(variable);
112            invalidateVariablesWithInitialValueCache();
113        }
114        
115        /** The list of constraints in the model */
116        public Vector constraints() { return iConstraints; }
117        /** The number of constraints in the model */
118        public int countConstraints() { return iConstraints.size(); }
119        /** Adds a constraint to the model */
120        public void addConstraint(Constraint constraint) {
121            constraint.setModel(this);
122            iConstraints.addElement(constraint);
123            if (constraint instanceof InfoProvider)
124                    iInfoProviders.addElement(constraint);
125            for (Enumeration e=iModelListeners.elements();e.hasMoreElements();)
126                ((ModelListener)e.nextElement()).constraintAdded(constraint);
127        }
128        /** Removes a constraint from the model */
129        public void removeConstraint(Constraint constraint) {
130            constraint.setModel(null);
131            iConstraints.removeElement(constraint);
132            if (constraint instanceof InfoProvider)
133                    iInfoProviders.removeElement(constraint);
134            for (Enumeration e=iModelListeners.elements();e.hasMoreElements();)
135                ((ModelListener)e.nextElement()).constraintRemoved(constraint);
136        }
137    
138        /** The list of global constraints in the model */
139        public Vector globalConstraints() { return iGlobalConstraints; }
140        /** The number of global constraints in the model */
141        public int countGlobalConstraints() { return iGlobalConstraints.size(); }
142        /** Adds a global constraint to the model */
143        public void addGlobalConstraint(GlobalConstraint constraint) {
144            constraint.setModel(this);
145            iGlobalConstraints.addElement(constraint);
146            if (constraint instanceof InfoProvider)
147                iInfoProviders.addElement(constraint);
148            for (Enumeration e=iModelListeners.elements();e.hasMoreElements();)
149                ((ModelListener)e.nextElement()).constraintAdded(constraint);
150        }
151        /** Removes a global constraint from the model */
152        public void removeGlobalConstraint(GlobalConstraint constraint) {
153            constraint.setModel(null);
154            iGlobalConstraints.removeElement(constraint);
155            if (constraint instanceof InfoProvider)
156                iInfoProviders.removeElement(constraint);
157            for (Enumeration e=iModelListeners.elements();e.hasMoreElements();)
158                ((ModelListener)e.nextElement()).constraintRemoved(constraint);
159        }
160    
161        /** The list of unassigned variables in the model */
162        public EnumerableCollection unassignedVariables() {
163            if (iUnassignedVariables!=null) return iUnassignedVariables;
164            FastVector un = new FastVector(iVariables.size());
165            for (Enumeration e=iVariables.elements();e.hasMoreElements();) {
166                Variable variable = (Variable)e.nextElement();
167                if (variable.getAssignment()==null) un.add(variable);
168            }
169            return un;
170        }
171        
172        /** Number of unassigned variables */
173        public int nrUnassignedVariables() {
174            if (iUnassignedVariables!=null) return iUnassignedVariables.size();
175            return iVariables.size() - iNrAssignedVariables;
176        }
177    
178        /** The list of assigned variables in the model */
179        public EnumerableCollection assignedVariables() { 
180            if (iAssignedVariables!=null) return iAssignedVariables;
181            FastVector as = new FastVector(iVariables.size());
182            for (Enumeration e=iVariables.elements();e.hasMoreElements();) {
183                Variable variable = (Variable)e.nextElement();
184                if (variable.getAssignment()!=null) as.add(variable);
185            }
186            return as;
187        }
188        
189        /** Number of assigned variables */
190        public int nrAssignedVariables() {
191            if (iAssignedVariables!=null) return iAssignedVariables.size();
192            return iNrAssignedVariables;
193        }
194        
195        /** The list of perturbation variables in the model, i.e., the variables which has an initial value but which are not 
196         * assigned with this value.
197         */
198        public EnumerableCollection perturbVariables() {
199            if (iPerturbVariables!=null) return iPerturbVariables;
200            EnumerableCollection perturbances = new FastVector();
201            for (Enumeration e=variablesWithInitialValue().elements();e.hasMoreElements();) {
202                Variable variable = (Variable)e.nextElement();
203                if (variable.getAssignment()!=null) {
204                    if (!variable.getInitialAssignment().equals(variable.getAssignment())) perturbances.addElement(variable);
205                } else {
206                    boolean hasPerturbance = false;
207                    for (Enumeration x=variable.hardConstraints().elements();!hasPerturbance && x.hasMoreElements();) {
208                            Constraint constraint = (Constraint)x.nextElement();
209                            if (constraint.inConflict(variable.getInitialAssignment()))
210                                    hasPerturbance=true;
211                    }
212                    for (Enumeration x=globalConstraints().elements();!hasPerturbance && x.hasMoreElements();) {
213                        GlobalConstraint constraint = (GlobalConstraint)x.nextElement();
214                        if (constraint.inConflict(variable.getInitialAssignment()))
215                            hasPerturbance=true;
216                    }
217                    if (hasPerturbance) perturbances.addElement(variable);
218                }
219            }
220            iPerturbVariables = perturbances;
221            return perturbances;
222        }
223        
224        /** The list of perturbation variables in the model, i.e., the variables which has an initial value but which are not 
225         * assigned with this value. Only variables from the given set are considered.
226         */
227        public EnumerableCollection perturbVariables(Vector variables) {
228            EnumerableCollection perturbances = new FastVector();
229            for (Enumeration e=variables.elements();e.hasMoreElements();) {
230                    Variable variable = (Variable)e.nextElement();
231                    if (variable.getInitialAssignment()==null) continue;
232                if (variable.getAssignment()!=null) {
233                    if (!variable.getInitialAssignment().equals(variable.getAssignment())) perturbances.addElement(variable);
234                } else {
235                    boolean hasPerturbance = false;
236                    for (Enumeration x=variable.hardConstraints().elements();!hasPerturbance && x.hasMoreElements();) {
237                        Constraint constraint = (Constraint)x.nextElement();
238                        if (constraint.inConflict(variable.getInitialAssignment()))
239                            hasPerturbance=true;
240                    }
241                    for (Enumeration x=globalConstraints().elements();!hasPerturbance && x.hasMoreElements();) {
242                        GlobalConstraint constraint = (GlobalConstraint)x.nextElement();
243                        if (constraint.inConflict(variable.getInitialAssignment()))
244                            hasPerturbance=true;
245                    }
246                    if (hasPerturbance) perturbances.addElement(variable);
247                }
248            }
249            return perturbances;
250        }
251        
252        
253        /** Returns the set of confliction variables with this value, if it is assigned to its variable */
254        public Set conflictValues(Value value) {
255            HashSet conflictValues = new HashSet();
256            for (Enumeration c=value.variable().hardConstraints().elements(); c.hasMoreElements();)
257                ((Constraint)c.nextElement()).computeConflicts(value, conflictValues);
258            for (Enumeration c=globalConstraints().elements(); c.hasMoreElements();)
259                ((GlobalConstraint)c.nextElement()).computeConflicts(value, conflictValues);
260            return conflictValues;
261        }
262        
263        /** Return true if the given value is in conflict with a hard constraint */
264        public boolean inConflict(Value value) {
265            for (Enumeration c=value.variable().hardConstraints().elements(); c.hasMoreElements();)
266                if (((Constraint)c.nextElement()).inConflict(value)) return true;
267            for (Enumeration c=globalConstraints().elements(); c.hasMoreElements();)
268                if (((GlobalConstraint)c.nextElement()).inConflict(value)) return true;
269            return false;
270        }
271        
272        /** The list of variales without initial value */
273        public Vector variablesWithInitialValue() {
274            if (iVariablesWithInitialValueCache!=null)
275                    return iVariablesWithInitialValueCache;
276            iVariablesWithInitialValueCache = new FastVector();
277            for (Enumeration e=variables().elements();e.hasMoreElements();) {
278                Variable variable = (Variable)e.nextElement();
279                if (variable.getInitialAssignment()!=null) iVariablesWithInitialValueCache.addElement(variable);
280            }
281            return iVariablesWithInitialValueCache;
282        }
283        
284        /** Invalidates cache containing all variables that possess an initial value */
285        protected void invalidateVariablesWithInitialValueCache() {
286            iVariablesWithInitialValueCache = null;
287        }
288        
289        /** Called before a value is assigned to its variable */
290        public void beforeAssigned(long iteration, Value value) {
291            for (Enumeration e=iModelListeners.elements();e.hasMoreElements();)
292                ((ModelListener)e.nextElement()).beforeAssigned(iteration, value);
293        }
294        
295        /** Called before a value is unassigned from its variable */
296        public void beforeUnassigned(long iteration, Value value) {
297            for (Enumeration e=iModelListeners.elements();e.hasMoreElements();)
298                ((ModelListener)e.nextElement()).beforeUnassigned(iteration, value);
299        }
300        
301        /** Called after a value is assigned to its variable */
302        public void afterAssigned(long iteration, Value value) {
303            if (iUnassignedVariables!=null) iUnassignedVariables.removeElement(value.variable());
304            if (iAssignedVariables!=null) iAssignedVariables.addElement(value.variable());
305            iNrAssignedVariables++;
306            iPerturbVariables = null;
307            for (Enumeration e=iModelListeners.elements();e.hasMoreElements();)
308                ((ModelListener)e.nextElement()).afterAssigned(iteration, value);
309        }
310        
311        /** Called after a value is unassigned from its variable */
312        public void afterUnassigned(long iteration, Value value) {
313            if (iUnassignedVariables!=null) iUnassignedVariables.addElement(value.variable());
314            if (iAssignedVariables!=null) iAssignedVariables.removeElement(value.variable());
315            iNrAssignedVariables--;
316            iPerturbVariables = null;
317            for (Enumeration e=iModelListeners.elements();e.hasMoreElements();)
318                ((ModelListener)e.nextElement()).afterUnassigned(iteration, value);
319        }
320        
321        public String toString() {
322            Collections.sort(variables(), new Comparator() {
323                public int compare(Object o1, Object o2) {
324                    Variable v1 = (Variable)o1;
325                    Variable v2 = (Variable)o2;
326                    return (v1.getName()!=null?v1.getName().compareTo(v2.getName()):(int)(v1.getId()-v2.getId()));
327                }
328            });
329            return "Model{\n    variables="+ToolBox.col2string(variables(),2)+
330            ",\n    constraints="+ToolBox.col2string(constraints(),2)+
331            ",\n    #unassigned="+nrUnassignedVariables()+
332            ",\n    unassigned="+ToolBox.col2string(unassignedVariables(),2)+
333            ",\n    #perturbations="+perturbVariables().size()+"+"+(variables().size()-variablesWithInitialValue().size())+
334            ",\n    perturbations="+ToolBox.col2string(perturbVariables(),2)+
335            ",\n    info="+getInfo()+
336            "\n  }";
337        }
338        
339        protected String getPerc(double value, double min, double max) {
340                if (max==min) return sPercentageFormat.format(100.0);
341            return sPercentageFormat.format(100.0 - 100.0*(value-min)/(max-min));
342        }
343    
344        protected String getPercRev(double value, double min, double max) {
345            if (max==min) return sPercentageFormat.format(0.0);
346            return sPercentageFormat.format(100.0*(value-min)/(max-min));
347        }
348        
349        
350        /** Returns information about the current solution. Information from all model listeners and constraints is also included.
351         */
352        public java.util.Hashtable getInfo() {
353            java.util.Hashtable ret = new java.util.Hashtable();
354            ret.put("Assigned variables", getPercRev(nrAssignedVariables(),0,variables().size())+"% ("+nrAssignedVariables()+"/"+variables().size()+")");
355            int nrVarsWithInitialValue = variablesWithInitialValue().size(); 
356            if (nrVarsWithInitialValue>0) {
357                    ret.put("Perturbation variables", getPercRev(perturbVariables().size(),0,nrVarsWithInitialValue)+"% ("+perturbVariables().size()+" + "+(variables().size()-nrVarsWithInitialValue)+")");
358            }
359            ret.put("Overall solution value", sDoubleFormat.format(getTotalValue()));
360            for (Enumeration e=iInfoProviders.elements();e.hasMoreElements();)
361                ((InfoProvider)e.nextElement()).getInfo(ret);
362            return ret;
363        }
364        
365        /**
366         * Extended information about current solution. 
367         * Similar to {@link Model#getInfo()}, but some more information (that is more expensive to compute) might be added.
368         */
369        public java.util.Hashtable getExtendedInfo() {
370            return getInfo();
371        }
372        
373        /** Returns information about the current solution. Information from all model listeners and constraints is also included.
374         * Only variables from the given set are considered.
375         */
376        public java.util.Hashtable getInfo(Vector variables) {
377            java.util.Hashtable ret = new java.util.Hashtable();
378            int assigned = 0, perturb = 0, nrVarsWithInitialValue = 0;
379            for (Enumeration e=variables.elements();e.hasMoreElements();) {
380                    Variable variable = (Variable)e.nextElement();
381                    if (variable.getAssignment()!=null) assigned++;
382                    if (variable.getInitialAssignment()!=null) {
383                            nrVarsWithInitialValue++;
384                    if (variable.getAssignment()!=null) {
385                        if (!variable.getInitialAssignment().equals(variable.getAssignment())) perturb++;
386                    } else {
387                        boolean hasPerturbance = false;
388                        for (Enumeration x=variable.hardConstraints().elements();!hasPerturbance && x.hasMoreElements();) {
389                            Constraint constraint = (Constraint)x.nextElement();
390                            if (constraint.inConflict(variable.getInitialAssignment()))
391                                hasPerturbance=true;
392                        }
393                        for (Enumeration x=globalConstraints().elements();!hasPerturbance && x.hasMoreElements();) {
394                            GlobalConstraint constraint = (GlobalConstraint)x.nextElement();
395                            if (constraint.inConflict(variable.getInitialAssignment()))
396                                hasPerturbance=true;
397                        }
398                        if (hasPerturbance) perturb++;
399                    }
400                    }
401            }
402            ret.put("Assigned variables", getPercRev(assigned,0,variables.size())+"% ("+assigned+"/"+variables.size()+")");
403            if (nrVarsWithInitialValue>0) {
404                    ret.put("Perturbation variables", getPercRev(perturb,0,nrVarsWithInitialValue)+"% ("+perturb+" + "+(variables.size()-nrVarsWithInitialValue)+")");
405            }
406            ret.put("Overall solution value", sDoubleFormat.format(getTotalValue(variables)));
407            for (Enumeration e=iInfoProviders.elements();e.hasMoreElements();)
408                ((InfoProvider)e.nextElement()).getInfo(ret, variables);
409            return ret;
410        }
411        
412        /** Returns the number of unassigned variables in the best ever found solution */
413        public int getBestUnassignedVariables() { return iBestUnassignedVariables; }
414        /** Returns the number of perturbation variables in the best ever found solution */
415        public int getBestPerturbations() { return iBestPerturbations; }
416        /** Save the current assignment as the best ever found assignment */
417        public void saveBest() {
418            iBestUnassignedVariables = nrUnassignedVariables();//unassignedVariables().size();
419            iBestPerturbations = perturbVariables().size();
420            for (Enumeration e=variables().elements();e.hasMoreElements();) {
421                Variable variable = (Variable)e.nextElement();
422                variable.setBestAssignment(variable.getAssignment());
423            }
424        }
425        /** Clear the best ever found assignment */
426        public void clearBest() {
427            iBestUnassignedVariables = -1;
428            iBestPerturbations = 0;
429            for (Enumeration e=variables().elements();e.hasMoreElements();) {
430                Variable variable = (Variable)e.nextElement();
431                variable.setBestAssignment(null);
432            }
433        }
434        /** Restore the best ever found assignment into the current assignment*/
435        public void restoreBest() {
436            for (Enumeration e=variables().elements();e.hasMoreElements();) {
437                Variable variable = (Variable)e.nextElement();
438                if (variable.getAssignment()!=null && !variable.getAssignment().equals(variable.getBestAssignment()))
439                    variable.unassign(0);
440            }
441            HashSet problems = new HashSet();
442            for (Enumeration e=ToolBox.sortEnumeration(variables().elements(), new BestAssignmentComparator());e.hasMoreElements();) {
443                Variable variable = (Variable)e.nextElement();
444                if (variable.getBestAssignment()!=null && variable.getAssignment()==null) {
445                    Set confs = conflictValues(variable.getBestAssignment());
446                    if (!confs.isEmpty()) {
447                        sLogger.error("restore best problem: assignment "+variable.getName()+" = "+variable.getBestAssignment().getName());
448                        for (Enumeration en=variable.hardConstraints().elements();en.hasMoreElements();) {
449                            Constraint c=(Constraint)en.nextElement();
450                            Set x = new HashSet();
451                            c.computeConflicts(variable.getBestAssignment(),x);
452                            if (!x.isEmpty()) {
453                                sLogger.error("  constraint "+c.getClass().getName()+" "+c.getName()+" causes the following conflicts "+x);
454                            }
455                        }
456                        for (Enumeration en=globalConstraints().elements();en.hasMoreElements();) {
457                            GlobalConstraint c=(GlobalConstraint)en.nextElement();
458                            Set x = new HashSet();
459                            c.computeConflicts(variable.getBestAssignment(),x);
460                            if (!x.isEmpty()) {
461                                sLogger.error("  global constraint "+c.getClass().getName()+" "+c.getName()+" causes the following conflicts "+x);
462                            }
463                        }
464                        problems.add(variable.getBestAssignment());
465                    } else variable.assign(0,variable.getBestAssignment());
466                }
467            }
468            int attempt = 0;
469            while (!problems.isEmpty() && attempt<=100) {
470                attempt++;
471                Value value = (Value)ToolBox.random(problems); problems.remove(value);
472                Variable variable = value.variable();            
473                Set confs = conflictValues(value);
474                if (!confs.isEmpty()) {
475                    sLogger.error("restore best problem (again, att="+attempt+"): assignment "+variable.getName()+" = "+value.getName());
476                    for (Enumeration en=variable.hardConstraints().elements();en.hasMoreElements();) {
477                        Constraint c=(Constraint)en.nextElement();
478                        Set x = new HashSet();
479                        c.computeConflicts(value,x);
480                        if (!x.isEmpty()) sLogger.error("  constraint "+c.getClass().getName()+" "+c.getName()+" causes the following conflicts "+x);
481                    }
482                    for (Enumeration en=globalConstraints().elements();en.hasMoreElements();) {
483                        GlobalConstraint c=(GlobalConstraint)en.nextElement();
484                        Set x = new HashSet();
485                        c.computeConflicts(value,x);
486                        if (!x.isEmpty()) sLogger.error("  constraint "+c.getClass().getName()+" "+c.getName()+" causes the following conflicts "+x);
487                    }
488                    for (Iterator i=confs.iterator();i.hasNext();)
489                            ((Value)i.next()).variable().unassign(0);
490                    problems.addAll(confs);
491                }
492                variable.assign(0,value);
493            }
494        }
495        
496        /** The list of unassigned variables in the best ever found solution*/
497        public EnumerableCollection bestUnassignedVariables() {
498            if (iBestUnassignedVariables<0) return unassignedVariables();
499            EnumerableCollection ret = new FastVector(variables().size());
500            for (Enumeration e=variables().elements();e.hasMoreElements();) {
501                Variable variable = (Variable)e.nextElement();
502                if (variable.getBestAssignment()==null) ret.addElement(variable);
503            }
504            return ret;
505        }
506        
507        /** Value of the current solution. It is the sum of all assigned values, i.e., {@link Value#toDouble()}.*/
508        public double getTotalValue() {
509            double valCurrent = 0;
510            for (Enumeration e=assignedVariables().elements();e.hasMoreElements();)
511                valCurrent += ((Variable)e.nextElement()).getAssignment().toDouble();
512            return valCurrent;
513        }
514        
515        /** Value of the current solution. It is the sum of all assigned values, i.e., {@link Value#toDouble()}.
516         * Only variables from the given set are considered.
517         **/
518        public double getTotalValue(Vector variables) {
519            double valCurrent = 0;
520            for (Enumeration e=variables.elements();e.hasMoreElements();) {
521                    Variable variable = (Variable)e.nextElement();
522                    if (variable.getAssignment()!=null)
523                            valCurrent += variable.getAssignment().toDouble();
524            }
525            return valCurrent;
526        }
527        
528        private Vector iModelListeners = new FastVector();
529        /** Adds a model listener */
530        public void addModelListener(ModelListener listener) {
531            iModelListeners.addElement(listener);
532            if (listener instanceof InfoProvider)
533                    iInfoProviders.addElement(listener);
534            for (Enumeration e=iConstraints.elements();e.hasMoreElements();)
535                listener.constraintAdded((Constraint)e.nextElement());
536            for (Enumeration e=iVariables.elements();e.hasMoreElements();)
537                listener.variableAdded((Variable)e.nextElement());
538        }
539        /** Removes a model listener */
540        public void removeModelListener(ModelListener listener) {
541            if (listener instanceof InfoProvider)
542                    iInfoProviders.removeElement(listener);
543            for (Enumeration e=iVariables.elements();e.hasMoreElements();)
544                listener.variableRemoved((Variable)e.nextElement());
545            for (Enumeration e=iConstraints.elements();e.hasMoreElements();)
546                listener.constraintRemoved((Constraint)e.nextElement());
547            iModelListeners.removeElement(listener);
548        }
549        
550        /** Model initialization */
551        public boolean init(Solver solver) {
552            boolean ok = true;
553            for (Enumeration e=iModelListeners.elements();ok && e.hasMoreElements();)
554                ok = ((ModelListener)e.nextElement()).init(solver);
555            return ok;
556        }
557        /** The list of model listeners */
558        public Vector getModelListeners() { return iModelListeners; }
559        /** The list of model listeners that are of the given class*/
560        public ModelListener modelListenerOfType(Class type) {
561            for (Enumeration e=iModelListeners.elements();e.hasMoreElements();) {
562                ModelListener listener = (ModelListener)e.nextElement();
563                if (listener.getClass() == type) return listener;
564            }
565            return null;
566        }
567        /** The list of constraints which are in a conflict with the given value if it is assigned to its variable.
568         * This means the constraints, which adds a value into the set of conflicting values in {@link Constraint#computeConflicts(Value, Set)}.
569         */
570        public Hashtable conflictConstraints(Value value) {
571            Hashtable conflictConstraints = new Hashtable();
572            for (Enumeration c=value.variable().hardConstraints().elements(); c.hasMoreElements();) {
573                Constraint constraint = (Constraint)c.nextElement();
574                HashSet conflicts = new HashSet();
575                constraint.computeConflicts(value, conflicts);
576                if (conflicts!=null && !conflicts.isEmpty()) {
577                    conflictConstraints.put(constraint,conflicts);
578                }
579            }
580            for (Enumeration c=globalConstraints().elements(); c.hasMoreElements();) {
581                GlobalConstraint constraint = (GlobalConstraint)c.nextElement();
582                HashSet conflicts = new HashSet();
583                constraint.computeConflicts(value, conflicts);
584                if (conflicts!=null && !conflicts.isEmpty()) {
585                    conflictConstraints.put(constraint,conflicts);
586                }
587            }
588            return conflictConstraints;
589        }
590        /** The list of hard constraints which contain at least one variable that is not assigned. */
591        public Vector unassignedHardConstraints() {
592            Vector ret = new FastVector();
593            for (Enumeration c=constraints().elements();c.hasMoreElements();) {
594                Constraint constraint = (Constraint)c.nextElement();
595                if (!constraint.isHard()) continue;
596                boolean assigned = true;
597                for (Enumeration v=constraint.variables().elements();assigned && v.hasMoreElements();)
598                    if (((Variable)v.nextElement()).getAssignment()==null) assigned=false;
599                if (!assigned)
600                    ret.addElement(constraint);
601            }
602            if (!unassignedVariables().isEmpty())
603                ret.addAll(globalConstraints());
604            return ret;
605        }
606        
607        private class BestAssignmentComparator implements Comparator {
608            public int compare(Object o1, Object o2) {
609                Variable v1=(Variable)o1;
610                Variable v2=(Variable)o2;
611                return (int)(v1.getBestAssignmentIteration()-v2.getBestAssignmentIteration());
612            }
613        }
614    
615        /** Registered info providers (see {@link InfoProvider}) */
616        protected Vector getInfoProviders() { return iInfoProviders; }
617    }