@@ -523,8 +523,10 @@ def export_graph(self, filepath=None):
523
523
for dep in lcd :
524
524
lcd_line_numbers [dep ] = [x .line_number for x , lat in lcd [dep ]["dependencies" ]]
525
525
# add color scheme
526
- graph .graph ["node" ] = {"colorscheme" : "set312" }
527
- graph .graph ["edge" ] = {"colorscheme" : "set312" }
526
+ graph .graph ["node" ] = {"colorscheme" : "spectral9" }
527
+ graph .graph ["edge" ] = {"colorscheme" : "spectral9" }
528
+ min_color = 2
529
+ available_colors = 8
528
530
529
531
# create LCD edges
530
532
for dep in lcd_line_numbers :
@@ -543,21 +545,14 @@ def export_graph(self, filepath=None):
543
545
for n in cp :
544
546
graph .nodes [n .line_number ]["instruction_form" ].latency_cp = n .latency_cp
545
547
546
- # color CP and LCD
548
+ # Make the critical path bold.
547
549
for n in graph .nodes :
548
550
if n in cp_line_numbers :
549
551
# graph.nodes[n]['color'] = 1
550
552
graph .nodes [n ]["style" ] = "bold"
551
553
graph .nodes [n ]["penwidth" ] = 4
552
- for col , dep in enumerate (lcd ):
553
- if n in lcd_line_numbers [dep ]:
554
- if "style" not in graph .nodes [n ]:
555
- graph .nodes [n ]["style" ] = "filled"
556
- elif ",filled" not in graph .nodes [n ]["style" ]:
557
- graph .nodes [n ]["style" ] += ",filled"
558
- graph .nodes [n ]["fillcolor" ] = 2 + col % 11
559
554
560
- # color edges
555
+ # Make critical path edges bold.
561
556
for e in graph .edges :
562
557
if (
563
558
graph .nodes [e [0 ]]["instruction_form" ].line_number in cp_line_numbers
@@ -571,12 +566,81 @@ def export_graph(self, filepath=None):
571
566
if bold_edge :
572
567
graph .edges [e ]["style" ] = "bold"
573
568
graph .edges [e ]["penwidth" ] = 3
574
- for dep in lcd_line_numbers :
575
- if (
576
- graph .nodes [e [0 ]]["instruction_form" ].line_number in lcd_line_numbers [dep ]
577
- and graph .nodes [e [1 ]]["instruction_form" ].line_number in lcd_line_numbers [dep ]
578
- ):
579
- graph .edges [e ]["color" ] = graph .nodes [e [1 ]]["fillcolor" ]
569
+
570
+ # Color the cycles created by loop-carried dependencies, longest first, never recoloring
571
+ # any node, so that the longest LCD and most long chains that are involved in the loop are
572
+ # legible.
573
+ for i , dep in enumerate (sorted (lcd , key = lambda dep : - lcd [dep ]["latency" ])):
574
+ # For cycles that are broken by already-colored (longer) cycles, the color need not be
575
+ # the same for each yet-uncolored arc.
576
+ # Do not use the same color for such an arc as for the cycles that delimit it. This is
577
+ # always possible with 3 colors, as each arc is only adjacent to the preceding and
578
+ # following interrupting cycles.
579
+ # Since we color edges as well as nodes, there would be room for a more interesting
580
+ # graph coloring problem: we could avoid having unrelated arcs with the same color
581
+ # meeting at the same vertex, and retain the same color between arcs of the same cycle
582
+ # that are interrupted by a single vertex. We mostly ignore this problem.
583
+
584
+ # The longest cycle will always have color 1, the second longest cycle will always have
585
+ # color 2 except where it overlaps with with the longest cycle, etc.; for arcs that are
586
+ # part of short cycles, the colors will be less predictable.
587
+ default_color = min_color + i % available_colors
588
+ arc = []
589
+ arc_source = lcd_line_numbers [dep ][- 1 ]
590
+ arcs = []
591
+ for n in lcd_line_numbers [dep ]:
592
+ if "fillcolor" in graph .nodes [n ]:
593
+ arcs .append ((arc , (arc_source , n )))
594
+ arc = []
595
+ arc_source = n
596
+ else :
597
+ arc .append (n )
598
+ if not arcs : # Unconstrained cycle.
599
+ arcs .append ((arc , tuple ()))
600
+ else :
601
+ arcs .append ((arc , (arc_source , lcd_line_numbers [dep ][0 ])))
602
+ # Try to color the whole cycle with its default color, then with a single color, then
603
+ # with different colors by arc, preferring the default.
604
+ forbidden_colors = set (
605
+ graph .nodes [n ]["fillcolor" ] for arc , extremities in arcs for n in extremities
606
+ if "fillcolor" in graph .nodes [n ]
607
+ )
608
+ global_color = None
609
+ if default_color not in forbidden_colors :
610
+ global_color = default_color
611
+ elif len (forbidden_colors ) < available_colors :
612
+ global_color = next (
613
+ c for c in range (min_color , min_color + available_colors + 1 )
614
+ if c not in forbidden_colors
615
+ )
616
+ for arc , extremities in arcs :
617
+ if global_color :
618
+ color = global_color
619
+ else :
620
+ color = default_color
621
+ while color in (graph .nodes [n ].get ("fillcolor" ) for n in extremities ):
622
+ color = min_color + (color + 1 ) % available_colors
623
+ for n in arc :
624
+ if "style" not in graph .nodes [n ]:
625
+ graph .nodes [n ]["style" ] = "filled"
626
+ else :
627
+ graph .nodes [n ]["style" ] += ",filled"
628
+ graph .nodes [n ]["fillcolor" ] = color
629
+ if extremities :
630
+ (source , sink ) = extremities
631
+ else :
632
+ source = sink = arc [0 ]
633
+ arc = arc [1 :]
634
+ for u , v in zip ([source ] + arc , arc + [sink ]):
635
+ # The backward edge of the cycle is represented as the corresponding forward
636
+ # edge with the attribute dir=back.
637
+ edge = graph .edges [v , u ] if (v , u ) in graph .edges else graph .edges [u , v ]
638
+ if arc :
639
+ if "color" in edge :
640
+ raise AssertionError (
641
+ f"Recoloring { u } ->{ v } in arc ({ source } ) { arc } ({ sink } ) of { dep } "
642
+ )
643
+ edge ["color" ] = color
580
644
581
645
# rename node from [idx] to [idx mnemonic] and add shape
582
646
mapping = {}
0 commit comments