129def fit_log_line(x: list[int], y: list[float], skip_until: int = 0, all_points: bool =
False, add_next: list[int] = []) -> FitLine |
None:
131 Fit a line to log-transformed data points for trend line analysis.
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.
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
144 A FitLine object containing the fitted line parameters, or None if fitting failed
153 warnings.warn(f
'The trend line starts after the max data, so it is not shown')
156 logx, logy = np.log2(x[skip:]), np.log2(y[skip:])
157 m, b = np.polyfit(logx, logy, 1)
159 fit = np.poly1d((m, b))
161 for next_x
in add_next:
162 next_logx = np.log2(next_x)
164 logx = np.append(logx, next_logx)
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)
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])),
201def load_agg_data(data_structure: str, data_type: DataType) -> dict[tuple[int, int, int], tuple[int, int]]:
203 Load and aggregate benchmark data from a CSV file.
205 Reads benchmark data for a specific method and data type, aggregating duplicate entries.
208 data_structure: Name of the data structure/implementation being benchmarked
209 data_type: Type of benchmark data (construction, removal, or search)
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
216 data = defaultdict[tuple[int, int, int], tuple[int, int]](
lambda: (0, 0))
218 with open(
get_file(data_structure, data_type), newline=
'')
as file:
219 reader = csv.reader(file)
222 if data_type == DataType.REMOVAL:
223 n, m, time_ns, num_repetitions, par = map(int, row)
226 n, m, l, time_ns, num_repetitions, par = map(int, row)
228 if not par
in (0, START_PARALLEL):
231 entry = data[(n, m, l)]
232 data[(n, m, l)] = (entry[0] + time_ns, entry[1] + num_repetitions)
237def load_data(data_structure: str, data_type: DataType, match: tuple[int, int, int]) -> tuple[tuple[int], tuple[float]]:
239 Extract a specific data series from the benchmark data.
241 Loads aggregated data and extracts a specific parameter series based on the match tuple.
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
250 Tuple of (x values, y values) sorted by x, where x is the selected parameter and y is elapsed time
256 match_n, match_m, match_l = match
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:
261 y.append(time_ns / num_repetitions)
262 elif match_n == n
and match_m == -1
and match_l == l:
264 y.append(time_ns / num_repetitions)
265 elif match_n == n
and match_m == m
and match_l == -1:
267 y.append(time_ns / num_repetitions)
269 A, B = zip(*sorted(zip(x, y)))
273def moving_average(x: list[int], y: list[float], window: float = 1.9) -> tuple[list[int], list[float]]:
275 Apply a moving average smoothing to data points.
277 First consolidates duplicate x values, then applies a geometric window smoothing
278 to reduce noise in the data while preserving trends.
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)
286 Tuple of (smoothed x values, smoothed y values)
289 new_x: list[int] = []
290 new_y: list[float] = []
296 while i < len(x) - 1
and x[i] == x[i + 1]:
297 total_sum += y[i + 1]
302 new_y.append(total_sum / total_count)
308 x_array = np.array(x)
309 y_array = np.array(y)
312 moving_averages = np.zeros_like(y_array)
315 for i, x_val
in enumerate(x_array):
317 lower_bound = x_val / window
318 upper_bound = x_val * window
321 window_indices = np.where((x_array >= lower_bound) & (x_array <= upper_bound))[0]
324 moving_averages[i] = np.mean(y_array[window_indices])
327 return x, moving_averages.tolist()
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 =
''):
331 Add a data series to the current plot with optional trend line.
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.
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)
348 x, y =
load_data(method_name, data_type, match)
351 x, y = zip(*[(x[i], y[i])
for i
in range(len(x))
if x[i] <= max_value])
355 label = label
if label
else method_name
359 if draw_best_fit
and fit:
360 p = plt.loglog(x, y, base=2, marker =
'o', markersize = markersize, linestyle =
'None')
361 pcolor = p[0].get_color()
363 sign =
'+' if fit.b >= 0
else '-'
365 plt.loglog(fit.x, fit.y, base=2, color=pcolor, linestyle=
'--',
366 label=f
'{label}: $T({fitlabel}) = {fit.m:.1f} {fitlabel} {sign} {abs(fit.b):.1f}, R^2 = {fit.r2:.2f}$')
368 plt.loglog(x, y, base=2, marker =
'o', markersize = markersize, linestyle =
'None', label=label)
372 Create plots comparing construction time for different implementations.
375 1. Construction time vs. LCP length (ℓ)
376 2. Construction time vs. key length (m)
378 Some plots are commented out in the code but can be uncommented if needed.
381 savefig: If True, save the figures to files; if False, display them
396 plt.figure(num = 1, figsize = (8, 5), dpi =
get_dpi(savefig), facecolor =
'w', edgecolor =
'k')
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)
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)
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)
408 plot(figure_name=
'construction-time-lcp-comparison', save=savefig, ylabel=
'Time (ns)', xlabel=
'Mean LCP Length ($\\ell$)')
410 plt.figure(num = 2, figsize = (8, 5), dpi =
get_dpi(savefig), facecolor =
'w', edgecolor =
'k')
412 add_data_point_to_plot(
'ctrie++', DataType.CONSTRUCTION, (2**10, -1, 2**10), fitlabel=
'm', label=
'\\texttt{c-trie++}', 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)
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)
420 plot(figure_name=
'construction-time-key-length-comparison', save=savefig, ylabel=
'Time (ns)', xlabel=
'Key Length ($m$)')
424 Create plots comparing search time for different implementations.
427 1. Search time vs. key length (m)
428 2. Search time vs. LCP length (ℓ)
430 Some plots are commented out in the code but can be uncommented if needed.
433 savefig: If True, save the figures to files; if False, display them
448 plt.figure(num = 101, figsize = (8, 5), dpi =
get_dpi(savefig), facecolor =
'w', edgecolor =
'k')
450 add_data_point_to_plot(
'ctrie++', DataType.SEARCH, (2**10, -1, 2**10), fitlabel=
'm', label=
'\\texttt{c-trie++}', 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)
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)
458 plot(figure_name=
'search-time-key-length-comparison', save=savefig, ylabel=
'Time (ns)', xlabel=
'Key Length ($m$)')
460 plt.figure(num = 102, figsize = (8, 5), dpi =
get_dpi(savefig), facecolor =
'w', edgecolor =
'k')
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)
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)
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)
470 plot(figure_name=
'search-time-lcp-comparison', save=savefig, ylabel=
'Time (ns)', xlabel=
'Mean LCP Length ($\\ell$)')
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='')