|
| 1 | +import numpy as np |
| 2 | +import matplotlib.pyplot as plt |
| 3 | +from matplotlib.patches import Patch |
| 4 | + |
| 5 | +# Define the range for rho (Grinding Depth) |
| 6 | +rho = np.linspace(1, 256, 1000) |
| 7 | +f = 0.05 |
| 8 | +cost_per_cpu_hour = 0.01 |
| 9 | +w_O = 20 * (2 * rho - 1) |
| 10 | +w_O_hours = w_O / 3600 |
| 11 | +epsilon = 1e-100 # Avoid log(0) |
| 12 | + |
| 13 | +# Praos cost functions |
| 14 | +def ant_glance_praos(rho): return 5e-10 * 2**(rho - 2) |
| 15 | +def ant_patrol_praos(rho): return 5e-10 * 2**(rho - 1) + 2.16e-2 * 2**(rho - 1) / rho |
| 16 | +def owl_stare_praos(rho): return 5e-10 * 2**(rho - 2) + 5e-2 * 2**(rho - 1) / rho |
| 17 | +def owl_survey_praos(rho): return 5e-10 * 2**(rho - 2) + 7.16e-2 * 2**(rho - 1) / rho |
| 18 | + |
| 19 | +# Phalanx cost function |
| 20 | +def phalanx_cost(rho, scale): return (scale * 2**(rho - 1)) / rho |
| 21 | + |
| 22 | +# Compute log10(Cost) from CPU count |
| 23 | +def log_cost(n_cpu): return np.log10(np.maximum(n_cpu * cost_per_cpu_hour * w_O_hours, epsilon)) |
| 24 | + |
| 25 | +# Curves for Praos |
| 26 | +praos_curves = { |
| 27 | + 'Ant Glance Praos': ('blue', log_cost(ant_glance_praos(rho))), |
| 28 | + 'Ant Patrol Praos': ('orange', log_cost(ant_patrol_praos(rho))), |
| 29 | + 'Owl Stare Praos': ('green', log_cost(owl_stare_praos(rho))), |
| 30 | + 'Owl Survey Praos': ('red', log_cost(owl_survey_praos(rho))), |
| 31 | +} |
| 32 | + |
| 33 | +# Curves for Phalanx configurations |
| 34 | +phalanx_curves = { |
| 35 | + 'Phalanx$_{1/100}$': ('#6A5ACD', log_cost(phalanx_cost(rho, 2.16e2))), |
| 36 | + 'Phalanx$_{1/10}$': ('#228B22', log_cost(phalanx_cost(rho, 2.16e3))), |
| 37 | + 'Phalanx$_{max}$': ('#B22222', log_cost(phalanx_cost(rho, 2.16e4))), |
| 38 | +} |
| 39 | + |
| 40 | +# Feasibility zones |
| 41 | +zones = [ |
| 42 | + (-10, 4, 'green', 'Trivial'), |
| 43 | + (4, 6, 'yellow', 'Feasible'), |
| 44 | + (6, 9, '#FFA07A', 'Possible'), |
| 45 | + (9, 12, '#FF6347', 'Borderline Infeasible'), |
| 46 | + (12, 90, 'red', 'Infeasible') |
| 47 | +] |
| 48 | + |
| 49 | +# Function to find crossing points and annotate |
| 50 | +def annotate_crossings(log_costs, color, threshold, position='above'): |
| 51 | + # Find indices where the curve crosses the threshold |
| 52 | + indices = np.where((log_costs[:-1] < threshold) & (log_costs[1:] >= threshold))[0] |
| 53 | + if len(indices) > 0: |
| 54 | + idx = indices[0] |
| 55 | + rho_val = rho[idx] |
| 56 | + plt.scatter(rho_val, threshold, color=color, marker='o', s=50, zorder=5) |
| 57 | + # Position above or below based on the curve |
| 58 | + if position == 'below': |
| 59 | + plt.annotate(f'{rho_val:.1f}', |
| 60 | + xy=(rho_val, threshold), |
| 61 | + xytext=(rho_val + 1.1, threshold - 0.4), |
| 62 | + fontsize=8, color=color) |
| 63 | + elif position == 'green': |
| 64 | + plt.annotate(f'{rho_val:.1f}', |
| 65 | + xy=(rho_val, threshold), |
| 66 | + xytext=(rho_val - 0.6, threshold - 0.9), |
| 67 | + fontsize=8, color=color) |
| 68 | + else: |
| 69 | + plt.annotate(f'{rho_val:.1f}', |
| 70 | + xy=(rho_val, threshold), |
| 71 | + xytext=(rho_val - 1, threshold + 0.3), |
| 72 | + fontsize=8, color=color) |
| 73 | + |
| 74 | + |
| 75 | +# # Annotate where curves cross threshold lines |
| 76 | +# def annotate_crossings(log_costs, color, threshold, position='above'): |
| 77 | +# indices = np.where((log_costs[:-1] < threshold) & (log_costs[1:] >= threshold))[0] |
| 78 | +# if len(indices) > 0: |
| 79 | +# idx = indices[0] |
| 80 | +# rho_val = rho[idx] |
| 81 | +# plt.scatter(rho_val, threshold, color=color, marker='o', s=50, zorder=5) |
| 82 | +# offset = {'above': (1, 0.5), 'below': (1.1, -0.4), 'green': (-0.6, -0.9)}.get(position, (1, 0.5)) |
| 83 | +# plt.annotate(f'{rho_val:.1f}', xy=(rho_val, threshold), |
| 84 | +# xytext=(rho_val + offset[0], threshold + offset[1]), |
| 85 | +# fontsize=8, color=color) |
| 86 | + |
| 87 | +# Unified curve list for annotation logic |
| 88 | +curves = [ |
| 89 | + (praos_curves['Ant Glance Praos'][1], 'blue', 'above'), |
| 90 | + (praos_curves['Ant Patrol Praos'][1], 'orange', 'below'), |
| 91 | + (praos_curves['Owl Stare Praos'][1], 'green', 'green'), |
| 92 | + (praos_curves['Owl Survey Praos'][1], 'red', 'above'), |
| 93 | + (phalanx_curves['Phalanx$_{1/100}$'][1], '#6A5ACD', 'above'), |
| 94 | + (phalanx_curves['Phalanx$_{1/10}$'][1], '#228B22', 'above'), |
| 95 | + (phalanx_curves['Phalanx$_{max}$'][1], '#B22222', 'above') |
| 96 | +] |
| 97 | + |
| 98 | +# Plotting |
| 99 | +plt.figure(figsize=(12, 7)) |
| 100 | + |
| 101 | +# Plot each Praos and Phalanx curve |
| 102 | +for label, (color, values) in praos_curves.items(): |
| 103 | + plt.plot(rho, values, label=label, color=color, linewidth=2) |
| 104 | +for label, (color, values) in phalanx_curves.items(): |
| 105 | + plt.plot(rho, values, label=label, color=color, linestyle='--', linewidth=2) |
| 106 | + |
| 107 | +# Add feasibility zones |
| 108 | +for y0, y1, color, label in zones: |
| 109 | + plt.axhspan(y0, y1, color=color, alpha=0.1, label=label) |
| 110 | + |
| 111 | +# Annotate crossings |
| 112 | +for threshold, _, _, _ in zones: |
| 113 | + for log_costs, color, position in curves: |
| 114 | + annotate_crossings(log_costs, color, threshold, position) |
| 115 | + |
| 116 | +# Axis labels and title |
| 117 | +plt.xlabel(r'$\rho$ (Grinding Depth)', fontsize=14) |
| 118 | +plt.ylabel(r'$\log_{10}(\mathrm{Cost\ (USD)})$', fontsize=14) |
| 119 | +plt.title('Cost of Grinding Attacks Across Praos and Phalanx Scenarios', fontsize=16) |
| 120 | +plt.grid(True, linestyle='--', alpha=0.7) |
| 121 | +plt.xlim(0, 256) |
| 122 | +y_max = max(np.max(v[np.isfinite(v)]) for _, v in list(praos_curves.values()) + list(phalanx_curves.values())) + 5 |
| 123 | +plt.ylim(-5, y_max) |
| 124 | + |
| 125 | +# Custom legend |
| 126 | +legend_elements = [ |
| 127 | + *[plt.Line2D([0], [0], color=color, lw=2, label=label) for label, (color, _) in praos_curves.items()], |
| 128 | + *[plt.Line2D([0], [0], color=color, lw=2, linestyle='--', label=label) for label, (color, _) in phalanx_curves.items()], |
| 129 | + *[Patch(facecolor=color, alpha=0.1, label=label) for _, _, color, label in zones] |
| 130 | +] |
| 131 | +plt.legend(handles=legend_elements, fontsize=10, loc='lower right', |
| 132 | + bbox_to_anchor=(1, 0), ncol=2, handletextpad=0.5, columnspacing=1.5) |
| 133 | + |
| 134 | +# Final layout and save |
| 135 | +plt.subplots_adjust(left=0.1, right=0.95, top=0.9, bottom=0.2) |
| 136 | + |
| 137 | +plt.show() |
0 commit comments