107def fit_log_line(x: list[int], y: list[float], skip_until: int = 0, all_points: bool =
False, add_next: list[int] = []) -> FitLine |
None:
108 """Fit a line to log-transformed data points for trend line analysis.
110 Performs linear regression on log2-transformed data to analyze asymptotic behavior.
111 Can skip initial data points to focus on asymptotic behavior at scale.
114 x: List of x-coordinates (typically problem sizes)
115 y: List of y-coordinates (typically running times)
116 skip_until: Skip data points with x values less than this threshold
117 all_points: If True, include all points in the result; if False, only include endpoints
118 add_next: Additional x values to extrapolate to
121 A FitLine object containing the fitted line parameters, or None if fitting failed
129 warnings.warn(f
'The trend line starts after the max data, so it is not shown')
132 logx, logy = np.log2(x[skip:]), np.log2(y[skip:])
133 m, b = np.polyfit(logx, logy, 1)
135 fit = np.poly1d((m, b))
137 for next_x
in add_next:
138 next_logx = np.log2(next_x)
140 logx = np.append(logx, next_logx)
143 expected_y = fit(logx)
144 average_y = np.sum(logy) / len(logy)
145 r2 = np.sum((expected_y[:len(expected_y) - len(add_next)] - average_y) ** 2) / np.sum((logy - average_y) ** 2)
148 x = tuple(x[skip::]
if all_points
else x[skip::len(x) - skip - 1]),
149 y = tuple(2 ** (expected_y
if all_points
else expected_y[::len(expected_y) - 1])),
248def moving_average(x: list[int], y: list[float], window: float = 1.9) -> tuple[list[int], list[float]]:
249 """Apply a moving average smoothing to data points.
251 First consolidates duplicate x values, then applies a geometric window smoothing
252 to reduce noise in the data while preserving trends.
255 x: List of x-coordinates
256 y: List of y-coordinates
257 window: Window size factor (points within x/window to x*window are averaged)
260 Tuple of (smoothed x values, smoothed y values)
262 new_x: list[int] = []
263 new_y: list[float] = []
269 while i < len(x) - 1
and x[i] == x[i + 1]:
270 total_sum += y[i + 1]
275 new_y.append(total_sum / total_count)
281 x_array = np.array(x)
282 y_array = np.array(y)
285 moving_averages = np.zeros_like(y_array)
288 for i, x_val
in enumerate(x_array):
290 lower_bound = x_val / window
291 upper_bound = x_val * window
294 window_indices = np.where((x_array >= lower_bound) & (x_array <= upper_bound))[0]
297 moving_averages[i] = np.mean(y_array[window_indices])
300 return x, moving_averages.tolist()
302def add_data_point_to_plot(method_name: str, data_type: DataType, index: int, cutoff: int = 0, skip_until: int=0, fitlabel: str=
'', max_value: int=-1, markersize: float = POINT_SIZE, draw_best_fit: bool =
True, label: str =
''):
303 """Add a data series to the current plot with optional trend line.
305 Loads data for a specific method and parameter, applies smoothing, and adds it to the plot.
306 Optionally fits and adds a trend line with equation and R² value in the legend.
309 method_name: Name of the method/implementation being benchmarked
310 data_type: Type of benchmark data (construction, removal, or search)
311 index: Which parameter to plot against (0 for n, 1 for m, 2 for l)
312 cutoff: Filter value for the parallel cutoff parameter
313 skip_until: Skip data points with x values less than this threshold for trend line fitting
314 fitlabel: Label to use in the trend line equation (e.g., 'n', 'm', 'L')
315 max_value: Maximum x value to include (-1 for no limit)
316 markersize: Size of the markers for data points
317 draw_best_fit: If True, fit and draw a trend line; if False, only plot data points
318 label: Custom label for the data series (defaults to method_name if empty)
323 x, y = zip(*[(x[i], y[i])
for i
in range(len(x))
if x[i] <= max_value])
327 label = label
if label
else method_name
331 if draw_best_fit
and fit:
332 p = plt.loglog(x, y, base=2, marker =
'o', markersize = markersize, linestyle =
'None')
333 pcolor = p[0].get_color()
335 sign =
'+' if fit.b >= 0
else '-'
337 plt.loglog(fit.x, fit.y, base=2, color=pcolor, linestyle=
'--', alpha=0.7, linewidth=0.8,
338 label=f
'{label}: $T({fitlabel}) = {fit.m:.1f} {fitlabel} {sign} {abs(fit.b):.1f}, R^2 = {fit.r2:.2f}$')
340 plt.loglog(x, y, base=2, marker =
'o', markersize = markersize, linestyle =
'None', label=label)
345 """Create plots comparing construction time for different implementations.
347 Generates three plots:
348 1. Construction time vs. number of keys (n)
349 2. Construction time vs. total key length (N)
350 3. Construction time vs. total LCP length (L)
353 savefig: If True, save the figures to files; if False, display them
355 plt.figure(num = 0, figsize = (8, 5), dpi =
get_dpi(savefig), facecolor =
'w', edgecolor =
'k')
360 add_data_point_to_plot(
'c-trie++', DataType.CONSTRUCTION, 0, fitlabel=
'n', skip_until=skip_until,markersize=1, label=
'\\texttt{c-trie++}')
362 add_data_point_to_plot(
'MI-ZT', DataType.CONSTRUCTION, 0, fitlabel=
'n', skip_until=skip_until,markersize=1)
363 add_data_point_to_plot(
'PZT', DataType.CONSTRUCTION, 0, cutoff=cutoff, fitlabel=
'n', skip_until=skip_until,markersize=1)
364 add_data_point_to_plot(
'MI-PZT', DataType.CONSTRUCTION, 0, cutoff=cutoff, fitlabel=
'n', skip_until=skip_until,markersize=1)
366 plot(figure_name=
'construction-time-num-keys-comparison', save=savefig, ylabel=
'Time (ns)', xlabel=
'Number of Keys ($n$)')
369 plt.figure(num = 1, figsize = (8, 5), dpi =
get_dpi(savefig), facecolor =
'w', edgecolor =
'k')
373 add_data_point_to_plot(
'c-trie++', DataType.CONSTRUCTION, 1, fitlabel=
'N', skip_until=skip_until, label=
'\\texttt{c-trie++}')
376 add_data_point_to_plot(
'PZT', DataType.CONSTRUCTION, 1, cutoff=cutoff, fitlabel=
'N', skip_until=skip_until)
377 add_data_point_to_plot(
'MI-PZT', DataType.CONSTRUCTION, 1, cutoff=cutoff, fitlabel=
'N', skip_until=skip_until)
379 plot(figure_name=
'construction-time-total-key-length-comparison', save=savefig, ylabel=
'Time (ns)', xlabel=
'Total Key Length ($N$)')
382 plt.figure(num = 2, figsize = (8, 5), dpi =
get_dpi(savefig), facecolor =
'w', edgecolor =
'k')
386 add_data_point_to_plot(
'c-trie++', DataType.CONSTRUCTION, 2, fitlabel=
'L', skip_until=skip_until, label=
'\\texttt{c-trie++}')
389 add_data_point_to_plot(
'PZT', DataType.CONSTRUCTION, 2, cutoff=cutoff, fitlabel=
'L', skip_until=skip_until)
390 add_data_point_to_plot(
'MI-PZT', DataType.CONSTRUCTION, 2, cutoff=cutoff, fitlabel=
'L', skip_until=skip_until)
392 plot(figure_name=
'construction-time-total-LCP-length-comparison', save=savefig, ylabel=
'Time (ns)', xlabel=
'Total LCP Length ($L$)')
417 """Create plots comparing search time for different implementations.
420 1. Search time vs. number of keys (n)
421 2. Search time vs. LCP length (ℓ)
424 savefig: If True, save the figures to files; if False, display them
426 plt.figure(num=300, figsize=(8, 5), dpi =
get_dpi(savefig), facecolor=
'w', edgecolor=
'k')
430 add_data_point_to_plot(
'c-trie++', DataType.SEARCH, 0, draw_best_fit=
False, markersize=4, label=
'\\texttt{c-trie++}')
436 plot(figure_name=
'search-time-num-keys-comparison', save=savefig, ylabel=
'Time (ns)', xlabel=
'Number of Keys ($n$)')
450 plt.figure(num=302, figsize=(8, 5), dpi =
get_dpi(savefig), facecolor=
'w', edgecolor=
'k')
452 add_data_point_to_plot(
'c-trie++', DataType.SEARCH, 2, fitlabel=
'\\ell', skip_until=skip_until, label=
'\\texttt{c-trie++}')
456 add_data_point_to_plot(
'MI-PZT', DataType.SEARCH, 2, cutoff=cutoff, fitlabel=
'\\ell', skip_until=skip_until)
458 plot(figure_name=
'search-time-lcp-length-comparison', save=savefig, ylabel=
'Time (ns)', xlabel=
'LCP Length ($\\ell$)')
add_data_point_to_plot(str method_name, DataType data_type, int index, int cutoff=0, int skip_until=0, str fitlabel='', int max_value=-1, float markersize=POINT_SIZE, bool draw_best_fit=True, str label='')