flwr-nightly 1.18.0.dev20250404__py3-none-any.whl → 1.18.0.dev20250408__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/cli/new/new.py +3 -3
- flwr/client/app.py +51 -51
- flwr/client/client_app.py +138 -136
- flwr/client/grpc_client/connection.py +12 -12
- flwr/client/mod/localdp_mod.py +5 -5
- flwr/common/exit/exit.py +6 -6
- flwr/common/record/arrayrecord.py +31 -31
- flwr/common/record/configrecord.py +13 -13
- flwr/common/record/metricrecord.py +16 -16
- flwr/common/record/recorddict.py +31 -31
- flwr/common/retry_invoker.py +9 -9
- flwr/server/app.py +10 -10
- flwr/server/grid/grid.py +1 -1
- flwr/server/server_app.py +64 -57
- flwr/server/strategy/dp_adaptive_clipping.py +16 -16
- flwr/server/strategy/dp_fixed_clipping.py +16 -16
- flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +12 -12
- {flwr_nightly-1.18.0.dev20250404.dist-info → flwr_nightly-1.18.0.dev20250408.dist-info}/METADATA +1 -1
- {flwr_nightly-1.18.0.dev20250404.dist-info → flwr_nightly-1.18.0.dev20250408.dist-info}/RECORD +21 -21
- {flwr_nightly-1.18.0.dev20250404.dist-info → flwr_nightly-1.18.0.dev20250408.dist-info}/WHEEL +0 -0
- {flwr_nightly-1.18.0.dev20250404.dist-info → flwr_nightly-1.18.0.dev20250408.dist-info}/entry_points.txt +0 -0
@@ -60,7 +60,7 @@ def _check_value(value: ConfigRecordValues) -> None:
|
|
60
60
|
|
61
61
|
|
62
62
|
class ConfigRecord(TypedDict[str, ConfigRecordValues]):
|
63
|
-
"""
|
63
|
+
"""Config record.
|
64
64
|
|
65
65
|
A :code:`ConfigRecord` is a Python dictionary designed to ensure that
|
66
66
|
each key-value pair adheres to specified data types. A :code:`ConfigRecord`
|
@@ -90,18 +90,18 @@ class ConfigRecord(TypedDict[str, ConfigRecordValues]):
|
|
90
90
|
encourage you to use a :code:`ArrayRecord` instead if these are of high
|
91
91
|
dimensionality.
|
92
92
|
|
93
|
-
Let's see some examples of how to construct a :code:`ConfigRecord` from scratch
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
93
|
+
Let's see some examples of how to construct a :code:`ConfigRecord` from scratch::
|
94
|
+
|
95
|
+
from flwr.common import ConfigRecord
|
96
|
+
|
97
|
+
# A `ConfigRecord` is a specialized Python dictionary
|
98
|
+
record = ConfigRecord({"lr": 0.1, "batch-size": 128})
|
99
|
+
# You can add more content to an existing record
|
100
|
+
record["compute-average"] = True
|
101
|
+
# It also supports lists
|
102
|
+
record["loss-fn-coefficients"] = [0.4, 0.25, 0.35]
|
103
|
+
# And string values (among other types)
|
104
|
+
record["path-to-S3"] = "s3://bucket_name/folder1/fileA.json"
|
105
105
|
|
106
106
|
Just like the other types of records in a :code:`flwr.common.RecordDict`, types are
|
107
107
|
enforced. If you need to add a custom data structure or object, we recommend to
|
@@ -60,7 +60,7 @@ def _check_value(value: MetricRecordValues) -> None:
|
|
60
60
|
|
61
61
|
|
62
62
|
class MetricRecord(TypedDict[str, MetricRecordValues]):
|
63
|
-
"""
|
63
|
+
"""Metric record.
|
64
64
|
|
65
65
|
A :code:`MetricRecord` is a Python dictionary designed to ensure that
|
66
66
|
each key-value pair adheres to specified data types. A :code:`MetricRecord`
|
@@ -89,27 +89,27 @@ class MetricRecord(TypedDict[str, MetricRecordValues]):
|
|
89
89
|
Common to these examples is that the output can be typically represented by
|
90
90
|
a single scalar (:code:`int`, :code:`float`) or list of scalars.
|
91
91
|
|
92
|
-
Let's see some examples of how to construct a :code:`MetricRecord` from scratch
|
92
|
+
Let's see some examples of how to construct a :code:`MetricRecord` from scratch::
|
93
93
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
94
|
+
from flwr.common import MetricRecord
|
95
|
+
|
96
|
+
# A `MetricRecord` is a specialized Python dictionary
|
97
|
+
record = MetricRecord({"accuracy": 0.94})
|
98
|
+
# You can add more content to an existing record
|
99
|
+
record["loss"] = 0.01
|
100
|
+
# It also supports lists
|
101
|
+
record["loss-historic"] = [0.9, 0.5, 0.01]
|
102
102
|
|
103
103
|
Since types are enforced, the types of the objects inserted are checked. For a
|
104
104
|
:code:`MetricRecord`, value types allowed are those in defined in
|
105
105
|
:code:`flwr.common.MetricRecordValues`. Similarly, only :code:`str` keys are
|
106
|
-
allowed
|
106
|
+
allowed::
|
107
|
+
|
108
|
+
from flwr.common import MetricRecord
|
107
109
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
>>> # Add unsupported value
|
112
|
-
>>> record["something-unsupported"] = {'a': 123} # Will throw a `TypeError`
|
110
|
+
record = MetricRecord() # an empty record
|
111
|
+
# Add unsupported value
|
112
|
+
record["something-unsupported"] = {'a': 123} # Will throw a `TypeError`
|
113
113
|
|
114
114
|
If you need a more versatily type of record try :code:`ConfigRecord` or
|
115
115
|
:code:`ArrayRecord`.
|
flwr/common/record/recorddict.py
CHANGED
@@ -103,40 +103,40 @@ class RecordDict(TypedDict[str, RecordType]):
|
|
103
103
|
are Python dictionaries designed to ensure that each key-value pair
|
104
104
|
adheres to specified data types.
|
105
105
|
|
106
|
-
Let's see an example
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
106
|
+
Let's see an example::
|
107
|
+
|
108
|
+
from flwr.common import RecordDict
|
109
|
+
from flwr.common import ArrayRecord, ConfigRecord, MetricRecord
|
110
|
+
|
111
|
+
# Let's begin with an empty record
|
112
|
+
my_records = RecordDict()
|
113
|
+
|
114
|
+
# We can create a ConfigRecord
|
115
|
+
c_record = ConfigRecord({"lr": 0.1, "batch-size": 128})
|
116
|
+
# Adding it to the RecordDict would look like this
|
117
|
+
my_records["my_config"] = c_record
|
118
|
+
|
119
|
+
# We can create a MetricRecord following a similar process
|
120
|
+
m_record = MetricRecord({"accuracy": 0.93, "losses": [0.23, 0.1]})
|
121
|
+
# Adding it to the RecordDict would look like this
|
122
|
+
my_records["my_metrics"] = m_record
|
123
123
|
|
124
124
|
Adding an :code:`ArrayRecord` follows the same steps as above but first,
|
125
125
|
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
|
-
|
126
|
+
For example::
|
127
|
+
|
128
|
+
from flwr.common import Array
|
129
|
+
# Creating an ArrayRecord would look like this
|
130
|
+
arr_np = np.random.randn(3, 3)
|
131
|
+
|
132
|
+
# You can use the built-in tool to serialize the array
|
133
|
+
arr = Array(arr_np)
|
134
|
+
|
135
|
+
# Finally, create the record
|
136
|
+
arr_record = ArrayRecord({"my_array": arr})
|
137
|
+
|
138
|
+
# Adding it to the RecordDict would look like this
|
139
|
+
my_records["my_parameters"] = arr_record
|
140
140
|
|
141
141
|
For additional examples on how to construct each of the records types shown
|
142
142
|
above, please refer to the documentation for :code:`ConfigRecord`,
|
flwr/common/retry_invoker.py
CHANGED
@@ -166,15 +166,15 @@ class RetryInvoker:
|
|
166
166
|
|
167
167
|
Examples
|
168
168
|
--------
|
169
|
-
Initialize a `RetryInvoker` with exponential backoff and invoke a function
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
169
|
+
Initialize a `RetryInvoker` with exponential backoff and invoke a function::
|
170
|
+
|
171
|
+
invoker = RetryInvoker(
|
172
|
+
exponential, # Or use `lambda: exponential(3, 2)` to pass arguments
|
173
|
+
grpc.RpcError,
|
174
|
+
max_tries=3,
|
175
|
+
max_time=None,
|
176
|
+
)
|
177
|
+
invoker.invoke(my_func, arg1, arg2, kw1=kwarg1)
|
178
178
|
"""
|
179
179
|
|
180
180
|
# pylint: disable-next=too-many-arguments
|
flwr/server/app.py
CHANGED
@@ -183,19 +183,19 @@ def start_server( # pylint: disable=too-many-arguments,too-many-locals
|
|
183
183
|
|
184
184
|
Examples
|
185
185
|
--------
|
186
|
-
Starting an insecure server
|
186
|
+
Starting an insecure server::
|
187
187
|
|
188
|
-
|
188
|
+
start_server()
|
189
189
|
|
190
|
-
Starting
|
190
|
+
Starting a TLS-enabled server::
|
191
191
|
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
192
|
+
start_server(
|
193
|
+
certificates=(
|
194
|
+
Path("/crts/root.pem").read_bytes(),
|
195
|
+
Path("/crts/localhost.crt").read_bytes(),
|
196
|
+
Path("/crts/localhost.key").read_bytes()
|
197
|
+
)
|
198
|
+
)
|
199
199
|
"""
|
200
200
|
msg = (
|
201
201
|
"flwr.server.start_server() is deprecated."
|
flwr/server/grid/grid.py
CHANGED
flwr/server/server_app.py
CHANGED
@@ -52,6 +52,30 @@ GRID_USAGE_EXAMPLE = """
|
|
52
52
|
# Your existing ServerApp code ...
|
53
53
|
"""
|
54
54
|
|
55
|
+
BOTH_MAIN_FN_SERVER_FN_PROVIDED_ERROR_MSG = (
|
56
|
+
"Use either a custom main function or a `Strategy`, but not both."
|
57
|
+
"""
|
58
|
+
|
59
|
+
Use the `ServerApp` with an existing `Strategy`:
|
60
|
+
|
61
|
+
server_config = ServerConfig(num_rounds=3)
|
62
|
+
strategy = FedAvg()
|
63
|
+
|
64
|
+
app = ServerApp(
|
65
|
+
server_config=server_config,
|
66
|
+
strategy=strategy,
|
67
|
+
)
|
68
|
+
|
69
|
+
Use the `ServerApp` with a custom main function:
|
70
|
+
|
71
|
+
app = ServerApp()
|
72
|
+
|
73
|
+
@app.main()
|
74
|
+
def main(grid: Grid, context: Context) -> None:
|
75
|
+
print("ServerApp running")
|
76
|
+
"""
|
77
|
+
)
|
78
|
+
|
55
79
|
DRIVER_DEPRECATION_MSG = """
|
56
80
|
The `Driver` class is deprecated, it will be removed in a future release.
|
57
81
|
"""
|
@@ -70,25 +94,25 @@ class ServerApp: # pylint: disable=too-many-instance-attributes
|
|
70
94
|
|
71
95
|
Examples
|
72
96
|
--------
|
73
|
-
Use the ``ServerApp`` with an existing ``Strategy
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
Use the ``ServerApp`` with a custom main function
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
97
|
+
Use the ``ServerApp`` with an existing ``Strategy``::
|
98
|
+
|
99
|
+
def server_fn(context: Context):
|
100
|
+
server_config = ServerConfig(num_rounds=3)
|
101
|
+
strategy = FedAvg()
|
102
|
+
return ServerAppComponents(
|
103
|
+
strategy=strategy,
|
104
|
+
server_config=server_config,
|
105
|
+
)
|
106
|
+
|
107
|
+
app = ServerApp(server_fn=server_fn)
|
108
|
+
|
109
|
+
Use the ``ServerApp`` with a custom main function::
|
110
|
+
|
111
|
+
app = ServerApp()
|
112
|
+
|
113
|
+
@app.main()
|
114
|
+
def main(grid: Grid, context: Context) -> None:
|
115
|
+
print("ServerApp running")
|
92
116
|
"""
|
93
117
|
|
94
118
|
# pylint: disable=too-many-arguments,too-many-positional-arguments
|
@@ -156,38 +180,19 @@ class ServerApp: # pylint: disable=too-many-instance-attributes
|
|
156
180
|
|
157
181
|
Examples
|
158
182
|
--------
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
183
|
+
::
|
184
|
+
|
185
|
+
app = ServerApp()
|
186
|
+
|
187
|
+
@app.main()
|
188
|
+
def main(grid: Grid, context: Context) -> None:
|
189
|
+
print("ServerApp running")
|
164
190
|
"""
|
165
191
|
|
166
192
|
def main_decorator(main_fn: ServerAppCallable) -> ServerAppCallable:
|
167
193
|
"""Register the main fn with the ServerApp object."""
|
168
194
|
if self._server or self._config or self._strategy or self._client_manager:
|
169
|
-
raise ValueError(
|
170
|
-
"""Use either a custom main function or a `Strategy`, but not both.
|
171
|
-
|
172
|
-
Use the `ServerApp` with an existing `Strategy`:
|
173
|
-
|
174
|
-
>>> server_config = ServerConfig(num_rounds=3)
|
175
|
-
>>> strategy = FedAvg()
|
176
|
-
>>>
|
177
|
-
>>> app = ServerApp(
|
178
|
-
>>> server_config=server_config,
|
179
|
-
>>> strategy=strategy,
|
180
|
-
>>> )
|
181
|
-
|
182
|
-
Use the `ServerApp` with a custom main function:
|
183
|
-
|
184
|
-
>>> app = ServerApp()
|
185
|
-
>>>
|
186
|
-
>>> @app.main()
|
187
|
-
>>> def main(grid: Grid, context: Context) -> None:
|
188
|
-
>>> print("ServerApp running")
|
189
|
-
""",
|
190
|
-
)
|
195
|
+
raise ValueError(BOTH_MAIN_FN_SERVER_FN_PROVIDED_ERROR_MSG)
|
191
196
|
|
192
197
|
sig = inspect.signature(main_fn)
|
193
198
|
param = list(sig.parameters.values())[0]
|
@@ -219,17 +224,19 @@ class ServerApp: # pylint: disable=too-many-instance-attributes
|
|
219
224
|
|
220
225
|
Examples
|
221
226
|
--------
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
227
|
+
::
|
228
|
+
|
229
|
+
app = ServerApp()
|
230
|
+
|
231
|
+
@app.lifespan()
|
232
|
+
def lifespan(context: Context) -> None:
|
233
|
+
# Perform initialization tasks before the app starts
|
234
|
+
print("Initializing ServerApp")
|
235
|
+
|
236
|
+
yield # ServerApp is running
|
237
|
+
|
238
|
+
# Perform cleanup tasks after the app stops
|
239
|
+
print("Cleaning up ServerApp")
|
233
240
|
"""
|
234
241
|
|
235
242
|
def lifespan_decorator(
|
@@ -77,15 +77,15 @@ class DifferentialPrivacyServerSideAdaptiveClipping(Strategy):
|
|
77
77
|
|
78
78
|
Examples
|
79
79
|
--------
|
80
|
-
Create a strategy
|
80
|
+
Create a strategy::
|
81
81
|
|
82
|
-
|
82
|
+
strategy = fl.server.strategy.FedAvg( ... )
|
83
83
|
|
84
|
-
Wrap the strategy with the DifferentialPrivacyServerSideAdaptiveClipping wrapper
|
84
|
+
Wrap the strategy with the DifferentialPrivacyServerSideAdaptiveClipping wrapper::
|
85
85
|
|
86
|
-
|
87
|
-
|
88
|
-
|
86
|
+
dp_strategy = DifferentialPrivacyServerSideAdaptiveClipping(
|
87
|
+
strategy, cfg.noise_multiplier, cfg.num_sampled_clients, ...
|
88
|
+
)
|
89
89
|
"""
|
90
90
|
|
91
91
|
# pylint: disable=too-many-arguments,too-many-instance-attributes,too-many-positional-arguments
|
@@ -290,21 +290,21 @@ class DifferentialPrivacyClientSideAdaptiveClipping(Strategy):
|
|
290
290
|
|
291
291
|
Examples
|
292
292
|
--------
|
293
|
-
Create a strategy
|
293
|
+
Create a strategy::
|
294
294
|
|
295
|
-
|
295
|
+
strategy = fl.server.strategy.FedAvg(...)
|
296
296
|
|
297
|
-
Wrap the strategy with the `DifferentialPrivacyClientSideAdaptiveClipping` wrapper
|
297
|
+
Wrap the strategy with the `DifferentialPrivacyClientSideAdaptiveClipping` wrapper::
|
298
298
|
|
299
|
-
|
300
|
-
|
301
|
-
|
299
|
+
dp_strategy = DifferentialPrivacyClientSideAdaptiveClipping(
|
300
|
+
strategy, cfg.noise_multiplier, cfg.num_sampled_clients
|
301
|
+
)
|
302
302
|
|
303
|
-
On the client, add the `adaptiveclipping_mod` to the client-side mods
|
303
|
+
On the client, add the `adaptiveclipping_mod` to the client-side mods::
|
304
304
|
|
305
|
-
|
306
|
-
|
307
|
-
|
305
|
+
app = fl.client.ClientApp(
|
306
|
+
client_fn=client_fn, mods=[adaptiveclipping_mod]
|
307
|
+
)
|
308
308
|
"""
|
309
309
|
|
310
310
|
# pylint: disable=too-many-arguments,too-many-instance-attributes,too-many-positional-arguments
|
@@ -64,15 +64,15 @@ class DifferentialPrivacyServerSideFixedClipping(Strategy):
|
|
64
64
|
|
65
65
|
Examples
|
66
66
|
--------
|
67
|
-
Create a strategy
|
67
|
+
Create a strategy::
|
68
68
|
|
69
|
-
|
69
|
+
strategy = fl.server.strategy.FedAvg( ... )
|
70
70
|
|
71
|
-
Wrap the strategy with the DifferentialPrivacyServerSideFixedClipping wrapper
|
71
|
+
Wrap the strategy with the DifferentialPrivacyServerSideFixedClipping wrapper::
|
72
72
|
|
73
|
-
|
74
|
-
|
75
|
-
|
73
|
+
dp_strategy = DifferentialPrivacyServerSideFixedClipping(
|
74
|
+
strategy, cfg.noise_multiplier, cfg.clipping_norm, cfg.num_sampled_clients
|
75
|
+
)
|
76
76
|
"""
|
77
77
|
|
78
78
|
# pylint: disable=too-many-arguments,too-many-instance-attributes
|
@@ -228,21 +228,21 @@ class DifferentialPrivacyClientSideFixedClipping(Strategy):
|
|
228
228
|
|
229
229
|
Examples
|
230
230
|
--------
|
231
|
-
Create a strategy
|
231
|
+
Create a strategy::
|
232
232
|
|
233
|
-
|
233
|
+
strategy = fl.server.strategy.FedAvg(...)
|
234
234
|
|
235
|
-
Wrap the strategy with the `DifferentialPrivacyClientSideFixedClipping` wrapper
|
235
|
+
Wrap the strategy with the `DifferentialPrivacyClientSideFixedClipping` wrapper::
|
236
236
|
|
237
|
-
|
238
|
-
|
239
|
-
|
237
|
+
dp_strategy = DifferentialPrivacyClientSideFixedClipping(
|
238
|
+
strategy, cfg.noise_multiplier, cfg.clipping_norm, cfg.num_sampled_clients
|
239
|
+
)
|
240
240
|
|
241
|
-
On the client, add the `fixedclipping_mod` to the client-side mods
|
241
|
+
On the client, add the `fixedclipping_mod` to the client-side mods::
|
242
242
|
|
243
|
-
|
244
|
-
|
245
|
-
|
243
|
+
app = fl.client.ClientApp(
|
244
|
+
client_fn=client_fn, mods=[fixedclipping_mod]
|
245
|
+
)
|
246
246
|
"""
|
247
247
|
|
248
248
|
# pylint: disable=too-many-arguments,too-many-instance-attributes
|
@@ -96,18 +96,18 @@ def start_grpc_server( # pylint: disable=too-many-arguments,R0917
|
|
96
96
|
|
97
97
|
Examples
|
98
98
|
--------
|
99
|
-
Starting a
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
99
|
+
Starting a TLS-enabled server::
|
100
|
+
|
101
|
+
from pathlib import Path
|
102
|
+
start_grpc_server(
|
103
|
+
client_manager=ClientManager(),
|
104
|
+
server_address="localhost:8080",
|
105
|
+
certificates=(
|
106
|
+
Path("/crts/root.pem").read_bytes(),
|
107
|
+
Path("/crts/localhost.crt").read_bytes(),
|
108
|
+
Path("/crts/localhost.key").read_bytes(),
|
109
|
+
),
|
110
|
+
)
|
111
111
|
"""
|
112
112
|
servicer = FlowerServiceServicer(client_manager)
|
113
113
|
add_servicer_to_server_fn = add_FlowerServiceServicer_to_server
|