flwr-nightly 1.7.0.dev20240130__py3-none-any.whl → 1.7.0.dev20240201__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/app.py +25 -7
- flwr/client/dpfedavg_numpy_client.py +8 -1
- flwr/client/middleware/__init__.py +2 -0
- flwr/client/{secure_aggregation → middleware/secure_aggregation}/__init__.py +3 -7
- flwr/client/{secure_aggregation/secaggplus_handler.py → middleware/secure_aggregation/secaggplus_middleware.py} +147 -105
- flwr/common/dp.py +4 -0
- flwr/common/secure_aggregation/secaggplus_constants.py +2 -0
- flwr/driver/app.py +3 -1
- flwr/driver/driver.py +4 -3
- flwr/driver/grpc_driver.py +4 -4
- flwr/server/app.py +1 -1
- flwr/server/fleet/grpc_bidi/flower_service_servicer.py +6 -3
- flwr/server/fleet/grpc_rere/fleet_servicer.py +7 -7
- flwr/server/strategy/dpfedavg_adaptive.py +8 -1
- flwr/server/strategy/dpfedavg_fixed.py +8 -2
- flwr/server/strategy/fedxgb_bagging.py +5 -0
- flwr/server/strategy/fedxgb_cyclic.py +5 -0
- {flwr_nightly-1.7.0.dev20240130.dist-info → flwr_nightly-1.7.0.dev20240201.dist-info}/METADATA +17 -5
- {flwr_nightly-1.7.0.dev20240130.dist-info → flwr_nightly-1.7.0.dev20240201.dist-info}/RECORD +22 -23
- flwr/client/secure_aggregation/handler.py +0 -43
- {flwr_nightly-1.7.0.dev20240130.dist-info → flwr_nightly-1.7.0.dev20240201.dist-info}/LICENSE +0 -0
- {flwr_nightly-1.7.0.dev20240130.dist-info → flwr_nightly-1.7.0.dev20240201.dist-info}/WHEEL +0 -0
- {flwr_nightly-1.7.0.dev20240130.dist-info → flwr_nightly-1.7.0.dev20240201.dist-info}/entry_points.txt +0 -0
flwr/client/app.py
CHANGED
@@ -18,7 +18,7 @@
|
|
18
18
|
import argparse
|
19
19
|
import sys
|
20
20
|
import time
|
21
|
-
from logging import INFO, WARN
|
21
|
+
from logging import DEBUG, INFO, WARN
|
22
22
|
from pathlib import Path
|
23
23
|
from typing import Callable, ContextManager, Optional, Tuple, Union
|
24
24
|
|
@@ -62,7 +62,12 @@ def run_client() -> None:
|
|
62
62
|
"the '--root-certificates' option when running in insecure mode, "
|
63
63
|
"or omit '--insecure' to use HTTPS."
|
64
64
|
)
|
65
|
-
log(
|
65
|
+
log(
|
66
|
+
WARN,
|
67
|
+
"Option `--insecure` was set. "
|
68
|
+
"Starting insecure HTTP client connected to %s.",
|
69
|
+
args.server,
|
70
|
+
)
|
66
71
|
root_certificates = None
|
67
72
|
else:
|
68
73
|
# Load the certificates if provided, or load the system certificates
|
@@ -71,11 +76,19 @@ def run_client() -> None:
|
|
71
76
|
root_certificates = None
|
72
77
|
else:
|
73
78
|
root_certificates = Path(cert_path).read_bytes()
|
79
|
+
log(
|
80
|
+
DEBUG,
|
81
|
+
"Starting secure HTTPS client connected to %s "
|
82
|
+
"with the following certificates: %s.",
|
83
|
+
args.server,
|
84
|
+
cert_path,
|
85
|
+
)
|
74
86
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
87
|
+
log(
|
88
|
+
DEBUG,
|
89
|
+
"The Flower client uses `%s` to execute tasks",
|
90
|
+
args.callable,
|
91
|
+
)
|
79
92
|
|
80
93
|
callable_dir = args.dir
|
81
94
|
if callable_dir is not None:
|
@@ -88,7 +101,7 @@ def run_client() -> None:
|
|
88
101
|
_start_client_internal(
|
89
102
|
server_address=args.server,
|
90
103
|
load_flower_callable_fn=_load,
|
91
|
-
transport="grpc-rere",
|
104
|
+
transport="rest" if args.rest else "grpc-rere",
|
92
105
|
root_certificates=root_certificates,
|
93
106
|
insecure=args.insecure,
|
94
107
|
)
|
@@ -111,6 +124,11 @@ def _parse_args_client() -> argparse.ArgumentParser:
|
|
111
124
|
help="Run the client without HTTPS. By default, the client runs with "
|
112
125
|
"HTTPS enabled. Use this flag only if you understand the risks.",
|
113
126
|
)
|
127
|
+
parser.add_argument(
|
128
|
+
"--rest",
|
129
|
+
action="store_true",
|
130
|
+
help="Use REST as a transport layer for the client.",
|
131
|
+
)
|
114
132
|
parser.add_argument(
|
115
133
|
"--root-certificates",
|
116
134
|
metavar="ROOT_CERT",
|
@@ -22,13 +22,20 @@ import numpy as np
|
|
22
22
|
|
23
23
|
from flwr.client.numpy_client import NumPyClient
|
24
24
|
from flwr.common.dp import add_gaussian_noise, clip_by_l2
|
25
|
+
from flwr.common.logger import warn_deprecated_feature
|
25
26
|
from flwr.common.typing import Config, NDArrays, Scalar
|
26
27
|
|
27
28
|
|
28
29
|
class DPFedAvgNumPyClient(NumPyClient):
|
29
|
-
"""Wrapper for configuring a Flower client for DP.
|
30
|
+
"""Wrapper for configuring a Flower client for DP.
|
31
|
+
|
32
|
+
Warning
|
33
|
+
-------
|
34
|
+
This class is deprecated and will be removed in a future release.
|
35
|
+
"""
|
30
36
|
|
31
37
|
def __init__(self, client: NumPyClient) -> None:
|
38
|
+
warn_deprecated_feature("`DPFedAvgNumPyClient` wrapper")
|
32
39
|
super().__init__()
|
33
40
|
self.client = client
|
34
41
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright
|
1
|
+
# Copyright 2023 Flower Labs GmbH. All Rights Reserved.
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -13,12 +13,8 @@
|
|
13
13
|
# limitations under the License.
|
14
14
|
# ==============================================================================
|
15
15
|
"""Secure Aggregation handlers."""
|
16
|
-
|
17
|
-
|
18
|
-
from .handler import SecureAggregationHandler
|
19
|
-
from .secaggplus_handler import SecAggPlusHandler
|
16
|
+
from .secaggplus_middleware import secaggplus_middleware
|
20
17
|
|
21
18
|
__all__ = [
|
22
|
-
"
|
23
|
-
"SecureAggregationHandler",
|
19
|
+
"secaggplus_middleware",
|
24
20
|
]
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright
|
1
|
+
# Copyright 2024 Flower Labs GmbH. All Rights Reserved.
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -17,18 +17,18 @@
|
|
17
17
|
|
18
18
|
import os
|
19
19
|
from dataclasses import dataclass, field
|
20
|
-
from logging import
|
21
|
-
from typing import Any, Dict, List,
|
22
|
-
|
23
|
-
from flwr.client.
|
24
|
-
from flwr.
|
25
|
-
from flwr.common import
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
parameters_to_ndarrays,
|
30
|
-
)
|
20
|
+
from logging import INFO, WARNING
|
21
|
+
from typing import Any, Callable, Dict, List, Tuple, cast
|
22
|
+
|
23
|
+
from flwr.client.typing import FlowerCallable
|
24
|
+
from flwr.common import ndarray_to_bytes, parameters_to_ndarrays
|
25
|
+
from flwr.common import recordset_compat as compat
|
26
|
+
from flwr.common.configsrecord import ConfigsRecord
|
27
|
+
from flwr.common.constant import TASK_TYPE_FIT
|
28
|
+
from flwr.common.context import Context
|
31
29
|
from flwr.common.logger import log
|
30
|
+
from flwr.common.message import Message, Metadata
|
31
|
+
from flwr.common.recordset import RecordSet
|
32
32
|
from flwr.common.secure_aggregation.crypto.shamir import create_shares
|
33
33
|
from flwr.common.secure_aggregation.crypto.symmetric_encryption import (
|
34
34
|
bytes_to_private_key,
|
@@ -56,7 +56,6 @@ from flwr.common.secure_aggregation.secaggplus_constants import (
|
|
56
56
|
KEY_DESTINATION_LIST,
|
57
57
|
KEY_MASKED_PARAMETERS,
|
58
58
|
KEY_MOD_RANGE,
|
59
|
-
KEY_PARAMETERS,
|
60
59
|
KEY_PUBLIC_KEY_1,
|
61
60
|
KEY_PUBLIC_KEY_2,
|
62
61
|
KEY_SAMPLE_NUMBER,
|
@@ -68,6 +67,8 @@ from flwr.common.secure_aggregation.secaggplus_constants import (
|
|
68
67
|
KEY_STAGE,
|
69
68
|
KEY_TARGET_RANGE,
|
70
69
|
KEY_THRESHOLD,
|
70
|
+
RECORD_KEY_CONFIGS,
|
71
|
+
RECORD_KEY_STATE,
|
71
72
|
STAGE_COLLECT_MASKED_INPUT,
|
72
73
|
STAGE_SETUP,
|
73
74
|
STAGE_SHARE_KEYS,
|
@@ -79,9 +80,7 @@ from flwr.common.secure_aggregation.secaggplus_utils import (
|
|
79
80
|
share_keys_plaintext_concat,
|
80
81
|
share_keys_plaintext_separate,
|
81
82
|
)
|
82
|
-
from flwr.common.typing import
|
83
|
-
|
84
|
-
from .handler import SecureAggregationHandler
|
83
|
+
from flwr.common.typing import ConfigsRecordValues, FitRes
|
85
84
|
|
86
85
|
|
87
86
|
@dataclass
|
@@ -89,6 +88,8 @@ from .handler import SecureAggregationHandler
|
|
89
88
|
class SecAggPlusState:
|
90
89
|
"""State of the SecAgg+ protocol."""
|
91
90
|
|
91
|
+
current_stage: str = STAGE_UNMASK
|
92
|
+
|
92
93
|
sid: int = 0
|
93
94
|
sample_num: int = 0
|
94
95
|
share_num: int = 0
|
@@ -112,70 +113,115 @@ class SecAggPlusState:
|
|
112
113
|
ss2_dict: Dict[int, bytes] = field(default_factory=dict)
|
113
114
|
public_keys_dict: Dict[int, Tuple[bytes, bytes]] = field(default_factory=dict)
|
114
115
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
116
|
+
def __init__(self, **kwargs: ConfigsRecordValues) -> None:
|
117
|
+
for k, v in kwargs.items():
|
118
|
+
if k.endswith(":V"):
|
119
|
+
continue
|
120
|
+
new_v: Any = v
|
121
|
+
if k.endswith(":K"):
|
122
|
+
k = k[:-2]
|
123
|
+
keys = cast(List[int], v)
|
124
|
+
values = cast(List[bytes], kwargs[f"{k}:V"])
|
125
|
+
if len(values) > len(keys):
|
126
|
+
updated_values = [
|
127
|
+
tuple(values[i : i + 2]) for i in range(0, len(values), 2)
|
128
|
+
]
|
129
|
+
new_v = dict(zip(keys, updated_values))
|
130
|
+
else:
|
131
|
+
new_v = dict(zip(keys, values))
|
132
|
+
self.__setattr__(k, new_v)
|
133
|
+
|
134
|
+
def to_dict(self) -> Dict[str, ConfigsRecordValues]:
|
135
|
+
"""Convert the state to a dictionary."""
|
136
|
+
ret = vars(self)
|
137
|
+
for k in list(ret.keys()):
|
138
|
+
if isinstance(ret[k], dict):
|
139
|
+
# Replace dict with two lists
|
140
|
+
v = cast(Dict[str, Any], ret.pop(k))
|
141
|
+
ret[f"{k}:K"] = list(v.keys())
|
142
|
+
if k == "public_keys_dict":
|
143
|
+
v_list: List[bytes] = []
|
144
|
+
for b1_b2 in cast(List[Tuple[bytes, bytes]], v.values()):
|
145
|
+
v_list.extend(b1_b2)
|
146
|
+
ret[f"{k}:V"] = v_list
|
147
|
+
else:
|
148
|
+
ret[f"{k}:V"] = list(v.values())
|
149
|
+
return ret
|
150
|
+
|
151
|
+
|
152
|
+
def _get_fit_fn(
|
153
|
+
msg: Message, ctxt: Context, call_next: FlowerCallable
|
154
|
+
) -> Callable[[], FitRes]:
|
155
|
+
"""Get the fit function."""
|
156
|
+
|
157
|
+
def fit() -> FitRes:
|
158
|
+
out_msg = call_next(msg, ctxt)
|
159
|
+
return compat.recordset_to_fitres(out_msg.message, keep_input=False)
|
160
|
+
|
161
|
+
return fit
|
162
|
+
|
163
|
+
|
164
|
+
def secaggplus_middleware(
|
165
|
+
msg: Message,
|
166
|
+
ctxt: Context,
|
167
|
+
call_next: FlowerCallable,
|
168
|
+
) -> Message:
|
169
|
+
"""Handle incoming message and return results, following the SecAgg+ protocol."""
|
170
|
+
# Ignore non-fit messages
|
171
|
+
if msg.metadata.task_type != TASK_TYPE_FIT:
|
172
|
+
return call_next(msg, ctxt)
|
173
|
+
|
174
|
+
# Retrieve local state
|
175
|
+
if RECORD_KEY_STATE not in ctxt.state.configs:
|
176
|
+
ctxt.state.set_configs(RECORD_KEY_STATE, ConfigsRecord({}))
|
177
|
+
state_dict = ctxt.state.get_configs(RECORD_KEY_STATE).data
|
178
|
+
state = SecAggPlusState(**state_dict)
|
179
|
+
|
180
|
+
# Retrieve incoming configs
|
181
|
+
configs = msg.message.get_configs(RECORD_KEY_CONFIGS).data
|
134
182
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
183
|
+
# Check the validity of the next stage
|
184
|
+
check_stage(state.current_stage, configs)
|
185
|
+
|
186
|
+
# Update the current stage
|
187
|
+
state.current_stage = cast(str, configs.pop(KEY_STAGE))
|
188
|
+
|
189
|
+
# Check the validity of the configs based on the current stage
|
190
|
+
check_configs(state.current_stage, configs)
|
191
|
+
|
192
|
+
# Execute
|
193
|
+
if state.current_stage == STAGE_SETUP:
|
194
|
+
res = _setup(state, configs)
|
195
|
+
elif state.current_stage == STAGE_SHARE_KEYS:
|
196
|
+
res = _share_keys(state, configs)
|
197
|
+
elif state.current_stage == STAGE_COLLECT_MASKED_INPUT:
|
198
|
+
fit = _get_fit_fn(msg, ctxt, call_next)
|
199
|
+
res = _collect_masked_input(state, configs, fit)
|
200
|
+
elif state.current_stage == STAGE_UNMASK:
|
201
|
+
res = _unmask(state, configs)
|
202
|
+
else:
|
203
|
+
raise ValueError(f"Unknown secagg stage: {state.current_stage}")
|
152
204
|
|
153
|
-
|
154
|
-
|
205
|
+
# Save state
|
206
|
+
ctxt.state.set_configs(RECORD_KEY_STATE, ConfigsRecord(state.to_dict()))
|
155
207
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
return _share_keys(self._shared_state, named_values)
|
162
|
-
if self._current_stage == STAGE_COLLECT_MASKED_INPUT:
|
163
|
-
return _collect_masked_input(self._shared_state, named_values)
|
164
|
-
if self._current_stage == STAGE_UNMASK:
|
165
|
-
return _unmask(self._shared_state, named_values)
|
166
|
-
raise ValueError(f"Unknown secagg stage: {self._current_stage}")
|
208
|
+
# Return message
|
209
|
+
return Message(
|
210
|
+
metadata=Metadata(0, "", "", "", TASK_TYPE_FIT),
|
211
|
+
message=RecordSet(configs={RECORD_KEY_CONFIGS: ConfigsRecord(res, False)}),
|
212
|
+
)
|
167
213
|
|
168
214
|
|
169
|
-
def check_stage(current_stage: str,
|
215
|
+
def check_stage(current_stage: str, configs: Dict[str, ConfigsRecordValues]) -> None:
|
170
216
|
"""Check the validity of the next stage."""
|
171
217
|
# Check the existence of KEY_STAGE
|
172
|
-
if KEY_STAGE not in
|
218
|
+
if KEY_STAGE not in configs:
|
173
219
|
raise KeyError(
|
174
220
|
f"The required key '{KEY_STAGE}' is missing from the input `named_values`."
|
175
221
|
)
|
176
222
|
|
177
223
|
# Check the value type of the KEY_STAGE
|
178
|
-
next_stage =
|
224
|
+
next_stage = configs[KEY_STAGE]
|
179
225
|
if not isinstance(next_stage, str):
|
180
226
|
raise TypeError(
|
181
227
|
f"The value for the key '{KEY_STAGE}' must be of type {str}, "
|
@@ -198,8 +244,8 @@ def check_stage(current_stage: str, named_values: Dict[str, Value]) -> None:
|
|
198
244
|
|
199
245
|
|
200
246
|
# pylint: disable-next=too-many-branches
|
201
|
-
def
|
202
|
-
"""Check the validity of the
|
247
|
+
def check_configs(stage: str, configs: Dict[str, ConfigsRecordValues]) -> None:
|
248
|
+
"""Check the validity of the configs."""
|
203
249
|
# Check `named_values` for the setup stage
|
204
250
|
if stage == STAGE_SETUP:
|
205
251
|
key_type_pairs = [
|
@@ -212,7 +258,7 @@ def check_named_values(stage: str, named_values: Dict[str, Value]) -> None:
|
|
212
258
|
(KEY_MOD_RANGE, int),
|
213
259
|
]
|
214
260
|
for key, expected_type in key_type_pairs:
|
215
|
-
if key not in
|
261
|
+
if key not in configs:
|
216
262
|
raise KeyError(
|
217
263
|
f"Stage {STAGE_SETUP}: the required key '{key}' is "
|
218
264
|
"missing from the input `named_values`."
|
@@ -220,14 +266,14 @@ def check_named_values(stage: str, named_values: Dict[str, Value]) -> None:
|
|
220
266
|
# Bool is a subclass of int in Python,
|
221
267
|
# so `isinstance(v, int)` will return True even if v is a boolean.
|
222
268
|
# pylint: disable-next=unidiomatic-typecheck
|
223
|
-
if type(
|
269
|
+
if type(configs[key]) is not expected_type:
|
224
270
|
raise TypeError(
|
225
271
|
f"Stage {STAGE_SETUP}: The value for the key '{key}' "
|
226
272
|
f"must be of type {expected_type}, "
|
227
|
-
f"but got {type(
|
273
|
+
f"but got {type(configs[key])} instead."
|
228
274
|
)
|
229
275
|
elif stage == STAGE_SHARE_KEYS:
|
230
|
-
for key, value in
|
276
|
+
for key, value in configs.items():
|
231
277
|
if (
|
232
278
|
not isinstance(value, list)
|
233
279
|
or len(value) != 2
|
@@ -242,18 +288,17 @@ def check_named_values(stage: str, named_values: Dict[str, Value]) -> None:
|
|
242
288
|
key_type_pairs = [
|
243
289
|
(KEY_CIPHERTEXT_LIST, bytes),
|
244
290
|
(KEY_SOURCE_LIST, int),
|
245
|
-
(KEY_PARAMETERS, bytes),
|
246
291
|
]
|
247
292
|
for key, expected_type in key_type_pairs:
|
248
|
-
if key not in
|
293
|
+
if key not in configs:
|
249
294
|
raise KeyError(
|
250
295
|
f"Stage {STAGE_COLLECT_MASKED_INPUT}: "
|
251
296
|
f"the required key '{key}' is "
|
252
297
|
"missing from the input `named_values`."
|
253
298
|
)
|
254
|
-
if not isinstance(
|
299
|
+
if not isinstance(configs[key], list) or any(
|
255
300
|
elm
|
256
|
-
for elm in cast(List[Any],
|
301
|
+
for elm in cast(List[Any], configs[key])
|
257
302
|
# pylint: disable-next=unidiomatic-typecheck
|
258
303
|
if type(elm) is not expected_type
|
259
304
|
):
|
@@ -268,15 +313,15 @@ def check_named_values(stage: str, named_values: Dict[str, Value]) -> None:
|
|
268
313
|
(KEY_DEAD_SECURE_ID_LIST, int),
|
269
314
|
]
|
270
315
|
for key, expected_type in key_type_pairs:
|
271
|
-
if key not in
|
316
|
+
if key not in configs:
|
272
317
|
raise KeyError(
|
273
318
|
f"Stage {STAGE_UNMASK}: "
|
274
319
|
f"the required key '{key}' is "
|
275
320
|
"missing from the input `named_values`."
|
276
321
|
)
|
277
|
-
if not isinstance(
|
322
|
+
if not isinstance(configs[key], list) or any(
|
278
323
|
elm
|
279
|
-
for elm in cast(List[Any],
|
324
|
+
for elm in cast(List[Any], configs[key])
|
280
325
|
# pylint: disable-next=unidiomatic-typecheck
|
281
326
|
if type(elm) is not expected_type
|
282
327
|
):
|
@@ -289,9 +334,11 @@ def check_named_values(stage: str, named_values: Dict[str, Value]) -> None:
|
|
289
334
|
raise ValueError(f"Unknown secagg stage: {stage}")
|
290
335
|
|
291
336
|
|
292
|
-
def _setup(
|
337
|
+
def _setup(
|
338
|
+
state: SecAggPlusState, configs: Dict[str, ConfigsRecordValues]
|
339
|
+
) -> Dict[str, ConfigsRecordValues]:
|
293
340
|
# Assigning parameter values to object fields
|
294
|
-
sec_agg_param_dict =
|
341
|
+
sec_agg_param_dict = configs
|
295
342
|
state.sample_num = cast(int, sec_agg_param_dict[KEY_SAMPLE_NUMBER])
|
296
343
|
state.sid = cast(int, sec_agg_param_dict[KEY_SECURE_ID])
|
297
344
|
log(INFO, "Client %d: starting stage 0...", state.sid)
|
@@ -324,9 +371,9 @@ def _setup(state: SecAggPlusState, named_values: Dict[str, Value]) -> Dict[str,
|
|
324
371
|
|
325
372
|
# pylint: disable-next=too-many-locals
|
326
373
|
def _share_keys(
|
327
|
-
state: SecAggPlusState,
|
328
|
-
) -> Dict[str,
|
329
|
-
named_bytes_tuples = cast(Dict[str, Tuple[bytes, bytes]],
|
374
|
+
state: SecAggPlusState, configs: Dict[str, ConfigsRecordValues]
|
375
|
+
) -> Dict[str, ConfigsRecordValues]:
|
376
|
+
named_bytes_tuples = cast(Dict[str, Tuple[bytes, bytes]], configs)
|
330
377
|
key_dict = {int(sid): (pk1, pk2) for sid, (pk1, pk2) in named_bytes_tuples.items()}
|
331
378
|
log(INFO, "Client %d: starting stage 1...", state.sid)
|
332
379
|
state.public_keys_dict = key_dict
|
@@ -386,12 +433,14 @@ def _share_keys(
|
|
386
433
|
|
387
434
|
# pylint: disable-next=too-many-locals
|
388
435
|
def _collect_masked_input(
|
389
|
-
state: SecAggPlusState,
|
390
|
-
|
436
|
+
state: SecAggPlusState,
|
437
|
+
configs: Dict[str, ConfigsRecordValues],
|
438
|
+
fit: Callable[[], FitRes],
|
439
|
+
) -> Dict[str, ConfigsRecordValues]:
|
391
440
|
log(INFO, "Client %d: starting stage 2...", state.sid)
|
392
441
|
available_clients: List[int] = []
|
393
|
-
ciphertexts = cast(List[bytes],
|
394
|
-
srcs = cast(List[int],
|
442
|
+
ciphertexts = cast(List[bytes], configs[KEY_CIPHERTEXT_LIST])
|
443
|
+
srcs = cast(List[int], configs[KEY_SOURCE_LIST])
|
395
444
|
if len(ciphertexts) + 1 < state.threshold:
|
396
445
|
raise ValueError("Not enough available neighbour clients.")
|
397
446
|
|
@@ -417,18 +466,9 @@ def _collect_masked_input(
|
|
417
466
|
state.sk1_share_dict[src] = sk1_share
|
418
467
|
|
419
468
|
# Fit client
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
fit_res = state.client.fit(
|
424
|
-
FitIns(parameters=ndarrays_to_parameters(parameters), config={})
|
425
|
-
)
|
426
|
-
parameters_factor = fit_res.num_examples
|
427
|
-
parameters = parameters_to_ndarrays(fit_res.parameters)
|
428
|
-
elif isinstance(state.client, NumPyClient):
|
429
|
-
parameters, parameters_factor, _ = state.client.fit(parameters, {})
|
430
|
-
else:
|
431
|
-
log(ERROR, "Client %d: fit function is missing.", state.sid)
|
469
|
+
fit_res = fit()
|
470
|
+
parameters_factor = fit_res.num_examples
|
471
|
+
parameters = parameters_to_ndarrays(fit_res.parameters)
|
432
472
|
|
433
473
|
# Quantize parameter update (vector)
|
434
474
|
quantized_parameters = quantize(
|
@@ -468,11 +508,13 @@ def _collect_masked_input(
|
|
468
508
|
}
|
469
509
|
|
470
510
|
|
471
|
-
def _unmask(
|
511
|
+
def _unmask(
|
512
|
+
state: SecAggPlusState, configs: Dict[str, ConfigsRecordValues]
|
513
|
+
) -> Dict[str, ConfigsRecordValues]:
|
472
514
|
log(INFO, "Client %d: starting stage 3...", state.sid)
|
473
515
|
|
474
|
-
active_sids = cast(List[int],
|
475
|
-
dead_sids = cast(List[int],
|
516
|
+
active_sids = cast(List[int], configs[KEY_ACTIVE_SECURE_ID_LIST])
|
517
|
+
dead_sids = cast(List[int], configs[KEY_DEAD_SECURE_ID_LIST])
|
476
518
|
# Send private mask seed share for every avaliable client (including itclient)
|
477
519
|
# Send first private key share for building pairwise mask for every dropped client
|
478
520
|
if len(active_sids) < state.threshold:
|
flwr/common/dp.py
CHANGED
@@ -19,11 +19,13 @@ from typing import Tuple
|
|
19
19
|
|
20
20
|
import numpy as np
|
21
21
|
|
22
|
+
from flwr.common.logger import warn_deprecated_feature
|
22
23
|
from flwr.common.typing import NDArrays
|
23
24
|
|
24
25
|
|
25
26
|
# Calculates the L2-norm of a potentially ragged array
|
26
27
|
def _get_update_norm(update: NDArrays) -> float:
|
28
|
+
warn_deprecated_feature("`_get_update_norm` method")
|
27
29
|
flattened_update = update[0]
|
28
30
|
for i in range(1, len(update)):
|
29
31
|
flattened_update = np.append(flattened_update, update[i])
|
@@ -32,6 +34,7 @@ def _get_update_norm(update: NDArrays) -> float:
|
|
32
34
|
|
33
35
|
def add_gaussian_noise(update: NDArrays, std_dev: float) -> NDArrays:
|
34
36
|
"""Add iid Gaussian noise to each floating point value in the update."""
|
37
|
+
warn_deprecated_feature("`add_gaussian_noise` method")
|
35
38
|
update_noised = [
|
36
39
|
layer + np.random.normal(0, std_dev, layer.shape) for layer in update
|
37
40
|
]
|
@@ -40,6 +43,7 @@ def add_gaussian_noise(update: NDArrays, std_dev: float) -> NDArrays:
|
|
40
43
|
|
41
44
|
def clip_by_l2(update: NDArrays, threshold: float) -> Tuple[NDArrays, bool]:
|
42
45
|
"""Scales the update so thats its L2 norm is upper-bound to threshold."""
|
46
|
+
warn_deprecated_feature("`clip_by_l2` method")
|
43
47
|
update_norm = _get_update_norm(update)
|
44
48
|
scaling_factor = min(1, threshold / update_norm)
|
45
49
|
update_clipped: NDArrays = [layer * scaling_factor for layer in update]
|
@@ -14,6 +14,8 @@
|
|
14
14
|
# ==============================================================================
|
15
15
|
"""Constants for the SecAgg/SecAgg+ protocol."""
|
16
16
|
|
17
|
+
RECORD_KEY_STATE = "secaggplus_state"
|
18
|
+
RECORD_KEY_CONFIGS = "secaggplus_configs"
|
17
19
|
|
18
20
|
# Names of stages
|
19
21
|
STAGE_SETUP = "setup"
|
flwr/driver/app.py
CHANGED
@@ -110,7 +110,9 @@ def start_driver( # pylint: disable=too-many-arguments, too-many-locals
|
|
110
110
|
# Create the Driver
|
111
111
|
if isinstance(root_certificates, str):
|
112
112
|
root_certificates = Path(root_certificates).read_bytes()
|
113
|
-
driver = GrpcDriver(
|
113
|
+
driver = GrpcDriver(
|
114
|
+
driver_service_address=address, root_certificates=root_certificates
|
115
|
+
)
|
114
116
|
driver.connect()
|
115
117
|
lock = threading.Lock()
|
116
118
|
|
flwr/driver/driver.py
CHANGED
@@ -49,10 +49,10 @@ class Driver:
|
|
49
49
|
def __init__(
|
50
50
|
self,
|
51
51
|
driver_service_address: str = DEFAULT_SERVER_ADDRESS_DRIVER,
|
52
|
-
|
52
|
+
root_certificates: Optional[bytes] = None,
|
53
53
|
) -> None:
|
54
54
|
self.addr = driver_service_address
|
55
|
-
self.
|
55
|
+
self.root_certificates = root_certificates
|
56
56
|
self.grpc_driver: Optional[GrpcDriver] = None
|
57
57
|
self.run_id: Optional[int] = None
|
58
58
|
self.node = Node(node_id=0, anonymous=True)
|
@@ -62,7 +62,8 @@ class Driver:
|
|
62
62
|
if self.grpc_driver is None or self.run_id is None:
|
63
63
|
# Connect and create run
|
64
64
|
self.grpc_driver = GrpcDriver(
|
65
|
-
driver_service_address=self.addr,
|
65
|
+
driver_service_address=self.addr,
|
66
|
+
root_certificates=self.root_certificates,
|
66
67
|
)
|
67
68
|
self.grpc_driver.connect()
|
68
69
|
res = self.grpc_driver.create_run(CreateRunRequest())
|
flwr/driver/grpc_driver.py
CHANGED
@@ -51,10 +51,10 @@ class GrpcDriver:
|
|
51
51
|
def __init__(
|
52
52
|
self,
|
53
53
|
driver_service_address: str = DEFAULT_SERVER_ADDRESS_DRIVER,
|
54
|
-
|
54
|
+
root_certificates: Optional[bytes] = None,
|
55
55
|
) -> None:
|
56
56
|
self.driver_service_address = driver_service_address
|
57
|
-
self.
|
57
|
+
self.root_certificates = root_certificates
|
58
58
|
self.channel: Optional[grpc.Channel] = None
|
59
59
|
self.stub: Optional[DriverStub] = None
|
60
60
|
|
@@ -66,8 +66,8 @@ class GrpcDriver:
|
|
66
66
|
return
|
67
67
|
self.channel = create_channel(
|
68
68
|
server_address=self.driver_service_address,
|
69
|
-
insecure=(self.
|
70
|
-
root_certificates=self.
|
69
|
+
insecure=(self.root_certificates is None),
|
70
|
+
root_certificates=self.root_certificates,
|
71
71
|
)
|
72
72
|
self.stub = DriverStub(self.channel)
|
73
73
|
log(INFO, "[Driver] Connected to %s", self.driver_service_address)
|
flwr/server/app.py
CHANGED
@@ -532,7 +532,7 @@ def _run_fleet_api_grpc_rere(
|
|
532
532
|
"""Run Fleet API (gRPC, request-response)."""
|
533
533
|
# Create Fleet API gRPC server
|
534
534
|
fleet_servicer = FleetServicer(
|
535
|
-
|
535
|
+
state_factory=state_factory,
|
536
536
|
)
|
537
537
|
fleet_add_servicer_to_server_fn = add_FleetServicer_to_server
|
538
538
|
fleet_grpc_server = generic_create_grpc_server(
|
@@ -18,7 +18,7 @@ Relevant knowledge for reading this modules code:
|
|
18
18
|
- https://github.com/grpc/grpc/blob/master/doc/statuscodes.md
|
19
19
|
"""
|
20
20
|
|
21
|
-
|
21
|
+
import uuid
|
22
22
|
from typing import Callable, Iterator
|
23
23
|
|
24
24
|
import grpc
|
@@ -91,9 +91,12 @@ class FlowerServiceServicer(transport_pb2_grpc.FlowerServiceServicer):
|
|
91
91
|
wrapping the actual message
|
92
92
|
- The `Join` method is (pretty much) unaware of the protocol
|
93
93
|
"""
|
94
|
-
peer
|
94
|
+
# When running Flower behind a proxy, the peer can be the same for
|
95
|
+
# different clients, so instead of `cid: str = context.peer()` we
|
96
|
+
# use a `UUID4` that is unique.
|
97
|
+
cid: str = uuid.uuid4().hex
|
95
98
|
bridge = self.grpc_bridge_factory()
|
96
|
-
client_proxy = self.client_proxy_factory(
|
99
|
+
client_proxy = self.client_proxy_factory(cid, bridge)
|
97
100
|
is_success = register_client_proxy(self.client_manager, client_proxy, context)
|
98
101
|
|
99
102
|
if is_success:
|
@@ -32,14 +32,14 @@ from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
|
|
32
32
|
PushTaskResResponse,
|
33
33
|
)
|
34
34
|
from flwr.server.fleet.message_handler import message_handler
|
35
|
-
from flwr.server.state import
|
35
|
+
from flwr.server.state import StateFactory
|
36
36
|
|
37
37
|
|
38
38
|
class FleetServicer(fleet_pb2_grpc.FleetServicer):
|
39
39
|
"""Fleet API servicer."""
|
40
40
|
|
41
|
-
def __init__(self,
|
42
|
-
self.
|
41
|
+
def __init__(self, state_factory: StateFactory) -> None:
|
42
|
+
self.state_factory = state_factory
|
43
43
|
|
44
44
|
def CreateNode(
|
45
45
|
self, request: CreateNodeRequest, context: grpc.ServicerContext
|
@@ -48,7 +48,7 @@ class FleetServicer(fleet_pb2_grpc.FleetServicer):
|
|
48
48
|
log(INFO, "FleetServicer.CreateNode")
|
49
49
|
return message_handler.create_node(
|
50
50
|
request=request,
|
51
|
-
state=self.state,
|
51
|
+
state=self.state_factory.state(),
|
52
52
|
)
|
53
53
|
|
54
54
|
def DeleteNode(
|
@@ -58,7 +58,7 @@ class FleetServicer(fleet_pb2_grpc.FleetServicer):
|
|
58
58
|
log(INFO, "FleetServicer.DeleteNode")
|
59
59
|
return message_handler.delete_node(
|
60
60
|
request=request,
|
61
|
-
state=self.state,
|
61
|
+
state=self.state_factory.state(),
|
62
62
|
)
|
63
63
|
|
64
64
|
def PullTaskIns(
|
@@ -68,7 +68,7 @@ class FleetServicer(fleet_pb2_grpc.FleetServicer):
|
|
68
68
|
log(INFO, "FleetServicer.PullTaskIns")
|
69
69
|
return message_handler.pull_task_ins(
|
70
70
|
request=request,
|
71
|
-
state=self.state,
|
71
|
+
state=self.state_factory.state(),
|
72
72
|
)
|
73
73
|
|
74
74
|
def PushTaskRes(
|
@@ -78,5 +78,5 @@ class FleetServicer(fleet_pb2_grpc.FleetServicer):
|
|
78
78
|
log(INFO, "FleetServicer.PushTaskRes")
|
79
79
|
return message_handler.push_task_res(
|
80
80
|
request=request,
|
81
|
-
state=self.state,
|
81
|
+
state=self.state_factory.state(),
|
82
82
|
)
|
@@ -24,6 +24,7 @@ from typing import Dict, List, Optional, Tuple, Union
|
|
24
24
|
import numpy as np
|
25
25
|
|
26
26
|
from flwr.common import FitIns, FitRes, Parameters, Scalar
|
27
|
+
from flwr.common.logger import warn_deprecated_feature
|
27
28
|
from flwr.server.client_manager import ClientManager
|
28
29
|
from flwr.server.client_proxy import ClientProxy
|
29
30
|
from flwr.server.strategy.dpfedavg_fixed import DPFedAvgFixed
|
@@ -31,7 +32,12 @@ from flwr.server.strategy.strategy import Strategy
|
|
31
32
|
|
32
33
|
|
33
34
|
class DPFedAvgAdaptive(DPFedAvgFixed):
|
34
|
-
"""Wrapper for configuring a Strategy for DP with Adaptive Clipping.
|
35
|
+
"""Wrapper for configuring a Strategy for DP with Adaptive Clipping.
|
36
|
+
|
37
|
+
Warning
|
38
|
+
-------
|
39
|
+
This class is deprecated and will be removed in a future release.
|
40
|
+
"""
|
35
41
|
|
36
42
|
# pylint: disable=too-many-arguments,too-many-instance-attributes
|
37
43
|
def __init__(
|
@@ -45,6 +51,7 @@ class DPFedAvgAdaptive(DPFedAvgFixed):
|
|
45
51
|
clip_norm_target_quantile: float = 0.5,
|
46
52
|
clip_count_stddev: Optional[float] = None,
|
47
53
|
) -> None:
|
54
|
+
warn_deprecated_feature("`DPFedAvgAdaptive` wrapper")
|
48
55
|
super().__init__(
|
49
56
|
strategy=strategy,
|
50
57
|
num_sampled_clients=num_sampled_clients,
|
@@ -17,11 +17,11 @@
|
|
17
17
|
Paper: arxiv.org/pdf/1710.06963.pdf
|
18
18
|
"""
|
19
19
|
|
20
|
-
|
21
20
|
from typing import Dict, List, Optional, Tuple, Union
|
22
21
|
|
23
22
|
from flwr.common import EvaluateIns, EvaluateRes, FitIns, FitRes, Parameters, Scalar
|
24
23
|
from flwr.common.dp import add_gaussian_noise
|
24
|
+
from flwr.common.logger import warn_deprecated_feature
|
25
25
|
from flwr.common.parameter import ndarrays_to_parameters, parameters_to_ndarrays
|
26
26
|
from flwr.server.client_manager import ClientManager
|
27
27
|
from flwr.server.client_proxy import ClientProxy
|
@@ -29,7 +29,12 @@ from flwr.server.strategy.strategy import Strategy
|
|
29
29
|
|
30
30
|
|
31
31
|
class DPFedAvgFixed(Strategy):
|
32
|
-
"""Wrapper for configuring a Strategy for DP with Fixed Clipping.
|
32
|
+
"""Wrapper for configuring a Strategy for DP with Fixed Clipping.
|
33
|
+
|
34
|
+
Warning
|
35
|
+
-------
|
36
|
+
This class is deprecated and will be removed in a future release.
|
37
|
+
"""
|
33
38
|
|
34
39
|
# pylint: disable=too-many-arguments,too-many-instance-attributes
|
35
40
|
def __init__(
|
@@ -40,6 +45,7 @@ class DPFedAvgFixed(Strategy):
|
|
40
45
|
noise_multiplier: float = 1,
|
41
46
|
server_side_noising: bool = True,
|
42
47
|
) -> None:
|
48
|
+
warn_deprecated_feature("`DPFedAvgFixed` wrapper")
|
43
49
|
super().__init__()
|
44
50
|
self.strategy = strategy
|
45
51
|
# Doing fixed-size subsampling as in https://arxiv.org/abs/1905.03871.
|
@@ -44,6 +44,11 @@ class FedXgbBagging(FedAvg):
|
|
44
44
|
self.global_model: Optional[bytes] = None
|
45
45
|
super().__init__(**kwargs)
|
46
46
|
|
47
|
+
def __repr__(self) -> str:
|
48
|
+
"""Compute a string representation of the strategy."""
|
49
|
+
rep = f"FedXgbBagging(accept_failures={self.accept_failures})"
|
50
|
+
return rep
|
51
|
+
|
47
52
|
def aggregate_fit(
|
48
53
|
self,
|
49
54
|
server_round: int,
|
@@ -37,6 +37,11 @@ class FedXgbCyclic(FedAvg):
|
|
37
37
|
self.global_model: Optional[bytes] = None
|
38
38
|
super().__init__(**kwargs)
|
39
39
|
|
40
|
+
def __repr__(self) -> str:
|
41
|
+
"""Compute a string representation of the strategy."""
|
42
|
+
rep = f"FedXgbCyclic(accept_failures={self.accept_failures})"
|
43
|
+
return rep
|
44
|
+
|
40
45
|
def aggregate_fit(
|
41
46
|
self,
|
42
47
|
server_round: int,
|
{flwr_nightly-1.7.0.dev20240130.dist-info → flwr_nightly-1.7.0.dev20240201.dist-info}/METADATA
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: flwr-nightly
|
3
|
-
Version: 1.7.0.
|
3
|
+
Version: 1.7.0.dev20240201
|
4
4
|
Summary: Flower: A Friendly Federated Learning Framework
|
5
5
|
Home-page: https://flower.dev
|
6
6
|
License: Apache-2.0
|
@@ -82,8 +82,7 @@ design of Flower is based on a few guiding principles:
|
|
82
82
|
|
83
83
|
- **Framework-agnostic**: Different machine learning frameworks have different
|
84
84
|
strengths. Flower can be used with any machine learning framework, for
|
85
|
-
example, [PyTorch](https://pytorch.org),
|
86
|
-
[TensorFlow](https://tensorflow.org), [Hugging Face Transformers](https://huggingface.co/), [PyTorch Lightning](https://pytorchlightning.ai/), [scikit-learn](https://scikit-learn.org/), [JAX](https://jax.readthedocs.io/), [TFLite](https://tensorflow.org/lite/), [fastai](https://www.fast.ai/), [Pandas](https://pandas.pydata.org/) for federated analytics, or even raw [NumPy](https://numpy.org/)
|
85
|
+
example, [PyTorch](https://pytorch.org), [TensorFlow](https://tensorflow.org), [Hugging Face Transformers](https://huggingface.co/), [PyTorch Lightning](https://pytorchlightning.ai/), [scikit-learn](https://scikit-learn.org/), [JAX](https://jax.readthedocs.io/), [TFLite](https://tensorflow.org/lite/), [fastai](https://www.fast.ai/), [MLX](https://ml-explore.github.io/mlx/build/html/index.html), [XGBoost](https://xgboost.readthedocs.io/en/stable/), [Pandas](https://pandas.pydata.org/) for federated analytics, or even raw [NumPy](https://numpy.org/)
|
87
86
|
for users who enjoy computing gradients by hand.
|
88
87
|
|
89
88
|
- **Understandable**: Flower is written with maintainability in mind. The
|
@@ -129,7 +128,7 @@ Stay tuned, more tutorials are coming soon. Topics include **Privacy and Securit
|
|
129
128
|
- [Quickstart (TensorFlow)](https://flower.dev/docs/framework/tutorial-quickstart-tensorflow.html)
|
130
129
|
- [Quickstart (PyTorch)](https://flower.dev/docs/framework/tutorial-quickstart-pytorch.html)
|
131
130
|
- [Quickstart (Hugging Face)](https://flower.dev/docs/framework/tutorial-quickstart-huggingface.html)
|
132
|
-
- [Quickstart (PyTorch Lightning
|
131
|
+
- [Quickstart (PyTorch Lightning)](https://flower.dev/docs/framework/tutorial-quickstart-pytorch-lightning.html)
|
133
132
|
- [Quickstart (Pandas)](https://flower.dev/docs/framework/tutorial-quickstart-pandas.html)
|
134
133
|
- [Quickstart (fastai)](https://flower.dev/docs/framework/tutorial-quickstart-fastai.html)
|
135
134
|
- [Quickstart (JAX)](https://flower.dev/docs/framework/tutorial-quickstart-jax.html)
|
@@ -148,11 +147,17 @@ Flower Baselines is a collection of community-contributed projects that reproduc
|
|
148
147
|
- [FedMLB](https://github.com/adap/flower/tree/main/baselines/fedmlb)
|
149
148
|
- [FedPer](https://github.com/adap/flower/tree/main/baselines/fedper)
|
150
149
|
- [FedProx](https://github.com/adap/flower/tree/main/baselines/fedprox)
|
150
|
+
- [FedNova](https://github.com/adap/flower/tree/main/baselines/fednova)
|
151
|
+
- [HeteroFL](https://github.com/adap/flower/tree/main/baselines/heterofl)
|
152
|
+
- [FedAvgM](https://github.com/adap/flower/tree/main/baselines/fedavgm)
|
151
153
|
- [FedWav2vec2](https://github.com/adap/flower/tree/main/baselines/fedwav2vec2)
|
152
154
|
- [FjORD](https://github.com/adap/flower/tree/main/baselines/fjord)
|
153
155
|
- [MOON](https://github.com/adap/flower/tree/main/baselines/moon)
|
154
156
|
- [niid-Bench](https://github.com/adap/flower/tree/main/baselines/niid_bench)
|
155
157
|
- [TAMUNA](https://github.com/adap/flower/tree/main/baselines/tamuna)
|
158
|
+
- [FedVSSL](https://github.com/adap/flower/tree/main/baselines/fedvssl)
|
159
|
+
- [FedXGBoost](https://github.com/adap/flower/tree/main/baselines/hfedxgboost)
|
160
|
+
- [FedPara](https://github.com/adap/flower/tree/main/baselines/fedpara)
|
156
161
|
- [FedAvg](https://github.com/adap/flower/tree/main/baselines/flwr_baselines/flwr_baselines/publications/fedavg_mnist)
|
157
162
|
- [FedOpt](https://github.com/adap/flower/tree/main/baselines/flwr_baselines/flwr_baselines/publications/adaptive_federated_optimization)
|
158
163
|
|
@@ -174,16 +179,23 @@ Quickstart examples:
|
|
174
179
|
- [Quickstart (Pandas)](https://github.com/adap/flower/tree/main/examples/quickstart-pandas)
|
175
180
|
- [Quickstart (JAX)](https://github.com/adap/flower/tree/main/examples/quickstart-jax)
|
176
181
|
- [Quickstart (scikit-learn)](https://github.com/adap/flower/tree/main/examples/sklearn-logreg-mnist)
|
182
|
+
- [Quickstart (XGBoost)](https://github.com/adap/flower/tree/main/examples/xgboost-quickstart)
|
177
183
|
- [Quickstart (Android [TFLite])](https://github.com/adap/flower/tree/main/examples/android)
|
178
184
|
- [Quickstart (iOS [CoreML])](https://github.com/adap/flower/tree/main/examples/ios)
|
185
|
+
- [Quickstart (MLX)](https://github.com/adap/flower/tree/main/examples/quickstart-mlx)
|
186
|
+
- [Quickstart (XGBoost)](https://github.com/adap/flower/tree/main/examples/xgboost-quickstart)
|
179
187
|
|
180
188
|
Other [examples](https://github.com/adap/flower/tree/main/examples):
|
181
189
|
|
182
190
|
- [Raspberry Pi & Nvidia Jetson Tutorial](https://github.com/adap/flower/tree/main/examples/embedded-devices)
|
183
191
|
- [PyTorch: From Centralized to Federated](https://github.com/adap/flower/tree/main/examples/pytorch-from-centralized-to-federated)
|
192
|
+
- [Vertical FL](https://github.com/adap/flower/tree/main/examples/vertical-fl)
|
193
|
+
- [Federated Finetuning of OpenAI's Whisper](https://github.com/adap/flower/tree/main/examples/whisper-federated-finetuning)
|
194
|
+
- [Comprehensive XGBoost](https://github.com/adap/flower/tree/main/examples/xgboost-comprehensive)
|
184
195
|
- [Advanced Flower with TensorFlow/Keras](https://github.com/adap/flower/tree/main/examples/advanced-tensorflow)
|
185
196
|
- [Advanced Flower with PyTorch](https://github.com/adap/flower/tree/main/examples/advanced-pytorch)
|
186
|
-
- Single-Machine Simulation of Federated Learning Systems ([PyTorch](https://github.com/adap/flower/tree/main/examples/
|
197
|
+
- Single-Machine Simulation of Federated Learning Systems ([PyTorch](https://github.com/adap/flower/tree/main/examples/simulation-pytorch)) ([Tensorflow](https://github.com/adap/flower/tree/main/examples/simulation-tensorflow))
|
198
|
+
- [Comprehensive Flower+XGBoost](https://github.com/adap/flower/tree/main/examples/xgboost-comprehensive)
|
187
199
|
- [Flower through Docker Compose and with Grafana dashboard](https://github.com/adap/flower/tree/main/examples/flower-via-docker-compose)
|
188
200
|
|
189
201
|
## Community
|
{flwr_nightly-1.7.0.dev20240130.dist-info → flwr_nightly-1.7.0.dev20240201.dist-info}/RECORD
RENAMED
@@ -1,8 +1,8 @@
|
|
1
1
|
flwr/__init__.py,sha256=6zbcS7z2q-VUdmpFppLH6BacsE-ZFmfq6OvtKNOyYE0,981
|
2
2
|
flwr/client/__init__.py,sha256=2T4enmlE4PsoKiGTvXwBKSlhOjZ7MXRy5oCGNf0UH9Y,1111
|
3
|
-
flwr/client/app.py,sha256=
|
3
|
+
flwr/client/app.py,sha256=EMN_gRPn6Jn9sDUaDRHFHVR7p4a9a1VWq358-7ToZmg,19947
|
4
4
|
flwr/client/client.py,sha256=ATcsqAMS9zpMBJ9ZUbBeB7BEPWX_VWISONy0p6Wxl5g,8210
|
5
|
-
flwr/client/dpfedavg_numpy_client.py,sha256=
|
5
|
+
flwr/client/dpfedavg_numpy_client.py,sha256=9Tnig4iml2J88HBKNahegjXjbfvIQyBtaIQaqjbeqsA,7435
|
6
6
|
flwr/client/flower.py,sha256=QKWBjB8YbzlKfC1EPVpxLfiYRL078C7BL5hPaX_rUMY,4211
|
7
7
|
flwr/client/grpc_client/__init__.py,sha256=LsnbqXiJhgQcB0XzAlUQgPx011Uf7Y7yabIC1HxivJ8,735
|
8
8
|
flwr/client/grpc_client/connection.py,sha256=x50raKKVKVNjNNOr2u1gOS2Ts6ohs2cAGrRXZw6sgLE,8226
|
@@ -11,16 +11,15 @@ flwr/client/grpc_rere_client/connection.py,sha256=Ey1xN0a69nv59MV8-BC8BpYwPqhufU
|
|
11
11
|
flwr/client/message_handler/__init__.py,sha256=abHvBRJJiiaAMNgeILQbMOa6h8WqMK2BcnvxwQZFpic,719
|
12
12
|
flwr/client/message_handler/message_handler.py,sha256=cknnjvgmNdBZOcPfJ4bBjcIK8ZDqXZMiXK69-toumNs,5900
|
13
13
|
flwr/client/message_handler/task_handler.py,sha256=_mmUiMKlnO196Jqw9sEgTevkWQm7ra-Vf3JzQD-Cx_A,4128
|
14
|
-
flwr/client/middleware/__init__.py,sha256=
|
14
|
+
flwr/client/middleware/__init__.py,sha256=mYWAt8YUy2aiL-kK7LDASgMpbsYxfRoE4iptf1zene4,874
|
15
|
+
flwr/client/middleware/secure_aggregation/__init__.py,sha256=3GinEPdTOULN5D9BD2ujX0uEEUHytlKoCT81rErw6sw,819
|
16
|
+
flwr/client/middleware/secure_aggregation/secaggplus_middleware.py,sha256=A6fpS3uQEP_vMeRZ688GK7LJ13WlP1TQksyMapvHGHU,20111
|
15
17
|
flwr/client/middleware/utils.py,sha256=8fWgKhDr_HV7ZawcgcPKqfwdDUpDfAtLItxtTi3oC5Q,1281
|
16
18
|
flwr/client/node_state.py,sha256=0hk1RZuFZ3_S7Y8Q0BCP60NljxnT9vqPmIflvCCGxnQ,1819
|
17
19
|
flwr/client/node_state_tests.py,sha256=H3sO526boAlDS491fO5xvZhl5XNDzzl-I8DiaVmnsXM,2195
|
18
20
|
flwr/client/numpy_client.py,sha256=42e8_gZU5gwzpvVXQr6LEWEGfpWYTcQ0MY0F0owNlAc,10310
|
19
21
|
flwr/client/rest_client/__init__.py,sha256=ThwOnkMdzxo_UuyTI47Q7y9oSpuTgNT2OuFvJCfuDiw,735
|
20
22
|
flwr/client/rest_client/connection.py,sha256=NK3PFGJvOwh3bUNSp5nfkfLHa6T5kzQ0XN9ire9F4tk,11964
|
21
|
-
flwr/client/secure_aggregation/__init__.py,sha256=XCDycteBTivym0zwkwqXhFMCAoDoHBZQg5GxdMnFCfA,888
|
22
|
-
flwr/client/secure_aggregation/handler.py,sha256=oRyGCerz92aED7UydYwJ7OFVxUwHqedG3PshHrdZq-Y,1522
|
23
|
-
flwr/client/secure_aggregation/secaggplus_handler.py,sha256=2jKtRhoJaVRmMJgJ2v8VRUCw2ko7uhTL2_h8CZVFwZA,18928
|
24
23
|
flwr/client/typing.py,sha256=pk0eIYcLUXRic_rRxQxW7Vdp7b6cRjlRiagpMs7HicQ,1023
|
25
24
|
flwr/common/__init__.py,sha256=qttep0POwoigzB5pcraZa4YMt9jsCSfeibcrTQMUjIc,2884
|
26
25
|
flwr/common/address.py,sha256=iTAN9jtmIGMrWFnx9XZQl45ZEtQJVZZLYPRBSNVARGI,1882
|
@@ -28,7 +27,7 @@ flwr/common/configsrecord.py,sha256=i41syiXeG4CyWr4ujOCxTDBZKiClNnNgR4xbufxThh0,
|
|
28
27
|
flwr/common/constant.py,sha256=tQxp9yXKXVajh6obsUlFs4UYIiTBxl4bGHnj9ds7NLQ,1263
|
29
28
|
flwr/common/context.py,sha256=OP4mTxsCdU2LS63TegdLHaEDpxULZ4AQ4ybZl7jEdDU,1329
|
30
29
|
flwr/common/date.py,sha256=UWhBZj49yX9LD4BmatS_ZFZu_-kweGh0KQJ1djyWWH4,891
|
31
|
-
flwr/common/dp.py,sha256=
|
30
|
+
flwr/common/dp.py,sha256=Hc3lLHihjexbJaD_ft31gdv9XRcwOTgDBwJzICuok3A,2004
|
32
31
|
flwr/common/grpc.py,sha256=qVLB0d6bCuaBRW5YB0vEZXsR7Bo3R2lh4ONiCocqwRI,2270
|
33
32
|
flwr/common/logger.py,sha256=qX_gqEyrmGOH0x_r8uQ1Vskz4fGvEij9asdo4DUOPY8,4135
|
34
33
|
flwr/common/message.py,sha256=GQuhFmvJIrCG6h4L-jQMsn6KYh-nmNqNNo5qVe7ye5w,1847
|
@@ -44,17 +43,17 @@ flwr/common/secure_aggregation/crypto/shamir.py,sha256=yY35ZgHlB4YyGW_buG-1X-0M-
|
|
44
43
|
flwr/common/secure_aggregation/crypto/symmetric_encryption.py,sha256=-zDyQoTsHHQjR7o-92FNIikg1zM_Ke9yynaD5u2BXbQ,3546
|
45
44
|
flwr/common/secure_aggregation/ndarrays_arithmetic.py,sha256=KAHCEHGSTJ6mCgnC8dTpIx6URk11s5XxWTHI_7ToGIg,2979
|
46
45
|
flwr/common/secure_aggregation/quantization.py,sha256=appui7GGrkRPsupF59TkapeV4Na_CyPi73JtJ1pimdI,2310
|
47
|
-
flwr/common/secure_aggregation/secaggplus_constants.py,sha256=
|
46
|
+
flwr/common/secure_aggregation/secaggplus_constants.py,sha256=pITi-nuzrNvKWR42XwVFBuejv1RdGLwmuErLp0X0t_Y,1686
|
48
47
|
flwr/common/secure_aggregation/secaggplus_utils.py,sha256=PleDyDu7jHNAfbRoEaoQiOjxG6iMl9yA8rNKYTfnyFw,3155
|
49
48
|
flwr/common/serde.py,sha256=rXUSH9TNFsRDBTdr9bGCKGLyjNuzagUOQVynS_luWkI,19643
|
50
49
|
flwr/common/telemetry.py,sha256=se_-pHgEWcmN09ChSpTeek72l1UJHf7GbwXBB1KXBjQ,7683
|
51
50
|
flwr/common/typing.py,sha256=3Wu6Ol1Ja6Gb0WdlcXVEn1EHYJbc4oRRJA81vEegxBo,4382
|
52
51
|
flwr/common/version.py,sha256=A0MKvyKPrV8wLg0YCAODTqM71v26NEH36c6JYtfgg0o,667
|
53
52
|
flwr/driver/__init__.py,sha256=NQ4KeZ5fP9wdxGjcr2cP41_7TLuuYQ3u4J7GwYtQ488,870
|
54
|
-
flwr/driver/app.py,sha256=
|
55
|
-
flwr/driver/driver.py,sha256=
|
53
|
+
flwr/driver/app.py,sha256=rhACazH0J06Tnqy5aU0DoJlx5ZksK4b0NgHkM1bAE38,7268
|
54
|
+
flwr/driver/driver.py,sha256=BhCXx55Lk9WdpcfjlHhKyLbGdiIvawIvMWYPewIaI8I,4009
|
56
55
|
flwr/driver/driver_client_proxy.py,sha256=wKC3R-293dOqxNytzf78z6tuDCXORbuJAdICyZ5jeK8,6126
|
57
|
-
flwr/driver/grpc_driver.py,sha256=
|
56
|
+
flwr/driver/grpc_driver.py,sha256=Ekcxx4Miqume02fiBjBRJhItaOFNpn120WT3uRIPWIc,4585
|
58
57
|
flwr/flower/__init__.py,sha256=aUO5V7ovBwDoXuS9Dqlpzq_m_VGRpYiRgR2jpokRPy8,786
|
59
58
|
flwr/proto/__init__.py,sha256=hbY7JYakwZwCkYgCNlmHdc8rtvfoJbAZLalMdc--CGc,683
|
60
59
|
flwr/proto/driver_pb2.py,sha256=JHIdjNPTgp6YHD-_lz5ZZFB0VIOR3_GmcaOTN4jndc4,3115
|
@@ -83,7 +82,7 @@ flwr/proto/transport_pb2_grpc.py,sha256=vLN3EHtx2aEEMCO4f1Upu-l27BPzd3-5pV-u8wPc
|
|
83
82
|
flwr/proto/transport_pb2_grpc.pyi,sha256=AGXf8RiIiW2J5IKMlm_3qT3AzcDa4F3P5IqUjve_esA,766
|
84
83
|
flwr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
85
84
|
flwr/server/__init__.py,sha256=SG7XJWnskPZu30VfY5WmXbfYKeb8UFrths4qprAb0yg,1377
|
86
|
-
flwr/server/app.py,sha256=
|
85
|
+
flwr/server/app.py,sha256=WYAyWQZksetbzR8Gos_LSQOTzzhzapuRhjXdks6K9xM,25619
|
87
86
|
flwr/server/client_manager.py,sha256=T8UDSRJBVD3fyIDI7NTAA-NA7GPrMNNgH2OAF54RRxE,6127
|
88
87
|
flwr/server/client_proxy.py,sha256=8ScGDvP3jHbl8DV3hyFID5N5VEVlXn8ZTQXtkdOfssI,2234
|
89
88
|
flwr/server/criterion.py,sha256=ypbAexbztzGUxNen9RCHF91QeqiEQix4t4Ih3E-42MM,1061
|
@@ -91,12 +90,12 @@ flwr/server/driver/__init__.py,sha256=STB1_DASVEg7Cu6L7VYxTzV7UMkgtBkFim09Z82Dh8
|
|
91
90
|
flwr/server/driver/driver_servicer.py,sha256=8JwftxoMMqHPAEgvE2TDDvm4zW2-7xaHYoji5kb6k8k,4553
|
92
91
|
flwr/server/fleet/__init__.py,sha256=C6GCSD5eP5Of6_dIeSe1jx9HnV0icsvWyQ5EKAUHJRU,711
|
93
92
|
flwr/server/fleet/grpc_bidi/__init__.py,sha256=mgGJGjwT6VU7ovC1gdnnqttjyBPlNIcZnYRqx4K3IBQ,735
|
94
|
-
flwr/server/fleet/grpc_bidi/flower_service_servicer.py,sha256=
|
93
|
+
flwr/server/fleet/grpc_bidi/flower_service_servicer.py,sha256=DVYlKxOVHwdQVux9XKJTomWwoLLlhhPhvvluU50hO4E,5956
|
95
94
|
flwr/server/fleet/grpc_bidi/grpc_bridge.py,sha256=LSOmabFXAQxKycQOliplKmigbmVwdm-D4CI-hJ0Pav0,6458
|
96
95
|
flwr/server/fleet/grpc_bidi/grpc_client_proxy.py,sha256=MubuEEAZAcKzx9LYAB87B2sjvZWWyQS3RPAJJm1GS4I,4695
|
97
96
|
flwr/server/fleet/grpc_bidi/grpc_server.py,sha256=TY_Z8ComreNNOOKveeUO6eE5hoSuTvx_EWqQl0zRADQ,11779
|
98
97
|
flwr/server/fleet/grpc_rere/__init__.py,sha256=bEJOMWbSlqkw-y5ZHtEXczhoSlAxErcRYffmTMQAV8M,758
|
99
|
-
flwr/server/fleet/grpc_rere/fleet_servicer.py,sha256=
|
98
|
+
flwr/server/fleet/grpc_rere/fleet_servicer.py,sha256=I6-mfOIKjxiGgHuL2AeiKqYnXDY_qBN1YXDJw1cttcA,2705
|
100
99
|
flwr/server/fleet/message_handler/__init__.py,sha256=hEY0l61ojH8Iz30_K1btm1HJ6J49iZJSFUsVYqUTw3A,731
|
101
100
|
flwr/server/fleet/message_handler/message_handler.py,sha256=_SauWQUO6VCMRRNXDfg3gv2h1FKG7e7p8F-Nj4aY6OM,2823
|
102
101
|
flwr/server/fleet/rest_rere/__init__.py,sha256=VKDvDq5H8koOUztpmQacVzGJXPLEEkL1Vmolxt3mvnY,735
|
@@ -111,8 +110,8 @@ flwr/server/state/state_factory.py,sha256=91cSB-KOAFM37z7T098WxTkVeKNaAZ_mTI75sn
|
|
111
110
|
flwr/server/strategy/__init__.py,sha256=EDTv_lU67VmZ8pRqy5fabQDhq5x4oRiD-KHoXhOIWMs,2096
|
112
111
|
flwr/server/strategy/aggregate.py,sha256=QyRIJtI5gnuY1NbgrcrOvkHxGIxBvApq7d9Y4xl-6W4,13468
|
113
112
|
flwr/server/strategy/bulyan.py,sha256=8GsSVJzRSoSWE2zQUKqC3Z795grdN9xpmc3MSGGXnzM,6532
|
114
|
-
flwr/server/strategy/dpfedavg_adaptive.py,sha256=
|
115
|
-
flwr/server/strategy/dpfedavg_fixed.py,sha256=
|
113
|
+
flwr/server/strategy/dpfedavg_adaptive.py,sha256=hLJkPQJl1bHjwrBNg3PSRFKf3no0hg5EHiFaWhHlWqw,4877
|
114
|
+
flwr/server/strategy/dpfedavg_fixed.py,sha256=mV_UimlGigUJjc2a9dmUig5EuVZ4yGIb9pkq31bKG1A,7217
|
116
115
|
flwr/server/strategy/fault_tolerant_fedavg.py,sha256=veGcehB6rXT_MihNDrD1v5JY-TxJi7fybdDl-OZooDQ,5900
|
117
116
|
flwr/server/strategy/fedadagrad.py,sha256=9yoVdZOFTjQ7DpaVrYLH9ca88WgJVWepld6UXybGQMY,6505
|
118
117
|
flwr/server/strategy/fedadam.py,sha256=Zvqo6oChwB2aDGHeLXHNE74nHGwkFAWODLZ8f6Dtq1g,6763
|
@@ -123,8 +122,8 @@ flwr/server/strategy/fedmedian.py,sha256=HpmUkLLXWgmMgQiEK2cG1l5nOd99ykAWdUxV5uU
|
|
123
122
|
flwr/server/strategy/fedopt.py,sha256=xqu-7513C8bFBw2qrzvduk9o2mT0sPHNVLKmVbJ3V4U,5242
|
124
123
|
flwr/server/strategy/fedprox.py,sha256=BBmIDoRtDeb3TpigO1beBZ79wIVy2UsVDIDnxktoyas,6862
|
125
124
|
flwr/server/strategy/fedtrimmedavg.py,sha256=p12uA7EN0k_CfYeh513P3m8mH3h14SmR3C_MQ9vw6Sc,5890
|
126
|
-
flwr/server/strategy/fedxgb_bagging.py,sha256=
|
127
|
-
flwr/server/strategy/fedxgb_cyclic.py,sha256=
|
125
|
+
flwr/server/strategy/fedxgb_bagging.py,sha256=Fm6bkulhPkes2k5lyG7oxgwtdJrXeNWDa-FCc_5AGVY,6080
|
126
|
+
flwr/server/strategy/fedxgb_cyclic.py,sha256=keiPOtd843-5uWWpRqJkEhvoNqTEB4zSIr51F6soosQ,5607
|
128
127
|
flwr/server/strategy/fedxgb_nn_avg.py,sha256=tA6X43juce0ShfRgfeNxRIcpSUlgVNwGB8vYoUuNFhI,4047
|
129
128
|
flwr/server/strategy/fedyogi.py,sha256=fG9i1WEdUXTYh5mTmagGLHqc12OogEsj3s3IopwM4ZA,6801
|
130
129
|
flwr/server/strategy/krum.py,sha256=yaYAZw4KOL84nc_PZAp43rBl0pXC0dT6y46sEuZrirA,6285
|
@@ -139,8 +138,8 @@ flwr/simulation/ray_transport/__init__.py,sha256=FsaAnzC4cw4DqoouBCix6496k29jACk
|
|
139
138
|
flwr/simulation/ray_transport/ray_actor.py,sha256=G_g50ISt3Knf0zuX1wmw39gsDXSoMI5f3rmYZWGrUh4,17062
|
140
139
|
flwr/simulation/ray_transport/ray_client_proxy.py,sha256=UxQEzWmklp3WO2V7LH5vNyAgYL7KYFFZQa1HTUSgEqY,9429
|
141
140
|
flwr/simulation/ray_transport/utils.py,sha256=5dwzUppfJP8lrpBU1rvhzfPZqAeGo8wx-hOm8wy_HmA,3376
|
142
|
-
flwr_nightly-1.7.0.
|
143
|
-
flwr_nightly-1.7.0.
|
144
|
-
flwr_nightly-1.7.0.
|
145
|
-
flwr_nightly-1.7.0.
|
146
|
-
flwr_nightly-1.7.0.
|
141
|
+
flwr_nightly-1.7.0.dev20240201.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
142
|
+
flwr_nightly-1.7.0.dev20240201.dist-info/METADATA,sha256=9Dy5grkrz2m-B-DQV_fT1We-X7-wXAEhYB5dyEH181c,14811
|
143
|
+
flwr_nightly-1.7.0.dev20240201.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
|
144
|
+
flwr_nightly-1.7.0.dev20240201.dist-info/entry_points.txt,sha256=1uLlD5tIunkzALMfMWnqjdE_D5hRUX_I1iMmOMv6tZI,181
|
145
|
+
flwr_nightly-1.7.0.dev20240201.dist-info/RECORD,,
|
@@ -1,43 +0,0 @@
|
|
1
|
-
# Copyright 2020 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
|
-
"""Message Handler for Secure Aggregation (abstract base class)."""
|
16
|
-
|
17
|
-
|
18
|
-
from abc import ABC, abstractmethod
|
19
|
-
from typing import Dict
|
20
|
-
|
21
|
-
from flwr.common.typing import Value
|
22
|
-
|
23
|
-
|
24
|
-
class SecureAggregationHandler(ABC):
|
25
|
-
"""Abstract base class for secure aggregation message handlers."""
|
26
|
-
|
27
|
-
@abstractmethod
|
28
|
-
def handle_secure_aggregation(
|
29
|
-
self, named_values: Dict[str, Value]
|
30
|
-
) -> Dict[str, Value]:
|
31
|
-
"""Handle incoming Secure Aggregation message and return results.
|
32
|
-
|
33
|
-
Parameters
|
34
|
-
----------
|
35
|
-
named_values : Dict[str, Value]
|
36
|
-
The named values retrieved from the SecureAggregation sub-message
|
37
|
-
of Task message in the server's TaskIns.
|
38
|
-
|
39
|
-
Returns
|
40
|
-
-------
|
41
|
-
Dict[str, Value]
|
42
|
-
The final/intermediate results of the Secure Aggregation protocol.
|
43
|
-
"""
|
{flwr_nightly-1.7.0.dev20240130.dist-info → flwr_nightly-1.7.0.dev20240201.dist-info}/LICENSE
RENAMED
File without changes
|
File without changes
|
File without changes
|