|
1 | 1 | import sys |
2 | 2 | sys.path.insert(0, '..') |
| 3 | +import math |
3 | 4 |
|
4 | 5 | from manim import * |
5 | 6 | from manim_voiceover import VoiceoverScene |
|
8 | 9 |
|
9 | 10 | from scene_utils import setup_scene |
10 | 11 |
|
11 | | -matrices = [ |
12 | | - ('pct20stif', 'Structural Analysis', 1, 2), |
13 | | - ('rw5151', 'Statistics and Mathematics', 1, 5), |
14 | | - ('lpl1', 'Resource Optimization', 2, 4), |
15 | | - ('poli_large', 'Economic Planning', 1, 5), |
16 | | - ('conf5_0-4x4-10', 'Theoretical Quantum Chemistry', 1, 5), |
17 | | -] |
| 12 | + |
| 13 | +def get_chart_config(values): |
| 14 | + """Return y_range, scale factor, and format function based on max value.""" |
| 15 | + max_val = max(values) |
| 16 | + |
| 17 | + if max_val >= 1.0: # Billions scale |
| 18 | + # Round up to nice number for y_max |
| 19 | + y_max = math.ceil(max_val * 1.2) # 20% headroom |
| 20 | + y_step = max(1, y_max // 4) |
| 21 | + scale = 1.0 |
| 22 | + suffix = "B" |
| 23 | + else: # Millions scale (values are in billions, so 0.375 = 375M) |
| 24 | + # Convert to millions for display |
| 25 | + max_millions = max_val * 1000 |
| 26 | + y_max_millions = math.ceil(max_millions / 100) * 100 # Round to nearest 100M |
| 27 | + y_max = y_max_millions / 1000 # Back to billions for chart |
| 28 | + y_step = y_max / 4 |
| 29 | + scale = 1000 # Multiply by 1000 to get millions |
| 30 | + suffix = "M" |
| 31 | + |
| 32 | + return y_max, y_step, scale, suffix |
| 33 | + |
| 34 | + |
| 35 | +def format_value(val, scale, suffix): |
| 36 | + """Format value for bar label.""" |
| 37 | + display_val = val * scale if scale > 1 else val |
| 38 | + if display_val >= 10: |
| 39 | + return f"{int(display_val)}{suffix}" |
| 40 | + else: |
| 41 | + return f"{display_val:.1f}{suffix}" |
| 42 | + |
| 43 | +# Graph labels with edge counts |
| 44 | +GRAPHS = ["Twitter\n1.5B", "web\n1.9B", "kron\n4.2B", "urand\n4.3B"] |
| 45 | + |
| 46 | +# Placeholder benchmark results (billions of edges/second) |
| 47 | +BENCHMARKS = { |
| 48 | + "Breadth First Search": [5.8, 7.1, 7.6, 5.0], |
| 49 | + "PageRank": [0.148, 0.337, 0.217, 0.375], |
| 50 | + "Connected Components": [1.2, 1.1, 1.1, 1.1], |
| 51 | + "Single Source Shortest Path": [0.181, 0.717, 0.257, 0.175], |
| 52 | +} |
| 53 | + |
| 54 | +ALGORITHM_SUMMARIES = { |
| 55 | + "Breadth First Search": "BFS explores a graph level by level, traversing all nodes reachable from a starting node.", |
| 56 | + "PageRank": "PageRank computes the importance of each node based on the structure of incoming links.", |
| 57 | + "Connected Components": "Connected components identifies groups of nodes that are reachable from one another.", |
| 58 | + "Single Source Shortest Path": "SSSP finds the minimum cost path from a source node to all other nodes in a weighted graph.", |
| 59 | +} |
18 | 60 |
|
19 | 61 | class Scene4(VoiceoverScene, Scene): |
20 | 62 | def construct(self): |
21 | 63 | setup_scene(self) |
22 | 64 |
|
| 65 | + title = Text("SuiteSparse GraphBLAS Performance", font_size=36).to_edge(UP) |
| 66 | + y_label = Text("Edges/Second", font_size=20).rotate(PI/2) |
| 67 | + |
23 | 68 | with self.voiceover( |
24 | | - """Sparse graphs and linear algebra play an important |
25 | | - role in many scientific and engineering disciplines, |
26 | | - including:""" |
| 69 | + """SuiteSparse GraphBLAS achieves impressive performance across |
| 70 | + standard graph algorithm benchmarks on four large-scale graphs |
| 71 | + ranging from 1.5 to 4.3 billion edges.""" |
27 | 72 | ): |
28 | | - title = Tex("Graphs are Everywhere").scale(1.5).to_edge(UP) |
29 | 73 | self.play(Write(title)) |
30 | | - self.wait(3) |
31 | 74 |
|
32 | | - for subdir, description, ai, gi in matrices: |
33 | | - matrix_image = ImageMobject(f"../scraped_images/{subdir}/image_{ai}_inv.jpg").scale(1.5).to_edge(LEFT) |
34 | | - graph_image = ImageMobject(f"../scraped_images/{subdir}/image_{gi}.jpg").scale(1.5).to_edge(RIGHT) |
| 75 | + for bench_name, values in BENCHMARKS.items(): |
| 76 | + subtitle = Text(bench_name, font_size=28).next_to(title, DOWN) |
35 | 77 |
|
36 | | - self.play(FadeIn(matrix_image), FadeIn(graph_image)) |
37 | | - with self.voiceover(description): |
38 | | - self.wait(2) |
39 | | - self.play(FadeOut(matrix_image), FadeOut(graph_image)) |
| 78 | + # Get dynamic y-axis configuration based on data scale |
| 79 | + y_max, y_step, scale, suffix = get_chart_config(values) |
40 | 80 |
|
41 | | - self.play(FadeOut(title)) |
| 81 | + # Create chart with zero values initially |
| 82 | + chart = BarChart( |
| 83 | + values=[0, 0, 0, 0], |
| 84 | + bar_names=GRAPHS, |
| 85 | + y_range=[0, y_max, y_step], |
| 86 | + y_length=4, |
| 87 | + x_length=10, |
| 88 | + bar_colors=[BLUE, GREEN, ORANGE, RED], |
| 89 | + ).scale(0.9) |
42 | 90 |
|
43 | | - with self.voiceover( |
44 | | - """In our next video, we will introduce the Python |
45 | | - bindings for GraphBLAS and explore the duality between |
46 | | - matrix multiplication and graph traversal in more |
47 | | - depth. We will also introduce fundamental GraphBLAS |
48 | | - concepts including semirings, accumulation, and |
49 | | - masking.""" |
50 | | - ): |
51 | | - title = Tex("Coming Next").scale(1.5).to_edge(UP) |
52 | | - |
53 | | - bullet_points = BulletedList( |
54 | | - "Install Python library", |
55 | | - "Matrix Multiplication as Breadth First Search.", |
56 | | - "Using Semirings to perform combining operations.", |
57 | | - "Accumulating results as you go.", |
58 | | - "Masking or including only certain values.", |
59 | | - font_size=36 |
60 | | - ) |
61 | | - bullet_points.next_to(title, DOWN, buff=0.5) |
| 91 | + y_label_copy = y_label.copy().next_to(chart, LEFT) |
62 | 92 |
|
63 | | - self.play(Write(title)) |
64 | | - self.play(FadeIn(bullet_points, shift=UP, lag_ratio=0.1)) |
65 | | - self.wait(3) |
| 93 | + with self.voiceover(f"Results for {bench_name}."): |
| 94 | + self.play(Write(subtitle)) |
| 95 | + self.play(Create(chart), FadeIn(y_label_copy)) |
| 96 | + # Animate bars growing from zero to actual values |
| 97 | + self.play(chart.animate.change_bar_values(values)) |
| 98 | + |
| 99 | + # Add value labels above each bar |
| 100 | + bar_labels = VGroup() |
| 101 | + for i, (bar, val) in enumerate(zip(chart.bars, values)): |
| 102 | + label = Text(format_value(val, scale, suffix), font_size=18) |
| 103 | + label.next_to(bar, UP, buff=0.1) |
| 104 | + bar_labels.add(label) |
| 105 | + self.play(FadeIn(bar_labels)) |
| 106 | + self.wait(1) |
| 107 | + |
| 108 | + with self.voiceover(ALGORITHM_SUMMARIES[bench_name]): |
| 109 | + self.wait(0.5) # Chart stays visible during voiceover |
| 110 | + |
| 111 | + self.play(FadeOut(chart), FadeOut(subtitle), FadeOut(y_label_copy), FadeOut(bar_labels)) |
| 112 | + |
| 113 | + self.play(FadeOut(title)) |
| 114 | + self.wait(0.5) |
0 commit comments