001    package net.sf.cpsolver.ifs.extension;
002    
003    
004    import java.util.*;
005    
006    import net.sf.cpsolver.ifs.heuristics.*;
007    import net.sf.cpsolver.ifs.model.*;
008    import net.sf.cpsolver.ifs.solution.*;
009    import net.sf.cpsolver.ifs.solver.*;
010    import net.sf.cpsolver.ifs.util.*;
011    
012    /**
013     * Conflict-based statistics.
014     * <br><br>
015     * The idea behind it is to memorize conflicts and to avoid their potential repetition. When a value v0 is assigned to a 
016     * variable V0, hard conflicts with previously assigned variables (e.g., V1 = v1, V2 = v2, ... Vm = vm) may occur. 
017     * These variables V1,...,Vm have to be unassigned before the value v0 is assigned to the variable V0. These unassignments, 
018     * together with the reason for their unassignment (i.e., the assignment V0 = v0), and a counter tracking how many times 
019     * such an event occurred in the past, is stored in memory.
020     * <br><br>
021     * Later, if a variable is selected for assignment again, the stored information about repetition of past hard conflicts
022     * can be taken into account, e.g., in the value selection heuristics. Assume that the variable V0 is selected for an
023     * assignment again (e.g., because it became unassigned as a result of a later assignment), we can weight the number of
024     * hard conflicts created in the past for each possible value of this variable. In the above example, the existing
025     * assignment V1 = v1 can prohibit the selection of value v0 for variable V0 if there is again a conflict with the
026     * assignment V1 = v1.
027     * <br><br>
028     * Conflict-based statistics are a data structure which memorizes the number of hard conflicts that have occurred 
029     * during the search (e.g., that assignment V0 = v0 resulted c1 times in an unassignment of V1 = v1, c2 times of 
030     * V2 = v2, . . . and cm times of Vm = vm). More precisely, they form an array
031     * <ul>
032     * CBS[Va = va, Vb != vb] = cab,
033     * </ul>
034     * stating that the assignment Va = va caused the unassignment of Vb = vb a total of cab times in the past. Note that 
035     * in case of n-ary constraints (where n > 2), this does not imply that the assignments Va = va and Vb = vb cannot be used
036     * together. The proposed conflict-based statistics do not actually work with any constraint, they only memorize 
037     * unassignments and the assignment that caused them. Let us consider a variable Va selected by the 
038     * {@link VariableSelection#selectVariable(Solution)} function and a value va selected by 
039     * {@link ValueSelection#selectValue(Solution, Variable)}. Once the assignment Vb = vb is selected by 
040     * {@link Model#conflictValues(Value)} to be unassigned, the array cell CBS[Va = va, Vb != vb] is incremented by one.
041     * <br><br>
042     * The data structure is implemented as a hash table, storing information for conflict-based statistics. A counter is 
043     * maintained for the tuple A = a and B != b. This counter is increased when the value a is assigned to the variable A 
044     * and b is unassigned from B. The example of this structure
045     * <ul>
046     * A = a &nbsp;&nbsp;&nbsp; &#8594; &nbsp;&nbsp;&nbsp; 3 x B != b, &nbsp; 4 x B != c, &nbsp; 2 x C != a, &nbsp; 120 x D != a
047     * </ul>
048     * expresses that variable B lost its assignment b three times and its assignment c four times, variable C lost its 
049     * assignment a two times, and D lost its assignment a 120 times, all because of later assignments of value a to 
050     * variable A. This structure is being used in the value selection heuristics to evaluate existing conflicts with
051     * the assigned variables. For example, if there is a variable A selected and if the value a is in conflict with the 
052     * assignment B = b, we know that a similar problem has already occurred 3x in the past, and hence the conflict A = a is 
053     * weighted with the number 3.
054     * <br><br>
055     * Then, a min-conflict value selection criterion, which selects a value with the minimal number of conflicts with the 
056     * existing assignments, can be easily adapted to a weighted min-conflict criterion. The value with the smallest sum of the
057     * number of conflicts multiplied by their frequencies is selected. Stated in another way, the weighted min-conflict 
058     * approach helps the value selection heuristics to select a value that might cause more conflicts than another
059     * value, but these conflicts occurred less frequently, and therefore they have a lower weighted sum.
060     * <br><br>
061     * The conflict-based statistics has also implemented the following extensions: <ul>
062     * <li> If a variable is selected for an assignment, the above presented structure can also tell how many potential
063     * conflicts a value can cause in the future. In the above example, we already know that four times a later assignment
064     * of A=a caused that value c was unassigned from B. We can try to minimize such future conflicts by selecting
065     * a different value of the variable B while A is still unbound.
066     * <li> The memorized conflicts can be aged according to how far they have occurred in the past. For example, a conflict
067     * which occurred 1000 iterations ago can have half the weight of a conflict which occurred during the last iteration
068     * or it can be forgotten at all.
069     * </ul>
070     * Furthermore, the presented conflict-based statistics can be used not only inside the solving mechanism. The 
071     * constructed "implications" together with the information about frequency of their occurrences can be easily accessed 
072     * by users or by some add-on deductive engine to identify inconsistencies1 and/or hard parts of the input problem. 
073     * The user can then modify the input requirements in order to eliminate problems found and let the solver continue the 
074     * search with this modified input problem.
075     * <br><br>
076     * Parameters:
077     * <br>
078     * <table border='1'><tr><th>Parameter</th><th>Type</th><th>Comment</th></tr>
079     * <tr><td>ConflictStatistics.Ageing</td><td>{@link Double}</td><td>Ageing of the conflict-based statistics. Every memorized conflict is aged (multiplited) by this factor for every iteration which passed from the time it was memorized. For instance, if there was a conflict 10 iterations ago, its value is ageing^10 (default is 1.0 -- no ageing).</td></tr>
080     * <tr><td>ConflictStatistics.AgeingHalfTime</td><td>{@link Integer}</td><td>Another way how to express ageing: number of iterations to decrease a conflict to 1/2 (default is 0 -- no ageing)</td></tr>
081     * </table>
082     *
083     * @see Solver
084     * @see Model
085     * @see ValueSelection
086     * @see VariableSelection
087     *
088     * @version
089     * IFS 1.1 (Iterative Forward Search)<br>
090     * Copyright (C) 2006 Tomáš Müller<br>
091     * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
092     * Lazenska 391, 76314 Zlin, Czech Republic<br>
093     * <br>
094     * This library is free software; you can redistribute it and/or
095     * modify it under the terms of the GNU Lesser General Public
096     * License as published by the Free Software Foundation; either
097     * version 2.1 of the License, or (at your option) any later version.
098     * <br><br>
099     * This library is distributed in the hope that it will be useful,
100     * but WITHOUT ANY WARRANTY; without even the implied warranty of
101     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
102     * Lesser General Public License for more details.
103     * <br><br>
104     * You should have received a copy of the GNU Lesser General Public
105     * License along with this library; if not, write to the Free Software
106     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
107     */
108    public class ConflictStatistics extends Extension implements ConstraintListener, InfoProvider {
109        private static final String PARAM_AGEING = "ConflictStatistics.Ageing";
110        private static final String PARAM_HALF_AGE = "ConflictStatistics.AgeingHalfTime";
111        private static final String PARAM_PRINT = "ConflictStatistics.Print";
112        
113        private double iAgeing = 1.0;
114        private boolean iPrint = false;
115        
116        private Hashtable iAssignments = new Hashtable();
117        private Hashtable iUnassignedVariables = new Hashtable();
118        private Hashtable iNoGoods = new Hashtable();
119        
120        public ConflictStatistics(Solver solver, DataProperties properties) {
121            super(solver, properties);
122            iAgeing = properties.getPropertyDouble(PARAM_AGEING, iAgeing);
123            int halfAge = properties.getPropertyInt(PARAM_HALF_AGE, 0);
124            if (halfAge > 0) iAgeing = Math.exp(Math.log(0.5) / ((double)halfAge));
125            iPrint = properties.getPropertyBoolean(PARAM_PRINT, iPrint);
126        }
127        
128        public void register(Model model) {
129            super.register(model);
130        }
131        
132        public void unregister(Model model) {
133            super.unregister(model);
134        }
135        
136        private void variableUnassigned( long iteration, Value unassignedValue, Assignment noGood) {
137            if (iteration<=0) return;
138            Assignment unass = new Assignment(iteration, unassignedValue, iAgeing);
139            Vector noGoodsForUnassignment = (Vector)iNoGoods.get(unass);
140            if (noGoodsForUnassignment != null) {
141                if (noGoodsForUnassignment.contains(noGood)) {
142                    ((Assignment)noGoodsForUnassignment.elementAt(noGoodsForUnassignment.indexOf(noGood))).incCounter(iteration);
143                } else {
144                    noGoodsForUnassignment.addElement(noGood);
145                }
146            } else {
147                noGoodsForUnassignment = new FastVector();
148                noGoodsForUnassignment.addElement(noGood);
149                iNoGoods.put(unass, noGoodsForUnassignment);
150            }
151        }
152        
153        public void reset() {
154            iUnassignedVariables.clear();
155            iAssignments.clear();
156        }
157        
158        public Hashtable getNoGoods() {
159            return iNoGoods;
160        }
161        
162        public void variableUnassigned(long iteration, Value unassignedValue, Value assignedValue) {
163            if (iteration<=0) return;
164            Assignment ass = new Assignment(iteration, assignedValue, iAgeing);
165            Assignment unass = new Assignment(iteration, unassignedValue, iAgeing);
166            if (iAssignments.containsKey(unass)) {
167                Vector asss = (Vector)iAssignments.get(unass);
168                if (asss.contains(ass)) {
169                    ((Assignment)asss.elementAt(asss.indexOf(ass))).incCounter(iteration);
170                } else {
171                    asss.addElement(ass);
172                }
173            } else {
174                Vector asss = new FastVector();
175                asss.addElement(ass);
176                iAssignments.put(unass, asss);
177            }
178            if (iUnassignedVariables.containsKey(unassignedValue.variable())) {
179                Vector asss = (Vector)iUnassignedVariables.get(unassignedValue.variable());
180                if (asss.contains(ass)) {
181                    ((Assignment)asss.elementAt(asss.indexOf(ass))).incCounter( iteration);
182                } else {
183                    asss.addElement(ass);
184                }
185            }
186            else {
187                Vector asss = new FastVector();
188                asss.addElement(ass);
189                iUnassignedVariables.put(unassignedValue.variable(), asss);
190            }
191        }
192        
193        /** Counts number of unassignments of the given conflicting values caused by the assignment
194         * of the given value.
195         */
196        public double countRemovals(long iteration, Collection conflictValues, Value value) {
197            long ret = 0;
198            for (Iterator i = conflictValues.iterator(); i.hasNext();) {
199                Value conflictValue = (Value)i.next();
200                ret += countRemovals(iteration, conflictValue, value);
201                // tady bylo +1
202            }
203            return ret;
204        }
205        
206        /** Counts number of unassignments of the given conflicting value caused by the assignment
207         * of the given value.
208         */
209        public double countRemovals(long iteration, Value conflictValue, Value value) {
210            Vector asss = (Vector)iUnassignedVariables.get(conflictValue.variable());
211            if (asss == null)
212                return 0;
213            Assignment ass = new Assignment(iteration, value, iAgeing);
214            int idx = asss.indexOf(ass);
215            if (idx < 0)
216                return 0;
217            return ((Assignment)asss.elementAt(idx)).getCounter(iteration);
218        }
219        
220        /** Counts potential number of unassignments of if the given value is selected.
221         */
222        public long countPotentialConflicts(long iteration, Value value, int limit) {
223            Vector asss = (Vector)iAssignments.get(new Assignment(iteration, value, iAgeing));
224            if (asss == null) return 0;
225            long count = 0;
226            for (Enumeration i = asss.elements(); i.hasMoreElements();) {
227                Assignment ass = (Assignment)i.nextElement();
228                if (ass.getValue().variable().getAssignment() == null) {
229                    if (limit >= 0) {
230                        count += ass.getCounter(iteration) * Math.max(0,1+limit - value.variable().getModel().conflictValues(ass.getValue()).size());
231                    }
232                    else {
233                        count += ass.getCounter(iteration);
234                    }
235                }
236            }
237            return count;
238        }
239        
240        public String toString() {
241            StringBuffer sb = new StringBuffer("Statistics{");
242            for (Enumeration e1 = ToolBox.sortEnumeration(iUnassignedVariables.keys(), Assignment.getComparator(0));e1.hasMoreElements();) {
243                Variable variable = (Variable)e1.nextElement();
244                if (variable.countAssignments() < 100) continue;
245                sb.append("\n      ").append(variable.countAssignments() + "x ").append(variable.getName()).append(" <= {");
246                for (Enumeration e2 = ToolBox.sortEnumeration(((Vector)iUnassignedVariables.get(variable)).elements(),Assignment.getComparator(0));e2.hasMoreElements();) {
247                    Assignment x = (Assignment)e2.nextElement();
248                    if (x.getCounter(0) >= 10)
249                        sb.append("\n        ").append(x.toString(0, true)).append(e2.hasMoreElements() ? "," : "");
250                }
251                sb.append("\n      }");
252            }
253            sb.append("\n    }");
254            return sb.toString();
255        }
256        
257        public void getInfo(Dictionary info) {
258            //info.put("Statistics: IsGood.Time",sTimeFormat.format(((double)iIsGoodTime)/60000.0)+" min");
259            //info.put("Statistics: NoGood.Time",sTimeFormat.format(((double)iNoGoodTime)/60000.0)+" min");
260            /*info.put("Statistics: VariableAssigned.Time",sTimeFormat.format(((double)iVariableAssignedTime)/60000.0)+" min");
261             *info.put("Statistics: VariableUnassigned.Time",sTimeFormat.format(((double)iVariableUnassignedTime)/60000.0)+" min");
262             *info.put("Statistics: Bad assignments:", String.valueOf(iBadAssignments.size()));*/
263        }
264        
265        public void getInfo(Dictionary info, Vector variables) {
266        }
267    
268        public void constraintBeforeAssigned( long iteration, Constraint constraint, Value assigned, Set unassigned) {
269        }
270        
271        /** Increments appropriate counters when there is a value unassigned */
272        public void constraintAfterAssigned(long iteration, Constraint constraint, Value assigned, Set unassigned) {
273            if (iteration<=0) return;
274            if (unassigned == null || unassigned.isEmpty())
275                return;
276            if (iPrint) {
277                //AssignmentSet noGoods = AssignmentSet.createAssignmentSet(iteration,unassigned, iAgeing);
278                //noGoods.addAssignment(iteration, assigned, iAgeing);
279                //noGoods.setConstraint(constraint);
280                    Assignment noGood = new Assignment(iteration,assigned, iAgeing);
281                    noGood.setConstraint(constraint);
282                for (Iterator i = unassigned.iterator(); i.hasNext();) {
283                    Value unassignedValue = (Value)i.next();
284                    variableUnassigned(iteration, unassignedValue, noGood);
285                    variableUnassigned(iteration, unassignedValue, assigned);
286                }
287            } else {
288                for (Iterator i = unassigned.iterator(); i.hasNext();) {
289                    Value unassignedValue = (Value)i.next();
290                    variableUnassigned(iteration, unassignedValue, assigned);
291                }
292            }
293        }
294        
295        public void constraintAdded(Constraint constraint) {
296            constraint.addConstraintListener(this);
297        }
298        public void constraintRemoved(Constraint constraint) {
299            constraint.removeConstraintListener(this);
300        }
301    }