flwr-nightly 1.5.0.dev20230614__py3-none-any.whl → 1.5.0.dev20230615__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- flwr/client/dpfedavg_numpy_client.py +97 -0
- flwr/driver/driver_client_manager.py +9 -0
- flwr/server/state/in_memory_state.py +8 -0
- flwr/server/strategy/dpfedavg_adaptive.py +1 -0
- flwr/server/strategy/dpfedavg_fixed.py +46 -1
- flwr/server/utils/tensorboard.py +1 -2
- {flwr_nightly-1.5.0.dev20230614.dist-info → flwr_nightly-1.5.0.dev20230615.dist-info}/METADATA +1 -1
- {flwr_nightly-1.5.0.dev20230614.dist-info → flwr_nightly-1.5.0.dev20230615.dist-info}/RECORD +11 -11
- {flwr_nightly-1.5.0.dev20230614.dist-info → flwr_nightly-1.5.0.dev20230615.dist-info}/LICENSE +0 -0
- {flwr_nightly-1.5.0.dev20230614.dist-info → flwr_nightly-1.5.0.dev20230615.dist-info}/WHEEL +0 -0
- {flwr_nightly-1.5.0.dev20230614.dist-info → flwr_nightly-1.5.0.dev20230615.dist-info}/entry_points.txt +0 -0
@@ -33,14 +33,82 @@ class DPFedAvgNumPyClient(NumPyClient):
|
|
33
33
|
self.client = client
|
34
34
|
|
35
35
|
def get_properties(self, config: Config) -> Dict[str, Scalar]:
|
36
|
+
"""Get client properties using the given Numpy client.
|
37
|
+
|
38
|
+
Parameters
|
39
|
+
----------
|
40
|
+
config : Config
|
41
|
+
Configuration parameters requested by the server.
|
42
|
+
This can be used to tell the client which properties
|
43
|
+
are needed along with some Scalar attributes.
|
44
|
+
|
45
|
+
Returns
|
46
|
+
-------
|
47
|
+
properties : Dict[str, Scalar]
|
48
|
+
A dictionary mapping arbitrary string keys to values of type
|
49
|
+
bool, bytes, float, int, or str. It can be used to communicate
|
50
|
+
arbitrary property values back to the server.
|
51
|
+
"""
|
36
52
|
return self.client.get_properties(config)
|
37
53
|
|
38
54
|
def get_parameters(self, config: Dict[str, Scalar]) -> NDArrays:
|
55
|
+
"""Return the current local model parameters.
|
56
|
+
|
57
|
+
Parameters
|
58
|
+
----------
|
59
|
+
config : Config
|
60
|
+
Configuration parameters requested by the server.
|
61
|
+
This can be used to tell the client which parameters
|
62
|
+
are needed along with some Scalar attributes.
|
63
|
+
|
64
|
+
Returns
|
65
|
+
-------
|
66
|
+
parameters : NDArrays
|
67
|
+
The local model parameters as a list of NumPy ndarrays.
|
68
|
+
"""
|
39
69
|
return self.client.get_parameters(config)
|
40
70
|
|
41
71
|
def fit(
|
42
72
|
self, parameters: NDArrays, config: Dict[str, Scalar]
|
43
73
|
) -> Tuple[NDArrays, int, Dict[str, Scalar]]:
|
74
|
+
"""Train the provided parameters using the locally held dataset.
|
75
|
+
|
76
|
+
This method first updates the local model using the original parameters
|
77
|
+
provided. It then calculates the update by subtracting the original
|
78
|
+
parameters from the updated model. The update is then clipped by an L2
|
79
|
+
norm and Gaussian noise is added if specified by the configuration.
|
80
|
+
|
81
|
+
The update is then applied to the original parameters to obtain the
|
82
|
+
updated parameters which are returned along with the number of examples
|
83
|
+
used and metrics computed during the fitting process.
|
84
|
+
|
85
|
+
Parameters
|
86
|
+
----------
|
87
|
+
parameters : NDArrays
|
88
|
+
The current (global) model parameters.
|
89
|
+
config : Dict[str, Scalar]
|
90
|
+
Configuration parameters which allow the
|
91
|
+
server to influence training on the client. It can be used to
|
92
|
+
communicate arbitrary values from the server to the client, for
|
93
|
+
example, to set the number of (local) training epochs.
|
94
|
+
|
95
|
+
Returns
|
96
|
+
-------
|
97
|
+
parameters : NDArrays
|
98
|
+
The locally updated model parameters.
|
99
|
+
num_examples : int
|
100
|
+
The number of examples used for training.
|
101
|
+
metrics : Dict[str, Scalar]
|
102
|
+
A dictionary mapping arbitrary string keys to values of type
|
103
|
+
bool, bytes, float, int, or str. It can be used to communicate
|
104
|
+
arbitrary values back to the server.
|
105
|
+
|
106
|
+
Raises
|
107
|
+
------
|
108
|
+
Exception
|
109
|
+
If any required configuration parameters are not provided or are of
|
110
|
+
the wrong type.
|
111
|
+
"""
|
44
112
|
original_params = copy.deepcopy(parameters)
|
45
113
|
# Getting the updated model from the wrapped client
|
46
114
|
updated_params, num_examples, metrics = self.client.fit(parameters, config)
|
@@ -80,4 +148,33 @@ class DPFedAvgNumPyClient(NumPyClient):
|
|
80
148
|
def evaluate(
|
81
149
|
self, parameters: NDArrays, config: Dict[str, Scalar]
|
82
150
|
) -> Tuple[float, int, Dict[str, Scalar]]:
|
151
|
+
"""Evaluate the provided parameters using the locally held dataset.
|
152
|
+
|
153
|
+
Parameters
|
154
|
+
----------
|
155
|
+
parameters : NDArrays
|
156
|
+
The current (global) model parameters.
|
157
|
+
config : Dict[str, Scalar]
|
158
|
+
Configuration parameters which allow the server to influence
|
159
|
+
evaluation on the client. It can be used to communicate
|
160
|
+
arbitrary values from the server to the client, for example,
|
161
|
+
to influence the number of examples used for evaluation.
|
162
|
+
|
163
|
+
Returns
|
164
|
+
-------
|
165
|
+
loss : float
|
166
|
+
The evaluation loss of the model on the local dataset.
|
167
|
+
num_examples : int
|
168
|
+
The number of examples used for evaluation.
|
169
|
+
metrics : Dict[str, Scalar]
|
170
|
+
A dictionary mapping arbitrary string keys to values of
|
171
|
+
type bool, bytes, float, int, or str. It can be used to
|
172
|
+
communicate arbitrary values back to the server.
|
173
|
+
|
174
|
+
Warning
|
175
|
+
-------
|
176
|
+
The previous return type format (int, float, float) and the
|
177
|
+
extended format (int, float, float, Dict[str, Scalar]) have been
|
178
|
+
deprecated and removed since Flower 0.19.
|
179
|
+
"""
|
83
180
|
return self.client.evaluate(parameters, config)
|
@@ -85,10 +85,12 @@ class DriverClientManager(ClientManager):
|
|
85
85
|
raise NotImplementedError("DriverClientManager.unregister is not implemented")
|
86
86
|
|
87
87
|
def all(self) -> Dict[str, ClientProxy]:
|
88
|
+
"""Return all available clients."""
|
88
89
|
self._update_nodes()
|
89
90
|
return self.clients
|
90
91
|
|
91
92
|
def wait_for(self, num_clients: int, timeout: int = 86400) -> bool:
|
93
|
+
"""Wait until at least `num_clients` are available."""
|
92
94
|
start_time = time.time()
|
93
95
|
while time.time() < start_time + timeout:
|
94
96
|
self._update_nodes()
|
@@ -103,6 +105,7 @@ class DriverClientManager(ClientManager):
|
|
103
105
|
min_num_clients: Optional[int] = None,
|
104
106
|
criterion: Optional[Criterion] = None,
|
105
107
|
) -> List[ClientProxy]:
|
108
|
+
"""Sample a number of Flower ClientProxy instances."""
|
106
109
|
if min_num_clients is None:
|
107
110
|
min_num_clients = num_clients
|
108
111
|
self.wait_for(min_num_clients)
|
@@ -128,6 +131,12 @@ class DriverClientManager(ClientManager):
|
|
128
131
|
return [self.clients[cid] for cid in sampled_cids]
|
129
132
|
|
130
133
|
def _update_nodes(self) -> None:
|
134
|
+
"""Update the nodes list in the client manager.
|
135
|
+
|
136
|
+
This method communicates with the associated driver to get all node ids. Each
|
137
|
+
node id is then converted into a `DriverClientProxy` instance and stored in the
|
138
|
+
`clients` dictionary with node id as key.
|
139
|
+
"""
|
131
140
|
get_nodes_res = self.driver.get_nodes(req=driver_pb2.GetNodesRequest())
|
132
141
|
all_node_ids = get_nodes_res.node_ids
|
133
142
|
for node_id in all_node_ids:
|
@@ -158,9 +158,17 @@ class InMemoryState(State):
|
|
158
158
|
del self.task_res_store[task_id]
|
159
159
|
|
160
160
|
def num_task_ins(self) -> int:
|
161
|
+
"""Calculate the number of task_ins in store.
|
162
|
+
|
163
|
+
This includes delivered but not yet deleted task_ins.
|
164
|
+
"""
|
161
165
|
return len(self.task_ins_store)
|
162
166
|
|
163
167
|
def num_task_res(self) -> int:
|
168
|
+
"""Calculate the number of task_res in store.
|
169
|
+
|
170
|
+
This includes delivered but not yet deleted task_res.
|
171
|
+
"""
|
164
172
|
return len(self.task_res_store)
|
165
173
|
|
166
174
|
def register_node(self, node_id: int) -> None:
|
@@ -113,6 +113,7 @@ class DPFedAvgAdaptive(DPFedAvgFixed):
|
|
113
113
|
results: List[Tuple[ClientProxy, FitRes]],
|
114
114
|
failures: List[Union[Tuple[ClientProxy, FitRes], BaseException]],
|
115
115
|
) -> Tuple[Optional[Parameters], Dict[str, Scalar]]:
|
116
|
+
"""Aggregate training results as in DPFedAvgFixed and update clip norms."""
|
116
117
|
if failures:
|
117
118
|
return None, {}
|
118
119
|
new_global_model = super().aggregate_fit(server_round, results, failures)
|
@@ -67,12 +67,34 @@ class DPFedAvgFixed(Strategy):
|
|
67
67
|
def initialize_parameters(
|
68
68
|
self, client_manager: ClientManager
|
69
69
|
) -> Optional[Parameters]:
|
70
|
+
"""Initialize global model parameters using given strategy."""
|
70
71
|
return self.strategy.initialize_parameters(client_manager)
|
71
72
|
|
72
73
|
def configure_fit(
|
73
74
|
self, server_round: int, parameters: Parameters, client_manager: ClientManager
|
74
75
|
) -> List[Tuple[ClientProxy, FitIns]]:
|
75
|
-
"""Configure the next round of training.
|
76
|
+
"""Configure the next round of training incorporating Differential Privacy (DP).
|
77
|
+
|
78
|
+
Configuration of the next training round includes information related to DP,
|
79
|
+
such as clip norm and noise stddev.
|
80
|
+
|
81
|
+
Parameters
|
82
|
+
----------
|
83
|
+
server_round : int
|
84
|
+
The current round of federated learning.
|
85
|
+
parameters : Parameters
|
86
|
+
The current (global) model parameters.
|
87
|
+
client_manager : ClientManager
|
88
|
+
The client manager which holds all currently connected clients.
|
89
|
+
|
90
|
+
Returns
|
91
|
+
-------
|
92
|
+
fit_configuration : List[Tuple[ClientProxy, FitIns]]
|
93
|
+
A list of tuples. Each tuple in the list identifies a `ClientProxy` and the
|
94
|
+
`FitIns` for this particular `ClientProxy`. If a particular `ClientProxy`
|
95
|
+
is not included in this list, it means that this `ClientProxy`
|
96
|
+
will not participate in the next round of federated learning.
|
97
|
+
"""
|
76
98
|
additional_config = {"dpfedavg_clip_norm": self.clip_norm}
|
77
99
|
if not self.server_side_noising:
|
78
100
|
additional_config[
|
@@ -91,6 +113,26 @@ class DPFedAvgFixed(Strategy):
|
|
91
113
|
def configure_evaluate(
|
92
114
|
self, server_round: int, parameters: Parameters, client_manager: ClientManager
|
93
115
|
) -> List[Tuple[ClientProxy, EvaluateIns]]:
|
116
|
+
"""Configure the next round of evaluation using the specified strategy.
|
117
|
+
|
118
|
+
Parameters
|
119
|
+
----------
|
120
|
+
server_round : int
|
121
|
+
The current round of federated learning.
|
122
|
+
parameters : Parameters
|
123
|
+
The current (global) model parameters.
|
124
|
+
client_manager : ClientManager
|
125
|
+
The client manager which holds all currently connected clients.
|
126
|
+
|
127
|
+
Returns
|
128
|
+
-------
|
129
|
+
evaluate_configuration : List[Tuple[ClientProxy, EvaluateIns]]
|
130
|
+
A list of tuples. Each tuple in the list identifies a `ClientProxy` and the
|
131
|
+
`EvaluateIns` for this particular `ClientProxy`. If a particular
|
132
|
+
`ClientProxy` is not included in this list, it means that this
|
133
|
+
`ClientProxy` will not participate in the next round of federated
|
134
|
+
evaluation.
|
135
|
+
"""
|
94
136
|
return self.strategy.configure_evaluate(
|
95
137
|
server_round, parameters, client_manager
|
96
138
|
)
|
@@ -101,6 +143,7 @@ class DPFedAvgFixed(Strategy):
|
|
101
143
|
results: List[Tuple[ClientProxy, FitRes]],
|
102
144
|
failures: List[Union[Tuple[ClientProxy, FitRes], BaseException]],
|
103
145
|
) -> Tuple[Optional[Parameters], Dict[str, Scalar]]:
|
146
|
+
"""Aggregate training results using unweighted aggregation."""
|
104
147
|
if failures:
|
105
148
|
return None, {}
|
106
149
|
# Forcing unweighted aggregation, as in https://arxiv.org/abs/1905.03871.
|
@@ -121,9 +164,11 @@ class DPFedAvgFixed(Strategy):
|
|
121
164
|
results: List[Tuple[ClientProxy, EvaluateRes]],
|
122
165
|
failures: List[Union[Tuple[ClientProxy, EvaluateRes], BaseException]],
|
123
166
|
) -> Tuple[Optional[float], Dict[str, Scalar]]:
|
167
|
+
"""Aggregate evaluation losses using the given strategy."""
|
124
168
|
return self.strategy.aggregate_evaluate(server_round, results, failures)
|
125
169
|
|
126
170
|
def evaluate(
|
127
171
|
self, server_round: int, parameters: Parameters
|
128
172
|
) -> Optional[Tuple[float, Dict[str, Scalar]]]:
|
173
|
+
"""Evaluate model parameters using an evaluation function from the strategy."""
|
129
174
|
return self.strategy.evaluate(server_round, parameters)
|
flwr/server/utils/tensorboard.py
CHANGED
@@ -73,8 +73,7 @@ def tensorboard(logdir: str) -> Callable[[Strategy], Strategy]:
|
|
73
73
|
"""Return overloaded Strategy Wrapper."""
|
74
74
|
|
75
75
|
class TBWrapper(strategy_class): # type: ignore
|
76
|
-
"""Strategy wrapper
|
77
|
-
logging."""
|
76
|
+
"""Strategy wrapper that hooks into some methods for TensorBoard logging."""
|
78
77
|
|
79
78
|
def aggregate_evaluate(
|
80
79
|
self,
|
{flwr_nightly-1.5.0.dev20230614.dist-info → flwr_nightly-1.5.0.dev20230615.dist-info}/RECORD
RENAMED
@@ -2,7 +2,7 @@ flwr/__init__.py,sha256=kuBqHEfjUCwKVS60acHjXOEQhxfqg5S3ldIh3FJ4VYk,952
|
|
2
2
|
flwr/client/__init__.py,sha256=UkiAEKds0mtpmVTZvvpJHKfTC_VWzflwL5IQUdbAf6U,1164
|
3
3
|
flwr/client/app.py,sha256=0pRrsuqThwOGFv8tBEV-7YU_1t4mLQCMCVG2nGzyvq0,13835
|
4
4
|
flwr/client/client.py,sha256=-jPs28BQC3h5QFahPEHn1zHdiy6fTyk7TG9NS5Nw_e8,7677
|
5
|
-
flwr/client/dpfedavg_numpy_client.py,sha256=
|
5
|
+
flwr/client/dpfedavg_numpy_client.py,sha256=5mOZuwdnwYtoytlxD0RsVLcIENkFM7a5amScSBzVpDw,7209
|
6
6
|
flwr/client/grpc_client/__init__.py,sha256=svcQQhSaBJujNfZevHBsQrMKmqggpq-3I8Y6FUm_trM,728
|
7
7
|
flwr/client/grpc_client/connection.py,sha256=SyQRnlfdXkuoc9aYXbwRU6GlPDwmNB5O_u_JMxCzO6Q,4293
|
8
8
|
flwr/client/grpc_rere_client/__init__.py,sha256=9v4H6EumqwZePsY9icP0WdVSJ4UzH1t9MRyYX-a0--Q,745
|
@@ -28,7 +28,7 @@ flwr/common/version.py,sha256=lSE8kmuIEtB4E9Es2JmLi0W6CMn3YB74L_-VQyGNknI,848
|
|
28
28
|
flwr/driver/__init__.py,sha256=G9gvW60tQNN_yfXHr5gqwR5p_LSitE1r4I0GquysHZo,809
|
29
29
|
flwr/driver/app.py,sha256=BSFOBQpIS_3gSpfZ-D_McuuYac0-gG13h7mBrz_QExI,4826
|
30
30
|
flwr/driver/driver.py,sha256=XpdgPMLbWzKIPS3g0PW9o8eU8-rIFD5mMG3lTKs-eTI,3906
|
31
|
-
flwr/driver/driver_client_manager.py,sha256=
|
31
|
+
flwr/driver/driver_client_manager.py,sha256=Xt4HxI88DVv7Oq1OPPtn0ceWJo2at_dc06nlmDYdL4Q,4877
|
32
32
|
flwr/driver/driver_client_proxy.py,sha256=tpMb8Ti-OV0Qv-TwfCzG6Lt9IYRHE3JuZt3SnRKtV0k,5653
|
33
33
|
flwr/proto/__init__.py,sha256=fg57BzxAeODasVkv19rCvp5UMA0Gh8E2HVhw3ietB6Q,676
|
34
34
|
flwr/proto/driver_pb2.py,sha256=aQSzL3aUlyeazgt_JKfP64D-1pI1uz9QoCAlcH_2uNU,4663
|
@@ -76,14 +76,14 @@ flwr/server/fleet/rest_rere/rest_api.py,sha256=4FYkB7aGC7qR5Qmdgbkm_FzYW0pWw3EYp
|
|
76
76
|
flwr/server/history.py,sha256=xI7LfjV67VQZkKoHRpzILG1zKb_a0pMyL2vpn15BsxM,4897
|
77
77
|
flwr/server/server.py,sha256=amrh_Xpfkq8EQdnDubDn3NPyIuv8-SIm6wZ8bhdb9mE,15958
|
78
78
|
flwr/server/state/__init__.py,sha256=jcTV_f7T8w72NWkAY9kNPFis-FOd3Zm5jLE3CRmvOkE,996
|
79
|
-
flwr/server/state/in_memory_state.py,sha256=
|
79
|
+
flwr/server/state/in_memory_state.py,sha256=jnnQ5xmXbOISUxud2XJZC2U5AAX2_8AOsoTR2QXkg28,6774
|
80
80
|
flwr/server/state/sqlite_state.py,sha256=A25q1qioWo-_oP4ThM-CuLIH6FCTBRKPBKxbzn9_GAY,19296
|
81
81
|
flwr/server/state/state.py,sha256=nIzCzAzNwPrfXTltn1sroG6JN-Ti0PJAE4Mhhhm7lzU,4833
|
82
82
|
flwr/server/state/state_factory.py,sha256=nrHildx-4O6ko48TCHRwJSWg7ebAxCAqdENdcIlrSkA,1647
|
83
83
|
flwr/server/strategy/__init__.py,sha256=_y7B6IAmP7e6m09yNLtS26M8wc8JQLmuezgsKpdgxV4,1667
|
84
84
|
flwr/server/strategy/aggregate.py,sha256=lshQsPyTRx5VxxRGejLw1klsg4mtGcYQJtMVXJou3sY,6099
|
85
|
-
flwr/server/strategy/dpfedavg_adaptive.py,sha256=
|
86
|
-
flwr/server/strategy/dpfedavg_fixed.py,sha256=
|
85
|
+
flwr/server/strategy/dpfedavg_adaptive.py,sha256=ZqcVBgA1YZ0JCsXbxkgHNCef9xG3Ri1MhZoA7Pxk6xI,4591
|
86
|
+
flwr/server/strategy/dpfedavg_fixed.py,sha256=FCY0KaVvNZ81H-KN3HSujRG44Tu8uVOnGeDg-7v4dG0,6932
|
87
87
|
flwr/server/strategy/fault_tolerant_fedavg.py,sha256=nuDBxqXjH4Qo9BMgsxnvFMo3MNE-xsxdteVw-1QqSg4,5830
|
88
88
|
flwr/server/strategy/fedadagrad.py,sha256=Z-L2-C0IpdBO_sypKXakTMPdKNYOEHEoNXylky3Uwq4,6722
|
89
89
|
flwr/server/strategy/fedadam.py,sha256=sjpIfZankmvEBOZMiWvO4GlynxtoqMtaSiGzl2O4EpE,7004
|
@@ -100,14 +100,14 @@ flwr/server/strategy/krum.py,sha256=70aRw6_J2Ny9i8-kEOAyVLnONn-oGefgM0Ks5OHLa0A,
|
|
100
100
|
flwr/server/strategy/qfedavg.py,sha256=V9p0jRLnEbQaO4UDNB7675XQfGXrbw_E-_p-Lq6LrzE,10131
|
101
101
|
flwr/server/strategy/strategy.py,sha256=wrlVh4Kam-B5Np1Q7NxMNk6lF6EnwhUowjovVsUKPWo,7484
|
102
102
|
flwr/server/utils/__init__.py,sha256=I3aifvGufkoK1Od-w1Xz_nicgAYllqXScQiRU6A5xvA,901
|
103
|
-
flwr/server/utils/tensorboard.py,sha256=
|
103
|
+
flwr/server/utils/tensorboard.py,sha256=VNxz2BvOMu4q1Y5s-Mgoweb63YvYb0Hqu5hhMJK3SDU,5069
|
104
104
|
flwr/server/utils/validator.py,sha256=hp4PU5FB-W68HaFT8fLLWba-ftGhfTBB880Opes_pok,4940
|
105
105
|
flwr/simulation/__init__.py,sha256=ZtAt5bMbbp9IJp1QQOhACN6_qhSPDtGmHF8gypUOZ9s,1271
|
106
106
|
flwr/simulation/app.py,sha256=vYnyaX9pjAYAosFtciSunUYLY7UQ15nBhOFZt8mW45Q,7826
|
107
107
|
flwr/simulation/ray_transport/__init__.py,sha256=eJ3pijYkI7XhbX2rLu6FBGTo8hZkFL8RSj4twhApOGw,727
|
108
108
|
flwr/simulation/ray_transport/ray_client_proxy.py,sha256=EE11BBsvM1zOGfQK1ilpWXTPyaJrgH4ehDN3XSe1rsw,5535
|
109
|
-
flwr_nightly-1.5.0.
|
110
|
-
flwr_nightly-1.5.0.
|
111
|
-
flwr_nightly-1.5.0.
|
112
|
-
flwr_nightly-1.5.0.
|
113
|
-
flwr_nightly-1.5.0.
|
109
|
+
flwr_nightly-1.5.0.dev20230615.dist-info/METADATA,sha256=9TIY9A0TWNWC8o0GeAMW1uhVfAJ_2_tw6ZEG5HbAMKM,12838
|
110
|
+
flwr_nightly-1.5.0.dev20230615.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
111
|
+
flwr_nightly-1.5.0.dev20230615.dist-info/WHEEL,sha256=vVCvjcmxuUltf8cYhJ0sJMRDLr1XsPuxEId8YDzbyCY,88
|
112
|
+
flwr_nightly-1.5.0.dev20230615.dist-info/entry_points.txt,sha256=1uLlD5tIunkzALMfMWnqjdE_D5hRUX_I1iMmOMv6tZI,181
|
113
|
+
flwr_nightly-1.5.0.dev20230615.dist-info/RECORD,,
|
{flwr_nightly-1.5.0.dev20230614.dist-info → flwr_nightly-1.5.0.dev20230615.dist-info}/LICENSE
RENAMED
File without changes
|
File without changes
|
File without changes
|