fl-data-generator 0.1.0__tar.gz

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.
Files changed (27) hide show
  1. fl_data_generator-0.1.0/LICENSE +21 -0
  2. fl_data_generator-0.1.0/PKG-INFO +233 -0
  3. fl_data_generator-0.1.0/README.md +198 -0
  4. fl_data_generator-0.1.0/pyproject.toml +83 -0
  5. fl_data_generator-0.1.0/setup.cfg +4 -0
  6. fl_data_generator-0.1.0/src/fl_data_generator/__init__.py +16 -0
  7. fl_data_generator-0.1.0/src/fl_data_generator/_validation.py +60 -0
  8. fl_data_generator-0.1.0/src/fl_data_generator/dataset.py +422 -0
  9. fl_data_generator-0.1.0/src/fl_data_generator/dirichlet.py +123 -0
  10. fl_data_generator-0.1.0/src/fl_data_generator/hybrid.py +138 -0
  11. fl_data_generator-0.1.0/src/fl_data_generator/iid.py +49 -0
  12. fl_data_generator-0.1.0/src/fl_data_generator/pathological.py +137 -0
  13. fl_data_generator-0.1.0/src/fl_data_generator/quantity.py +83 -0
  14. fl_data_generator-0.1.0/src/fl_data_generator.egg-info/PKG-INFO +233 -0
  15. fl_data_generator-0.1.0/src/fl_data_generator.egg-info/SOURCES.txt +25 -0
  16. fl_data_generator-0.1.0/src/fl_data_generator.egg-info/dependency_links.txt +1 -0
  17. fl_data_generator-0.1.0/src/fl_data_generator.egg-info/requires.txt +10 -0
  18. fl_data_generator-0.1.0/src/fl_data_generator.egg-info/top_level.txt +1 -0
  19. fl_data_generator-0.1.0/tests/test_dataset.py +254 -0
  20. fl_data_generator-0.1.0/tests/test_dirichlet.py +198 -0
  21. fl_data_generator-0.1.0/tests/test_hybrid.py +131 -0
  22. fl_data_generator-0.1.0/tests/test_iid.py +71 -0
  23. fl_data_generator-0.1.0/tests/test_pathological.py +206 -0
  24. fl_data_generator-0.1.0/tests/test_properties.py +200 -0
  25. fl_data_generator-0.1.0/tests/test_quantity.py +100 -0
  26. fl_data_generator-0.1.0/tests/test_readme.py +20 -0
  27. fl_data_generator-0.1.0/tests/test_validation.py +92 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mostafa Khastkhodaei
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,233 @@
1
+ Metadata-Version: 2.4
2
+ Name: fl-data-generator
3
+ Version: 0.1.0
4
+ Summary: A lightweight, framework-agnostic library for IID and non-IID data partitioning in Federated Learning.
5
+ Author-email: Mostafa Khastkhodaei <dev.khastkhodaei@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/khastkhodaei/fl-data-generator
8
+ Project-URL: Bug Tracker, https://github.com/khastkhodaei/fl-data-generator/issues
9
+ Project-URL: Source Code, https://github.com/khastkhodaei/fl-data-generator
10
+ Keywords: federated-learning,data-partitioning,non-iid,dirichlet,label-skew,quantity-skew,machine-learning
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Requires-Python: >=3.9
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: numpy>=1.20.0
27
+ Provides-Extra: dev
28
+ Requires-Dist: hypothesis>=6.0.0; extra == "dev"
29
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
30
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
31
+ Requires-Dist: ruff~=0.17.0; extra == "dev"
32
+ Provides-Extra: viz
33
+ Requires-Dist: matplotlib>=3.5.0; extra == "viz"
34
+ Dynamic: license-file
35
+
36
+ # fl-data-generator
37
+
38
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
39
+ [![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
40
+
41
+ A lightweight, framework-agnostic Python library for **IID** and **Non-IID** data partitioning in **Federated Learning (FL)**.
42
+
43
+ ---
44
+
45
+ ## Overview
46
+
47
+ In Federated Learning research and benchmarks, simulating realistic data heterogeneity across distributed clients is essential. **fl-data-generator** simplifies partitioning arbitrary datasets into realistic client distributions without tying you to any specific deep learning framework.
48
+
49
+ Whether you are using PyTorch, TensorFlow, JAX, or federated frameworks like Flower, FedML, or PySyft, `fl-data-generator` provides clean sample indices and dataset wrappers for your clients.
50
+
51
+ ---
52
+
53
+ ## Features
54
+
55
+ - **IID Partitioning**: Uniform random sample allocation across all clients.
56
+ - **Dirichlet Partitioning**: Continuous label distribution skew ($\text{Dir}(\alpha)$) over class proportions, with client minimum size constraints (`min_size`).
57
+ - **Pathological Non-IID Partitioning**: Allocation of a fixed number of classes ($k$ classes per client, following the NIID-Bench benchmark scheme).
58
+ - **Quantity Skew Partitioning**: Volume heterogeneity where client dataset sizes vary according to a Dirichlet distribution ($\text{Dir}(\beta)$).
59
+ - **Hybrid Partitioning (Experimental)**: Sequential joint label skew and quantity skew allocation combining Dirichlet $\alpha$ and $\beta$.
60
+ - **FederatedDataset Wrapper**: High-level wrapper supporting built-in computer vision datasets (`mnist`, `cifar10`, `cifar100`, `fashion_mnist`) as well as custom `(X, y)` tuples and PyTorch `Dataset` instances.
61
+
62
+ ---
63
+
64
+ ## Installation
65
+
66
+ ### From Source
67
+
68
+ ```bash
69
+ git clone https://github.com/khastkhodaei/fl-data-generator.git
70
+ cd fl-data-generator
71
+ pip install -e .
72
+ ```
73
+
74
+ ### With Optional Extras
75
+
76
+ ```bash
77
+ # Development dependencies (pytest, hypothesis, ruff, etc.)
78
+ pip install -e ".[dev]"
79
+
80
+ # Visualization support (matplotlib)
81
+ pip install -e ".[viz]"
82
+ ```
83
+
84
+ ---
85
+
86
+ ## Runnable Examples
87
+
88
+ ### 1. IID Partitioning
89
+
90
+ Uniformly partitions sample indices across clients.
91
+
92
+ ```python
93
+ import numpy as np
94
+ from fl_data_generator import iid_partition
95
+
96
+ labels = np.array([0, 1] * 50) # 100 samples
97
+ client_dict = iid_partition(labels, num_clients=4, seed=42)
98
+
99
+ print("Client sample counts:", [len(indices) for indices in client_dict.values()])
100
+ # Real Output: Client sample counts: [25, 25, 25, 25]
101
+ ```
102
+
103
+ ### 2. Dirichlet Label Skew Partitioning
104
+
105
+ Simulates non-IID label skew parameterized by concentration parameter `alpha`. Smaller $\alpha$ generates severe distribution skew, while larger $\alpha$ approaches uniform class distributions.
106
+
107
+ ```python
108
+ import numpy as np
109
+ from fl_data_generator import dirichlet_partition
110
+
111
+ labels = np.repeat(np.arange(4), 50) # 200 samples across 4 classes
112
+ client_dict = dirichlet_partition(labels, num_clients=3, alpha=0.5, seed=42)
113
+
114
+ print("Client sample counts:", [len(indices) for indices in client_dict.values()])
115
+ # Real Output: Client sample counts: [114, 42, 44]
116
+ ```
117
+
118
+ ### 3. Pathological Non-IID Partitioning ($k$ Classes per Client)
119
+
120
+ Partitions data such that each client receives samples from exactly $k$ distinct classes (NIID-Bench #C=k scheme).
121
+
122
+ ```python
123
+ import numpy as np
124
+ from fl_data_generator import pathological_partition
125
+
126
+ labels = np.repeat(np.arange(4), 50) # 200 samples across 4 classes
127
+ client_dict = pathological_partition(labels, num_clients=4, classes_per_client=2, seed=42)
128
+
129
+ client_0_labels = labels[client_dict[0]]
130
+ print("Client 0 distinct classes:", np.unique(client_0_labels).tolist())
131
+ # Real Output: Client 0 distinct classes: [1, 2]
132
+ ```
133
+
134
+ ### 4. Quantity Skew Partitioning (Volume Heterogeneity)
135
+
136
+ Allocates varying numbers of samples to clients according to a Dirichlet distribution parameterized by concentration parameter `beta`.
137
+
138
+ ```python
139
+ import numpy as np
140
+ from fl_data_generator import quantity_skew_partition
141
+
142
+ labels = np.arange(200) # 200 samples
143
+ client_dict = quantity_skew_partition(labels, num_clients=4, beta=0.5, seed=42)
144
+
145
+ print("Client sample counts:", [len(indices) for indices in client_dict.values()])
146
+ # Real Output: Client sample counts: [52, 72, 16, 60]
147
+ ```
148
+
149
+ ### 5. Hybrid Partitioning (Experimental)
150
+
151
+ Combines quantity skew ($\beta$) and label skew ($\alpha$) via sequential allocation.
152
+
153
+ ```python
154
+ import numpy as np
155
+ from fl_data_generator import hybrid_partition
156
+
157
+ labels = np.repeat(np.arange(4), 50) # 200 samples across 4 classes
158
+ client_dict = hybrid_partition(labels, num_clients=4, alpha=0.5, beta=0.5, seed=42)
159
+
160
+ print("Client sample counts:", [len(indices) for indices in client_dict.values()])
161
+ # Real Output: Client sample counts: [56, 89, 2, 53]
162
+ ```
163
+
164
+ ### 6. FederatedDataset Interface
165
+
166
+ High-level interface for creating client-specific datasets and PyTorch DataLoaders.
167
+
168
+ ```python
169
+ import numpy as np
170
+ from fl_data_generator import FederatedDataset
171
+
172
+ X = np.random.default_rng(42).standard_normal((100, 5))
173
+ y = np.array([0, 1] * 50)
174
+
175
+ fed_data = FederatedDataset((X, y), num_clients=4, partition="iid", seed=42)
176
+ client_0_ds = fed_data.get_client_dataset(0)
177
+
178
+ print(f"Client 0 sample count: {len(client_0_ds)}")
179
+ features, label = client_0_ds[0]
180
+ print(f"Feature shape: {features.shape}, Label: {label}")
181
+ # Real Output:
182
+ # Client 0 sample count: 25
183
+ # Feature shape: (5,), Label: 1
184
+ ```
185
+
186
+ ---
187
+
188
+ ## Roadmap
189
+
190
+ The following experimental features and partition strategies are planned for future releases:
191
+ - **Feature Skew (Covariate Shift)**: Partitioning based on continuous feature space clusters or synthetic domain perturbations.
192
+ - **Power-Law Quantity Skew**: Heavy-tailed sample volume distribution across clients.
193
+ - **Asymmetric Class Topologies**: Custom directed class sharing graphs.
194
+
195
+ ---
196
+
197
+ ## Project Structure
198
+
199
+ ```text
200
+ fl-data-generator/
201
+ ├── .github/
202
+ │ └── workflows/
203
+ │ └── ci.yml # GitHub Actions CI matrix workflow
204
+ ├── src/
205
+ │ └── fl_data_generator/ # Core library package
206
+ │ ├── __init__.py # Public API exports
207
+ │ ├── _validation.py # Input validation utilities
208
+ │ ├── dataset.py # FederatedDataset interface & torchvision loaders
209
+ │ ├── dirichlet.py # Dirichlet label skew partitioner
210
+ │ ├── hybrid.py # Hybrid label & quantity skew partitioner
211
+ │ ├── iid.py # Uniform IID partitioner
212
+ │ ├── pathological.py # Pathological k-class partitioner
213
+ │ └── quantity.py # Quantity skew partitioner
214
+ ├── tests/ # Unit, integration, and property test suite
215
+ │ ├── test_dataset.py
216
+ │ ├── test_dirichlet.py
217
+ │ ├── test_hybrid.py
218
+ │ ├── test_iid.py
219
+ │ ├── test_pathological.py
220
+ │ ├── test_properties.py
221
+ │ ├── test_quantity.py
222
+ │ ├── test_readme.py
223
+ │ └── test_validation.py
224
+ ├── pyproject.toml # Package build configuration & Ruff settings
225
+ ├── LICENSE # MIT License
226
+ └── README.md # Project documentation
227
+ ```
228
+
229
+ ---
230
+
231
+ ## License
232
+
233
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
@@ -0,0 +1,198 @@
1
+ # fl-data-generator
2
+
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
+ [![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
5
+
6
+ A lightweight, framework-agnostic Python library for **IID** and **Non-IID** data partitioning in **Federated Learning (FL)**.
7
+
8
+ ---
9
+
10
+ ## Overview
11
+
12
+ In Federated Learning research and benchmarks, simulating realistic data heterogeneity across distributed clients is essential. **fl-data-generator** simplifies partitioning arbitrary datasets into realistic client distributions without tying you to any specific deep learning framework.
13
+
14
+ Whether you are using PyTorch, TensorFlow, JAX, or federated frameworks like Flower, FedML, or PySyft, `fl-data-generator` provides clean sample indices and dataset wrappers for your clients.
15
+
16
+ ---
17
+
18
+ ## Features
19
+
20
+ - **IID Partitioning**: Uniform random sample allocation across all clients.
21
+ - **Dirichlet Partitioning**: Continuous label distribution skew ($\text{Dir}(\alpha)$) over class proportions, with client minimum size constraints (`min_size`).
22
+ - **Pathological Non-IID Partitioning**: Allocation of a fixed number of classes ($k$ classes per client, following the NIID-Bench benchmark scheme).
23
+ - **Quantity Skew Partitioning**: Volume heterogeneity where client dataset sizes vary according to a Dirichlet distribution ($\text{Dir}(\beta)$).
24
+ - **Hybrid Partitioning (Experimental)**: Sequential joint label skew and quantity skew allocation combining Dirichlet $\alpha$ and $\beta$.
25
+ - **FederatedDataset Wrapper**: High-level wrapper supporting built-in computer vision datasets (`mnist`, `cifar10`, `cifar100`, `fashion_mnist`) as well as custom `(X, y)` tuples and PyTorch `Dataset` instances.
26
+
27
+ ---
28
+
29
+ ## Installation
30
+
31
+ ### From Source
32
+
33
+ ```bash
34
+ git clone https://github.com/khastkhodaei/fl-data-generator.git
35
+ cd fl-data-generator
36
+ pip install -e .
37
+ ```
38
+
39
+ ### With Optional Extras
40
+
41
+ ```bash
42
+ # Development dependencies (pytest, hypothesis, ruff, etc.)
43
+ pip install -e ".[dev]"
44
+
45
+ # Visualization support (matplotlib)
46
+ pip install -e ".[viz]"
47
+ ```
48
+
49
+ ---
50
+
51
+ ## Runnable Examples
52
+
53
+ ### 1. IID Partitioning
54
+
55
+ Uniformly partitions sample indices across clients.
56
+
57
+ ```python
58
+ import numpy as np
59
+ from fl_data_generator import iid_partition
60
+
61
+ labels = np.array([0, 1] * 50) # 100 samples
62
+ client_dict = iid_partition(labels, num_clients=4, seed=42)
63
+
64
+ print("Client sample counts:", [len(indices) for indices in client_dict.values()])
65
+ # Real Output: Client sample counts: [25, 25, 25, 25]
66
+ ```
67
+
68
+ ### 2. Dirichlet Label Skew Partitioning
69
+
70
+ Simulates non-IID label skew parameterized by concentration parameter `alpha`. Smaller $\alpha$ generates severe distribution skew, while larger $\alpha$ approaches uniform class distributions.
71
+
72
+ ```python
73
+ import numpy as np
74
+ from fl_data_generator import dirichlet_partition
75
+
76
+ labels = np.repeat(np.arange(4), 50) # 200 samples across 4 classes
77
+ client_dict = dirichlet_partition(labels, num_clients=3, alpha=0.5, seed=42)
78
+
79
+ print("Client sample counts:", [len(indices) for indices in client_dict.values()])
80
+ # Real Output: Client sample counts: [114, 42, 44]
81
+ ```
82
+
83
+ ### 3. Pathological Non-IID Partitioning ($k$ Classes per Client)
84
+
85
+ Partitions data such that each client receives samples from exactly $k$ distinct classes (NIID-Bench #C=k scheme).
86
+
87
+ ```python
88
+ import numpy as np
89
+ from fl_data_generator import pathological_partition
90
+
91
+ labels = np.repeat(np.arange(4), 50) # 200 samples across 4 classes
92
+ client_dict = pathological_partition(labels, num_clients=4, classes_per_client=2, seed=42)
93
+
94
+ client_0_labels = labels[client_dict[0]]
95
+ print("Client 0 distinct classes:", np.unique(client_0_labels).tolist())
96
+ # Real Output: Client 0 distinct classes: [1, 2]
97
+ ```
98
+
99
+ ### 4. Quantity Skew Partitioning (Volume Heterogeneity)
100
+
101
+ Allocates varying numbers of samples to clients according to a Dirichlet distribution parameterized by concentration parameter `beta`.
102
+
103
+ ```python
104
+ import numpy as np
105
+ from fl_data_generator import quantity_skew_partition
106
+
107
+ labels = np.arange(200) # 200 samples
108
+ client_dict = quantity_skew_partition(labels, num_clients=4, beta=0.5, seed=42)
109
+
110
+ print("Client sample counts:", [len(indices) for indices in client_dict.values()])
111
+ # Real Output: Client sample counts: [52, 72, 16, 60]
112
+ ```
113
+
114
+ ### 5. Hybrid Partitioning (Experimental)
115
+
116
+ Combines quantity skew ($\beta$) and label skew ($\alpha$) via sequential allocation.
117
+
118
+ ```python
119
+ import numpy as np
120
+ from fl_data_generator import hybrid_partition
121
+
122
+ labels = np.repeat(np.arange(4), 50) # 200 samples across 4 classes
123
+ client_dict = hybrid_partition(labels, num_clients=4, alpha=0.5, beta=0.5, seed=42)
124
+
125
+ print("Client sample counts:", [len(indices) for indices in client_dict.values()])
126
+ # Real Output: Client sample counts: [56, 89, 2, 53]
127
+ ```
128
+
129
+ ### 6. FederatedDataset Interface
130
+
131
+ High-level interface for creating client-specific datasets and PyTorch DataLoaders.
132
+
133
+ ```python
134
+ import numpy as np
135
+ from fl_data_generator import FederatedDataset
136
+
137
+ X = np.random.default_rng(42).standard_normal((100, 5))
138
+ y = np.array([0, 1] * 50)
139
+
140
+ fed_data = FederatedDataset((X, y), num_clients=4, partition="iid", seed=42)
141
+ client_0_ds = fed_data.get_client_dataset(0)
142
+
143
+ print(f"Client 0 sample count: {len(client_0_ds)}")
144
+ features, label = client_0_ds[0]
145
+ print(f"Feature shape: {features.shape}, Label: {label}")
146
+ # Real Output:
147
+ # Client 0 sample count: 25
148
+ # Feature shape: (5,), Label: 1
149
+ ```
150
+
151
+ ---
152
+
153
+ ## Roadmap
154
+
155
+ The following experimental features and partition strategies are planned for future releases:
156
+ - **Feature Skew (Covariate Shift)**: Partitioning based on continuous feature space clusters or synthetic domain perturbations.
157
+ - **Power-Law Quantity Skew**: Heavy-tailed sample volume distribution across clients.
158
+ - **Asymmetric Class Topologies**: Custom directed class sharing graphs.
159
+
160
+ ---
161
+
162
+ ## Project Structure
163
+
164
+ ```text
165
+ fl-data-generator/
166
+ ├── .github/
167
+ │ └── workflows/
168
+ │ └── ci.yml # GitHub Actions CI matrix workflow
169
+ ├── src/
170
+ │ └── fl_data_generator/ # Core library package
171
+ │ ├── __init__.py # Public API exports
172
+ │ ├── _validation.py # Input validation utilities
173
+ │ ├── dataset.py # FederatedDataset interface & torchvision loaders
174
+ │ ├── dirichlet.py # Dirichlet label skew partitioner
175
+ │ ├── hybrid.py # Hybrid label & quantity skew partitioner
176
+ │ ├── iid.py # Uniform IID partitioner
177
+ │ ├── pathological.py # Pathological k-class partitioner
178
+ │ └── quantity.py # Quantity skew partitioner
179
+ ├── tests/ # Unit, integration, and property test suite
180
+ │ ├── test_dataset.py
181
+ │ ├── test_dirichlet.py
182
+ │ ├── test_hybrid.py
183
+ │ ├── test_iid.py
184
+ │ ├── test_pathological.py
185
+ │ ├── test_properties.py
186
+ │ ├── test_quantity.py
187
+ │ ├── test_readme.py
188
+ │ └── test_validation.py
189
+ ├── pyproject.toml # Package build configuration & Ruff settings
190
+ ├── LICENSE # MIT License
191
+ └── README.md # Project documentation
192
+ ```
193
+
194
+ ---
195
+
196
+ ## License
197
+
198
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
@@ -0,0 +1,83 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "fl-data-generator"
7
+ version = "0.1.0"
8
+ description = "A lightweight, framework-agnostic library for IID and non-IID data partitioning in Federated Learning."
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = { text = "MIT" }
12
+ authors = [
13
+ { name = "Mostafa Khastkhodaei", email = "dev.khastkhodaei@gmail.com" }
14
+ ]
15
+ keywords = [
16
+ "federated-learning",
17
+ "data-partitioning",
18
+ "non-iid",
19
+ "dirichlet",
20
+ "label-skew",
21
+ "quantity-skew",
22
+ "machine-learning"
23
+ ]
24
+ classifiers = [
25
+ "Development Status :: 3 - Alpha",
26
+ "Intended Audience :: Science/Research",
27
+ "Intended Audience :: Developers",
28
+ "License :: OSI Approved :: MIT License",
29
+ "Programming Language :: Python :: 3",
30
+ "Programming Language :: Python :: 3.9",
31
+ "Programming Language :: Python :: 3.10",
32
+ "Programming Language :: Python :: 3.11",
33
+ "Programming Language :: Python :: 3.12",
34
+ "Programming Language :: Python :: 3.13",
35
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
36
+ "Topic :: Software Development :: Libraries :: Python Modules",
37
+ ]
38
+ dependencies = [
39
+ "numpy>=1.20.0",
40
+ ]
41
+
42
+ [project.optional-dependencies]
43
+ dev = [
44
+ "hypothesis>=6.0.0",
45
+ "pytest>=7.0.0",
46
+ "pytest-cov>=4.0.0",
47
+ "ruff~=0.17.0",
48
+ ]
49
+ viz = [
50
+ "matplotlib>=3.5.0",
51
+ ]
52
+
53
+ [project.urls]
54
+ "Homepage" = "https://github.com/khastkhodaei/fl-data-generator"
55
+ "Bug Tracker" = "https://github.com/khastkhodaei/fl-data-generator/issues"
56
+ "Source Code" = "https://github.com/khastkhodaei/fl-data-generator"
57
+
58
+ [tool.setuptools]
59
+ package-dir = {"" = "src"}
60
+
61
+ [tool.setuptools.packages.find]
62
+ where = ["src"]
63
+ include = ["fl_data_generator*"]
64
+
65
+ [tool.pytest.ini_options]
66
+ pythonpath = ["src"]
67
+ testpaths = ["tests"]
68
+
69
+ [tool.ruff]
70
+ line-length = 88
71
+ target-version = "py38"
72
+
73
+ [tool.ruff.lint]
74
+ select = [
75
+ "E4",
76
+ "E7",
77
+ "E9",
78
+ "F",
79
+ ]
80
+ ignore = [
81
+ "FA100",
82
+ ]
83
+
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,16 @@
1
+ from .dataset import FederatedDataset, NumpyDataset
2
+ from .dirichlet import dirichlet_partition
3
+ from .hybrid import hybrid_partition
4
+ from .iid import iid_partition
5
+ from .pathological import pathological_partition
6
+ from .quantity import quantity_skew_partition
7
+
8
+ __all__ = [
9
+ "FederatedDataset",
10
+ "NumpyDataset",
11
+ "dirichlet_partition",
12
+ "hybrid_partition",
13
+ "iid_partition",
14
+ "pathological_partition",
15
+ "quantity_skew_partition",
16
+ ]
@@ -0,0 +1,60 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Sequence
4
+
5
+ import numpy as np
6
+
7
+
8
+ def validate_labels(labels: Sequence | np.ndarray) -> np.ndarray:
9
+ """Validate and cast dataset labels to a 1D int64 numpy array.
10
+
11
+ Parameters
12
+ ----------
13
+ labels : Sequence or np.ndarray
14
+ Dataset labels as an array-like or sequence.
15
+
16
+ Returns
17
+ -------
18
+ np.ndarray
19
+ A 1D numpy array with dtype np.int64.
20
+
21
+ Raises
22
+ ------
23
+ ValueError
24
+ If labels is empty, not 1-dimensional, contains NaN or Inf, or
25
+ cannot be converted to integer values without loss of information.
26
+ TypeError
27
+ If labels elements cannot be converted to numeric values.
28
+ """
29
+ arr = np.asarray(labels)
30
+
31
+ if arr.ndim != 1:
32
+ raise ValueError("labels must be 1-dimensional; consider using labels.ravel()")
33
+
34
+ if arr.size == 0:
35
+ raise ValueError("labels cannot be empty")
36
+
37
+ if not np.issubdtype(arr.dtype, np.number):
38
+ # Could be object array with strings or other non-numeric types
39
+ try:
40
+ arr = arr.astype(np.float64)
41
+ except (ValueError, TypeError) as exc:
42
+ raise TypeError("labels must contain numeric values") from exc
43
+
44
+ # Check for NaN or Inf
45
+ if np.isnan(arr).any():
46
+ raise ValueError("labels contains NaN values")
47
+
48
+ if np.isinf(arr).any():
49
+ raise ValueError("labels contains infinite values")
50
+
51
+ # Check if integer-valued
52
+ if np.issubdtype(arr.dtype, np.floating):
53
+ if not np.all(np.floor(arr) == arr):
54
+ raise ValueError("labels must be integer-valued")
55
+ arr = arr.astype(np.int64)
56
+ elif np.issubdtype(arr.dtype, np.integer):
57
+ if arr.dtype != np.int64:
58
+ arr = arr.astype(np.int64)
59
+
60
+ return arr