flwr-nightly 1.8.0.dev20240223__py3-none-any.whl → 1.8.0.dev20240227__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
flwr/client/__init__.py CHANGED
@@ -19,7 +19,7 @@ from .app import run_client_app as run_client_app
19
19
  from .app import start_client as start_client
20
20
  from .app import start_numpy_client as start_numpy_client
21
21
  from .client import Client as Client
22
- from .clientapp import ClientApp as ClientApp
22
+ from .client_app import ClientApp as ClientApp
23
23
  from .numpy_client import NumPyClient as NumPyClient
24
24
  from .typing import ClientFn as ClientFn
25
25
 
flwr/client/app.py CHANGED
@@ -23,7 +23,7 @@ from pathlib import Path
23
23
  from typing import Callable, ContextManager, Optional, Tuple, Union
24
24
 
25
25
  from flwr.client.client import Client
26
- from flwr.client.clientapp import ClientApp
26
+ from flwr.client.client_app import ClientApp
27
27
  from flwr.client.typing import ClientFn
28
28
  from flwr.common import GRPC_MAX_MESSAGE_LENGTH, EventType, Message, event
29
29
  from flwr.common.address import parse_address
@@ -34,9 +34,10 @@ from flwr.common.constant import (
34
34
  TRANSPORT_TYPE_REST,
35
35
  TRANSPORT_TYPES,
36
36
  )
37
+ from flwr.common.exit_handlers import register_exit_handlers
37
38
  from flwr.common.logger import log, warn_deprecated_feature, warn_experimental_feature
38
39
 
39
- from .clientapp import load_client_app
40
+ from .client_app import load_client_app
40
41
  from .grpc_client.connection import grpc_connection
41
42
  from .grpc_rere_client.connection import grpc_request_response
42
43
  from .message_handler.message_handler import handle_control_message
@@ -104,7 +105,7 @@ def run_client_app() -> None:
104
105
  root_certificates=root_certificates,
105
106
  insecure=args.insecure,
106
107
  )
107
- event(EventType.RUN_CLIENT_APP_LEAVE)
108
+ register_exit_handlers(event_type=EventType.RUN_CLIENT_APP_LEAVE)
108
109
 
109
110
 
110
111
  def _parse_args_run_client_app() -> argparse.ArgumentParser:
@@ -98,7 +98,7 @@ def handle_legacy_message_from_msgtype(
98
98
  client_fn: ClientFn, message: Message, context: Context
99
99
  ) -> Message:
100
100
  """Handle legacy message in the inner most mod."""
101
- client = client_fn(str(message.metadata.dst_node_id))
101
+ client = client_fn(str(message.metadata.partition_id))
102
102
 
103
103
  # Check if NumPyClient is returend
104
104
  if isinstance(client, NumPyClient):
@@ -15,10 +15,12 @@
15
15
  """Mods."""
16
16
 
17
17
 
18
+ from .centraldp_mods import fixedclipping_mod
18
19
  from .secure_aggregation.secaggplus_mod import secaggplus_mod
19
20
  from .utils import make_ffn
20
21
 
21
22
  __all__ = [
22
23
  "make_ffn",
23
24
  "secaggplus_mod",
25
+ "fixedclipping_mod",
24
26
  ]
@@ -0,0 +1,76 @@
1
+ # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+ """Clipping modifiers for central DP with client-side clipping."""
16
+
17
+
18
+ from flwr.client.typing import ClientAppCallable
19
+ from flwr.common import ndarrays_to_parameters, parameters_to_ndarrays
20
+ from flwr.common import recordset_compat as compat
21
+ from flwr.common.constant import MESSAGE_TYPE_FIT
22
+ from flwr.common.context import Context
23
+ from flwr.common.differential_privacy import compute_clip_model_update
24
+ from flwr.common.differential_privacy_constants import KEY_CLIPPING_NORM
25
+ from flwr.common.message import Message
26
+
27
+
28
+ def fixedclipping_mod(
29
+ msg: Message, ctxt: Context, call_next: ClientAppCallable
30
+ ) -> Message:
31
+ """Client-side fixed clipping modifier.
32
+
33
+ This mod needs to be used with the DifferentialPrivacyClientSideFixedClipping
34
+ server-side strategy wrapper.
35
+
36
+ The wrapper sends the clipping_norm value to the client.
37
+
38
+ This mod clips the client model updates before sending them to the server.
39
+
40
+ It operates on messages with type MESSAGE_TYPE_FIT.
41
+
42
+ Notes
43
+ -----
44
+ Consider the order of mods when using multiple.
45
+
46
+ Typically, fixedclipping_mod should be the last to operate on params.
47
+ """
48
+ if msg.metadata.message_type != MESSAGE_TYPE_FIT:
49
+ return call_next(msg, ctxt)
50
+ fit_ins = compat.recordset_to_fitins(msg.content, keep_input=True)
51
+ if KEY_CLIPPING_NORM not in fit_ins.config:
52
+ raise KeyError(
53
+ f"The {KEY_CLIPPING_NORM} value is not supplied by the "
54
+ f"DifferentialPrivacyClientSideFixedClipping wrapper at"
55
+ f" the server side."
56
+ )
57
+
58
+ clipping_norm = float(fit_ins.config[KEY_CLIPPING_NORM])
59
+ server_to_client_params = parameters_to_ndarrays(fit_ins.parameters)
60
+
61
+ # Call inner app
62
+ out_msg = call_next(msg, ctxt)
63
+ fit_res = compat.recordset_to_fitres(out_msg.content, keep_input=True)
64
+
65
+ client_to_server_params = parameters_to_ndarrays(fit_res.parameters)
66
+
67
+ # Clip the client update
68
+ compute_clip_model_update(
69
+ client_to_server_params,
70
+ server_to_client_params,
71
+ clipping_norm,
72
+ )
73
+
74
+ fit_res.parameters = ndarrays_to_parameters(client_to_server_params)
75
+ out_msg.content = compat.fitres_to_recordset(fit_res, keep_input=True)
76
+ return out_msg
@@ -14,6 +14,8 @@
14
14
  # ==============================================================================
15
15
  """Constants for differential privacy."""
16
16
 
17
+
18
+ KEY_CLIPPING_NORM = "clipping_norm"
17
19
  CLIENTS_DISCREPANCY_WARNING = (
18
20
  "The number of clients returning parameters (%s)"
19
21
  " differs from the number of sampled clients (%s)."
@@ -0,0 +1,87 @@
1
+ # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+ """Common function to register exit handlers for server and client."""
16
+
17
+
18
+ import sys
19
+ from signal import SIGINT, SIGTERM, signal
20
+ from threading import Thread
21
+ from types import FrameType
22
+ from typing import List, Optional
23
+
24
+ from grpc import Server
25
+
26
+ from flwr.common.telemetry import EventType, event
27
+
28
+
29
+ def register_exit_handlers(
30
+ event_type: EventType,
31
+ grpc_servers: Optional[List[Server]] = None,
32
+ bckg_threads: Optional[List[Thread]] = None,
33
+ ) -> None:
34
+ """Register exit handlers for `SIGINT` and `SIGTERM` signals.
35
+
36
+ Parameters
37
+ ----------
38
+ event_type : EventType
39
+ The telemetry event that should be logged before exit.
40
+ grpc_servers: Optional[List[Server]] (default: None)
41
+ An otpional list of gRPC servers that need to be gracefully
42
+ terminated before exiting.
43
+ bckg_threads: Optional[List[Thread]] (default: None)
44
+ An optional list of threads that need to be gracefully
45
+ terminated before exiting.
46
+ """
47
+ default_handlers = {
48
+ SIGINT: None,
49
+ SIGTERM: None,
50
+ }
51
+
52
+ def graceful_exit_handler( # type: ignore
53
+ signalnum,
54
+ frame: FrameType, # pylint: disable=unused-argument
55
+ ) -> None:
56
+ """Exit handler to be registered with `signal.signal`.
57
+
58
+ When called will reset signal handler to original signal handler from
59
+ default_handlers.
60
+ """
61
+ # Reset to default handler
62
+ signal(signalnum, default_handlers[signalnum])
63
+
64
+ event_res = event(event_type=event_type)
65
+
66
+ if grpc_servers is not None:
67
+ for grpc_server in grpc_servers:
68
+ grpc_server.stop(grace=1)
69
+
70
+ if bckg_threads is not None:
71
+ for bckg_thread in bckg_threads:
72
+ bckg_thread.join()
73
+
74
+ # Ensure event has happend
75
+ event_res.result()
76
+
77
+ # Setup things for graceful exit
78
+ sys.exit(0)
79
+
80
+ default_handlers[SIGINT] = signal( # type: ignore
81
+ SIGINT,
82
+ graceful_exit_handler, # type: ignore
83
+ )
84
+ default_handlers[SIGTERM] = signal( # type: ignore
85
+ SIGTERM,
86
+ graceful_exit_handler, # type: ignore
87
+ )
flwr/common/message.py CHANGED
@@ -14,7 +14,6 @@
14
14
  # ==============================================================================
15
15
  """Message."""
16
16
 
17
-
18
17
  from __future__ import annotations
19
18
 
20
19
  from dataclasses import dataclass
@@ -46,6 +45,10 @@ class Metadata: # pylint: disable=too-many-instance-attributes
46
45
  message_type : str
47
46
  A string that encodes the action to be executed on
48
47
  the receiving end.
48
+ partition_id : Optional[int]
49
+ An identifier that can be used when loading a particular
50
+ data partition for a ClientApp. Making use of this identifier
51
+ is more relevant when conducting simulations.
49
52
  """
50
53
 
51
54
  _run_id: int
@@ -56,6 +59,7 @@ class Metadata: # pylint: disable=too-many-instance-attributes
56
59
  _group_id: str
57
60
  _ttl: str
58
61
  _message_type: str
62
+ _partition_id: int | None
59
63
 
60
64
  def __init__( # pylint: disable=too-many-arguments
61
65
  self,
@@ -67,6 +71,7 @@ class Metadata: # pylint: disable=too-many-instance-attributes
67
71
  group_id: str,
68
72
  ttl: str,
69
73
  message_type: str,
74
+ partition_id: int | None = None,
70
75
  ) -> None:
71
76
  self._run_id = run_id
72
77
  self._message_id = message_id
@@ -76,6 +81,7 @@ class Metadata: # pylint: disable=too-many-instance-attributes
76
81
  self._group_id = group_id
77
82
  self._ttl = ttl
78
83
  self._message_type = message_type
84
+ self._partition_id = partition_id
79
85
 
80
86
  @property
81
87
  def run_id(self) -> int:
@@ -137,6 +143,16 @@ class Metadata: # pylint: disable=too-many-instance-attributes
137
143
  """Set message_type."""
138
144
  self._message_type = value
139
145
 
146
+ @property
147
+ def partition_id(self) -> int | None:
148
+ """An identifier telling which data partition a ClientApp should use."""
149
+ return self._partition_id
150
+
151
+ @partition_id.setter
152
+ def partition_id(self, value: int) -> None:
153
+ """Set patition_id."""
154
+ self._partition_id = value
155
+
140
156
 
141
157
  @dataclass
142
158
  class Message:
@@ -202,6 +218,7 @@ class Message:
202
218
  group_id=self.metadata.group_id,
203
219
  ttl=ttl,
204
220
  message_type=self.metadata.message_type,
221
+ partition_id=self.metadata.partition_id,
205
222
  ),
206
223
  content=content,
207
224
  )
flwr/server/app.py CHANGED
@@ -22,8 +22,6 @@ import threading
22
22
  from logging import ERROR, INFO, WARN
23
23
  from os.path import isfile
24
24
  from pathlib import Path
25
- from signal import SIGINT, SIGTERM, signal
26
- from types import FrameType
27
25
  from typing import List, Optional, Tuple
28
26
 
29
27
  import grpc
@@ -36,6 +34,7 @@ from flwr.common.constant import (
36
34
  TRANSPORT_TYPE_REST,
37
35
  TRANSPORT_TYPE_VCE,
38
36
  )
37
+ from flwr.common.exit_handlers import register_exit_handlers
39
38
  from flwr.common.logger import log
40
39
  from flwr.proto.driver_pb2_grpc import ( # pylint: disable=E0611
41
40
  add_DriverServicer_to_server,
@@ -212,10 +211,10 @@ def run_driver_api() -> None:
212
211
  )
213
212
 
214
213
  # Graceful shutdown
215
- _register_exit_handlers(
214
+ register_exit_handlers(
215
+ event_type=EventType.RUN_DRIVER_API_LEAVE,
216
216
  grpc_servers=[grpc_server],
217
217
  bckg_threads=[],
218
- event_type=EventType.RUN_DRIVER_API_LEAVE,
219
218
  )
220
219
 
221
220
  # Block
@@ -280,10 +279,10 @@ def run_fleet_api() -> None:
280
279
  raise ValueError(f"Unknown fleet_api_type: {args.fleet_api_type}")
281
280
 
282
281
  # Graceful shutdown
283
- _register_exit_handlers(
282
+ register_exit_handlers(
283
+ event_type=EventType.RUN_FLEET_API_LEAVE,
284
284
  grpc_servers=grpc_servers,
285
285
  bckg_threads=bckg_threads,
286
- event_type=EventType.RUN_FLEET_API_LEAVE,
287
286
  )
288
287
 
289
288
  # Block
@@ -375,10 +374,10 @@ def run_superlink() -> None:
375
374
  raise ValueError(f"Unknown fleet_api_type: {args.fleet_api_type}")
376
375
 
377
376
  # Graceful shutdown
378
- _register_exit_handlers(
377
+ register_exit_handlers(
378
+ event_type=EventType.RUN_SUPERLINK_LEAVE,
379
379
  grpc_servers=grpc_servers,
380
380
  bckg_threads=bckg_threads,
381
- event_type=EventType.RUN_SUPERLINK_LEAVE,
382
381
  )
383
382
 
384
383
  # Block
@@ -413,52 +412,6 @@ def _try_obtain_certificates(
413
412
  return certificates
414
413
 
415
414
 
416
- def _register_exit_handlers(
417
- grpc_servers: List[grpc.Server],
418
- bckg_threads: List[threading.Thread],
419
- event_type: EventType,
420
- ) -> None:
421
- default_handlers = {
422
- SIGINT: None,
423
- SIGTERM: None,
424
- }
425
-
426
- def graceful_exit_handler( # type: ignore
427
- signalnum,
428
- frame: FrameType, # pylint: disable=unused-argument
429
- ) -> None:
430
- """Exit handler to be registered with signal.signal.
431
-
432
- When called will reset signal handler to original signal handler from
433
- default_handlers.
434
- """
435
- # Reset to default handler
436
- signal(signalnum, default_handlers[signalnum])
437
-
438
- event_res = event(event_type=event_type)
439
-
440
- for grpc_server in grpc_servers:
441
- grpc_server.stop(grace=1)
442
-
443
- for bckg_thread in bckg_threads:
444
- bckg_thread.join()
445
-
446
- # Ensure event has happend
447
- event_res.result()
448
-
449
- # Setup things for graceful exit
450
- sys.exit(0)
451
-
452
- default_handlers[SIGINT] = signal( # type: ignore
453
- SIGINT,
454
- graceful_exit_handler, # type: ignore
455
- )
456
- default_handlers[SIGTERM] = signal( # type: ignore
457
- SIGTERM,
458
- graceful_exit_handler, # type: ignore
459
- )
460
-
461
-
462
415
  def _run_driver_api_grpc(
463
416
  address: str,
464
417
  state_factory: StateFactory,
@@ -44,6 +44,7 @@ class Driver:
44
44
  Tuple containing root certificate, server certificate, and private key
45
45
  to start a secure SSL-enabled server. The tuple is expected to have
46
46
  three bytes elements in the following order:
47
+
47
48
  * CA certificate.
48
49
  * server certificate.
49
50
  * server private key.
@@ -16,7 +16,10 @@
16
16
 
17
17
 
18
18
  from .bulyan import Bulyan as Bulyan
19
- from .dp_fixed_clipping import DifferentialPrivacyServerSideFixedClipping
19
+ from .dp_fixed_clipping import (
20
+ DifferentialPrivacyClientSideFixedClipping,
21
+ DifferentialPrivacyServerSideFixedClipping,
22
+ )
20
23
  from .dpfedavg_adaptive import DPFedAvgAdaptive as DPFedAvgAdaptive
21
24
  from .dpfedavg_fixed import DPFedAvgFixed as DPFedAvgFixed
22
25
  from .fault_tolerant_fedavg import FaultTolerantFedAvg as FaultTolerantFedAvg
@@ -59,4 +62,5 @@ __all__ = [
59
62
  "DPFedAvgFixed",
60
63
  "Strategy",
61
64
  "DifferentialPrivacyServerSideFixedClipping",
65
+ "DifferentialPrivacyClientSideFixedClipping",
62
66
  ]
@@ -36,7 +36,10 @@ from flwr.common.differential_privacy import (
36
36
  add_gaussian_noise_to_params,
37
37
  compute_clip_model_update,
38
38
  )
39
- from flwr.common.differential_privacy_constants import CLIENTS_DISCREPANCY_WARNING
39
+ from flwr.common.differential_privacy_constants import (
40
+ CLIENTS_DISCREPANCY_WARNING,
41
+ KEY_CLIPPING_NORM,
42
+ )
40
43
  from flwr.common.logger import log
41
44
  from flwr.server.client_manager import ClientManager
42
45
  from flwr.server.client_proxy import ClientProxy
@@ -44,7 +47,8 @@ from flwr.server.strategy.strategy import Strategy
44
47
 
45
48
 
46
49
  class DifferentialPrivacyServerSideFixedClipping(Strategy):
47
- """Wrapper for Central DP with Server Side Fixed Clipping.
50
+ """Strategy wrapper for central differential privacy with server-side fixed
51
+ clipping.
48
52
 
49
53
  Parameters
50
54
  ----------
@@ -185,3 +189,153 @@ class DifferentialPrivacyServerSideFixedClipping(Strategy):
185
189
  ) -> Optional[Tuple[float, Dict[str, Scalar]]]:
186
190
  """Evaluate model parameters using an evaluation function from the strategy."""
187
191
  return self.strategy.evaluate(server_round, parameters)
192
+
193
+
194
+ class DifferentialPrivacyClientSideFixedClipping(Strategy):
195
+ """Strategy wrapper for central differential privacy with client-side fixed
196
+ clipping.
197
+
198
+ Use `fixedclipping_mod` modifier at the client side.
199
+
200
+ In comparison to `DifferentialPrivacyServerSideFixedClipping`,
201
+ which performs clipping on the server-side, `DifferentialPrivacyClientSideFixedClipping`
202
+ expects clipping to happen on the client-side, usually by using the built-in
203
+ `fixedclipping_mod `.
204
+
205
+ Parameters
206
+ ----------
207
+ strategy : Strategy
208
+ The strategy to which DP functionalities will be added by this wrapper.
209
+ noise_multiplier : float
210
+ The noise multiplier for the Gaussian mechanism for model updates.
211
+ A value of 1.0 or higher is recommended for strong privacy.
212
+ clipping_norm : float
213
+ The value of the clipping norm.
214
+ num_sampled_clients : int
215
+ The number of clients that are sampled on each round.
216
+
217
+ Examples
218
+ --------
219
+ Create a strategy:
220
+
221
+ >>> strategy = fl.server.strategy.FedAvg(...)
222
+
223
+ Wrap the strategy with the `DifferentialPrivacyServerSideFixedClipping` wrapper:
224
+
225
+ >>> DifferentialPrivacyClientSideFixedClipping(
226
+ >>> strategy, cfg.noise_multiplier, cfg.clipping_norm, cfg.num_sampled_clients
227
+ >>> )
228
+
229
+ On the client, add the `fixedclipping_mod` to the client-side mods:
230
+
231
+ >>> app = fl.client.ClientApp(
232
+ >>> client_fn=FlowerClient().to_client(), mods=[fixedclipping_mod]
233
+ >>> )
234
+ """
235
+
236
+ # pylint: disable=too-many-arguments,too-many-instance-attributes
237
+ def __init__(
238
+ self,
239
+ strategy: Strategy,
240
+ noise_multiplier: float,
241
+ clipping_norm: float,
242
+ num_sampled_clients: int,
243
+ ) -> None:
244
+ super().__init__()
245
+
246
+ self.strategy = strategy
247
+
248
+ if noise_multiplier < 0:
249
+ raise ValueError("The noise multiplier should be a non-negative value.")
250
+
251
+ if clipping_norm <= 0:
252
+ raise ValueError("The clipping threshold should be a positive value.")
253
+
254
+ if num_sampled_clients <= 0:
255
+ raise ValueError(
256
+ "The number of sampled clients should be a positive value."
257
+ )
258
+
259
+ self.noise_multiplier = noise_multiplier
260
+ self.clipping_norm = clipping_norm
261
+ self.num_sampled_clients = num_sampled_clients
262
+
263
+ def __repr__(self) -> str:
264
+ """Compute a string representation of the strategy."""
265
+ rep = "Differential Privacy Strategy Wrapper (Client-Side Fixed Clipping)"
266
+ return rep
267
+
268
+ def initialize_parameters(
269
+ self, client_manager: ClientManager
270
+ ) -> Optional[Parameters]:
271
+ """Initialize global model parameters using given strategy."""
272
+ return self.strategy.initialize_parameters(client_manager)
273
+
274
+ def configure_fit(
275
+ self, server_round: int, parameters: Parameters, client_manager: ClientManager
276
+ ) -> List[Tuple[ClientProxy, FitIns]]:
277
+ """Configure the next round of training."""
278
+ additional_config = {KEY_CLIPPING_NORM: self.clipping_norm}
279
+ inner_strategy_config_result = self.strategy.configure_fit(
280
+ server_round, parameters, client_manager
281
+ )
282
+ for _, fit_ins in inner_strategy_config_result:
283
+ fit_ins.config.update(additional_config)
284
+
285
+ return inner_strategy_config_result
286
+
287
+ def configure_evaluate(
288
+ self, server_round: int, parameters: Parameters, client_manager: ClientManager
289
+ ) -> List[Tuple[ClientProxy, EvaluateIns]]:
290
+ """Configure the next round of evaluation."""
291
+ return self.strategy.configure_evaluate(
292
+ server_round, parameters, client_manager
293
+ )
294
+
295
+ def aggregate_fit(
296
+ self,
297
+ server_round: int,
298
+ results: List[Tuple[ClientProxy, FitRes]],
299
+ failures: List[Union[Tuple[ClientProxy, FitRes], BaseException]],
300
+ ) -> Tuple[Optional[Parameters], Dict[str, Scalar]]:
301
+ """Add noise to the aggregated parameters."""
302
+ if failures:
303
+ return None, {}
304
+
305
+ if len(results) != self.num_sampled_clients:
306
+ log(
307
+ WARNING,
308
+ CLIENTS_DISCREPANCY_WARNING,
309
+ len(results),
310
+ self.num_sampled_clients,
311
+ )
312
+
313
+ # Pass the new parameters for aggregation
314
+ aggregated_params, metrics = self.strategy.aggregate_fit(
315
+ server_round, results, failures
316
+ )
317
+
318
+ # Add Gaussian noise to the aggregated parameters
319
+ if aggregated_params:
320
+ aggregated_params = add_gaussian_noise_to_params(
321
+ aggregated_params,
322
+ self.noise_multiplier,
323
+ self.clipping_norm,
324
+ self.num_sampled_clients,
325
+ )
326
+ return aggregated_params, metrics
327
+
328
+ def aggregate_evaluate(
329
+ self,
330
+ server_round: int,
331
+ results: List[Tuple[ClientProxy, EvaluateRes]],
332
+ failures: List[Union[Tuple[ClientProxy, EvaluateRes], BaseException]],
333
+ ) -> Tuple[Optional[float], Dict[str, Scalar]]:
334
+ """Aggregate evaluation losses using the given strategy."""
335
+ return self.strategy.aggregate_evaluate(server_round, results, failures)
336
+
337
+ def evaluate(
338
+ self, server_round: int, parameters: Parameters
339
+ ) -> Optional[Tuple[float, Dict[str, Scalar]]]:
340
+ """Evaluate model parameters using an evaluation function from the strategy."""
341
+ return self.strategy.evaluate(server_round, parameters)
@@ -18,7 +18,7 @@
18
18
  from abc import ABC, abstractmethod
19
19
  from typing import Callable, Dict, Tuple
20
20
 
21
- from flwr.client.clientapp import ClientApp
21
+ from flwr.client.client_app import ClientApp
22
22
  from flwr.common.context import Context
23
23
  from flwr.common.message import Message
24
24
  from flwr.common.typing import ConfigsRecordValues
@@ -53,6 +53,10 @@ class Backend(ABC):
53
53
  def is_worker_idle(self) -> bool:
54
54
  """Report whether a backend worker is idle and can therefore run a ClientApp."""
55
55
 
56
+ @abstractmethod
57
+ async def terminate(self) -> None:
58
+ """Terminate backend."""
59
+
56
60
  @abstractmethod
57
61
  async def process_message(
58
62
  self,
@@ -14,12 +14,13 @@
14
14
  # ==============================================================================
15
15
  """Ray backend for the Fleet API using the Simulation Engine."""
16
16
 
17
- import asyncio
18
17
  import pathlib
19
- from logging import INFO
18
+ from logging import ERROR, INFO
20
19
  from typing import Callable, Dict, List, Tuple, Union
21
20
 
22
- from flwr.client.clientapp import ClientApp
21
+ import ray
22
+
23
+ from flwr.client.client_app import ClientApp, LoadClientAppError
23
24
  from flwr.common.context import Context
24
25
  from flwr.common.logger import log
25
26
  from flwr.common.message import Message
@@ -28,6 +29,7 @@ from flwr.simulation.ray_transport.ray_actor import (
28
29
  ClientAppActor,
29
30
  init_ray,
30
31
  )
32
+ from flwr.simulation.ray_transport.utils import enable_tf_gpu_growth
31
33
 
32
34
  from .backend import Backend, BackendConfig
33
35
 
@@ -46,6 +48,9 @@ class RayBackend(Backend):
46
48
  log(INFO, "Initialising: %s", self.__class__.__name__)
47
49
  log(INFO, "Backend config: %s", backend_config)
48
50
 
51
+ if not pathlib.Path(work_dir).exists():
52
+ raise ValueError(f"Specified work_dir {work_dir} does not exist.")
53
+
49
54
  # Init ray and append working dir if needed
50
55
  runtime_env = (
51
56
  self._configure_runtime_env(work_dir=work_dir) if work_dir else None
@@ -56,7 +61,9 @@ class RayBackend(Backend):
56
61
  self.client_resources_key = "client_resources"
57
62
 
58
63
  # Create actor pool
59
- actor_kwargs = backend_config.get("actor_kwargs", {})
64
+ use_tf = backend_config.get("tensorflow", False)
65
+ actor_kwargs = {"on_actor_init_fn": enable_tf_gpu_growth} if use_tf else {}
66
+
60
67
  client_resources = self._validate_client_resources(config=backend_config)
61
68
  self.pool = BasicActorPool(
62
69
  actor_type=ClientAppActor,
@@ -136,18 +143,34 @@ class RayBackend(Backend):
136
143
  """
137
144
  node_id = message.metadata.dst_node_id
138
145
 
139
- # Submite a task to the pool
140
- future = await self.pool.submit(
141
- lambda a, a_fn, mssg, cid, state: a.run.remote(a_fn, mssg, cid, state),
142
- (app, message, str(node_id), context),
143
- )
146
+ try:
147
+ # Submite a task to the pool
148
+ future = await self.pool.submit(
149
+ lambda a, a_fn, mssg, cid, state: a.run.remote(a_fn, mssg, cid, state),
150
+ (app, message, str(node_id), context),
151
+ )
152
+
153
+ await future
144
154
 
145
- await asyncio.wait([future])
155
+ # Fetch result
156
+ (
157
+ out_mssg,
158
+ updated_context,
159
+ ) = await self.pool.fetch_result_and_return_actor_to_pool(future)
146
160
 
147
- # Fetch result
148
- (
149
- out_mssg,
150
- updated_context,
151
- ) = await self.pool.fetch_result_and_return_actor_to_pool(future)
161
+ return out_mssg, updated_context
152
162
 
153
- return out_mssg, updated_context
163
+ except LoadClientAppError as load_ex:
164
+ log(
165
+ ERROR,
166
+ "An exception was raised when processing a message. Terminating %s",
167
+ self.__class__.__name__,
168
+ )
169
+ await self.terminate()
170
+ raise load_ex
171
+
172
+ async def terminate(self) -> None:
173
+ """Terminate all actors in actor pool."""
174
+ await self.pool.terminate_all_actors()
175
+ ray.shutdown()
176
+ log(INFO, "Terminated %s", self.__class__.__name__)
@@ -14,11 +14,12 @@
14
14
  # ==============================================================================
15
15
  """Fleet VirtualClientEngine API."""
16
16
 
17
+ import asyncio
17
18
  import json
18
19
  from logging import ERROR, INFO
19
- from typing import Dict
20
+ from typing import Dict, Optional
20
21
 
21
- from flwr.client.clientapp import ClientApp, load_client_app
22
+ from flwr.client.client_app import ClientApp, load_client_app
22
23
  from flwr.client.node_state import NodeState
23
24
  from flwr.common.logger import log
24
25
  from flwr.server.superlink.state import StateFactory
@@ -49,6 +50,7 @@ def start_vce(
49
50
  backend_config_json_stream: str,
50
51
  state_factory: StateFactory,
51
52
  working_dir: str,
53
+ f_stop: Optional[asyncio.Event] = None,
52
54
  ) -> None:
53
55
  """Start Fleet API with the VirtualClientEngine (VCE)."""
54
56
  # Register SuperNodes
@@ -16,6 +16,7 @@
16
16
 
17
17
 
18
18
  import os
19
+ import threading
19
20
  from datetime import datetime, timedelta
20
21
  from logging import ERROR
21
22
  from typing import Dict, List, Optional, Set
@@ -35,6 +36,7 @@ class InMemoryState(State):
35
36
  self.run_ids: Set[int] = set()
36
37
  self.task_ins_store: Dict[UUID, TaskIns] = {}
37
38
  self.task_res_store: Dict[UUID, TaskRes] = {}
39
+ self.lock = threading.Lock()
38
40
 
39
41
  def store_task_ins(self, task_ins: TaskIns) -> Optional[UUID]:
40
42
  """Store one TaskIns."""
@@ -57,7 +59,8 @@ class InMemoryState(State):
57
59
  task_ins.task_id = str(task_id)
58
60
  task_ins.task.created_at = created_at.isoformat()
59
61
  task_ins.task.ttl = ttl.isoformat()
60
- self.task_ins_store[task_id] = task_ins
62
+ with self.lock:
63
+ self.task_ins_store[task_id] = task_ins
61
64
 
62
65
  # Return the new task_id
63
66
  return task_id
@@ -71,22 +74,23 @@ class InMemoryState(State):
71
74
 
72
75
  # Find TaskIns for node_id that were not delivered yet
73
76
  task_ins_list: List[TaskIns] = []
74
- for _, task_ins in self.task_ins_store.items():
75
- # pylint: disable=too-many-boolean-expressions
76
- if (
77
- node_id is not None # Not anonymous
78
- and task_ins.task.consumer.anonymous is False
79
- and task_ins.task.consumer.node_id == node_id
80
- and task_ins.task.delivered_at == ""
81
- ) or (
82
- node_id is None # Anonymous
83
- and task_ins.task.consumer.anonymous is True
84
- and task_ins.task.consumer.node_id == 0
85
- and task_ins.task.delivered_at == ""
86
- ):
87
- task_ins_list.append(task_ins)
88
- if limit and len(task_ins_list) == limit:
89
- break
77
+ with self.lock:
78
+ for _, task_ins in self.task_ins_store.items():
79
+ # pylint: disable=too-many-boolean-expressions
80
+ if (
81
+ node_id is not None # Not anonymous
82
+ and task_ins.task.consumer.anonymous is False
83
+ and task_ins.task.consumer.node_id == node_id
84
+ and task_ins.task.delivered_at == ""
85
+ ) or (
86
+ node_id is None # Anonymous
87
+ and task_ins.task.consumer.anonymous is True
88
+ and task_ins.task.consumer.node_id == 0
89
+ and task_ins.task.delivered_at == ""
90
+ ):
91
+ task_ins_list.append(task_ins)
92
+ if limit and len(task_ins_list) == limit:
93
+ break
90
94
 
91
95
  # Mark all of them as delivered
92
96
  delivered_at = now().isoformat()
@@ -164,7 +168,8 @@ class InMemoryState(State):
164
168
  task_res_to_be_deleted.add(task_res_id)
165
169
 
166
170
  for task_id in task_ins_to_be_deleted:
167
- del self.task_ins_store[task_id]
171
+ with self.lock:
172
+ del self.task_ins_store[task_id]
168
173
  for task_id in task_res_to_be_deleted:
169
174
  del self.task_res_store[task_id]
170
175
 
@@ -18,14 +18,14 @@ import asyncio
18
18
  import threading
19
19
  import traceback
20
20
  from abc import ABC
21
- from logging import ERROR, WARNING
21
+ from logging import DEBUG, ERROR, WARNING
22
22
  from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Type, Union
23
23
 
24
24
  import ray
25
25
  from ray import ObjectRef
26
26
  from ray.util.actor_pool import ActorPool
27
27
 
28
- from flwr.client.clientapp import ClientApp
28
+ from flwr.client.client_app import ClientApp, LoadClientAppError
29
29
  from flwr.common import Context, Message
30
30
  from flwr.common.logger import log
31
31
 
@@ -46,7 +46,7 @@ class VirtualClientEngineActor(ABC):
46
46
 
47
47
  def terminate(self) -> None:
48
48
  """Manually terminate Actor object."""
49
- log(WARNING, "Manually terminating %s}", self.__class__.__name__)
49
+ log(WARNING, "Manually terminating %s", self.__class__.__name__)
50
50
  ray.actor.exit_actor()
51
51
 
52
52
  def run(
@@ -67,6 +67,9 @@ class VirtualClientEngineActor(ABC):
67
67
  # Handle task message
68
68
  out_message = app(message=message, context=context)
69
69
 
70
+ except LoadClientAppError as load_ex:
71
+ raise load_ex
72
+
70
73
  except Exception as ex:
71
74
  client_trace = traceback.format_exc()
72
75
  mssg = (
@@ -434,7 +437,9 @@ class BasicActorPool:
434
437
  self.client_resources = client_resources
435
438
 
436
439
  # Queue of idle actors
437
- self.pool: "asyncio.Queue[Type[VirtualClientEngineActor]]" = asyncio.Queue()
440
+ self.pool: "asyncio.Queue[Type[VirtualClientEngineActor]]" = asyncio.Queue(
441
+ maxsize=1024
442
+ )
438
443
  self.num_actors = 0
439
444
 
440
445
  # Resolve arguments to pass during actor init
@@ -464,6 +469,16 @@ class BasicActorPool:
464
469
  await self.pool.put(self.create_actor_fn()) # type: ignore
465
470
  self.num_actors += num_actors
466
471
 
472
+ async def terminate_all_actors(self) -> None:
473
+ """Terminate actors in pool."""
474
+ num_terminated = 0
475
+ while self.pool.qsize():
476
+ actor = await self.pool.get()
477
+ actor.terminate.remote() # type: ignore
478
+ num_terminated += 1
479
+
480
+ log(DEBUG, "Terminated %i actors", num_terminated)
481
+
467
482
  async def submit(
468
483
  self, actor_fn: Any, job: Tuple[ClientAppFn, Message, str, Context]
469
484
  ) -> Any:
@@ -21,7 +21,7 @@ from typing import Optional
21
21
 
22
22
  from flwr import common
23
23
  from flwr.client import ClientFn
24
- from flwr.client.clientapp import ClientApp
24
+ from flwr.client.client_app import ClientApp
25
25
  from flwr.client.node_state import NodeState
26
26
  from flwr.common import Message, Metadata, RecordSet
27
27
  from flwr.common.constant import (
@@ -111,6 +111,7 @@ class RayActorClientProxy(ClientProxy):
111
111
  reply_to_message="",
112
112
  ttl=str(timeout) if timeout else "",
113
113
  message_type=message_type,
114
+ partition_id=int(self.cid),
114
115
  ),
115
116
  )
116
117
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: flwr-nightly
3
- Version: 1.8.0.dev20240223
3
+ Version: 1.8.0.dev20240227
4
4
  Summary: Flower: A Friendly Federated Learning Framework
5
5
  Home-page: https://flower.ai
6
6
  License: Apache-2.0
@@ -17,19 +17,20 @@ flwr/cli/new/templates/app/flower.toml.tpl,sha256=nGEU30gV6A2ZaRPt0ZVUjsxoevic4J
17
17
  flwr/cli/new/templates/app/requirements.pytorch.txt.tpl,sha256=9Z70jsiCPdsbuorhicrSdO6PVQn-3196vKZ5Ka2GkK0,87
18
18
  flwr/cli/new/templates/app/requirements.tensorflow.txt.tpl,sha256=WTbIgK5G_iG0W-xtvQLCZMxL_Og26rFXial2TkYH5dw,211
19
19
  flwr/cli/utils.py,sha256=9cCEIt8QmJXz85JNmPk1IHPd7p8E3KDn6h5CfF0nDL4,1926
20
- flwr/client/__init__.py,sha256=ypNGqhSDxEPX_6XwcQyVseBf8oS_Zvs_JkrLbVIeKxw,1186
21
- flwr/client/app.py,sha256=BxQGhdhFiw9JrKu-TcgmyshN02K9QpgWPxFVEcQ4Mb8,19983
20
+ flwr/client/__init__.py,sha256=futk_IdY_N1h8BTve4Iru51bxm7H1gv58ZPIXWi5XUA,1187
21
+ flwr/client/app.py,sha256=WUfnjDmlhL6bajdRj22PQjW2u3d6ONvWoDzOSAeIRcc,20074
22
22
  flwr/client/client.py,sha256=Vp9UkOkoHdNfn6iMYZsj_5m_GICiFfUlKEVaLad-YhM,8183
23
- flwr/client/clientapp.py,sha256=jrDgJBswP2hD1YdGgQoI3GU_NkliYWVU8glBJLOVzQY,4205
23
+ flwr/client/client_app.py,sha256=jrDgJBswP2hD1YdGgQoI3GU_NkliYWVU8glBJLOVzQY,4205
24
24
  flwr/client/dpfedavg_numpy_client.py,sha256=9Tnig4iml2J88HBKNahegjXjbfvIQyBtaIQaqjbeqsA,7435
25
25
  flwr/client/grpc_client/__init__.py,sha256=LsnbqXiJhgQcB0XzAlUQgPx011Uf7Y7yabIC1HxivJ8,735
26
26
  flwr/client/grpc_client/connection.py,sha256=QJKv39MlcDMLr2YQ80ulm-2mD3bAozEz3VKnNsymbYs,8381
27
27
  flwr/client/grpc_rere_client/__init__.py,sha256=avn6W_vHEM_yZEB1S7hCZgnTbXb6ZujqRP_vAzyXu-0,752
28
28
  flwr/client/grpc_rere_client/connection.py,sha256=QfshoyA9yYuHK15Vb0hlB0QDv0dQRqD2p74MmKAYjws,6842
29
29
  flwr/client/message_handler/__init__.py,sha256=abHvBRJJiiaAMNgeILQbMOa6h8WqMK2BcnvxwQZFpic,719
30
- flwr/client/message_handler/message_handler.py,sha256=3XhIcWhhouw0TBOhOsC-Us0PKt_0xEBRkuCjOr0onuw,6593
30
+ flwr/client/message_handler/message_handler.py,sha256=369gEm8t1Tbp_Y74XlOGMy_mvD1zawD-dLwsBL174tY,6594
31
31
  flwr/client/message_handler/task_handler.py,sha256=ZDJBKmrn2grRMNl1rU1iGs7FiMHL5VmZiSp_6h9GHVU,1824
32
- flwr/client/mod/__init__.py,sha256=2WIdmUl-Z2CJkayrkDF21V-5Qe_iBW8AHa7F2ctK3P4,840
32
+ flwr/client/mod/__init__.py,sha256=6LRDFRjUAMevk2TQ_azVs2zczumXcyKGzAQDRWLoe7A,911
33
+ flwr/client/mod/centraldp_mods.py,sha256=zhzjikh7PG3DCpkPxBKJFR8fG4QNouhuE7l8wexMz-U,2893
33
34
  flwr/client/mod/secure_aggregation/__init__.py,sha256=AzCdezuzX2BfXUuxVRwXdv8-zUIXoU-Bf6u4LRhzvg8,796
34
35
  flwr/client/mod/secure_aggregation/secaggplus_mod.py,sha256=z_5t1YzqLs91ZLW5Yoo7Ozqw9_nyVuEpJ7Noa2a34bs,19890
35
36
  flwr/client/mod/utils.py,sha256=lvETHcCYsSWz7h8I772hCV_kZspxqlMqzriMZ-SxmKc,1226
@@ -45,11 +46,12 @@ flwr/common/constant.py,sha256=jVUVKXo1cFb2HpRYqV70WKMG4RqCVrq7H6KC7zXs23Y,1572
45
46
  flwr/common/context.py,sha256=ounF-mWPPtXGwtae3sg5EhF58ScviOa3MVqxRpGVu-8,1313
46
47
  flwr/common/date.py,sha256=UWhBZj49yX9LD4BmatS_ZFZu_-kweGh0KQJ1djyWWH4,891
47
48
  flwr/common/differential_privacy.py,sha256=pVSKRhciVNtdBlhoz1H0--8N5PMLjdO_bA1PLGq4WZ8,2969
48
- flwr/common/differential_privacy_constants.py,sha256=_yGgdFMlXzhNibnETSjedA6vbnQykNMxnsgs0TELOr4,1011
49
+ flwr/common/differential_privacy_constants.py,sha256=LUP9YurHRDm5--9jATnN2ddrnBSdEX30qvcys0BlUlY,1048
49
50
  flwr/common/dp.py,sha256=Hc3lLHihjexbJaD_ft31gdv9XRcwOTgDBwJzICuok3A,2004
51
+ flwr/common/exit_handlers.py,sha256=2Nt0wLhc17KQQsLPFSRAjjhUiEFfJK6tNozdGiIY4Fs,2812
50
52
  flwr/common/grpc.py,sha256=qVLB0d6bCuaBRW5YB0vEZXsR7Bo3R2lh4ONiCocqwRI,2270
51
53
  flwr/common/logger.py,sha256=qX_gqEyrmGOH0x_r8uQ1Vskz4fGvEij9asdo4DUOPY8,4135
52
- flwr/common/message.py,sha256=tXkOAIYnPN5cW2OVggVTAUSaBxEDqoFGpST7uPpIAho,6198
54
+ flwr/common/message.py,sha256=lCbaYFKSTI_Fpot-mJsq4rTvj46ZvqNAz9LVcJBlF1Q,6901
53
55
  flwr/common/parameter.py,sha256=-bFAUayToYDF50FZGrBC1hQYJCQDtB2bbr3ZuVLMtdE,2095
54
56
  flwr/common/record/__init__.py,sha256=33OaDW2bvaW952DFHH1amHclv4AuDZu385jXjHhXoog,1054
55
57
  flwr/common/record/configsrecord.py,sha256=qL-hQ6ZFOOWJYCUHeFiao2vcO5rnk585Ns5Yxfb1sp4,3378
@@ -99,7 +101,7 @@ flwr/proto/transport_pb2_grpc.py,sha256=vLN3EHtx2aEEMCO4f1Upu-l27BPzd3-5pV-u8wPc
99
101
  flwr/proto/transport_pb2_grpc.pyi,sha256=AGXf8RiIiW2J5IKMlm_3qT3AzcDa4F3P5IqUjve_esA,766
100
102
  flwr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
101
103
  flwr/server/__init__.py,sha256=_Wv3UkZzSXzbKWXyl2yY8tU6oqf_XqIruggpHjnmikE,1662
102
- flwr/server/app.py,sha256=0-p--_yF8rHTPxjrwZnBkEZgEeyAAqJ-i18vdCHTq5g,26328
104
+ flwr/server/app.py,sha256=wf0CCq5y7kPcf_Jy3Dh8YhopUWo3v2eEVmyF9KhWsZY,25101
103
105
  flwr/server/client_manager.py,sha256=T8UDSRJBVD3fyIDI7NTAA-NA7GPrMNNgH2OAF54RRxE,6127
104
106
  flwr/server/client_proxy.py,sha256=8ScGDvP3jHbl8DV3hyFID5N5VEVlXn8ZTQXtkdOfssI,2234
105
107
  flwr/server/compat/__init__.py,sha256=KNvRFANbIc8LFRKHsBVfxcbOSekEImWgNq_gapCkbic,812
@@ -107,17 +109,17 @@ flwr/server/compat/app.py,sha256=HyElz6bENXipjhKVBbGuAnYQDfEXz0ZvbaBeSWxvJnQ,797
107
109
  flwr/server/compat/driver_client_proxy.py,sha256=S3lI6Wi7fpkbg6GEi6TSzB6RHsrIdg1w44sLe-94UUk,6148
108
110
  flwr/server/criterion.py,sha256=ypbAexbztzGUxNen9RCHF91QeqiEQix4t4Ih3E-42MM,1061
109
111
  flwr/server/driver/__init__.py,sha256=yYyVX1FcDiDFM6rw0-DSZpuRy0EoWRfG9puwlQUswFA,820
110
- flwr/server/driver/driver.py,sha256=DApIRFeEzReZpdAsbFQ68haSUONMstblyOt6qcseMNc,9540
112
+ flwr/server/driver/driver.py,sha256=zAUu6A8Ko4pYgusW-dkOGyGqCDRZOUF1ldGG4bTm8S0,9541
111
113
  flwr/server/driver/grpc_driver.py,sha256=Ekcxx4Miqume02fiBjBRJhItaOFNpn120WT3uRIPWIc,4585
112
114
  flwr/server/history.py,sha256=W7PHCFX7dLXrdnaVfl5V4tuzmtxh6zArkWYxVXvTZ1c,4904
113
115
  flwr/server/run_serverapp.py,sha256=tYNbz11xMvkbcMty6u5nkLHmZhwIjSO78CfW-Aas5R8,4488
114
116
  flwr/server/server.py,sha256=kUIqgLIXnWcSrhEhXXkaZPRooYhTGGX-RDCYzG9J76g,17495
115
117
  flwr/server/server_app.py,sha256=avNQ7AMMKsn09ly81C3UBgOfHhM_R29l4MrzlalGoj8,5892
116
118
  flwr/server/server_config.py,sha256=yOHpkdyuhOm--Gy_4Vofvu6jCDxhyECEDpIy02beuCg,1018
117
- flwr/server/strategy/__init__.py,sha256=qYOURtpO5DtaDXzvo0wd1lv0cP3wGQxbhqn3s5i8--k,2220
119
+ flwr/server/strategy/__init__.py,sha256=-VCKDaWfVpiplrgbbtJ0rUOmQ180oIh7bgenE9UDDlM,2327
118
120
  flwr/server/strategy/aggregate.py,sha256=QyRIJtI5gnuY1NbgrcrOvkHxGIxBvApq7d9Y4xl-6W4,13468
119
121
  flwr/server/strategy/bulyan.py,sha256=8GsSVJzRSoSWE2zQUKqC3Z795grdN9xpmc3MSGGXnzM,6532
120
- flwr/server/strategy/dp_fixed_clipping.py,sha256=185P6F17uOtRByDaQSCpKj6jCGgP4271xhoMLUl2oR8,6671
122
+ flwr/server/strategy/dp_fixed_clipping.py,sha256=h7yICGeC-1CsJgOWlH8MpMVXE23gUV7GzefrvHoGYBw,12187
121
123
  flwr/server/strategy/dpfedavg_adaptive.py,sha256=hLJkPQJl1bHjwrBNg3PSRFKf3no0hg5EHiFaWhHlWqw,4877
122
124
  flwr/server/strategy/dpfedavg_fixed.py,sha256=G0yYxrPoM-MHQ889DYN3OeNiEeU0yQrjgAzcq0G653w,7219
123
125
  flwr/server/strategy/fault_tolerant_fedavg.py,sha256=veGcehB6rXT_MihNDrD1v5JY-TxJi7fybdDl-OZooDQ,5900
@@ -154,11 +156,11 @@ flwr/server/superlink/fleet/rest_rere/__init__.py,sha256=VKDvDq5H8koOUztpmQacVzG
154
156
  flwr/server/superlink/fleet/rest_rere/rest_api.py,sha256=7JCs7NW4Qq8W5QhXxqsQNFiCLlRY-b_iD420vH1Mu-U,5906
155
157
  flwr/server/superlink/fleet/vce/__init__.py,sha256=bogHbcWSXkD7wZkqUXiLRKQTJUs7jtr5uwaGlmoA-Yc,785
156
158
  flwr/server/superlink/fleet/vce/backend/__init__.py,sha256=oBIzmnrSSRvH_H0vRGEGWhWzQQwqe3zn6e13RsNwlIY,1466
157
- flwr/server/superlink/fleet/vce/backend/backend.py,sha256=AFBgP6rT9vtiHXagmt5B3BxwglZqVO6pK8hsFOGcFr4,2166
158
- flwr/server/superlink/fleet/vce/backend/raybackend.py,sha256=ojJRZzgTynh1UKlpDjOoqi8kR6iJyA9SJKQ4ZWDgCEY,5496
159
- flwr/server/superlink/fleet/vce/vce_api.py,sha256=GC7WKxTJOF2NhHn-cwR_no1vJA1NHr_Dp8IMRT8Ng6c,3029
159
+ flwr/server/superlink/fleet/vce/backend/backend.py,sha256=LJsKl7oixVvptcG98Rd9ejJycNWcEVB0ODvSreLGp-A,2260
160
+ flwr/server/superlink/fleet/vce/backend/raybackend.py,sha256=EYnLpX9bTRBrLiEW0F2LLMC6kt7Zhcy10zxqeKKfIGg,6351
161
+ flwr/server/superlink/fleet/vce/vce_api.py,sha256=TiDuQVdClc38heSjD3b2jo7kZg-KZuNhzh0OdJmmwT4,3099
160
162
  flwr/server/superlink/state/__init__.py,sha256=ij-7Ms-hyordQdRmGQxY1-nVa4OhixJ0jr7_YDkys0s,1003
161
- flwr/server/superlink/state/in_memory_state.py,sha256=viGGGyg7LwwxKfCnndV6Tp9poVeZTrbN9z0tt-4qrqI,7903
163
+ flwr/server/superlink/state/in_memory_state.py,sha256=sZX5XcpnU9cafhhC4Or5oRGIbKR2AKdjIfBDtMVGNLQ,8105
162
164
  flwr/server/superlink/state/sqlite_state.py,sha256=Adc2g1DecAN9Cl9F8lekuTb885mIHiOi6sQv4nxbmSc,21203
163
165
  flwr/server/superlink/state/state.py,sha256=JtsI92HfdKd8KzBQ9Om7A7xwngDXVxtET2Bk9aQ7nao,5316
164
166
  flwr/server/superlink/state/state_factory.py,sha256=91cSB-KOAFM37z7T098WxTkVeKNaAZ_mTI75snn2_tk,1654
@@ -169,11 +171,11 @@ flwr/server/utils/validator.py,sha256=IJN2475yyD_i_9kg_SJ_JodIuZh58ufpWGUDQRAqu2
169
171
  flwr/simulation/__init__.py,sha256=E2eD5FlTmZZ80u21FmWCkacrM7O4mrEHD8iXqeCaBUQ,1278
170
172
  flwr/simulation/app.py,sha256=WqJxdXTEuehwMW605p5NMmvBbKYx5tuqnV3Mp7jSWXM,13904
171
173
  flwr/simulation/ray_transport/__init__.py,sha256=FsaAnzC4cw4DqoouBCix6496k29jACkfeIam55BvW9g,734
172
- flwr/simulation/ray_transport/ray_actor.py,sha256=XEX0u5TUtn6iQjUiK94kChSD4pqyrJPlZtvvRYDhgbk,19400
173
- flwr/simulation/ray_transport/ray_client_proxy.py,sha256=8I1g8nK0Vz7ygpOSzGZ1oXTfwi1vez8fxYWCdSXBrXE,6290
174
+ flwr/simulation/ray_transport/ray_actor.py,sha256=zRETW_xuCAOLRFaYnQ-q3IBSz0LIv_0RifGuhgWaYOg,19872
175
+ flwr/simulation/ray_transport/ray_client_proxy.py,sha256=DVuequIvgbXQGYz_8oIWghnE_sPiiswAGOVfHNNF4U8,6335
174
176
  flwr/simulation/ray_transport/utils.py,sha256=TYdtfg1P9VfTdLMOJlifInGpxWHYs9UfUqIv2wfkRLA,2392
175
- flwr_nightly-1.8.0.dev20240223.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
176
- flwr_nightly-1.8.0.dev20240223.dist-info/METADATA,sha256=TymbNYF34sdpEoSS6lM3kXq87wBbwrfWg24wni-TKgc,15040
177
- flwr_nightly-1.8.0.dev20240223.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
178
- flwr_nightly-1.8.0.dev20240223.dist-info/entry_points.txt,sha256=S1zLNFLrz0uPWs4Zrgo2EPY0iQiIcCJHrIAlnQkkOBI,262
179
- flwr_nightly-1.8.0.dev20240223.dist-info/RECORD,,
177
+ flwr_nightly-1.8.0.dev20240227.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
178
+ flwr_nightly-1.8.0.dev20240227.dist-info/METADATA,sha256=a6sTBhHs6TpkUp9isUB69U9Q7PoY_or1jNb7gA44z2w,15040
179
+ flwr_nightly-1.8.0.dev20240227.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
180
+ flwr_nightly-1.8.0.dev20240227.dist-info/entry_points.txt,sha256=S1zLNFLrz0uPWs4Zrgo2EPY0iQiIcCJHrIAlnQkkOBI,262
181
+ flwr_nightly-1.8.0.dev20240227.dist-info/RECORD,,
File without changes