Skip to content

flgo.simulator.default_simulator

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

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.

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