coloring: add `POSetConflictGraph:order`
[nit.git] / src / compiler / coloring.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 module coloring
16
17 import poset
18
19 # Build a conflict graph from a POSet
20 class POSetConflictGraph[E: Object]
21
22 # Core is composed by:
23 # * elements that have mutiple direct parents
24 # * parents of elements that have multiple direct parents
25 # REQUIRE: is_colored
26 var core = new HashSet[E]
27
28 # Border is composed by minimal elements of the core:
29 # * that have multiple direct parents
30 # * but whose subelements are all in single inheritance
31 # REQUIRE: is_colored
32 var border = new HashSet[E]
33
34 # The crown is composed by the elements that are:
35 # * not part of the core nor the border
36 # * are in single inheritance
37 # REQUIRE: is_colored
38 var crown = new HashSet[E]
39
40 # Conflict graph of the POSet
41 # Elements X and Y are in conflict if either:
42 # * X and Y are the same element
43 # * Y is a subelement of X
44 # * X and Y have common sub elements
45 # REQUIRE: is_colored
46 var conflicts = new HashMap[E, Set[E]]
47
48 var poset: POSet[E]
49
50 # The linearisation order to visit elements in the poset
51 var order: Array[E] is noinit
52
53 init do
54 extract_core
55 extract_border
56 extract_crown
57 compute_conflicts
58 order = poset.linearize(poset)
59 end
60
61 # Compute the set of elements forming the core of the poset hierarchy.
62 private fun extract_core do
63 core.clear
64 for e in poset do
65 if poset[e].direct_greaters.length > 1 then
66 core.add_all(poset[e].greaters)
67 end
68 end
69 end
70
71 # Compute the set of elements composing the border of the core
72 # Elements belonging to the `border` are removed from the `core`
73 private fun extract_border do
74 border.clear
75 for e in core do
76 if not is_border(e) then continue
77 border.add(e)
78 end
79 for e in border do core.remove(e)
80 end
81
82 private fun is_border(e: E): Bool do
83 for child in poset[e].direct_smallers do
84 if core.has(child) then return false
85 end
86 return true
87 end
88
89 # Compute the set of elements belonging to the crown of the inheritance hierarchy.
90 private fun extract_crown do
91 crown.clear
92 for e in poset do
93 if not core.has(e) and not border.has(e) then crown.add(e)
94 end
95 end
96
97 # Check for conflict in the core.
98 # Start from border and tag every ancestors
99 private fun compute_conflicts do
100 conflicts.clear
101 for e in border do add_conflicts(poset[e].greaters)
102 end
103
104 private fun add_conflict(e, o: E) do
105 if not conflicts.has_key(e) then conflicts[e] = new HashSet[E]
106 if not conflicts.has_key(o) then conflicts[o] = new HashSet[E]
107 conflicts[e].add(o)
108 conflicts[o].add(e)
109 end
110
111 private fun add_conflicts(es: Collection[E]) do
112 for e1 in es do
113 for e2 in es do add_conflict(e1, e2)
114 end
115 end
116
117 # Used for debugging only
118 fun pretty_print do
119 #print "core: {core.join(" ")} ({core.length})"
120 #print "border: {border.join(" ")} ({border.length})"
121 #print "crown: {crown.join(" ")} ({crown.length})"
122 print "conflicts:"
123 for e, c in conflicts do print " {e}: {c.join(" ")}"
124 end
125 end
126
127 # Colorize elements from a POSet
128 # Two elements from a POSet cannot have the same color if they share common subelements
129 #
130 # Example:
131 #
132 # ~~~raw
133 # A
134 # / | \
135 # / | \
136 # B C D
137 # | /| |
138 # | / | |
139 # |/ | |
140 # E F G
141 # |
142 # H
143 # ~~~
144 #
145 # Conflicts:
146 #
147 # * A: {B, C, D, E, F, G, H}
148 # * B: {A, C, E, H}
149 # * C: {A, E, H, F}
150 # * D: {A, G}
151 # * E: {A, B, C, H}
152 # * F: {A, C}
153 # * G: {A, D}
154 # * H: {A, B, C, E}
155 #
156 # Possible colors:
157 #
158 # * A:0, B:1, C: 2, D: 1, E: 3, F:3, G:2, H:4
159 #
160 # see: Ducournau, R. (2011).
161 # Coloring, a versatile technique for implementing object-oriented languages.
162 # Software: Practice and Experience, 41(6), 627–659.
163 class POSetColorer[E: Object]
164
165 # Is the poset already colored?
166 var is_colored = false
167
168 # Resulting ids
169 # REQUIRE: is_colored
170 fun ids: Map[E, Int] do
171 assert is_colored
172 return ids_cache
173 end
174 private var ids_cache = new HashMap[E, Int]
175
176 # Resulting colors
177 # REQUIRE: is_colored
178 fun colors: Map[E, Int] do
179 assert is_colored
180 return colors_cache
181 end
182 private var colors_cache = new HashMap[E, Int]
183
184 # REQUIRE: is_colored
185 fun poset: POSet[E] do
186 assert is_colored
187 return poset_cache
188 end
189 private var poset_cache: POSet[E] is noinit
190
191 # REQUIRE: is_colored
192 fun conflicts: Map[E, Set[E]] do
193 assert is_colored
194 return conflicts_cache
195 end
196 private var conflicts_cache: Map[E, Set[E]] is noinit
197
198 private var graph: POSetConflictGraph[E] is noinit
199
200 # Start coloring on given POSet
201 fun colorize(poset: POSet[E]) do
202 poset_cache = poset
203 graph = new POSetConflictGraph[E](poset)
204 allocate_ids
205 compute_colors
206 conflicts_cache = graph.conflicts
207 is_colored = true
208 end
209
210 private fun allocate_ids do
211 ids_cache.clear
212 var elements = new HashSet[E].from(poset_cache.to_a)
213 for e in poset_cache.linearize(elements) do
214 ids_cache[e] = ids_cache.length
215 end
216 end
217
218 # Colorize core, border and crown in that order
219 private fun compute_colors do
220 colors_cache.clear
221 colorize_core
222 colorize_set(graph.border)
223 colorize_set(graph.crown)
224 end
225
226 # Core elements cannot have the same color than:
227 # * one of their parents
228 # * one of their conflicting elements
229 private fun colorize_core do
230 for e in poset_cache.linearize(graph.core) do
231 var color = min_color(e)
232 var conflicts = graph.conflicts[e]
233 while not is_color_free(color, conflicts) do
234 color += 1
235 end
236 colors_cache[e] = color
237 end
238 end
239
240 # Other elements inherit color fron their direct parents
241 private fun colorize_set(set: Set[E]) do
242 for e in poset_cache.linearize(set) do colors_cache[e] = min_color(e)
243 end
244
245 # Get the next minimal color from direct parents
246 private fun min_color(e: E): Int do
247 var max_color = -1
248 for p in poset_cache[e].direct_greaters do
249 if not colors_cache.has_key(p) then continue
250 var color = colors_cache[p]
251 if color > max_color then max_color = color
252 end
253 return max_color + 1
254 end
255
256 private fun is_color_free(color: Int, set: Collection[E]): Bool do
257 for e in set do
258 if colors_cache.has_key(e) and colors_cache[e] == color then return false
259 end
260 return true
261 end
262
263 # Used for debugging only
264 fun pretty_print do
265 print "ids:"
266 for e, id in ids do print " {e}: {id}"
267 print "colors:"
268 for e, c in colors do print " {e}: {c}"
269 end
270 end
271
272 # Colorize a collection of buckets
273 # Two elements cannot have the same color if they both appear in the same bucket
274 # No coloring order is garantied
275 #
276 # Example:
277 # buckets[A] = {x1, x2}
278 # buckets[B] = {x1, x3, x4}
279 # buckets[C] = {x2, x3}
280 # Conflicts:
281 # x1: {x2, x3, x4}
282 # x2: {x1, x3}
283 # x3: {x1, x2, x4}
284 # x4: {x1, x3}
285 # Possible colors:
286 # x1: 0, x2: 1, x3: 2, x4: 1
287 class BucketsColorer[H: Object, E: Object]
288 private var colors = new HashMap[E, Int]
289 private var conflicts = new HashMap[E, Set[E]]
290
291 # Start bucket coloring
292 fun colorize(buckets: Map[H, Set[E]]): Map[E, Int] do
293 compute_conflicts(buckets)
294 var min_color = 0
295 for holder, hbuckets in buckets do
296 for bucket in hbuckets do
297 if colors.has_key(bucket) then continue
298 var color = min_color
299 while not is_color_free(bucket, color) do
300 color += 1
301 end
302 colors[bucket] = color
303 color = min_color
304 end
305 end
306 return colors
307 end
308
309 private fun is_color_free(bucket: E, color: Int): Bool do
310 if conflicts.has_key(bucket) then
311 for other in conflicts[bucket] do
312 if colors.has_key(other) and colors[other] == color then return false
313 end
314 end
315 return true
316 end
317
318 private fun compute_conflicts(buckets: Map[H, Set[E]]) do
319 conflicts.clear
320 for holder, hbuckets in buckets do
321 for bucket in hbuckets do
322 if not conflicts.has_key(bucket) then conflicts[bucket] = new HashSet[E]
323 for obucket in hbuckets do
324 if obucket == bucket then continue
325 if not conflicts.has_key(obucket) then conflicts[obucket] = new HashSet[E]
326 conflicts[bucket].add(obucket)
327 conflicts[obucket].add(bucket)
328 end
329 end
330 end
331 end
332 end
333
334 # Colorize a collection of buckets using a poset and a conflict graph
335 # Two elements cannot have the same color if they both appear in the same bucket
336 # The use of a POSet hierarchy optimize the coloring
337 # Buckets elements are colored using linearization order starting
338 class POSetBucketsColorer[H: Object, E: Object]
339 private var colors = new HashMap[E, Int]
340 private var poset: POSet[H]
341 private var conflicts: Map[H, Set[H]]
342
343 # Colorize buckets using the POSet and conflict graph
344 fun colorize(buckets: Map[H, Set[E]]): Map[E, Int] do
345 colors.clear
346 for h in poset.linearize(buckets.keys) do
347 var color = min_color(poset[h].direct_greaters, buckets)
348 for bucket in buckets[h] do
349 if colors.has_key(bucket) then continue
350 while not is_color_free(color, h, buckets) do color += 1
351 colors[bucket] = color
352 color += 1
353 end
354 end
355 return colors
356 end
357
358 # Get the next available color considering used colors by other buckets
359 private fun min_color(others: Collection[H], buckets: Map[H, Set[E]]): Int do
360 var min = -1
361 for holder in others do
362 var color = max_color(holder, buckets)
363 if color > min then min = color
364 end
365 return min + 1
366 end
367
368 # Return the max color used by a class
369 private fun max_color(holder: H, buckets: Map[H, Set[E]]): Int do
370 var max = -1
371 for bucket in buckets[holder] do
372 if not colors.has_key(bucket) then continue
373 var color = colors[bucket]
374 if color > max then max = color
375 end
376 return max
377 end
378
379 # Check if the color is free for this holder
380 private fun is_color_free(color: Int, holder: H, buckets: Map[H, Set[E]]): Bool do
381 if not conflicts.has_key(holder) then return true
382 for conflict in conflicts[holder] do
383 for bucket in buckets[conflict] do
384 if not colors.has_key(bucket) then continue
385 if colors[bucket] == color then return false
386 end
387 end
388 return true
389 end
390 end