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 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(WARN, "Option `--insecure` was set. Starting insecure HTTP client.")
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
- print(args.root_certificates)
76
- print(args.server)
77
- print(args.dir)
78
- print(args.callable)
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", # Only
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
 
@@ -15,8 +15,10 @@
15
15
  """Middleware layers."""
16
16
 
17
17
 
18
+ from .secure_aggregation.secaggplus_middleware import secaggplus_middleware
18
19
  from .utils import make_ffn
19
20
 
20
21
  __all__ = [
21
22
  "make_ffn",
23
+ "secaggplus_middleware",
22
24
  ]
@@ -1,4 +1,4 @@
1
- # Copyright 2020 Flower Labs GmbH. All Rights Reserved.
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
- "SecAggPlusHandler",
23
- "SecureAggregationHandler",
19
+ "secaggplus_middleware",
24
20
  ]
@@ -1,4 +1,4 @@
1
- # Copyright 2020 Flower Labs GmbH. All Rights Reserved.
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 ERROR, INFO, WARNING
21
- from typing import Any, Dict, List, Optional, Tuple, Union, cast
22
-
23
- from flwr.client.client import Client
24
- from flwr.client.numpy_client import NumPyClient
25
- from flwr.common import (
26
- bytes_to_ndarray,
27
- ndarray_to_bytes,
28
- ndarrays_to_parameters,
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 FitIns, Value
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
- client: Optional[Union[Client, NumPyClient]] = None
116
-
117
-
118
- class SecAggPlusHandler(SecureAggregationHandler):
119
- """Message handler for the SecAgg+ protocol."""
120
-
121
- _shared_state = SecAggPlusState()
122
- _current_stage = STAGE_UNMASK
123
-
124
- def handle_secure_aggregation(
125
- self, named_values: Dict[str, Value]
126
- ) -> Dict[str, Value]:
127
- """Handle incoming message and return results, following the SecAgg+ protocol.
128
-
129
- Parameters
130
- ----------
131
- named_values : Dict[str, Value]
132
- The named values retrieved from the SecureAggregation sub-message
133
- of Task message in the server's TaskIns.
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
- Returns
136
- -------
137
- Dict[str, Value]
138
- The final/intermediate results of the SecAgg+ protocol.
139
- """
140
- # Check if self is a client
141
- if not isinstance(self, (Client, NumPyClient)):
142
- raise TypeError(
143
- "The subclass of SecAggPlusHandler must be "
144
- "the subclass of Client or NumPyClient."
145
- )
146
-
147
- # Check the validity of the next stage
148
- check_stage(self._current_stage, named_values)
149
-
150
- # Update the current stage
151
- self._current_stage = cast(str, named_values.pop(KEY_STAGE))
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
- # Check the validity of the `named_values` based on the current stage
154
- check_named_values(self._current_stage, named_values)
205
+ # Save state
206
+ ctxt.state.set_configs(RECORD_KEY_STATE, ConfigsRecord(state.to_dict()))
155
207
 
156
- # Execute
157
- if self._current_stage == STAGE_SETUP:
158
- self._shared_state = SecAggPlusState(client=self)
159
- return _setup(self._shared_state, named_values)
160
- if self._current_stage == STAGE_SHARE_KEYS:
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, named_values: Dict[str, Value]) -> None:
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 named_values:
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 = named_values[KEY_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 check_named_values(stage: str, named_values: Dict[str, Value]) -> None:
202
- """Check the validity of the input `named_values`."""
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 named_values:
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(named_values[key]) is not expected_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(named_values[key])} instead."
273
+ f"but got {type(configs[key])} instead."
228
274
  )
229
275
  elif stage == STAGE_SHARE_KEYS:
230
- for key, value in named_values.items():
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 named_values:
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(named_values[key], list) or any(
299
+ if not isinstance(configs[key], list) or any(
255
300
  elm
256
- for elm in cast(List[Any], named_values[key])
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 named_values:
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(named_values[key], list) or any(
322
+ if not isinstance(configs[key], list) or any(
278
323
  elm
279
- for elm in cast(List[Any], named_values[key])
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(state: SecAggPlusState, named_values: Dict[str, Value]) -> Dict[str, Value]:
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 = named_values
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, named_values: Dict[str, Value]
328
- ) -> Dict[str, Value]:
329
- named_bytes_tuples = cast(Dict[str, Tuple[bytes, bytes]], named_values)
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, named_values: Dict[str, Value]
390
- ) -> Dict[str, Value]:
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], named_values[KEY_CIPHERTEXT_LIST])
394
- srcs = cast(List[int], named_values[KEY_SOURCE_LIST])
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
- parameters_bytes = cast(List[bytes], named_values[KEY_PARAMETERS])
421
- parameters = [bytes_to_ndarray(w) for w in parameters_bytes]
422
- if isinstance(state.client, Client):
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(state: SecAggPlusState, named_values: Dict[str, Value]) -> Dict[str, Value]:
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], named_values[KEY_ACTIVE_SECURE_ID_LIST])
475
- dead_sids = cast(List[int], named_values[KEY_DEAD_SECURE_ID_LIST])
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(driver_service_address=address, certificates=root_certificates)
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
- certificates: Optional[bytes] = None,
52
+ root_certificates: Optional[bytes] = None,
53
53
  ) -> None:
54
54
  self.addr = driver_service_address
55
- self.certificates = certificates
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, certificates=self.certificates
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())
@@ -51,10 +51,10 @@ class GrpcDriver:
51
51
  def __init__(
52
52
  self,
53
53
  driver_service_address: str = DEFAULT_SERVER_ADDRESS_DRIVER,
54
- certificates: Optional[bytes] = None,
54
+ root_certificates: Optional[bytes] = None,
55
55
  ) -> None:
56
56
  self.driver_service_address = driver_service_address
57
- self.certificates = certificates
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.certificates is None),
70
- root_certificates=self.certificates,
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
- state=state_factory.state(),
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: str = context.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(peer, bridge)
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 State
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, state: State) -> None:
42
- self.state = state
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,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: flwr-nightly
3
- Version: 1.7.0.dev20240130
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 [code example])](https://flower.dev/docs/framework/tutorial-quickstart-pytorch-lightning.html)
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/simulation_pytorch)) ([Tensorflow](https://github.com/adap/flower/tree/main/examples/simulation_tensorflow))
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
@@ -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=eBZocV3uFm1w0H6Vp6DmelojSzIHKlcXHVOZzHowE7c,19483
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=0XryFdCMM_RLiNLCr6evLp-6R7ZjeMmRUROIgzRmtmc,7215
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=Eo3JvAV5XqmyRySNqeiw93YNETmmP5ixEOMeBA6ah4w,769
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=hF45cPElXxcQsh4AoquAyaTrNi0xCrIcKx7xOcV_1XU,1782
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=m5UDo7IgRkMS3yixhzz7DhhAv6VAQMCghglMygSPU_k,1606
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=rTBmbDt3kUN9-j3I-HwZE43Y1oSjVb3DR3R2ntt4f5c,7249
55
- flwr/driver/driver.py,sha256=S9KsRlpf7j3y6jGaAgu4TwM_AyHlZFhJV-HgfwJKxks,3967
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=KlJT_wWVm9xx2xapzC0Kgn1UDnBBem514aaTtzJSlkw,4560
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=KeRcABvbNnTXHdsz-WPMjNcgLRrdZR6MLNTX8bIK83Y,25619
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=v1oDksCIphGoTCMbz68DjaTk7Pw89wjGTJUw4rezV1Q,5756
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=tG_t9_Fpbs-K1uT5B6GeLdrQCVcOXAdtqBGg_f7ZVVw,2603
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=Wu0StMixNy-1fIJ3Bo2HcytoDQv5Xe5dCZcrsMTOBdg,4660
115
- flwr/server/strategy/dpfedavg_fixed.py,sha256=roWpIRhup30P1tEgijsJGBAPqtLIuJZ9etFeukwKpjU,7004
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=BqtHfdRrLW5AhnBiN_aiECWcgIvgFEzku_76D98vMXg,5895
127
- flwr/server/strategy/fedxgb_cyclic.py,sha256=CjlFdy-0lxM4m1O6lYL2498lImPHhxiTkiOvPTXKj24,5423
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.dev20240130.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
143
- flwr_nightly-1.7.0.dev20240130.dist-info/METADATA,sha256=clCdgXSkrZfi3u8GQhP0L2Hf3uuxexRQCfU5WPAWwAk,13584
144
- flwr_nightly-1.7.0.dev20240130.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
145
- flwr_nightly-1.7.0.dev20240130.dist-info/entry_points.txt,sha256=1uLlD5tIunkzALMfMWnqjdE_D5hRUX_I1iMmOMv6tZI,181
146
- flwr_nightly-1.7.0.dev20240130.dist-info/RECORD,,
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
- """