-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmain_deltas.py
300 lines (254 loc) · 11.6 KB
/
main_deltas.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
from algorithms import *
import networkx as nx
import random
from math import log
from itertools import product, chain
import os, json
def create_problem(Network, Agents, T, N, K, param):
# param: (mp, n_gossip, gamma, p, n_repeat)
mp, _, gamma, _, _ = param
# set gamma and MP protocols in Agents
for agent in Agents:
agent.gamma = gamma
agent.mp = mp
agent.history = deque(maxlen=gamma * len(Network))
# create problem instance
return Problem(Network, Agents, T, N, K, param)
# mode: "uniform" or "nonuniform"
def create_uniform_arm_sets(N, K, k):
while True:
arm_sets = [random.sample(range(K), k) for _ in range(N)] # uniformly random
# check if all arms are covered
if set(range(K)) == set(chain.from_iterable(arm_sets)):
break
return arm_sets
## previous ver
# def create_nonuniform_arm_sets(Network, K, arm_sets_uniform):
# N = Network.number_of_nodes()
# arm_sets = arm_sets_uniform
# # prob distribution w.r.t. which the agents to be assigned the globally optimal arm K - 1
# ps = [Network.degree(v) / (2 * Network.number_of_edges()) for v in range(N)]
# # make sure that the globally optimal arm K - 1 is in every arm set, initially
# for arm_set in arm_sets:
# if K - 1 not in arm_set:
# arm_set.append(K - 1)
# # remove K - 1 from agents w.p. (roughly) proportional to the degree
# remove_agents = np.random.choice(range(N), int(2*N / 3), replace=False, p=ps)
# for v in remove_agents:
# arm_sets[v].remove(K - 1)
# return arm_sets
# Thompson model-like arm distribution
# randomized algorithm that distributes the arms in a maximally separated fashion
def create_nonuniform_arm_sets(Network, K, k, min_dist=5):
N = Network.number_of_nodes()
arm_sets = [[] for _ in range(N)]
Network_power = nx.power(Network, min_dist)
vertices_not_covered = set(range(N))
for a in range(K):
# random maximal independent set, whose vertices are at least min_dist apart
max_ind_set = nx.maximal_independent_set(Network_power)
for v in max_ind_set:
arm_sets[v].append(a)
if v in vertices_not_covered:
vertices_not_covered.remove(v)
while len(vertices_not_covered) > 0:
v = vertices_not_covered.pop()
# for a in range(K):
# for w in Network.neighbors(v):
# if a not in arm_sets[w]:
# arm_sets[v].append(a)
# if v in vertices_not_covered:
# vertices_not_covered.remove(v)
arm_sets[v] = np.random.choice(range(K), k, replace=False).tolist()
return arm_sets
def main_parallel(Network, Agents_, T, N, K, mps, n_gossips, gammas, ps, n_repeats):
exp_type = "vary_gamma"
# n_gossip, mp, gamma, p, repeat
params = list(product(mps, n_gossips, gammas, ps, range(n_repeats)))
# run experiment only once for baseline
params = [item for item in params if "baseline" not in item[0] or ("baseline" in item[0] and item[1] is None)]
# remove Hitting+Gossiping
params = [item for item in params if "Absorption" not in item[0] or item[1] is None]
# remove RS+Gossiping
params = [item for item in params if "RandomStop" not in item[0] or item[1] is None]
def F(param):
# reseeding!
np.random.seed(int.from_bytes(os.urandom(4), byteorder='little'))
# create problem instance
Agents = deepcopy(Agents_)
problem = create_problem(Network, Agents, T, N, K, param)
result = run_ucb(problem, param[-2])
# print process id
print(f"Finished: {multiprocess.process.current_process()}, {param}")
return result # Group_Regrets, Group_Communications, np.array(Edge_Messages), message_absorption_times
# run the experiments in parallel
with Pool() as pool:
everything = pool.map_async(F, params)
everything = everything.get()
if exp_type == "vary_p":
length = len(ps)
l = ps
x_label = "p"
gamma = gammas[0]
elif exp_type == "vary_gamma":
length = len(gammas)
l = gammas
x_label = "gamma"
p = ps[0]
elif exp_type == "vary_t":
length = T
l = range(T)
x_label = "t"
p = ps[0]
gamma = gammas[0]
else:
raise ValueError("Are we fixing p or gamma?")
print("Data collection and plotting!")
# partial_param = (n_gossip, mp)
def f(partial_param):
total_regret = np.zeros((n_repeats, length))
total_communication = np.zeros((n_repeats, length))
edge_messages = np.zeros((n_repeats, length))
for repeat, i in product(range(n_repeats), range(length)):
if exp_type == "vary_t":
idx = params.index(partial_param + (gamma, p, repeat))
total_regret[repeat][i] = everything[idx][0][i]
total_communication[repeat][i] = everything[idx][1][i]
# if i == 0:
# message_absorption_times += everything[idx][3]
# number of messages passing through the bottleneck edge!
edge_messages[repeat][i] = everything[idx][2][i]
else:
if exp_type == "vary_p":
idx = params.index(partial_param + (gamma, l[i], repeat))
elif exp_type == "vary_gamma":
idx = params.index(partial_param + (l[i], p, repeat))
else:
raise ValueError("Are we fixing p or gamma?")
# final regret and final communication complexity only!
total_regret[repeat][i] = everything[idx][0][-1]
total_communication[repeat][i] = everything[idx][1][-1]
return total_regret, total_communication, edge_messages
# collect datas in parallel
partial_params = list(product(mps, n_gossips))
# run experiment only once for baseline
partial_params = [item for item in partial_params if
"baseline" not in item[0] or ("baseline" in item[0] and item[1] is None)]
# remove Hitting+Gossiping
partial_params = [item for item in partial_params if "Absorption" not in item[0] or item[1] is None]
# remove RS+Gossiping
partial_params = [item for item in partial_params if "RandomStop" not in item[0] or item[1] is None]
with Pool() as pool:
finals = pool.map_async(f, partial_params)
finals = finals.get()
final_regrets_mean, final_regrets_std = [], []
final_communications_mean, final_communications_std = [], []
final_messages_mean, final_messages_std = [], []
for total_regret, total_communication, edge_message in finals:
# for total_regret, total_communication, edge_message, total_absorption_time in finals:
final_regrets_mean.append(np.mean(total_regret, axis=0))
final_regrets_std.append(np.std(total_regret, axis=0))
final_communications_mean.append(np.mean(total_communication, axis=0))
final_communications_std.append(np.std(total_communication, axis=0))
# final_absorption_times.append(total_absorption_time)
final_messages_mean.append(np.mean(edge_message, axis=0))
final_messages_std.append(np.std(edge_message, axis=0))
return final_regrets_mean, final_regrets_std
if __name__ == '__main__':
num_clusters = 4 # for SBM
size_cluster = 25
N = size_cluster * num_clusters # number of agents
er_ps = [1e-2 / N, 1e-1 / N, 1 / N, 2 / N, 3 / N, 4 / N, 5 / N, 6 / N, 7 / N, 8 / N, 9 / N, 10 / N]
T = int(1e3) # number of iterations
K = 50 # total number of arms
k = 20 # number of arms per agent
n_repeats = 10
# create communication networks
Networks = {}
if not os.path.exists("deltas/networks"):
os.makedirs("deltas/networks")
base_path = f"deltas/results-uniform_N_{N}_K_{K}_k_{k}"
if not os.path.exists(base_path):
os.makedirs(base_path)
# arm set and their mean rewards
if not os.path.exists(f"{base_path}/means.npz"):
tmp = np.sort(np.random.uniform(size=K))
np.savez(f"{base_path}/means.npz", tmp=tmp)
else:
tmp = np.load(f"{base_path}/means.npz")
tmp = tmp['tmp']
# if not os.path.exists("results-{uniform}/means.npz"):
# tmp = np.sort(np.random.uniform(size=K))
# np.savez("results-{uniform}/means.npz", tmp=tmp)
# else:
# tmp = np.load("results-{uniform}/means.npz")
# tmp = tmp['tmp']
# tmp = [0.5 * int(k == K-1) + 0.5 for k in range(K)]
# tmp = [np.random.uniform() * int(k != K-1) + int(k == K-1) for k in range(K)]
reward_avgs = {a: tmp[a] for a in range(K)}
total_arm_set = list(range(K))
# uniform arm distribution
if not os.path.exists(f"{base_path}/arm_sets_uniform.json"):
arm_sets_uniform = create_uniform_arm_sets(N, K, k)
with open(f"{base_path}/arm_sets_uniform.json", "w") as f:
json.dump(arm_sets_uniform, f)
else:
with open(f"{base_path}/arm_sets_uniform.json", "r") as f:
arm_sets_uniform = json.load(f)
regret_Flooding_mean, regret_FwA_mean = [], []
regret_Flooding_std, regret_FwA_std = [], []
for er_p in er_ps:
print(er_p)
## Erodos-Renyi
# if the graph is disconnected, keep trying other seeds until the graph is connected.
u = 2023
Network_ER = nx.erdos_renyi_graph(N, er_p, seed=u)
Network_ER.name = f"ER_{er_p}"
pos_ER = nx.spring_layout(Network_ER)
Networks[er_p] = (Network_ER, pos_ER)
plot_network(Network_ER, pos_ER, parent="deltas/networks")
# experiments
# create paths
if not os.path.exists(f"{base_path}/networks"):
os.makedirs(f"{base_path}/networks")
# load Network
Network, pos = Network_ER, pos_ER
arm_sets = arm_sets_uniform
# create agents with the distributed arms
Agents = [Agent(v, arm_sets[v], reward_avgs, Network, 0, 0, K) for v in range(N)]
# plot arm-specific networks
for arm in range(K):
color_map = []
for v in range(N):
if arm in Agents[v].arm_set:
color_map.append('red')
else:
color_map.append('blue')
# color the agents corresponding to the arm
f = plt.figure(1000 * arm)
plot_network(Network, pos, fname=f"deltas/networks/{Network.name}_{arm}.pdf", node_color=color_map)
plt.close()
mps, n_gossips = ["Flooding-Absorption", "Flooding"], [None]
gammas = [4]
ps = [1.0]
means, stds = main_parallel(Network, Agents, T, N, K, mps, n_gossips, gammas, ps, n_repeats)
regret_Flooding_mean.append(means[0])
regret_Flooding_std.append(stds[0])
regret_FwA_mean.append(means[1])
regret_FwA_std.append(stds[1])
regret_Flooding_mean = np.array(regret_Flooding_mean)
regret_Flooding_std = np.array(regret_Flooding_std)
regret_FwA_mean = np.array(regret_FwA_mean)
regret_FwA_std = np.array(regret_FwA_std)
# save and plot
np.savez(f"{base_path}/regret_Flooding.npz", regret_Flooding_mean=regret_Flooding_mean, regret_Flooding_std=regret_Flooding_std)
np.savez(f"{base_path}/regret_FwA.npz", regret_FwA_mean=regret_FwA_mean, regret_FwA_std=regret_FwA_std)
plt.figure()
plt.errorbar(er_ps, regret_Flooding_mean[:, -1], yerr=regret_Flooding_std[:, -1], label="Flooding", capsize=5)
plt.errorbar(er_ps, regret_FwA_mean[:, -1], yerr=regret_FwA_std[:, -1], label="Flooding-Absorption", capsize=5)
plt.xlabel("er_p")
plt.ylabel("Regret")
plt.legend()
plt.savefig(f"{base_path}/regret.pdf")
plt.show()
plt.clf()