flwr-nightly 1.13.0.dev20241106__py3-none-any.whl → 1.13.0.dev20241111__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.

Files changed (45) hide show
  1. flwr/cli/run/run.py +16 -5
  2. flwr/client/app.py +10 -6
  3. flwr/client/clientapp/app.py +21 -16
  4. flwr/client/nodestate/__init__.py +25 -0
  5. flwr/client/nodestate/in_memory_nodestate.py +38 -0
  6. flwr/client/nodestate/nodestate.py +30 -0
  7. flwr/client/nodestate/nodestate_factory.py +37 -0
  8. flwr/common/args.py +83 -0
  9. flwr/common/config.py +10 -0
  10. flwr/common/constant.py +0 -1
  11. flwr/common/logger.py +6 -2
  12. flwr/common/object_ref.py +47 -16
  13. flwr/common/typing.py +1 -1
  14. flwr/proto/exec_pb2.py +14 -17
  15. flwr/proto/exec_pb2.pyi +6 -20
  16. flwr/proto/run_pb2.py +32 -27
  17. flwr/proto/run_pb2.pyi +26 -0
  18. flwr/proto/simulationio_pb2.py +2 -2
  19. flwr/proto/simulationio_pb2_grpc.py +34 -0
  20. flwr/proto/simulationio_pb2_grpc.pyi +13 -0
  21. flwr/server/app.py +45 -20
  22. flwr/server/driver/driver.py +1 -1
  23. flwr/server/driver/grpc_driver.py +2 -6
  24. flwr/server/driver/inmemory_driver.py +1 -3
  25. flwr/server/run_serverapp.py +2 -2
  26. flwr/server/serverapp/app.py +16 -72
  27. flwr/server/strategy/aggregate.py +4 -4
  28. flwr/server/superlink/linkstate/in_memory_linkstate.py +5 -16
  29. flwr/server/superlink/linkstate/linkstate.py +5 -4
  30. flwr/server/superlink/linkstate/sqlite_linkstate.py +6 -15
  31. flwr/server/superlink/linkstate/utils.py +2 -33
  32. flwr/server/superlink/simulation/simulationio_servicer.py +22 -1
  33. flwr/simulation/__init__.py +3 -1
  34. flwr/simulation/app.py +273 -345
  35. flwr/simulation/legacy_app.py +382 -0
  36. flwr/simulation/run_simulation.py +1 -1
  37. flwr/superexec/deployment.py +1 -1
  38. flwr/superexec/exec_servicer.py +2 -2
  39. flwr/superexec/executor.py +4 -3
  40. flwr/superexec/simulation.py +44 -102
  41. {flwr_nightly-1.13.0.dev20241106.dist-info → flwr_nightly-1.13.0.dev20241111.dist-info}/METADATA +5 -4
  42. {flwr_nightly-1.13.0.dev20241106.dist-info → flwr_nightly-1.13.0.dev20241111.dist-info}/RECORD +45 -39
  43. {flwr_nightly-1.13.0.dev20241106.dist-info → flwr_nightly-1.13.0.dev20241111.dist-info}/entry_points.txt +1 -0
  44. {flwr_nightly-1.13.0.dev20241106.dist-info → flwr_nightly-1.13.0.dev20241111.dist-info}/LICENSE +0 -0
  45. {flwr_nightly-1.13.0.dev20241106.dist-info → flwr_nightly-1.13.0.dev20241111.dist-info}/WHEEL +0 -0
@@ -40,7 +40,6 @@ from .utils import (
40
40
  generate_rand_int_from_bytes,
41
41
  has_valid_sub_status,
42
42
  is_valid_transition,
43
- make_node_unavailable_taskres,
44
43
  )
45
44
 
46
45
 
@@ -257,21 +256,6 @@ class InMemoryLinkState(LinkState): # pylint: disable=R0902,R0904
257
256
  task_res_list.append(task_res)
258
257
  replied_task_ids.add(reply_to)
259
258
 
260
- # Check if the node is offline
261
- for task_id in task_ids - replied_task_ids:
262
- task_ins = self.task_ins_store.get(task_id)
263
- if task_ins is None:
264
- continue
265
- node_id = task_ins.task.consumer.node_id
266
- online_until, _ = self.node_ids[node_id]
267
- # Generate a TaskRes containing an error reply if the node is offline.
268
- if online_until < time.time():
269
- err_taskres = make_node_unavailable_taskres(
270
- ref_taskins=task_ins,
271
- )
272
- self.task_res_store[UUID(err_taskres.task_id)] = err_taskres
273
- task_res_list.append(err_taskres)
274
-
275
259
  # Mark all of them as delivered
276
260
  delivered_at = now().isoformat()
277
261
  for task_res in task_res_list:
@@ -451,6 +435,11 @@ class InMemoryLinkState(LinkState): # pylint: disable=R0902,R0904
451
435
  """Retrieve all currently stored `node_public_keys` as a set."""
452
436
  return self.node_public_keys
453
437
 
438
+ def get_run_ids(self) -> set[int]:
439
+ """Retrieve all run IDs."""
440
+ with self.lock:
441
+ return set(self.run_ids.keys())
442
+
454
443
  def get_run(self, run_id: int) -> Optional[Run]:
455
444
  """Retrieve information about the run with the specified `run_id`."""
456
445
  with self.lock:
@@ -163,6 +163,10 @@ class LinkState(abc.ABC): # pylint: disable=R0904
163
163
  ) -> int:
164
164
  """Create a new run for the specified `fab_hash`."""
165
165
 
166
+ @abc.abstractmethod
167
+ def get_run_ids(self) -> set[int]:
168
+ """Retrieve all run IDs."""
169
+
166
170
  @abc.abstractmethod
167
171
  def get_run(self, run_id: int) -> Optional[Run]:
168
172
  """Retrieve information about the run with the specified `run_id`.
@@ -175,10 +179,7 @@ class LinkState(abc.ABC): # pylint: disable=R0904
175
179
  Returns
176
180
  -------
177
181
  Optional[Run]
178
- A dataclass instance containing three elements if `run_id` is valid:
179
- - `run_id`: The identifier of the run, same as the specified `run_id`.
180
- - `fab_id`: The identifier of the FAB used in the specified run.
181
- - `fab_version`: The version of the FAB used in the specified run.
182
+ The `Run` instance if found; otherwise, `None`.
182
183
  """
183
184
 
184
185
  @abc.abstractmethod
@@ -57,7 +57,6 @@ from .utils import (
57
57
  generate_rand_int_from_bytes,
58
58
  has_valid_sub_status,
59
59
  is_valid_transition,
60
- make_node_unavailable_taskres,
61
60
  )
62
61
 
63
62
  SQL_CREATE_TABLE_NODE = """
@@ -640,20 +639,6 @@ class SqliteLinkState(LinkState): # pylint: disable=R0904
640
639
  data = {f"id_{i}": str(node_id) for i, node_id in enumerate(offline_node_ids)}
641
640
  task_ins_rows = self.query(query, data)
642
641
 
643
- # Make TaskRes containing node unavailabe error
644
- for row in task_ins_rows:
645
- for row in rows:
646
- # Convert values from sint64 to uint64
647
- convert_sint64_values_in_dict_to_uint64(
648
- row, ["run_id", "producer_node_id", "consumer_node_id"]
649
- )
650
-
651
- task_ins = dict_to_task_ins(row)
652
- err_taskres = make_node_unavailable_taskres(
653
- ref_taskins=task_ins,
654
- )
655
- result.append(err_taskres)
656
-
657
642
  return result
658
643
 
659
644
  def num_task_ins(self) -> int:
@@ -917,6 +902,12 @@ class SqliteLinkState(LinkState): # pylint: disable=R0904
917
902
  result: set[bytes] = {row["public_key"] for row in rows}
918
903
  return result
919
904
 
905
+ def get_run_ids(self) -> set[int]:
906
+ """Retrieve all run IDs."""
907
+ query = "SELECT run_id FROM run;"
908
+ rows = self.query(query)
909
+ return {convert_sint64_to_uint64(row["run_id"]) for row in rows}
910
+
920
911
  def get_run(self, run_id: int) -> Optional[Run]:
921
912
  """Retrieve information about the run with the specified `run_id`."""
922
913
  # Convert the uint64 value to sint64 for SQLite
@@ -15,21 +15,15 @@
15
15
  """Utility functions for State."""
16
16
 
17
17
 
18
- import time
19
- from logging import ERROR
20
18
  from os import urandom
21
- from uuid import uuid4
22
19
 
23
- from flwr.common import ConfigsRecord, Context, log, serde
24
- from flwr.common.constant import ErrorCode, Status, SubStatus
20
+ from flwr.common import ConfigsRecord, Context, serde
21
+ from flwr.common.constant import Status, SubStatus
25
22
  from flwr.common.typing import RunStatus
26
- from flwr.proto.error_pb2 import Error # pylint: disable=E0611
27
23
  from flwr.proto.message_pb2 import Context as ProtoContext # pylint: disable=E0611
28
- from flwr.proto.node_pb2 import Node # pylint: disable=E0611
29
24
 
30
25
  # pylint: disable=E0611
31
26
  from flwr.proto.recordset_pb2 import ConfigsRecord as ProtoConfigsRecord
32
- from flwr.proto.task_pb2 import Task, TaskIns, TaskRes # pylint: disable=E0611
33
27
 
34
28
  NODE_UNAVAILABLE_ERROR_REASON = (
35
29
  "Error: Node Unavailable - The destination node is currently unavailable. "
@@ -161,31 +155,6 @@ def configsrecord_from_bytes(configsrecord_bytes: bytes) -> ConfigsRecord:
161
155
  )
162
156
 
163
157
 
164
- def make_node_unavailable_taskres(ref_taskins: TaskIns) -> TaskRes:
165
- """Generate a TaskRes with a node unavailable error from a TaskIns."""
166
- current_time = time.time()
167
- ttl = ref_taskins.task.ttl - (current_time - ref_taskins.task.created_at)
168
- if ttl < 0:
169
- log(ERROR, "Creating TaskRes for TaskIns that exceeds its TTL.")
170
- ttl = 0
171
- return TaskRes(
172
- task_id=str(uuid4()),
173
- group_id=ref_taskins.group_id,
174
- run_id=ref_taskins.run_id,
175
- task=Task(
176
- producer=Node(node_id=ref_taskins.task.consumer.node_id, anonymous=False),
177
- consumer=Node(node_id=ref_taskins.task.producer.node_id, anonymous=False),
178
- created_at=current_time,
179
- ttl=ttl,
180
- ancestry=[ref_taskins.task_id],
181
- task_type=ref_taskins.task.task_type,
182
- error=Error(
183
- code=ErrorCode.NODE_UNAVAILABLE, reason=NODE_UNAVAILABLE_ERROR_REASON
184
- ),
185
- ),
186
- )
187
-
188
-
189
158
  def is_valid_transition(current_status: RunStatus, new_status: RunStatus) -> bool:
190
159
  """Check if a transition between two run statuses is valid.
191
160
 
@@ -23,6 +23,7 @@ from grpc import ServicerContext
23
23
  from flwr.common.constant import Status
24
24
  from flwr.common.logger import log
25
25
  from flwr.common.serde import (
26
+ configs_record_to_proto,
26
27
  context_from_proto,
27
28
  context_to_proto,
28
29
  fab_to_proto,
@@ -36,6 +37,8 @@ from flwr.proto.log_pb2 import ( # pylint: disable=E0611
36
37
  PushLogsResponse,
37
38
  )
38
39
  from flwr.proto.run_pb2 import ( # pylint: disable=E0611
40
+ GetFederationOptionsRequest,
41
+ GetFederationOptionsResponse,
39
42
  UpdateRunStatusRequest,
40
43
  UpdateRunStatusResponse,
41
44
  )
@@ -123,10 +126,28 @@ class SimulationIoServicer(simulationio_pb2_grpc.SimulationIoServicer):
123
126
  self, request: PushLogsRequest, context: grpc.ServicerContext
124
127
  ) -> PushLogsResponse:
125
128
  """Push logs."""
126
- log(DEBUG, "ServerAppIoServicer.PushLogs")
129
+ log(DEBUG, "SimultionIoServicer.PushLogs")
127
130
  state = self.state_factory.state()
128
131
 
129
132
  # Add logs to LinkState
130
133
  merged_logs = "".join(request.logs)
131
134
  state.add_serverapp_log(request.run_id, merged_logs)
132
135
  return PushLogsResponse()
136
+
137
+ def GetFederationOptions(
138
+ self, request: GetFederationOptionsRequest, context: ServicerContext
139
+ ) -> GetFederationOptionsResponse:
140
+ """Get Federation Options associated with a run."""
141
+ log(DEBUG, "SimultionIoServicer.GetFederationOptions")
142
+ state = self.state_factory.state()
143
+
144
+ federation_options = state.get_federation_options(request.run_id)
145
+ if federation_options is None:
146
+ context.abort(
147
+ grpc.StatusCode.FAILED_PRECONDITION,
148
+ "Expected federation options to be set, but none available.",
149
+ )
150
+ return GetFederationOptionsResponse()
151
+ return GetFederationOptionsResponse(
152
+ federation_options=configs_record_to_proto(federation_options)
153
+ )
@@ -17,13 +17,14 @@
17
17
 
18
18
  import importlib
19
19
 
20
+ from flwr.simulation.app import run_simulation_process
20
21
  from flwr.simulation.run_simulation import run_simulation
21
22
  from flwr.simulation.simulationio_connection import SimulationIoConnection
22
23
 
23
24
  is_ray_installed = importlib.util.find_spec("ray") is not None
24
25
 
25
26
  if is_ray_installed:
26
- from flwr.simulation.app import start_simulation
27
+ from flwr.simulation.legacy_app import start_simulation
27
28
  else:
28
29
  RAY_IMPORT_ERROR: str = """Unable to import module `ray`.
29
30
 
@@ -40,5 +41,6 @@ To install the necessary dependencies, install `flwr` with the `simulation` extr
40
41
  __all__ = [
41
42
  "SimulationIoConnection",
42
43
  "run_simulation",
44
+ "run_simulation_process",
43
45
  "start_simulation",
44
46
  ]