flwr-nightly 1.10.0.dev20240707__py3-none-any.whl → 1.11.0.dev20240724__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.
Potentially problematic release.
This version of flwr-nightly might be problematic. Click here for more details.
- flwr/cli/build.py +16 -2
- flwr/cli/config_utils.py +47 -27
- flwr/cli/install.py +17 -1
- flwr/cli/new/new.py +32 -21
- flwr/cli/new/templates/app/code/{client.hf.py.tpl → client.huggingface.py.tpl} +15 -5
- flwr/cli/new/templates/app/code/client.jax.py.tpl +2 -1
- flwr/cli/new/templates/app/code/client.mlx.py.tpl +36 -13
- flwr/cli/new/templates/app/code/client.numpy.py.tpl +2 -1
- flwr/cli/new/templates/app/code/client.pytorch.py.tpl +16 -5
- flwr/cli/new/templates/app/code/client.sklearn.py.tpl +6 -3
- flwr/cli/new/templates/app/code/client.tensorflow.py.tpl +25 -5
- flwr/cli/new/templates/app/code/flwr_tune/app.py.tpl +22 -19
- flwr/cli/new/templates/app/code/flwr_tune/client.py.tpl +5 -3
- flwr/cli/new/templates/app/code/flwr_tune/server.py.tpl +1 -1
- flwr/cli/new/templates/app/code/server.huggingface.py.tpl +23 -0
- flwr/cli/new/templates/app/code/server.jax.py.tpl +16 -8
- flwr/cli/new/templates/app/code/server.mlx.py.tpl +12 -7
- flwr/cli/new/templates/app/code/server.numpy.py.tpl +16 -8
- flwr/cli/new/templates/app/code/server.pytorch.py.tpl +15 -13
- flwr/cli/new/templates/app/code/server.sklearn.py.tpl +17 -10
- flwr/cli/new/templates/app/code/server.tensorflow.py.tpl +16 -13
- flwr/cli/new/templates/app/code/{task.hf.py.tpl → task.huggingface.py.tpl} +14 -2
- flwr/cli/new/templates/app/code/task.mlx.py.tpl +14 -2
- flwr/cli/new/templates/app/code/task.pytorch.py.tpl +14 -3
- flwr/cli/new/templates/app/code/task.tensorflow.py.tpl +13 -1
- flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +9 -12
- flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +38 -0
- flwr/cli/new/templates/app/pyproject.jax.toml.tpl +17 -11
- flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +17 -12
- flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +12 -12
- flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +13 -12
- flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +12 -12
- flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +15 -12
- flwr/cli/run/run.py +133 -54
- flwr/client/app.py +56 -24
- flwr/client/client_app.py +28 -8
- flwr/client/grpc_adapter_client/connection.py +3 -2
- flwr/client/grpc_client/connection.py +3 -2
- flwr/client/grpc_rere_client/connection.py +17 -6
- flwr/client/message_handler/message_handler.py +1 -1
- flwr/client/node_state.py +59 -12
- flwr/client/node_state_tests.py +4 -3
- flwr/client/rest_client/connection.py +19 -8
- flwr/client/supernode/app.py +39 -39
- flwr/client/typing.py +2 -2
- flwr/common/config.py +92 -2
- flwr/common/constant.py +3 -0
- flwr/common/context.py +24 -9
- flwr/common/logger.py +25 -0
- flwr/common/object_ref.py +84 -21
- flwr/common/serde.py +45 -0
- flwr/common/telemetry.py +17 -0
- flwr/common/typing.py +5 -0
- flwr/proto/common_pb2.py +36 -0
- flwr/proto/common_pb2.pyi +121 -0
- flwr/proto/common_pb2_grpc.py +4 -0
- flwr/proto/common_pb2_grpc.pyi +4 -0
- flwr/proto/driver_pb2.py +24 -19
- flwr/proto/driver_pb2.pyi +21 -1
- flwr/proto/exec_pb2.py +20 -11
- flwr/proto/exec_pb2.pyi +41 -1
- flwr/proto/run_pb2.py +12 -7
- flwr/proto/run_pb2.pyi +22 -1
- flwr/proto/task_pb2.py +7 -8
- flwr/server/__init__.py +2 -0
- flwr/server/compat/legacy_context.py +5 -4
- flwr/server/driver/grpc_driver.py +82 -140
- flwr/server/run_serverapp.py +40 -18
- flwr/server/server_app.py +56 -10
- flwr/server/serverapp_components.py +52 -0
- flwr/server/superlink/driver/driver_servicer.py +18 -3
- flwr/server/superlink/fleet/message_handler/message_handler.py +13 -2
- flwr/server/superlink/fleet/vce/backend/__init__.py +1 -1
- flwr/server/superlink/fleet/vce/backend/backend.py +4 -4
- flwr/server/superlink/fleet/vce/backend/raybackend.py +10 -10
- flwr/server/superlink/fleet/vce/vce_api.py +149 -117
- flwr/server/superlink/state/in_memory_state.py +11 -3
- flwr/server/superlink/state/sqlite_state.py +23 -8
- flwr/server/superlink/state/state.py +7 -2
- flwr/server/typing.py +2 -0
- flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +18 -2
- flwr/simulation/__init__.py +1 -1
- flwr/simulation/app.py +4 -3
- flwr/simulation/ray_transport/ray_actor.py +15 -19
- flwr/simulation/ray_transport/ray_client_proxy.py +22 -9
- flwr/simulation/run_simulation.py +269 -70
- flwr/superexec/app.py +17 -11
- flwr/superexec/deployment.py +111 -35
- flwr/superexec/exec_grpc.py +5 -1
- flwr/superexec/exec_servicer.py +6 -1
- flwr/superexec/executor.py +21 -0
- flwr/superexec/simulation.py +181 -0
- {flwr_nightly-1.10.0.dev20240707.dist-info → flwr_nightly-1.11.0.dev20240724.dist-info}/METADATA +3 -2
- {flwr_nightly-1.10.0.dev20240707.dist-info → flwr_nightly-1.11.0.dev20240724.dist-info}/RECORD +97 -91
- flwr/cli/new/templates/app/code/server.hf.py.tpl +0 -17
- flwr/cli/new/templates/app/pyproject.hf.toml.tpl +0 -37
- {flwr_nightly-1.10.0.dev20240707.dist-info → flwr_nightly-1.11.0.dev20240724.dist-info}/LICENSE +0 -0
- {flwr_nightly-1.10.0.dev20240707.dist-info → flwr_nightly-1.11.0.dev20240724.dist-info}/WHEEL +0 -0
- {flwr_nightly-1.10.0.dev20240707.dist-info → flwr_nightly-1.11.0.dev20240724.dist-info}/entry_points.txt +0 -0
flwr/superexec/deployment.py
CHANGED
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
"""Deployment engine executor."""
|
|
16
16
|
|
|
17
17
|
import subprocess
|
|
18
|
-
import sys
|
|
19
18
|
from logging import ERROR, INFO
|
|
19
|
+
from pathlib import Path
|
|
20
20
|
from typing import Optional
|
|
21
21
|
|
|
22
22
|
from typing_extensions import override
|
|
@@ -25,6 +25,8 @@ from flwr.cli.config_utils import get_fab_metadata
|
|
|
25
25
|
from flwr.cli.install import install_from_fab
|
|
26
26
|
from flwr.common.grpc import create_channel
|
|
27
27
|
from flwr.common.logger import log
|
|
28
|
+
from flwr.common.serde import user_config_to_proto
|
|
29
|
+
from flwr.common.typing import UserConfig
|
|
28
30
|
from flwr.proto.driver_pb2 import CreateRunRequest # pylint: disable=E0611
|
|
29
31
|
from flwr.proto.driver_pb2_grpc import DriverStub
|
|
30
32
|
from flwr.server.driver.grpc_driver import DEFAULT_SERVER_ADDRESS_DRIVER
|
|
@@ -33,65 +35,139 @@ from .executor import Executor, RunTracker
|
|
|
33
35
|
|
|
34
36
|
|
|
35
37
|
class DeploymentEngine(Executor):
|
|
36
|
-
"""Deployment engine executor.
|
|
38
|
+
"""Deployment engine executor.
|
|
39
|
+
|
|
40
|
+
Parameters
|
|
41
|
+
----------
|
|
42
|
+
superlink: str (default: "0.0.0.0:9091")
|
|
43
|
+
Address of the SuperLink to connect to.
|
|
44
|
+
root_certificates: Optional[str] (default: None)
|
|
45
|
+
Specifies the path to the PEM-encoded root certificate file for
|
|
46
|
+
establishing secure HTTPS connections.
|
|
47
|
+
flwr_dir: Optional[str] (default: None)
|
|
48
|
+
The path containing installed Flower Apps.
|
|
49
|
+
"""
|
|
37
50
|
|
|
38
51
|
def __init__(
|
|
39
52
|
self,
|
|
40
|
-
|
|
41
|
-
root_certificates: Optional[
|
|
53
|
+
superlink: str = DEFAULT_SERVER_ADDRESS_DRIVER,
|
|
54
|
+
root_certificates: Optional[str] = None,
|
|
55
|
+
flwr_dir: Optional[str] = None,
|
|
42
56
|
) -> None:
|
|
43
|
-
self.
|
|
44
|
-
|
|
57
|
+
self.superlink = superlink
|
|
58
|
+
if root_certificates is None:
|
|
59
|
+
self.root_certificates = None
|
|
60
|
+
self.root_certificates_bytes = None
|
|
61
|
+
else:
|
|
62
|
+
self.root_certificates = root_certificates
|
|
63
|
+
self.root_certificates_bytes = Path(root_certificates).read_bytes()
|
|
64
|
+
self.flwr_dir = flwr_dir
|
|
45
65
|
self.stub: Optional[DriverStub] = None
|
|
46
66
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
67
|
+
@override
|
|
68
|
+
def set_config(
|
|
69
|
+
self,
|
|
70
|
+
config: UserConfig,
|
|
71
|
+
) -> None:
|
|
72
|
+
"""Set executor config arguments.
|
|
73
|
+
|
|
74
|
+
Parameters
|
|
75
|
+
----------
|
|
76
|
+
config : UserConfig
|
|
77
|
+
A dictionary for configuration values.
|
|
78
|
+
Supported configuration key/value pairs:
|
|
79
|
+
- "superlink": str
|
|
80
|
+
The address of the SuperLink Driver API.
|
|
81
|
+
- "root-certificates": str
|
|
82
|
+
The path to the root certificates.
|
|
83
|
+
- "flwr-dir": str
|
|
84
|
+
The path to the Flower directory.
|
|
85
|
+
"""
|
|
86
|
+
if not config:
|
|
87
|
+
return
|
|
88
|
+
if superlink_address := config.get("superlink"):
|
|
89
|
+
if not isinstance(superlink_address, str):
|
|
90
|
+
raise ValueError("The `superlink` value should be of type `str`.")
|
|
91
|
+
self.superlink = superlink_address
|
|
92
|
+
if root_certificates := config.get("root-certificates"):
|
|
93
|
+
if not isinstance(root_certificates, str):
|
|
94
|
+
raise ValueError(
|
|
95
|
+
"The `root-certificates` value should be of type `str`."
|
|
96
|
+
)
|
|
97
|
+
self.root_certificates = root_certificates
|
|
98
|
+
self.root_certificates_bytes = Path(str(root_certificates)).read_bytes()
|
|
99
|
+
if flwr_dir := config.get("flwr-dir"):
|
|
100
|
+
if not isinstance(flwr_dir, str):
|
|
101
|
+
raise ValueError("The `flwr-dir` value should be of type `str`.")
|
|
102
|
+
self.flwr_dir = str(flwr_dir)
|
|
55
103
|
|
|
56
|
-
def
|
|
104
|
+
def _connect(self) -> None:
|
|
105
|
+
if self.stub is not None:
|
|
106
|
+
return
|
|
107
|
+
channel = create_channel(
|
|
108
|
+
server_address=self.superlink,
|
|
109
|
+
insecure=(self.root_certificates_bytes is None),
|
|
110
|
+
root_certificates=self.root_certificates_bytes,
|
|
111
|
+
)
|
|
112
|
+
self.stub = DriverStub(channel)
|
|
113
|
+
|
|
114
|
+
def _create_run(
|
|
115
|
+
self,
|
|
116
|
+
fab_id: str,
|
|
117
|
+
fab_version: str,
|
|
118
|
+
override_config: UserConfig,
|
|
119
|
+
) -> int:
|
|
57
120
|
if self.stub is None:
|
|
58
121
|
self._connect()
|
|
59
122
|
|
|
60
123
|
assert self.stub is not None
|
|
61
124
|
|
|
62
|
-
req = CreateRunRequest(
|
|
125
|
+
req = CreateRunRequest(
|
|
126
|
+
fab_id=fab_id,
|
|
127
|
+
fab_version=fab_version,
|
|
128
|
+
override_config=user_config_to_proto(override_config),
|
|
129
|
+
)
|
|
63
130
|
res = self.stub.CreateRun(request=req)
|
|
64
131
|
return int(res.run_id)
|
|
65
132
|
|
|
66
133
|
@override
|
|
67
|
-
def start_run(
|
|
134
|
+
def start_run(
|
|
135
|
+
self,
|
|
136
|
+
fab_file: bytes,
|
|
137
|
+
override_config: UserConfig,
|
|
138
|
+
federation_config: UserConfig,
|
|
139
|
+
) -> Optional[RunTracker]:
|
|
68
140
|
"""Start run using the Flower Deployment Engine."""
|
|
69
141
|
try:
|
|
70
142
|
# Install FAB to flwr dir
|
|
71
143
|
fab_version, fab_id = get_fab_metadata(fab_file)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
# Install FAB Python package
|
|
75
|
-
subprocess.check_call(
|
|
76
|
-
[sys.executable, "-m", "pip", "install", str(fab_path)],
|
|
77
|
-
stdout=subprocess.DEVNULL,
|
|
78
|
-
stderr=subprocess.DEVNULL,
|
|
79
|
-
)
|
|
144
|
+
install_from_fab(fab_file, None, True)
|
|
80
145
|
|
|
81
146
|
# Call SuperLink to create run
|
|
82
|
-
run_id: int = self._create_run(fab_id, fab_version)
|
|
147
|
+
run_id: int = self._create_run(fab_id, fab_version, override_config)
|
|
83
148
|
log(INFO, "Created run %s", str(run_id))
|
|
84
149
|
|
|
85
|
-
|
|
150
|
+
command = [
|
|
151
|
+
"flower-server-app",
|
|
152
|
+
"--run-id",
|
|
153
|
+
str(run_id),
|
|
154
|
+
"--superlink",
|
|
155
|
+
str(self.superlink),
|
|
156
|
+
]
|
|
157
|
+
|
|
158
|
+
if self.flwr_dir:
|
|
159
|
+
command.append("--flwr-dir")
|
|
160
|
+
command.append(self.flwr_dir)
|
|
161
|
+
|
|
162
|
+
if self.root_certificates is None:
|
|
163
|
+
command.append("--insecure")
|
|
164
|
+
else:
|
|
165
|
+
command.append("--root-certificates")
|
|
166
|
+
command.append(self.root_certificates)
|
|
167
|
+
|
|
168
|
+
# Execute the command
|
|
86
169
|
proc = subprocess.Popen( # pylint: disable=consider-using-with
|
|
87
|
-
|
|
88
|
-
"flower-server-app",
|
|
89
|
-
"--run-id",
|
|
90
|
-
str(run_id),
|
|
91
|
-
"--insecure",
|
|
92
|
-
],
|
|
93
|
-
stdout=subprocess.PIPE,
|
|
94
|
-
stderr=subprocess.PIPE,
|
|
170
|
+
command,
|
|
95
171
|
text=True,
|
|
96
172
|
)
|
|
97
173
|
log(INFO, "Started run %s", str(run_id))
|
flwr/superexec/exec_grpc.py
CHANGED
|
@@ -21,6 +21,7 @@ import grpc
|
|
|
21
21
|
|
|
22
22
|
from flwr.common import GRPC_MAX_MESSAGE_LENGTH
|
|
23
23
|
from flwr.common.logger import log
|
|
24
|
+
from flwr.common.typing import UserConfig
|
|
24
25
|
from flwr.proto.exec_pb2_grpc import add_ExecServicer_to_server
|
|
25
26
|
from flwr.server.superlink.fleet.grpc_bidi.grpc_server import generic_create_grpc_server
|
|
26
27
|
|
|
@@ -32,8 +33,11 @@ def run_superexec_api_grpc(
|
|
|
32
33
|
address: str,
|
|
33
34
|
executor: Executor,
|
|
34
35
|
certificates: Optional[Tuple[bytes, bytes, bytes]],
|
|
36
|
+
config: UserConfig,
|
|
35
37
|
) -> grpc.Server:
|
|
36
38
|
"""Run SuperExec API (gRPC, request-response)."""
|
|
39
|
+
executor.set_config(config)
|
|
40
|
+
|
|
37
41
|
exec_servicer: grpc.Server = ExecServicer(
|
|
38
42
|
executor=executor,
|
|
39
43
|
)
|
|
@@ -45,7 +49,7 @@ def run_superexec_api_grpc(
|
|
|
45
49
|
certificates=certificates,
|
|
46
50
|
)
|
|
47
51
|
|
|
48
|
-
log(INFO, "Flower
|
|
52
|
+
log(INFO, "Starting Flower SuperExec gRPC server on %s", address)
|
|
49
53
|
superexec_grpc_server.start()
|
|
50
54
|
|
|
51
55
|
return superexec_grpc_server
|
flwr/superexec/exec_servicer.py
CHANGED
|
@@ -21,6 +21,7 @@ from typing import Any, Dict, Generator
|
|
|
21
21
|
import grpc
|
|
22
22
|
|
|
23
23
|
from flwr.common.logger import log
|
|
24
|
+
from flwr.common.serde import user_config_from_proto
|
|
24
25
|
from flwr.proto import exec_pb2_grpc # pylint: disable=E0611
|
|
25
26
|
from flwr.proto.exec_pb2 import ( # pylint: disable=E0611
|
|
26
27
|
StartRunRequest,
|
|
@@ -45,7 +46,11 @@ class ExecServicer(exec_pb2_grpc.ExecServicer):
|
|
|
45
46
|
"""Create run ID."""
|
|
46
47
|
log(INFO, "ExecServicer.StartRun")
|
|
47
48
|
|
|
48
|
-
run = self.executor.start_run(
|
|
49
|
+
run = self.executor.start_run(
|
|
50
|
+
request.fab_file,
|
|
51
|
+
user_config_from_proto(request.override_config),
|
|
52
|
+
user_config_from_proto(request.federation_config),
|
|
53
|
+
)
|
|
49
54
|
|
|
50
55
|
if run is None:
|
|
51
56
|
log(ERROR, "Executor failed to start run")
|
flwr/superexec/executor.py
CHANGED
|
@@ -19,6 +19,8 @@ from dataclasses import dataclass
|
|
|
19
19
|
from subprocess import Popen
|
|
20
20
|
from typing import Optional
|
|
21
21
|
|
|
22
|
+
from flwr.common.typing import UserConfig
|
|
23
|
+
|
|
22
24
|
|
|
23
25
|
@dataclass
|
|
24
26
|
class RunTracker:
|
|
@@ -31,10 +33,25 @@ class RunTracker:
|
|
|
31
33
|
class Executor(ABC):
|
|
32
34
|
"""Execute and monitor a Flower run."""
|
|
33
35
|
|
|
36
|
+
@abstractmethod
|
|
37
|
+
def set_config(
|
|
38
|
+
self,
|
|
39
|
+
config: UserConfig,
|
|
40
|
+
) -> None:
|
|
41
|
+
"""Register provided config as class attributes.
|
|
42
|
+
|
|
43
|
+
Parameters
|
|
44
|
+
----------
|
|
45
|
+
config : UserConfig
|
|
46
|
+
A dictionary for configuration values.
|
|
47
|
+
"""
|
|
48
|
+
|
|
34
49
|
@abstractmethod
|
|
35
50
|
def start_run(
|
|
36
51
|
self,
|
|
37
52
|
fab_file: bytes,
|
|
53
|
+
override_config: UserConfig,
|
|
54
|
+
federation_config: UserConfig,
|
|
38
55
|
) -> Optional[RunTracker]:
|
|
39
56
|
"""Start a run using the given Flower FAB ID and version.
|
|
40
57
|
|
|
@@ -45,6 +62,10 @@ class Executor(ABC):
|
|
|
45
62
|
----------
|
|
46
63
|
fab_file : bytes
|
|
47
64
|
The Flower App Bundle file bytes.
|
|
65
|
+
override_config: UserConfig
|
|
66
|
+
The config overrides dict sent by the user (using `flwr run`).
|
|
67
|
+
federation_config: UserConfig
|
|
68
|
+
The federation options dict sent by the user (using `flwr run`).
|
|
48
69
|
|
|
49
70
|
Returns
|
|
50
71
|
-------
|
|
@@ -0,0 +1,181 @@
|
|
|
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
|
+
"""Simulation engine executor."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
import subprocess
|
|
19
|
+
import sys
|
|
20
|
+
from logging import ERROR, INFO, WARN
|
|
21
|
+
from typing import Optional
|
|
22
|
+
|
|
23
|
+
from typing_extensions import override
|
|
24
|
+
|
|
25
|
+
from flwr.cli.config_utils import load_and_validate
|
|
26
|
+
from flwr.cli.install import install_from_fab
|
|
27
|
+
from flwr.common.constant import RUN_ID_NUM_BYTES
|
|
28
|
+
from flwr.common.logger import log
|
|
29
|
+
from flwr.common.typing import UserConfig
|
|
30
|
+
from flwr.server.superlink.state.utils import generate_rand_int_from_bytes
|
|
31
|
+
|
|
32
|
+
from .executor import Executor, RunTracker
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _user_config_to_str(user_config: UserConfig) -> str:
|
|
36
|
+
"""Convert override user config to string."""
|
|
37
|
+
user_config_list_str = []
|
|
38
|
+
for key, value in user_config.items():
|
|
39
|
+
if isinstance(value, bool):
|
|
40
|
+
user_config_list_str.append(f"{key}={str(value).lower()}")
|
|
41
|
+
elif isinstance(value, (int, float)):
|
|
42
|
+
user_config_list_str.append(f"{key}={value}")
|
|
43
|
+
elif isinstance(value, str):
|
|
44
|
+
user_config_list_str.append(f'{key}="{value}"')
|
|
45
|
+
else:
|
|
46
|
+
raise ValueError(
|
|
47
|
+
"Only types `bool`, `float`, `int` and `str` are supported"
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
user_config_str = ",".join(user_config_list_str)
|
|
51
|
+
return user_config_str
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class SimulationEngine(Executor):
|
|
55
|
+
"""Simulation engine executor.
|
|
56
|
+
|
|
57
|
+
Parameters
|
|
58
|
+
----------
|
|
59
|
+
num_supernodes: Opitonal[str] (default: None)
|
|
60
|
+
Total number of nodes to involve in the simulation.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
def __init__(
|
|
64
|
+
self,
|
|
65
|
+
num_supernodes: Optional[int] = None,
|
|
66
|
+
) -> None:
|
|
67
|
+
self.num_supernodes = num_supernodes
|
|
68
|
+
|
|
69
|
+
@override
|
|
70
|
+
def set_config(
|
|
71
|
+
self,
|
|
72
|
+
config: UserConfig,
|
|
73
|
+
) -> None:
|
|
74
|
+
"""Set executor config arguments.
|
|
75
|
+
|
|
76
|
+
Parameters
|
|
77
|
+
----------
|
|
78
|
+
config : UserConfig
|
|
79
|
+
A dictionary for configuration values.
|
|
80
|
+
Supported configuration key/value pairs:
|
|
81
|
+
- "num-supernodes": int
|
|
82
|
+
Number of nodes to register for the simulation.
|
|
83
|
+
"""
|
|
84
|
+
if num_supernodes := config.get("num-supernodes"):
|
|
85
|
+
if not isinstance(num_supernodes, int):
|
|
86
|
+
raise ValueError("The `num-supernodes` value should be of type `int`.")
|
|
87
|
+
self.num_supernodes = num_supernodes
|
|
88
|
+
elif self.num_supernodes is None:
|
|
89
|
+
log(
|
|
90
|
+
ERROR,
|
|
91
|
+
"To start a run with the simulation plugin, please specify "
|
|
92
|
+
"the number of SuperNodes. This can be done by using the "
|
|
93
|
+
"`--executor-config` argument when launching the SuperExec.",
|
|
94
|
+
)
|
|
95
|
+
raise ValueError(
|
|
96
|
+
"`num-supernodes` must not be `None`, it must be a valid "
|
|
97
|
+
"positive integer."
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
@override
|
|
101
|
+
def start_run(
|
|
102
|
+
self,
|
|
103
|
+
fab_file: bytes,
|
|
104
|
+
override_config: UserConfig,
|
|
105
|
+
federation_config: UserConfig,
|
|
106
|
+
) -> Optional[RunTracker]:
|
|
107
|
+
"""Start run using the Flower Simulation Engine."""
|
|
108
|
+
if self.num_supernodes is None:
|
|
109
|
+
raise ValueError(
|
|
110
|
+
"Error in `SuperExec` (`SimulationEngine` executor):\n\n"
|
|
111
|
+
"`num-supernodes` must not be `None`, it must be a valid "
|
|
112
|
+
"positive integer. In order to start this simulation executor "
|
|
113
|
+
"with a specified number of `SuperNodes`, you can either provide "
|
|
114
|
+
"a `--executor` that has been initialized with a number of nodes "
|
|
115
|
+
"to the `flower-superexec` CLI, or `--executor-config num-supernodes=N`"
|
|
116
|
+
"to the `flower-superexec` CLI."
|
|
117
|
+
)
|
|
118
|
+
try:
|
|
119
|
+
|
|
120
|
+
# Install FAB to flwr dir
|
|
121
|
+
fab_path = install_from_fab(fab_file, None, True)
|
|
122
|
+
|
|
123
|
+
# Install FAB Python package
|
|
124
|
+
subprocess.check_call(
|
|
125
|
+
[sys.executable, "-m", "pip", "install", "--no-deps", str(fab_path)],
|
|
126
|
+
stdout=subprocess.DEVNULL,
|
|
127
|
+
stderr=subprocess.DEVNULL,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# Load and validate config
|
|
131
|
+
config, errors, warnings = load_and_validate(fab_path / "pyproject.toml")
|
|
132
|
+
if errors:
|
|
133
|
+
raise ValueError(errors)
|
|
134
|
+
|
|
135
|
+
if warnings:
|
|
136
|
+
log(WARN, warnings)
|
|
137
|
+
|
|
138
|
+
if config is None:
|
|
139
|
+
raise ValueError(
|
|
140
|
+
"Config extracted from FAB's pyproject.toml is not valid"
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
# In Simulation there is no SuperLink, still we create a run_id
|
|
144
|
+
run_id = generate_rand_int_from_bytes(RUN_ID_NUM_BYTES)
|
|
145
|
+
log(INFO, "Created run %s", str(run_id))
|
|
146
|
+
|
|
147
|
+
# Prepare commnand
|
|
148
|
+
command = [
|
|
149
|
+
"flower-simulation",
|
|
150
|
+
"--app",
|
|
151
|
+
f"{str(fab_path)}",
|
|
152
|
+
"--num-supernodes",
|
|
153
|
+
f"{federation_config.get('num-supernodes', self.num_supernodes)}",
|
|
154
|
+
"--run-id",
|
|
155
|
+
str(run_id),
|
|
156
|
+
]
|
|
157
|
+
|
|
158
|
+
if override_config:
|
|
159
|
+
override_config_str = _user_config_to_str(override_config)
|
|
160
|
+
command.extend(["--run-config", f"{override_config_str}"])
|
|
161
|
+
|
|
162
|
+
# Start Simulation
|
|
163
|
+
proc = subprocess.Popen( # pylint: disable=consider-using-with
|
|
164
|
+
command,
|
|
165
|
+
text=True,
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
log(INFO, "Started run %s", str(run_id))
|
|
169
|
+
|
|
170
|
+
return RunTracker(
|
|
171
|
+
run_id=run_id,
|
|
172
|
+
proc=proc,
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# pylint: disable-next=broad-except
|
|
176
|
+
except Exception as e:
|
|
177
|
+
log(ERROR, "Could not start run: %s", str(e))
|
|
178
|
+
return None
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
executor = SimulationEngine()
|
{flwr_nightly-1.10.0.dev20240707.dist-info → flwr_nightly-1.11.0.dev20240724.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: flwr-nightly
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.11.0.dev20240724
|
|
4
4
|
Summary: Flower: A Friendly Federated Learning Framework
|
|
5
5
|
Home-page: https://flower.ai
|
|
6
6
|
License: Apache-2.0
|
|
@@ -33,7 +33,7 @@ Classifier: Typing :: Typed
|
|
|
33
33
|
Provides-Extra: rest
|
|
34
34
|
Provides-Extra: simulation
|
|
35
35
|
Requires-Dist: cryptography (>=42.0.4,<43.0.0)
|
|
36
|
-
Requires-Dist: grpcio (>=1.60.0,<2.0.0)
|
|
36
|
+
Requires-Dist: grpcio (>=1.60.0,<2.0.0,!=1.64.2,!=1.65.1)
|
|
37
37
|
Requires-Dist: iterators (>=0.0.2,<0.0.3)
|
|
38
38
|
Requires-Dist: numpy (>=1.21.0,<2.0.0)
|
|
39
39
|
Requires-Dist: pathspec (>=0.12.1,<0.13.0)
|
|
@@ -43,6 +43,7 @@ Requires-Dist: ray (==2.10.0) ; (python_version >= "3.8" and python_version < "3
|
|
|
43
43
|
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: tomli (>=2.0.1,<3.0.0)
|
|
46
|
+
Requires-Dist: tomli-w (>=1.0.0,<2.0.0)
|
|
46
47
|
Requires-Dist: typer[all] (>=0.9.0,<0.10.0)
|
|
47
48
|
Requires-Dist: uvicorn[standard] (>=0.23.0,<0.24.0) ; extra == "rest"
|
|
48
49
|
Project-URL: Documentation, https://flower.ai
|