flwr-nightly 1.20.0.dev20250627__py3-none-any.whl → 1.20.0.dev20250630__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/common/constant.py +1 -0
- flwr/common/inflatable_utils.py +2 -0
- flwr/common/record/array.py +88 -22
- flwr/common/record/arraychunk.py +62 -0
- {flwr_nightly-1.20.0.dev20250627.dist-info → flwr_nightly-1.20.0.dev20250630.dist-info}/METADATA +1 -1
- {flwr_nightly-1.20.0.dev20250627.dist-info → flwr_nightly-1.20.0.dev20250630.dist-info}/RECORD +8 -7
- {flwr_nightly-1.20.0.dev20250627.dist-info → flwr_nightly-1.20.0.dev20250630.dist-info}/WHEEL +0 -0
- {flwr_nightly-1.20.0.dev20250627.dist-info → flwr_nightly-1.20.0.dev20250630.dist-info}/entry_points.txt +0 -0
flwr/common/constant.py
CHANGED
@@ -135,6 +135,7 @@ GC_THRESHOLD = 200_000_000 # 200 MB
|
|
135
135
|
# Constants for Inflatable
|
136
136
|
HEAD_BODY_DIVIDER = b"\x00"
|
137
137
|
HEAD_VALUE_DIVIDER = " "
|
138
|
+
MAX_ARRAY_CHUNK_SIZE = 20_971_520 # 20 MB
|
138
139
|
|
139
140
|
# Constants for serialization
|
140
141
|
INT64_MAX_VALUE = 9223372036854775807 # (1 << 63) - 1
|
flwr/common/inflatable_utils.py
CHANGED
@@ -40,9 +40,11 @@ from .inflatable import (
|
|
40
40
|
)
|
41
41
|
from .message import Message
|
42
42
|
from .record import Array, ArrayRecord, ConfigRecord, MetricRecord, RecordDict
|
43
|
+
from .record.arraychunk import ArrayChunk
|
43
44
|
|
44
45
|
# Helper registry that maps names of classes to their type
|
45
46
|
inflatable_class_registry: dict[str, type[InflatableObject]] = {
|
47
|
+
ArrayChunk.__qualname__: ArrayChunk,
|
46
48
|
Array.__qualname__: Array,
|
47
49
|
ArrayRecord.__qualname__: ArrayRecord,
|
48
50
|
ConfigRecord.__qualname__: ConfigRecord,
|
flwr/common/record/array.py
CHANGED
@@ -17,6 +17,7 @@
|
|
17
17
|
|
18
18
|
from __future__ import annotations
|
19
19
|
|
20
|
+
import json
|
20
21
|
import sys
|
21
22
|
from dataclasses import dataclass
|
22
23
|
from io import BytesIO
|
@@ -24,11 +25,15 @@ from typing import TYPE_CHECKING, Any, cast, overload
|
|
24
25
|
|
25
26
|
import numpy as np
|
26
27
|
|
27
|
-
from
|
28
|
-
|
29
|
-
|
30
|
-
|
28
|
+
from ..constant import MAX_ARRAY_CHUNK_SIZE, SType
|
29
|
+
from ..inflatable import (
|
30
|
+
InflatableObject,
|
31
|
+
add_header_to_object_body,
|
32
|
+
get_object_body,
|
33
|
+
get_object_children_ids_from_object_content,
|
34
|
+
)
|
31
35
|
from ..typing import NDArray
|
36
|
+
from .arraychunk import ArrayChunk
|
32
37
|
|
33
38
|
if TYPE_CHECKING:
|
34
39
|
import torch
|
@@ -252,16 +257,48 @@ class Array(InflatableObject):
|
|
252
257
|
ndarray_deserialized = np.load(bytes_io, allow_pickle=False)
|
253
258
|
return cast(NDArray, ndarray_deserialized)
|
254
259
|
|
260
|
+
@property
|
261
|
+
def children(self) -> dict[str, InflatableObject]:
|
262
|
+
"""Return a dictionary of ArrayChunks with their Object IDs as keys."""
|
263
|
+
return dict(self.slice_array())
|
264
|
+
|
265
|
+
def slice_array(self) -> list[tuple[str, InflatableObject]]:
|
266
|
+
"""Slice Array data and construct a list of ArrayChunks."""
|
267
|
+
children: list[tuple[str, InflatableObject]] = []
|
268
|
+
# memoryview allows for zero-copy slicing
|
269
|
+
data_view = memoryview(self.data)
|
270
|
+
for start in range(0, len(data_view), MAX_ARRAY_CHUNK_SIZE):
|
271
|
+
end = min(start + MAX_ARRAY_CHUNK_SIZE, len(data_view))
|
272
|
+
ac = ArrayChunk(data_view[start:end])
|
273
|
+
children.append((ac.object_id, ac))
|
274
|
+
return children
|
275
|
+
|
255
276
|
def deflate(self) -> bytes:
|
256
277
|
"""Deflate the Array."""
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
)
|
263
|
-
|
264
|
-
|
278
|
+
array_metadata: dict[str, str | tuple[int, ...] | list[int]] = {}
|
279
|
+
|
280
|
+
# We want to record all object_id even if repeated
|
281
|
+
# it can happend that chunks carry the exact same data
|
282
|
+
# for example when the array has only zeros
|
283
|
+
children_list = self.slice_array()
|
284
|
+
# Let's not save the entire object_id but a mapping to those
|
285
|
+
# that will be carried in the object head
|
286
|
+
# (replace a long object_id with a single scalar)
|
287
|
+
unique_children = list(self.children.keys())
|
288
|
+
arraychunk_ids = [unique_children.index(ch_id) for ch_id, _ in children_list]
|
289
|
+
|
290
|
+
# The deflated Array carries everything but the data
|
291
|
+
# The `arraychunk_ids` will be used during Array inflation
|
292
|
+
# to rematerialize the data from ArrayChunk objects.
|
293
|
+
array_metadata = {
|
294
|
+
"dtype": self.dtype,
|
295
|
+
"shape": self.shape,
|
296
|
+
"stype": self.stype,
|
297
|
+
"arraychunk_ids": arraychunk_ids,
|
298
|
+
}
|
299
|
+
|
300
|
+
# Serialize metadata dict
|
301
|
+
obj_body = json.dumps(array_metadata).encode("utf-8")
|
265
302
|
return add_header_to_object_body(object_body=obj_body, obj=self)
|
266
303
|
|
267
304
|
@classmethod
|
@@ -276,26 +313,55 @@ class Array(InflatableObject):
|
|
276
313
|
The deflated object content of the Array.
|
277
314
|
|
278
315
|
children : Optional[dict[str, InflatableObject]] (default: None)
|
279
|
-
Must be ``None``. ``Array``
|
280
|
-
Providing
|
316
|
+
Must be ``None``. ``Array`` must have child objects.
|
317
|
+
Providing no children will raise a ``ValueError``.
|
281
318
|
|
282
319
|
Returns
|
283
320
|
-------
|
284
321
|
Array
|
285
322
|
The inflated Array.
|
286
323
|
"""
|
287
|
-
if children:
|
288
|
-
raise ValueError("`Array` objects
|
324
|
+
if not children:
|
325
|
+
raise ValueError("`Array` objects must have children.")
|
289
326
|
|
290
327
|
obj_body = get_object_body(object_content, cls)
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
328
|
+
|
329
|
+
# Extract children IDs from head
|
330
|
+
children_ids = get_object_children_ids_from_object_content(object_content)
|
331
|
+
# Decode the Array body
|
332
|
+
array_metadata: dict[str, str | tuple[int, ...] | list[int]] = json.loads(
|
333
|
+
obj_body.decode(encoding="utf-8")
|
297
334
|
)
|
298
335
|
|
336
|
+
# Verify children ids in body match those passed for inflation
|
337
|
+
chunk_ids_indices = cast(list[int], array_metadata["arraychunk_ids"])
|
338
|
+
# Convert indices back to IDs
|
339
|
+
chunk_ids = [children_ids[i] for i in chunk_ids_indices]
|
340
|
+
# Check consistency
|
341
|
+
unique_arrayschunks = set(chunk_ids)
|
342
|
+
children_obj_ids = set(children.keys())
|
343
|
+
if unique_arrayschunks != children_obj_ids:
|
344
|
+
raise ValueError(
|
345
|
+
"Unexpected set of `children`. "
|
346
|
+
f"Expected {unique_arrayschunks} but got {children_obj_ids}."
|
347
|
+
)
|
348
|
+
|
349
|
+
# Materialize Array with empty data
|
350
|
+
array = cls(
|
351
|
+
dtype=cast(str, array_metadata["dtype"]),
|
352
|
+
shape=cast(tuple[int], tuple(array_metadata["shape"])),
|
353
|
+
stype=cast(str, array_metadata["stype"]),
|
354
|
+
data=b"",
|
355
|
+
)
|
356
|
+
|
357
|
+
# Now inject data from chunks
|
358
|
+
buff = bytearray()
|
359
|
+
for ch_id in chunk_ids:
|
360
|
+
buff += cast(ArrayChunk, children[ch_id]).data
|
361
|
+
|
362
|
+
array.data = bytes(buff)
|
363
|
+
return array
|
364
|
+
|
299
365
|
@property
|
300
366
|
def object_id(self) -> str:
|
301
367
|
"""Get object ID."""
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# Copyright 2025 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
|
+
"""ArrayChunk."""
|
16
|
+
|
17
|
+
|
18
|
+
from __future__ import annotations
|
19
|
+
|
20
|
+
from dataclasses import dataclass
|
21
|
+
|
22
|
+
from ..inflatable import InflatableObject, add_header_to_object_body, get_object_body
|
23
|
+
|
24
|
+
|
25
|
+
@dataclass
|
26
|
+
class ArrayChunk(InflatableObject):
|
27
|
+
"""ArrayChunk type."""
|
28
|
+
|
29
|
+
data: memoryview
|
30
|
+
|
31
|
+
def __init__(self, data: bytes) -> None:
|
32
|
+
self.data = memoryview(data)
|
33
|
+
|
34
|
+
def deflate(self) -> bytes:
|
35
|
+
"""Deflate the ArrayChunk."""
|
36
|
+
return add_header_to_object_body(object_body=self.data, obj=self)
|
37
|
+
|
38
|
+
@classmethod
|
39
|
+
def inflate(
|
40
|
+
cls, object_content: bytes, children: dict[str, InflatableObject] | None = None
|
41
|
+
) -> ArrayChunk:
|
42
|
+
"""Inflate an ArrayChunk from bytes.
|
43
|
+
|
44
|
+
Parameters
|
45
|
+
----------
|
46
|
+
object_content : bytes
|
47
|
+
The deflated object content of the ArrayChunk.
|
48
|
+
|
49
|
+
children : Optional[dict[str, InflatableObject]] (default: None)
|
50
|
+
Must be ``None``. ``ArrayChunk`` does not support child objects.
|
51
|
+
Providing any children will raise a ``ValueError``.
|
52
|
+
|
53
|
+
Returns
|
54
|
+
-------
|
55
|
+
ArrayChunk
|
56
|
+
The inflated ArrayChunk.
|
57
|
+
"""
|
58
|
+
if children:
|
59
|
+
raise ValueError("`ArrayChunk` objects do not have children.")
|
60
|
+
|
61
|
+
obj_body = get_object_body(object_content, cls)
|
62
|
+
return cls(data=memoryview(obj_body))
|
{flwr_nightly-1.20.0.dev20250627.dist-info → flwr_nightly-1.20.0.dev20250630.dist-info}/METADATA
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: flwr-nightly
|
3
|
-
Version: 1.20.0.
|
3
|
+
Version: 1.20.0.dev20250630
|
4
4
|
Summary: Flower: A Friendly Federated AI Framework
|
5
5
|
License: Apache-2.0
|
6
6
|
Keywords: Artificial Intelligence,Federated AI,Federated Analytics,Federated Evaluation,Federated Learning,Flower,Machine Learning
|
{flwr_nightly-1.20.0.dev20250627.dist-info → flwr_nightly-1.20.0.dev20250630.dist-info}/RECORD
RENAMED
@@ -108,7 +108,7 @@ flwr/common/args.py,sha256=-aX_jVnSaDrJR2KZ8Wq0Y3dQHII4R4MJtJOIXzVUA0c,5417
|
|
108
108
|
flwr/common/auth_plugin/__init__.py,sha256=3rzPkVLn9WyB5n7HLk1XGDw3SLCqRWAU1_CnglcWPfw,970
|
109
109
|
flwr/common/auth_plugin/auth_plugin.py,sha256=kXx5o39vJchaPv28sK9qO6H_UXSWym6zRBbCa7sUwtQ,4825
|
110
110
|
flwr/common/config.py,sha256=glcZDjco-amw1YfQcYTFJ4S1pt9APoexT-mf1QscuHs,13960
|
111
|
-
flwr/common/constant.py,sha256=
|
111
|
+
flwr/common/constant.py,sha256=HMLDvtiWGcGUfHpyyMx1rau34eXdMuBsvn-koRD8-pQ,8234
|
112
112
|
flwr/common/context.py,sha256=Be8obQR_OvEDy1OmshuUKxGRQ7Qx89mf5F4xlhkR10s,2407
|
113
113
|
flwr/common/date.py,sha256=1ZT2cRSpC2DJqprOVTLXYCR_O2_OZR0zXO_brJ3LqWc,1554
|
114
114
|
flwr/common/differential_privacy.py,sha256=FdlpdpPl_H_2HJa8CQM1iCUGBBQ5Dc8CzxmHERM-EoE,6148
|
@@ -125,14 +125,15 @@ flwr/common/heartbeat.py,sha256=SyEpNDnmJ0lni0cWO67rcoJVKasCLmkNHm3dKLeNrLU,5749
|
|
125
125
|
flwr/common/inflatable.py,sha256=GDL9oBKs16_yyVdlH6kBf493O5xll_h9V7XB5Mpx1Hc,9524
|
126
126
|
flwr/common/inflatable_grpc_utils.py,sha256=ZpwtgF1tGD6NwQkCidbhbeBPDBZ1Nx9eGMHQ04eNEE8,3554
|
127
127
|
flwr/common/inflatable_rest_utils.py,sha256=KiZd06XRiXcl_WewOrag0JTvUQt5kZ74UIsQ3FCAXGc,3580
|
128
|
-
flwr/common/inflatable_utils.py,sha256
|
128
|
+
flwr/common/inflatable_utils.py,sha256=XfgkPl8GKuR7tuOpYVZ3zK4n-LbgMZtwoNxMfUbpiCE,12489
|
129
129
|
flwr/common/logger.py,sha256=JbRf6E2vQxXzpDBq1T8IDUJo_usu3gjWEBPQ6uKcmdg,13049
|
130
130
|
flwr/common/message.py,sha256=xAL7iZN5-n-xPQpgoSFvxNrzs8fmiiPfoU0DjNQEhRw,19953
|
131
131
|
flwr/common/object_ref.py,sha256=p3SfTeqo3Aj16SkB-vsnNn01zswOPdGNBitcbRnqmUk,9134
|
132
132
|
flwr/common/parameter.py,sha256=UVw6sOgehEFhFs4uUCMl2kfVq1PD6ncmWgPLMsZPKPE,2095
|
133
133
|
flwr/common/pyproject.py,sha256=2SU6yJW7059SbMXgzjOdK1GZRWO6AixDH7BmdxbMvHI,1386
|
134
134
|
flwr/common/record/__init__.py,sha256=cNGccdDoxttqgnUgyKRIqLWULjW-NaSmOufVxtXq-sw,1197
|
135
|
-
flwr/common/record/array.py,sha256=
|
135
|
+
flwr/common/record/array.py,sha256=oApWRwxh1bY2MivQcAz9YHkrspkYZVvy1OkBsWolMec,14541
|
136
|
+
flwr/common/record/arraychunk.py,sha256=2ubMzsRNZFL4cc-wXNrJkcTSJUD3nL8CeX5PZpEhNSo,2019
|
136
137
|
flwr/common/record/arrayrecord.py,sha256=CpoqYXM6Iv4XEc9SryCMYmw-bIvP8ut6xWJzRwYJzdU,18008
|
137
138
|
flwr/common/record/configrecord.py,sha256=G7U0q39kB0Kyi0zMxFmPxcVemL9NgwVS1qjvI4BRQuU,9763
|
138
139
|
flwr/common/record/conversion_utils.py,sha256=wbNCzy7oAqaA3-arhls_EqRZYXRC4YrWIoE-Gy82fJ0,1191
|
@@ -360,7 +361,7 @@ flwr/supernode/servicer/__init__.py,sha256=lucTzre5WPK7G1YLCfaqg3rbFWdNSb7ZTt-ca
|
|
360
361
|
flwr/supernode/servicer/clientappio/__init__.py,sha256=7Oy62Y_oijqF7Dxi6tpcUQyOpLc_QpIRZ83NvwmB0Yg,813
|
361
362
|
flwr/supernode/servicer/clientappio/clientappio_servicer.py,sha256=d3GdIabycUoDBDL_eVlt513knGSjQW3-9lG6Cw4QEk4,5719
|
362
363
|
flwr/supernode/start_client_internal.py,sha256=DAXuReZ1FCXt9Y1KbM0p-dI50ROWPEJXzfKrl14OE6k,18233
|
363
|
-
flwr_nightly-1.20.0.
|
364
|
-
flwr_nightly-1.20.0.
|
365
|
-
flwr_nightly-1.20.0.
|
366
|
-
flwr_nightly-1.20.0.
|
364
|
+
flwr_nightly-1.20.0.dev20250630.dist-info/METADATA,sha256=B3mOdd5CdmCCEJsFc4A65ifNMx4_vvr7UHDi-OuIAzY,15910
|
365
|
+
flwr_nightly-1.20.0.dev20250630.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
366
|
+
flwr_nightly-1.20.0.dev20250630.dist-info/entry_points.txt,sha256=jNpDXGBGgs21RqUxelF_jwGaxtqFwm-MQyfz-ZqSjrA,367
|
367
|
+
flwr_nightly-1.20.0.dev20250630.dist-info/RECORD,,
|
{flwr_nightly-1.20.0.dev20250627.dist-info → flwr_nightly-1.20.0.dev20250630.dist-info}/WHEEL
RENAMED
File without changes
|
File without changes
|