flwr 1.17.0__py3-none-any.whl → 1.19.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- flwr/__init__.py +1 -1
- flwr/app/__init__.py +15 -0
- flwr/app/error.py +68 -0
- flwr/app/metadata.py +223 -0
- flwr/cli/__init__.py +1 -1
- flwr/cli/app.py +21 -2
- flwr/cli/build.py +83 -58
- flwr/cli/cli_user_auth_interceptor.py +1 -1
- flwr/cli/config_utils.py +53 -17
- flwr/cli/example.py +1 -1
- flwr/cli/install.py +1 -1
- flwr/cli/log.py +4 -4
- flwr/cli/login/__init__.py +1 -1
- flwr/cli/login/login.py +15 -8
- flwr/cli/ls.py +16 -37
- flwr/cli/new/__init__.py +1 -1
- flwr/cli/new/new.py +4 -4
- flwr/cli/new/templates/__init__.py +1 -1
- flwr/cli/new/templates/app/__init__.py +1 -1
- flwr/cli/new/templates/app/code/__init__.py +1 -1
- flwr/cli/new/templates/app/code/client.baseline.py.tpl +1 -1
- flwr/cli/new/templates/app/code/flwr_tune/__init__.py +1 -1
- flwr/cli/new/templates/app/code/flwr_tune/client_app.py.tpl +4 -4
- flwr/cli/new/templates/app/code/model.baseline.py.tpl +1 -1
- flwr/cli/new/templates/app/code/server.baseline.py.tpl +2 -3
- flwr/cli/new/templates/app/code/task.sklearn.py.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +14 -17
- flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +4 -4
- flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.jax.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +1 -1
- flwr/cli/run/__init__.py +1 -1
- flwr/cli/run/run.py +11 -19
- flwr/cli/stop.py +3 -3
- flwr/cli/utils.py +42 -17
- flwr/client/__init__.py +3 -3
- flwr/client/client.py +1 -1
- flwr/client/client_app.py +140 -138
- flwr/client/clientapp/__init__.py +1 -8
- flwr/client/clientapp/utils.py +1 -1
- flwr/client/dpfedavg_numpy_client.py +1 -1
- flwr/client/grpc_adapter_client/__init__.py +1 -1
- flwr/client/grpc_adapter_client/connection.py +5 -5
- flwr/client/grpc_rere_client/__init__.py +1 -1
- flwr/client/grpc_rere_client/client_interceptor.py +1 -1
- flwr/client/grpc_rere_client/connection.py +131 -61
- flwr/client/grpc_rere_client/grpc_adapter.py +35 -7
- flwr/client/message_handler/__init__.py +1 -1
- flwr/client/message_handler/message_handler.py +2 -2
- flwr/client/mod/__init__.py +1 -1
- flwr/client/mod/centraldp_mods.py +1 -1
- flwr/client/mod/comms_mods.py +39 -20
- flwr/client/mod/localdp_mod.py +6 -6
- flwr/client/mod/secure_aggregation/__init__.py +1 -1
- flwr/client/mod/secure_aggregation/secagg_mod.py +1 -1
- flwr/client/mod/secure_aggregation/secaggplus_mod.py +1 -1
- flwr/client/mod/utils.py +1 -1
- flwr/client/numpy_client.py +1 -1
- flwr/client/rest_client/__init__.py +1 -1
- flwr/client/rest_client/connection.py +174 -68
- flwr/client/run_info_store.py +1 -1
- flwr/client/typing.py +1 -1
- flwr/clientapp/__init__.py +15 -0
- flwr/common/__init__.py +3 -3
- flwr/common/address.py +1 -1
- flwr/common/args.py +1 -1
- flwr/common/auth_plugin/__init__.py +3 -1
- flwr/common/auth_plugin/auth_plugin.py +30 -4
- flwr/common/config.py +1 -1
- flwr/common/constant.py +37 -8
- flwr/common/context.py +1 -1
- flwr/common/date.py +1 -1
- flwr/common/differential_privacy.py +1 -1
- flwr/common/differential_privacy_constants.py +1 -1
- flwr/common/dp.py +1 -1
- flwr/common/event_log_plugin/event_log_plugin.py +3 -3
- flwr/common/exit/exit.py +6 -6
- flwr/common/exit_handlers.py +31 -1
- flwr/common/grpc.py +1 -1
- flwr/common/heartbeat.py +165 -0
- flwr/common/inflatable.py +290 -0
- flwr/common/inflatable_grpc_utils.py +99 -0
- flwr/common/inflatable_rest_utils.py +99 -0
- flwr/common/inflatable_utils.py +341 -0
- flwr/common/logger.py +1 -1
- flwr/common/message.py +137 -252
- flwr/common/object_ref.py +1 -1
- flwr/common/parameter.py +1 -1
- flwr/common/pyproject.py +1 -1
- flwr/common/record/__init__.py +3 -2
- flwr/common/record/array.py +323 -0
- flwr/common/record/arrayrecord.py +121 -243
- flwr/common/record/configrecord.py +71 -16
- flwr/common/record/conversion_utils.py +2 -2
- flwr/common/record/metricrecord.py +71 -20
- flwr/common/record/recorddict.py +207 -90
- flwr/common/record/typeddict.py +1 -1
- flwr/common/recorddict_compat.py +2 -2
- flwr/common/retry_invoker.py +15 -11
- flwr/common/secure_aggregation/__init__.py +1 -1
- flwr/common/secure_aggregation/crypto/__init__.py +1 -1
- flwr/common/secure_aggregation/crypto/shamir.py +52 -30
- flwr/common/secure_aggregation/crypto/symmetric_encryption.py +1 -1
- flwr/common/secure_aggregation/ndarrays_arithmetic.py +1 -1
- flwr/common/secure_aggregation/quantization.py +1 -1
- flwr/common/secure_aggregation/secaggplus_constants.py +1 -1
- flwr/common/secure_aggregation/secaggplus_utils.py +1 -1
- flwr/common/serde.py +60 -184
- flwr/common/serde_utils.py +175 -0
- flwr/common/telemetry.py +2 -2
- flwr/common/typing.py +6 -4
- flwr/common/version.py +1 -1
- flwr/compat/__init__.py +15 -0
- flwr/compat/client/__init__.py +15 -0
- flwr/{client → compat/client}/app.py +71 -211
- flwr/{client → compat/client}/grpc_client/__init__.py +1 -1
- flwr/{client → compat/client}/grpc_client/connection.py +13 -13
- flwr/compat/common/__init__.py +15 -0
- flwr/compat/server/__init__.py +15 -0
- flwr/compat/server/app.py +174 -0
- flwr/compat/simulation/__init__.py +15 -0
- flwr/proto/__init__.py +1 -1
- flwr/proto/fleet_pb2.py +32 -27
- flwr/proto/fleet_pb2.pyi +49 -35
- flwr/proto/fleet_pb2_grpc.py +117 -13
- flwr/proto/fleet_pb2_grpc.pyi +47 -6
- flwr/proto/heartbeat_pb2.py +33 -0
- flwr/proto/heartbeat_pb2.pyi +66 -0
- flwr/proto/heartbeat_pb2_grpc.py +4 -0
- flwr/proto/heartbeat_pb2_grpc.pyi +4 -0
- flwr/proto/message_pb2.py +28 -11
- flwr/proto/message_pb2.pyi +125 -0
- flwr/proto/recorddict_pb2.py +16 -28
- flwr/proto/recorddict_pb2.pyi +46 -64
- flwr/proto/run_pb2.py +24 -32
- flwr/proto/run_pb2.pyi +4 -52
- flwr/proto/serverappio_pb2.py +32 -23
- flwr/proto/serverappio_pb2.pyi +45 -3
- flwr/proto/serverappio_pb2_grpc.py +138 -34
- flwr/proto/serverappio_pb2_grpc.pyi +54 -13
- flwr/proto/simulationio_pb2.py +12 -11
- flwr/proto/simulationio_pb2_grpc.py +35 -0
- flwr/proto/simulationio_pb2_grpc.pyi +14 -0
- flwr/server/__init__.py +2 -2
- flwr/server/app.py +69 -187
- flwr/server/client_manager.py +1 -1
- flwr/server/client_proxy.py +1 -1
- flwr/server/compat/__init__.py +1 -1
- flwr/server/compat/app.py +1 -1
- flwr/server/compat/app_utils.py +51 -29
- flwr/server/compat/legacy_context.py +1 -1
- flwr/server/criterion.py +1 -1
- flwr/server/fleet_event_log_interceptor.py +2 -2
- flwr/server/grid/grid.py +3 -3
- flwr/server/grid/grpc_grid.py +104 -34
- flwr/server/grid/inmemory_grid.py +5 -4
- flwr/server/history.py +1 -1
- flwr/server/run_serverapp.py +1 -1
- flwr/server/server.py +1 -1
- flwr/server/server_app.py +65 -58
- flwr/server/server_config.py +1 -1
- flwr/server/serverapp/__init__.py +1 -1
- flwr/server/serverapp/app.py +19 -1
- flwr/server/serverapp_components.py +1 -1
- flwr/server/strategy/__init__.py +1 -1
- flwr/server/strategy/aggregate.py +1 -1
- flwr/server/strategy/bulyan.py +2 -2
- flwr/server/strategy/dp_adaptive_clipping.py +17 -17
- flwr/server/strategy/dp_fixed_clipping.py +17 -17
- flwr/server/strategy/dpfedavg_adaptive.py +1 -1
- flwr/server/strategy/dpfedavg_fixed.py +1 -1
- flwr/server/strategy/fault_tolerant_fedavg.py +1 -1
- flwr/server/strategy/fedadagrad.py +1 -1
- flwr/server/strategy/fedadam.py +1 -1
- flwr/server/strategy/fedavg.py +1 -1
- flwr/server/strategy/fedavg_android.py +1 -1
- flwr/server/strategy/fedavgm.py +1 -1
- flwr/server/strategy/fedmedian.py +1 -1
- flwr/server/strategy/fedopt.py +1 -1
- flwr/server/strategy/fedprox.py +1 -1
- flwr/server/strategy/fedtrimmedavg.py +1 -1
- flwr/server/strategy/fedxgb_bagging.py +1 -1
- flwr/server/strategy/fedxgb_cyclic.py +1 -1
- flwr/server/strategy/fedxgb_nn_avg.py +3 -2
- flwr/server/strategy/fedyogi.py +1 -1
- flwr/server/strategy/krum.py +1 -1
- flwr/server/strategy/qfedavg.py +1 -1
- flwr/server/strategy/strategy.py +1 -1
- flwr/server/superlink/__init__.py +1 -1
- flwr/server/superlink/ffs/__init__.py +3 -1
- flwr/server/superlink/ffs/disk_ffs.py +1 -1
- flwr/server/superlink/ffs/ffs.py +1 -1
- flwr/server/superlink/ffs/ffs_factory.py +1 -1
- flwr/server/superlink/fleet/__init__.py +1 -1
- flwr/server/superlink/fleet/grpc_adapter/__init__.py +1 -1
- flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +14 -4
- flwr/server/superlink/fleet/grpc_bidi/__init__.py +1 -1
- flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py +1 -1
- flwr/server/superlink/fleet/grpc_bidi/grpc_bridge.py +1 -1
- flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py +1 -1
- flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +13 -13
- flwr/server/superlink/fleet/grpc_rere/__init__.py +1 -1
- flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +102 -8
- flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +1 -1
- flwr/server/superlink/fleet/message_handler/__init__.py +1 -1
- flwr/server/superlink/fleet/message_handler/message_handler.py +136 -19
- flwr/server/superlink/fleet/rest_rere/__init__.py +1 -1
- flwr/server/superlink/fleet/rest_rere/rest_api.py +73 -12
- flwr/server/superlink/fleet/vce/__init__.py +1 -1
- flwr/server/superlink/fleet/vce/backend/__init__.py +1 -1
- flwr/server/superlink/fleet/vce/backend/backend.py +1 -1
- flwr/server/superlink/fleet/vce/backend/raybackend.py +1 -1
- flwr/server/superlink/fleet/vce/vce_api.py +7 -4
- flwr/server/superlink/linkstate/__init__.py +1 -1
- flwr/server/superlink/linkstate/in_memory_linkstate.py +139 -44
- flwr/server/superlink/linkstate/linkstate.py +54 -21
- flwr/server/superlink/linkstate/linkstate_factory.py +1 -1
- flwr/server/superlink/linkstate/sqlite_linkstate.py +150 -56
- flwr/server/superlink/linkstate/utils.py +34 -30
- flwr/server/superlink/serverappio/serverappio_grpc.py +3 -0
- flwr/server/superlink/serverappio/serverappio_servicer.py +211 -57
- flwr/server/superlink/simulation/__init__.py +1 -1
- flwr/server/superlink/simulation/simulationio_grpc.py +1 -1
- flwr/server/superlink/simulation/simulationio_servicer.py +26 -2
- flwr/server/superlink/utils.py +45 -3
- flwr/server/typing.py +1 -1
- flwr/server/utils/__init__.py +1 -1
- flwr/server/utils/tensorboard.py +1 -1
- flwr/server/utils/validator.py +3 -3
- flwr/server/workflow/__init__.py +1 -1
- flwr/server/workflow/constant.py +1 -1
- flwr/server/workflow/default_workflows.py +1 -1
- flwr/server/workflow/secure_aggregation/__init__.py +1 -1
- flwr/server/workflow/secure_aggregation/secagg_workflow.py +1 -1
- flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +1 -1
- flwr/serverapp/__init__.py +15 -0
- flwr/simulation/__init__.py +1 -1
- flwr/simulation/app.py +18 -1
- flwr/simulation/legacy_app.py +1 -1
- flwr/simulation/ray_transport/__init__.py +1 -1
- flwr/simulation/ray_transport/ray_actor.py +1 -1
- flwr/simulation/ray_transport/ray_client_proxy.py +1 -1
- flwr/simulation/ray_transport/utils.py +1 -1
- flwr/simulation/run_simulation.py +2 -2
- flwr/simulation/simulationio_connection.py +1 -1
- flwr/supercore/__init__.py +15 -0
- flwr/supercore/object_store/__init__.py +24 -0
- flwr/supercore/object_store/in_memory_object_store.py +229 -0
- flwr/supercore/object_store/object_store.py +192 -0
- flwr/supercore/object_store/object_store_factory.py +44 -0
- flwr/superexec/__init__.py +1 -1
- flwr/superexec/app.py +1 -1
- flwr/superexec/deployment.py +7 -3
- flwr/superexec/exec_event_log_interceptor.py +4 -4
- flwr/superexec/exec_grpc.py +8 -4
- flwr/superexec/exec_servicer.py +126 -24
- flwr/superexec/exec_user_auth_interceptor.py +38 -9
- flwr/superexec/executor.py +5 -1
- flwr/superexec/simulation.py +8 -2
- flwr/superlink/__init__.py +15 -0
- flwr/{client/supernode → supernode}/__init__.py +1 -8
- flwr/{client/nodestate/nodestate.py → supernode/cli/__init__.py} +8 -15
- flwr/{client/supernode/app.py → supernode/cli/flower_supernode.py} +4 -13
- flwr/supernode/cli/flwr_clientapp.py +81 -0
- flwr/{client → supernode}/nodestate/__init__.py +1 -1
- flwr/supernode/nodestate/in_memory_nodestate.py +190 -0
- flwr/supernode/nodestate/nodestate.py +212 -0
- flwr/{client → supernode}/nodestate/nodestate_factory.py +1 -1
- flwr/supernode/runtime/__init__.py +15 -0
- flwr/{client/clientapp/app.py → supernode/runtime/run_clientapp.py} +26 -57
- flwr/supernode/servicer/__init__.py +15 -0
- flwr/supernode/servicer/clientappio/__init__.py +24 -0
- flwr/{client/clientapp → supernode/servicer/clientappio}/clientappio_servicer.py +1 -1
- flwr/supernode/start_client_internal.py +491 -0
- {flwr-1.17.0.dist-info → flwr-1.19.0.dist-info}/METADATA +6 -5
- flwr-1.19.0.dist-info/RECORD +365 -0
- {flwr-1.17.0.dist-info → flwr-1.19.0.dist-info}/WHEEL +1 -1
- {flwr-1.17.0.dist-info → flwr-1.19.0.dist-info}/entry_points.txt +2 -2
- flwr/client/heartbeat.py +0 -74
- flwr/client/nodestate/in_memory_nodestate.py +0 -38
- flwr-1.17.0.dist-info/LICENSE +0 -202
- flwr-1.17.0.dist-info/RECORD +0 -333
|
@@ -15,12 +15,21 @@
|
|
|
15
15
|
"""MetricRecord."""
|
|
16
16
|
|
|
17
17
|
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
18
20
|
from logging import WARN
|
|
19
|
-
from typing import
|
|
21
|
+
from typing import cast, get_args
|
|
20
22
|
|
|
21
23
|
from flwr.common.typing import MetricRecordValues, MetricScalar
|
|
22
24
|
|
|
25
|
+
# pylint: disable=E0611
|
|
26
|
+
from flwr.proto.recorddict_pb2 import MetricRecord as ProtoMetricRecord
|
|
27
|
+
from flwr.proto.recorddict_pb2 import MetricRecordValue as ProtoMetricRecordValue
|
|
28
|
+
|
|
29
|
+
# pylint: enable=E0611
|
|
30
|
+
from ..inflatable import InflatableObject, add_header_to_object_body, get_object_body
|
|
23
31
|
from ..logger import log
|
|
32
|
+
from ..serde_utils import record_value_dict_from_proto, record_value_dict_to_proto
|
|
24
33
|
from .typeddict import TypedDict
|
|
25
34
|
|
|
26
35
|
|
|
@@ -59,8 +68,8 @@ def _check_value(value: MetricRecordValues) -> None:
|
|
|
59
68
|
is_valid(value)
|
|
60
69
|
|
|
61
70
|
|
|
62
|
-
class MetricRecord(TypedDict[str, MetricRecordValues]):
|
|
63
|
-
"""
|
|
71
|
+
class MetricRecord(TypedDict[str, MetricRecordValues], InflatableObject):
|
|
72
|
+
"""Metric record.
|
|
64
73
|
|
|
65
74
|
A :code:`MetricRecord` is a Python dictionary designed to ensure that
|
|
66
75
|
each key-value pair adheres to specified data types. A :code:`MetricRecord`
|
|
@@ -89,27 +98,27 @@ class MetricRecord(TypedDict[str, MetricRecordValues]):
|
|
|
89
98
|
Common to these examples is that the output can be typically represented by
|
|
90
99
|
a single scalar (:code:`int`, :code:`float`) or list of scalars.
|
|
91
100
|
|
|
92
|
-
Let's see some examples of how to construct a :code:`MetricRecord` from scratch
|
|
101
|
+
Let's see some examples of how to construct a :code:`MetricRecord` from scratch::
|
|
102
|
+
|
|
103
|
+
from flwr.common import MetricRecord
|
|
93
104
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
>>> # It also supports lists
|
|
101
|
-
>>> record["loss-historic"] = [0.9, 0.5, 0.01]
|
|
105
|
+
# A `MetricRecord` is a specialized Python dictionary
|
|
106
|
+
record = MetricRecord({"accuracy": 0.94})
|
|
107
|
+
# You can add more content to an existing record
|
|
108
|
+
record["loss"] = 0.01
|
|
109
|
+
# It also supports lists
|
|
110
|
+
record["loss-historic"] = [0.9, 0.5, 0.01]
|
|
102
111
|
|
|
103
112
|
Since types are enforced, the types of the objects inserted are checked. For a
|
|
104
113
|
:code:`MetricRecord`, value types allowed are those in defined in
|
|
105
114
|
:code:`flwr.common.MetricRecordValues`. Similarly, only :code:`str` keys are
|
|
106
|
-
allowed
|
|
115
|
+
allowed::
|
|
107
116
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
117
|
+
from flwr.common import MetricRecord
|
|
118
|
+
|
|
119
|
+
record = MetricRecord() # an empty record
|
|
120
|
+
# Add unsupported value
|
|
121
|
+
record["something-unsupported"] = {'a': 123} # Will throw a `TypeError`
|
|
113
122
|
|
|
114
123
|
If you need a more versatily type of record try :code:`ConfigRecord` or
|
|
115
124
|
:code:`ArrayRecord`.
|
|
@@ -117,7 +126,7 @@ class MetricRecord(TypedDict[str, MetricRecordValues]):
|
|
|
117
126
|
|
|
118
127
|
def __init__(
|
|
119
128
|
self,
|
|
120
|
-
metric_dict:
|
|
129
|
+
metric_dict: dict[str, MetricRecordValues] | None = None,
|
|
121
130
|
keep_input: bool = True,
|
|
122
131
|
) -> None:
|
|
123
132
|
super().__init__(_check_key, _check_value)
|
|
@@ -143,6 +152,48 @@ class MetricRecord(TypedDict[str, MetricRecordValues]):
|
|
|
143
152
|
num_bytes += len(k)
|
|
144
153
|
return num_bytes
|
|
145
154
|
|
|
155
|
+
def deflate(self) -> bytes:
|
|
156
|
+
"""Deflate object."""
|
|
157
|
+
protos = record_value_dict_to_proto(self, [float, int], ProtoMetricRecordValue)
|
|
158
|
+
obj_body = ProtoMetricRecord(
|
|
159
|
+
items=[ProtoMetricRecord.Item(key=k, value=v) for k, v in protos.items()]
|
|
160
|
+
).SerializeToString()
|
|
161
|
+
return add_header_to_object_body(object_body=obj_body, obj=self)
|
|
162
|
+
|
|
163
|
+
@classmethod
|
|
164
|
+
def inflate(
|
|
165
|
+
cls, object_content: bytes, children: dict[str, InflatableObject] | None = None
|
|
166
|
+
) -> MetricRecord:
|
|
167
|
+
"""Inflate a MetricRecord from bytes.
|
|
168
|
+
|
|
169
|
+
Parameters
|
|
170
|
+
----------
|
|
171
|
+
object_content : bytes
|
|
172
|
+
The deflated object content of the MetricRecord.
|
|
173
|
+
|
|
174
|
+
children : Optional[dict[str, InflatableObject]] (default: None)
|
|
175
|
+
Must be ``None``. ``MetricRecord`` does not support child objects.
|
|
176
|
+
Providing any children will raise a ``ValueError``.
|
|
177
|
+
|
|
178
|
+
Returns
|
|
179
|
+
-------
|
|
180
|
+
MetricRecord
|
|
181
|
+
The inflated MetricRecord.
|
|
182
|
+
"""
|
|
183
|
+
if children:
|
|
184
|
+
raise ValueError("`MetricRecord` objects do not have children.")
|
|
185
|
+
|
|
186
|
+
obj_body = get_object_body(object_content, cls)
|
|
187
|
+
metric_record_proto = ProtoMetricRecord.FromString(obj_body)
|
|
188
|
+
protos = {item.key: item.value for item in metric_record_proto.items}
|
|
189
|
+
return cls(
|
|
190
|
+
metric_dict=cast(
|
|
191
|
+
dict[str, MetricRecordValues],
|
|
192
|
+
record_value_dict_from_proto(protos),
|
|
193
|
+
),
|
|
194
|
+
keep_input=False,
|
|
195
|
+
)
|
|
196
|
+
|
|
146
197
|
|
|
147
198
|
class MetricsRecord(MetricRecord):
|
|
148
199
|
"""Deprecated class ``MetricsRecord``, use ``MetricRecord`` instead.
|
|
@@ -174,7 +225,7 @@ class MetricsRecord(MetricRecord):
|
|
|
174
225
|
|
|
175
226
|
def __init__(
|
|
176
227
|
self,
|
|
177
|
-
metric_dict:
|
|
228
|
+
metric_dict: dict[str, MetricRecordValues] | None = None,
|
|
178
229
|
keep_input: bool = True,
|
|
179
230
|
):
|
|
180
231
|
if not MetricsRecord._warning_logged:
|
flwr/common/record/recorddict.py
CHANGED
|
@@ -17,10 +17,12 @@
|
|
|
17
17
|
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
|
+
import json
|
|
20
21
|
from logging import WARN
|
|
21
22
|
from textwrap import indent
|
|
22
23
|
from typing import TypeVar, Union, cast
|
|
23
24
|
|
|
25
|
+
from ..inflatable import InflatableObject, add_header_to_object_body, get_object_body
|
|
24
26
|
from ..logger import log
|
|
25
27
|
from .arrayrecord import ArrayRecord
|
|
26
28
|
from .configrecord import ConfigRecord
|
|
@@ -29,6 +31,22 @@ from .typeddict import TypedDict
|
|
|
29
31
|
|
|
30
32
|
RecordType = Union[ArrayRecord, MetricRecord, ConfigRecord]
|
|
31
33
|
|
|
34
|
+
|
|
35
|
+
class _WarningTracker:
|
|
36
|
+
"""A class to track warnings for deprecated properties."""
|
|
37
|
+
|
|
38
|
+
def __init__(self) -> None:
|
|
39
|
+
# These variables are used to ensure that the deprecation warnings
|
|
40
|
+
# for the deprecated properties/class are logged only once.
|
|
41
|
+
self.recordset_init_logged = False
|
|
42
|
+
self.recorddict_init_logged = False
|
|
43
|
+
self.parameters_records_logged = False
|
|
44
|
+
self.metrics_records_logged = False
|
|
45
|
+
self.configs_records_logged = False
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
_warning_tracker = _WarningTracker()
|
|
49
|
+
|
|
32
50
|
T = TypeVar("T")
|
|
33
51
|
|
|
34
52
|
|
|
@@ -81,7 +99,7 @@ class _SyncedDict(TypedDict[str, T]):
|
|
|
81
99
|
)
|
|
82
100
|
|
|
83
101
|
|
|
84
|
-
class RecordDict(TypedDict[str, RecordType]):
|
|
102
|
+
class RecordDict(TypedDict[str, RecordType], InflatableObject):
|
|
85
103
|
"""RecordDict stores groups of arrays, metrics and configs.
|
|
86
104
|
|
|
87
105
|
A :class:`RecordDict` is the unified mechanism by which arrays,
|
|
@@ -103,48 +121,74 @@ class RecordDict(TypedDict[str, RecordType]):
|
|
|
103
121
|
are Python dictionaries designed to ensure that each key-value pair
|
|
104
122
|
adheres to specified data types.
|
|
105
123
|
|
|
106
|
-
Let's see an example
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
124
|
+
Let's see an example::
|
|
125
|
+
|
|
126
|
+
from flwr.common import RecordDict
|
|
127
|
+
from flwr.common import ArrayRecord, ConfigRecord, MetricRecord
|
|
128
|
+
|
|
129
|
+
# Let's begin with an empty record
|
|
130
|
+
my_records = RecordDict()
|
|
131
|
+
|
|
132
|
+
# We can create a ConfigRecord
|
|
133
|
+
c_record = ConfigRecord({"lr": 0.1, "batch-size": 128})
|
|
134
|
+
# Adding it to the RecordDict would look like this
|
|
135
|
+
my_records["my_config"] = c_record
|
|
136
|
+
|
|
137
|
+
# We can create a MetricRecord following a similar process
|
|
138
|
+
m_record = MetricRecord({"accuracy": 0.93, "losses": [0.23, 0.1]})
|
|
139
|
+
# Adding it to the RecordDict would look like this
|
|
140
|
+
my_records["my_metrics"] = m_record
|
|
123
141
|
|
|
124
142
|
Adding an :code:`ArrayRecord` follows the same steps as above but first,
|
|
125
143
|
the array needs to be serialized and represented as a :code:`flwr.common.Array`.
|
|
126
|
-
For example
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
144
|
+
For example::
|
|
145
|
+
|
|
146
|
+
from flwr.common import Array
|
|
147
|
+
# Creating an ArrayRecord would look like this
|
|
148
|
+
arr_np = np.random.randn(3, 3)
|
|
149
|
+
|
|
150
|
+
# You can use the built-in tool to serialize the array
|
|
151
|
+
arr = Array(arr_np)
|
|
152
|
+
|
|
153
|
+
# Finally, create the record
|
|
154
|
+
arr_record = ArrayRecord({"my_array": arr})
|
|
155
|
+
|
|
156
|
+
# Adding it to the RecordDict would look like this
|
|
157
|
+
my_records["my_parameters"] = arr_record
|
|
140
158
|
|
|
141
159
|
For additional examples on how to construct each of the records types shown
|
|
142
160
|
above, please refer to the documentation for :code:`ConfigRecord`,
|
|
143
161
|
:code:`MetricRecord` and :code:`ArrayRecord`.
|
|
144
162
|
"""
|
|
145
163
|
|
|
146
|
-
def __init__(
|
|
164
|
+
def __init__(
|
|
165
|
+
self,
|
|
166
|
+
records: dict[str, RecordType] | None = None,
|
|
167
|
+
*,
|
|
168
|
+
parameters_records: dict[str, ArrayRecord] | None = None,
|
|
169
|
+
metrics_records: dict[str, MetricRecord] | None = None,
|
|
170
|
+
configs_records: dict[str, ConfigRecord] | None = None,
|
|
171
|
+
) -> None:
|
|
147
172
|
super().__init__(_check_key, _check_value)
|
|
173
|
+
|
|
174
|
+
# Warning for deprecated usage
|
|
175
|
+
if (
|
|
176
|
+
parameters_records is not None
|
|
177
|
+
or metrics_records is not None
|
|
178
|
+
or configs_records is not None
|
|
179
|
+
):
|
|
180
|
+
log(
|
|
181
|
+
WARN,
|
|
182
|
+
"The arguments `parameters_records`, `metrics_records`, and "
|
|
183
|
+
"`configs_records` of `RecordDict` are deprecated and will "
|
|
184
|
+
"be removed in a future release. "
|
|
185
|
+
"Please pass all records using the `records` argument instead.",
|
|
186
|
+
)
|
|
187
|
+
records = records or {}
|
|
188
|
+
records.update(parameters_records or {})
|
|
189
|
+
records.update(metrics_records or {})
|
|
190
|
+
records.update(configs_records or {})
|
|
191
|
+
|
|
148
192
|
if records is not None:
|
|
149
193
|
for key, record in records.items():
|
|
150
194
|
self[key] = record
|
|
@@ -196,6 +240,120 @@ class RecordDict(TypedDict[str, RecordType]):
|
|
|
196
240
|
type(value).__name__,
|
|
197
241
|
)
|
|
198
242
|
|
|
243
|
+
@property
|
|
244
|
+
def parameters_records(self) -> TypedDict[str, ArrayRecord]:
|
|
245
|
+
"""Deprecated property.
|
|
246
|
+
|
|
247
|
+
Use ``array_records`` instead.
|
|
248
|
+
"""
|
|
249
|
+
if _warning_tracker.parameters_records_logged:
|
|
250
|
+
_warning_tracker.parameters_records_logged = True
|
|
251
|
+
log(
|
|
252
|
+
WARN,
|
|
253
|
+
"The `parameters_records` property of `RecordDict` "
|
|
254
|
+
"(formerly `RecordSet`) is deprecated and will be removed in a "
|
|
255
|
+
"future release. Please use the `array_records` property instead.",
|
|
256
|
+
)
|
|
257
|
+
return self.array_records
|
|
258
|
+
|
|
259
|
+
@property
|
|
260
|
+
def metrics_records(self) -> TypedDict[str, MetricRecord]:
|
|
261
|
+
"""Deprecated property.
|
|
262
|
+
|
|
263
|
+
Use ``metric_records`` instead.
|
|
264
|
+
"""
|
|
265
|
+
if not _warning_tracker.metrics_records_logged:
|
|
266
|
+
_warning_tracker.metrics_records_logged = True
|
|
267
|
+
log(
|
|
268
|
+
WARN,
|
|
269
|
+
"The `metrics_records` property of `RecordDict` "
|
|
270
|
+
"(formerly `RecordSet`) is deprecated and will be removed in a "
|
|
271
|
+
"future release. Please use the `metric_records` property instead.",
|
|
272
|
+
)
|
|
273
|
+
return self.metric_records
|
|
274
|
+
|
|
275
|
+
@property
|
|
276
|
+
def configs_records(self) -> TypedDict[str, ConfigRecord]:
|
|
277
|
+
"""Deprecated property.
|
|
278
|
+
|
|
279
|
+
Use ``config_records`` instead.
|
|
280
|
+
"""
|
|
281
|
+
if not _warning_tracker.configs_records_logged:
|
|
282
|
+
_warning_tracker.configs_records_logged = True
|
|
283
|
+
log(
|
|
284
|
+
WARN,
|
|
285
|
+
"The `configs_records` property of `RecordDict` "
|
|
286
|
+
"(formerly `RecordSet`) is deprecated and will be removed in a "
|
|
287
|
+
"future release. Please use the `config_records` property instead.",
|
|
288
|
+
)
|
|
289
|
+
return self.config_records
|
|
290
|
+
|
|
291
|
+
@property
|
|
292
|
+
def children(self) -> dict[str, InflatableObject]:
|
|
293
|
+
"""Return a dictionary of records with their Object IDs as keys."""
|
|
294
|
+
return {record.object_id: record for record in self.values()}
|
|
295
|
+
|
|
296
|
+
def deflate(self) -> bytes:
|
|
297
|
+
"""Deflate the RecordDict."""
|
|
298
|
+
# record_name: record_object_id mapping
|
|
299
|
+
record_refs: dict[str, str] = {}
|
|
300
|
+
|
|
301
|
+
for record_name, record in self.items():
|
|
302
|
+
record_refs[record_name] = record.object_id
|
|
303
|
+
|
|
304
|
+
# Serialize references dict
|
|
305
|
+
object_body = json.dumps(record_refs).encode("utf-8")
|
|
306
|
+
return add_header_to_object_body(object_body=object_body, obj=self)
|
|
307
|
+
|
|
308
|
+
@classmethod
|
|
309
|
+
def inflate(
|
|
310
|
+
cls, object_content: bytes, children: dict[str, InflatableObject] | None = None
|
|
311
|
+
) -> RecordDict:
|
|
312
|
+
"""Inflate an RecordDict from bytes.
|
|
313
|
+
|
|
314
|
+
Parameters
|
|
315
|
+
----------
|
|
316
|
+
object_content : bytes
|
|
317
|
+
The deflated object content of the RecordDict.
|
|
318
|
+
children : Optional[dict[str, InflatableObject]] (default: None)
|
|
319
|
+
Dictionary of children InflatableObjects mapped to their Object IDs.
|
|
320
|
+
These children enable the full inflation of the RecordDict. Default is None.
|
|
321
|
+
|
|
322
|
+
Returns
|
|
323
|
+
-------
|
|
324
|
+
RecordDict
|
|
325
|
+
The inflated RecordDict.
|
|
326
|
+
"""
|
|
327
|
+
if children is None:
|
|
328
|
+
children = {}
|
|
329
|
+
|
|
330
|
+
# Inflate mapping of record_names (keys in the RecordDict) to Record' object IDs
|
|
331
|
+
obj_body = get_object_body(object_content, cls)
|
|
332
|
+
record_refs: dict[str, str] = json.loads(obj_body.decode(encoding="utf-8"))
|
|
333
|
+
|
|
334
|
+
unique_records = set(record_refs.values())
|
|
335
|
+
children_obj_ids = set(children.keys())
|
|
336
|
+
if unique_records != children_obj_ids:
|
|
337
|
+
raise ValueError(
|
|
338
|
+
"Unexpected set of `children`. "
|
|
339
|
+
f"Expected {unique_records} but got {children_obj_ids}."
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
# Ensure children are one of the *Record objects exepecte in a RecordDict
|
|
343
|
+
if not all(
|
|
344
|
+
isinstance(ch, (ArrayRecord, ConfigRecord, MetricRecord))
|
|
345
|
+
for ch in children.values()
|
|
346
|
+
):
|
|
347
|
+
raise ValueError(
|
|
348
|
+
"`Children` are expected to be of type `ArrayRecord`, "
|
|
349
|
+
"`ConfigRecord` or `MetricRecord`."
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
# Instantiate new RecordDict
|
|
353
|
+
return RecordDict(
|
|
354
|
+
{name: children[object_id] for name, object_id in record_refs.items()} # type: ignore
|
|
355
|
+
)
|
|
356
|
+
|
|
199
357
|
|
|
200
358
|
class RecordSet(RecordDict):
|
|
201
359
|
"""Deprecated class ``RecordSet``, use ``RecordDict`` instead.
|
|
@@ -223,66 +381,25 @@ class RecordSet(RecordDict):
|
|
|
223
381
|
my_content = RecordDict()
|
|
224
382
|
"""
|
|
225
383
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
384
|
+
def __init__(
|
|
385
|
+
self,
|
|
386
|
+
records: dict[str, RecordType] | None = None,
|
|
387
|
+
*,
|
|
388
|
+
parameters_records: dict[str, ArrayRecord] | None = None,
|
|
389
|
+
metrics_records: dict[str, MetricRecord] | None = None,
|
|
390
|
+
configs_records: dict[str, ConfigRecord] | None = None,
|
|
391
|
+
) -> None:
|
|
392
|
+
if not _warning_tracker.recordset_init_logged:
|
|
393
|
+
_warning_tracker.recordset_init_logged = True
|
|
234
394
|
log(
|
|
235
395
|
WARN,
|
|
236
396
|
"The `RecordSet` class has been renamed to `RecordDict`. "
|
|
237
397
|
"Support for `RecordSet` will be removed in a future release. "
|
|
238
398
|
"Please update your code accordingly.",
|
|
239
399
|
)
|
|
240
|
-
super().__init__(
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
Use ``array_records`` instead.
|
|
247
|
-
"""
|
|
248
|
-
if not RecordSet._warning_logged_params:
|
|
249
|
-
RecordSet._warning_logged_params = True
|
|
250
|
-
log(
|
|
251
|
-
WARN,
|
|
252
|
-
"`RecordSet.parameters_records` has been deprecated "
|
|
253
|
-
"and will be removed in a future release. Please use "
|
|
254
|
-
"`RecordDict.array_records` instead.",
|
|
255
|
-
)
|
|
256
|
-
return self.array_records
|
|
257
|
-
|
|
258
|
-
@property
|
|
259
|
-
def metrics_records(self) -> TypedDict[str, MetricRecord]:
|
|
260
|
-
"""Deprecated property.
|
|
261
|
-
|
|
262
|
-
Use ``metric_records`` instead.
|
|
263
|
-
"""
|
|
264
|
-
if not RecordSet._warning_logged_metrics:
|
|
265
|
-
RecordSet._warning_logged_metrics = True
|
|
266
|
-
log(
|
|
267
|
-
WARN,
|
|
268
|
-
"`RecordSet.metrics_records` has been deprecated "
|
|
269
|
-
"and will be removed in a future release. Please use "
|
|
270
|
-
"`RecordDict.metric_records` instead.",
|
|
271
|
-
)
|
|
272
|
-
return self.metric_records
|
|
273
|
-
|
|
274
|
-
@property
|
|
275
|
-
def configs_records(self) -> TypedDict[str, ConfigRecord]:
|
|
276
|
-
"""Deprecated property.
|
|
277
|
-
|
|
278
|
-
Use ``config_records`` instead.
|
|
279
|
-
"""
|
|
280
|
-
if not RecordSet._warning_logged_configs:
|
|
281
|
-
RecordSet._warning_logged_configs = True
|
|
282
|
-
log(
|
|
283
|
-
WARN,
|
|
284
|
-
"`RecordSet.configs_records` has been deprecated "
|
|
285
|
-
"and will be removed in a future release. Please use "
|
|
286
|
-
"`RecordDict.config_records` instead.",
|
|
287
|
-
)
|
|
288
|
-
return self.config_records
|
|
400
|
+
super().__init__(
|
|
401
|
+
records,
|
|
402
|
+
parameters_records=parameters_records,
|
|
403
|
+
metrics_records=metrics_records,
|
|
404
|
+
configs_records=configs_records,
|
|
405
|
+
)
|
flwr/common/record/typeddict.py
CHANGED
flwr/common/recorddict_compat.py
CHANGED
|
@@ -111,12 +111,12 @@ def parameters_to_arrayrecord(parameters: Parameters, keep_input: bool) -> Array
|
|
|
111
111
|
else:
|
|
112
112
|
tensor = parameters.tensors.pop(0)
|
|
113
113
|
ordered_dict[str(idx)] = Array(
|
|
114
|
-
data=tensor, dtype="", stype=tensor_type, shape=
|
|
114
|
+
data=tensor, dtype="", stype=tensor_type, shape=()
|
|
115
115
|
)
|
|
116
116
|
|
|
117
117
|
if num_arrays == 0:
|
|
118
118
|
ordered_dict[EMPTY_TENSOR_KEY] = Array(
|
|
119
|
-
data=b"", dtype="", stype=tensor_type, shape=
|
|
119
|
+
data=b"", dtype="", stype=tensor_type, shape=()
|
|
120
120
|
)
|
|
121
121
|
return ArrayRecord(ordered_dict, keep_input=keep_input)
|
|
122
122
|
|
flwr/common/retry_invoker.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright
|
|
1
|
+
# Copyright 2025 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.
|
|
@@ -25,10 +25,12 @@ from typing import Any, Callable, Optional, Union, cast
|
|
|
25
25
|
|
|
26
26
|
import grpc
|
|
27
27
|
|
|
28
|
+
from flwr.client.grpc_rere_client.grpc_adapter import GrpcAdapter
|
|
28
29
|
from flwr.common.constant import MAX_RETRY_DELAY
|
|
29
30
|
from flwr.common.logger import log
|
|
30
31
|
from flwr.common.typing import RunNotRunningException
|
|
31
32
|
from flwr.proto.clientappio_pb2_grpc import ClientAppIoStub
|
|
33
|
+
from flwr.proto.fleet_pb2_grpc import FleetStub
|
|
32
34
|
from flwr.proto.serverappio_pb2_grpc import ServerAppIoStub
|
|
33
35
|
from flwr.proto.simulationio_pb2_grpc import SimulationIoStub
|
|
34
36
|
|
|
@@ -166,15 +168,15 @@ class RetryInvoker:
|
|
|
166
168
|
|
|
167
169
|
Examples
|
|
168
170
|
--------
|
|
169
|
-
Initialize a `RetryInvoker` with exponential backoff and invoke a function
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
171
|
+
Initialize a `RetryInvoker` with exponential backoff and invoke a function::
|
|
172
|
+
|
|
173
|
+
invoker = RetryInvoker(
|
|
174
|
+
exponential, # Or use `lambda: exponential(3, 2)` to pass arguments
|
|
175
|
+
grpc.RpcError,
|
|
176
|
+
max_tries=3,
|
|
177
|
+
max_time=None,
|
|
178
|
+
)
|
|
179
|
+
invoker.invoke(my_func, arg1, arg2, kw1=kwarg1)
|
|
178
180
|
"""
|
|
179
181
|
|
|
180
182
|
# pylint: disable-next=too-many-arguments
|
|
@@ -366,7 +368,9 @@ def _make_simple_grpc_retry_invoker() -> RetryInvoker:
|
|
|
366
368
|
|
|
367
369
|
|
|
368
370
|
def _wrap_stub(
|
|
369
|
-
stub: Union[
|
|
371
|
+
stub: Union[
|
|
372
|
+
ServerAppIoStub, ClientAppIoStub, SimulationIoStub, FleetStub, GrpcAdapter
|
|
373
|
+
],
|
|
370
374
|
retry_invoker: RetryInvoker,
|
|
371
375
|
) -> None:
|
|
372
376
|
"""Wrap a gRPC stub with a retry invoker."""
|