flwr-nightly 1.8.0.dev20240220__py3-none-any.whl → 1.8.0.dev20240222__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- flwr/client/app.py +1 -3
- flwr/common/__init__.py +2 -0
- flwr/common/constant.py +14 -0
- flwr/common/record/__init__.py +2 -0
- flwr/common/record/conversion_utils.py +40 -0
- flwr/common/record/parametersrecord.py +19 -2
- flwr/common/version.py +0 -1
- flwr/server/app.py +76 -44
- flwr/server/compat/app.py +1 -2
- flwr/server/server.py +45 -2
- flwr/server/strategy/dpfedavg_fixed.py +3 -3
- flwr/server/superlink/fleet/vce/__init__.py +21 -0
- flwr/server/superlink/fleet/vce/vce_api.py +71 -0
- flwr/simulation/app.py +3 -4
- {flwr_nightly-1.8.0.dev20240220.dist-info → flwr_nightly-1.8.0.dev20240222.dist-info}/METADATA +5 -4
- {flwr_nightly-1.8.0.dev20240220.dist-info → flwr_nightly-1.8.0.dev20240222.dist-info}/RECORD +19 -16
- {flwr_nightly-1.8.0.dev20240220.dist-info → flwr_nightly-1.8.0.dev20240222.dist-info}/LICENSE +0 -0
- {flwr_nightly-1.8.0.dev20240220.dist-info → flwr_nightly-1.8.0.dev20240222.dist-info}/WHEEL +0 -0
- {flwr_nightly-1.8.0.dev20240220.dist-info → flwr_nightly-1.8.0.dev20240222.dist-info}/entry_points.txt +0 -0
flwr/client/app.py
CHANGED
@@ -506,9 +506,7 @@ def start_numpy_client(
|
|
506
506
|
)
|
507
507
|
|
508
508
|
|
509
|
-
def _init_connection(
|
510
|
-
transport: Optional[str], server_address: str
|
511
|
-
) -> Tuple[
|
509
|
+
def _init_connection(transport: Optional[str], server_address: str) -> Tuple[
|
512
510
|
Callable[
|
513
511
|
[str, bool, int, Union[bytes, str, None]],
|
514
512
|
ContextManager[
|
flwr/common/__init__.py
CHANGED
@@ -31,6 +31,7 @@ from .record import ConfigsRecord as ConfigsRecord
|
|
31
31
|
from .record import MetricsRecord as MetricsRecord
|
32
32
|
from .record import ParametersRecord as ParametersRecord
|
33
33
|
from .record import RecordSet as RecordSet
|
34
|
+
from .record import array_from_numpy as array_from_numpy
|
34
35
|
from .telemetry import EventType as EventType
|
35
36
|
from .telemetry import event as event
|
36
37
|
from .typing import ClientMessage as ClientMessage
|
@@ -58,6 +59,7 @@ from .typing import Status as Status
|
|
58
59
|
|
59
60
|
__all__ = [
|
60
61
|
"Array",
|
62
|
+
"array_from_numpy",
|
61
63
|
"bytes_to_ndarray",
|
62
64
|
"ClientMessage",
|
63
65
|
"Code",
|
flwr/common/constant.py
CHANGED
@@ -15,6 +15,8 @@
|
|
15
15
|
"""Flower constants."""
|
16
16
|
|
17
17
|
|
18
|
+
from __future__ import annotations
|
19
|
+
|
18
20
|
MISSING_EXTRA_REST = """
|
19
21
|
Extra dependencies required for using the REST-based Fleet API are missing.
|
20
22
|
|
@@ -26,13 +28,25 @@ To use the REST API, install `flwr` with the `rest` extra:
|
|
26
28
|
TRANSPORT_TYPE_GRPC_BIDI = "grpc-bidi"
|
27
29
|
TRANSPORT_TYPE_GRPC_RERE = "grpc-rere"
|
28
30
|
TRANSPORT_TYPE_REST = "rest"
|
31
|
+
TRANSPORT_TYPE_VCE = "vce"
|
29
32
|
TRANSPORT_TYPES = [
|
30
33
|
TRANSPORT_TYPE_GRPC_BIDI,
|
31
34
|
TRANSPORT_TYPE_GRPC_RERE,
|
32
35
|
TRANSPORT_TYPE_REST,
|
36
|
+
TRANSPORT_TYPE_VCE,
|
33
37
|
]
|
34
38
|
|
35
39
|
MESSAGE_TYPE_GET_PROPERTIES = "get_properties"
|
36
40
|
MESSAGE_TYPE_GET_PARAMETERS = "get_parameters"
|
37
41
|
MESSAGE_TYPE_FIT = "fit"
|
38
42
|
MESSAGE_TYPE_EVALUATE = "evaluate"
|
43
|
+
|
44
|
+
|
45
|
+
class SType:
|
46
|
+
"""Serialisation type."""
|
47
|
+
|
48
|
+
NUMPY = "numpy.ndarray"
|
49
|
+
|
50
|
+
def __new__(cls) -> SType:
|
51
|
+
"""Prevent instantiation."""
|
52
|
+
raise TypeError(f"{cls.__name__} cannot be instantiated.")
|
flwr/common/record/__init__.py
CHANGED
@@ -15,12 +15,14 @@
|
|
15
15
|
"""Record APIs."""
|
16
16
|
|
17
17
|
from .configsrecord import ConfigsRecord
|
18
|
+
from .conversion_utils import array_from_numpy
|
18
19
|
from .metricsrecord import MetricsRecord
|
19
20
|
from .parametersrecord import Array, ParametersRecord
|
20
21
|
from .recordset import RecordSet
|
21
22
|
|
22
23
|
__all__ = [
|
23
24
|
"Array",
|
25
|
+
"array_from_numpy",
|
24
26
|
"ConfigsRecord",
|
25
27
|
"MetricsRecord",
|
26
28
|
"ParametersRecord",
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# Copyright 2024 Flower Labs GmbH. All Rights Reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
# ==============================================================================
|
15
|
+
"""Conversion utility functions for Records."""
|
16
|
+
|
17
|
+
|
18
|
+
from io import BytesIO
|
19
|
+
|
20
|
+
import numpy as np
|
21
|
+
|
22
|
+
from ..constant import SType
|
23
|
+
from ..typing import NDArray
|
24
|
+
from .parametersrecord import Array
|
25
|
+
|
26
|
+
|
27
|
+
def array_from_numpy(ndarray: NDArray) -> Array:
|
28
|
+
"""Create Array from NumPy ndarray."""
|
29
|
+
buffer = BytesIO()
|
30
|
+
# WARNING: NEVER set allow_pickle to true.
|
31
|
+
# Reason: loading pickled data can execute arbitrary code
|
32
|
+
# Source: https://numpy.org/doc/stable/reference/generated/numpy.save.html
|
33
|
+
np.save(buffer, ndarray, allow_pickle=False)
|
34
|
+
data = buffer.getvalue()
|
35
|
+
return Array(
|
36
|
+
dtype=str(ndarray.dtype),
|
37
|
+
shape=list(ndarray.shape),
|
38
|
+
stype=SType.NUMPY,
|
39
|
+
data=data,
|
40
|
+
)
|
@@ -14,10 +14,14 @@
|
|
14
14
|
# ==============================================================================
|
15
15
|
"""ParametersRecord and Array."""
|
16
16
|
|
17
|
-
|
18
17
|
from dataclasses import dataclass
|
19
|
-
from
|
18
|
+
from io import BytesIO
|
19
|
+
from typing import List, Optional, OrderedDict, cast
|
20
|
+
|
21
|
+
import numpy as np
|
20
22
|
|
23
|
+
from ..constant import SType
|
24
|
+
from ..typing import NDArray
|
21
25
|
from .typeddict import TypedDict
|
22
26
|
|
23
27
|
|
@@ -51,6 +55,19 @@ class Array:
|
|
51
55
|
stype: str
|
52
56
|
data: bytes
|
53
57
|
|
58
|
+
def numpy(self) -> NDArray:
|
59
|
+
"""Return the array as a NumPy array."""
|
60
|
+
if self.stype != SType.NUMPY:
|
61
|
+
raise TypeError(
|
62
|
+
f"Unsupported serialization type for numpy conversion: '{self.stype}'"
|
63
|
+
)
|
64
|
+
bytes_io = BytesIO(self.data)
|
65
|
+
# WARNING: NEVER set allow_pickle to true.
|
66
|
+
# Reason: loading pickled data can execute arbitrary code
|
67
|
+
# Source: https://numpy.org/doc/stable/reference/generated/numpy.load.html
|
68
|
+
ndarray_deserialized = np.load(bytes_io, allow_pickle=False)
|
69
|
+
return cast(NDArray, ndarray_deserialized)
|
70
|
+
|
54
71
|
|
55
72
|
def _check_key(key: str) -> None:
|
56
73
|
"""Check if key is of expected type."""
|
flwr/common/version.py
CHANGED
flwr/server/app.py
CHANGED
@@ -34,6 +34,7 @@ from flwr.common.constant import (
|
|
34
34
|
MISSING_EXTRA_REST,
|
35
35
|
TRANSPORT_TYPE_GRPC_RERE,
|
36
36
|
TRANSPORT_TYPE_REST,
|
37
|
+
TRANSPORT_TYPE_VCE,
|
37
38
|
)
|
38
39
|
from flwr.common.logger import log
|
39
40
|
from flwr.proto.driver_pb2_grpc import ( # pylint: disable=E0611
|
@@ -43,17 +44,18 @@ from flwr.proto.fleet_pb2_grpc import ( # pylint: disable=E0611
|
|
43
44
|
add_FleetServicer_to_server,
|
44
45
|
)
|
45
46
|
|
46
|
-
from .client_manager import ClientManager
|
47
|
+
from .client_manager import ClientManager
|
47
48
|
from .history import History
|
48
|
-
from .server import Server
|
49
|
+
from .server import Server, init_defaults, run_fl
|
49
50
|
from .server_config import ServerConfig
|
50
|
-
from .strategy import
|
51
|
+
from .strategy import Strategy
|
51
52
|
from .superlink.driver.driver_servicer import DriverServicer
|
52
53
|
from .superlink.fleet.grpc_bidi.grpc_server import (
|
53
54
|
generic_create_grpc_server,
|
54
55
|
start_grpc_server,
|
55
56
|
)
|
56
57
|
from .superlink.fleet.grpc_rere.fleet_servicer import FleetServicer
|
58
|
+
from .superlink.fleet.vce import start_vce
|
57
59
|
from .superlink.state import StateFactory
|
58
60
|
|
59
61
|
ADDRESS_DRIVER_API = "0.0.0.0:9091"
|
@@ -183,47 +185,6 @@ def start_server( # pylint: disable=too-many-arguments,too-many-locals
|
|
183
185
|
return hist
|
184
186
|
|
185
187
|
|
186
|
-
def init_defaults(
|
187
|
-
server: Optional[Server],
|
188
|
-
config: Optional[ServerConfig],
|
189
|
-
strategy: Optional[Strategy],
|
190
|
-
client_manager: Optional[ClientManager],
|
191
|
-
) -> Tuple[Server, ServerConfig]:
|
192
|
-
"""Create server instance if none was given."""
|
193
|
-
if server is None:
|
194
|
-
if client_manager is None:
|
195
|
-
client_manager = SimpleClientManager()
|
196
|
-
if strategy is None:
|
197
|
-
strategy = FedAvg()
|
198
|
-
server = Server(client_manager=client_manager, strategy=strategy)
|
199
|
-
elif strategy is not None:
|
200
|
-
log(WARN, "Both server and strategy were provided, ignoring strategy")
|
201
|
-
|
202
|
-
# Set default config values
|
203
|
-
if config is None:
|
204
|
-
config = ServerConfig()
|
205
|
-
|
206
|
-
return server, config
|
207
|
-
|
208
|
-
|
209
|
-
def run_fl(
|
210
|
-
server: Server,
|
211
|
-
config: ServerConfig,
|
212
|
-
) -> History:
|
213
|
-
"""Train a model on the given server and return the History object."""
|
214
|
-
hist = server.fit(num_rounds=config.num_rounds, timeout=config.round_timeout)
|
215
|
-
log(INFO, "app_fit: losses_distributed %s", str(hist.losses_distributed))
|
216
|
-
log(INFO, "app_fit: metrics_distributed_fit %s", str(hist.metrics_distributed_fit))
|
217
|
-
log(INFO, "app_fit: metrics_distributed %s", str(hist.metrics_distributed))
|
218
|
-
log(INFO, "app_fit: losses_centralized %s", str(hist.losses_centralized))
|
219
|
-
log(INFO, "app_fit: metrics_centralized %s", str(hist.metrics_centralized))
|
220
|
-
|
221
|
-
# Graceful shutdown
|
222
|
-
server.disconnect_all_clients(timeout=config.round_timeout)
|
223
|
-
|
224
|
-
return hist
|
225
|
-
|
226
|
-
|
227
188
|
def run_driver_api() -> None:
|
228
189
|
"""Run Flower server (Driver API)."""
|
229
190
|
log(INFO, "Starting Flower server (Driver API)")
|
@@ -401,6 +362,15 @@ def run_superlink() -> None:
|
|
401
362
|
certificates=certificates,
|
402
363
|
)
|
403
364
|
grpc_servers.append(fleet_server)
|
365
|
+
elif args.fleet_api_type == TRANSPORT_TYPE_VCE:
|
366
|
+
_run_fleet_api_vce(
|
367
|
+
num_supernodes=args.num_supernodes,
|
368
|
+
client_app_module_name=args.client_app,
|
369
|
+
backend_name=args.backend,
|
370
|
+
backend_config_json_stream=args.backend_config,
|
371
|
+
working_dir=args.dir,
|
372
|
+
state_factory=state_factory,
|
373
|
+
)
|
404
374
|
else:
|
405
375
|
raise ValueError(f"Unknown fleet_api_type: {args.fleet_api_type}")
|
406
376
|
|
@@ -537,6 +507,27 @@ def _run_fleet_api_grpc_rere(
|
|
537
507
|
return fleet_grpc_server
|
538
508
|
|
539
509
|
|
510
|
+
# pylint: disable=too-many-arguments
|
511
|
+
def _run_fleet_api_vce(
|
512
|
+
num_supernodes: int,
|
513
|
+
client_app_module_name: str,
|
514
|
+
backend_name: str,
|
515
|
+
backend_config_json_stream: str,
|
516
|
+
working_dir: str,
|
517
|
+
state_factory: StateFactory,
|
518
|
+
) -> None:
|
519
|
+
log(INFO, "Flower VCE: Starting Fleet API (VirtualClientEngine)")
|
520
|
+
|
521
|
+
start_vce(
|
522
|
+
num_supernodes=num_supernodes,
|
523
|
+
client_app_module_name=client_app_module_name,
|
524
|
+
backend_name=backend_name,
|
525
|
+
backend_config_json_stream=backend_config_json_stream,
|
526
|
+
state_factory=state_factory,
|
527
|
+
working_dir=working_dir,
|
528
|
+
)
|
529
|
+
|
530
|
+
|
540
531
|
# pylint: disable=import-outside-toplevel,too-many-arguments
|
541
532
|
def _run_fleet_api_rest(
|
542
533
|
host: str,
|
@@ -714,6 +705,14 @@ def _add_args_fleet_api(parser: argparse.ArgumentParser) -> None:
|
|
714
705
|
help="Start a Fleet API server (REST, experimental)",
|
715
706
|
)
|
716
707
|
|
708
|
+
ex_group.add_argument(
|
709
|
+
"--vce",
|
710
|
+
action="store_const",
|
711
|
+
dest="fleet_api_type",
|
712
|
+
const=TRANSPORT_TYPE_VCE,
|
713
|
+
help="Start a Fleet API server (VirtualClientEngine)",
|
714
|
+
)
|
715
|
+
|
717
716
|
# Fleet API gRPC-rere options
|
718
717
|
grpc_rere_group = parser.add_argument_group(
|
719
718
|
"Fleet API (gRPC-rere) server options", ""
|
@@ -749,3 +748,36 @@ def _add_args_fleet_api(parser: argparse.ArgumentParser) -> None:
|
|
749
748
|
type=int,
|
750
749
|
default=1,
|
751
750
|
)
|
751
|
+
|
752
|
+
# Fleet API VCE options
|
753
|
+
vce_group = parser.add_argument_group("Fleet API (VCE) server options", "")
|
754
|
+
vce_group.add_argument(
|
755
|
+
"--client-app",
|
756
|
+
help="For example: `client:app` or `project.package.module:wrapper.app`.",
|
757
|
+
)
|
758
|
+
vce_group.add_argument(
|
759
|
+
"--num-supernodes",
|
760
|
+
type=int,
|
761
|
+
help="Number of simulated SuperNodes.",
|
762
|
+
)
|
763
|
+
vce_group.add_argument(
|
764
|
+
"--backend",
|
765
|
+
default="ray",
|
766
|
+
type=str,
|
767
|
+
help="Simulation backend that executes the ClientApp.",
|
768
|
+
)
|
769
|
+
vce_group.add_argument(
|
770
|
+
"--backend-config",
|
771
|
+
type=str,
|
772
|
+
default='{"client_resources": {"num_cpus":1, "num_gpus":0.0}, "tensorflow": 0}',
|
773
|
+
help='A JSON formatted stream, e.g \'{"<keyA>":<value>, "<keyB>":<value>}\' to '
|
774
|
+
"configure a backend. Values supported in <value> are those included by "
|
775
|
+
"`flwr.common.typing.ConfigsRecordValues`. ",
|
776
|
+
)
|
777
|
+
parser.add_argument(
|
778
|
+
"--dir",
|
779
|
+
default="",
|
780
|
+
help="Add specified directory to the PYTHONPATH and load"
|
781
|
+
"ClientApp from there."
|
782
|
+
" Default: current working directory.",
|
783
|
+
)
|
flwr/server/compat/app.py
CHANGED
@@ -26,10 +26,9 @@ from flwr.common import EventType, event
|
|
26
26
|
from flwr.common.address import parse_address
|
27
27
|
from flwr.common.logger import log, warn_deprecated_feature
|
28
28
|
from flwr.proto import driver_pb2 # pylint: disable=E0611
|
29
|
-
from flwr.server.app import init_defaults, run_fl
|
30
29
|
from flwr.server.client_manager import ClientManager
|
31
30
|
from flwr.server.history import History
|
32
|
-
from flwr.server.server import Server
|
31
|
+
from flwr.server.server import Server, init_defaults, run_fl
|
33
32
|
from flwr.server.server_config import ServerConfig
|
34
33
|
from flwr.server.strategy import Strategy
|
35
34
|
|
flwr/server/server.py
CHANGED
@@ -17,7 +17,7 @@
|
|
17
17
|
|
18
18
|
import concurrent.futures
|
19
19
|
import timeit
|
20
|
-
from logging import DEBUG, INFO
|
20
|
+
from logging import DEBUG, INFO, WARN
|
21
21
|
from typing import Dict, List, Optional, Tuple, Union
|
22
22
|
|
23
23
|
from flwr.common import (
|
@@ -33,11 +33,13 @@ from flwr.common import (
|
|
33
33
|
)
|
34
34
|
from flwr.common.logger import log
|
35
35
|
from flwr.common.typing import GetParametersIns
|
36
|
-
from flwr.server.client_manager import ClientManager
|
36
|
+
from flwr.server.client_manager import ClientManager, SimpleClientManager
|
37
37
|
from flwr.server.client_proxy import ClientProxy
|
38
38
|
from flwr.server.history import History
|
39
39
|
from flwr.server.strategy import FedAvg, Strategy
|
40
40
|
|
41
|
+
from .server_config import ServerConfig
|
42
|
+
|
41
43
|
FitResultsAndFailures = Tuple[
|
42
44
|
List[Tuple[ClientProxy, FitRes]],
|
43
45
|
List[Union[Tuple[ClientProxy, FitRes], BaseException]],
|
@@ -441,3 +443,44 @@ def _handle_finished_future_after_evaluate(
|
|
441
443
|
|
442
444
|
# Not successful, client returned a result where the status code is not OK
|
443
445
|
failures.append(result)
|
446
|
+
|
447
|
+
|
448
|
+
def init_defaults(
|
449
|
+
server: Optional[Server],
|
450
|
+
config: Optional[ServerConfig],
|
451
|
+
strategy: Optional[Strategy],
|
452
|
+
client_manager: Optional[ClientManager],
|
453
|
+
) -> Tuple[Server, ServerConfig]:
|
454
|
+
"""Create server instance if none was given."""
|
455
|
+
if server is None:
|
456
|
+
if client_manager is None:
|
457
|
+
client_manager = SimpleClientManager()
|
458
|
+
if strategy is None:
|
459
|
+
strategy = FedAvg()
|
460
|
+
server = Server(client_manager=client_manager, strategy=strategy)
|
461
|
+
elif strategy is not None:
|
462
|
+
log(WARN, "Both server and strategy were provided, ignoring strategy")
|
463
|
+
|
464
|
+
# Set default config values
|
465
|
+
if config is None:
|
466
|
+
config = ServerConfig()
|
467
|
+
|
468
|
+
return server, config
|
469
|
+
|
470
|
+
|
471
|
+
def run_fl(
|
472
|
+
server: Server,
|
473
|
+
config: ServerConfig,
|
474
|
+
) -> History:
|
475
|
+
"""Train a model on the given server and return the History object."""
|
476
|
+
hist = server.fit(num_rounds=config.num_rounds, timeout=config.round_timeout)
|
477
|
+
log(INFO, "app_fit: losses_distributed %s", str(hist.losses_distributed))
|
478
|
+
log(INFO, "app_fit: metrics_distributed_fit %s", str(hist.metrics_distributed_fit))
|
479
|
+
log(INFO, "app_fit: metrics_distributed %s", str(hist.metrics_distributed))
|
480
|
+
log(INFO, "app_fit: losses_centralized %s", str(hist.losses_centralized))
|
481
|
+
log(INFO, "app_fit: metrics_centralized %s", str(hist.metrics_centralized))
|
482
|
+
|
483
|
+
# Graceful shutdown
|
484
|
+
server.disconnect_all_clients(timeout=config.round_timeout)
|
485
|
+
|
486
|
+
return hist
|
@@ -104,9 +104,9 @@ class DPFedAvgFixed(Strategy):
|
|
104
104
|
"""
|
105
105
|
additional_config = {"dpfedavg_clip_norm": self.clip_norm}
|
106
106
|
if not self.server_side_noising:
|
107
|
-
additional_config[
|
108
|
-
|
109
|
-
|
107
|
+
additional_config["dpfedavg_noise_stddev"] = (
|
108
|
+
self._calc_client_noise_stddev()
|
109
|
+
)
|
110
110
|
|
111
111
|
client_instructions = self.strategy.configure_fit(
|
112
112
|
server_round, parameters, client_manager
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# Copyright 2024 Flower Labs GmbH. All Rights Reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
# ==============================================================================
|
15
|
+
"""Fleet VirtualClientEngine side."""
|
16
|
+
|
17
|
+
from .vce_api import start_vce
|
18
|
+
|
19
|
+
__all__ = [
|
20
|
+
"start_vce",
|
21
|
+
]
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# Copyright 2024 Flower Labs GmbH. All Rights Reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
# ==============================================================================
|
15
|
+
"""Fleet VirtualClientEngine API."""
|
16
|
+
|
17
|
+
import json
|
18
|
+
from logging import INFO
|
19
|
+
from typing import Dict
|
20
|
+
|
21
|
+
from flwr.client.clientapp import ClientApp, load_client_app
|
22
|
+
from flwr.client.node_state import NodeState
|
23
|
+
from flwr.common.logger import log
|
24
|
+
from flwr.server.superlink.state import StateFactory
|
25
|
+
|
26
|
+
NodeToPartitionMapping = Dict[int, int]
|
27
|
+
|
28
|
+
|
29
|
+
def _register_nodes(
|
30
|
+
num_nodes: int, state_factory: StateFactory
|
31
|
+
) -> NodeToPartitionMapping:
|
32
|
+
"""Register nodes with the StateFactory and create node-id:partition-id mapping."""
|
33
|
+
nodes_mapping: NodeToPartitionMapping = {}
|
34
|
+
state = state_factory.state()
|
35
|
+
for i in range(num_nodes):
|
36
|
+
node_id = state.create_node()
|
37
|
+
nodes_mapping[node_id] = i
|
38
|
+
log(INFO, "Registered %i nodes", len(nodes_mapping))
|
39
|
+
return nodes_mapping
|
40
|
+
|
41
|
+
|
42
|
+
# pylint: disable=too-many-arguments,unused-argument
|
43
|
+
def start_vce(
|
44
|
+
num_supernodes: int,
|
45
|
+
client_app_module_name: str,
|
46
|
+
backend_name: str,
|
47
|
+
backend_config_json_stream: str,
|
48
|
+
state_factory: StateFactory,
|
49
|
+
working_dir: str,
|
50
|
+
) -> None:
|
51
|
+
"""Start Fleet API with the VirtualClientEngine (VCE)."""
|
52
|
+
# Register SuperNodes
|
53
|
+
nodes_mapping = _register_nodes(
|
54
|
+
num_nodes=num_supernodes, state_factory=state_factory
|
55
|
+
)
|
56
|
+
|
57
|
+
# Construct mapping of NodeStates
|
58
|
+
node_states: Dict[int, NodeState] = {}
|
59
|
+
for node_id in nodes_mapping:
|
60
|
+
node_states[node_id] = NodeState()
|
61
|
+
|
62
|
+
# Load backend config
|
63
|
+
_ = json.loads(backend_config_json_stream)
|
64
|
+
|
65
|
+
log(INFO, "client_app_str = %s", client_app_module_name)
|
66
|
+
|
67
|
+
def _load() -> ClientApp:
|
68
|
+
app: ClientApp = load_client_app(client_app_module_name)
|
69
|
+
return app
|
70
|
+
|
71
|
+
# start backend
|
flwr/simulation/app.py
CHANGED
@@ -28,10 +28,9 @@ from ray.util.scheduling_strategies import NodeAffinitySchedulingStrategy
|
|
28
28
|
from flwr.client import ClientFn
|
29
29
|
from flwr.common import EventType, event
|
30
30
|
from flwr.common.logger import log
|
31
|
-
from flwr.server import Server
|
32
|
-
from flwr.server.app import init_defaults, run_fl
|
33
31
|
from flwr.server.client_manager import ClientManager
|
34
32
|
from flwr.server.history import History
|
33
|
+
from flwr.server.server import Server, init_defaults, run_fl
|
35
34
|
from flwr.server.server_config import ServerConfig
|
36
35
|
from flwr.server.strategy import Strategy
|
37
36
|
from flwr.simulation.ray_transport.ray_actor import (
|
@@ -220,7 +219,7 @@ def start_simulation(
|
|
220
219
|
log(
|
221
220
|
INFO,
|
222
221
|
"Optimize your simulation with Flower VCE: "
|
223
|
-
"https://flower.
|
222
|
+
"https://flower.ai/docs/framework/how-to-run-simulations.html",
|
224
223
|
)
|
225
224
|
|
226
225
|
# Log the resources that a single client will be able to use
|
@@ -338,7 +337,7 @@ def start_simulation(
|
|
338
337
|
"disconnected. The head node might still be alive but cannot accommodate "
|
339
338
|
"any actor with resources: %s."
|
340
339
|
"\nTake a look at the Flower simulation examples for guidance "
|
341
|
-
"<https://flower.
|
340
|
+
"<https://flower.ai/docs/framework/how-to-run-simulations.html>.",
|
342
341
|
client_resources,
|
343
342
|
client_resources,
|
344
343
|
)
|
{flwr_nightly-1.8.0.dev20240220.dist-info → flwr_nightly-1.8.0.dev20240222.dist-info}/METADATA
RENAMED
@@ -1,12 +1,12 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: flwr-nightly
|
3
|
-
Version: 1.8.0.
|
3
|
+
Version: 1.8.0.dev20240222
|
4
4
|
Summary: Flower: A Friendly Federated Learning Framework
|
5
|
-
Home-page: https://flower.
|
5
|
+
Home-page: https://flower.ai
|
6
6
|
License: Apache-2.0
|
7
7
|
Keywords: flower,fl,federated learning,federated analytics,federated evaluation,machine learning
|
8
8
|
Author: The Flower Authors
|
9
|
-
Author-email: hello@flower.
|
9
|
+
Author-email: hello@flower.ai
|
10
10
|
Requires-Python: >=3.8,<4.0
|
11
11
|
Classifier: Development Status :: 5 - Production/Stable
|
12
12
|
Classifier: Intended Audience :: Developers
|
@@ -44,7 +44,7 @@ Requires-Dist: requests (>=2.31.0,<3.0.0) ; extra == "rest"
|
|
44
44
|
Requires-Dist: starlette (>=0.31.0,<0.32.0) ; extra == "rest"
|
45
45
|
Requires-Dist: typer[all] (>=0.9.0,<0.10.0)
|
46
46
|
Requires-Dist: uvicorn[standard] (>=0.23.0,<0.24.0) ; extra == "rest"
|
47
|
-
Project-URL: Documentation, https://flower.
|
47
|
+
Project-URL: Documentation, https://flower.ai
|
48
48
|
Project-URL: Repository, https://github.com/adap/flower
|
49
49
|
Description-Content-Type: text/markdown
|
50
50
|
|
@@ -199,6 +199,7 @@ Other [examples](https://github.com/adap/flower/tree/main/examples):
|
|
199
199
|
- Single-Machine Simulation of Federated Learning Systems ([PyTorch](https://github.com/adap/flower/tree/main/examples/simulation-pytorch)) ([Tensorflow](https://github.com/adap/flower/tree/main/examples/simulation-tensorflow))
|
200
200
|
- [Comprehensive Flower+XGBoost](https://github.com/adap/flower/tree/main/examples/xgboost-comprehensive)
|
201
201
|
- [Flower through Docker Compose and with Grafana dashboard](https://github.com/adap/flower/tree/main/examples/flower-via-docker-compose)
|
202
|
+
- [Flower with KaplanMeierFitter from the lifelines library](https://github.com/adap/flower/tree/main/examples/federated-kaplna-meier-fitter)
|
202
203
|
|
203
204
|
## Community
|
204
205
|
|
{flwr_nightly-1.8.0.dev20240220.dist-info → flwr_nightly-1.8.0.dev20240222.dist-info}/RECORD
RENAMED
@@ -18,7 +18,7 @@ flwr/cli/new/templates/app/requirements.pytorch.txt.tpl,sha256=9Z70jsiCPdsbuorhi
|
|
18
18
|
flwr/cli/new/templates/app/requirements.tensorflow.txt.tpl,sha256=WTbIgK5G_iG0W-xtvQLCZMxL_Og26rFXial2TkYH5dw,211
|
19
19
|
flwr/cli/utils.py,sha256=9cCEIt8QmJXz85JNmPk1IHPd7p8E3KDn6h5CfF0nDL4,1926
|
20
20
|
flwr/client/__init__.py,sha256=ypNGqhSDxEPX_6XwcQyVseBf8oS_Zvs_JkrLbVIeKxw,1186
|
21
|
-
flwr/client/app.py,sha256=
|
21
|
+
flwr/client/app.py,sha256=BxQGhdhFiw9JrKu-TcgmyshN02K9QpgWPxFVEcQ4Mb8,19983
|
22
22
|
flwr/client/client.py,sha256=Vp9UkOkoHdNfn6iMYZsj_5m_GICiFfUlKEVaLad-YhM,8183
|
23
23
|
flwr/client/clientapp.py,sha256=jrDgJBswP2hD1YdGgQoI3GU_NkliYWVU8glBJLOVzQY,4205
|
24
24
|
flwr/client/dpfedavg_numpy_client.py,sha256=9Tnig4iml2J88HBKNahegjXjbfvIQyBtaIQaqjbeqsA,7435
|
@@ -39,9 +39,9 @@ flwr/client/numpy_client.py,sha256=u76GWAdHmJM88Agm2EgLQSvO8Jnk225mJTk-_TmPjFE,1
|
|
39
39
|
flwr/client/rest_client/__init__.py,sha256=ThwOnkMdzxo_UuyTI47Q7y9oSpuTgNT2OuFvJCfuDiw,735
|
40
40
|
flwr/client/rest_client/connection.py,sha256=363X5FZPg2H8f9dZk_IhUrVK-DGiDHfIodJNat-W8XE,11903
|
41
41
|
flwr/client/typing.py,sha256=c9EvjlEjasxn1Wqx6bGl6Xg6vM1gMFfmXht-E2i5J-k,1006
|
42
|
-
flwr/common/__init__.py,sha256=
|
42
|
+
flwr/common/__init__.py,sha256=Pm7NIXPNUxQGe8mMv9L7xFi5VohYnJEpgR-RP7gMDQQ,3466
|
43
43
|
flwr/common/address.py,sha256=iTAN9jtmIGMrWFnx9XZQl45ZEtQJVZZLYPRBSNVARGI,1882
|
44
|
-
flwr/common/constant.py,sha256
|
44
|
+
flwr/common/constant.py,sha256=jVUVKXo1cFb2HpRYqV70WKMG4RqCVrq7H6KC7zXs23Y,1572
|
45
45
|
flwr/common/context.py,sha256=ounF-mWPPtXGwtae3sg5EhF58ScviOa3MVqxRpGVu-8,1313
|
46
46
|
flwr/common/date.py,sha256=UWhBZj49yX9LD4BmatS_ZFZu_-kweGh0KQJ1djyWWH4,891
|
47
47
|
flwr/common/differential_privacy.py,sha256=pVSKRhciVNtdBlhoz1H0--8N5PMLjdO_bA1PLGq4WZ8,2969
|
@@ -51,10 +51,11 @@ flwr/common/grpc.py,sha256=qVLB0d6bCuaBRW5YB0vEZXsR7Bo3R2lh4ONiCocqwRI,2270
|
|
51
51
|
flwr/common/logger.py,sha256=qX_gqEyrmGOH0x_r8uQ1Vskz4fGvEij9asdo4DUOPY8,4135
|
52
52
|
flwr/common/message.py,sha256=tXkOAIYnPN5cW2OVggVTAUSaBxEDqoFGpST7uPpIAho,6198
|
53
53
|
flwr/common/parameter.py,sha256=-bFAUayToYDF50FZGrBC1hQYJCQDtB2bbr3ZuVLMtdE,2095
|
54
|
-
flwr/common/record/__init__.py,sha256=
|
54
|
+
flwr/common/record/__init__.py,sha256=33OaDW2bvaW952DFHH1amHclv4AuDZu385jXjHhXoog,1054
|
55
55
|
flwr/common/record/configsrecord.py,sha256=qL-hQ6ZFOOWJYCUHeFiao2vcO5rnk585Ns5Yxfb1sp4,3378
|
56
|
+
flwr/common/record/conversion_utils.py,sha256=n3I3SI2P6hUjyxbWNc0QAch-SEhfMK6Hm-UUaplAlUc,1393
|
56
57
|
flwr/common/record/metricsrecord.py,sha256=iEihUVNqwyZtbim7FfqJ7ksEbPef6cqTZ1w4-_C5afw,3360
|
57
|
-
flwr/common/record/parametersrecord.py,sha256=
|
58
|
+
flwr/common/record/parametersrecord.py,sha256=ii8Ol5W3dk_5YUWSFbbLC5KWep2eqlSWfCsgSM4Pedg,4331
|
58
59
|
flwr/common/record/recordset.py,sha256=OeRcBMGqx9vutWRz1xkujBPHlVpU58R1EcFRHEQrePo,2351
|
59
60
|
flwr/common/record/typeddict.py,sha256=2NW8JF27p1uNWaqDbJ7bMkItA5x4ygYT8aHrf8NaqnE,3879
|
60
61
|
flwr/common/recordset_compat.py,sha256=MXabdTaE9ItZ05oid6YLWtdbvgGHbTXsnMoTSJYYpcY,13842
|
@@ -70,7 +71,7 @@ flwr/common/secure_aggregation/secaggplus_utils.py,sha256=PleDyDu7jHNAfbRoEaoQiO
|
|
70
71
|
flwr/common/serde.py,sha256=0N2SG6T71rVIGPiUIBIZfyWyNGQTp-MjDpy9sDUvrXc,20622
|
71
72
|
flwr/common/telemetry.py,sha256=JkFB6WBOskqAJfzSM-l6tQfRiSi2oiysClfg0-5T7NY,7782
|
72
73
|
flwr/common/typing.py,sha256=3Wu6Ol1Ja6Gb0WdlcXVEn1EHYJbc4oRRJA81vEegxBo,4382
|
73
|
-
flwr/common/version.py,sha256=
|
74
|
+
flwr/common/version.py,sha256=_RDSMGZPEuGKYViZuXPotDtXMvh4iyDH9XOCO4qtPO8,666
|
74
75
|
flwr/proto/__init__.py,sha256=hbY7JYakwZwCkYgCNlmHdc8rtvfoJbAZLalMdc--CGc,683
|
75
76
|
flwr/proto/driver_pb2.py,sha256=JHIdjNPTgp6YHD-_lz5ZZFB0VIOR3_GmcaOTN4jndc4,3115
|
76
77
|
flwr/proto/driver_pb2.pyi,sha256=xwl2AqIWn0SwAlg-x5RUQeqr6DC48eywnqmD7gbaaFs,4670
|
@@ -98,11 +99,11 @@ flwr/proto/transport_pb2_grpc.py,sha256=vLN3EHtx2aEEMCO4f1Upu-l27BPzd3-5pV-u8wPc
|
|
98
99
|
flwr/proto/transport_pb2_grpc.pyi,sha256=AGXf8RiIiW2J5IKMlm_3qT3AzcDa4F3P5IqUjve_esA,766
|
99
100
|
flwr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
100
101
|
flwr/server/__init__.py,sha256=_Wv3UkZzSXzbKWXyl2yY8tU6oqf_XqIruggpHjnmikE,1662
|
101
|
-
flwr/server/app.py,sha256=
|
102
|
+
flwr/server/app.py,sha256=0-p--_yF8rHTPxjrwZnBkEZgEeyAAqJ-i18vdCHTq5g,26328
|
102
103
|
flwr/server/client_manager.py,sha256=T8UDSRJBVD3fyIDI7NTAA-NA7GPrMNNgH2OAF54RRxE,6127
|
103
104
|
flwr/server/client_proxy.py,sha256=8ScGDvP3jHbl8DV3hyFID5N5VEVlXn8ZTQXtkdOfssI,2234
|
104
105
|
flwr/server/compat/__init__.py,sha256=KNvRFANbIc8LFRKHsBVfxcbOSekEImWgNq_gapCkbic,812
|
105
|
-
flwr/server/compat/app.py,sha256=
|
106
|
+
flwr/server/compat/app.py,sha256=lfPCbY4WVMvKdscBxraikBn97SEuSwR_dUDoTHTMe7w,7859
|
106
107
|
flwr/server/compat/driver_client_proxy.py,sha256=S3lI6Wi7fpkbg6GEi6TSzB6RHsrIdg1w44sLe-94UUk,6148
|
107
108
|
flwr/server/criterion.py,sha256=ypbAexbztzGUxNen9RCHF91QeqiEQix4t4Ih3E-42MM,1061
|
108
109
|
flwr/server/driver/__init__.py,sha256=yYyVX1FcDiDFM6rw0-DSZpuRy0EoWRfG9puwlQUswFA,820
|
@@ -110,7 +111,7 @@ flwr/server/driver/driver.py,sha256=DApIRFeEzReZpdAsbFQ68haSUONMstblyOt6qcseMNc,
|
|
110
111
|
flwr/server/driver/grpc_driver.py,sha256=Ekcxx4Miqume02fiBjBRJhItaOFNpn120WT3uRIPWIc,4585
|
111
112
|
flwr/server/history.py,sha256=W7PHCFX7dLXrdnaVfl5V4tuzmtxh6zArkWYxVXvTZ1c,4904
|
112
113
|
flwr/server/run_serverapp.py,sha256=tYNbz11xMvkbcMty6u5nkLHmZhwIjSO78CfW-Aas5R8,4488
|
113
|
-
flwr/server/server.py,sha256=
|
114
|
+
flwr/server/server.py,sha256=kUIqgLIXnWcSrhEhXXkaZPRooYhTGGX-RDCYzG9J76g,17495
|
114
115
|
flwr/server/server_app.py,sha256=avNQ7AMMKsn09ly81C3UBgOfHhM_R29l4MrzlalGoj8,5892
|
115
116
|
flwr/server/server_config.py,sha256=yOHpkdyuhOm--Gy_4Vofvu6jCDxhyECEDpIy02beuCg,1018
|
116
117
|
flwr/server/strategy/__init__.py,sha256=qYOURtpO5DtaDXzvo0wd1lv0cP3wGQxbhqn3s5i8--k,2220
|
@@ -118,7 +119,7 @@ flwr/server/strategy/aggregate.py,sha256=QyRIJtI5gnuY1NbgrcrOvkHxGIxBvApq7d9Y4xl
|
|
118
119
|
flwr/server/strategy/bulyan.py,sha256=8GsSVJzRSoSWE2zQUKqC3Z795grdN9xpmc3MSGGXnzM,6532
|
119
120
|
flwr/server/strategy/dp_fixed_clipping.py,sha256=185P6F17uOtRByDaQSCpKj6jCGgP4271xhoMLUl2oR8,6671
|
120
121
|
flwr/server/strategy/dpfedavg_adaptive.py,sha256=hLJkPQJl1bHjwrBNg3PSRFKf3no0hg5EHiFaWhHlWqw,4877
|
121
|
-
flwr/server/strategy/dpfedavg_fixed.py,sha256=
|
122
|
+
flwr/server/strategy/dpfedavg_fixed.py,sha256=G0yYxrPoM-MHQ889DYN3OeNiEeU0yQrjgAzcq0G653w,7219
|
122
123
|
flwr/server/strategy/fault_tolerant_fedavg.py,sha256=veGcehB6rXT_MihNDrD1v5JY-TxJi7fybdDl-OZooDQ,5900
|
123
124
|
flwr/server/strategy/fedadagrad.py,sha256=9yoVdZOFTjQ7DpaVrYLH9ca88WgJVWepld6UXybGQMY,6505
|
124
125
|
flwr/server/strategy/fedadam.py,sha256=Zvqo6oChwB2aDGHeLXHNE74nHGwkFAWODLZ8f6Dtq1g,6763
|
@@ -151,6 +152,8 @@ flwr/server/superlink/fleet/message_handler/__init__.py,sha256=hEY0l61ojH8Iz30_K
|
|
151
152
|
flwr/server/superlink/fleet/message_handler/message_handler.py,sha256=iUBTJ2fzVL0XB8vfaEzCNJ6okeuOWrK6LJe__ElP9x8,2833
|
152
153
|
flwr/server/superlink/fleet/rest_rere/__init__.py,sha256=VKDvDq5H8koOUztpmQacVzGJXPLEEkL1Vmolxt3mvnY,735
|
153
154
|
flwr/server/superlink/fleet/rest_rere/rest_api.py,sha256=7JCs7NW4Qq8W5QhXxqsQNFiCLlRY-b_iD420vH1Mu-U,5906
|
155
|
+
flwr/server/superlink/fleet/vce/__init__.py,sha256=bogHbcWSXkD7wZkqUXiLRKQTJUs7jtr5uwaGlmoA-Yc,785
|
156
|
+
flwr/server/superlink/fleet/vce/vce_api.py,sha256=okSRawmpRD5FntvBYFgAv55dQtXJbZLzHr2XxEqGArM,2351
|
154
157
|
flwr/server/superlink/state/__init__.py,sha256=ij-7Ms-hyordQdRmGQxY1-nVa4OhixJ0jr7_YDkys0s,1003
|
155
158
|
flwr/server/superlink/state/in_memory_state.py,sha256=viGGGyg7LwwxKfCnndV6Tp9poVeZTrbN9z0tt-4qrqI,7903
|
156
159
|
flwr/server/superlink/state/sqlite_state.py,sha256=Adc2g1DecAN9Cl9F8lekuTb885mIHiOi6sQv4nxbmSc,21203
|
@@ -161,13 +164,13 @@ flwr/server/utils/__init__.py,sha256=RQVbo-bcsVtp_lJBf7dL5w01FbLrr7v3YedeGp5_YMs
|
|
161
164
|
flwr/server/utils/tensorboard.py,sha256=k0G6bqsLx7wfYbH2KtXsDYcOCfyIeE12-hefXA7lZdg,5485
|
162
165
|
flwr/server/utils/validator.py,sha256=IJN2475yyD_i_9kg_SJ_JodIuZh58ufpWGUDQRAqu2s,4740
|
163
166
|
flwr/simulation/__init__.py,sha256=E2eD5FlTmZZ80u21FmWCkacrM7O4mrEHD8iXqeCaBUQ,1278
|
164
|
-
flwr/simulation/app.py,sha256=
|
167
|
+
flwr/simulation/app.py,sha256=WqJxdXTEuehwMW605p5NMmvBbKYx5tuqnV3Mp7jSWXM,13904
|
165
168
|
flwr/simulation/ray_transport/__init__.py,sha256=FsaAnzC4cw4DqoouBCix6496k29jACkfeIam55BvW9g,734
|
166
169
|
flwr/simulation/ray_transport/ray_actor.py,sha256=mg-vsqt8w6ZtGLkHlz6SnZtZz5HcQY0gYiKSlzYT27A,16591
|
167
170
|
flwr/simulation/ray_transport/ray_client_proxy.py,sha256=8I1g8nK0Vz7ygpOSzGZ1oXTfwi1vez8fxYWCdSXBrXE,6290
|
168
171
|
flwr/simulation/ray_transport/utils.py,sha256=TYdtfg1P9VfTdLMOJlifInGpxWHYs9UfUqIv2wfkRLA,2392
|
169
|
-
flwr_nightly-1.8.0.
|
170
|
-
flwr_nightly-1.8.0.
|
171
|
-
flwr_nightly-1.8.0.
|
172
|
-
flwr_nightly-1.8.0.
|
173
|
-
flwr_nightly-1.8.0.
|
172
|
+
flwr_nightly-1.8.0.dev20240222.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
173
|
+
flwr_nightly-1.8.0.dev20240222.dist-info/METADATA,sha256=GyA7xLs4BePK4FYLf19kYaR5vZT9SmSl3Qj5sZ6eosc,15040
|
174
|
+
flwr_nightly-1.8.0.dev20240222.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
175
|
+
flwr_nightly-1.8.0.dev20240222.dist-info/entry_points.txt,sha256=S1zLNFLrz0uPWs4Zrgo2EPY0iQiIcCJHrIAlnQkkOBI,262
|
176
|
+
flwr_nightly-1.8.0.dev20240222.dist-info/RECORD,,
|
{flwr_nightly-1.8.0.dev20240220.dist-info → flwr_nightly-1.8.0.dev20240222.dist-info}/LICENSE
RENAMED
File without changes
|
File without changes
|
File without changes
|