diff --git a/.github/workflows/docker-client.yml b/.github/workflows/docker-client.yml
index 3c2d83596733..5b7e27a3eb0a 100644
--- a/.github/workflows/docker-client.yml
+++ b/.github/workflows/docker-client.yml
@@ -4,7 +4,7 @@ on:
workflow_dispatch:
inputs:
flwr-version:
- description: "Version of Flower e.g. (1.7.0)."
+ description: "Version of Flower"
required: true
type: string
diff --git a/.github/workflows/docker-server.yml b/.github/workflows/docker-server.yml
index 1e43715207d4..f3717aa28dc6 100644
--- a/.github/workflows/docker-server.yml
+++ b/.github/workflows/docker-server.yml
@@ -4,7 +4,7 @@ on:
workflow_dispatch:
inputs:
flwr-version:
- description: "Version of Flower e.g. (1.7.0)."
+ description: "Version of Flower"
required: true
type: string
base-image-tag:
diff --git a/baselines/doc/source/conf.py b/baselines/doc/source/conf.py
index dad8650cddaa..a9525c44ab7b 100644
--- a/baselines/doc/source/conf.py
+++ b/baselines/doc/source/conf.py
@@ -37,7 +37,7 @@
author = "The Flower Authors"
# The full version, including alpha/beta/rc tags
-release = "1.7.0"
+release = "1.8.0"
# -- General configuration ---------------------------------------------------
diff --git a/datasets/doc/source/how-to-use-with-pytorch.rst b/datasets/doc/source/how-to-use-with-pytorch.rst
index 613f00a9a059..4228ead2a281 100644
--- a/datasets/doc/source/how-to-use-with-pytorch.rst
+++ b/datasets/doc/source/how-to-use-with-pytorch.rst
@@ -63,7 +63,7 @@ expected by a model with a convolutional layer.
If you want to divide the dataset, you can use (at any point before passing the dataset to the DataLoader)::
- partition_train_test = partition.train_test_split(test_size=0.2)
+ partition_train_test = partition.train_test_split(test_size=0.2, seed=42)
partition_train = partition_train_test["train"]
partition_test = partition_train_test["test"]
diff --git a/datasets/e2e/pytorch/pytorch_test.py b/datasets/e2e/pytorch/pytorch_test.py
index 5bac8f770f23..1f5e4cbb3ad1 100644
--- a/datasets/e2e/pytorch/pytorch_test.py
+++ b/datasets/e2e/pytorch/pytorch_test.py
@@ -65,7 +65,7 @@ def _create_trainloader(self, batch_size: int) -> DataLoader:
partition_id = 0
fds = FederatedDataset(dataset=self.dataset_name, partitioners={"train": 100})
partition = fds.load_partition(partition_id, "train")
- partition_train_test = partition.train_test_split(test_size=0.2)
+ partition_train_test = partition.train_test_split(test_size=0.2, seed=42)
partition_train_test = partition_train_test.map(
lambda img: {"img": self.transforms(img)}, input_columns="img"
)
diff --git a/datasets/e2e/scikit-learn/sklearn_test.py b/datasets/e2e/scikit-learn/sklearn_test.py
index e5e6d347ee37..7ce4659b6cd8 100644
--- a/datasets/e2e/scikit-learn/sklearn_test.py
+++ b/datasets/e2e/scikit-learn/sklearn_test.py
@@ -29,7 +29,7 @@ def _get_partition_data(self):
fds = FederatedDataset(dataset=self.dataset_name, partitioners={"train": 10})
partition = fds.load_partition(partition_id, "train")
partition.set_format("numpy")
- partition_train_test = partition.train_test_split(test_size=0.2)
+ partition_train_test = partition.train_test_split(test_size=0.2, seed=42)
X_train, y_train = partition_train_test["train"]["image"], partition_train_test[
"train"]["label"]
X_test, y_test = partition_train_test["test"]["image"], partition_train_test[
diff --git a/datasets/flwr_datasets/utils.py b/datasets/flwr_datasets/utils.py
index a6e4fa8d0f0b..346d897ccdd6 100644
--- a/datasets/flwr_datasets/utils.py
+++ b/datasets/flwr_datasets/utils.py
@@ -133,6 +133,7 @@ def divide_dataset(
>>> train_test = divide_dataset(dataset=partition, division=division)
>>> train, test = train_test["train"], train_test["test"]
"""
+ _check_division_config_correctness(division)
dataset_length = len(dataset)
ranges = _create_division_indices_ranges(dataset_length, division)
if isinstance(division, (list, tuple)):
@@ -162,7 +163,7 @@ def _create_division_indices_ranges(
for fraction in division:
end_idx += int(dataset_length * fraction)
ranges.append(range(start_idx, end_idx))
- start_idx += end_idx
+ start_idx = end_idx
elif isinstance(division, dict):
ranges = []
start_idx = 0
@@ -170,7 +171,7 @@ def _create_division_indices_ranges(
for fraction in division.values():
end_idx += int(dataset_length * fraction)
ranges.append(range(start_idx, end_idx))
- start_idx += end_idx
+ start_idx = end_idx
else:
TypeError(
f"The type of the `division` should be dict, "
@@ -274,6 +275,7 @@ def concatenate_divisions(
concatenated_divisions : Dataset
A dataset created as concatenation of the divisions from all partitions.
"""
+ _check_division_config_correctness(partition_division)
divisions = []
zero_len_divisions = 0
for partition_id in range(partitioner.num_partitions):
diff --git a/datasets/flwr_datasets/utils_test.py b/datasets/flwr_datasets/utils_test.py
index 3bf5afddf978..4add9f88eeb5 100644
--- a/datasets/flwr_datasets/utils_test.py
+++ b/datasets/flwr_datasets/utils_test.py
@@ -31,13 +31,32 @@
"expected_concatenation_size",
),
[
+ # Create 1 division
+ ((1.0,), [40], 0, 40),
+ ({"train": 1.0}, [40], "train", 40),
+ # Create 2 divisions
((0.8, 0.2), [32, 8], 1, 8),
- ([0.8, 0.2], [32, 8], 1, 8),
({"train": 0.8, "test": 0.2}, [32, 8], "test", 8),
+ # Create 3 divisions
+ ([0.6, 0.2, 0.2], [24, 8, 8], 1, 8),
+ ({"train": 0.6, "valid": 0.2, "test": 0.2}, [24, 8, 8], "test", 8),
+ # Create 4 divisions
+ ([0.4, 0.2, 0.2, 0.2], [16, 8, 8, 8], 1, 8),
+ ({"0": 0.4, "1": 0.2, "2": 0.2, "3": 0.2}, [16, 8, 8, 8], "1", 8),
# Not full dataset
+ # Create 1 division
+ ([0.8], [32], 0, 32),
+ ({"train": 0.8}, [32], "train", 32),
+ # Create 2 divisions
([0.2, 0.1], [8, 4], 1, 4),
((0.2, 0.1), [8, 4], 0, 8),
({"train": 0.2, "test": 0.1}, [8, 4], "test", 4),
+ # Create 3 divisions
+ ([0.6, 0.2, 0.1], [24, 8, 4], 2, 4),
+ ({"train": 0.6, "valid": 0.2, "test": 0.1}, [24, 8, 4], "test", 4),
+ # Create 4 divisions
+ ([0.4, 0.2, 0.1, 0.2], [16, 8, 4, 8], 2, 4),
+ ({"0": 0.4, "1": 0.2, "2": 0.1, "3": 0.2}, [16, 8, 4, 8], "2", 4),
],
)
class UtilsTests(unittest.TestCase):
@@ -60,7 +79,7 @@ def test_correct_sizes(self) -> None:
else:
lengths = [len(split) for split in divided_dataset.values()]
- self.assertEqual(lengths, self.sizes)
+ self.assertEqual(self.sizes, lengths)
def test_correct_return_types(self) -> None:
"""Test correct types of the divided dataset based on the config."""
diff --git a/dev/update-examples.sh b/dev/update-examples.sh
index c802e21503b7..07cb90932875 100755
--- a/dev/update-examples.sh
+++ b/dev/update-examples.sh
@@ -16,20 +16,47 @@ echo "---" >> $INDEX
echo "maxdepth: 1" >> $INDEX
echo "---" >> $INDEX
-rm -f "examples/doc/source/*.md"
+rm -f examples/doc/source/*.md
cd examples/
for d in $(printf '%s\n' */ | sort -V); do
example=${d%/}
- # For each example, copy the README into the source of the Example docs
- [[ $example != doc ]] && cp $example/README.md $ROOT/examples/doc/source/$example.md 2>&1 >/dev/null
- # For each example, copy all images of the _static folder into the examples
- # docs static folder
- [[ $example != doc ]] && [ -d "$example/_static" ] && {
- cp $example/_static/**.{jpg,png,jpeg} $ROOT/examples/doc/source/_static/ 2>/dev/null || true
- }
- # For each example, insert the name of the example into the index file
- [[ $example != doc ]] && (echo $INSERT_LINE; echo a; echo $example; echo .; echo wq) | ed $INDEX 2>&1 >/dev/null
+
+ if [[ $example != doc ]]; then
+
+ for file in $example/*.md; do
+ # For each example, copy the README into the source of the Example docs
+ if [[ $(basename "$file") = "README.md" ]]; then
+ cp $file $ROOT/examples/doc/source/$example.md 2>&1 >/dev/null
+ else
+ # If the example contains other markdown files, copy them to the source of the Example docs
+ cp $file $ROOT/examples/doc/source/$(basename "$file") 2>&1 >/dev/null
+ fi
+ done
+
+ gh_text="[
](https://github.com/adap/flower/blob/main/examples/$example)"
+ readme_file="$ROOT/examples/doc/source/$example.md"
+
+ if ! grep -Fq "$gh_text" "$readme_file"; then
+ awk -v text="$gh_text" '
+ /^# / && !found {
+ print $0 "\n" text;
+ found=1;
+ next;
+ }
+ { print }
+ ' "$readme_file" > tmpfile && mv tmpfile "$readme_file"
+ fi
+
+ # For each example, copy all images of the _static folder into the examples
+ # docs static folder
+ [ -d "$example/_static" ] && {
+ cp $example/_static/**.{jpg,png,jpeg} $ROOT/examples/doc/source/_static/ 2>/dev/null || true
+ }
+ # For each example, insert the name of the example into the index file
+ (echo $INSERT_LINE; echo a; echo $example; echo .; echo wq) | ed $INDEX 2>&1 >/dev/null
+
+ fi
done
echo "\`\`\`" >> $INDEX
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 64a4f5611b7c..84d95a5fc885 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -86,7 +86,7 @@
author = "The Flower Authors"
# The full version, including alpha/beta/rc tags
-release = "1.8.0"
+release = "1.9.0"
# -- General configuration ---------------------------------------------------
diff --git a/doc/source/ref-changelog.md b/doc/source/ref-changelog.md
index 94ef33fa6d33..c742b8cd9cbe 100644
--- a/doc/source/ref-changelog.md
+++ b/doc/source/ref-changelog.md
@@ -1,5 +1,13 @@
# Changelog
+## Unreleased
+
+### What's new?
+
+### Incompatible changes
+
+None
+
## v1.8.0 (2024-04-03)
### Thanks to our contributors
diff --git a/doc/source/tutorial-series-get-started-with-flower-pytorch.ipynb b/doc/source/tutorial-series-get-started-with-flower-pytorch.ipynb
index 2b8dd382bb79..6f3d24286c99 100644
--- a/doc/source/tutorial-series-get-started-with-flower-pytorch.ipynb
+++ b/doc/source/tutorial-series-get-started-with-flower-pytorch.ipynb
@@ -145,7 +145,7 @@
" for partition_id in range(NUM_CLIENTS):\n",
" partition = fds.load_partition(partition_id, \"train\")\n",
" partition = partition.with_transform(apply_transforms)\n",
- " partition = partition.train_test_split(train_size=0.8)\n",
+ " partition = partition.train_test_split(train_size=0.8, seed=42)\n",
" trainloaders.append(DataLoader(partition[\"train\"], batch_size=BATCH_SIZE))\n",
" valloaders.append(DataLoader(partition[\"test\"], batch_size=BATCH_SIZE))\n",
" testset = fds.load_split(\"test\").with_transform(apply_transforms)\n",
diff --git a/examples/advanced-pytorch/client.py b/examples/advanced-pytorch/client.py
index d4c8abe3d404..7c1420a2cecd 100644
--- a/examples/advanced-pytorch/client.py
+++ b/examples/advanced-pytorch/client.py
@@ -46,7 +46,7 @@ def fit(self, parameters, config):
batch_size: int = config["batch_size"]
epochs: int = config["local_epochs"]
- train_valid = self.trainset.train_test_split(self.validation_split)
+ train_valid = self.trainset.train_test_split(self.validation_split, seed=42)
trainset = train_valid["train"]
valset = train_valid["test"]
diff --git a/examples/advanced-pytorch/utils.py b/examples/advanced-pytorch/utils.py
index fd9dab19a70d..c47b4fa38593 100644
--- a/examples/advanced-pytorch/utils.py
+++ b/examples/advanced-pytorch/utils.py
@@ -14,7 +14,7 @@ def load_partition(partition_id, toy: bool = False):
fds = FederatedDataset(dataset="cifar10", partitioners={"train": 10})
partition = fds.load_partition(partition_id)
# Divide data on each node: 80% train, 20% test
- partition_train_test = partition.train_test_split(test_size=0.2)
+ partition_train_test = partition.train_test_split(test_size=0.2, seed=42)
partition_train_test = partition_train_test.with_transform(apply_transforms)
return partition_train_test["train"], partition_train_test["test"]
diff --git a/examples/advanced-tensorflow/client.py b/examples/advanced-tensorflow/client.py
index 17d1d2306270..b658a1f9ea04 100644
--- a/examples/advanced-tensorflow/client.py
+++ b/examples/advanced-tensorflow/client.py
@@ -123,7 +123,7 @@ def load_partition(idx: int):
partition.set_format("numpy")
# Divide data on each node: 80% train, 20% test
- partition = partition.train_test_split(test_size=0.2)
+ partition = partition.train_test_split(test_size=0.2, seed=42)
x_train, y_train = partition["train"]["img"] / 255.0, partition["train"]["label"]
x_test, y_test = partition["test"]["img"] / 255.0, partition["test"]["label"]
return x_train, y_train, x_test, y_test
diff --git a/examples/app-pytorch/client.py b/examples/app-pytorch/client.py
index ebbe977ecab1..eb84968bb986 100644
--- a/examples/app-pytorch/client.py
+++ b/examples/app-pytorch/client.py
@@ -18,7 +18,6 @@
# Define FlowerClient and client_fn
class FlowerClient(NumPyClient):
-
def fit(self, parameters, config):
set_weights(net, parameters)
results = train(net, trainloader, testloader, epochs=1, device=DEVICE)
diff --git a/examples/app-pytorch/pyproject.toml b/examples/app-pytorch/pyproject.toml
index 67a956fc083e..c00e38aef19b 100644
--- a/examples/app-pytorch/pyproject.toml
+++ b/examples/app-pytorch/pyproject.toml
@@ -11,7 +11,6 @@ authors = ["The Flower Authors "]
[tool.poetry.dependencies]
python = "^3.8"
# Mandatory dependencies
-flwr = { version = "1.8.0", extras = ["simulation"] }
-flwr-datasets = { version = "0.0.2", extras = ["vision"] }
+flwr = { version = "^1.8.0", extras = ["simulation"] }
torch = "2.2.1"
torchvision = "0.17.1"
diff --git a/examples/app-pytorch/requirements.txt b/examples/app-pytorch/requirements.txt
index 170d1cabafbe..117e30b2ad56 100644
--- a/examples/app-pytorch/requirements.txt
+++ b/examples/app-pytorch/requirements.txt
@@ -1,4 +1,3 @@
-flwr[simulation]==1.8.0
-flwr-datasets[vision]==0.0.2
+flwr[simulation]>=1.8.0
torch==2.2.1
torchvision==0.17.1
diff --git a/examples/app-secure-aggregation/pyproject.toml b/examples/app-secure-aggregation/pyproject.toml
index 5a1cf3c2f26e..fb1f636d8c33 100644
--- a/examples/app-secure-aggregation/pyproject.toml
+++ b/examples/app-secure-aggregation/pyproject.toml
@@ -11,4 +11,4 @@ authors = ["The Flower Authors "]
[tool.poetry.dependencies]
python = "^3.8"
# Mandatory dependencies
-flwr = { version = "1.8.0", extras = ["simulation"] }
+flwr = { version = "^1.8.0", extras = ["simulation"] }
diff --git a/examples/app-secure-aggregation/requirements.txt b/examples/app-secure-aggregation/requirements.txt
index 780aea8ab8fb..2d8be098f264 100644
--- a/examples/app-secure-aggregation/requirements.txt
+++ b/examples/app-secure-aggregation/requirements.txt
@@ -1 +1 @@
-flwr[simulation]==1.8.0
+flwr[simulation]>=1.8.0
diff --git a/examples/custom-mods/README.md b/examples/custom-mods/README.md
index b0ad668c2dec..6b03abcfbfe0 100644
--- a/examples/custom-mods/README.md
+++ b/examples/custom-mods/README.md
@@ -288,7 +288,7 @@ $ tree .
pip install -r requirements.txt
```
-For [W&B](wandb.ai) you will also need a valid account.
+For [W&B](https://wandb.ai) you will also need a valid account.
### Start the long-running Flower server (SuperLink)
@@ -328,7 +328,7 @@ flower-server-app server:app --insecure
### Check the results
-For W&B, you will need to login to the [website](wandb.ai).
+For W&B, you will need to login to the [website](https://wandb.ai).
For TensorBoard, you will need to run the following command in your terminal:
diff --git a/examples/custom-mods/client.py b/examples/custom-mods/client.py
index 2b87a24da19d..614daef6bcf6 100644
--- a/examples/custom-mods/client.py
+++ b/examples/custom-mods/client.py
@@ -86,7 +86,6 @@ def wandb_mod(msg: Message, context: Context, app: ClientAppCallable) -> Message
# if the `ClientApp` just processed a "fit" message, let's log some metrics to W&B
if reply.metadata.message_type == MessageType.TRAIN and reply.has_content():
-
metrics = reply.content.configs_records
results_to_log = dict(metrics.get("fitres.metrics", ConfigsRecord()))
diff --git a/examples/doc/source/_static/.gitignore b/examples/doc/source/_static/.gitignore
index c2412a5912cc..887023baf484 100644
--- a/examples/doc/source/_static/.gitignore
+++ b/examples/doc/source/_static/.gitignore
@@ -3,3 +3,4 @@
!favicon.ico
!flower-logo.png
!tmux_jtop_view.gif
+!view-gh.png
diff --git a/examples/doc/source/_static/view-gh.png b/examples/doc/source/_static/view-gh.png
new file mode 100644
index 000000000000..afc3f07bc2d5
Binary files /dev/null and b/examples/doc/source/_static/view-gh.png differ
diff --git a/examples/doc/source/conf.py b/examples/doc/source/conf.py
index bf177aa5ae24..fd93502cf5df 100644
--- a/examples/doc/source/conf.py
+++ b/examples/doc/source/conf.py
@@ -30,7 +30,7 @@
author = "The Flower Authors"
# The full version, including alpha/beta/rc tags
-release = "1.8.0"
+release = "1.9.0"
# -- General configuration ---------------------------------------------------
diff --git a/examples/embedded-devices/client_pytorch.py b/examples/embedded-devices/client_pytorch.py
index 6bd69c16567e..411052bfb1ea 100644
--- a/examples/embedded-devices/client_pytorch.py
+++ b/examples/embedded-devices/client_pytorch.py
@@ -108,7 +108,7 @@ def apply_transforms(batch):
for partition_id in range(NUM_CLIENTS):
partition = fds.load_partition(partition_id, "train")
# Divide data on each node: 90% train, 10% test
- partition = partition.train_test_split(test_size=0.1)
+ partition = partition.train_test_split(test_size=0.1, seed=42)
partition = partition.with_transform(apply_transforms)
trainsets.append(partition["train"])
validsets.append(partition["test"])
diff --git a/examples/embedded-devices/client_tf.py b/examples/embedded-devices/client_tf.py
index 49c63ce5d9dc..3df75f76312b 100644
--- a/examples/embedded-devices/client_tf.py
+++ b/examples/embedded-devices/client_tf.py
@@ -44,7 +44,7 @@ def prepare_dataset(use_mnist: bool):
partition = fds.load_partition(partition_id, "train")
partition.set_format("numpy")
# Divide data on each node: 90% train, 10% test
- partition = partition.train_test_split(test_size=0.1)
+ partition = partition.train_test_split(test_size=0.1, seed=42)
x_train, y_train = (
partition["train"][img_key] / 255.0,
partition["train"]["label"],
diff --git a/examples/fl-dp-sa/fl_dp_sa/task.py b/examples/fl-dp-sa/fl_dp_sa/task.py
index 3d506263d5a3..64ba10f20376 100644
--- a/examples/fl-dp-sa/fl_dp_sa/task.py
+++ b/examples/fl-dp-sa/fl_dp_sa/task.py
@@ -42,7 +42,7 @@ def load_data(partition_id):
fds = FederatedDataset(dataset="mnist", partitioners={"train": 100})
partition = fds.load_partition(partition_id)
# Divide data on each node: 80% train, 20% test
- partition_train_test = partition.train_test_split(test_size=0.2)
+ partition_train_test = partition.train_test_split(test_size=0.2, seed=42)
pytorch_transforms = Compose([ToTensor(), Normalize((0.5,), (0.5,))])
def apply_transforms(batch):
diff --git a/examples/fl-dp-sa/pyproject.toml b/examples/fl-dp-sa/pyproject.toml
index c6ef6815b136..1ca343b072d9 100644
--- a/examples/fl-dp-sa/pyproject.toml
+++ b/examples/fl-dp-sa/pyproject.toml
@@ -15,7 +15,7 @@ readme = "README.md"
[tool.poetry.dependencies]
python = "^3.9"
# Mandatory dependencies
-flwr = { version = "1.8.0", extras = ["simulation"] }
+flwr = { version = "^1.8.0", extras = ["simulation"] }
flwr-datasets = { version = "0.0.2", extras = ["vision"] }
torch = "2.2.1"
torchvision = "0.17.1"
diff --git a/examples/fl-dp-sa/requirements.txt b/examples/fl-dp-sa/requirements.txt
index 170d1cabafbe..f20b9d71e339 100644
--- a/examples/fl-dp-sa/requirements.txt
+++ b/examples/fl-dp-sa/requirements.txt
@@ -1,4 +1,4 @@
-flwr[simulation]==1.8.0
+flwr[simulation]>=1.8.0
flwr-datasets[vision]==0.0.2
torch==2.2.1
torchvision==0.17.1
diff --git a/examples/flower-via-docker-compose/helpers/load_data.py b/examples/flower-via-docker-compose/helpers/load_data.py
index 1f2784946868..b7d6b0de26c5 100644
--- a/examples/flower-via-docker-compose/helpers/load_data.py
+++ b/examples/flower-via-docker-compose/helpers/load_data.py
@@ -25,7 +25,7 @@ def load_data(data_sampling_percentage=0.5, client_id=1, total_clients=2):
partition.set_format("numpy")
# Divide data on each client: 80% train, 20% test
- partition = partition.train_test_split(test_size=0.2)
+ partition = partition.train_test_split(test_size=0.2, seed=42)
x_train, y_train = partition["train"]["img"] / 255.0, partition["train"]["label"]
x_test, y_test = partition["test"]["img"] / 255.0, partition["test"]["label"]
diff --git a/examples/flower-via-docker-compose/requirements.txt b/examples/flower-via-docker-compose/requirements.txt
index b93e5b1d9f2b..d08937c4d02a 100644
--- a/examples/flower-via-docker-compose/requirements.txt
+++ b/examples/flower-via-docker-compose/requirements.txt
@@ -1,4 +1,4 @@
-flwr==1.7.0
+flwr==1.8.0
tensorflow==2.13.1
numpy==1.24.3
prometheus_client == 0.19.0
diff --git a/examples/pytorch-from-centralized-to-federated/cifar.py b/examples/pytorch-from-centralized-to-federated/cifar.py
index 277a21da2e70..c592b63b0042 100644
--- a/examples/pytorch-from-centralized-to-federated/cifar.py
+++ b/examples/pytorch-from-centralized-to-federated/cifar.py
@@ -56,7 +56,7 @@ def load_data(partition_id: int):
fds = FederatedDataset(dataset="cifar10", partitioners={"train": 10})
partition = fds.load_partition(partition_id)
# Divide data on each node: 80% train, 20% test
- partition_train_test = partition.train_test_split(test_size=0.2)
+ partition_train_test = partition.train_test_split(test_size=0.2, seed=42)
pytorch_transforms = Compose(
[ToTensor(), Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]
)
diff --git a/examples/quickstart-huggingface/client.py b/examples/quickstart-huggingface/client.py
index 9be08d0cbcf4..db8bf51d36da 100644
--- a/examples/quickstart-huggingface/client.py
+++ b/examples/quickstart-huggingface/client.py
@@ -22,7 +22,7 @@ def load_data(partition_id):
fds = FederatedDataset(dataset="imdb", partitioners={"train": 1_000})
partition = fds.load_partition(partition_id)
# Divide data: 80% train, 20% test
- partition_train_test = partition.train_test_split(test_size=0.2)
+ partition_train_test = partition.train_test_split(test_size=0.2, seed=42)
tokenizer = AutoTokenizer.from_pretrained(CHECKPOINT)
diff --git a/examples/quickstart-mlcube/dev/mnist.py b/examples/quickstart-mlcube/dev/mnist.py
index e52e2cba85c7..55fb8fae62a7 100644
--- a/examples/quickstart-mlcube/dev/mnist.py
+++ b/examples/quickstart-mlcube/dev/mnist.py
@@ -36,6 +36,7 @@ def create_directory(path: str) -> None:
def download(task_args: List[str]) -> None:
"""Task: download.
+
Input parameters:
--data_dir
"""
@@ -81,6 +82,7 @@ def download(task_args: List[str]) -> None:
def train(task_args: List[str]) -> None:
"""Task: train.
+
Input parameters:
--data_dir, --log_dir, --model_dir, --parameters_file
"""
@@ -175,6 +177,7 @@ def train(task_args: List[str]) -> None:
def evaluate(task_args: List[str]) -> None:
"""Task: train.
+
Input parameters:
--data_dir, --log_dir, --model_dir, --parameters_file
"""
diff --git a/examples/quickstart-mlx/client.py b/examples/quickstart-mlx/client.py
index faba2b94d6bd..344cfc65e42d 100644
--- a/examples/quickstart-mlx/client.py
+++ b/examples/quickstart-mlx/client.py
@@ -107,7 +107,7 @@ def evaluate(self, parameters, config):
fds = FederatedDataset(dataset="mnist", partitioners={"train": 3})
partition = fds.load_partition(partition_id=args.partition_id)
- partition_splits = partition.train_test_split(test_size=0.2)
+ partition_splits = partition.train_test_split(test_size=0.2, seed=42)
partition_splits["train"].set_format("numpy")
partition_splits["test"].set_format("numpy")
diff --git a/examples/quickstart-pytorch-lightning/mnist.py b/examples/quickstart-pytorch-lightning/mnist.py
index 95342f4fb9b3..2f6100fe94cc 100644
--- a/examples/quickstart-pytorch-lightning/mnist.py
+++ b/examples/quickstart-pytorch-lightning/mnist.py
@@ -82,9 +82,11 @@ def load_data(partition):
partition = partition.with_transform(apply_transforms)
# 20 % for on federated evaluation
- partition_full = partition.train_test_split(test_size=0.2)
+ partition_full = partition.train_test_split(test_size=0.2, seed=42)
# 60 % for the federated train and 20 % for the federated validation (both in fit)
- partition_train_valid = partition_full["train"].train_test_split(train_size=0.75)
+ partition_train_valid = partition_full["train"].train_test_split(
+ train_size=0.75, seed=42
+ )
trainloader = DataLoader(
partition_train_valid["train"],
batch_size=32,
diff --git a/examples/quickstart-pytorch/client.py b/examples/quickstart-pytorch/client.py
index e58dbf7ea0bc..be4be88b8f8d 100644
--- a/examples/quickstart-pytorch/client.py
+++ b/examples/quickstart-pytorch/client.py
@@ -74,7 +74,7 @@ def load_data(partition_id):
fds = FederatedDataset(dataset="cifar10", partitioners={"train": 3})
partition = fds.load_partition(partition_id)
# Divide data on each node: 80% train, 20% test
- partition_train_test = partition.train_test_split(test_size=0.2)
+ partition_train_test = partition.train_test_split(test_size=0.2, seed=42)
pytorch_transforms = Compose(
[ToTensor(), Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]
)
diff --git a/examples/quickstart-tensorflow/client.py b/examples/quickstart-tensorflow/client.py
index 6ef11898f0b0..a906e9865d3b 100644
--- a/examples/quickstart-tensorflow/client.py
+++ b/examples/quickstart-tensorflow/client.py
@@ -30,7 +30,7 @@
partition.set_format("numpy")
# Divide data on each node: 80% train, 20% test
-partition = partition.train_test_split(test_size=0.2)
+partition = partition.train_test_split(test_size=0.2, seed=42)
x_train, y_train = partition["train"]["img"] / 255.0, partition["train"]["label"]
x_test, y_test = partition["test"]["img"] / 255.0, partition["test"]["label"]
diff --git a/examples/simulation-pytorch/sim.ipynb b/examples/simulation-pytorch/sim.ipynb
index 6dda1ef9319d..d225069cb444 100644
--- a/examples/simulation-pytorch/sim.ipynb
+++ b/examples/simulation-pytorch/sim.ipynb
@@ -497,7 +497,7 @@
" client_dataset = dataset.load_partition(int(cid), \"train\")\n",
"\n",
" # Now let's split it into train (90%) and validation (10%)\n",
- " client_dataset_splits = client_dataset.train_test_split(test_size=0.1)\n",
+ " client_dataset_splits = client_dataset.train_test_split(test_size=0.1, seed=42)\n",
"\n",
" trainset = client_dataset_splits[\"train\"]\n",
" valset = client_dataset_splits[\"test\"]\n",
diff --git a/examples/simulation-pytorch/sim.py b/examples/simulation-pytorch/sim.py
index 6fb750f2e59c..db68e75653fc 100644
--- a/examples/simulation-pytorch/sim.py
+++ b/examples/simulation-pytorch/sim.py
@@ -94,7 +94,7 @@ def client_fn(cid: str) -> fl.client.Client:
client_dataset = dataset.load_partition(int(cid), "train")
# Now let's split it into train (90%) and validation (10%)
- client_dataset_splits = client_dataset.train_test_split(test_size=0.1)
+ client_dataset_splits = client_dataset.train_test_split(test_size=0.1, seed=42)
trainset = client_dataset_splits["train"]
valset = client_dataset_splits["test"]
diff --git a/examples/simulation-tensorflow/sim.ipynb b/examples/simulation-tensorflow/sim.ipynb
index 797e2dcc603e..26b7260b5f1c 100644
--- a/examples/simulation-tensorflow/sim.ipynb
+++ b/examples/simulation-tensorflow/sim.ipynb
@@ -179,7 +179,7 @@
" client_dataset = dataset.load_partition(int(cid), \"train\")\n",
"\n",
" # Now let's split it into train (90%) and validation (10%)\n",
- " client_dataset_splits = client_dataset.train_test_split(test_size=0.1)\n",
+ " client_dataset_splits = client_dataset.train_test_split(test_size=0.1, seed=42)\n",
"\n",
" trainset = client_dataset_splits[\"train\"].to_tf_dataset(\n",
" columns=\"image\", label_cols=\"label\", batch_size=32\n",
diff --git a/examples/simulation-tensorflow/sim.py b/examples/simulation-tensorflow/sim.py
index e94e5ec96850..4014e3c6be72 100644
--- a/examples/simulation-tensorflow/sim.py
+++ b/examples/simulation-tensorflow/sim.py
@@ -83,7 +83,7 @@ def client_fn(cid: str) -> fl.client.Client:
client_dataset = dataset.load_partition(int(cid), "train")
# Now let's split it into train (90%) and validation (10%)
- client_dataset_splits = client_dataset.train_test_split(test_size=0.1)
+ client_dataset_splits = client_dataset.train_test_split(test_size=0.1, seed=42)
trainset = client_dataset_splits["train"].to_tf_dataset(
columns="image", label_cols="label", batch_size=32
diff --git a/examples/vertical-fl/README.md b/examples/vertical-fl/README.md
index 78588180d3d6..d8c599d617c4 100644
--- a/examples/vertical-fl/README.md
+++ b/examples/vertical-fl/README.md
@@ -123,7 +123,7 @@ In `task.py`, you'll find the preprocessing functions we'll apply to our data:
'Adult' for ages between 11 and 40, and 'Elderly' for those over 40. If the age
isn't listed, we'll label it as 'Unknown'.
- ```python3
+ ```python
def _bin_age(age_series):
bins = [-np.inf, 10, 40, np.inf]
labels = ["Child", "Adult", "Elderly"]
@@ -138,7 +138,7 @@ In `task.py`, you'll find the preprocessing functions we'll apply to our data:
understand social status and family roles, simplifying rare titles into a single
'Rare' category and converting any French titles to their English equivalents.
- ```python3
+ ```python
def _extract_title(name_series):
titles = name_series.str.extract(" ([A-Za-z]+)\.", expand=False)
rare_titles = {
@@ -170,7 +170,7 @@ In `task.py`, you'll find the preprocessing functions we'll apply to our data:
'Pclass', 'Embarked', 'Title', 'Cabin', and the binned 'Age' into One-Hot
encodings.
- ```python3
+ ```python
def _create_features(df):
# Convert 'Age' to numeric, coercing errors to NaN
df["Age"] = pd.to_numeric(df["Age"], errors="coerce")
@@ -190,7 +190,7 @@ In `task.py`, you'll find the preprocessing functions we'll apply to our data:
In `task.py`, we also partition our data for our 3 clients to mirror real-life
collaborations where different organizations hold different feature sets:
-```python3
+```python
def _partition_data(df, all_keywords):
partitions = []
keywords_sets = [{"Parch", "Cabin", "Pclass"}, {"Sex", "Title"}]
@@ -236,7 +236,7 @@ collective intelligence without sharing sensitive information.
Note that our final data processing function looks like that:
-```python3
+```python
def get_partitions_and_label():
df = pd.read_csv("_static/data/train.csv")
processed_df = df.dropna(subset=["Embarked", "Fare"]).copy()
@@ -259,7 +259,7 @@ Each client's model is a neural network designed to operate on a distinct subset
of features held by a client. In this example we will use simple linear
regression models.
-```python3
+```python
class ClientModel(nn.Module):
def __init__(self, input_size):
super(ClientModel, self).__init__()
@@ -281,7 +281,7 @@ The server's model acts as the central aggregator in the VFL system. It's also a
neural network but with a slightly different architecture tailored to its role
in aggregating the client models' outputs.
-```python3
+```python
class ServerModel(nn.Module):
def __init__(self):
super(ServerModel, self).__init__()
@@ -305,7 +305,7 @@ a probability score indicative of the likelihood of survival.
The strategy we will write to perform the aggregation will inherit from `FedAvg`
and set the following additional attributes:
-```python3
+```python
self.model = ServerModel(12)
self.initial_parameters = ndarrays_to_parameters(
[val.cpu().numpy() for _, val in self.model.state_dict().items()]
@@ -319,7 +319,7 @@ With `labels` given as an argument to the strategy.
We then redefine the `aggregate_fit` method:
-```python3
+```python
def aggregate_fit(
self,
rnd,
@@ -406,7 +406,7 @@ The last thing we have to do is to redefine the `aggregate_evaluate` function to
disable distributed evaluation (as the clients do not hold any labels to test
their local models).
-```python3
+```python
def aggregate_evaluate(
self,
rnd,
@@ -420,7 +420,7 @@ def aggregate_evaluate(
Our `FlowerClient` class is going to be quite straight forward.
-```python3
+```python
class FlowerClient(fl.client.NumPyClient):
def __init__(self, cid, data):
self.cid = cid
@@ -487,7 +487,7 @@ the `aggregate_evaluate` function of the strategy.
Putting everything together, to start our simulation we use the following
function:
-```python3
+```python
hist = fl.simulation.start_simulation(
client_fn=client_fn,
num_clients=3,
diff --git a/examples/vit-finetune/client.py b/examples/vit-finetune/client.py
index 68d98926feeb..bf91fa0c4328 100644
--- a/examples/vit-finetune/client.py
+++ b/examples/vit-finetune/client.py
@@ -8,9 +8,7 @@
class FedViTClient(NumPyClient):
-
def __init__(self, trainset):
-
self.trainset = trainset
self.model = get_model()
diff --git a/examples/vit-finetune/main.py b/examples/vit-finetune/main.py
index 1257246304a1..c629a6f68980 100644
--- a/examples/vit-finetune/main.py
+++ b/examples/vit-finetune/main.py
@@ -19,7 +19,6 @@
def main():
-
args = parser.parse_args()
# To control the degree of parallelism
diff --git a/examples/whisper-federated-finetuning/utils.py b/examples/whisper-federated-finetuning/utils.py
index 21fe0309151c..117cf7100ddd 100644
--- a/examples/whisper-federated-finetuning/utils.py
+++ b/examples/whisper-federated-finetuning/utils.py
@@ -107,10 +107,10 @@ def prepare_silences_dataset(train_dataset, ratio_silence: float = 0.1) -> Datas
"""Generate silences for the train set.
One of the classes in the SpeechCommands datatset is `silence`. However, the dataset
- does not include clips of silence. It does however include 5 long files with different
- background sounds. The taks of this function is to extract several (defined by `ratio_silence`)
- one-second long clips from those background audio files. Later, those audio clips will be
- included into the training set.
+ does not include clips of silence. It does however include 5 long files with
+ different background sounds. The taks of this function is to extract several
+ (defined by `ratio_silence`) one-second long clips from those background audio
+ files. Later, those audio clips will be included into the training set.
"""
# retrieve original silence audio clips
silences = [d for d in train_dataset if d["label"] == 35]
@@ -138,9 +138,9 @@ def prepare_silences_dataset(train_dataset, ratio_silence: float = 0.1) -> Datas
def construct_client_mapping(full_trainset, num_clients: int = 100):
"""Create a mapping to partition the dataset into `num_client` buckets.
- These buckets contain the same number of `spekaer_id` but likely different
- number of training exampes since each `speaker_id` in SpeechCommands does
- provide different amounts of data to the dataset.
+ These buckets contain the same number of `spekaer_id` but likely different number of
+ training exampes since each `speaker_id` in SpeechCommands does provide different
+ amounts of data to the dataset.
"""
client_ids = list(set(full_trainset["speaker_id"]))
client_ids.remove(
@@ -191,7 +191,7 @@ def set_params(model: torch.nn.ModuleList, params: List[fl.common.NDArrays]):
def get_model(device, num_classes, compile: bool = True):
- """Create model: Whisper-tiny Encoder + classification head"""
+ """Create model: Whisper-tiny Encoder + classification head."""
encoder = WhisperForConditionalGeneration.from_pretrained(
"openai/whisper-tiny"
).get_encoder()
diff --git a/pyproject.toml b/pyproject.toml
index 3c211e9cf8d8..65dcd3d4c930 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
[tool.poetry]
name = "flwr"
-version = "1.8.0"
+version = "1.9.0"
description = "Flower: A Friendly Federated Learning Framework"
license = "Apache-2.0"
authors = ["The Flower Authors "]
diff --git a/src/py/flwr/cli/new/templates/app/code/task.pytorch.py.tpl b/src/py/flwr/cli/new/templates/app/code/task.pytorch.py.tpl
index 85460564b6ef..b30c65a285b5 100644
--- a/src/py/flwr/cli/new/templates/app/code/task.pytorch.py.tpl
+++ b/src/py/flwr/cli/new/templates/app/code/task.pytorch.py.tpl
@@ -39,7 +39,7 @@ def load_data(partition_id, num_partitions):
fds = FederatedDataset(dataset="cifar10", partitioners={"train": num_partitions})
partition = fds.load_partition(partition_id)
# Divide data on each node: 80% train, 20% test
- partition_train_test = partition.train_test_split(test_size=0.2)
+ partition_train_test = partition.train_test_split(test_size=0.2, seed=42)
pytorch_transforms = Compose(
[ToTensor(), Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]
)
diff --git a/src/py/flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl b/src/py/flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl
index da0e15b903f8..46a5508fe2ac 100644
--- a/src/py/flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl
+++ b/src/py/flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl
@@ -15,7 +15,7 @@ readme = "README.md"
[tool.poetry.dependencies]
python = "^3.9"
# Mandatory dependencies
-flwr-nightly = { version = "1.8.0.dev20240313", extras = ["simulation"] }
+flwr = { version = "^1.8.0", extras = ["simulation"] }
flwr-datasets = { version = "0.0.2", extras = ["vision"] }
torch = "2.2.1"
torchvision = "0.17.1"
diff --git a/src/py/flwr/cli/new/templates/app/requirements.pytorch.txt.tpl b/src/py/flwr/cli/new/templates/app/requirements.pytorch.txt.tpl
index ddb8a814447b..f20b9d71e339 100644
--- a/src/py/flwr/cli/new/templates/app/requirements.pytorch.txt.tpl
+++ b/src/py/flwr/cli/new/templates/app/requirements.pytorch.txt.tpl
@@ -1,4 +1,4 @@
-flwr-nightly[simulation]==1.8.0.dev20240313
+flwr[simulation]>=1.8.0
flwr-datasets[vision]==0.0.2
torch==2.2.1
torchvision==0.17.1
diff --git a/src/py/flwr/client/app.py b/src/py/flwr/client/app.py
index 1720405ab867..4fa9c80c6cdf 100644
--- a/src/py/flwr/client/app.py
+++ b/src/py/flwr/client/app.py
@@ -456,12 +456,13 @@ def _load_client_app() -> ClientApp:
continue
log(INFO, "")
- log(
- INFO,
- "[RUN %s, ROUND %s]",
- message.metadata.run_id,
- message.metadata.group_id,
- )
+ if len(message.metadata.group_id) > 0:
+ log(
+ INFO,
+ "[RUN %s, ROUND %s]",
+ message.metadata.run_id,
+ message.metadata.group_id,
+ )
log(
INFO,
"Received: %s message %s",
diff --git a/src/py/flwr/common/retry_invoker.py b/src/py/flwr/common/retry_invoker.py
index 7cec319e7906..d12124b89840 100644
--- a/src/py/flwr/common/retry_invoker.py
+++ b/src/py/flwr/common/retry_invoker.py
@@ -261,6 +261,7 @@ def try_call_event_handler(
try:
ret = target(*args, **kwargs)
except self.recoverable_exceptions as err:
+ state.exception = err
# Check if giveup event should be triggered
max_tries_exceeded = try_cnt == self.max_tries
max_time_exceeded = (