flwr-nightly 1.8.0.dev20240223__py3-none-any.whl → 1.8.0.dev20240227__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- flwr/client/__init__.py +1 -1
- flwr/client/app.py +4 -3
- flwr/client/message_handler/message_handler.py +1 -1
- flwr/client/mod/__init__.py +2 -0
- flwr/client/mod/centraldp_mods.py +76 -0
- flwr/common/differential_privacy_constants.py +2 -0
- flwr/common/exit_handlers.py +87 -0
- flwr/common/message.py +18 -1
- flwr/server/app.py +7 -54
- flwr/server/driver/driver.py +1 -0
- flwr/server/strategy/__init__.py +5 -1
- flwr/server/strategy/dp_fixed_clipping.py +156 -2
- flwr/server/superlink/fleet/vce/backend/backend.py +5 -1
- flwr/server/superlink/fleet/vce/backend/raybackend.py +39 -16
- flwr/server/superlink/fleet/vce/vce_api.py +4 -2
- flwr/server/superlink/state/in_memory_state.py +23 -18
- flwr/simulation/ray_transport/ray_actor.py +19 -4
- flwr/simulation/ray_transport/ray_client_proxy.py +2 -1
- {flwr_nightly-1.8.0.dev20240223.dist-info → flwr_nightly-1.8.0.dev20240227.dist-info}/METADATA +1 -1
- {flwr_nightly-1.8.0.dev20240223.dist-info → flwr_nightly-1.8.0.dev20240227.dist-info}/RECORD +24 -22
- /flwr/client/{clientapp.py → client_app.py} +0 -0
- {flwr_nightly-1.8.0.dev20240223.dist-info → flwr_nightly-1.8.0.dev20240227.dist-info}/LICENSE +0 -0
- {flwr_nightly-1.8.0.dev20240223.dist-info → flwr_nightly-1.8.0.dev20240227.dist-info}/WHEEL +0 -0
- {flwr_nightly-1.8.0.dev20240223.dist-info → flwr_nightly-1.8.0.dev20240227.dist-info}/entry_points.txt +0 -0
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 .
|
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.
|
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 .
|
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
|
-
|
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.
|
101
|
+
client = client_fn(str(message.metadata.partition_id))
|
102
102
|
|
103
103
|
# Check if NumPyClient is returend
|
104
104
|
if isinstance(client, NumPyClient):
|
flwr/client/mod/__init__.py
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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,
|
flwr/server/driver/driver.py
CHANGED
@@ -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.
|
flwr/server/strategy/__init__.py
CHANGED
@@ -16,7 +16,10 @@
|
|
16
16
|
|
17
17
|
|
18
18
|
from .bulyan import Bulyan as Bulyan
|
19
|
-
from .dp_fixed_clipping import
|
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
|
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
|
-
"""
|
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.
|
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
|
-
|
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
|
-
|
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
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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.
|
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.
|
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
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
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
|
-
|
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.
|
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
|
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.
|
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
|
|
{flwr_nightly-1.8.0.dev20240223.dist-info → flwr_nightly-1.8.0.dev20240227.dist-info}/RECORD
RENAMED
@@ -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=
|
21
|
-
flwr/client/app.py,sha256=
|
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/
|
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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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
|
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=
|
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=
|
158
|
-
flwr/server/superlink/fleet/vce/backend/raybackend.py,sha256=
|
159
|
-
flwr/server/superlink/fleet/vce/vce_api.py,sha256=
|
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=
|
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=
|
173
|
-
flwr/simulation/ray_transport/ray_client_proxy.py,sha256=
|
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.
|
176
|
-
flwr_nightly-1.8.0.
|
177
|
-
flwr_nightly-1.8.0.
|
178
|
-
flwr_nightly-1.8.0.
|
179
|
-
flwr_nightly-1.8.0.
|
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
|
{flwr_nightly-1.8.0.dev20240223.dist-info → flwr_nightly-1.8.0.dev20240227.dist-info}/LICENSE
RENAMED
File without changes
|
File without changes
|
File without changes
|