1 ovy      1.1 /*
  2 ovy      1.1   Some utilities for MultiMaps
  3 ovy      1.1 */
  4 ovy      1.1 
  5 ovy      1.1 package harpoon.Analysis.MemOpt;
  6 ovy      1.1 
  7 cananian 1.2 import harpoon.Analysis.GraphColoring.Color;
  8 cananian 1.2 import harpoon.Analysis.GraphColoring.ColorFactory;
  9 cananian 1.2 import harpoon.Analysis.GraphColoring.DefaultSparseNode;
 10 cananian 1.2 import harpoon.Analysis.GraphColoring.SimpleGraphColorer;
 11 cananian 1.2 import harpoon.Analysis.GraphColoring.SparseGraph;
 12 cananian 1.2 import harpoon.Analysis.GraphColoring.SparseNode;
 13 cananian 1.2 import harpoon.Analysis.GraphColoring.UnboundedGraphColorer;
 14 cananian 1.3 import net.cscott.jutil.AggregateSetFactory;
 15 cananian 1.3 import net.cscott.jutil.GenericMultiMap;
 16 cananian 1.3 import net.cscott.jutil.MultiMap;
 17 cananian 1.3 import net.cscott.jutil.WorkSet;
 18 cananian 1.2 import java.util.ArrayList;
 19 cananian 1.2 import java.util.Collection;
 20 cananian 1.2 import java.util.Comparator;
 21 cananian 1.2 import java.util.HashMap;
 22 cananian 1.2 import java.util.HashSet;
 23 cananian 1.2 import java.util.Iterator;
 24 cananian 1.2 import java.util.LinkedList;
 25 cananian 1.2 import java.util.List;
 26 cananian 1.2 import java.util.Map;
 27 cananian 1.2 import java.util.Set;
 28 ovy      1.1 
 29 ovy      1.1 public final class MultiMapUtils { 
 30 ovy      1.1     /*
 31 ovy      1.1       "Closes" the given multi-map, i.e. makes it transitive.
 32 ovy      1.1       only nodes accessible from startingNodes are processed.
 33 ovy      1.1       Time: O(N*M)
 34 ovy      1.1       N: number of nodes in startingNodes
 35 ovy      1.1       M: number of edges accessible from startingNodes
 36 ovy      1.1 
 37 ovy      1.1       Only uses iterator from startingNodes, i.e. other ops can be slow
 38 ovy      1.1 
 39 ovy      1.1       UPDATE: now forbids closures over dontCloseOver. I *really* need this
 40 ovy      1.1         capability. dontCloseOver can be null, and it'll be ignored.
 41 ovy      1.1         For sane results, dontCloseOver should be a subset of startingNodes.
 42 ovy      1.1 
 43 ovy      1.1       FIXME: I have to make this specification clearer. 
 44 ovy      1.1     */
 45 ovy      1.1     static MultiMap multiMapClosure(MultiMap oldMap, Collection startingNodes,
 46 ovy      1.1                                     Set dontCloseOver) {
 47 ovy      1.1         MultiMap map = new GenericMultiMap(new AggregateSetFactory());
 48 ovy      1.1         
 49 ovy      1.1         Set reachable = new HashSet();
 50 ovy      1.1         WorkSet workset = new WorkSet();
 51 ovy      1.1         
 52 ovy      1.1         for (Iterator i = startingNodes.iterator(); i.hasNext(); ) {
 53 ovy      1.1             Object x = i.next();
 54 ovy      1.1             
 55 ovy      1.1             reachable.clear();
 56 ovy      1.1 
 57 ovy      1.1             workset.add(x);
 58 ovy      1.1 
 59 ovy      1.1             while (!workset.isEmpty()) {
 60 ovy      1.1                 Object y = workset.removeFirst();
 61 ovy      1.1 
 62 ovy      1.1                 if (dontCloseOver != null && y!= x &&
 63 ovy      1.1                     dontCloseOver.contains(y)) continue;
 64 ovy      1.1 
 65 ovy      1.1                 for (Iterator j = oldMap.getValues(y).iterator();
 66 ovy      1.1                      j.hasNext(); ) {
 67 ovy      1.1                     Object z = j.next();
 68 ovy      1.1 
 69 ovy      1.1                     if (reachable.add(z) && z!=x) {
 70 ovy      1.1                             workset.addLast(z);
 71 ovy      1.1                         }
 72 ovy      1.1                 }
 73 ovy      1.1             
 74 ovy      1.1             }
 75 ovy      1.1             map.addAll(x, reachable);
 76 ovy      1.1         }
 77 ovy      1.1 
 78 ovy      1.1         return map;
 79 ovy      1.1     }
 80 ovy      1.1 
 81 ovy      1.1 
 82 ovy      1.1     // compute map inverse, as per relevantNodes (i.e, only relevantNodes
 83 ovy      1.1     //   will appear on the right hand side
 84 ovy      1.1     // Only uses iterator from relevantNodes, i.e. other ops can be slow
 85 ovy      1.1     // Relevantnodes can be null to invert the whole map
 86 ovy      1.1      static MultiMap multiMapInvert(MultiMap oldMap, Collection relevantNodes) {
 87 ovy      1.1         MultiMap map = new GenericMultiMap(new AggregateSetFactory());
 88 ovy      1.1 
 89 ovy      1.1         if (relevantNodes == null) relevantNodes = new ArrayList(oldMap.keySet());
 90 ovy      1.1 
 91 ovy      1.1         for (Iterator i = relevantNodes.iterator(); i.hasNext(); ) {
 92 ovy      1.1             Object x = i.next();
 93 ovy      1.1 
 94 ovy      1.1             for (Iterator j = oldMap.getValues(x).iterator(); j.hasNext(); ) {
 95 ovy      1.1                 Object y = j.next();
 96 ovy      1.1                 map.add(y, x);
 97 ovy      1.1 
 98 ovy      1.1             }
 99 ovy      1.1         }
100 ovy      1.1 
101 ovy      1.1         return map;
102 ovy      1.1     }
103 ovy      1.1 
104 ovy      1.1     // computes the union of the mappings of the elements in the collection
105 ovy      1.1     // Only uses iterator from keys, i.e. other ops can be slow
106 ovy      1.1     static Set multiMapUnion(MultiMap map, Collection keys) {
107 ovy      1.1         Set union = new HashSet();
108 ovy      1.1         
109 ovy      1.1         for (Iterator i = keys.iterator(); i.hasNext(); ) {
110 ovy      1.1             Object x = i.next();
111 ovy      1.1             union.addAll(map.getValues(x));
112 ovy      1.1         }
113 ovy      1.1 
114 ovy      1.1         return union;
115 ovy      1.1     }
116 ovy      1.1 
117 ovy      1.1     // filters the given multimap through the given key and value sets
118 ovy      1.1     //   only mappings with key in relevantKeys and values in relValues
119 ovy      1.1     //   are kept
120 ovy      1.1     // one or both of relKeys and relValues can be null, in which case they
121 ovy      1.1     //   will be ignored. also, they can be equal
122 ovy      1.1     // this uses the contains operations of both collections, which should
123 ovy      1.1     //   therefore be fast
124 ovy      1.1     static MultiMap multiMapFilter(MultiMap oldMap, Collection relevantKeys, Collection relevantValues) {
125 ovy      1.1         MultiMap newMap = new GenericMultiMap(new AggregateSetFactory());
126 ovy      1.1 
127 ovy      1.1         for (Iterator it = oldMap.keySet().iterator(); it.hasNext(); ) {
128 ovy      1.1             Object key = it.next();
129 ovy      1.1 
130 ovy      1.1             // add this key?
131 ovy      1.1             if (relevantKeys == null ||
132 ovy      1.1                 relevantKeys.contains(key)) {
133 ovy      1.1 
134 cananian 1.4                 for (Object value : oldMap.getValues(key)) {
135 ovy      1.1                     if (relevantValues == null ||
136 ovy      1.1                         relevantValues.contains(value)) {
137 ovy      1.1                         newMap.add(key, value);
138 ovy      1.1                     }
139 ovy      1.1                 }
140 ovy      1.1             }
141 ovy      1.1         }
142 ovy      1.1 
143 ovy      1.1         return newMap;
144 ovy      1.1     }
145 ovy      1.1 
146 ovy      1.1     // similar to the above, but filters only keys
147 ovy      1.1     // optimized on the assumption that:
148 ovy      1.1     //   relevantKeys is a small subset of oldMap.keySet()
149 ovy      1.1     // uses only the iterator in relevantKeys(), i.e. other ops can be slow
150 ovy      1.1     static MultiMap multiMapFilterKeys(MultiMap oldMap, Collection relevantKeys) {
151 ovy      1.1         MultiMap newMap = new GenericMultiMap(new AggregateSetFactory());
152 ovy      1.1 
153 ovy      1.1         for (Iterator it = relevantKeys.iterator(); it.hasNext(); ) {
154 ovy      1.1             Object key = it.next(); 
155 ovy      1.1             newMap.addAll(key, oldMap.getValues(key));
156 ovy      1.1         }
157 ovy      1.1 
158 ovy      1.1         return newMap;
159 ovy      1.1     }
160 ovy      1.1 
161 ovy      1.1     // add a value for all the specified keys
162 ovy      1.1     static void multiMapAddAll(MultiMap map, Collection keys, Object value) {
163 ovy      1.1         for (Iterator it = keys.iterator(); it.hasNext(); ) {
164 ovy      1.1             Object key = it.next();
165 ovy      1.1             map.add(key, value);
166 ovy      1.1         }
167 ovy      1.1     }
168 ovy      1.1 
169 ovy      1.1     // this does not really belong here...
170 ovy      1.1     // optimized on the assumption that:
171 ovy      1.1     //   - b is a large collection with a fast contains() operation
172 ovy      1.1     //   - a is a rather small collection (does not have to have a fast contains()
173 ovy      1.1 
174 ovy      1.1     static Set intersect(Collection a, Collection b) {
175 ovy      1.1         Set intersection = new HashSet();
176 ovy      1.1 
177 ovy      1.1         for (Iterator i = a.iterator(); i.hasNext(); ) {
178 ovy      1.1             Object x = i.next();
179 ovy      1.1             if (b.contains(x)) {
180 ovy      1.1                 intersection.add(x);
181 ovy      1.1             }
182 ovy      1.1         }
183 ovy      1.1 
184 ovy      1.1         return intersection;
185 ovy      1.1     }
186 ovy      1.1 
187 ovy      1.1     static void ensureSymmetric(MultiMap map) {
188 ovy      1.1         List keyList = new ArrayList(map.keySet());
189 ovy      1.1 
190 ovy      1.1         for (Iterator it = keyList.iterator(); it.hasNext(); ) {
191 ovy      1.1             Object key = it.next();
192 ovy      1.1 
193 ovy      1.1             multiMapAddAll(map, map.getValues(key), key);
194 ovy      1.1         }
195 ovy      1.1     }
196 ovy      1.1 
197 ovy      1.1     // this does not belong here and WILL be deleted
198 ovy      1.1     // new and better code is in MyGraphColorer
199 ovy      1.1     // keeping it around just in case for a few more days
200 ovy      1.1     static Collection computeCompatibleClasses(final MultiMap incompatible) {
201 ovy      1.1 
202 ovy      1.1         // decreasing order of incompatibilites
203 ovy      1.1         Comparator myComparator = new Comparator() {
204 ovy      1.1                 public int compare(Object a, Object b) {
205 ovy      1.1                     int sizeA = incompatible.getValues(a).size();
206 ovy      1.1                     int sizeB = incompatible.getValues(b).size();
207 ovy      1.1 
208 ovy      1.1                     return sizeB - sizeA;
209 ovy      1.1                 }
210 ovy      1.1                 
211 ovy      1.1             };
212 ovy      1.1 
213 ovy      1.1         
214 ovy      1.1         Object[] nodes = incompatible.keySet().toArray();
215 ovy      1.1 
216 ovy      1.1         // sort on decreasing order of #incompatible
217 ovy      1.1         // Arrays.sort(nodes, myComparator);
218 ovy      1.1 
219 ovy      1.1         Collection classes = new LinkedList();
220 ovy      1.1         
221 ovy      1.1         for (int i = 0; i < nodes.length; i++) {
222 ovy      1.1             Object candidate = nodes[i];
223 ovy      1.1             
224 ovy      1.1             boolean assigned = false;
225 ovy      1.1 
226 ovy      1.1             for (Iterator it = classes.iterator(); it.hasNext() && !assigned; ) {
227 ovy      1.1                 Collection members = (Collection) it.next();
228 ovy      1.1                 boolean compatible = true;
229 ovy      1.1                 for (Iterator it2 = members.iterator(); it2.hasNext() && compatible; ) {
230 ovy      1.1                     Object member = it2.next();
231 ovy      1.1 
232 ovy      1.1                     compatible &= !incompatible.contains(candidate, member);
233 ovy      1.1                 }
234 ovy      1.1 
235 ovy      1.1                 if (compatible) {
236 ovy      1.1                     members.add(candidate);
237 ovy      1.1                     assigned = true;
238 ovy      1.1                 }
239 ovy      1.1             }
240 ovy      1.1 
241 ovy      1.1             if (!assigned) {
242 ovy      1.1                 Collection newClass = new LinkedList();
243 ovy      1.1                 newClass.add(candidate);
244 ovy      1.1 
245 ovy      1.1                 classes.add(newClass);
246 ovy      1.1             }
247 ovy      1.1         }
248 ovy      1.1 
249 ovy      1.1         return classes;
250 ovy      1.1     }
251 ovy      1.1 
252 ovy      1.1     // alternate algorithm for above using Felix's UnboundedGraphColoring
253 ovy      1.1     //   this will be deleted too, since that alg performs very poorly
254 ovy      1.1     // currently does not return anything, just prints a result for comparison
255 ovy      1.1     static Collection computeCompatibleClassesAlt(final MultiMap incompatible) {
256 ovy      1.1         Map tokens2nodes = new HashMap();
257 ovy      1.1         SparseGraph graph = new SparseGraph();
258 ovy      1.1 
259 ovy      1.1         // nodes
260 ovy      1.1         for (Iterator it = incompatible.keySet().iterator(); it.hasNext(); ) {
261 ovy      1.1             Object token = it.next();
262 ovy      1.1             SparseNode node = new DefaultSparseNode();
263 ovy      1.1             tokens2nodes.put(token, node);
264 ovy      1.1             graph.addNode(node);
265 ovy      1.1         }
266 ovy      1.1 
267 ovy      1.1         // edges
268 ovy      1.1         for (Iterator it = incompatible.keySet().iterator(); it.hasNext(); ) {
269 ovy      1.1             Object token1 = it.next();
270 ovy      1.1             SparseNode node1 = (SparseNode) tokens2nodes.get(token1);
271 ovy      1.1             for (Iterator it2 = incompatible.getValues(token1).iterator();
272 ovy      1.1                  it2.hasNext(); ) {
273 ovy      1.1                 Object token2 = it2.next();
274 ovy      1.1                 SparseNode node2 = (SparseNode) tokens2nodes.get(token2);
275 ovy      1.1                 graph.makeEdge(node1, node2);
276 ovy      1.1             }
277 ovy      1.1         }
278 ovy      1.1 
279 ovy      1.1         ColorFactory cfactory = new ColorFactory() {
280 ovy      1.1                 protected Color newColor() {
281 ovy      1.1                     return new Color() {
282 ovy      1.1                             };
283 ovy      1.1                 }
284 ovy      1.1             };
285 ovy      1.1         
286 ovy      1.1         UnboundedGraphColorer colorer =
287 ovy      1.1             new UnboundedGraphColorer(new SimpleGraphColorer(),
288 ovy      1.1                                       cfactory);
289 ovy      1.1 
290 ovy      1.1         colorer.findColoring(graph);
291 ovy      1.1 
292 ovy      1.1         System.out.println("*** # classes via alt method: "
293 ovy      1.1                            + cfactory.getColors().size());
294 ovy      1.1 
295 ovy      1.1         return null;
296 ovy      1.1     }        
297 ovy      1.1     
298 ovy      1.1 }