Rename REAMDE to README.md
[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]
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 # The associated poset
49 var poset: POSet[E]
50
51 # The linearisation order to visit elements in the poset
52 var order: Array[E] is noinit
53
54 init do
55 extract_core
56 extract_border
57 extract_crown
58 compute_conflicts
59 order = poset.linearize(poset)
60 end
61
62 # Compute the set of elements forming the core of the poset hierarchy.
63 private fun extract_core do
64 core.clear
65 for e in poset do
66 if poset[e].direct_greaters.length > 1 then
67 core.add_all(poset[e].greaters)
68 end
69 end
70 end
71
72 # Compute the set of elements composing the border of the core
73 # Elements belonging to the `border` are removed from the `core`
74 private fun extract_border do
75 border.clear
76 for e in core do
77 if not is_border(e) then continue
78 border.add(e)
79 end
80 for e in border do core.remove(e)
81 end
82
83 private fun is_border(e: E): Bool do
84 for child in poset[e].direct_smallers do
85 if core.has(child) then return false
86 end
87 return true
88 end
89
90 # Compute the set of elements belonging to the crown of the inheritance hierarchy.
91 private fun extract_crown do
92 crown.clear
93 for e in poset do
94 if not core.has(e) and not border.has(e) then crown.add(e)
95 end
96 end
97
98 # Check for conflict in the core.
99 # Start from border and tag every ancestors
100 private fun compute_conflicts do
101 conflicts.clear
102 for e in border do add_conflicts(poset[e].greaters)
103 end
104
105 private fun add_conflict(e, o: E) do
106 if not conflicts.has_key(e) then conflicts[e] = new HashSet[E]
107 if not conflicts.has_key(o) then conflicts[o] = new HashSet[E]
108 conflicts[e].add(o)
109 conflicts[o].add(e)
110 end
111
112 private fun add_conflicts(es: Collection[E]) do
113 for e1 in es do
114 for e2 in es do add_conflict(e1, e2)
115 end
116 end
117
118 # Used for debugging only
119 fun pretty_print do
120 #print "core: {core.join(" ")} ({core.length})"
121 #print "border: {border.join(" ")} ({border.length})"
122 #print "crown: {crown.join(" ")} ({crown.length})"
123 print "conflicts:"
124 for e, c in conflicts do print " {e or else "NULL"}: {c.join(" ")}"
125 end
126 end
127
128 redef class POSet[E]
129 fun to_conflict_graph: POSetConflictGraph[E] do return new POSetConflictGraph[E](self)
130 end
131
132 # Colorize elements from a POSet
133 # Two elements from a POSet cannot have the same color if they share common subelements
134 #
135 # Example:
136 #
137 # ~~~raw
138 # A
139 # / | \
140 # / | \
141 # B C D
142 # | /| |
143 # | / | |
144 # |/ | |
145 # E F G
146 # |
147 # H
148 # ~~~
149 #
150 # Conflicts:
151 #
152 # * A: {B, C, D, E, F, G, H}
153 # * B: {A, C, E, H}
154 # * C: {A, E, H, F}
155 # * D: {A, G}
156 # * E: {A, B, C, H}
157 # * F: {A, C}
158 # * G: {A, D}
159 # * H: {A, B, C, E}
160 #
161 # Possible colors:
162 #
163 # * A:0, B:1, C: 2, D: 1, E: 3, F:3, G:2, H:4
164 #
165 # see: Ducournau, R. (2011).
166 # Coloring, a versatile technique for implementing object-oriented languages.
167 # Software: Practice and Experience, 41(6), 627–659.
168 class POSetColorer[E: Object]
169
170 # Is the poset already colored?
171 var is_colored = false
172
173 # Resulting ids
174 # REQUIRE: is_colored
175 fun ids: Map[E, Int] do
176 assert is_colored
177 return ids_cache
178 end
179 private var ids_cache = new HashMap[E, Int]
180
181 # Resulting colors
182 # REQUIRE: is_colored
183 fun colors: Map[E, Int] do
184 assert is_colored
185 return colors_cache
186 end
187 private var colors_cache = new HashMap[E, Int]
188
189 # REQUIRE: is_colored
190 fun poset: POSet[E] do
191 assert is_colored
192 return poset_cache
193 end
194 private var poset_cache: POSet[E] is noinit
195
196 # REQUIRE: is_colored
197 fun conflicts: Map[E, Set[E]] do
198 assert is_colored
199 return conflicts_cache
200 end
201 private var conflicts_cache: Map[E, Set[E]] is noinit
202
203 private var graph: POSetConflictGraph[E] is noinit
204
205 # Start coloring on given POSet
206 fun colorize(poset: POSet[E]) do
207 poset_cache = poset
208 graph = new POSetConflictGraph[E](poset)
209 allocate_ids
210 compute_colors
211 conflicts_cache = graph.conflicts
212 is_colored = true
213 end
214
215 private fun allocate_ids do
216 ids_cache.clear
217 var elements = new HashSet[E].from(poset_cache.to_a)
218 for e in poset_cache.linearize(elements) do
219 ids_cache[e] = ids_cache.length
220 end
221 end
222
223 # Colorize core, border and crown in that order
224 private fun compute_colors do
225 colors_cache.clear
226 colorize_core
227 colorize_set(graph.border)
228 colorize_set(graph.crown)
229 end
230
231 # Core elements cannot have the same color than:
232 # * one of their parents
233 # * one of their conflicting elements
234 private fun colorize_core do
235 for e in poset_cache.linearize(graph.core) do
236 var color = min_color(e)
237 var conflicts = graph.conflicts[e]
238 while not is_color_free(color, conflicts) do
239 color += 1
240 end
241 colors_cache[e] = color
242 end
243 end
244
245 # Other elements inherit color fron their direct parents
246 private fun colorize_set(set: Set[E]) do
247 for e in poset_cache.linearize(set) do colors_cache[e] = min_color(e)
248 end
249
250 # Get the next minimal color from direct parents
251 private fun min_color(e: E): Int do
252 var max_color = -1
253 for p in poset_cache[e].direct_greaters do
254 if not colors_cache.has_key(p) then continue
255 var color = colors_cache[p]
256 if color > max_color then max_color = color
257 end
258 return max_color + 1
259 end
260
261 private fun is_color_free(color: Int, set: Collection[E]): Bool do
262 for e in set do
263 if colors_cache.has_key(e) and colors_cache[e] == color then return false
264 end
265 return true
266 end
267
268 # Used for debugging only
269 fun pretty_print do
270 print "ids:"
271 for e, id in ids do print " {e}: {id}"
272 print "colors:"
273 for e, c in colors do print " {e}: {c}"
274 end
275 end
276
277 # Colorize elements `E` introduced by holders `H` in a `POSet`.
278 #
279 # Two elements cannot have the same color if they are introduced or inherited by a same holder.
280 class POSetGroupColorer[H: Object, E: Object]
281
282 # The associated conflict graph containing the poset.
283 #
284 # The conflict graph is used instead of the original poset so that the conflict graph can be reused
285 # in different coloration based on the same poset.
286 var graph: POSetConflictGraph[H]
287
288 # The elements to color.
289 #
290 # For each holder, the collection of introduced elements is given.
291 #
292 # A single element must not be introduced in more than one holder.
293 var buckets: Map[H, Collection[E]]
294
295 # The associated poset.
296 #
297 # alias for `graph.poset`
298 fun poset: POSet[H] do return graph.poset
299
300 # The resulting coloring
301 #
302 # Each element from buckets is associated to its own color
303 var colors: Map[E, Int] is lazy do
304 for h in graph.poset do
305 used_colors[h] = new HashSet[Int]
306 end
307 compute_colors
308 return colors_cache
309 end
310
311 # Resulting colors
312 private var colors_cache = new HashMap[E, Int]
313
314 # Set of known used colors
315 private var used_colors = new HashMap[H, HashSet[Int]]
316
317 # Build table layout of elements `E` for the holder `h`.
318 #
319 # `null` is used to fill places without elements (holes).
320 fun build_layout(h: H): Array[nullable E]
321 do
322 var table = new Array[nullable E]
323 for s in poset[h].greaters do
324 var bucket = buckets.get_or_null(s)
325 if bucket == null then continue
326 for e in bucket do
327 var color = colors[e]
328 if table.length <= color then
329 for i in [table.length .. color[ do
330 table[i] = null
331 end
332 else
333 assert table[color] == null else print "in {h}, for {color}: {table[color] or else ""} vs {e}"
334 end
335 table[color] = e
336 end
337 end
338 return table
339 end
340
341 # Colorize core, border and crown in that order
342 private fun compute_colors do
343 colors_cache.clear
344 colorize_core
345 colorize_set(graph.border)
346 colorize_set(graph.crown)
347 end
348
349 # Core elements cannot have the same color than:
350 # * one of their parents
351 # * one of their conflicting elements
352 private fun colorize_core do
353 for h in graph.order do
354 if not graph.core.has(h) then continue
355
356 var color = inherit_color(h)
357 var mincolor = color
358 var bucket = buckets.get_or_null(h)
359 if bucket == null then continue
360 var conflicts = graph.conflicts[h]
361 var parents = poset[h].greaters
362 for e in bucket do
363 color = next_free_color(color, parents)
364 color = next_free_color(color, conflicts)
365 colors_cache[e] = color
366 used_colors[h].add color
367 #print "{h}: color[{color}] <- {e}"
368 if mincolor == color then mincolor += 1
369 color += 1
370 end
371 min_colors[h] = mincolor
372 end
373 end
374
375 # Other elements inherit color from their direct parents
376 private fun colorize_set(set: Set[H]) do
377 for h in graph.order do
378 if not set.has(h) then continue
379
380 var color = inherit_color(h)
381 var mincolor = color
382 var bucket = buckets.get_or_null(h)
383 if bucket == null then continue
384 var parents = poset[h].greaters
385 for e in bucket do
386 color = next_free_color(color, parents)
387 colors_cache[e] = color
388 used_colors[h].add color
389 #print "{h}: color[{color}] <- {e} (set)"
390 if mincolor == color then mincolor += 1
391 color += 1
392 end
393 min_colors[h] = mincolor
394 end
395 end
396
397 # Get the first available free color.
398 private fun inherit_color(h: H): Int
399 do
400 var res = 0
401 for p in poset[h].direct_greaters do
402 var m = min_colors[p]
403 if m > res then res = m
404 end
405 min_colors[h] = res
406 return res
407 end
408
409 # The first available color for each holder.
410 #
411 # Is used by children to start their coloring.
412 #
413 # Is updated at the end of a coloring step.
414 private var min_colors = new HashMap[H, Int]
415
416 private fun next_free_color(color: Int, set: Collection[H]): Int do
417 loop
418 for h in set do
419 if used_colors[h].has(color) then
420 #print "\tin {h}, {color} is used"
421 color += 1
422 continue label
423 end
424 end
425 break
426 end label
427 return color
428 end
429
430 # Used for debugging only
431 fun pretty_print do
432 print "colors:"
433 for e, c in colors do print " {e}: {c}"
434 end
435 end
436
437 # Colorize a collection of buckets
438 # Two elements cannot have the same color if they both appear in the same bucket
439 # No coloring order is garantied
440 #
441 # Example:
442 # buckets[A] = {x1, x2}
443 # buckets[B] = {x1, x3, x4}
444 # buckets[C] = {x2, x3}
445 # Conflicts:
446 # x1: {x2, x3, x4}
447 # x2: {x1, x3}
448 # x3: {x1, x2, x4}
449 # x4: {x1, x3}
450 # Possible colors:
451 # x1: 0, x2: 1, x3: 2, x4: 1
452 class BucketsColorer[H: Object, E: Object]
453 private var colors = new HashMap[E, Int]
454 private var conflicts = new HashMap[E, Set[E]]
455
456 # Start bucket coloring
457 fun colorize(buckets: Map[H, Set[E]]): Map[E, Int] do
458 compute_conflicts(buckets)
459 var min_color = 0
460 for holder, hbuckets in buckets do
461 for bucket in hbuckets do
462 if colors.has_key(bucket) then continue
463 var color = min_color
464 while not is_color_free(bucket, color) do
465 color += 1
466 end
467 colors[bucket] = color
468 color = min_color
469 end
470 end
471 return colors
472 end
473
474 private fun is_color_free(bucket: E, color: Int): Bool do
475 if conflicts.has_key(bucket) then
476 for other in conflicts[bucket] do
477 if colors.has_key(other) and colors[other] == color then return false
478 end
479 end
480 return true
481 end
482
483 private fun compute_conflicts(buckets: Map[H, Set[E]]) do
484 conflicts.clear
485 for holder, hbuckets in buckets do
486 for bucket in hbuckets do
487 if not conflicts.has_key(bucket) then conflicts[bucket] = new HashSet[E]
488 for obucket in hbuckets do
489 if obucket == bucket then continue
490 if not conflicts.has_key(obucket) then conflicts[obucket] = new HashSet[E]
491 conflicts[bucket].add(obucket)
492 conflicts[obucket].add(bucket)
493 end
494 end
495 end
496 end
497 end
498
499 # Colorize a collection of buckets using a poset and a conflict graph
500 # Two elements cannot have the same color if they both appear in the same bucket
501 # The use of a POSet hierarchy optimize the coloring
502 # Buckets elements are colored using linearization order starting
503 class POSetBucketsColorer[H: Object, E: Object]
504 private var colors = new HashMap[E, Int]
505 private var poset: POSet[H]
506 private var conflicts: Map[H, Set[H]]
507
508 # Colorize buckets using the POSet and conflict graph
509 fun colorize(buckets: Map[H, Set[E]]): Map[E, Int] do
510 colors.clear
511 for h in poset.linearize(buckets.keys) do
512 var color = min_color(poset[h].direct_greaters, buckets)
513 for bucket in buckets[h] do
514 if colors.has_key(bucket) then continue
515 while not is_color_free(color, h, buckets) do color += 1
516 colors[bucket] = color
517 color += 1
518 end
519 end
520 return colors
521 end
522
523 # Get the next available color considering used colors by other buckets
524 private fun min_color(others: Collection[H], buckets: Map[H, Set[E]]): Int do
525 var min = -1
526 for holder in others do
527 var color = max_color(holder, buckets)
528 if color > min then min = color
529 end
530 return min + 1
531 end
532
533 # Return the max color used by a class
534 private fun max_color(holder: H, buckets: Map[H, Set[E]]): Int do
535 var max = -1
536 for bucket in buckets[holder] do
537 if not colors.has_key(bucket) then continue
538 var color = colors[bucket]
539 if color > max then max = color
540 end
541 return max
542 end
543
544 # Check if the color is free for this holder
545 private fun is_color_free(color: Int, holder: H, buckets: Map[H, Set[E]]): Bool do
546 if not conflicts.has_key(holder) then return true
547 for conflict in conflicts[holder] do
548 for bucket in buckets[conflict] do
549 if not colors.has_key(bucket) then continue
550 if colors[bucket] == color then return false
551 end
552 end
553 return true
554 end
555 end