flwr-nightly 1.8.0.dev20240314__py3-none-any.whl → 1.15.0.dev20250114__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- flwr/cli/app.py +16 -2
- flwr/cli/build.py +181 -0
- flwr/cli/cli_user_auth_interceptor.py +90 -0
- flwr/cli/config_utils.py +343 -0
- flwr/cli/example.py +4 -1
- flwr/cli/install.py +253 -0
- flwr/cli/log.py +182 -0
- flwr/{server/superlink/state → cli/login}/__init__.py +4 -10
- flwr/cli/login/login.py +88 -0
- flwr/cli/ls.py +327 -0
- flwr/cli/new/__init__.py +1 -0
- flwr/cli/new/new.py +210 -66
- flwr/cli/new/templates/app/.gitignore.tpl +163 -0
- flwr/cli/new/templates/app/LICENSE.tpl +202 -0
- flwr/cli/new/templates/app/README.baseline.md.tpl +127 -0
- flwr/cli/new/templates/app/README.flowertune.md.tpl +66 -0
- flwr/cli/new/templates/app/README.md.tpl +16 -32
- flwr/cli/new/templates/app/code/__init__.baseline.py.tpl +1 -0
- flwr/cli/new/templates/app/code/__init__.py.tpl +1 -1
- flwr/cli/new/templates/app/code/client.baseline.py.tpl +58 -0
- flwr/cli/new/templates/app/code/client.huggingface.py.tpl +55 -0
- flwr/cli/new/templates/app/code/client.jax.py.tpl +50 -0
- flwr/cli/new/templates/app/code/client.mlx.py.tpl +73 -0
- flwr/cli/new/templates/app/code/client.numpy.py.tpl +7 -7
- flwr/cli/new/templates/app/code/client.pytorch.py.tpl +30 -21
- flwr/cli/new/templates/app/code/client.sklearn.py.tpl +63 -0
- flwr/cli/new/templates/app/code/client.tensorflow.py.tpl +57 -1
- flwr/cli/new/templates/app/code/dataset.baseline.py.tpl +36 -0
- flwr/cli/new/templates/app/code/flwr_tune/__init__.py +15 -0
- flwr/cli/new/templates/app/code/flwr_tune/client_app.py.tpl +126 -0
- flwr/cli/new/templates/app/code/flwr_tune/dataset.py.tpl +87 -0
- flwr/cli/new/templates/app/code/flwr_tune/models.py.tpl +78 -0
- flwr/cli/new/templates/app/code/flwr_tune/server_app.py.tpl +94 -0
- flwr/cli/new/templates/app/code/flwr_tune/strategy.py.tpl +83 -0
- flwr/cli/new/templates/app/code/model.baseline.py.tpl +80 -0
- flwr/cli/new/templates/app/code/server.baseline.py.tpl +46 -0
- flwr/cli/new/templates/app/code/server.huggingface.py.tpl +38 -0
- flwr/cli/new/templates/app/code/server.jax.py.tpl +26 -0
- flwr/cli/new/templates/app/code/server.mlx.py.tpl +31 -0
- flwr/cli/new/templates/app/code/server.numpy.py.tpl +22 -9
- flwr/cli/new/templates/app/code/server.pytorch.py.tpl +21 -18
- flwr/cli/new/templates/app/code/server.sklearn.py.tpl +36 -0
- flwr/cli/new/templates/app/code/server.tensorflow.py.tpl +29 -1
- flwr/cli/new/templates/app/code/strategy.baseline.py.tpl +1 -0
- flwr/cli/new/templates/app/code/task.huggingface.py.tpl +102 -0
- flwr/cli/new/templates/app/code/task.jax.py.tpl +57 -0
- flwr/cli/new/templates/app/code/task.mlx.py.tpl +102 -0
- flwr/cli/new/templates/app/code/task.numpy.py.tpl +7 -0
- flwr/cli/new/templates/app/code/task.pytorch.py.tpl +29 -24
- flwr/cli/new/templates/app/code/task.sklearn.py.tpl +67 -0
- flwr/cli/new/templates/app/code/task.tensorflow.py.tpl +53 -0
- flwr/cli/new/templates/app/code/utils.baseline.py.tpl +1 -0
- flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +138 -0
- flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +68 -0
- flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +46 -0
- flwr/cli/new/templates/app/pyproject.jax.toml.tpl +35 -0
- flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +39 -0
- flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +25 -12
- flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +29 -14
- flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +35 -0
- flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +29 -14
- flwr/cli/run/__init__.py +1 -0
- flwr/cli/run/run.py +212 -34
- flwr/cli/stop.py +130 -0
- flwr/cli/utils.py +240 -5
- flwr/client/__init__.py +3 -2
- flwr/client/app.py +432 -255
- flwr/client/client.py +1 -11
- flwr/client/client_app.py +74 -13
- flwr/client/clientapp/__init__.py +22 -0
- flwr/client/clientapp/app.py +259 -0
- flwr/client/clientapp/clientappio_servicer.py +244 -0
- flwr/client/clientapp/utils.py +115 -0
- flwr/client/dpfedavg_numpy_client.py +7 -8
- flwr/client/grpc_adapter_client/__init__.py +15 -0
- flwr/client/grpc_adapter_client/connection.py +98 -0
- flwr/client/grpc_client/connection.py +21 -7
- flwr/client/grpc_rere_client/__init__.py +1 -1
- flwr/client/grpc_rere_client/client_interceptor.py +176 -0
- flwr/client/grpc_rere_client/connection.py +163 -56
- flwr/client/grpc_rere_client/grpc_adapter.py +167 -0
- flwr/client/heartbeat.py +74 -0
- flwr/client/message_handler/__init__.py +1 -1
- flwr/client/message_handler/message_handler.py +10 -11
- flwr/client/mod/__init__.py +5 -5
- flwr/client/mod/centraldp_mods.py +4 -2
- flwr/client/mod/comms_mods.py +5 -4
- flwr/client/mod/localdp_mod.py +10 -5
- flwr/client/mod/secure_aggregation/__init__.py +1 -1
- flwr/client/mod/secure_aggregation/secaggplus_mod.py +26 -26
- flwr/client/mod/utils.py +2 -4
- flwr/client/nodestate/__init__.py +26 -0
- flwr/client/nodestate/in_memory_nodestate.py +38 -0
- flwr/client/nodestate/nodestate.py +31 -0
- flwr/client/nodestate/nodestate_factory.py +38 -0
- flwr/client/numpy_client.py +8 -31
- flwr/client/rest_client/__init__.py +1 -1
- flwr/client/rest_client/connection.py +199 -176
- flwr/client/run_info_store.py +112 -0
- flwr/client/supernode/__init__.py +24 -0
- flwr/client/supernode/app.py +321 -0
- flwr/client/typing.py +1 -0
- flwr/common/__init__.py +17 -11
- flwr/common/address.py +47 -3
- flwr/common/args.py +153 -0
- flwr/common/auth_plugin/__init__.py +24 -0
- flwr/common/auth_plugin/auth_plugin.py +121 -0
- flwr/common/config.py +243 -0
- flwr/common/constant.py +132 -1
- flwr/common/context.py +32 -2
- flwr/common/date.py +22 -4
- flwr/common/differential_privacy.py +2 -2
- flwr/common/dp.py +2 -4
- flwr/common/exit_handlers.py +3 -3
- flwr/common/grpc.py +164 -5
- flwr/common/logger.py +230 -12
- flwr/common/message.py +191 -106
- flwr/common/object_ref.py +179 -44
- flwr/common/pyproject.py +1 -0
- flwr/common/record/__init__.py +2 -1
- flwr/common/record/configsrecord.py +58 -18
- flwr/common/record/metricsrecord.py +57 -17
- flwr/common/record/parametersrecord.py +88 -20
- flwr/common/record/recordset.py +153 -30
- flwr/common/record/typeddict.py +30 -55
- flwr/common/recordset_compat.py +31 -12
- flwr/common/retry_invoker.py +123 -30
- flwr/common/secure_aggregation/__init__.py +1 -1
- flwr/common/secure_aggregation/crypto/__init__.py +1 -1
- flwr/common/secure_aggregation/crypto/shamir.py +11 -11
- flwr/common/secure_aggregation/crypto/symmetric_encryption.py +68 -4
- flwr/common/secure_aggregation/ndarrays_arithmetic.py +17 -17
- flwr/common/secure_aggregation/quantization.py +8 -8
- flwr/common/secure_aggregation/secaggplus_constants.py +1 -1
- flwr/common/secure_aggregation/secaggplus_utils.py +10 -12
- flwr/common/serde.py +298 -19
- flwr/common/telemetry.py +65 -29
- flwr/common/typing.py +120 -19
- flwr/common/version.py +17 -3
- flwr/proto/clientappio_pb2.py +45 -0
- flwr/proto/clientappio_pb2.pyi +132 -0
- flwr/proto/clientappio_pb2_grpc.py +135 -0
- flwr/proto/clientappio_pb2_grpc.pyi +53 -0
- flwr/proto/exec_pb2.py +62 -0
- flwr/proto/exec_pb2.pyi +212 -0
- flwr/proto/exec_pb2_grpc.py +237 -0
- flwr/proto/exec_pb2_grpc.pyi +93 -0
- flwr/proto/fab_pb2.py +31 -0
- flwr/proto/fab_pb2.pyi +65 -0
- flwr/proto/fab_pb2_grpc.py +4 -0
- flwr/proto/fab_pb2_grpc.pyi +4 -0
- flwr/proto/fleet_pb2.py +42 -23
- flwr/proto/fleet_pb2.pyi +123 -1
- flwr/proto/fleet_pb2_grpc.py +170 -0
- flwr/proto/fleet_pb2_grpc.pyi +61 -0
- flwr/proto/grpcadapter_pb2.py +32 -0
- flwr/proto/grpcadapter_pb2.pyi +43 -0
- flwr/proto/grpcadapter_pb2_grpc.py +66 -0
- flwr/proto/grpcadapter_pb2_grpc.pyi +24 -0
- flwr/proto/log_pb2.py +29 -0
- flwr/proto/log_pb2.pyi +39 -0
- flwr/proto/log_pb2_grpc.py +4 -0
- flwr/proto/log_pb2_grpc.pyi +4 -0
- flwr/proto/message_pb2.py +41 -0
- flwr/proto/message_pb2.pyi +128 -0
- flwr/proto/message_pb2_grpc.py +4 -0
- flwr/proto/message_pb2_grpc.pyi +4 -0
- flwr/proto/node_pb2.py +1 -1
- flwr/proto/recordset_pb2.py +35 -33
- flwr/proto/recordset_pb2.pyi +40 -14
- flwr/proto/run_pb2.py +64 -0
- flwr/proto/run_pb2.pyi +268 -0
- flwr/proto/run_pb2_grpc.py +4 -0
- flwr/proto/run_pb2_grpc.pyi +4 -0
- flwr/proto/serverappio_pb2.py +52 -0
- flwr/proto/{driver_pb2.pyi → serverappio_pb2.pyi} +62 -20
- flwr/proto/serverappio_pb2_grpc.py +410 -0
- flwr/proto/serverappio_pb2_grpc.pyi +160 -0
- flwr/proto/simulationio_pb2.py +38 -0
- flwr/proto/simulationio_pb2.pyi +65 -0
- flwr/proto/simulationio_pb2_grpc.py +239 -0
- flwr/proto/simulationio_pb2_grpc.pyi +94 -0
- flwr/proto/task_pb2.py +7 -8
- flwr/proto/task_pb2.pyi +8 -5
- flwr/proto/transport_pb2.py +8 -8
- flwr/proto/transport_pb2.pyi +9 -6
- flwr/server/__init__.py +2 -10
- flwr/server/app.py +579 -402
- flwr/server/client_manager.py +8 -6
- flwr/server/compat/app.py +6 -62
- flwr/server/compat/app_utils.py +14 -8
- flwr/server/compat/driver_client_proxy.py +25 -58
- flwr/server/compat/legacy_context.py +5 -4
- flwr/server/driver/__init__.py +2 -0
- flwr/server/driver/driver.py +36 -131
- flwr/server/driver/grpc_driver.py +217 -81
- flwr/server/driver/inmemory_driver.py +182 -0
- flwr/server/history.py +28 -29
- flwr/server/run_serverapp.py +15 -126
- flwr/server/server.py +50 -44
- flwr/server/server_app.py +59 -10
- flwr/server/serverapp/__init__.py +22 -0
- flwr/server/serverapp/app.py +256 -0
- flwr/server/serverapp_components.py +52 -0
- flwr/server/strategy/__init__.py +2 -2
- flwr/server/strategy/aggregate.py +37 -23
- flwr/server/strategy/bulyan.py +9 -9
- flwr/server/strategy/dp_adaptive_clipping.py +25 -25
- flwr/server/strategy/dp_fixed_clipping.py +23 -22
- flwr/server/strategy/dpfedavg_adaptive.py +8 -8
- flwr/server/strategy/dpfedavg_fixed.py +13 -12
- flwr/server/strategy/fault_tolerant_fedavg.py +11 -11
- flwr/server/strategy/fedadagrad.py +9 -9
- flwr/server/strategy/fedadam.py +20 -10
- flwr/server/strategy/fedavg.py +16 -16
- flwr/server/strategy/fedavg_android.py +17 -17
- flwr/server/strategy/fedavgm.py +9 -9
- flwr/server/strategy/fedmedian.py +5 -5
- flwr/server/strategy/fedopt.py +6 -6
- flwr/server/strategy/fedprox.py +7 -7
- flwr/server/strategy/fedtrimmedavg.py +8 -8
- flwr/server/strategy/fedxgb_bagging.py +12 -12
- flwr/server/strategy/fedxgb_cyclic.py +10 -10
- flwr/server/strategy/fedxgb_nn_avg.py +6 -6
- flwr/server/strategy/fedyogi.py +9 -9
- flwr/server/strategy/krum.py +9 -9
- flwr/server/strategy/qfedavg.py +16 -16
- flwr/server/strategy/strategy.py +10 -10
- flwr/server/superlink/driver/__init__.py +2 -2
- flwr/server/superlink/driver/serverappio_grpc.py +61 -0
- flwr/server/superlink/driver/serverappio_servicer.py +363 -0
- flwr/server/superlink/ffs/__init__.py +24 -0
- flwr/server/superlink/ffs/disk_ffs.py +108 -0
- flwr/server/superlink/ffs/ffs.py +79 -0
- flwr/server/superlink/ffs/ffs_factory.py +47 -0
- flwr/server/superlink/fleet/__init__.py +1 -1
- flwr/server/superlink/fleet/grpc_adapter/__init__.py +15 -0
- flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +162 -0
- flwr/server/superlink/fleet/grpc_bidi/__init__.py +1 -1
- flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py +4 -2
- flwr/server/superlink/fleet/grpc_bidi/grpc_bridge.py +3 -2
- flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py +1 -1
- flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +5 -154
- flwr/server/superlink/fleet/grpc_rere/__init__.py +1 -1
- flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +120 -13
- flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +228 -0
- flwr/server/superlink/fleet/message_handler/__init__.py +1 -1
- flwr/server/superlink/fleet/message_handler/message_handler.py +153 -9
- flwr/server/superlink/fleet/rest_rere/__init__.py +1 -1
- flwr/server/superlink/fleet/rest_rere/rest_api.py +119 -81
- flwr/server/superlink/fleet/vce/__init__.py +1 -0
- flwr/server/superlink/fleet/vce/backend/__init__.py +4 -4
- flwr/server/superlink/fleet/vce/backend/backend.py +8 -9
- flwr/server/superlink/fleet/vce/backend/raybackend.py +87 -68
- flwr/server/superlink/fleet/vce/vce_api.py +208 -146
- flwr/server/superlink/linkstate/__init__.py +28 -0
- flwr/server/superlink/linkstate/in_memory_linkstate.py +581 -0
- flwr/server/superlink/linkstate/linkstate.py +389 -0
- flwr/server/superlink/{state/state_factory.py → linkstate/linkstate_factory.py} +19 -10
- flwr/server/superlink/linkstate/sqlite_linkstate.py +1236 -0
- flwr/server/superlink/linkstate/utils.py +389 -0
- flwr/server/superlink/simulation/__init__.py +15 -0
- flwr/server/superlink/simulation/simulationio_grpc.py +65 -0
- flwr/server/superlink/simulation/simulationio_servicer.py +186 -0
- flwr/server/superlink/utils.py +65 -0
- flwr/server/typing.py +2 -0
- flwr/server/utils/__init__.py +1 -1
- flwr/server/utils/tensorboard.py +5 -5
- flwr/server/utils/validator.py +31 -11
- flwr/server/workflow/default_workflows.py +70 -26
- flwr/server/workflow/secure_aggregation/secagg_workflow.py +1 -0
- flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +40 -27
- flwr/simulation/__init__.py +12 -5
- flwr/simulation/app.py +247 -315
- flwr/simulation/legacy_app.py +402 -0
- flwr/simulation/ray_transport/__init__.py +1 -1
- flwr/simulation/ray_transport/ray_actor.py +42 -67
- flwr/simulation/ray_transport/ray_client_proxy.py +37 -17
- flwr/simulation/ray_transport/utils.py +1 -0
- flwr/simulation/run_simulation.py +306 -163
- flwr/simulation/simulationio_connection.py +89 -0
- flwr/superexec/__init__.py +15 -0
- flwr/superexec/app.py +59 -0
- flwr/superexec/deployment.py +188 -0
- flwr/superexec/exec_grpc.py +80 -0
- flwr/superexec/exec_servicer.py +231 -0
- flwr/superexec/exec_user_auth_interceptor.py +101 -0
- flwr/superexec/executor.py +96 -0
- flwr/superexec/simulation.py +124 -0
- {flwr_nightly-1.8.0.dev20240314.dist-info → flwr_nightly-1.15.0.dev20250114.dist-info}/METADATA +33 -26
- flwr_nightly-1.15.0.dev20250114.dist-info/RECORD +328 -0
- flwr_nightly-1.15.0.dev20250114.dist-info/entry_points.txt +12 -0
- flwr/cli/flower_toml.py +0 -140
- flwr/cli/new/templates/app/flower.toml.tpl +0 -13
- flwr/cli/new/templates/app/requirements.numpy.txt.tpl +0 -2
- flwr/cli/new/templates/app/requirements.pytorch.txt.tpl +0 -4
- flwr/cli/new/templates/app/requirements.tensorflow.txt.tpl +0 -4
- flwr/client/node_state.py +0 -48
- flwr/client/node_state_tests.py +0 -65
- flwr/proto/driver_pb2.py +0 -44
- flwr/proto/driver_pb2_grpc.py +0 -169
- flwr/proto/driver_pb2_grpc.pyi +0 -66
- flwr/server/superlink/driver/driver_grpc.py +0 -54
- flwr/server/superlink/driver/driver_servicer.py +0 -129
- flwr/server/superlink/state/in_memory_state.py +0 -230
- flwr/server/superlink/state/sqlite_state.py +0 -630
- flwr/server/superlink/state/state.py +0 -154
- flwr_nightly-1.8.0.dev20240314.dist-info/RECORD +0 -211
- flwr_nightly-1.8.0.dev20240314.dist-info/entry_points.txt +0 -9
- {flwr_nightly-1.8.0.dev20240314.dist-info → flwr_nightly-1.15.0.dev20250114.dist-info}/LICENSE +0 -0
- {flwr_nightly-1.8.0.dev20240314.dist-info → flwr_nightly-1.15.0.dev20250114.dist-info}/WHEEL +0 -0
flwr/common/record/recordset.py
CHANGED
@@ -15,65 +15,188 @@
|
|
15
15
|
"""RecordSet."""
|
16
16
|
|
17
17
|
|
18
|
+
from __future__ import annotations
|
19
|
+
|
18
20
|
from dataclasses import dataclass
|
19
|
-
from typing import
|
21
|
+
from typing import cast
|
20
22
|
|
21
23
|
from .configsrecord import ConfigsRecord
|
22
24
|
from .metricsrecord import MetricsRecord
|
23
25
|
from .parametersrecord import ParametersRecord
|
24
26
|
from .typeddict import TypedDict
|
25
27
|
|
26
|
-
T = TypeVar("T")
|
27
|
-
|
28
28
|
|
29
29
|
@dataclass
|
30
|
-
class
|
31
|
-
"""
|
30
|
+
class RecordSetData:
|
31
|
+
"""Inner data container for the RecordSet class."""
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
|
33
|
+
parameters_records: TypedDict[str, ParametersRecord]
|
34
|
+
metrics_records: TypedDict[str, MetricsRecord]
|
35
|
+
configs_records: TypedDict[str, ConfigsRecord]
|
36
36
|
|
37
37
|
def __init__(
|
38
38
|
self,
|
39
|
-
parameters_records:
|
40
|
-
metrics_records:
|
41
|
-
configs_records:
|
39
|
+
parameters_records: dict[str, ParametersRecord] | None = None,
|
40
|
+
metrics_records: dict[str, MetricsRecord] | None = None,
|
41
|
+
configs_records: dict[str, ConfigsRecord] | None = None,
|
42
42
|
) -> None:
|
43
|
-
|
44
|
-
|
45
|
-
if not isinstance(__v, __t):
|
46
|
-
raise TypeError(f"Expected `{__t}`, but `{type(__v)}` was passed.")
|
47
|
-
|
48
|
-
return _check_fn
|
49
|
-
|
50
|
-
self._parameters_records = TypedDict[str, ParametersRecord](
|
51
|
-
_get_check_fn(str), _get_check_fn(ParametersRecord)
|
43
|
+
self.parameters_records = TypedDict[str, ParametersRecord](
|
44
|
+
self._check_fn_str, self._check_fn_params
|
52
45
|
)
|
53
|
-
self.
|
54
|
-
|
46
|
+
self.metrics_records = TypedDict[str, MetricsRecord](
|
47
|
+
self._check_fn_str, self._check_fn_metrics
|
55
48
|
)
|
56
|
-
self.
|
57
|
-
|
49
|
+
self.configs_records = TypedDict[str, ConfigsRecord](
|
50
|
+
self._check_fn_str, self._check_fn_configs
|
58
51
|
)
|
59
52
|
if parameters_records is not None:
|
60
|
-
self.
|
53
|
+
self.parameters_records.update(parameters_records)
|
61
54
|
if metrics_records is not None:
|
62
|
-
self.
|
55
|
+
self.metrics_records.update(metrics_records)
|
63
56
|
if configs_records is not None:
|
64
|
-
self.
|
57
|
+
self.configs_records.update(configs_records)
|
58
|
+
|
59
|
+
def _check_fn_str(self, key: str) -> None:
|
60
|
+
if not isinstance(key, str):
|
61
|
+
raise TypeError(
|
62
|
+
f"Expected `{str.__name__}`, but "
|
63
|
+
f"received `{type(key).__name__}` for the key."
|
64
|
+
)
|
65
|
+
|
66
|
+
def _check_fn_params(self, record: ParametersRecord) -> None:
|
67
|
+
if not isinstance(record, ParametersRecord):
|
68
|
+
raise TypeError(
|
69
|
+
f"Expected `{ParametersRecord.__name__}`, but "
|
70
|
+
f"received `{type(record).__name__}` for the value."
|
71
|
+
)
|
72
|
+
|
73
|
+
def _check_fn_metrics(self, record: MetricsRecord) -> None:
|
74
|
+
if not isinstance(record, MetricsRecord):
|
75
|
+
raise TypeError(
|
76
|
+
f"Expected `{MetricsRecord.__name__}`, but "
|
77
|
+
f"received `{type(record).__name__}` for the value."
|
78
|
+
)
|
79
|
+
|
80
|
+
def _check_fn_configs(self, record: ConfigsRecord) -> None:
|
81
|
+
if not isinstance(record, ConfigsRecord):
|
82
|
+
raise TypeError(
|
83
|
+
f"Expected `{ConfigsRecord.__name__}`, but "
|
84
|
+
f"received `{type(record).__name__}` for the value."
|
85
|
+
)
|
86
|
+
|
87
|
+
|
88
|
+
class RecordSet:
|
89
|
+
"""RecordSet stores groups of parameters, metrics and configs.
|
90
|
+
|
91
|
+
A :code:`RecordSet` is the unified mechanism by which parameters,
|
92
|
+
metrics and configs can be either stored as part of a
|
93
|
+
`flwr.common.Context <flwr.common.Context.html>`_ in your apps
|
94
|
+
or communicated as part of a
|
95
|
+
`flwr.common.Message <flwr.common.Message.html>`_ between your apps.
|
96
|
+
|
97
|
+
Parameters
|
98
|
+
----------
|
99
|
+
parameters_records : Optional[Dict[str, ParametersRecord]]
|
100
|
+
A dictionary of :code:`ParametersRecords` that can be used to record
|
101
|
+
and communicate model parameters and high-dimensional arrays.
|
102
|
+
metrics_records : Optional[Dict[str, MetricsRecord]]
|
103
|
+
A dictionary of :code:`MetricsRecord` that can be used to record
|
104
|
+
and communicate scalar-valued metrics that are the result of performing
|
105
|
+
and action, for example, by a :code:`ClientApp`.
|
106
|
+
configs_records : Optional[Dict[str, ConfigsRecord]]
|
107
|
+
A dictionary of :code:`ConfigsRecord` that can be used to record
|
108
|
+
and communicate configuration values to an entity (e.g. to a
|
109
|
+
:code:`ClientApp`)
|
110
|
+
for it to adjust how an action is performed.
|
111
|
+
|
112
|
+
Examples
|
113
|
+
--------
|
114
|
+
A :code:`RecordSet` can hold three types of records, each designed
|
115
|
+
with an specific purpose. What is common to all of them is that they
|
116
|
+
are Python dictionaries designed to ensure that each key-value pair
|
117
|
+
adheres to specified data types.
|
118
|
+
|
119
|
+
Let's see an example.
|
120
|
+
|
121
|
+
>>> from flwr.common import RecordSet
|
122
|
+
>>> from flwr.common import ConfigsRecord, MetricsRecord, ParametersRecord
|
123
|
+
>>>
|
124
|
+
>>> # Let's begin with an empty record
|
125
|
+
>>> my_recordset = RecordSet()
|
126
|
+
>>>
|
127
|
+
>>> # We can create a ConfigsRecord
|
128
|
+
>>> c_record = ConfigsRecord({"lr": 0.1, "batch-size": 128})
|
129
|
+
>>> # Adding it to the record_set would look like this
|
130
|
+
>>> my_recordset.configs_records["my_config"] = c_record
|
131
|
+
>>>
|
132
|
+
>>> # We can create a MetricsRecord following a similar process
|
133
|
+
>>> m_record = MetricsRecord({"accuracy": 0.93, "losses": [0.23, 0.1]})
|
134
|
+
>>> # Adding it to the record_set would look like this
|
135
|
+
>>> my_recordset.metrics_records["my_metrics"] = m_record
|
136
|
+
|
137
|
+
Adding a :code:`ParametersRecord` follows the same steps as above but first,
|
138
|
+
the array needs to be serialized and represented as a :code:`flwr.common.Array`.
|
139
|
+
If the array is a :code:`NumPy` array, you can use the built-in utility function
|
140
|
+
`array_from_numpy <flwr.common.array_from_numpy.html>`_. It is often possible to
|
141
|
+
convert an array first to :code:`NumPy` and then use the aforementioned function.
|
142
|
+
|
143
|
+
>>> from flwr.common import array_from_numpy
|
144
|
+
>>> # Creating a ParametersRecord would look like this
|
145
|
+
>>> arr_np = np.random.randn(3, 3)
|
146
|
+
>>>
|
147
|
+
>>> # You can use the built-in tool to serialize the array
|
148
|
+
>>> arr = array_from_numpy(arr_np)
|
149
|
+
>>>
|
150
|
+
>>> # Finally, create the record
|
151
|
+
>>> p_record = ParametersRecord({"my_array": arr})
|
152
|
+
>>>
|
153
|
+
>>> # Adding it to the record_set would look like this
|
154
|
+
>>> my_recordset.parameters_records["my_parameters"] = p_record
|
155
|
+
|
156
|
+
For additional examples on how to construct each of the records types shown
|
157
|
+
above, please refer to the documentation for :code:`ConfigsRecord`,
|
158
|
+
:code:`MetricsRecord` and :code:`ParametersRecord`.
|
159
|
+
"""
|
160
|
+
|
161
|
+
def __init__(
|
162
|
+
self,
|
163
|
+
parameters_records: dict[str, ParametersRecord] | None = None,
|
164
|
+
metrics_records: dict[str, MetricsRecord] | None = None,
|
165
|
+
configs_records: dict[str, ConfigsRecord] | None = None,
|
166
|
+
) -> None:
|
167
|
+
data = RecordSetData(
|
168
|
+
parameters_records=parameters_records,
|
169
|
+
metrics_records=metrics_records,
|
170
|
+
configs_records=configs_records,
|
171
|
+
)
|
172
|
+
self.__dict__["_data"] = data
|
65
173
|
|
66
174
|
@property
|
67
175
|
def parameters_records(self) -> TypedDict[str, ParametersRecord]:
|
68
176
|
"""Dictionary holding ParametersRecord instances."""
|
69
|
-
|
177
|
+
data = cast(RecordSetData, self.__dict__["_data"])
|
178
|
+
return data.parameters_records
|
70
179
|
|
71
180
|
@property
|
72
181
|
def metrics_records(self) -> TypedDict[str, MetricsRecord]:
|
73
182
|
"""Dictionary holding MetricsRecord instances."""
|
74
|
-
|
183
|
+
data = cast(RecordSetData, self.__dict__["_data"])
|
184
|
+
return data.metrics_records
|
75
185
|
|
76
186
|
@property
|
77
187
|
def configs_records(self) -> TypedDict[str, ConfigsRecord]:
|
78
188
|
"""Dictionary holding ConfigsRecord instances."""
|
79
|
-
|
189
|
+
data = cast(RecordSetData, self.__dict__["_data"])
|
190
|
+
return data.configs_records
|
191
|
+
|
192
|
+
def __repr__(self) -> str:
|
193
|
+
"""Return a string representation of this instance."""
|
194
|
+
flds = ("parameters_records", "metrics_records", "configs_records")
|
195
|
+
view = ", ".join([f"{fld}={getattr(self, fld)!r}" for fld in flds])
|
196
|
+
return f"{self.__class__.__qualname__}({view})"
|
197
|
+
|
198
|
+
def __eq__(self, other: object) -> bool:
|
199
|
+
"""Compare two instances of the class."""
|
200
|
+
if not isinstance(other, self.__class__):
|
201
|
+
raise NotImplementedError
|
202
|
+
return self.__dict__ == other.__dict__
|
flwr/common/record/typeddict.py
CHANGED
@@ -15,99 +15,74 @@
|
|
15
15
|
"""Typed dict base class for *Records."""
|
16
16
|
|
17
17
|
|
18
|
-
from
|
18
|
+
from collections.abc import ItemsView, Iterator, KeysView, MutableMapping, ValuesView
|
19
|
+
from typing import Callable, Generic, TypeVar, cast
|
19
20
|
|
20
21
|
K = TypeVar("K") # Key type
|
21
22
|
V = TypeVar("V") # Value type
|
22
23
|
|
23
24
|
|
24
|
-
class TypedDict(Generic[K, V]):
|
25
|
+
class TypedDict(MutableMapping[K, V], Generic[K, V]):
|
25
26
|
"""Typed dictionary."""
|
26
27
|
|
27
28
|
def __init__(
|
28
29
|
self, check_key_fn: Callable[[K], None], check_value_fn: Callable[[V], None]
|
29
30
|
):
|
30
|
-
self.
|
31
|
-
self.
|
32
|
-
self.
|
31
|
+
self.__dict__["_check_key_fn"] = check_key_fn
|
32
|
+
self.__dict__["_check_value_fn"] = check_value_fn
|
33
|
+
self.__dict__["_data"] = {}
|
33
34
|
|
34
35
|
def __setitem__(self, key: K, value: V) -> None:
|
35
36
|
"""Set the given key to the given value after type checking."""
|
36
37
|
# Check the types of key and value
|
37
|
-
self._check_key_fn(key)
|
38
|
-
self._check_value_fn(value)
|
38
|
+
cast(Callable[[K], None], self.__dict__["_check_key_fn"])(key)
|
39
|
+
cast(Callable[[V], None], self.__dict__["_check_value_fn"])(value)
|
40
|
+
|
39
41
|
# Set key-value pair
|
40
|
-
self._data[key] = value
|
42
|
+
cast(dict[K, V], self.__dict__["_data"])[key] = value
|
41
43
|
|
42
44
|
def __delitem__(self, key: K) -> None:
|
43
45
|
"""Remove the item with the specified key."""
|
44
|
-
del self._data[key]
|
46
|
+
del cast(dict[K, V], self.__dict__["_data"])[key]
|
45
47
|
|
46
48
|
def __getitem__(self, item: K) -> V:
|
47
49
|
"""Return the value for the specified key."""
|
48
|
-
return self._data[item]
|
50
|
+
return cast(dict[K, V], self.__dict__["_data"])[item]
|
49
51
|
|
50
52
|
def __iter__(self) -> Iterator[K]:
|
51
53
|
"""Yield an iterator over the keys of the dictionary."""
|
52
|
-
return iter(self._data)
|
54
|
+
return iter(cast(dict[K, V], self.__dict__["_data"]))
|
53
55
|
|
54
56
|
def __repr__(self) -> str:
|
55
57
|
"""Return a string representation of the dictionary."""
|
56
|
-
return self._data.__repr__()
|
58
|
+
return cast(dict[K, V], self.__dict__["_data"]).__repr__()
|
57
59
|
|
58
60
|
def __len__(self) -> int:
|
59
61
|
"""Return the number of items in the dictionary."""
|
60
|
-
return len(self._data)
|
62
|
+
return len(cast(dict[K, V], self.__dict__["_data"]))
|
61
63
|
|
62
|
-
def __contains__(self, key:
|
64
|
+
def __contains__(self, key: object) -> bool:
|
63
65
|
"""Check if the dictionary contains the specified key."""
|
64
|
-
return key in self._data
|
66
|
+
return key in cast(dict[K, V], self.__dict__["_data"])
|
65
67
|
|
66
68
|
def __eq__(self, other: object) -> bool:
|
67
69
|
"""Compare this instance to another dictionary or TypedDict."""
|
70
|
+
data = cast(dict[K, V], self.__dict__["_data"])
|
68
71
|
if isinstance(other, TypedDict):
|
69
|
-
|
72
|
+
other_data = cast(dict[K, V], other.__dict__["_data"])
|
73
|
+
return data == other_data
|
70
74
|
if isinstance(other, dict):
|
71
|
-
return
|
75
|
+
return data == other
|
72
76
|
return NotImplemented
|
73
77
|
|
74
|
-
def
|
75
|
-
"""
|
76
|
-
return cast(
|
77
|
-
|
78
|
-
def keys(self) -> Iterator[K]:
|
79
|
-
"""R.keys() -> a set-like object providing a view on R's keys."""
|
80
|
-
return cast(Iterator[K], self._data.keys())
|
81
|
-
|
82
|
-
def values(self) -> Iterator[V]:
|
83
|
-
"""R.values() -> an object providing a view on R's values."""
|
84
|
-
return cast(Iterator[V], self._data.values())
|
85
|
-
|
86
|
-
def update(self, *args: Any, **kwargs: Any) -> None:
|
87
|
-
"""R.update([E, ]**F) -> None.
|
88
|
-
|
89
|
-
Update R from dict/iterable E and F.
|
90
|
-
"""
|
91
|
-
for key, value in dict(*args, **kwargs).items():
|
92
|
-
self[key] = value
|
93
|
-
|
94
|
-
def pop(self, key: K) -> V:
|
95
|
-
"""R.pop(k[,d]) -> v, remove specified key and return the corresponding value.
|
96
|
-
|
97
|
-
If key is not found, d is returned if given, otherwise KeyError is raised.
|
98
|
-
"""
|
99
|
-
return self._data.pop(key)
|
100
|
-
|
101
|
-
def get(self, key: K, default: V) -> V:
|
102
|
-
"""R.get(k[,d]) -> R[k] if k in R, else d.
|
103
|
-
|
104
|
-
d defaults to None.
|
105
|
-
"""
|
106
|
-
return self._data.get(key, default)
|
78
|
+
def keys(self) -> KeysView[K]:
|
79
|
+
"""D.keys() -> a set-like object providing a view on D's keys."""
|
80
|
+
return cast(dict[K, V], self.__dict__["_data"]).keys()
|
107
81
|
|
108
|
-
def
|
109
|
-
"""
|
82
|
+
def values(self) -> ValuesView[V]:
|
83
|
+
"""D.values() -> an object providing a view on D's values."""
|
84
|
+
return cast(dict[K, V], self.__dict__["_data"]).values()
|
110
85
|
|
111
|
-
|
112
|
-
"""
|
113
|
-
self._data.
|
86
|
+
def items(self) -> ItemsView[K, V]:
|
87
|
+
"""D.items() -> a set-like object providing a view on D's items."""
|
88
|
+
return cast(dict[K, V], self.__dict__["_data"]).items()
|
flwr/common/recordset_compat.py
CHANGED
@@ -15,7 +15,9 @@
|
|
15
15
|
"""RecordSet utilities."""
|
16
16
|
|
17
17
|
|
18
|
-
from
|
18
|
+
from collections import OrderedDict
|
19
|
+
from collections.abc import Mapping
|
20
|
+
from typing import Union, cast, get_args
|
19
21
|
|
20
22
|
from . import Array, ConfigsRecord, MetricsRecord, ParametersRecord, RecordSet
|
21
23
|
from .typing import (
|
@@ -35,6 +37,8 @@ from .typing import (
|
|
35
37
|
Status,
|
36
38
|
)
|
37
39
|
|
40
|
+
EMPTY_TENSOR_KEY = "_empty"
|
41
|
+
|
38
42
|
|
39
43
|
def parametersrecord_to_parameters(
|
40
44
|
record: ParametersRecord, keep_input: bool
|
@@ -55,11 +59,17 @@ def parametersrecord_to_parameters(
|
|
55
59
|
keep_input : bool
|
56
60
|
A boolean indicating whether entries in the record should be deleted from the
|
57
61
|
input dictionary immediately after adding them to the record.
|
62
|
+
|
63
|
+
Returns
|
64
|
+
-------
|
65
|
+
parameters : Parameters
|
66
|
+
The parameters in the legacy format Parameters.
|
58
67
|
"""
|
59
68
|
parameters = Parameters(tensors=[], tensor_type="")
|
60
69
|
|
61
70
|
for key in list(record.keys()):
|
62
|
-
|
71
|
+
if key != EMPTY_TENSOR_KEY:
|
72
|
+
parameters.tensors.append(record[key].data)
|
63
73
|
|
64
74
|
if not parameters.tensor_type:
|
65
75
|
# Setting from first array in record. Recall the warning in the docstrings
|
@@ -89,6 +99,11 @@ def parameters_to_parametersrecord(
|
|
89
99
|
A boolean indicating whether parameters should be deleted from the input
|
90
100
|
Parameters object (i.e. a list of serialized NumPy arrays) immediately after
|
91
101
|
adding them to the record.
|
102
|
+
|
103
|
+
Returns
|
104
|
+
-------
|
105
|
+
ParametersRecord
|
106
|
+
The ParametersRecord containing the provided parameters.
|
92
107
|
"""
|
93
108
|
tensor_type = parameters.tensor_type
|
94
109
|
|
@@ -103,12 +118,16 @@ def parameters_to_parametersrecord(
|
|
103
118
|
data=tensor, dtype="", stype=tensor_type, shape=[]
|
104
119
|
)
|
105
120
|
|
121
|
+
if num_arrays == 0:
|
122
|
+
ordered_dict[EMPTY_TENSOR_KEY] = Array(
|
123
|
+
data=b"", dtype="", stype=tensor_type, shape=[]
|
124
|
+
)
|
106
125
|
return ParametersRecord(ordered_dict, keep_input=keep_input)
|
107
126
|
|
108
127
|
|
109
128
|
def _check_mapping_from_recordscalartype_to_scalar(
|
110
129
|
record_data: Mapping[str, Union[ConfigsRecordValues, MetricsRecordValues]]
|
111
|
-
) ->
|
130
|
+
) -> dict[str, Scalar]:
|
112
131
|
"""Check mapping `common.*RecordValues` into `common.Scalar` is possible."""
|
113
132
|
for value in record_data.values():
|
114
133
|
if not isinstance(value, get_args(Scalar)):
|
@@ -119,14 +138,14 @@ def _check_mapping_from_recordscalartype_to_scalar(
|
|
119
138
|
"supported by the `common.RecordSet` infrastructure. "
|
120
139
|
f"You used type: {type(value)}"
|
121
140
|
)
|
122
|
-
return cast(
|
141
|
+
return cast(dict[str, Scalar], record_data)
|
123
142
|
|
124
143
|
|
125
144
|
def _recordset_to_fit_or_evaluate_ins_components(
|
126
145
|
recordset: RecordSet,
|
127
146
|
ins_str: str,
|
128
147
|
keep_input: bool,
|
129
|
-
) ->
|
148
|
+
) -> tuple[Parameters, dict[str, Scalar]]:
|
130
149
|
"""Derive Fit/Evaluate Ins from a RecordSet."""
|
131
150
|
# get Array and construct Parameters
|
132
151
|
parameters_record = recordset.parameters_records[f"{ins_str}.parameters"]
|
@@ -138,7 +157,7 @@ def _recordset_to_fit_or_evaluate_ins_components(
|
|
138
157
|
# get config dict
|
139
158
|
config_record = recordset.configs_records[f"{ins_str}.config"]
|
140
159
|
# pylint: disable-next=protected-access
|
141
|
-
config_dict = _check_mapping_from_recordscalartype_to_scalar(config_record
|
160
|
+
config_dict = _check_mapping_from_recordscalartype_to_scalar(config_record)
|
142
161
|
|
143
162
|
return parameters, config_dict
|
144
163
|
|
@@ -162,7 +181,7 @@ def _fit_or_evaluate_ins_to_recordset(
|
|
162
181
|
def _embed_status_into_recordset(
|
163
182
|
res_str: str, status: Status, recordset: RecordSet
|
164
183
|
) -> RecordSet:
|
165
|
-
status_dict:
|
184
|
+
status_dict: dict[str, ConfigsRecordValues] = {
|
166
185
|
"code": int(status.code.value),
|
167
186
|
"message": status.message,
|
168
187
|
}
|
@@ -206,7 +225,7 @@ def recordset_to_fitres(recordset: RecordSet, keep_input: bool) -> FitRes:
|
|
206
225
|
)
|
207
226
|
configs_record = recordset.configs_records[f"{ins_str}.metrics"]
|
208
227
|
# pylint: disable-next=protected-access
|
209
|
-
metrics = _check_mapping_from_recordscalartype_to_scalar(configs_record
|
228
|
+
metrics = _check_mapping_from_recordscalartype_to_scalar(configs_record)
|
210
229
|
status = _extract_status_from_recordset(ins_str, recordset)
|
211
230
|
|
212
231
|
return FitRes(
|
@@ -267,7 +286,7 @@ def recordset_to_evaluateres(recordset: RecordSet) -> EvaluateRes:
|
|
267
286
|
configs_record = recordset.configs_records[f"{ins_str}.metrics"]
|
268
287
|
|
269
288
|
# pylint: disable-next=protected-access
|
270
|
-
metrics = _check_mapping_from_recordscalartype_to_scalar(configs_record
|
289
|
+
metrics = _check_mapping_from_recordscalartype_to_scalar(configs_record)
|
271
290
|
status = _extract_status_from_recordset(ins_str, recordset)
|
272
291
|
|
273
292
|
return EvaluateRes(
|
@@ -307,7 +326,7 @@ def recordset_to_getparametersins(recordset: RecordSet) -> GetParametersIns:
|
|
307
326
|
"""Derive GetParametersIns from a RecordSet object."""
|
308
327
|
config_record = recordset.configs_records["getparametersins.config"]
|
309
328
|
# pylint: disable-next=protected-access
|
310
|
-
config_dict = _check_mapping_from_recordscalartype_to_scalar(config_record
|
329
|
+
config_dict = _check_mapping_from_recordscalartype_to_scalar(config_record)
|
311
330
|
|
312
331
|
return GetParametersIns(config=config_dict)
|
313
332
|
|
@@ -358,7 +377,7 @@ def recordset_to_getpropertiesins(recordset: RecordSet) -> GetPropertiesIns:
|
|
358
377
|
"""Derive GetPropertiesIns from a RecordSet object."""
|
359
378
|
config_record = recordset.configs_records["getpropertiesins.config"]
|
360
379
|
# pylint: disable-next=protected-access
|
361
|
-
config_dict = _check_mapping_from_recordscalartype_to_scalar(config_record
|
380
|
+
config_dict = _check_mapping_from_recordscalartype_to_scalar(config_record)
|
362
381
|
|
363
382
|
return GetPropertiesIns(config=config_dict)
|
364
383
|
|
@@ -377,7 +396,7 @@ def recordset_to_getpropertiesres(recordset: RecordSet) -> GetPropertiesRes:
|
|
377
396
|
res_str = "getpropertiesres"
|
378
397
|
config_record = recordset.configs_records[f"{res_str}.properties"]
|
379
398
|
# pylint: disable-next=protected-access
|
380
|
-
properties = _check_mapping_from_recordscalartype_to_scalar(config_record
|
399
|
+
properties = _check_mapping_from_recordscalartype_to_scalar(config_record)
|
381
400
|
|
382
401
|
status = _extract_status_from_recordset(res_str, recordset=recordset)
|
383
402
|
|