flwr-nightly 1.8.0.dev20240315__py3-none-any.whl → 1.15.0.dev20250115__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (312) hide show
  1. flwr/cli/app.py +16 -2
  2. flwr/cli/build.py +181 -0
  3. flwr/cli/cli_user_auth_interceptor.py +90 -0
  4. flwr/cli/config_utils.py +343 -0
  5. flwr/cli/example.py +4 -1
  6. flwr/cli/install.py +253 -0
  7. flwr/cli/log.py +182 -0
  8. flwr/{server/superlink/state → cli/login}/__init__.py +4 -10
  9. flwr/cli/login/login.py +88 -0
  10. flwr/cli/ls.py +327 -0
  11. flwr/cli/new/__init__.py +1 -0
  12. flwr/cli/new/new.py +210 -66
  13. flwr/cli/new/templates/app/.gitignore.tpl +163 -0
  14. flwr/cli/new/templates/app/LICENSE.tpl +202 -0
  15. flwr/cli/new/templates/app/README.baseline.md.tpl +127 -0
  16. flwr/cli/new/templates/app/README.flowertune.md.tpl +66 -0
  17. flwr/cli/new/templates/app/README.md.tpl +16 -32
  18. flwr/cli/new/templates/app/code/__init__.baseline.py.tpl +1 -0
  19. flwr/cli/new/templates/app/code/__init__.py.tpl +1 -1
  20. flwr/cli/new/templates/app/code/client.baseline.py.tpl +58 -0
  21. flwr/cli/new/templates/app/code/client.huggingface.py.tpl +55 -0
  22. flwr/cli/new/templates/app/code/client.jax.py.tpl +50 -0
  23. flwr/cli/new/templates/app/code/client.mlx.py.tpl +73 -0
  24. flwr/cli/new/templates/app/code/client.numpy.py.tpl +7 -7
  25. flwr/cli/new/templates/app/code/client.pytorch.py.tpl +30 -21
  26. flwr/cli/new/templates/app/code/client.sklearn.py.tpl +63 -0
  27. flwr/cli/new/templates/app/code/client.tensorflow.py.tpl +57 -1
  28. flwr/cli/new/templates/app/code/dataset.baseline.py.tpl +36 -0
  29. flwr/cli/new/templates/app/code/flwr_tune/__init__.py +15 -0
  30. flwr/cli/new/templates/app/code/flwr_tune/client_app.py.tpl +126 -0
  31. flwr/cli/new/templates/app/code/flwr_tune/dataset.py.tpl +87 -0
  32. flwr/cli/new/templates/app/code/flwr_tune/models.py.tpl +78 -0
  33. flwr/cli/new/templates/app/code/flwr_tune/server_app.py.tpl +94 -0
  34. flwr/cli/new/templates/app/code/flwr_tune/strategy.py.tpl +83 -0
  35. flwr/cli/new/templates/app/code/model.baseline.py.tpl +80 -0
  36. flwr/cli/new/templates/app/code/server.baseline.py.tpl +46 -0
  37. flwr/cli/new/templates/app/code/server.huggingface.py.tpl +38 -0
  38. flwr/cli/new/templates/app/code/server.jax.py.tpl +26 -0
  39. flwr/cli/new/templates/app/code/server.mlx.py.tpl +31 -0
  40. flwr/cli/new/templates/app/code/server.numpy.py.tpl +22 -9
  41. flwr/cli/new/templates/app/code/server.pytorch.py.tpl +21 -18
  42. flwr/cli/new/templates/app/code/server.sklearn.py.tpl +36 -0
  43. flwr/cli/new/templates/app/code/server.tensorflow.py.tpl +29 -1
  44. flwr/cli/new/templates/app/code/strategy.baseline.py.tpl +1 -0
  45. flwr/cli/new/templates/app/code/task.huggingface.py.tpl +102 -0
  46. flwr/cli/new/templates/app/code/task.jax.py.tpl +57 -0
  47. flwr/cli/new/templates/app/code/task.mlx.py.tpl +102 -0
  48. flwr/cli/new/templates/app/code/task.numpy.py.tpl +7 -0
  49. flwr/cli/new/templates/app/code/task.pytorch.py.tpl +29 -24
  50. flwr/cli/new/templates/app/code/task.sklearn.py.tpl +67 -0
  51. flwr/cli/new/templates/app/code/task.tensorflow.py.tpl +53 -0
  52. flwr/cli/new/templates/app/code/utils.baseline.py.tpl +1 -0
  53. flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +138 -0
  54. flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +68 -0
  55. flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +46 -0
  56. flwr/cli/new/templates/app/pyproject.jax.toml.tpl +35 -0
  57. flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +39 -0
  58. flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +25 -12
  59. flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +29 -14
  60. flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +35 -0
  61. flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +29 -14
  62. flwr/cli/run/__init__.py +1 -0
  63. flwr/cli/run/run.py +212 -34
  64. flwr/cli/stop.py +130 -0
  65. flwr/cli/utils.py +240 -5
  66. flwr/client/__init__.py +3 -2
  67. flwr/client/app.py +432 -255
  68. flwr/client/client.py +1 -11
  69. flwr/client/client_app.py +74 -13
  70. flwr/client/clientapp/__init__.py +22 -0
  71. flwr/client/clientapp/app.py +259 -0
  72. flwr/client/clientapp/clientappio_servicer.py +244 -0
  73. flwr/client/clientapp/utils.py +115 -0
  74. flwr/client/dpfedavg_numpy_client.py +7 -8
  75. flwr/client/grpc_adapter_client/__init__.py +15 -0
  76. flwr/client/grpc_adapter_client/connection.py +98 -0
  77. flwr/client/grpc_client/connection.py +21 -7
  78. flwr/client/grpc_rere_client/__init__.py +1 -1
  79. flwr/client/grpc_rere_client/client_interceptor.py +176 -0
  80. flwr/client/grpc_rere_client/connection.py +163 -56
  81. flwr/client/grpc_rere_client/grpc_adapter.py +167 -0
  82. flwr/client/heartbeat.py +74 -0
  83. flwr/client/message_handler/__init__.py +1 -1
  84. flwr/client/message_handler/message_handler.py +10 -11
  85. flwr/client/mod/__init__.py +5 -5
  86. flwr/client/mod/centraldp_mods.py +4 -2
  87. flwr/client/mod/comms_mods.py +5 -4
  88. flwr/client/mod/localdp_mod.py +10 -5
  89. flwr/client/mod/secure_aggregation/__init__.py +1 -1
  90. flwr/client/mod/secure_aggregation/secaggplus_mod.py +26 -26
  91. flwr/client/mod/utils.py +2 -4
  92. flwr/client/nodestate/__init__.py +26 -0
  93. flwr/client/nodestate/in_memory_nodestate.py +38 -0
  94. flwr/client/nodestate/nodestate.py +31 -0
  95. flwr/client/nodestate/nodestate_factory.py +38 -0
  96. flwr/client/numpy_client.py +8 -31
  97. flwr/client/rest_client/__init__.py +1 -1
  98. flwr/client/rest_client/connection.py +199 -176
  99. flwr/client/run_info_store.py +112 -0
  100. flwr/client/supernode/__init__.py +24 -0
  101. flwr/client/supernode/app.py +321 -0
  102. flwr/client/typing.py +1 -0
  103. flwr/common/__init__.py +17 -11
  104. flwr/common/address.py +47 -3
  105. flwr/common/args.py +153 -0
  106. flwr/common/auth_plugin/__init__.py +24 -0
  107. flwr/common/auth_plugin/auth_plugin.py +121 -0
  108. flwr/common/config.py +243 -0
  109. flwr/common/constant.py +135 -1
  110. flwr/common/context.py +32 -2
  111. flwr/common/date.py +22 -4
  112. flwr/common/differential_privacy.py +2 -2
  113. flwr/common/dp.py +2 -4
  114. flwr/common/exit_handlers.py +3 -3
  115. flwr/common/grpc.py +164 -5
  116. flwr/common/logger.py +230 -12
  117. flwr/common/message.py +191 -106
  118. flwr/common/object_ref.py +179 -44
  119. flwr/common/pyproject.py +1 -0
  120. flwr/common/record/__init__.py +2 -1
  121. flwr/common/record/configsrecord.py +58 -18
  122. flwr/common/record/metricsrecord.py +57 -17
  123. flwr/common/record/parametersrecord.py +88 -20
  124. flwr/common/record/recordset.py +153 -30
  125. flwr/common/record/typeddict.py +30 -55
  126. flwr/common/recordset_compat.py +31 -12
  127. flwr/common/retry_invoker.py +123 -30
  128. flwr/common/secure_aggregation/__init__.py +1 -1
  129. flwr/common/secure_aggregation/crypto/__init__.py +1 -1
  130. flwr/common/secure_aggregation/crypto/shamir.py +11 -11
  131. flwr/common/secure_aggregation/crypto/symmetric_encryption.py +68 -4
  132. flwr/common/secure_aggregation/ndarrays_arithmetic.py +17 -17
  133. flwr/common/secure_aggregation/quantization.py +8 -8
  134. flwr/common/secure_aggregation/secaggplus_constants.py +1 -1
  135. flwr/common/secure_aggregation/secaggplus_utils.py +10 -12
  136. flwr/common/serde.py +304 -23
  137. flwr/common/telemetry.py +65 -29
  138. flwr/common/typing.py +120 -19
  139. flwr/common/version.py +17 -3
  140. flwr/proto/clientappio_pb2.py +45 -0
  141. flwr/proto/clientappio_pb2.pyi +132 -0
  142. flwr/proto/clientappio_pb2_grpc.py +135 -0
  143. flwr/proto/clientappio_pb2_grpc.pyi +53 -0
  144. flwr/proto/exec_pb2.py +62 -0
  145. flwr/proto/exec_pb2.pyi +212 -0
  146. flwr/proto/exec_pb2_grpc.py +237 -0
  147. flwr/proto/exec_pb2_grpc.pyi +93 -0
  148. flwr/proto/fab_pb2.py +31 -0
  149. flwr/proto/fab_pb2.pyi +65 -0
  150. flwr/proto/fab_pb2_grpc.py +4 -0
  151. flwr/proto/fab_pb2_grpc.pyi +4 -0
  152. flwr/proto/fleet_pb2.py +42 -23
  153. flwr/proto/fleet_pb2.pyi +123 -1
  154. flwr/proto/fleet_pb2_grpc.py +170 -0
  155. flwr/proto/fleet_pb2_grpc.pyi +61 -0
  156. flwr/proto/grpcadapter_pb2.py +32 -0
  157. flwr/proto/grpcadapter_pb2.pyi +43 -0
  158. flwr/proto/grpcadapter_pb2_grpc.py +66 -0
  159. flwr/proto/grpcadapter_pb2_grpc.pyi +24 -0
  160. flwr/proto/log_pb2.py +29 -0
  161. flwr/proto/log_pb2.pyi +39 -0
  162. flwr/proto/log_pb2_grpc.py +4 -0
  163. flwr/proto/log_pb2_grpc.pyi +4 -0
  164. flwr/proto/message_pb2.py +41 -0
  165. flwr/proto/message_pb2.pyi +128 -0
  166. flwr/proto/message_pb2_grpc.py +4 -0
  167. flwr/proto/message_pb2_grpc.pyi +4 -0
  168. flwr/proto/node_pb2.py +2 -2
  169. flwr/proto/node_pb2.pyi +1 -4
  170. flwr/proto/recordset_pb2.py +35 -33
  171. flwr/proto/recordset_pb2.pyi +40 -14
  172. flwr/proto/run_pb2.py +64 -0
  173. flwr/proto/run_pb2.pyi +268 -0
  174. flwr/proto/run_pb2_grpc.py +4 -0
  175. flwr/proto/run_pb2_grpc.pyi +4 -0
  176. flwr/proto/serverappio_pb2.py +52 -0
  177. flwr/proto/{driver_pb2.pyi → serverappio_pb2.pyi} +62 -20
  178. flwr/proto/serverappio_pb2_grpc.py +410 -0
  179. flwr/proto/serverappio_pb2_grpc.pyi +160 -0
  180. flwr/proto/simulationio_pb2.py +38 -0
  181. flwr/proto/simulationio_pb2.pyi +65 -0
  182. flwr/proto/simulationio_pb2_grpc.py +239 -0
  183. flwr/proto/simulationio_pb2_grpc.pyi +94 -0
  184. flwr/proto/task_pb2.py +7 -8
  185. flwr/proto/task_pb2.pyi +8 -5
  186. flwr/proto/transport_pb2.py +8 -8
  187. flwr/proto/transport_pb2.pyi +9 -6
  188. flwr/server/__init__.py +2 -10
  189. flwr/server/app.py +579 -402
  190. flwr/server/client_manager.py +8 -6
  191. flwr/server/compat/app.py +6 -62
  192. flwr/server/compat/app_utils.py +14 -9
  193. flwr/server/compat/driver_client_proxy.py +25 -59
  194. flwr/server/compat/legacy_context.py +5 -4
  195. flwr/server/driver/__init__.py +2 -0
  196. flwr/server/driver/driver.py +36 -131
  197. flwr/server/driver/grpc_driver.py +220 -81
  198. flwr/server/driver/inmemory_driver.py +183 -0
  199. flwr/server/history.py +28 -29
  200. flwr/server/run_serverapp.py +15 -126
  201. flwr/server/server.py +50 -44
  202. flwr/server/server_app.py +59 -10
  203. flwr/server/serverapp/__init__.py +22 -0
  204. flwr/server/serverapp/app.py +256 -0
  205. flwr/server/serverapp_components.py +52 -0
  206. flwr/server/strategy/__init__.py +2 -2
  207. flwr/server/strategy/aggregate.py +37 -23
  208. flwr/server/strategy/bulyan.py +9 -9
  209. flwr/server/strategy/dp_adaptive_clipping.py +25 -25
  210. flwr/server/strategy/dp_fixed_clipping.py +23 -22
  211. flwr/server/strategy/dpfedavg_adaptive.py +8 -8
  212. flwr/server/strategy/dpfedavg_fixed.py +13 -12
  213. flwr/server/strategy/fault_tolerant_fedavg.py +11 -11
  214. flwr/server/strategy/fedadagrad.py +9 -9
  215. flwr/server/strategy/fedadam.py +20 -10
  216. flwr/server/strategy/fedavg.py +16 -16
  217. flwr/server/strategy/fedavg_android.py +17 -17
  218. flwr/server/strategy/fedavgm.py +9 -9
  219. flwr/server/strategy/fedmedian.py +5 -5
  220. flwr/server/strategy/fedopt.py +6 -6
  221. flwr/server/strategy/fedprox.py +7 -7
  222. flwr/server/strategy/fedtrimmedavg.py +8 -8
  223. flwr/server/strategy/fedxgb_bagging.py +12 -12
  224. flwr/server/strategy/fedxgb_cyclic.py +10 -10
  225. flwr/server/strategy/fedxgb_nn_avg.py +6 -6
  226. flwr/server/strategy/fedyogi.py +9 -9
  227. flwr/server/strategy/krum.py +9 -9
  228. flwr/server/strategy/qfedavg.py +16 -16
  229. flwr/server/strategy/strategy.py +10 -10
  230. flwr/server/superlink/driver/__init__.py +2 -2
  231. flwr/server/superlink/driver/serverappio_grpc.py +61 -0
  232. flwr/server/superlink/driver/serverappio_servicer.py +361 -0
  233. flwr/server/superlink/ffs/__init__.py +24 -0
  234. flwr/server/superlink/ffs/disk_ffs.py +108 -0
  235. flwr/server/superlink/ffs/ffs.py +79 -0
  236. flwr/server/superlink/ffs/ffs_factory.py +47 -0
  237. flwr/server/superlink/fleet/__init__.py +1 -1
  238. flwr/server/superlink/fleet/grpc_adapter/__init__.py +15 -0
  239. flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +162 -0
  240. flwr/server/superlink/fleet/grpc_bidi/__init__.py +1 -1
  241. flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py +4 -2
  242. flwr/server/superlink/fleet/grpc_bidi/grpc_bridge.py +3 -2
  243. flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py +1 -1
  244. flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +5 -154
  245. flwr/server/superlink/fleet/grpc_rere/__init__.py +1 -1
  246. flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +120 -13
  247. flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +228 -0
  248. flwr/server/superlink/fleet/message_handler/__init__.py +1 -1
  249. flwr/server/superlink/fleet/message_handler/message_handler.py +156 -13
  250. flwr/server/superlink/fleet/rest_rere/__init__.py +1 -1
  251. flwr/server/superlink/fleet/rest_rere/rest_api.py +119 -81
  252. flwr/server/superlink/fleet/vce/__init__.py +1 -0
  253. flwr/server/superlink/fleet/vce/backend/__init__.py +4 -4
  254. flwr/server/superlink/fleet/vce/backend/backend.py +8 -9
  255. flwr/server/superlink/fleet/vce/backend/raybackend.py +87 -68
  256. flwr/server/superlink/fleet/vce/vce_api.py +208 -146
  257. flwr/server/superlink/linkstate/__init__.py +28 -0
  258. flwr/server/superlink/linkstate/in_memory_linkstate.py +569 -0
  259. flwr/server/superlink/linkstate/linkstate.py +376 -0
  260. flwr/server/superlink/{state/state_factory.py → linkstate/linkstate_factory.py} +19 -10
  261. flwr/server/superlink/linkstate/sqlite_linkstate.py +1196 -0
  262. flwr/server/superlink/linkstate/utils.py +399 -0
  263. flwr/server/superlink/simulation/__init__.py +15 -0
  264. flwr/server/superlink/simulation/simulationio_grpc.py +65 -0
  265. flwr/server/superlink/simulation/simulationio_servicer.py +186 -0
  266. flwr/server/superlink/utils.py +65 -0
  267. flwr/server/typing.py +2 -0
  268. flwr/server/utils/__init__.py +1 -1
  269. flwr/server/utils/tensorboard.py +5 -5
  270. flwr/server/utils/validator.py +40 -45
  271. flwr/server/workflow/default_workflows.py +70 -26
  272. flwr/server/workflow/secure_aggregation/secagg_workflow.py +1 -0
  273. flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +40 -27
  274. flwr/simulation/__init__.py +12 -5
  275. flwr/simulation/app.py +247 -315
  276. flwr/simulation/legacy_app.py +404 -0
  277. flwr/simulation/ray_transport/__init__.py +1 -1
  278. flwr/simulation/ray_transport/ray_actor.py +42 -67
  279. flwr/simulation/ray_transport/ray_client_proxy.py +37 -17
  280. flwr/simulation/ray_transport/utils.py +1 -0
  281. flwr/simulation/run_simulation.py +306 -163
  282. flwr/simulation/simulationio_connection.py +89 -0
  283. flwr/superexec/__init__.py +15 -0
  284. flwr/superexec/app.py +59 -0
  285. flwr/superexec/deployment.py +188 -0
  286. flwr/superexec/exec_grpc.py +80 -0
  287. flwr/superexec/exec_servicer.py +231 -0
  288. flwr/superexec/exec_user_auth_interceptor.py +101 -0
  289. flwr/superexec/executor.py +96 -0
  290. flwr/superexec/simulation.py +124 -0
  291. {flwr_nightly-1.8.0.dev20240315.dist-info → flwr_nightly-1.15.0.dev20250115.dist-info}/METADATA +33 -26
  292. flwr_nightly-1.15.0.dev20250115.dist-info/RECORD +328 -0
  293. flwr_nightly-1.15.0.dev20250115.dist-info/entry_points.txt +12 -0
  294. flwr/cli/flower_toml.py +0 -140
  295. flwr/cli/new/templates/app/flower.toml.tpl +0 -13
  296. flwr/cli/new/templates/app/requirements.numpy.txt.tpl +0 -2
  297. flwr/cli/new/templates/app/requirements.pytorch.txt.tpl +0 -4
  298. flwr/cli/new/templates/app/requirements.tensorflow.txt.tpl +0 -4
  299. flwr/client/node_state.py +0 -48
  300. flwr/client/node_state_tests.py +0 -65
  301. flwr/proto/driver_pb2.py +0 -44
  302. flwr/proto/driver_pb2_grpc.py +0 -169
  303. flwr/proto/driver_pb2_grpc.pyi +0 -66
  304. flwr/server/superlink/driver/driver_grpc.py +0 -54
  305. flwr/server/superlink/driver/driver_servicer.py +0 -129
  306. flwr/server/superlink/state/in_memory_state.py +0 -230
  307. flwr/server/superlink/state/sqlite_state.py +0 -630
  308. flwr/server/superlink/state/state.py +0 -154
  309. flwr_nightly-1.8.0.dev20240315.dist-info/RECORD +0 -211
  310. flwr_nightly-1.8.0.dev20240315.dist-info/entry_points.txt +0 -9
  311. {flwr_nightly-1.8.0.dev20240315.dist-info → flwr_nightly-1.15.0.dev20250115.dist-info}/LICENSE +0 -0
  312. {flwr_nightly-1.8.0.dev20240315.dist-info → flwr_nightly-1.15.0.dev20250115.dist-info}/WHEEL +0 -0
@@ -0,0 +1,256 @@
1
+ # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+ """Flower ServerApp process."""
16
+
17
+
18
+ import argparse
19
+ import sys
20
+ from logging import DEBUG, ERROR, INFO
21
+ from pathlib import Path
22
+ from queue import Queue
23
+ from time import sleep
24
+ from typing import Optional
25
+
26
+ from flwr.cli.config_utils import get_fab_metadata
27
+ from flwr.cli.install import install_from_fab
28
+ from flwr.cli.utils import get_sha256_hash
29
+ from flwr.common.args import add_args_flwr_app_common
30
+ from flwr.common.config import (
31
+ get_flwr_dir,
32
+ get_fused_config_from_dir,
33
+ get_project_config,
34
+ get_project_dir,
35
+ )
36
+ from flwr.common.constant import (
37
+ SERVERAPPIO_API_DEFAULT_CLIENT_ADDRESS,
38
+ Status,
39
+ SubStatus,
40
+ )
41
+ from flwr.common.logger import (
42
+ log,
43
+ mirror_output_to_queue,
44
+ restore_output,
45
+ start_log_uploader,
46
+ stop_log_uploader,
47
+ )
48
+ from flwr.common.serde import (
49
+ context_from_proto,
50
+ context_to_proto,
51
+ fab_from_proto,
52
+ run_from_proto,
53
+ run_status_to_proto,
54
+ )
55
+ from flwr.common.telemetry import EventType, event
56
+ from flwr.common.typing import RunNotRunningException, RunStatus
57
+ from flwr.proto.run_pb2 import UpdateRunStatusRequest # pylint: disable=E0611
58
+ from flwr.proto.serverappio_pb2 import ( # pylint: disable=E0611
59
+ PullServerAppInputsRequest,
60
+ PullServerAppInputsResponse,
61
+ PushServerAppOutputsRequest,
62
+ )
63
+ from flwr.server.driver.grpc_driver import GrpcDriver
64
+ from flwr.server.run_serverapp import run as run_
65
+
66
+
67
+ def flwr_serverapp() -> None:
68
+ """Run process-isolated Flower ServerApp."""
69
+ # Capture stdout/stderr
70
+ log_queue: Queue[Optional[str]] = Queue()
71
+ mirror_output_to_queue(log_queue)
72
+
73
+ args = _parse_args_run_flwr_serverapp().parse_args()
74
+
75
+ log(INFO, "Starting Flower ServerApp")
76
+
77
+ if not args.insecure:
78
+ log(
79
+ ERROR,
80
+ "`flwr-serverapp` does not support TLS yet. "
81
+ "Please use the '--insecure' flag.",
82
+ )
83
+ sys.exit(1)
84
+
85
+ log(
86
+ DEBUG,
87
+ "Starting isolated `ServerApp` connected to SuperLink's ServerAppIo API at %s",
88
+ args.serverappio_api_address,
89
+ )
90
+ run_serverapp(
91
+ serverappio_api_address=args.serverappio_api_address,
92
+ log_queue=log_queue,
93
+ run_once=args.run_once,
94
+ flwr_dir=args.flwr_dir,
95
+ certificates=None,
96
+ )
97
+
98
+ # Restore stdout/stderr
99
+ restore_output()
100
+
101
+
102
+ def run_serverapp( # pylint: disable=R0914, disable=W0212, disable=R0915
103
+ serverappio_api_address: str,
104
+ log_queue: Queue[Optional[str]],
105
+ run_once: bool,
106
+ flwr_dir: Optional[str] = None,
107
+ certificates: Optional[bytes] = None,
108
+ ) -> None:
109
+ """Run Flower ServerApp process."""
110
+ driver = GrpcDriver(
111
+ serverappio_service_address=serverappio_api_address,
112
+ root_certificates=certificates,
113
+ )
114
+
115
+ # Resolve directory where FABs are installed
116
+ flwr_dir_ = get_flwr_dir(flwr_dir)
117
+ log_uploader = None
118
+ success = True
119
+ hash_run_id = None
120
+ while True:
121
+
122
+ try:
123
+ # Pull ServerAppInputs from LinkState
124
+ req = PullServerAppInputsRequest()
125
+ res: PullServerAppInputsResponse = driver._stub.PullServerAppInputs(req)
126
+ if not res.HasField("run"):
127
+ sleep(3)
128
+ run_status = None
129
+ continue
130
+
131
+ context = context_from_proto(res.context)
132
+ run = run_from_proto(res.run)
133
+ fab = fab_from_proto(res.fab)
134
+
135
+ hash_run_id = get_sha256_hash(run.run_id)
136
+
137
+ driver.set_run(run.run_id)
138
+
139
+ # Start log uploader for this run
140
+ log_uploader = start_log_uploader(
141
+ log_queue=log_queue,
142
+ node_id=0,
143
+ run_id=run.run_id,
144
+ stub=driver._stub,
145
+ )
146
+
147
+ log(DEBUG, "ServerApp process starts FAB installation.")
148
+ install_from_fab(fab.content, flwr_dir=flwr_dir_, skip_prompt=True)
149
+
150
+ fab_id, fab_version = get_fab_metadata(fab.content)
151
+
152
+ app_path = str(
153
+ get_project_dir(fab_id, fab_version, fab.hash_str, flwr_dir_)
154
+ )
155
+ config = get_project_config(app_path)
156
+
157
+ # Obtain server app reference and the run config
158
+ server_app_attr = config["tool"]["flwr"]["app"]["components"]["serverapp"]
159
+ server_app_run_config = get_fused_config_from_dir(
160
+ Path(app_path), run.override_config
161
+ )
162
+
163
+ # Update run_config in context
164
+ context.run_config = server_app_run_config
165
+
166
+ log(
167
+ DEBUG,
168
+ "Flower will load ServerApp `%s` in %s",
169
+ server_app_attr,
170
+ app_path,
171
+ )
172
+
173
+ # Change status to Running
174
+ run_status_proto = run_status_to_proto(RunStatus(Status.RUNNING, "", ""))
175
+ driver._stub.UpdateRunStatus(
176
+ UpdateRunStatusRequest(run_id=run.run_id, run_status=run_status_proto)
177
+ )
178
+
179
+ event(
180
+ EventType.FLWR_SERVERAPP_RUN_ENTER,
181
+ event_details={"run-id-hash": hash_run_id},
182
+ )
183
+
184
+ # Load and run the ServerApp with the Driver
185
+ updated_context = run_(
186
+ driver=driver,
187
+ server_app_dir=app_path,
188
+ server_app_attr=server_app_attr,
189
+ context=context,
190
+ )
191
+
192
+ # Send resulting context
193
+ context_proto = context_to_proto(updated_context)
194
+ out_req = PushServerAppOutputsRequest(
195
+ run_id=run.run_id, context=context_proto
196
+ )
197
+ _ = driver._stub.PushServerAppOutputs(out_req)
198
+
199
+ run_status = RunStatus(Status.FINISHED, SubStatus.COMPLETED, "")
200
+ except RunNotRunningException:
201
+ log(INFO, "")
202
+ log(INFO, "Run ID %s stopped.", run.run_id)
203
+ log(INFO, "")
204
+ run_status = None
205
+ success = False
206
+
207
+ except Exception as ex: # pylint: disable=broad-exception-caught
208
+ exc_entity = "ServerApp"
209
+ log(ERROR, "%s raised an exception", exc_entity, exc_info=ex)
210
+ run_status = RunStatus(Status.FINISHED, SubStatus.FAILED, str(ex))
211
+ success = False
212
+
213
+ finally:
214
+ # Stop log uploader for this run and upload final logs
215
+ if log_uploader:
216
+ stop_log_uploader(log_queue, log_uploader)
217
+ log_uploader = None
218
+
219
+ # Update run status
220
+ if run_status:
221
+ run_status_proto = run_status_to_proto(run_status)
222
+ driver._stub.UpdateRunStatus(
223
+ UpdateRunStatusRequest(
224
+ run_id=run.run_id, run_status=run_status_proto
225
+ )
226
+ )
227
+ event(
228
+ EventType.FLWR_SERVERAPP_RUN_LEAVE,
229
+ event_details={"run-id-hash": hash_run_id, "success": success},
230
+ )
231
+
232
+ # Stop the loop if `flwr-serverapp` is expected to process a single run
233
+ if run_once:
234
+ break
235
+
236
+
237
+ def _parse_args_run_flwr_serverapp() -> argparse.ArgumentParser:
238
+ """Parse flwr-serverapp command line arguments."""
239
+ parser = argparse.ArgumentParser(
240
+ description="Run a Flower ServerApp",
241
+ )
242
+ parser.add_argument(
243
+ "--serverappio-api-address",
244
+ default=SERVERAPPIO_API_DEFAULT_CLIENT_ADDRESS,
245
+ type=str,
246
+ help="Address of SuperLink's ServerAppIo API (IPv4, IPv6, or a domain name)."
247
+ f"By default, it is set to {SERVERAPPIO_API_DEFAULT_CLIENT_ADDRESS}.",
248
+ )
249
+ parser.add_argument(
250
+ "--run-once",
251
+ action="store_true",
252
+ help="When set, this process will start a single ServerApp for a pending Run. "
253
+ "If there is no pending Run, the process will exit.",
254
+ )
255
+ add_args_flwr_app_common(parser=parser)
256
+ return parser
@@ -0,0 +1,52 @@
1
+ # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+ """ServerAppComponents for the ServerApp."""
16
+
17
+
18
+ from dataclasses import dataclass
19
+ from typing import Optional
20
+
21
+ from .client_manager import ClientManager
22
+ from .server import Server
23
+ from .server_config import ServerConfig
24
+ from .strategy import Strategy
25
+
26
+
27
+ @dataclass
28
+ class ServerAppComponents: # pylint: disable=too-many-instance-attributes
29
+ """Components to construct a ServerApp.
30
+
31
+ Parameters
32
+ ----------
33
+ server : Optional[Server] (default: None)
34
+ A server implementation, either `flwr.server.Server` or a subclass
35
+ thereof. If no instance is provided, one will be created internally.
36
+ config : Optional[ServerConfig] (default: None)
37
+ Currently supported values are `num_rounds` (int, default: 1) and
38
+ `round_timeout` in seconds (float, default: None).
39
+ strategy : Optional[Strategy] (default: None)
40
+ An implementation of the abstract base class
41
+ `flwr.server.strategy.Strategy`. If no strategy is provided, then
42
+ `flwr.server.strategy.FedAvg` will be used.
43
+ client_manager : Optional[ClientManager] (default: None)
44
+ An implementation of the class `flwr.server.ClientManager`. If no
45
+ implementation is provided, then `flwr.server.SimpleClientManager`
46
+ will be used.
47
+ """
48
+
49
+ server: Optional[Server] = None
50
+ config: Optional[ServerConfig] = None
51
+ strategy: Optional[Strategy] = None
52
+ client_manager: Optional[ClientManager] = None
@@ -53,9 +53,10 @@ __all__ = [
53
53
  "DPFedAvgAdaptive",
54
54
  "DPFedAvgFixed",
55
55
  "DifferentialPrivacyClientSideAdaptiveClipping",
56
- "DifferentialPrivacyServerSideAdaptiveClipping",
57
56
  "DifferentialPrivacyClientSideFixedClipping",
57
+ "DifferentialPrivacyServerSideAdaptiveClipping",
58
58
  "DifferentialPrivacyServerSideFixedClipping",
59
+ "FaultTolerantFedAvg",
59
60
  "FedAdagrad",
60
61
  "FedAdam",
61
62
  "FedAvg",
@@ -69,7 +70,6 @@ __all__ = [
69
70
  "FedXgbCyclic",
70
71
  "FedXgbNnAvg",
71
72
  "FedYogi",
72
- "FaultTolerantFedAvg",
73
73
  "Krum",
74
74
  "QFedAvg",
75
75
  "Strategy",
@@ -15,8 +15,8 @@
15
15
  """Aggregation functions for strategy implementations."""
16
16
  # mypy: disallow_untyped_calls=False
17
17
 
18
- from functools import reduce
19
- from typing import Any, Callable, List, Tuple
18
+ from functools import partial, reduce
19
+ from typing import Any, Callable, Union
20
20
 
21
21
  import numpy as np
22
22
 
@@ -24,7 +24,7 @@ from flwr.common import FitRes, NDArray, NDArrays, parameters_to_ndarrays
24
24
  from flwr.server.client_proxy import ClientProxy
25
25
 
26
26
 
27
- def aggregate(results: List[Tuple[NDArrays, int]]) -> NDArrays:
27
+ def aggregate(results: list[tuple[NDArrays, int]]) -> NDArrays:
28
28
  """Compute weighted average."""
29
29
  # Calculate the total number of examples used during training
30
30
  num_examples_total = sum(num_examples for (_, num_examples) in results)
@@ -42,32 +42,46 @@ def aggregate(results: List[Tuple[NDArrays, int]]) -> NDArrays:
42
42
  return weights_prime
43
43
 
44
44
 
45
- def aggregate_inplace(results: List[Tuple[ClientProxy, FitRes]]) -> NDArrays:
45
+ def aggregate_inplace(results: list[tuple[ClientProxy, FitRes]]) -> NDArrays:
46
46
  """Compute in-place weighted average."""
47
47
  # Count total examples
48
48
  num_examples_total = sum(fit_res.num_examples for (_, fit_res) in results)
49
49
 
50
50
  # Compute scaling factors for each result
51
- scaling_factors = [
52
- fit_res.num_examples / num_examples_total for _, fit_res in results
53
- ]
51
+ scaling_factors = np.asarray(
52
+ [fit_res.num_examples / num_examples_total for _, fit_res in results]
53
+ )
54
+
55
+ def _try_inplace(
56
+ x: NDArray, y: Union[NDArray, np.float64], np_binary_op: np.ufunc
57
+ ) -> NDArray:
58
+ return ( # type: ignore[no-any-return]
59
+ np_binary_op(x, y, out=x)
60
+ if np.can_cast(y, x.dtype, casting="same_kind")
61
+ else np_binary_op(x, np.array(y, x.dtype), out=x)
62
+ )
54
63
 
55
64
  # Let's do in-place aggregation
56
65
  # Get first result, then add up each other
57
66
  params = [
58
- scaling_factors[0] * x for x in parameters_to_ndarrays(results[0][1].parameters)
67
+ _try_inplace(x, scaling_factors[0], np_binary_op=np.multiply)
68
+ for x in parameters_to_ndarrays(results[0][1].parameters)
59
69
  ]
60
- for i, (_, fit_res) in enumerate(results[1:]):
70
+
71
+ for i, (_, fit_res) in enumerate(results[1:], start=1):
61
72
  res = (
62
- scaling_factors[i + 1] * x
73
+ _try_inplace(x, scaling_factors[i], np_binary_op=np.multiply)
63
74
  for x in parameters_to_ndarrays(fit_res.parameters)
64
75
  )
65
- params = [reduce(np.add, layer_updates) for layer_updates in zip(params, res)]
76
+ params = [
77
+ reduce(partial(_try_inplace, np_binary_op=np.add), layer_updates)
78
+ for layer_updates in zip(params, res)
79
+ ]
66
80
 
67
81
  return params
68
82
 
69
83
 
70
- def aggregate_median(results: List[Tuple[NDArrays, int]]) -> NDArrays:
84
+ def aggregate_median(results: list[tuple[NDArrays, int]]) -> NDArrays:
71
85
  """Compute median."""
72
86
  # Create a list of weights and ignore the number of examples
73
87
  weights = [weights for weights, _ in results]
@@ -80,7 +94,7 @@ def aggregate_median(results: List[Tuple[NDArrays, int]]) -> NDArrays:
80
94
 
81
95
 
82
96
  def aggregate_krum(
83
- results: List[Tuple[NDArrays, int]], num_malicious: int, to_keep: int
97
+ results: list[tuple[NDArrays, int]], num_malicious: int, to_keep: int
84
98
  ) -> NDArrays:
85
99
  """Choose one parameter vector according to the Krum function.
86
100
 
@@ -119,7 +133,7 @@ def aggregate_krum(
119
133
 
120
134
  # pylint: disable=too-many-locals
121
135
  def aggregate_bulyan(
122
- results: List[Tuple[NDArrays, int]],
136
+ results: list[tuple[NDArrays, int]],
123
137
  num_malicious: int,
124
138
  aggregation_rule: Callable, # type: ignore
125
139
  **aggregation_rule_kwargs: Any,
@@ -128,7 +142,7 @@ def aggregate_bulyan(
128
142
 
129
143
  Parameters
130
144
  ----------
131
- results: List[Tuple[NDArrays, int]]
145
+ results: list[tuple[NDArrays, int]]
132
146
  Weights and number of samples for each of the client.
133
147
  num_malicious: int
134
148
  The maximum number of malicious clients.
@@ -155,7 +169,7 @@ def aggregate_bulyan(
155
169
  "It is needed to ensure that the method reduces the attacker's leeway to "
156
170
  "the one proved in the paper."
157
171
  )
158
- selected_models_set: List[Tuple[NDArrays, int]] = []
172
+ selected_models_set: list[tuple[NDArrays, int]] = []
159
173
 
160
174
  theta = len(results) - 2 * num_malicious
161
175
  beta = theta - 2 * num_malicious
@@ -200,7 +214,7 @@ def aggregate_bulyan(
200
214
  return parameters_aggregated
201
215
 
202
216
 
203
- def weighted_loss_avg(results: List[Tuple[int, float]]) -> float:
217
+ def weighted_loss_avg(results: list[tuple[int, float]]) -> float:
204
218
  """Aggregate evaluation results obtained from multiple clients."""
205
219
  num_total_evaluation_examples = sum(num_examples for (num_examples, _) in results)
206
220
  weighted_losses = [num_examples * loss for num_examples, loss in results]
@@ -208,7 +222,7 @@ def weighted_loss_avg(results: List[Tuple[int, float]]) -> float:
208
222
 
209
223
 
210
224
  def aggregate_qffl(
211
- parameters: NDArrays, deltas: List[NDArrays], hs_fll: List[NDArrays]
225
+ parameters: NDArrays, deltas: list[NDArrays], hs_fll: list[NDArrays]
212
226
  ) -> NDArrays:
213
227
  """Compute weighted average based on Q-FFL paper."""
214
228
  demominator: float = np.sum(np.asarray(hs_fll))
@@ -225,7 +239,7 @@ def aggregate_qffl(
225
239
  return new_parameters
226
240
 
227
241
 
228
- def _compute_distances(weights: List[NDArrays]) -> NDArray:
242
+ def _compute_distances(weights: list[NDArrays]) -> NDArray:
229
243
  """Compute distances between vectors.
230
244
 
231
245
  Input: weights - list of weights vectors
@@ -265,7 +279,7 @@ def _trim_mean(array: NDArray, proportiontocut: float) -> NDArray:
265
279
 
266
280
 
267
281
  def aggregate_trimmed_avg(
268
- results: List[Tuple[NDArrays, int]], proportiontocut: float
282
+ results: list[tuple[NDArrays, int]], proportiontocut: float
269
283
  ) -> NDArrays:
270
284
  """Compute trimmed average."""
271
285
  # Create a list of weights and ignore the number of examples
@@ -290,7 +304,7 @@ def _check_weights_equality(weights1: NDArrays, weights2: NDArrays) -> bool:
290
304
 
291
305
 
292
306
  def _find_reference_weights(
293
- reference_weights: NDArrays, list_of_weights: List[NDArrays]
307
+ reference_weights: NDArrays, list_of_weights: list[NDArrays]
294
308
  ) -> int:
295
309
  """Find the reference weights by looping through the `list_of_weights`.
296
310
 
@@ -320,7 +334,7 @@ def _find_reference_weights(
320
334
 
321
335
 
322
336
  def _aggregate_n_closest_weights(
323
- reference_weights: NDArrays, results: List[Tuple[NDArrays, int]], beta_closest: int
337
+ reference_weights: NDArrays, results: list[tuple[NDArrays, int]], beta_closest: int
324
338
  ) -> NDArrays:
325
339
  """Calculate element-wise mean of the `N` closest values.
326
340
 
@@ -332,7 +346,7 @@ def _aggregate_n_closest_weights(
332
346
  ----------
333
347
  reference_weights: NDArrays
334
348
  The weights from which the distances will be computed
335
- results: List[Tuple[NDArrays, int]]
349
+ results: list[tuple[NDArrays, int]]
336
350
  The weights from models
337
351
  beta_closest: int
338
352
  The number of the closest distance weights that will be averaged
@@ -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.
@@ -19,7 +19,7 @@ Paper: arxiv.org/abs/1802.07927
19
19
 
20
20
 
21
21
  from logging import WARNING
22
- from typing import Any, Callable, Dict, List, Optional, Tuple, Union
22
+ from typing import Any, Callable, Optional, Union
23
23
 
24
24
  from flwr.common import (
25
25
  FitRes,
@@ -86,12 +86,12 @@ class Bulyan(FedAvg):
86
86
  num_malicious_clients: int = 0,
87
87
  evaluate_fn: Optional[
88
88
  Callable[
89
- [int, NDArrays, Dict[str, Scalar]],
90
- Optional[Tuple[float, Dict[str, Scalar]]],
89
+ [int, NDArrays, dict[str, Scalar]],
90
+ Optional[tuple[float, dict[str, Scalar]]],
91
91
  ]
92
92
  ] = None,
93
- on_fit_config_fn: Optional[Callable[[int], Dict[str, Scalar]]] = None,
94
- on_evaluate_config_fn: Optional[Callable[[int], Dict[str, Scalar]]] = None,
93
+ on_fit_config_fn: Optional[Callable[[int], dict[str, Scalar]]] = None,
94
+ on_evaluate_config_fn: Optional[Callable[[int], dict[str, Scalar]]] = None,
95
95
  accept_failures: bool = True,
96
96
  initial_parameters: Optional[Parameters] = None,
97
97
  fit_metrics_aggregation_fn: Optional[MetricsAggregationFn] = None,
@@ -125,9 +125,9 @@ class Bulyan(FedAvg):
125
125
  def aggregate_fit(
126
126
  self,
127
127
  server_round: int,
128
- results: List[Tuple[ClientProxy, FitRes]],
129
- failures: List[Union[Tuple[ClientProxy, FitRes], BaseException]],
130
- ) -> Tuple[Optional[Parameters], Dict[str, Scalar]]:
128
+ results: list[tuple[ClientProxy, FitRes]],
129
+ failures: list[Union[tuple[ClientProxy, FitRes], BaseException]],
130
+ ) -> tuple[Optional[Parameters], dict[str, Scalar]]:
131
131
  """Aggregate fit results using Bulyan."""
132
132
  if not results:
133
133
  return None, {}