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