Zip and Skip Tries
Loading...
Searching...
No Matches
plot_synthetic.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2"""
3Plotting script for synthetic benchmark data.
4
5This script generates plots from benchmark data collected using synthetic_benchmark.cu.
6It creates various plots comparing the performance of different trie implementations
7on synthetic data with controlled properties, focusing on construction and search times.
8
9The script reads CSV files from the data-synthetic directory and generates plots in the
10figures directory. It supports plotting construction time and search time against
11different parameters (number of keys, key length, LCP length).
12
13The plots include trend lines with log-log regression to analyze asymptotic behavior.
14"""
15
16from enum import Enum, auto, unique
17import matplotlib.pyplot as plt
18
19from pathlib import Path
20from collections import defaultdict
21from functools import cache
22
23from typing import NamedTuple
24
25import numpy as np
26
27import csv
28import socket
29import warnings
30
31DATA_DIRECTORY = Path('data-synthetic')
32HOSTNAME = socket.gethostname()
33MAX_WORD_LENGTH = 2**22
34CONSTRUCTION_FILENAME = 'construction-data'
35REMOVAL_FILENAME = 'removal-data'
36SEARCH_FILENAME = 'search-data'
37START_PARALLEL = 1
38
39FIGURE_DIRECTORY = Path('figures')
40FIGURE_DIRECTORY.mkdir(parents=True, exist_ok=True)
41
42OKABE_COLORS = ['#000000', '#E69F00', '#56B4E9', '#009E73', '#F0E442', '#0072B2', '#D55E00', '#CC79A7']
43plt.rcParams['axes.prop_cycle'] = plt.cycler(color=OKABE_COLORS) # type: ignore
44plt.rcParams['text.usetex'] = True
45
46def get_dpi(savefig: bool):
47 """
48 Set the DPI (dots per inch) for figures based on whether they will be saved or displayed.
49
50 Args:
51 savefig: If True, use higher DPI (600) for saved figures, otherwise use lower DPI (300) for display
52 """
53 plt.rcParams['figure.dpi'] = 600 if savefig else 300
54
55# Plot configuration
56POINT_SIZE = 1 # Default size for data points in plots
57
58@unique
59class DataType(Enum):
60 """
61 Enumeration of benchmark data types.
62
63 Used to specify which type of benchmark data to load and plot.
64 """
65 CONSTRUCTION = auto() # Construction time benchmarks
66 REMOVAL = auto() # Removal time benchmarks
67 SEARCH = auto() # Search time benchmarks
68
69def get_file(method_name: str, data_type: DataType) -> Path:
70 """
71 Get the file path for benchmark data based on method name and data type.
72
73 Args:
74 method_name: Name of the method/implementation being benchmarked
75 data_type: Type of benchmark data (construction, removal, or search)
76
77 Returns:
78 Path object pointing to the CSV file containing the benchmark data
79 """
80
81 match data_type:
82 case DataType.CONSTRUCTION:
83 return DATA_DIRECTORY / f'{HOSTNAME}-{CONSTRUCTION_FILENAME}-{method_name}.csv'
84 case DataType.REMOVAL:
85 return DATA_DIRECTORY / f'{HOSTNAME}-{REMOVAL_FILENAME}-{method_name}.csv'
86 case DataType.SEARCH:
87 return DATA_DIRECTORY / f'{HOSTNAME}-{SEARCH_FILENAME}-{method_name}.csv'
88
89class FitLine(NamedTuple):
90 """
91 Represents a fitted trend line for log-log plots.
92
93 Stores the parameters of a linear regression on log-transformed data,
94 as well as the points to plot and the R² value indicating goodness of fit.
95 """
96 m: float # Slope of the fitted line in log-log space
97 b: float # Y-intercept of the fitted line in log-log space
98 x: tuple[int, ...] # X coordinates for plotting the trend line
99 y: tuple[float, ...] # Y coordinates for plotting the trend line
100 r2: float # R² value indicating goodness of fit
101
102def binary_search(x: list[int], key: int) -> int:
103 """
104 Perform binary search to find the insertion point for key in a sorted list.
105
106 Used to find the index where data points should be skipped when fitting trend lines.
107
108 Args:
109 x: Sorted list of integers to search in
110 key: Value to find the insertion point for
111
112 Returns:
113 Index where key should be inserted to maintain sorted order
114 """
115
116 low = 0
117 high = len(x)
118
119 while low < high:
120 mid = (high + low) // 2
121
122 if x[mid] < key:
123 low = mid + 1
124 else:
125 high = mid
126
127 return low
128
129def fit_log_line(x: list[int], y: list[float], skip_until: int = 0, all_points: bool = False, add_next: list[int] = []) -> FitLine | None:
130 """
131 Fit a line to log-transformed data points for trend line analysis.
132
133 Performs linear regression on log2-transformed data to analyze asymptotic behavior.
134 Can skip initial data points to focus on asymptotic behavior at scale.
135
136 Args:
137 x: List of x-coordinates (typically problem sizes)
138 y: List of y-coordinates (typically running times)
139 skip_until: Skip data points with x values less than this threshold
140 all_points: If True, include all points in the result; if False, only include endpoints
141 add_next: Additional x values to extrapolate to
142
143 Returns:
144 A FitLine object containing the fitted line parameters, or None if fitting failed
145 """
146
147 if skip_until > 0:
148 skip = binary_search(x, skip_until)
149 else:
150 skip = 0
151
152 if skip == len(x):
153 warnings.warn(f'The trend line starts after the max data, so it is not shown')
154 return None
155
156 logx, logy = np.log2(x[skip:]), np.log2(y[skip:])
157 m, b = np.polyfit(logx, logy, 1)
158
159 fit = np.poly1d((m, b))
160
161 for next_x in add_next:
162 next_logx = np.log2(next_x)
163 x.append(next_x)
164 logx = np.append(logx, next_logx)
165
166
167 expected_y = fit(logx)
168 average_y = np.sum(logy) / len(logy)
169 r2 = np.sum((expected_y[:len(expected_y) - len(add_next)] - average_y) ** 2) / np.sum((logy - average_y) ** 2)
170
171 return FitLine(m = m, b = b,
172 x = tuple(x[skip::] if all_points else x[skip::len(x) - skip - 1]),
173 y = tuple(2 ** (expected_y if all_points else expected_y[::len(expected_y) - 1])),
174 r2 = r2)
175
176def plot(figure_name: str, save: bool, ylabel: str, xlabel: str = 'Num nodes (n)', legend_loc: str = 'best'):
177 """
178 Finalize and display or save a plot.
179
180 Adds labels, legend, and either displays the plot or saves it to a file.
181
182 Args:
183 figure_name: Base name for the saved figure file (without extension)
184 save: If True, save the figure to a file; if False, display it
185 ylabel: Label for the y-axis
186 xlabel: Label for the x-axis (defaults to 'Num nodes (n)')
187 legend_loc: Position for the legend (defaults to 'best')
188 """
189
190 plt.xlabel(xlabel) # type: ignore
191 plt.ylabel(ylabel) # type: ignore
192
193 plt.legend(loc = legend_loc) # type: ignore
194 if save:
195 path = (FIGURE_DIRECTORY / figure_name).with_suffix('.png')
196 plt.savefig(path) # type: ignore
197 else:
198 plt.show() # type: ignore
199
200@cache
201def load_agg_data(data_structure: str, data_type: DataType) -> dict[tuple[int, int, int], tuple[int, int]]:
202 """
203 Load and aggregate benchmark data from a CSV file.
204
205 Reads benchmark data for a specific method and data type, aggregating duplicate entries.
206
207 Args:
208 data_structure: Name of the data structure/implementation being benchmarked
209 data_type: Type of benchmark data (construction, removal, or search)
210
211 Returns:
212 Dictionary mapping (n, m, l) tuples to (total_time, repetition_count) tuples,
213 where n is number of keys, m is key length, and l is LCP length
214 """
215
216 data = defaultdict[tuple[int, int, int], tuple[int, int]](lambda: (0, 0))
217
218 with open(get_file(data_structure, data_type), newline='') as file:
219 reader = csv.reader(file)
220
221 for row in reader:
222 if data_type == DataType.REMOVAL:
223 n, m, time_ns, num_repetitions, par = map(int, row)
224 l = m
225 else:
226 n, m, l, time_ns, num_repetitions, par = map(int, row)
227
228 if not par in (0, START_PARALLEL):
229 continue
230
231 entry = data[(n, m, l)]
232 data[(n, m, l)] = (entry[0] + time_ns, entry[1] + num_repetitions)
233
234 return data
235
236@cache
237def load_data(data_structure: str, data_type: DataType, match: tuple[int, int, int]) -> tuple[tuple[int], tuple[float]]:
238 """
239 Extract a specific data series from the benchmark data.
240
241 Loads aggregated data and extracts a specific parameter series based on the match tuple.
242
243 Args:
244 data_structure: Name of the data structure/implementation being benchmarked
245 data_type: Type of benchmark data (construction, removal, or search)
246 match: Tuple of (n, m, l) where -1 indicates the parameter to vary
247 For example, (-1, 1024, 1024) means vary n with m=1024 and l=1024
248
249 Returns:
250 Tuple of (x values, y values) sorted by x, where x is the selected parameter and y is elapsed time
251 """
252
253 x: list[int] = []
254 y: list[float] = []
255
256 match_n, match_m, match_l = match
257
258 for (n, m, l), (time_ns, num_repetitions) in load_agg_data(data_structure, data_type).items():
259 if match_n == -1 and match_m == m and match_l == l:
260 x.append(n)
261 y.append(time_ns / num_repetitions)
262 elif match_n == n and match_m == -1 and match_l == l:
263 x.append(m)
264 y.append(time_ns / num_repetitions)
265 elif match_n == n and match_m == m and match_l == -1:
266 x.append(l)
267 y.append(time_ns / num_repetitions)
268
269 A, B = zip(*sorted(zip(x, y)))
270 return A, B
271
272@cache
273def moving_average(x: list[int], y: list[float], window: float = 1.9) -> tuple[list[int], list[float]]:
274 """
275 Apply a moving average smoothing to data points.
276
277 First consolidates duplicate x values, then applies a geometric window smoothing
278 to reduce noise in the data while preserving trends.
279
280 Args:
281 x: List of x-coordinates
282 y: List of y-coordinates
283 window: Window size factor (points within x/window to x*window are averaged)
284
285 Returns:
286 Tuple of (smoothed x values, smoothed y values)
287 """
288
289 new_x: list[int] = []
290 new_y: list[float] = []
291
292 i = 0
293 while i < len(x):
294 total_sum = y[i]
295 total_count = 1
296 while i < len(x) - 1 and x[i] == x[i + 1]:
297 total_sum += y[i + 1]
298 total_count += 1
299 i += 1
300
301 new_x.append(x[i])
302 new_y.append(total_sum / total_count)
303 i += 1
304
305 x, y = new_x, new_y
306
307 # Convert x and y to numpy arrays for efficient computation
308 x_array = np.array(x)
309 y_array = np.array(y)
310
311 # Prepare an array to store the moving averages
312 moving_averages = np.zeros_like(y_array)
313
314 # Calculate the moving average for each element in x
315 for i, x_val in enumerate(x_array):
316 # Define the range for the moving average
317 lower_bound = x_val / window
318 upper_bound = x_val * window
319
320 # Get indices of x values that fall within the defined window
321 window_indices = np.where((x_array >= lower_bound) & (x_array <= upper_bound))[0]
322
323 # Calculate the average of y values within this window
324 moving_averages[i] = np.mean(y_array[window_indices])
325
326 # Return the original x values and the calculated moving averages
327 return x, moving_averages.tolist()
328
329def add_data_point_to_plot(method_name: str, data_type: DataType, match: tuple[int, int, int], skip_until: int = 0, fitlabel: str = '', max_value: int = -1, markersize: float = POINT_SIZE, draw_best_fit: bool = True, label: str = ''):
330 """
331 Add a data series to the current plot with optional trend line.
332
333 Loads data for a specific method and parameter combination, applies smoothing, and adds it to the plot.
334 Optionally fits and adds a trend line with equation and R² value in the legend.
335
336 Args:
337 method_name: Name of the method/implementation being benchmarked
338 data_type: Type of benchmark data (construction, removal, or search)
339 match: Tuple of (n, m, l) where -1 indicates the parameter to vary
340 skip_until: Skip data points with x values less than this threshold for trend line fitting
341 fitlabel: Label to use in the trend line equation (e.g., 'n', 'm', '\\ell')
342 max_value: Maximum x value to include (-1 for no limit)
343 markersize: Size of the markers for data points
344 draw_best_fit: If True, fit and draw a trend line; if False, only plot data points
345 label: Custom label for the data series (defaults to method_name if empty)
346 """
347
348 x, y = load_data(method_name, data_type, match)
349
350 if max_value > 0:
351 x, y = zip(*[(x[i], y[i]) for i in range(len(x)) if x[i] <= max_value])
352
353 x, y = moving_average(x, y) # type: ignore
354
355 label = label if label else method_name
356
357 fit = fit_log_line(x, y, skip_until=skip_until)
358
359 if draw_best_fit and fit:
360 p = plt.loglog(x, y, base=2, marker = 'o', markersize = markersize, linestyle = 'None') # type: ignore
361 pcolor = p[0].get_color() # type: ignore
362
363 sign = '+' if fit.b >= 0 else '-'
364
365 plt.loglog(fit.x, fit.y, base=2, color=pcolor, linestyle='--', # type: ignore
366 label=f'{label}: $T({fitlabel}) = {fit.m:.1f} {fitlabel} {sign} {abs(fit.b):.1f}, R^2 = {fit.r2:.2f}$')
367 else:
368 plt.loglog(x, y, base=2, marker = 'o', markersize = markersize, linestyle = 'None', label=label) # type: ignore
369
370def compare_construction_data(savefig: bool = False):
371 """
372 Create plots comparing construction time for different implementations.
373
374 Generates two plots:
375 1. Construction time vs. LCP length (ℓ)
376 2. Construction time vs. key length (m)
377
378 Some plots are commented out in the code but can be uncommented if needed.
379
380 Args:
381 savefig: If True, save the figures to files; if False, display them
382 """
383
384 # plt.figure(num = 0, figsize = (8, 5), dpi = get_dpi(savefig), facecolor = 'w', edgecolor = 'k') # type: ignore
385
386 # add_data_point_to_plot('ctrie++', DataType.CONSTRUCTION, (-1, 2**10, 2**10), fitlabel='n', label='\\texttt{c-trie++}', skip_until=2**8)
387 # # add_data_point_to_plot('skip-trie', DataType.CONSTRUCTION, (-1, 2**10, 2**10), fitlabel='n', label='ST', skip_until=2**8)
388 # add_data_point_to_plot('memory-intensive-zip-trie', DataType.CONSTRUCTION, (-1, 2**10, 2**10), fitlabel='n', label='MI-ZT', skip_until=2**8)
389 # add_data_point_to_plot('zip-trie', DataType.CONSTRUCTION, (-1, 2**10, 2**10), fitlabel='n', label='ZT', skip_until=2**8)
390 # # add_data_point_to_plot('parallel-skip-trie', DataType.CONSTRUCTION, (-1, 2**10, 2**10), fitlabel='n', label='PST', skip_until=2**8)
391 # add_data_point_to_plot('parallel-memory-intensive-zip-trie', DataType.CONSTRUCTION, (-1, 2**10, 2**10), fitlabel='n', label='MI-PZT', skip_until=2**8)
392 # add_data_point_to_plot('parallel-zip-trie', DataType.CONSTRUCTION, (-1, 2**10, 2**10), fitlabel='n', label='PZT', skip_until=2**8)
393
394 # plot(figure_name='construction-time-num-keys-comparison', save=savefig, ylabel='Time (ns)', xlabel='Number of Keys ($n$)')
395
396 plt.figure(num = 1, figsize = (8, 5), dpi = get_dpi(savefig), facecolor = 'w', edgecolor = 'k') # type: ignore
397
398 skip_until = 2**14
399
400 add_data_point_to_plot('ctrie++', DataType.CONSTRUCTION, (2**10, MAX_WORD_LENGTH, -1), fitlabel='\\ell', label='\\texttt{c-trie++}', skip_until=skip_until, draw_best_fit=False)
401 # add_data_point_to_plot('skip-trie', DataType.CONSTRUCTION, (2**10, MAX_WORD_LENGTH, -1), fitlabel='\\ell', label='ST', skip_until=2**14)
402 add_data_point_to_plot('memory-intensive-zip-trie', DataType.CONSTRUCTION, (2**10, MAX_WORD_LENGTH, -1), fitlabel='\\ell', label='MI-ZT', skip_until=skip_until, draw_best_fit=False)
403 add_data_point_to_plot('zip-trie', DataType.CONSTRUCTION, (2**10, MAX_WORD_LENGTH, -1), fitlabel='\\ell', label='ZT', skip_until=skip_until, draw_best_fit=False)
404 # add_data_point_to_plot('parallel-skip-trie', DataType.CONSTRUCTION, (2**10, MAX_WORD_LENGTH, -1), fitlabel='\\ell', label='PST', skip_until=2**14)
405 add_data_point_to_plot('parallel-memory-intensive-zip-trie', DataType.CONSTRUCTION, (2**10, MAX_WORD_LENGTH, -1), fitlabel='\\ell', label='MI-PZT', skip_until=skip_until, draw_best_fit=False)
406 add_data_point_to_plot('parallel-zip-trie', DataType.CONSTRUCTION, (2**10, MAX_WORD_LENGTH, -1), fitlabel='\\ell', label='PZT', skip_until=skip_until, draw_best_fit=False)
407
408 plot(figure_name='construction-time-lcp-comparison', save=savefig, ylabel='Time (ns)', xlabel='Mean LCP Length ($\\ell$)')
409
410 plt.figure(num = 2, figsize = (8, 5), dpi = get_dpi(savefig), facecolor = 'w', edgecolor = 'k') # type: ignore
411
412 add_data_point_to_plot('ctrie++', DataType.CONSTRUCTION, (2**10, -1, 2**10), fitlabel='m', label='\\texttt{c-trie++}', skip_until=2**11)
413 # add_data_point_to_plot('skip-trie', DataType.CONSTRUCTION, (2**10, -1, 2**10), fitlabel='m', label='ST', skip_until=2**11)
414 add_data_point_to_plot('memory-intensive-zip-trie', DataType.CONSTRUCTION, (2**10, -1, 2**10), fitlabel='m', label='MI-ZT', skip_until=2**11)
415 add_data_point_to_plot('zip-trie', DataType.CONSTRUCTION, (2**10, -1, 2**10), fitlabel='m', label='ZT', skip_until=2**11)
416 # add_data_point_to_plot('parallel-skip-trie', DataType.CONSTRUCTION, (2**10, -1, 2**10), fitlabel='m', label='PST', skip_until=2**11)
417 add_data_point_to_plot('parallel-memory-intensive-zip-trie', DataType.CONSTRUCTION, (2**10, -1, 2**10), fitlabel='m', label='MI-PZT', skip_until=2**11)
418 add_data_point_to_plot('parallel-zip-trie', DataType.CONSTRUCTION, (2**10, -1, 2**10), fitlabel='m', label='PZT', skip_until=2**11)
419
420 plot(figure_name='construction-time-key-length-comparison', save=savefig, ylabel='Time (ns)', xlabel='Key Length ($m$)')
421
422def compare_search_data(savefig: bool = False):
423 """
424 Create plots comparing search time for different implementations.
425
426 Generates two plots:
427 1. Search time vs. key length (m)
428 2. Search time vs. LCP length (ℓ)
429
430 Some plots are commented out in the code but can be uncommented if needed.
431
432 Args:
433 savefig: If True, save the figures to files; if False, display them
434 """
435
436 # plt.figure(num = 100, figsize = (8, 5), dpi = get_dpi(savefig), facecolor = 'w', edgecolor = 'k') # type: ignore
437
438 # add_data_point_to_plot('ctrie++', DataType.SEARCH, (-1, 2**10, 2**10), fitlabel='n', label='\\texttt{c-trie++}', skip_until=2**13)
439 # # add_data_point_to_plot('skip-trie', DataType.SEARCH, (-1, 2**10, 2**10), fitlabel='n', label='ST', skip_until=2**13)
440 # add_data_point_to_plot('memory-intensive-zip-trie', DataType.SEARCH, (-1, 2**10, 2**10), fitlabel='n', label='MI-ZT', skip_until=2**13)
441 # add_data_point_to_plot('zip-trie', DataType.SEARCH, (-1, 2**10, 2**10), fitlabel='n', label='ZT', skip_until=2**13)
442 # # add_data_point_to_plot('parallel-skip-trie', DataType.SEARCH, (-1, 2**10, 2**10), fitlabel='n', label='PST', skip_until=2**13)
443 # add_data_point_to_plot('parallel-memory-intensive-zip-trie', DataType.SEARCH, (-1, 2**10, 2**10), fitlabel='n', label='MI-PZT', skip_until=2**13)
444 # add_data_point_to_plot('parallel-zip-trie', DataType.SEARCH, (-1, 2**10, 2**10), fitlabel='n', label='PZT', skip_until=2**13)
445
446 # plot(figure_name='search-time-num-keys-comparison', save=savefig, ylabel='Time (ns)', xlabel='Number of Keys ($n$)')
447
448 plt.figure(num = 101, figsize = (8, 5), dpi = get_dpi(savefig), facecolor = 'w', edgecolor = 'k') # type: ignore
449
450 add_data_point_to_plot('ctrie++', DataType.SEARCH, (2**10, -1, 2**10), fitlabel='m', label='\\texttt{c-trie++}', draw_best_fit=False)
451 # add_data_point_to_plot('skip-trie', DataType.SEARCH, (2**10, -1, 2**10), fitlabel='m', label='ST', draw_best_fit=False)
452 add_data_point_to_plot('memory-intensive-zip-trie', DataType.SEARCH, (2**10, -1, 2**10), fitlabel='m', label='MI-ZT', draw_best_fit=False)
453 add_data_point_to_plot('zip-trie', DataType.SEARCH, (2**10, -1, 2**10), fitlabel='m', label='ZT', draw_best_fit=False)
454 # add_data_point_to_plot('parallel-skip-trie', DataType.SEARCH, (2**10, -1, 2**10), fitlabel='m', label='PST', draw_best_fit=False)
455 add_data_point_to_plot('parallel-memory-intensive-zip-trie', DataType.SEARCH, (2**10, -1, 2**10), fitlabel='m', label='MI-PZT', draw_best_fit=False)
456 add_data_point_to_plot('parallel-zip-trie', DataType.SEARCH, (2**10, -1, 2**10), fitlabel='m', label='PZT', draw_best_fit=False)
457
458 plot(figure_name='search-time-key-length-comparison', save=savefig, ylabel='Time (ns)', xlabel='Key Length ($m$)')
459
460 plt.figure(num = 102, figsize = (8, 5), dpi = get_dpi(savefig), facecolor = 'w', edgecolor = 'k') # type: ignore
461
462 add_data_point_to_plot('ctrie++', DataType.SEARCH, (2**10, MAX_WORD_LENGTH, -1), fitlabel='\\ell', label='\\texttt{c-trie++}', skip_until=2**14, draw_best_fit=False)
463 # add_data_point_to_plot('skip-trie', DataType.SEARCH, (2**10, MAX_WORD_LENGTH, -1), fitlabel='\\ell', label='ST', skip_until=2**14)
464 add_data_point_to_plot('memory-intensive-zip-trie', DataType.SEARCH, (2**10, MAX_WORD_LENGTH, -1), fitlabel='\\ell', label='MI-ZT', skip_until=2**14, draw_best_fit=False)
465 add_data_point_to_plot('zip-trie', DataType.SEARCH, (2**10, MAX_WORD_LENGTH, -1), fitlabel='\\ell', label='ZT', skip_until=2**14, draw_best_fit=False)
466 # add_data_point_to_plot('parallel-skip-trie', DataType.SEARCH, (2**10, MAX_WORD_LENGTH, -1), fitlabel='\\ell', label='PST', skip_until=2**14)
467 add_data_point_to_plot('parallel-memory-intensive-zip-trie', DataType.SEARCH, (2**10, MAX_WORD_LENGTH, -1), fitlabel='\\ell', label='MI-PZT', skip_until=2**14, draw_best_fit=False)
468 add_data_point_to_plot('parallel-zip-trie', DataType.SEARCH, (2**10, MAX_WORD_LENGTH, -1), fitlabel='\\ell', label='PZT', skip_until=2**14, draw_best_fit=False)
469
470 plot(figure_name='search-time-lcp-comparison', save=savefig, ylabel='Time (ns)', xlabel='Mean LCP Length ($\\ell$)')
471
472if __name__ == '__main__':
473 savefig = False
474 # savefig = True
475
477 compare_search_data(savefig)
int binary_search(list[int] x, int key)
tuple[tuple[int], tuple[float]] load_data(str data_structure, DataType data_type, tuple[int, int, int] match)
FitLine|None fit_log_line(list[int] x, list[float] y, int skip_until=0, bool all_points=False, list[int] add_next=[])
compare_search_data(bool savefig=False)
plot(str figure_name, bool save, str ylabel, str xlabel='Num nodes(n)', str legend_loc='best')
Path get_file(str method_name, DataType data_type)
tuple[list[int], list[float]] moving_average(list[int] x, list[float] y, float window=1.9)
add_data_point_to_plot(str method_name, DataType data_type, tuple[int, int, int] match, int skip_until=0, str fitlabel='', int max_value=-1, float markersize=POINT_SIZE, bool draw_best_fit=True, str label='')
compare_construction_data(bool savefig=False)
get_dpi(bool savefig)
dict[tuple[int, int, int], tuple[int, int]] load_agg_data(str data_structure, DataType data_type)