This simulator supports for the following system heterogeneity:
availability_modes = {
'IDL': ideal_client_availability,
'YMF': y_max_first_client_availability,
'MDF': more_data_first_client_availability,
'LDF': less_data_first_client_availability,
'YFF': y_fewer_first_client_availability,
'HOMO': homogeneous_client_availability,
'LN': lognormal_client_availability,
'SLN': sin_lognormal_client_availability,
'YC': y_cycle_client_availability,
}
connectivity_modes = {
'IDL': ideal_client_connectivity,
'HOMO': homogeneous_client_connectivity,
}
completeness_modes = {
'IDL': ideal_client_completeness,
'PDU': part_dynamic_uniform_client_completeness,
'FSU': full_static_unifrom_client_completeness,
'ADU': arbitrary_dynamic_unifrom_client_completeness,
'ASU': arbitrary_static_unifrom_client_completeness,
}
responsiveness_modes = {
'IDL': ideal_client_responsiveness,
'LN': lognormal_client_responsiveness,
'UNI': uniform_client_responsiveness,
}
arbitrary_dynamic_unifrom_client_completeness(simulator, a=1, b=1)
This setting follows the setting in the paper 'Tackling the Objective Inconsistency Problem in
Heterogeneous Federated Optimization' (http://arxiv.org/abs/2007.07481). The string mode
should be like
'FEDNOVA-Uniform(a,b)' where a
is the minimal value of the number of local_movielens_recommendation epochs and b
is the maximal
value. If this mode is active, the num_epochs
and num_steps
of clients will be disable.
Source code in flgo\simulator\default_simulator.py
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241 | def arbitrary_dynamic_unifrom_client_completeness(simulator, a=1, b=1):
"""
This setting follows the setting in the paper 'Tackling the Objective Inconsistency Problem in
Heterogeneous Federated Optimization' (http://arxiv.org/abs/2007.07481). The string `mode` should be like
'FEDNOVA-Uniform(a,b)' where `a` is the minimal value of the number of local_movielens_recommendation epochs and `b` is the maximal
value. If this mode is active, the `num_epochs` and `num_steps` of clients will be disable.
"""
simulator._incomplete_a = min(a, 1)
simulator._incomplete_b = max(b, simulator._incomplete_a)
def f(self, client_ids = []):
for cid in client_ids:
self.clients[cid].set_local_epochs(self.random_module.randint(low=self._incomplete_a, high=self._incomplete_b))
working_amounts = [self.clients[cid].num_steps for cid in self.all_clients]
self.set_variable(self.all_clients, 'working_amount', working_amounts)
return
return f
|
arbitrary_static_unifrom_client_completeness(simulator, a=1, b=1)
This setting follows the setting in the paper 'Tackling the Objective Inconsistency Problem in
Heterogeneous Federated Optimization' (http://arxiv.org/abs/2007.07481). The string mode
should be like
'FEDNOVA-Uniform(a,b)' where a
is the minimal value of the number of local_movielens_recommendation epochs and b
is the maximal
value. If this mode is active, the num_epochs
and num_steps
of clients will be disable.
Source code in flgo\simulator\default_simulator.py
243
244
245
246
247
248
249
250
251
252
253
254
255
256 | def arbitrary_static_unifrom_client_completeness(simulator, a=1, b=1):
"""
This setting follows the setting in the paper 'Tackling the Objective Inconsistency Problem in
Heterogeneous Federated Optimization' (http://arxiv.org/abs/2007.07481). The string `mode` should be like
'FEDNOVA-Uniform(a,b)' where `a` is the minimal value of the number of local_movielens_recommendation epochs and `b` is the maximal
value. If this mode is active, the `num_epochs` and `num_steps` of clients will be disable.
"""
a = min(a, 1)
b = max(b, a)
for cid in simulator.clients:
simulator.clients[cid].set_local_epochs(np.random.randint(low=a, high=b))
working_amounts = [simulator.clients[cid].num_steps for cid in simulator.all_clients]
simulator.set_variable(simulator.all_clients, 'working_amount', working_amounts)
return
|
homogeneous_client_availability(simulator, beta=0.2)
All the clients share a homogeneous active rate 1-beta
where beta ∈ [0,1)
Source code in flgo\simulator\default_simulator.py
116
117
118
119
120
121
122
123
124 | def homogeneous_client_availability(simulator, beta=0.2):
"""
All the clients share a homogeneous active rate `1-beta` where beta ∈ [0,1)
"""
# alpha = float(mode[mode.find('-') + 1:]) if mode.find('-') != -1 else 0.8
probs = [1.-beta for _ in simulator.clients]
simulator.set_variable(simulator.all_clients, 'prob_available', probs)
simulator.set_variable(simulator.all_clients, 'prob_unavailable', [1 - p for p in probs])
simulator.roundwise_fixed_availability = True
|
less_data_first_client_availability(simulator, beta=0.5)
Clients with less data will have a larger active rate at each round.
ci=(1-beta)^(-|Di|), pi=ci/cmax, beta ∈ [0,1)
Source code in flgo\simulator\default_simulator.py
86
87
88
89
90
91
92
93
94
95
96
97
98 | def less_data_first_client_availability(simulator, beta=0.5):
"""
Clients with less data will have a larger active rate at each round.
ci=(1-beta)^(-|Di|), pi=ci/cmax, beta ∈ [0,1)
"""
# alpha = float(mode[mode.find('-') + 1:]) if mode.find('-') != -1 else 0.1
prop = np.array([len(c.train_data) for c in simulator.server.clients])
prop = prop ** (-beta)
maxp = np.max(prop)
probs = prop/maxp
simulator.set_variable(simulator.all_clients, 'prob_available', probs)
simulator.set_variable(simulator.all_clients, 'prob_unavailable', [1 - p for p in probs])
simulator.roundwise_fixed_availability = True
|
lognormal_client_availability(simulator, beta=0.1)
The following two settings are from 'Federated Learning Under Intermittent
Client Availability and Time-Varying Communication Constraints' (http://arxiv.org/abs/2205.06730).
ci ~ logmal(0, lognormal(0, -ln(1-beta)), pi=ci/cmax
Source code in flgo\simulator\default_simulator.py
126
127
128
129
130
131
132
133
134
135
136
137 | def lognormal_client_availability(simulator, beta=0.1):
"""The following two settings are from 'Federated Learning Under Intermittent
Client Availability and Time-Varying Communication Constraints' (http://arxiv.org/abs/2205.06730).
ci ~ logmal(0, lognormal(0, -ln(1-beta)), pi=ci/cmax
"""
epsilon = 0.000001
Tks = [np.random.lognormal(0, -np.log(1 - beta - epsilon)) for _ in simulator.clients]
max_Tk = max(Tks)
probs = np.array(Tks)/max_Tk
simulator.set_variable(simulator.all_clients, 'prob_available', probs)
simulator.set_variable(simulator.all_clients, 'prob_unavailable', [1 - p for p in probs])
simulator.roundwise_fixed_availability = True
|
more_data_first_client_availability(simulator, beta=0.0001)
Clients with more data will have a larger active rate at each round.
e.g. ci=tanh(-|Di| ln(beta+epsilon)), pi=ci/cmax, beta ∈ [0,1)
Source code in flgo\simulator\default_simulator.py
73
74
75
76
77
78
79
80
81
82
83
84 | def more_data_first_client_availability(simulator, beta=0.0001):
"""
Clients with more data will have a larger active rate at each round.
e.g. ci=tanh(-|Di| ln(beta+epsilon)), pi=ci/cmax, beta ∈ [0,1)
"""
p = np.array([len(c.train_data) for c in simulator.server.clients])
p = p ** beta
maxp = np.max(p)
probs = p/maxp
simulator.set_variable(simulator.all_clients, 'prob_available', probs)
simulator.set_variable(simulator.all_clients, 'prob_unavailable', [1 - p for p in probs])
simulator.roundwise_fixed_availability = True
|
This setting follows the setting in the paper 'Federated Optimization in Heterogeneous Networks'
(http://arxiv.org/abs/1812.06127). The p
specifies the number of selected clients with
incomplete updates.
Source code in flgo\simulator\default_simulator.py
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219 | def part_dynamic_uniform_client_completeness(simulator, p=0.5):
"""
This setting follows the setting in the paper 'Federated Optimization in Heterogeneous Networks'
(http://arxiv.org/abs/1812.06127). The `p` specifies the number of selected clients with
incomplete updates.
"""
simulator.prob_incomplete = p
def f(self, client_ids = []):
was = []
for cid in client_ids:
wa = self.random_module.randint(low=0, high=self.clients[cid].num_steps) if self.random_module.rand() < self.prob_incomplete else self.clients[cid].num_steps
wa = max(1, wa)
was.append(wa)
self.clients[cid].num_steps = wa
self.set_variable(client_ids, 'working_amount', was)
return
return f
|
sin_lognormal_client_availability(simulator, beta=0.1)
This setting shares the same active rate distribution with LogNormal, however, the active rates are
also influenced by the time (i.e. communication round). The active rates obey a sin wave according to the
time with period T.
ci ~ logmal(0, lognormal(0, -ln(1-beta)), pi=ci/cmax, p(i,t)=(0.4sin((1+R%T)/T*2pi)+0.5) * pi
Source code in flgo\simulator\default_simulator.py
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162 | def sin_lognormal_client_availability(simulator, beta=0.1):
"""This setting shares the same active rate distribution with LogNormal, however, the active rates are
also influenced by the time (i.e. communication round). The active rates obey a sin wave according to the
time with period T.
ci ~ logmal(0, lognormal(0, -ln(1-beta)), pi=ci/cmax, p(i,t)=(0.4sin((1+R%T)/T*2pi)+0.5) * pi
"""
# beta = float(mode[mode.find('-') + 1:]) if mode.find('-') != -1 else 0.1
epsilon = 0.000001
Tks = [np.random.lognormal(0, -np.log(1 - beta - epsilon)) for _ in simulator.clients]
max_Tk = max(Tks)
q = np.array(Tks)/max_Tk
simulator.set_variable(simulator.all_clients, 'q', q)
simulator.set_variable(simulator.all_clients, 'prob_available', q)
def f(self):
T = 24
times = np.linspace(start=0, stop=2 * np.pi, num=T)
fts = 0.4 * np.sin(times) + 0.5
t = self.server.current_round % T
q = self.get_variable(self.all_clients, 'q')
probs = [fts[t]*qi for qi in q]
self.set_variable(self.all_clients, 'prob_available', probs)
self.set_variable(self.all_clients, 'prob_unavailable', [1 - p for p in probs])
simulator.roundwise_fixed_availability = True
return f
|
y_fewer_first_client_availability(simulator, beta=0.2)
Clients with fewer kinds of labels will owe a larger active rate.
ci = |set(Yi)|/|set(Y)|, pi = beta*ci + (1-beta)
Source code in flgo\simulator\default_simulator.py
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114 | def y_fewer_first_client_availability(simulator, beta=0.2):
"""
Clients with fewer kinds of labels will owe a larger active rate.
ci = |set(Yi)|/|set(Y)|, pi = beta*ci + (1-beta)
"""
label_num = len(set([int(simulator.server.test_data[di][-1]) for di in range(len(simulator.server.test_data))]))
probs = []
for c in simulator.server.clients:
train_set = set([int(c.train_data[di][-1]) for di in range(len(c.train_data))])
val_set = set([int(c.val_data[di][-1]) for di in range(len(c.val_data))])
label_set = train_set.union(val_set)
probs.append(beta * len(label_set) / label_num + (1 - beta))
simulator.set_variable(simulator.all_clients, 'prob_available', probs)
simulator.set_variable(simulator.all_clients, 'prob_unavailable', [1 - p for p in probs])
simulator.roundwise_fixed_availability = True
|
y_max_first_client_availability(simulator, beta=0.1)
This setting follows the activity mode in 'Fast Federated Learning in the
Presence of Arbitrary Device Unavailability' , where each client ci will be ready
for joining in a round with a static probability
pi = beta * min({label kept by ci}) / max({all labels}) + ( 1 - beta )
and the participation of client is independent across rounds. The string mode
should be like 'YMaxFirst-x' where x should be replaced by a float number.
Source code in flgo\simulator\default_simulator.py
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71 | def y_max_first_client_availability(simulator, beta=0.1):
"""
This setting follows the activity mode in 'Fast Federated Learning in the
Presence of Arbitrary Device Unavailability' , where each client ci will be ready
for joining in a round with a static probability:
pi = beta * min({label kept by ci}) / max({all labels}) + ( 1 - beta )
and the participation of client is independent across rounds. The string mode
should be like 'YMaxFirst-x' where x should be replaced by a float number.
"""
# alpha = float(mode[mode.find('-') + 1:]) if mode.find('-') != -1 else 0.1
def label_counter(dataset):
return collections.Counter([int(dataset[di][-1]) for di in range(len(dataset))])
label_num = len(label_counter(simulator.server.test_data))
probs = []
for c in simulator.get_clients():
c_counter = label_counter((c.train_data + c.val_data) if c.val_data is not None else c.train_data)
c_label = [lb for lb in c_counter.keys()]
probs.append((beta * min(c_label) / max(1, label_num - 1)) + (1 - beta))
simulator.set_variable(simulator.all_clients, 'prob_available', probs)
simulator.set_variable(simulator.all_clients, 'prob_unavailable', [1 - p for p in probs])
simulator.roundwise_fixed_availability = True
return
|