-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathline_graph_3_nodes_1_fee.py
359 lines (312 loc) · 14.6 KB
/
line_graph_3_nodes_1_fee.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
import networkx as nx
import numpy as np
import matplotlib
from scipy.optimize import curve_fit
from pyswarm import pso
from scipy.interpolate import InterpolatedUnivariateSpline
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
from transaction_simulator import simulate_transactions_fees, create_random_graph
import time
from scipy.optimize import curve_fit
from scipy.special import lambertw
def simulate_network_network_size_variation(node, capacity_range, transaction_amount, fee, epsilon, window_size, num_runs, avg_degree, checkpointing = False, checkpoint_interval = 20):
"""
Simulates a credit network with varying capacities and transaction fees, computes the success rate of transactions,
and optionally saves checkpoints of the simulation results.
Parameters:
num_nodes (int): The number of nodes in the credit network graph.
capacity_range (iterable): A range or sequence of capacities to be tested in the simulation.
transaction_amount (float): The amount involved in each transaction.
fee_range (iterable): A range or sequence of transaction fees to be tested.
epsilon (float): The convergence threshold for the success rate to determine the steady state.
window_size (int): The number of transactions processed in each iteration.
num_runs (int): The number of simulation runs for each combination of capacity and fee.
avg_degree (float): The average out-degree (number of outgoing edges) for nodes in the graph.
checkpointing (bool): Whether to save checkpoints of the results at intervals.
checkpoint_interval (int): The interval (in terms of runs) at which to save checkpoints.
Returns:
pandas.DataFrame: A DataFrame containing the results of the simulation with columns for capacities,
runs, success rates, and fees.
Note:
- The function creates a directed graph for each combination of capacity and fee, and for each run,
simulating transactions to calculate the success rate.
- Checkpoints are saved as pickle files if checkpointing is enabled.
"""
results = {
'run': [],
'success_rate': [],
'capacity': [],
'avg_path_length': [] # New field for average path length
}
total_execution_time = 0
for run in range(num_runs):
start_time = time.time()
for capacity in capacity_range:
# if run == 0:
# visualize = True
# else:
# visualize = False
G = create_random_graph(node, avg_degree, capacity, 'line')
pos = nx.spring_layout(G)
success_rate, avg_path_length = simulate_transactions_fees(G, capacity, node, epsilon, fee,
transaction_amount, window_size, pos, visualize=False,
visualize_initial=0
)
# print(f'Completed run {run}/{num_runs}, degree {degree}, fee {fee}')
results['run'].append(run)
results['success_rate'].append(success_rate)
results['capacity'].append(capacity)
results['avg_path_length'].append(avg_path_length)
if checkpointing == True and run % checkpoint_interval == 0:
checkpoint_df = pd.DataFrame(results)
checkpoint_filename = f'checkpoint_capacity_fixed_{capacity}_run_{run}.pkl'
checkpoint_df.to_pickle(checkpoint_filename)
# print(f'Saved checkpoint to {checkpoint_filename}')
print(f'Completed run {run}/{num_runs}')
end_time = time.time()
execution_time = end_time - start_time
total_execution_time += execution_time
remaining_fees = num_runs - (run + 1)
estimated_remaining_time = remaining_fees * (total_execution_time / (run + 1))
print(f"Processed capacity {run} in time {execution_time} seconds")
print(f"Estimated remaining time: {estimated_remaining_time / 60} minutes\n")
return pd.DataFrame(results)
def plot_results_network_size_variation(df, name, size = (8 / 1.2, 6 / 1.2)):
"""
Plots the results of the network simulation, showing the relationship between edge capacity, fees, and
transaction success rate.
Parameters:
df (pandas.DataFrame): A DataFrame containing the simulation results with columns for capacities,
success rates, and fees.
Note:
- The function generates two plots: a line plot showing success rates against capacities for different fees,
and a heatmap showing the success rate for each combination of fee and capacity.
- The plots are saved as image files.
"""
cmap = sns.cubehelix_palette(as_cmap=True)
bg_color = plt.gcf().get_facecolor()
sns.set_theme()
fig, ax = plt.subplots(figsize=size, dpi=300)
# sns.lineplot(data=df_filtered, x='nodes', y='success_rate', hue='fee', marker='o', alpha = 0.9, ci='sd', legend='full')
sns.lineplot(data=df, x='capacity', y='success_rate', marker='o', ci='sd', legend='full')
plt.xlabel('Capacity', fontsize=16)
plt.ylabel('Success Rate', fontsize=16)
plt.xticks(fontsize=14)
plt.yticks(fontsize=14)
plt.ylim([-0.01, 1.1])
# plt.xlim(left=-10)
plt.tight_layout()
fig.savefig(f'{name}_capacity.png', dpi=300, bbox_inches='tight')
plt.show()
fig, ax = plt.subplots(figsize=size, dpi=300)
sns.lineplot(data=df, x='capacity', y='avg_path_length', marker='o', ci='sd', legend='full')
# Calculate the y-ticks based on the data range and set them to only show one decimal place
# min_y, max_y = df['avg_path_length'].min(), df['avg_path_length'].max()
min_y = 1.0
max_y = 1.42
y_ticks = np.arange(np.floor(min_y * 10) / 10, np.ceil(max_y * 10) / 10, 0.1)
plt.yticks(y_ticks, [f'{tick:.1f}' for tick in y_ticks])
# Improve the legibility of the plot
plt.ylabel('Average path length', fontsize=16)
plt.xlabel('Capacity', fontsize=16)
plt.xticks(fontsize=14)
plt.yticks(fontsize=14)
# plt.ylim(y_ticks[0], y_ticks[-1])
#
# ax.xaxis.labelpad = 15
# ax.yaxis.labelpad = 15
# Adjust legend
plt.ylim(top=1.42)
# Set the limits appropriately
# Save the figure with tight layout
plt.tight_layout()
fig.savefig(f'{name}_path_lenght.png', dpi=300, bbox_inches='tight')
# Display the plot
plt.show()
# Configuration
num_nodes = 3
capacity_range = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30, 35, 40, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200, 1300, 1400, 1500, 1600, 1700, 1800, 1900, 2000, 2100, 2200, 2300, 2400, 2500, 3000, 3500, 4000, 5000]
transaction_amount = 1
fee_range = 0.0
epsilon = 0.002
num_runs = 10
window_size = 1000
avg_degree = 10
df1 = pd.read_pickle('3_node_line_len_vs_fee_capacity_denser.pkl')
df0 = pd.read_pickle('3_node_0_fee_line_len_vs_fee_capacity_denser.pkl')
df1['fee'] = 1.0
df0['fee'] = 0.0
df = pd.concat([df0, df1], ignore_index=True)
# Simulation
# df = simulate_network_network_size_variation(num_nodes, capacity_range, transaction_amount, fee_range, epsilon, window_size, num_runs, avg_degree, checkpointing=False, checkpoint_interval = num_runs)
# df.to_pickle('3_node_0_fee_line_len_vs_fee_capacity_denser.pkl')
plot_results_network_size_variation(df, '3_node_0_fee_line_denser')
mean_df = df.groupby('capacity')['success_rate'].agg(['mean', 'std']).reset_index()
print('Done!')
# Define the piecewise function with three different behaviors
def piecewise_function(x, a11, b11, c11, a33, x11):
# Create an empty array to store function values
a11, b11, c11, a33, x11 = map(float, [a11, b11, c11, a33, x11])
y = np.empty(x.shape)
expr = -np.exp(a11 / b11 - a33 / b11 - 1) * (c11 + x11)
x00 = ((-c11-x11)/(lambertw(expr)) - c11).real
# Print or log x00 value
# print("x00 value:", x00)
# if x00 >= 50 or x00 < 10:
# # print('x00 is too large')
# return np.inf
# Logarithmic for 1 - 20
cond1 = (x < x00)
y[cond1] = a11 + b11 * np.log(x[cond1] + c11)
# Linear for 20 - 2000
cond2 = (x >= x00) & (x < x11)
linear_start = a11 + b11 * np.log(x00 + c11)
linear_slope = (a33 - linear_start) / (x11 - x00)
y[cond2] = linear_start + linear_slope * (x[cond2] - x00)
# Constant for 2000 - end
cond3 = (x >= x11)
y[cond3] = a33
return y
# Define your piecewise_function and calculate_x00 here
def calculate_x00(a11, b11, c11, a33, x11):
# This function calculates x00 based on the parameters
a11, b11, c11, a33, x11 = map(float, [a11, b11, c11, a33, x11])
expr = -np.exp(a11 / b11 - a33 / b11 - 1) * (c11 + x11)
x00 = ((-c11-x11) / (lambertw(expr)) - c11).real
return x00
def calculate_rmse(y_observed, y_predicted):
return np.sqrt(np.mean((y_observed - y_predicted)**2))
def objective_function(params):
global iteration, start_time
current_time = time.time()
elapsed_time = current_time - start_time
# if iteration % 1000 == 0:
# print(f"Iteration {iteration}: Elapsed Time = {elapsed_time:.2f} seconds")
iteration += 1 # Increment the iteration
a11, b11, c11, a33, x11 = params
try:
# popt, pcov = curve_fit(piecewise_function, mean_df['capacity'], mean_df['mean'], p0=[a11, b11, c11, a33, x11])
# x00 = calculate_x00(*popt)
x_values = np.linspace(min(mean_df['capacity']), max(mean_df['capacity']), 1000000)
# predicted = piecewise_function(x_values, *popt)
predicted = piecewise_function(x_values, *params)
spline_interpolator = InterpolatedUnivariateSpline(mean_df['capacity'], mean_df['mean'])
interpolated_means = spline_interpolator(x_values)
rmse = calculate_rmse(interpolated_means, predicted)
# predicted = piecewise_function(mean_df['capacity'], *popt)
# rmse = calculate_rmse(mean_df['mean'], predicted)
alpha = 10
beta = 2
gamma = 0.22
if predicted is not np.inf and np.isnan(predicted).any() == False:
# Create boolean masks based on capacity values
# mask_log = mean_df['capacity'] <= 10
# mask_lin = (mean_df['capacity'] > 10) & (mean_df['capacity'] <= 2000)
# mask_const = mean_df['capacity'] > 2000
#
# # Use the masks to calculate RMSE for each segment
# rmse_log = calculate_rmse(mean_df['mean'][mask_log], predicted[mask_log])
# rmse_lin = calculate_rmse(mean_df['mean'][mask_lin], predicted[mask_lin])
# rmse_const = calculate_rmse(mean_df['mean'][mask_const], predicted[mask_const])
# Create masks based on x_values for each segment
mask_log = x_values <= 10
mask_lin = (x_values > 10) & (x_values <= 2000)
mask_const = x_values > 2000
# Use the masks to calculate RMSE for each segment
# Note: Make sure 'interpolated_means' and 'y_fitted' are arrays of the same length
rmse_log = calculate_rmse(interpolated_means[mask_log], predicted[mask_log])
rmse_lin = calculate_rmse(interpolated_means[mask_lin], predicted[mask_lin])
rmse_const = calculate_rmse(interpolated_means[mask_const], predicted[mask_const])
# if rmse_log > 0.0176 or rmse_const > 0.0033 or rmse_lin > 0.007:
# # return np.inf
# else:
scaled_rmse = alpha * rmse_log + beta * rmse_lin + gamma * rmse_const
return scaled_rmse
else:
return np.inf
except RuntimeError:
return np.inf
def objective_function_wrapper(params):
global iteration, start_time
result = objective_function(params, iteration, start_time)
iteration += 1
return result
# Set the bounds for the parameters in your function
# lb = [0.4, 0.01, -1.1, 0.85, 1000]
# ub = [0.6, 0.1, -0.8, 0.94, 3000]
lb = [0.53, 5.6e-2, -9.9e-1, 0.87, 1600]
ub = [0.55, 6.1e-2, -9.7e-1, 0.91, 1800]
global iteration, start_time
iteration = 0
start_time = time.time()
# Use Particle Swarm Optimization
xopt, fopt = pso(objective_function, lb, ub, debug = True, maxiter=200, swarmsize = 200)
# Initial guess for the parameters
# initial_guess = [ 0.55131752, 0.04371398, -0.99338461, 0.90358792, 2000] # Params for constant part and transition points
# popt, pcov = curve_fit(piecewise_function, mean_df['capacity'], mean_df['mean'], p0=xopt)
# #
# popt = [ 5.48458590e-01 , 5.75446022e-02 ,-9.76419302e-01 , 8.96383665e-01,
# 1.77790866e+03]
x00 = calculate_x00(*xopt)
print(x00)
print(xopt)
x_values = np.linspace(min(mean_df['capacity']), max(mean_df['capacity']), 1000000)
y_fitted = piecewise_function(x_values, *xopt)
fitted_df = pd.DataFrame({
'Capacity': x_values,
'Success Rate': y_fitted
})
cmap = sns.cubehelix_palette(as_cmap=True)
bg_color = plt.gcf().get_facecolor()
sns.set_theme()
fig, ax = plt.subplots(figsize=(8 / 1.2, 6 / 1.2), dpi=300)
# sns.lineplot(data=df_filtered, x='nodes', y='success_rate', hue='fee', marker='o', alpha = 0.9, ci='sd', legend='full')
sns.lineplot(data=mean_df, x='capacity', y='mean', marker='o', ci='sd', legend='full', label = 'Data')
sns.lineplot(data=fitted_df, x='Capacity', y='Success Rate', label='Fitted function', color='red', linestyle= 'dashed')
plt.xlabel('Capacity', fontsize=16)
plt.ylabel('Success Rate', fontsize=16)
plt.xticks(fontsize=14)
plt.yticks(fontsize=14)
plt.ylim([-0.01, 1.1])
# plt.xlim(left=-10)
plt.xlim([-0.01, 25])
plt.legend(loc='best')
plt.tight_layout()
# fig.savefig(f'fitted_function_3_nodes_1_fee_capacity.png', dpi=300, bbox_inches='tight')
plt.show()
plt.figure(figsize=(10, 6))
plt.scatter(mean_df['capacity'], mean_df['mean'], label='Data')
plt.plot(x_values, y_fitted, label='Fitted function', color='red')
plt.xlabel('Capacity')
plt.ylabel('Success Rate')
plt.xlim([-0.01, 25])
plt.legend()
plt.show()
plt.figure(figsize=(10, 6))
plt.scatter(mean_df['capacity'], mean_df['mean'], label='Data')
plt.plot(x_values, y_fitted, label='Fitted function', color='red')
plt.xlabel('Capacity')
plt.ylabel('Success Rate')
# plt.xlim([-0.01, 25])
plt.legend()
plt.show()
predicted = piecewise_function(mean_df['capacity'], *xopt)
plt.figure(figsize=(10, 6))
plt.scatter(mean_df['capacity'], mean_df['mean'], label='Data')
plt.plot(mean_df['capacity'], predicted, label='Fitted function', color='red', marker='o')
plt.xlabel('Capacity')
plt.ylabel('Success Rate')
plt.xlim([-0.01, 25])
plt.legend()
plt.show()
plt.figure(figsize=(10, 6))
plt.scatter(mean_df['capacity'], mean_df['mean'], label='Data')
plt.plot(mean_df['capacity'], predicted, label='Fitted function', color='red', marker='o')
plt.xlabel('Capacity')
plt.ylabel('Success Rate')
# plt.xlim([-0.01, 25])
plt.legend()
plt.show()
print('done!')