secure-fl 2026.3.16.dev1__tar.gz → 2026.3.17.dev2__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.
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/.gitignore +1 -1
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/PKG-INFO +1 -1
- secure_fl-2026.3.17.dev2/src/secure_fl/.build_number +1 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/benchmarks/__init__.py +6 -2
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/benchmarks/pipeline.py +19 -4
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/benchmarks/reporting.py +9 -4
- secure_fl-2026.3.17.dev2/src/secure_fl/cli/__main__.py +6 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/cli/handlers.py +17 -5
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/data/__init__.py +2 -2
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/data/benchmark_data.py +78 -18
- secure_fl-2026.3.17.dev2/src/secure_fl/proofs/server/aggregation.circom +55 -0
- secure_fl-2026.3.16.dev1/src/secure_fl/.build_number +0 -1
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/LICENSE +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/README.md +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/docs/research paper/README.md +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/experiments/README.md +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/pyproject.toml +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/__init__.py +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/__main__.py +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/benchmarks/defaults.py +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/benchmarks/execution.py +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/cli/__init__.py +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/cli/main.py +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/cli/setup.py +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/cli/zkp_check.py +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/core/__init__.py +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/core/_version.py +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/core/config.py +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/core/exceptions.py +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/core/types.py +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/federation/__init__.py +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/federation/aggregation.py +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/federation/client.py +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/federation/client_runtime.py +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/federation/client_utils.py +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/federation/server.py +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/federation/server_runtime.py +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/federation/stability_monitor.py +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/models/__init__.py +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/models/factory.py +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/models/mnist_cnn.py +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/models/simple.py +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/proofs/__init__.py +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/proofs/client_circuits/__init__.py +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/proofs/client_circuits/delta_bound.py +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/proofs/client_circuits/sgd_full_trace.cairo +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/proofs/server/pot12_final.ptau +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/proofs/server/test.txt +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/py.typed +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/utils/__init__.py +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/utils/helpers.py +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/utils/logging_config.py +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/zkp/__init__.py +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/zkp/base.py +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/zkp/client_proof_manager.py +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/zkp/proof_manager.py +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/zkp/proof_schema.py +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/zkp/quantization.py +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/zkp/server_proof_manager.py +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/zkp/server_setup.py +0 -0
- {secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/zkp/server_snark_io.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: secure-fl
|
|
3
|
-
Version: 2026.3.
|
|
3
|
+
Version: 2026.3.17.dev2
|
|
4
4
|
Summary: Dual-Verifiable Framework for Federated Learning using Zero-Knowledge Proofs
|
|
5
5
|
Project-URL: Homepage, https://github.com/krishantt/secure-fl
|
|
6
6
|
Project-URL: Bug Reports, https://github.com/krishantt/secure-fl/issues
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2026.03.17:2
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"""Benchmark orchestration helpers for experiment entrypoints."""
|
|
2
2
|
|
|
3
3
|
from secure_fl.data.benchmark_data import SUPPORTED_DATASETS
|
|
4
|
+
from secure_fl.data.benchmark_data import kfold_split_indices
|
|
4
5
|
from secure_fl.data.benchmark_data import load_dataset
|
|
5
|
-
from secure_fl.data.benchmark_data import maybe_subsample_dataset
|
|
6
6
|
from secure_fl.data.benchmark_data import normalize_datasets
|
|
7
7
|
from secure_fl.data.benchmark_data import partition_dataset
|
|
8
|
+
from secure_fl.data.benchmark_data import subsample_dataset
|
|
8
9
|
from secure_fl.models.factory import model_fn
|
|
9
10
|
|
|
10
11
|
from .defaults import PAPER_DEFAULT_ROUNDS
|
|
@@ -12,6 +13,7 @@ from .defaults import resolve_num_rounds
|
|
|
12
13
|
from .execution import run_training_rounds
|
|
13
14
|
from .pipeline import run_single
|
|
14
15
|
from .reporting import build_tradeoff_report
|
|
16
|
+
from .reporting import summarize_folds
|
|
15
17
|
from .reporting import summarize_repeats
|
|
16
18
|
|
|
17
19
|
__all__ = [
|
|
@@ -21,10 +23,12 @@ __all__ = [
|
|
|
21
23
|
"normalize_datasets",
|
|
22
24
|
"load_dataset",
|
|
23
25
|
"partition_dataset",
|
|
24
|
-
"
|
|
26
|
+
"subsample_dataset",
|
|
27
|
+
"kfold_split_indices",
|
|
25
28
|
"model_fn",
|
|
26
29
|
"run_single",
|
|
27
30
|
"run_training_rounds",
|
|
31
|
+
"summarize_folds",
|
|
28
32
|
"summarize_repeats",
|
|
29
33
|
"build_tradeoff_report",
|
|
30
34
|
]
|
|
@@ -10,8 +10,8 @@ from secure_fl import FedJSCMAggregator
|
|
|
10
10
|
from secure_fl import create_client
|
|
11
11
|
from secure_fl import torch_to_ndarrays
|
|
12
12
|
from secure_fl.data.benchmark_data import load_dataset
|
|
13
|
-
from secure_fl.data.benchmark_data import maybe_subsample_dataset
|
|
14
13
|
from secure_fl.data.benchmark_data import partition_dataset
|
|
14
|
+
from secure_fl.data.benchmark_data import subsample_dataset
|
|
15
15
|
from secure_fl.models.factory import model_fn
|
|
16
16
|
|
|
17
17
|
from .execution import run_training_rounds
|
|
@@ -30,11 +30,26 @@ def run_single(
|
|
|
30
30
|
min_proof_coverage: float,
|
|
31
31
|
max_train_samples: int,
|
|
32
32
|
max_test_samples: int,
|
|
33
|
+
train_set_override: torch.utils.data.Dataset | None = None,
|
|
34
|
+
eval_set_override: torch.utils.data.Dataset | None = None,
|
|
33
35
|
) -> dict[str, Any]:
|
|
34
36
|
"""Run one dataset/config benchmark and return rounds+summary."""
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
if train_set_override is not None and eval_set_override is not None:
|
|
38
|
+
train_set = train_set_override
|
|
39
|
+
test_set = eval_set_override
|
|
40
|
+
else:
|
|
41
|
+
loaded_train_set, loaded_test_set = load_dataset(dataset, seed)
|
|
42
|
+
train_set = (
|
|
43
|
+
train_set_override
|
|
44
|
+
if train_set_override is not None
|
|
45
|
+
else subsample_dataset(loaded_train_set, max_train_samples, seed)
|
|
46
|
+
)
|
|
47
|
+
test_set = (
|
|
48
|
+
eval_set_override
|
|
49
|
+
if eval_set_override is not None
|
|
50
|
+
else subsample_dataset(loaded_test_set, max_test_samples, seed + 1)
|
|
51
|
+
)
|
|
52
|
+
|
|
38
53
|
client_subsets = partition_dataset(train_set, num_clients, config["iid"], seed)
|
|
39
54
|
model_factory = model_fn(dataset)
|
|
40
55
|
|
|
@@ -7,11 +7,11 @@ from typing import Any
|
|
|
7
7
|
import numpy as np
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
def
|
|
11
|
-
"""Summarize
|
|
10
|
+
def summarize_folds(runs: list[dict[str, Any]]) -> dict[str, float]:
|
|
11
|
+
"""Summarize fold-level benchmark runs into mean/std/CI metrics."""
|
|
12
12
|
if not runs:
|
|
13
13
|
return {
|
|
14
|
-
"
|
|
14
|
+
"folds": 0.0,
|
|
15
15
|
"final_accuracy_mean": 0.0,
|
|
16
16
|
"final_accuracy_std": 0.0,
|
|
17
17
|
"final_accuracy_ci95": 0.0,
|
|
@@ -82,7 +82,7 @@ def summarize_repeats(runs: list[dict[str, Any]]) -> dict[str, float]:
|
|
|
82
82
|
ci95 = float(1.96 * final_acc.std(ddof=1) / np.sqrt(len(final_acc)))
|
|
83
83
|
|
|
84
84
|
return {
|
|
85
|
-
"
|
|
85
|
+
"folds": float(len(runs)),
|
|
86
86
|
"final_accuracy_mean": float(final_acc.mean()),
|
|
87
87
|
"final_accuracy_std": float(final_acc.std(ddof=0)),
|
|
88
88
|
"final_accuracy_ci95": ci95,
|
|
@@ -104,6 +104,11 @@ def summarize_repeats(runs: list[dict[str, Any]]) -> dict[str, float]:
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
|
|
107
|
+
def summarize_repeats(runs: list[dict[str, Any]]) -> dict[str, float]:
|
|
108
|
+
"""Backward-compatible alias for fold-based summarization."""
|
|
109
|
+
return summarize_folds(runs)
|
|
110
|
+
|
|
111
|
+
|
|
107
112
|
def build_tradeoff_report(results: dict[str, dict[str, Any]]) -> dict[str, Any]:
|
|
108
113
|
"""Build dataset-level baseline vs ZKP comparison report."""
|
|
109
114
|
report: dict[str, Any] = {"datasets": {}}
|
|
@@ -48,19 +48,27 @@ def run_server(
|
|
|
48
48
|
with open(config) as f:
|
|
49
49
|
cfg = yaml.safe_load(f)
|
|
50
50
|
else:
|
|
51
|
-
cfg = get_default_config()
|
|
51
|
+
cfg = get_default_config().to_dict()
|
|
52
52
|
|
|
53
|
-
cfg
|
|
53
|
+
server_cfg = cfg.get("server", {})
|
|
54
|
+
zkp_cfg = cfg.get("zkp", {})
|
|
55
|
+
server_runtime_cfg = {"host": host, "port": port, "num_rounds": rounds}
|
|
54
56
|
|
|
55
|
-
strategy_config = {
|
|
57
|
+
strategy_config = {
|
|
58
|
+
"momentum": float(server_cfg.get("momentum", 0.9)),
|
|
59
|
+
"learning_rate": float(server_cfg.get("learning_rate", 0.01)),
|
|
60
|
+
"enable_zkp": bool(zkp_cfg.get("enable_client_proofs", enable_zkp)),
|
|
61
|
+
"proof_rigor": proof_rigor,
|
|
62
|
+
"blockchain_verification": blockchain,
|
|
63
|
+
}
|
|
56
64
|
strategy_config.update(
|
|
57
65
|
{
|
|
58
66
|
"min_available_clients": min_clients,
|
|
59
67
|
"min_fit_clients": min_clients,
|
|
60
68
|
"min_evaluate_clients": min_clients,
|
|
61
|
-
"momentum": momentum,
|
|
62
69
|
"enable_zkp": enable_zkp,
|
|
63
70
|
"proof_rigor": proof_rigor,
|
|
71
|
+
"momentum": momentum,
|
|
64
72
|
"blockchain_verification": blockchain,
|
|
65
73
|
}
|
|
66
74
|
)
|
|
@@ -89,7 +97,11 @@ def run_server(
|
|
|
89
97
|
console=console,
|
|
90
98
|
) as progress:
|
|
91
99
|
task = progress.add_task("Starting Secure FL Server...", total=None)
|
|
92
|
-
server = SecureFlowerServer(
|
|
100
|
+
server = SecureFlowerServer(
|
|
101
|
+
strategy=strategy,
|
|
102
|
+
**server_runtime_cfg,
|
|
103
|
+
config=server_cfg,
|
|
104
|
+
)
|
|
93
105
|
progress.update(task, description="Server ready! Waiting for clients...")
|
|
94
106
|
|
|
95
107
|
table = Table(title="Server Configuration")
|
|
@@ -14,9 +14,9 @@ from torch.utils.data import Dataset
|
|
|
14
14
|
|
|
15
15
|
from secure_fl.data.benchmark_data import SUPPORTED_DATASETS
|
|
16
16
|
from secure_fl.data.benchmark_data import load_dataset
|
|
17
|
-
from secure_fl.data.benchmark_data import maybe_subsample_dataset
|
|
18
17
|
from secure_fl.data.benchmark_data import normalize_datasets
|
|
19
18
|
from secure_fl.data.benchmark_data import partition_dataset
|
|
19
|
+
from secure_fl.data.benchmark_data import subsample_dataset
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
class FederatedDataLoader:
|
|
@@ -81,5 +81,5 @@ __all__ = [
|
|
|
81
81
|
"normalize_datasets",
|
|
82
82
|
"load_dataset",
|
|
83
83
|
"partition_dataset",
|
|
84
|
-
"
|
|
84
|
+
"subsample_dataset",
|
|
85
85
|
]
|
|
@@ -158,19 +158,12 @@ def partition_dataset(
|
|
|
158
158
|
chunks = np.array_split(indices, num_clients)
|
|
159
159
|
return [Subset(dataset, chunk.tolist()) for chunk in chunks]
|
|
160
160
|
|
|
161
|
-
|
|
162
|
-
if
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
rng.shuffle(indices)
|
|
167
|
-
chunks = np.array_split(indices, num_clients)
|
|
168
|
-
return [Subset(dataset, chunk.tolist()) for chunk in chunks]
|
|
161
|
+
labels_arr = extract_dataset_labels(dataset)
|
|
162
|
+
if labels_arr is None:
|
|
163
|
+
rng.shuffle(indices)
|
|
164
|
+
chunks = np.array_split(indices, num_clients)
|
|
165
|
+
return [Subset(dataset, chunk.tolist()) for chunk in chunks]
|
|
169
166
|
|
|
170
|
-
if isinstance(labels, torch.Tensor):
|
|
171
|
-
labels_arr = labels.detach().cpu().numpy()
|
|
172
|
-
else:
|
|
173
|
-
labels_arr = np.asarray(labels)
|
|
174
167
|
unique_labels = np.unique(labels_arr)
|
|
175
168
|
client_bins: list[list[int]] = [[] for _ in range(num_clients)]
|
|
176
169
|
classes_per_client = max(2, len(unique_labels) // max(1, num_clients // 2))
|
|
@@ -190,15 +183,82 @@ def partition_dataset(
|
|
|
190
183
|
return [Subset(dataset, sorted(set(bin_idx))) for bin_idx in client_bins]
|
|
191
184
|
|
|
192
185
|
|
|
193
|
-
def
|
|
194
|
-
|
|
186
|
+
def extract_dataset_labels(dataset: Dataset[Any]) -> np.ndarray | None:
|
|
187
|
+
"""Extract label array from supported dataset wrappers."""
|
|
188
|
+
labels = getattr(dataset, "targets", None)
|
|
189
|
+
if labels is None:
|
|
190
|
+
labels = getattr(dataset, "labels", None)
|
|
191
|
+
|
|
192
|
+
if labels is not None:
|
|
193
|
+
if isinstance(labels, torch.Tensor):
|
|
194
|
+
return labels.detach().cpu().numpy().reshape(-1)
|
|
195
|
+
return np.asarray(labels).reshape(-1)
|
|
196
|
+
|
|
197
|
+
if isinstance(dataset, Subset):
|
|
198
|
+
base_labels = extract_dataset_labels(cast("Dataset[Any]", dataset.dataset))
|
|
199
|
+
if base_labels is None:
|
|
200
|
+
return None
|
|
201
|
+
subset_indices = np.asarray(dataset.indices, dtype=np.int64)
|
|
202
|
+
return base_labels[subset_indices]
|
|
203
|
+
|
|
204
|
+
if hasattr(dataset, "tensors") and len(dataset.tensors) >= 2:
|
|
205
|
+
labels_tensor = dataset.tensors[1]
|
|
206
|
+
if isinstance(labels_tensor, torch.Tensor):
|
|
207
|
+
return labels_tensor.detach().cpu().numpy().reshape(-1)
|
|
208
|
+
return np.asarray(labels_tensor).reshape(-1)
|
|
209
|
+
|
|
210
|
+
return None
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def kfold_split_indices(
|
|
214
|
+
dataset: Dataset[Any],
|
|
215
|
+
k_folds: int,
|
|
216
|
+
seed: int,
|
|
217
|
+
*,
|
|
218
|
+
stratified: bool = True,
|
|
219
|
+
) -> list[tuple[list[int], list[int]]]:
|
|
220
|
+
"""Create train/validation index splits for k-fold cross-validation."""
|
|
221
|
+
n = len(cast("Sized", dataset))
|
|
222
|
+
if k_folds < 2:
|
|
223
|
+
raise ValueError("k_folds must be >= 2")
|
|
224
|
+
if k_folds > n:
|
|
225
|
+
raise ValueError("k_folds cannot exceed dataset size")
|
|
226
|
+
|
|
227
|
+
rng = np.random.default_rng(seed)
|
|
228
|
+
all_indices = np.arange(n)
|
|
229
|
+
labels = extract_dataset_labels(dataset) if stratified else None
|
|
230
|
+
|
|
231
|
+
if labels is not None and len(labels) == n:
|
|
232
|
+
fold_val_indices: list[list[int]] = [[] for _ in range(k_folds)]
|
|
233
|
+
for cls in np.unique(labels):
|
|
234
|
+
cls_indices = np.where(labels == cls)[0]
|
|
235
|
+
rng.shuffle(cls_indices)
|
|
236
|
+
cls_parts = np.array_split(cls_indices, k_folds)
|
|
237
|
+
for fold_idx, part in enumerate(cls_parts):
|
|
238
|
+
fold_val_indices[fold_idx].extend(part.tolist())
|
|
239
|
+
else:
|
|
240
|
+
shuffled = all_indices.copy()
|
|
241
|
+
rng.shuffle(shuffled)
|
|
242
|
+
fold_val_indices = [part.tolist() for part in np.array_split(shuffled, k_folds)]
|
|
243
|
+
|
|
244
|
+
splits: list[tuple[list[int], list[int]]] = []
|
|
245
|
+
for fold_idx in range(k_folds):
|
|
246
|
+
val_indices = np.array(sorted(set(fold_val_indices[fold_idx])), dtype=np.int64)
|
|
247
|
+
train_indices = np.setdiff1d(all_indices, val_indices, assume_unique=False)
|
|
248
|
+
splits.append((train_indices.tolist(), val_indices.tolist()))
|
|
249
|
+
|
|
250
|
+
return splits
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def subsample_dataset(
|
|
254
|
+
dataset: Dataset[Any], sample_limit: int, seed: int
|
|
195
255
|
) -> Dataset[Any]:
|
|
196
|
-
"""
|
|
197
|
-
if
|
|
256
|
+
"""Return a deterministic random subset when sample limit is active."""
|
|
257
|
+
if sample_limit <= 0:
|
|
198
258
|
return dataset
|
|
199
259
|
n = len(cast("Sized", dataset))
|
|
200
|
-
if
|
|
260
|
+
if sample_limit >= n:
|
|
201
261
|
return dataset
|
|
202
262
|
rng = np.random.default_rng(seed)
|
|
203
|
-
indices = rng.choice(n, size=
|
|
263
|
+
indices = rng.choice(n, size=sample_limit, replace=False)
|
|
204
264
|
return Subset(dataset, indices.tolist())
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
pragma circom 2.0.0;
|
|
2
|
+
// Federated Learning Aggregation Circuit
|
|
3
|
+
|
|
4
|
+
template FedJSCMAggregation(n_clients, param_size) {
|
|
5
|
+
signal input client_deltas[n_clients][param_size];
|
|
6
|
+
signal input old_momentum[param_size];
|
|
7
|
+
signal input client_weights[n_clients];
|
|
8
|
+
signal input active_clients[n_clients];
|
|
9
|
+
signal input momentum_coeff;
|
|
10
|
+
signal input old_params[param_size];
|
|
11
|
+
|
|
12
|
+
signal output new_params[param_size];
|
|
13
|
+
signal output new_momentum[param_size];
|
|
14
|
+
|
|
15
|
+
// Intermediate signals for weighted sum
|
|
16
|
+
signal weighted_sum[param_size];
|
|
17
|
+
signal partial_sums[n_clients][param_size];
|
|
18
|
+
|
|
19
|
+
var SCALE = 1000000;
|
|
20
|
+
|
|
21
|
+
// Enforce active_clients as binary and zero-contribution for inactive slots.
|
|
22
|
+
for (var j = 0; j < n_clients; j++) {
|
|
23
|
+
active_clients[j] * (active_clients[j] - 1) === 0;
|
|
24
|
+
client_weights[j] * (1 - active_clients[j]) === 0;
|
|
25
|
+
for (var i = 0; i < param_size; i++) {
|
|
26
|
+
client_deltas[j][i] * (1 - active_clients[j]) === 0;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Calculate weighted deltas
|
|
31
|
+
for (var i = 0; i < param_size; i++) {
|
|
32
|
+
for (var j = 0; j < n_clients; j++) {
|
|
33
|
+
partial_sums[j][i] <== client_weights[j] * client_deltas[j][i] / SCALE;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Sum the weighted deltas
|
|
38
|
+
for (var i = 0; i < param_size; i++) {
|
|
39
|
+
var sum = 0;
|
|
40
|
+
for (var j = 0; j < n_clients; j++) {
|
|
41
|
+
sum += partial_sums[j][i];
|
|
42
|
+
}
|
|
43
|
+
weighted_sum[i] <== sum;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Compute momentum and new params
|
|
47
|
+
signal momentum_term[param_size];
|
|
48
|
+
for (var i = 0; i < param_size; i++) {
|
|
49
|
+
momentum_term[i] <== momentum_coeff * old_momentum[i] / SCALE;
|
|
50
|
+
new_momentum[i] <== momentum_term[i] + weighted_sum[i];
|
|
51
|
+
new_params[i] <== old_params[i] + new_momentum[i];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
component main = FedJSCMAggregation(8, 50);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
2026.03.16:1
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/federation/aggregation.py
RENAMED
|
File without changes
|
|
File without changes
|
{secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/federation/client_runtime.py
RENAMED
|
File without changes
|
{secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/federation/client_utils.py
RENAMED
|
File without changes
|
|
File without changes
|
{secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/federation/server_runtime.py
RENAMED
|
File without changes
|
{secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/federation/stability_monitor.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/proofs/server/pot12_final.ptau
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/zkp/client_proof_manager.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{secure_fl-2026.3.16.dev1 → secure_fl-2026.3.17.dev2}/src/secure_fl/zkp/server_proof_manager.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|