flwr-nightly 1.7.0.dev20240130__py3-none-any.whl → 1.7.0.dev20240201__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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
|