API reference
Auto-generated from docstrings. Import everything from the top-level
fed_playground package.
Driver
fed_playground.src.environment.Environment
Federated learning simulation environment.
Manages data loading, party creation, and round execution. After
:meth:run_simulation (or manually calling :meth:setup then
:meth:run_round) the training history is available in
self.history.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_parties
|
int
|
Number of federated parties (clients). |
required |
encryption_scheme
|
EncryptionScheme | None
|
Encryption scheme applied to model parameters
before transmission. Defaults to :class: |
None
|
aggregation_strategy
|
AggregationStrategy | None
|
Strategy for combining party updates. Defaults
to :class: |
None
|
n_features
|
int
|
Number of input features. Required when data_loader is
|
0
|
n_samples
|
int
|
Number of samples to generate. Required when data_loader
is |
0
|
model_class
|
type[Model]
|
:class: |
LinearRegressionModel
|
model_params
|
dict[str, Any] | None
|
Keyword arguments forwarded to model_class
(besides |
None
|
data_loader
|
DataLoader | None
|
Optional :class: |
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If n_parties < 1. |
ValueError
|
If data_loader is |
setup()
Initialise data, parties, and the orchestrator.
Must be called before :meth:run_round. :meth:run_simulation
calls this automatically.
Raises:
| Type | Description |
|---|---|
ValueError
|
If synthetic data is requested but n_features or n_samples is 0. |
run_round()
Execute one complete federated learning round.
Round steps:
- Orchestrator broadcasts the current global model to all parties.
- Each party trains locally and its loss is recorded.
- Orchestrator aggregates party updates into a new global model.
- The global model is evaluated on the held-out test set.
The recorded metrics are appended to self.history.
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If :meth: |
run_simulation(rounds=10, test_data=None)
Run a complete federated learning simulation.
Calls :meth:setup then executes rounds federated rounds, logging
progress after each one.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rounds
|
int
|
Number of federated rounds to execute. |
10
|
test_data
|
tuple[ndarray, ndarray] | None
|
Optional |
None
|
Returns:
| Type | Description |
|---|---|
dict[str, list]
|
History dictionary with keys |
dict[str, list]
|
|
fed_playground.src.orchestrator.Orchestrator
Central coordinator for a federated learning experiment.
The orchestrator owns the global model state and drives two key operations per round:
- :meth:
distribute_model— broadcast the current global parameters to all registered parties. - :meth:
aggregate_models— collect encrypted updates from all parties and combine them into a new global model.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
aggregation_strategy
|
AggregationStrategy
|
Strategy used to combine party updates. |
required |
encryption_scheme
|
EncryptionScheme
|
Scheme used during aggregation; must match the scheme used by all registered parties. |
required |
initial_model_params
|
ndarray | None
|
Starting global model parameters. When
|
None
|
register_party(party)
Add a party to the list of participants.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
party
|
Party
|
The :class: |
required |
distribute_model()
Send the current global model to all registered parties.
If no global model has been set yet (global_model_params is None),
this is a no-op — parties retain whatever parameters they were
initialised with.
aggregate_models()
Collect party updates and compute the new global model.
Each party's :meth:~fed_playground.src.party.Party.get_encrypted_model
is called, the results are passed to the aggregation strategy, and
global_model_params is updated with the result.
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If no parties are registered. |
fed_playground.src.party.Party
A single participant in the federated learning protocol.
Each party holds private data that is never shared directly. In every round it:
- Receives the global model from the :class:
~fed_playground.src.orchestrator.Orchestrator. - Fine-tunes that model on its local data.
- Returns (optionally encrypted) parameter updates.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
party_id
|
int
|
Unique integer identifier for this party. |
required |
model
|
Model
|
Initialised :class: |
required |
data
|
tuple[ndarray, ndarray]
|
Tuple |
required |
encryption_scheme
|
EncryptionScheme
|
Scheme used to encrypt outgoing parameters. |
required |
train_local_model()
Train the local model on this party's private data.
get_encrypted_model()
Return the current model parameters, encrypted by the active scheme.
Returns:
| Type | Description |
|---|---|
Any
|
Encrypted (or plaintext for :class: |
Any
|
parameter vector. |
update_model(global_model_params)
Update the local model with new global parameters.
If the incoming parameters are encrypted (e.g. the orchestrator forwarded an encrypted global model), they are decrypted first using this party's encryption scheme.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
global_model_params
|
Any
|
Parameters as received from the orchestrator — may be a plaintext numpy array or an encrypted object depending on the protocol. |
required |
Raises:
| Type | Description |
|---|---|
TypeError
|
If decryption succeeds but the result cannot be passed
to :meth: |
evaluate(X=None, y=None)
Evaluate the local model and return MSE.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray | None
|
Feature matrix for evaluation. When |
None
|
y
|
ndarray | None
|
Target vector for evaluation. When |
None
|
Returns:
| Type | Description |
|---|---|
float
|
Mean Squared Error on the provided (or local training) data. |
Models
fed_playground.src.models
Machine learning model implementations for federated learning.
All models implement the :class:Model ABC, which defines the five-method
interface expected by the rest of the framework: :meth:~Model.train,
:meth:~Model.get_parameters, :meth:~Model.set_parameters,
:meth:~Model.predict, and :meth:~Model.evaluate.
Model
Bases: ABC
Abstract base class for machine learning models.
Subclasses must implement all five abstract methods so that they can be
used interchangeably by :class:~fed_playground.src.party.Party and
:class:~fed_playground.src.environment.Environment.
train(X, y)
abstractmethod
Fit the model to the provided data.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
Feature matrix of shape |
required |
y
|
ndarray
|
Target vector of shape |
required |
get_parameters()
abstractmethod
Return the current model parameters as a flat numpy array.
Returns:
| Type | Description |
|---|---|
ndarray
|
1-D numpy array containing all trainable parameters. |
set_parameters(params)
abstractmethod
Replace the current model parameters.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
params
|
ndarray
|
Flat numpy array of the same length as returned by
:meth: |
required |
predict(X)
abstractmethod
Compute predictions for the given feature matrix.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
Feature matrix of shape |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Predicted values of shape |
evaluate(X, y)
Compute Mean Squared Error on the provided data.
This is the default metric for regression models. Classification models override it to return accuracy instead.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
Feature matrix of shape |
required |
y
|
ndarray
|
True target values of shape |
required |
Returns:
| Type | Description |
|---|---|
float
|
Mean Squared Error on the provided data. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If X contains zero samples. |
LinearRegressionModel
Bases: Model
Linear regression via mini-batch gradient descent.
Parameters are stored as a concatenation of [weights, bias] so that
they can be serialised and exchanged over the federated network as a single
flat array.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
input_dim
|
int
|
Number of input features. |
required |
learning_rate
|
float
|
Step size for gradient descent updates (default |
0.01
|
epochs
|
int
|
Number of full passes over the training data per :meth: |
1
|
train(X, y)
Train for self.epochs passes using MSE gradient descent.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
Feature matrix of shape |
required |
y
|
ndarray
|
Target vector of shape |
required |
get_parameters()
Return [weights..., bias] as a flat array.
Returns:
| Type | Description |
|---|---|
ndarray
|
1-D numpy array of length |
set_parameters(params)
Load weights and bias from a flat parameter array.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
params
|
ndarray
|
1-D array of length |
required |
predict(X)
Compute linear predictions.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
Feature matrix of shape |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Predicted values |
RidgeRegressionModel
Bases: Model
L2-regularised linear regression via the closed-form ridge solution.
Solves w = (XᵀX + αI)⁻¹Xᵀy exactly. The bias term is excluded from
regularisation, following standard practice.
Parameters are stored as [weights..., bias].
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
input_dim
|
int
|
Number of input features. |
required |
alpha
|
float
|
L2 regularisation strength (default |
1.0
|
train(X, y)
Fit the ridge model using the closed-form solution.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
Feature matrix of shape |
required |
y
|
ndarray
|
Target vector of shape |
required |
get_parameters()
Return [weights..., bias] as a flat array.
set_parameters(params)
Load parameters from a flat array.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
params
|
ndarray
|
1-D array of length |
required |
predict(X)
Compute linear predictions.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
Feature matrix of shape |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Predicted values of shape |
LogisticRegressionModel
Bases: Model
Binary logistic regression via gradient descent.
Uses the sigmoid activation and binary cross-entropy loss. Targets
y must be in {0, 1}. The :meth:evaluate method returns
accuracy (fraction of correct predictions) rather than MSE so
that downstream comparison with regression models is intentionally
visible.
Parameters are stored as [weights..., bias].
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
input_dim
|
int
|
Number of input features. |
required |
learning_rate
|
float
|
Gradient descent step size (default |
0.1
|
epochs
|
int
|
Passes over training data per :meth: |
10
|
threshold
|
float
|
Decision boundary for binary prediction (default |
0.5
|
train(X, y)
Train for self.epochs passes using sigmoid cross-entropy gradients.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
Feature matrix of shape |
required |
y
|
ndarray
|
Binary target vector of shape |
required |
get_parameters()
Return [weights..., bias] as a flat array.
set_parameters(params)
Load weights and bias from a flat parameter array.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
params
|
ndarray
|
1-D array of length |
required |
predict(X)
Return hard binary predictions (0 or 1) based on self.threshold.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
Feature matrix of shape |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Binary predictions of shape |
predict_proba(X)
Return class probabilities P(y=1 | x).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
Feature matrix of shape |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Probabilities of shape |
evaluate(X, y)
Compute classification accuracy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
Feature matrix of shape |
required |
y
|
ndarray
|
True binary labels of shape |
required |
Returns:
| Type | Description |
|---|---|
float
|
Accuracy in |
Raises:
| Type | Description |
|---|---|
ValueError
|
If X contains zero samples. |
MLPRegressorModel
Bases: Model
Single-hidden-layer MLP regressor implemented in pure NumPy.
Architecture: input_dim → hidden_dim → 1 with ReLU activation on
the hidden layer and a linear output neuron. Trained via full-batch
gradient descent and MSE loss.
Parameters are flattened as
[W1 (input_dim × hidden_dim), b1 (hidden_dim,), W2 (hidden_dim,), b2 (1,)].
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
input_dim
|
int
|
Number of input features. |
required |
hidden_dim
|
int
|
Width of the hidden layer (default |
16
|
learning_rate
|
float
|
Gradient descent step size (default |
0.01
|
epochs
|
int
|
Passes over training data per :meth: |
5
|
train(X, y)
Train for self.epochs passes using MSE backpropagation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
Feature matrix of shape |
required |
y
|
ndarray
|
Target vector of shape |
required |
get_parameters()
Return all parameters as a single flat array.
Returns:
| Type | Description |
|---|---|
ndarray
|
1-D numpy array of length |
ndarray
|
|
set_parameters(params)
Load all parameters from a flat array.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
params
|
ndarray
|
1-D array produced by :meth: |
required |
predict(X)
Compute regression predictions.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
Feature matrix of shape |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Predicted values of shape |
MLPClassifierModel
Bases: Model
Single-hidden-layer MLP classifier with softmax output (pure NumPy).
Architecture: input_dim → hidden_dim → n_classes with ReLU hidden
activation and softmax output. Trained via mini-batch SGD with
cross-entropy loss.
:meth:evaluate returns accuracy (fraction correct) rather than
MSE, making it suitable for classification tasks such as MNIST.
Parameters are flattened as
[W1 (input_dim × hidden_dim), b1 (hidden_dim,),
W2 (hidden_dim × n_classes), b2 (n_classes,)].
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
input_dim
|
int
|
Number of input features. |
required |
hidden_dim
|
int
|
Width of the hidden layer (default |
64
|
n_classes
|
int
|
Number of output classes (default |
10
|
learning_rate
|
float
|
SGD step size (default |
0.01
|
epochs
|
int
|
Passes over the local data per :meth: |
3
|
batch_size
|
int
|
Mini-batch size (default |
64
|
train(X, y)
Train with mini-batch SGD and cross-entropy loss.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
Feature matrix of shape |
required |
y
|
ndarray
|
Integer class labels of shape |
required |
get_parameters()
Return all parameters as a single flat array.
set_parameters(params)
Load all parameters from a flat array.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
params
|
ndarray
|
1-D array produced by :meth: |
required |
predict(X)
Return the predicted class index for each sample.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
Feature matrix of shape |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Integer class predictions of shape |
evaluate(X, y)
Compute classification accuracy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
Feature matrix of shape |
required |
y
|
ndarray
|
True integer class labels of shape |
required |
Returns:
| Type | Description |
|---|---|
float
|
Accuracy in |
Raises:
| Type | Description |
|---|---|
ValueError
|
If X contains zero samples. |
ClosedFormLinearRegressionModel
Bases: Model
Linear regression via the normal equation (closed-form solution).
Solves w = argmin ||Xw - y||² using :func:numpy.linalg.lstsq, which
is numerically stable even for rank-deficient design matrices.
Parameters are stored as a single flat array [weights..., bias] where
the bias corresponds to an implicit column of ones appended to X.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
input_dim
|
int
|
Number of input features. |
required |
train(X, y)
Solve the normal equations via least-squares.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
Feature matrix of shape |
required |
y
|
ndarray
|
Target vector of shape |
required |
get_parameters()
Return [weights..., bias] as a flat array.
Returns:
| Type | Description |
|---|---|
ndarray
|
1-D numpy array of length |
set_parameters(params)
Load parameters from a flat array.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
params
|
ndarray
|
1-D array of length |
required |
predict(X)
Compute linear predictions using the bias-augmented design matrix.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
Feature matrix of shape |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Predicted values of shape |
SVMModel
Bases: Model
Linear support vector machine trained with the Pegasos algorithm.
Reference: Shalev-Shwartz, Singer, Srebro & Cotter, "Pegasos: Primal Estimated sub-GrAdient SOlver for SVM", Mathematical Programming 2011.
Minimises the L2-regularised hinge loss
(λ/2)‖w‖² + (1/n) Σ max(0, 1 - yᵢ(w·xᵢ + b)) by stochastic sub-gradient
descent with the Pegasos step size η_t = 1/(λt). Binary labels are
supplied in {0, 1} and mapped internally to {-1, +1}; the bias term
is left unregularised. :meth:evaluate returns accuracy.
Parameters are stored as [weights..., bias].
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
input_dim
|
int
|
Number of input features. |
required |
lambda_reg
|
float
|
Regularisation strength |
0.01
|
epochs
|
int
|
Passes over the local data per :meth: |
20
|
seed
|
int
|
RNG seed for the sample order (default |
0
|
train(X, y)
Run Pegasos sub-gradient updates over the local data.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
Feature matrix of shape |
required |
y
|
ndarray
|
Binary labels of shape |
required |
get_parameters()
Return [weights..., bias] as a flat array.
set_parameters(params)
Load weights and bias from a flat parameter array.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
params
|
ndarray
|
1-D array of length |
required |
decision_function(X)
Return the signed distance w·x + b to the separating hyperplane.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
Feature matrix of shape |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Real-valued scores of shape |
predict(X)
Return hard binary predictions in {0, 1}.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
Feature matrix of shape |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Binary predictions of shape |
evaluate(X, y)
Compute classification accuracy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
Feature matrix of shape |
required |
y
|
ndarray
|
True binary labels of shape |
required |
Returns:
| Type | Description |
|---|---|
float
|
Accuracy in |
Raises:
| Type | Description |
|---|---|
ValueError
|
If X contains zero samples. |
LassoRegressionModel
Bases: Model
L1-regularised linear regression (LASSO) via cyclic coordinate descent.
References: Tibshirani, "Regression Shrinkage and Selection via the Lasso", JRSS-B 1996; Friedman, Hastie & Tibshirani, "Regularization Paths for Generalized Linear Models via Coordinate Descent", J. Stat. Soft. 2010.
Minimises (1/2n)‖y - Xw - b‖² + α‖w‖₁ with cyclic coordinate descent and
soft-thresholding S(ρ, α) = sign(ρ)·max(|ρ| - α, 0). The L1 penalty
drives weights of irrelevant features to exactly zero, yielding a sparse,
feature-selecting model. The intercept is not penalised.
Parameters are stored as [weights..., bias].
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
input_dim
|
int
|
Number of input features. |
required |
alpha
|
float
|
L1 regularisation strength (default |
0.1
|
max_iter
|
int
|
Maximum coordinate-descent sweeps per :meth: |
1000
|
tol
|
float
|
Convergence tolerance on the max coefficient change (default |
1e-06
|
train(X, y)
Fit by cyclic coordinate descent with soft-thresholding.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
Feature matrix of shape |
required |
y
|
ndarray
|
Target vector of shape |
required |
get_parameters()
Return [weights..., bias] as a flat array.
set_parameters(params)
Load weights and bias from a flat parameter array.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
params
|
ndarray
|
1-D array of length |
required |
predict(X)
Compute linear predictions X·weights + bias.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
Feature matrix of shape |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Predicted values of shape |
ElasticNetRegressionModel
Bases: Model
Elastic-net regression: blended L1/L2 penalty via coordinate descent.
Reference: Zou & Hastie, "Regularization and variable selection via the elastic net", JRSS-B 2005.
Minimises (1/2n)‖y - Xw - b‖² + α·ρ‖w‖₁ + (α(1-ρ)/2)‖w‖² where ρ is
the L1 ratio. The L1 part yields sparsity; the L2 part stabilises selection
among correlated features (the "grouping effect") that pure LASSO handles
poorly. Coordinate update with soft-thresholding::
w_j = S((1/n) xⱼ·r_j, α·ρ) / ((1/n)‖xⱼ‖² + α(1-ρ))
Recovers LASSO at l1_ratio=1 and ridge-like shrinkage at l1_ratio=0.
The intercept is unpenalised. Parameters are stored as [weights..., bias].
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
input_dim
|
int
|
Number of input features. |
required |
alpha
|
float
|
Overall regularisation strength (default |
0.1
|
l1_ratio
|
float
|
Mix between L1 ( |
0.5
|
max_iter
|
int
|
Maximum coordinate-descent sweeps (default |
1000
|
tol
|
float
|
Convergence tolerance on the max coefficient change (default |
1e-06
|
train(X, y)
Fit by cyclic coordinate descent with an L1/L2-blended penalty.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
Feature matrix of shape |
required |
y
|
ndarray
|
Target vector of shape |
required |
get_parameters()
Return [weights..., bias] as a flat array.
set_parameters(params)
Load weights and bias from a flat parameter array.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
params
|
ndarray
|
1-D array of length |
required |
predict(X)
Compute linear predictions X·weights + bias.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
Feature matrix of shape |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Predicted values of shape |
PoissonRegressionModel
Bases: Model
Poisson regression (log-linear GLM) for non-negative count targets.
Reference: Nelder & Wedderburn, "Generalized Linear Models", JRSS-A 1972.
Models E[y | x] = exp(w·x + b) (the canonical log link for the Poisson
family) and fits by gradient descent on the negative log-likelihood, whose
gradient is the elegant (1/n) Xᵀ(μ - y) with μ = exp(w·x + b).
Suitable for rates/counts (events per unit), where ordinary least squares
would wrongly allow negative predictions.
:meth:predict returns the expected count μ ≥ 0; :meth:evaluate
returns the mean Poisson deviance (the GLM-appropriate goodness-of-fit,
lower is better), not MSE.
Parameters are stored as [weights..., bias].
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
input_dim
|
int
|
Number of input features. |
required |
learning_rate
|
float
|
Gradient-descent step size (default |
0.01
|
epochs
|
int
|
Passes over the data per :meth: |
50
|
max_exponent
|
float
|
Clip on the linear predictor to avoid |
30.0
|
train(X, y)
Fit by gradient descent on the Poisson negative log-likelihood.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
Feature matrix of shape |
required |
y
|
ndarray
|
Non-negative count targets of shape |
required |
get_parameters()
Return [weights..., bias] as a flat array.
set_parameters(params)
Load weights and bias from a flat parameter array.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
params
|
ndarray
|
1-D array of length |
required |
predict(X)
Return the expected count μ = exp(w·x + b) (non-negative).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
Feature matrix of shape |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Predicted rates of shape |
evaluate(X, y)
Compute the mean Poisson deviance (lower is better).
D = (2/n) Σ [ y·log(y/μ) - (y - μ) ] with the convention
y·log(y/μ) = 0 when y = 0.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
Feature matrix of shape |
required |
y
|
ndarray
|
True counts of shape |
required |
Returns:
| Type | Description |
|---|---|
float
|
Mean Poisson deviance. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If X contains zero samples. |
HuberRegressionModel
Bases: Model
Robust linear regression with the Huber loss.
Reference: Huber, "Robust Estimation of a Location Parameter", Annals of Mathematical Statistics 1964.
Minimises Σ L_δ(yᵢ - (w·xᵢ + b)) where the Huber loss is quadratic for
small residuals and linear beyond a threshold δ::
L_δ(r) = ½r² if |r| ≤ δ
= δ(|r| - ½δ) otherwise
The linear tail caps the influence of large residuals, so a few grossly
corrupted target values cannot dominate the fit the way they do under
ordinary least squares. This is a robustness axis orthogonal to L1/L2
weight penalties (which regularise coefficients, not residuals). Fitted
by gradient descent; the gradient w.r.t. each prediction is the clipped
residual clip(r, -δ, δ).
Parameters are stored as [weights..., bias].
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
input_dim
|
int
|
Number of input features. |
required |
delta
|
float
|
Huber threshold separating the quadratic and linear regimes
(default |
1.0
|
learning_rate
|
float
|
Gradient-descent step size (default |
0.01
|
epochs
|
int
|
Passes over the data per :meth: |
100
|
train(X, y)
Fit by gradient descent on the Huber loss.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
Feature matrix of shape |
required |
y
|
ndarray
|
Target vector of shape |
required |
get_parameters()
Return [weights..., bias] as a flat array.
set_parameters(params)
Load weights and bias from a flat parameter array.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
params
|
ndarray
|
1-D array of length |
required |
predict(X)
Compute linear predictions X·weights + bias.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
Feature matrix of shape |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Predicted values of shape |
Aggregation strategies
fed_playground.src.aggregation
Aggregation strategies for the federated learning orchestrator.
Each strategy receives the list of (possibly encrypted) model updates collected
from all parties and returns a single aggregated result. The result is stored
as the new global model in the :class:~fed_playground.src.orchestrator.Orchestrator.
AggregationStrategy
Bases: ABC
Abstract base class for model aggregation strategies.
aggregate(encrypted_models, encryption_scheme)
abstractmethod
Aggregate a list of party model updates into a single result.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
encrypted_models
|
list[Any]
|
Non-empty list of (possibly encrypted) parameter vectors, one per registered party. |
required |
encryption_scheme
|
EncryptionScheme
|
The scheme in use; may be needed to perform homomorphic operations on ciphertexts. |
required |
Returns:
| Type | Description |
|---|---|
Any
|
Aggregated model parameters in the same representation as the inputs |
Any
|
(plaintext numpy array for :class: |
Any
|
ciphertext otherwise). |
Raises:
| Type | Description |
|---|---|
ValueError
|
If encrypted_models is empty. |
TrimmedMeanAggregation
Bases: AggregationStrategy
Byzantine-robust aggregation via coordinate-wise trimmed mean.
For each parameter dimension, removes the trim_fraction lowest and
highest values before computing the mean. This provides robustness
against a bounded fraction of malicious or corrupted party updates.
Requires plaintext (numpy) parameters; FHE ciphertexts are not supported.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
trim_fraction
|
float
|
Fraction of parties to trim from each tail (default |
0.1
|
aggregate(encrypted_models, encryption_scheme)
Compute the coordinate-wise trimmed mean of party updates.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
encrypted_models
|
list[Any]
|
Non-empty list of numpy parameter vectors. |
required |
encryption_scheme
|
EncryptionScheme
|
Active encryption scheme (used for summation to obtain plaintext arrays before trimming). |
required |
Returns:
| Type | Description |
|---|---|
Any
|
Trimmed-mean parameter vector as a numpy array. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If encrypted_models is empty or parameters are not numpy arrays. |
MedianAggregation
Bases: AggregationStrategy
Byzantine-robust aggregation via coordinate-wise median.
For each parameter dimension the median value across all party updates
is selected. This is tolerant of up to (n_parties - 1) / 2
arbitrarily corrupted parties.
Requires plaintext (numpy) parameters; FHE ciphertexts are not supported.
aggregate(encrypted_models, encryption_scheme)
Compute the coordinate-wise median of party updates.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
encrypted_models
|
list[Any]
|
Non-empty list of numpy parameter vectors. |
required |
encryption_scheme
|
EncryptionScheme
|
Active encryption scheme. |
required |
Returns:
| Type | Description |
|---|---|
Any
|
Median parameter vector as a numpy array. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If encrypted_models is empty. |
MeanAggregation
Bases: AggregationStrategy
Federated averaging (FedAvg) — compute the mean of all party updates.
When :class:~fed_playground.src.encryption.NoEncryption is used the
result is the true arithmetic mean.
For schemes that support only homomorphic addition (not scalar division), the aggregated result is the sum and the caller is responsible for dividing by the number of parties after decryption. This is documented behaviour and a deliberate trade-off for FHE compatibility.
aggregate(encrypted_models, encryption_scheme)
Compute the mean (or sum for opaque ciphertexts) of party updates.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
encrypted_models
|
list[Any]
|
Non-empty list of party parameter vectors. |
required |
encryption_scheme
|
EncryptionScheme
|
Active encryption scheme. |
required |
Returns:
| Type | Description |
|---|---|
Any
|
Arithmetic mean if parameters are numpy arrays; element-wise sum |
Any
|
otherwise (for FHE ciphertexts that do not support scalar division). |
Raises:
| Type | Description |
|---|---|
ValueError
|
If encrypted_models is empty. |
KrumAggregation
Bases: AggregationStrategy
Byzantine-robust aggregation via the Krum / Multi-Krum rule.
Reference: Blanchard, El Mhamdi, Guerraoui & Stainer, "Machine Learning with Adversaries: Byzantine Tolerant Gradient Descent", NeurIPS 2017.
For n party updates with at most n_byzantine (f) adversaries, Krum
scores each update by the sum of squared Euclidean distances to its
n - f - 2 nearest neighbours, then selects the single update with the
smallest score — the one most "supported" by a majority cluster, which by
construction cannot be a far-flung Byzantine outlier. Multi-Krum
(n_selected > 1) averages the n_selected lowest-scoring updates,
trading a little robustness for lower variance.
Krum's guarantee holds when 2f + 2 < n. With too few parties for that
bound this falls back to using all other updates as neighbours (still a
sensible "most central update" selector).
Requires plaintext (numpy) updates; incompatible with additive-masking
schemes (is_linear_only).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_byzantine
|
int
|
Assumed number of Byzantine parties |
1
|
n_selected
|
int
|
Updates to average (1 = classic Krum; >1 = Multi-Krum). |
1
|
aggregate(encrypted_models, encryption_scheme)
Select (Multi-)Krum update(s) and return their mean.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
encrypted_models
|
list[Any]
|
Non-empty list of numpy parameter vectors. |
required |
encryption_scheme
|
EncryptionScheme
|
Active encryption scheme. |
required |
Returns:
| Type | Description |
|---|---|
Any
|
The chosen Krum update, or the mean of the top |
Raises:
| Type | Description |
|---|---|
ValueError
|
If encrypted_models is empty or the scheme masks updates. |
GeometricMedianAggregation
Bases: AggregationStrategy
Robust aggregation via the geometric (spatial) median — the RFA rule.
Reference: Pillutla, Kakade & Harchaoui, "Robust Aggregation for Federated Learning", IEEE Transactions on Signal Processing, 2022.
Returns the point v minimising the sum of Euclidean distances to the
party updates, argmin_v Σ_i ‖v − x_i‖. Unlike the coordinate-wise
median this is rotation-equivariant, and it has an asymptotic breakdown
point of 0.5 (robust until half the mass is adversarial). Computed with
the smoothed Weiszfeld iteration
``v ← (Σ_i x_i / max(ε, ‖v − x_i‖)) / (Σ_i 1 / max(ε, ‖v − x_i‖))``
initialised at the mean. The ε floor keeps the update well-defined when
an iterate coincides with a data point.
Requires plaintext (numpy) updates; incompatible with additive-masking
schemes (is_linear_only).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
max_iter
|
int
|
Maximum Weiszfeld iterations (default |
100
|
eps
|
float
|
Smoothing floor on distances / convergence tolerance (default |
1e-08
|
aggregate(encrypted_models, encryption_scheme)
Compute the geometric median of the party updates.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
encrypted_models
|
list[Any]
|
Non-empty list of numpy parameter vectors. |
required |
encryption_scheme
|
EncryptionScheme
|
Active encryption scheme. |
required |
Returns:
| Type | Description |
|---|---|
Any
|
Geometric-median parameter vector as a numpy array. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If encrypted_models is empty or the scheme masks updates. |
BulyanAggregation
Bases: AggregationStrategy
Strong Byzantine-robust aggregation via Bulyan (Krum + trimmed mean).
Reference: El Mhamdi, Guerraoui & Rouault, "The Hidden Vulnerability of Distributed Learning in Byzantium", ICML 2018.
Bulyan closes a loophole in Krum: a single Krum-selected update can still be nudged far along one coordinate by a clever attacker. It runs in two stages:
- Selection — recursively apply Krum to pick
m = n - 2fcandidate updates (select the Krum-best, remove it, repeat), discarding the most outlying updates. - Coordinate-wise trimmed mean — over the selected set, for each
coordinate keep the
β = m - 2fvalues closest to that coordinate's median and average them, bounding any single dimension's corruption.
The guarantee requires n ≥ 4f + 3. With fewer parties this clamps the
selection/averaging counts to stay well-defined (degrading gracefully toward
a coordinate-wise median).
Requires plaintext (numpy) updates; incompatible with additive-masking
schemes (is_linear_only).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_byzantine
|
int
|
Assumed number of Byzantine parties |
1
|
aggregate(encrypted_models, encryption_scheme)
Run Bulyan selection + coordinate-wise trimmed mean.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
encrypted_models
|
list[Any]
|
Non-empty list of numpy parameter vectors. |
required |
encryption_scheme
|
EncryptionScheme
|
Active encryption scheme. |
required |
Returns:
| Type | Description |
|---|---|
Any
|
The Bulyan-aggregated parameter vector. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If encrypted_models is empty or the scheme masks updates. |
MedianOfMeansAggregation
Bases: AggregationStrategy
Robust aggregation via the median-of-means estimator.
References: Nemirovski & Yudin 1983; Lugosi & Mendelson, "Mean estimation and regression under heavy-tailed distributions: a survey", 2019.
Partitions the party updates into n_buckets groups, averages each group,
then takes the coordinate-wise median of the bucket means. Averaging
within buckets reduces variance (sub-Gaussian concentration even for
heavy-tailed updates); the outer median discards buckets contaminated by
Byzantine parties. Robust as long as a majority of buckets are clean — i.e.
n_buckets > 2f for f adversaries.
Requires plaintext (numpy) updates; incompatible with additive-masking
schemes (is_linear_only).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_buckets
|
int
|
Number of buckets |
5
|
seed
|
int
|
RNG seed for the random partition (default |
0
|
aggregate(encrypted_models, encryption_scheme)
Average within random buckets, then take the coordinate-wise median.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
encrypted_models
|
list[Any]
|
Non-empty list of numpy parameter vectors. |
required |
encryption_scheme
|
EncryptionScheme
|
Active encryption scheme. |
required |
Returns:
| Type | Description |
|---|---|
Any
|
The median-of-means parameter vector. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If encrypted_models is empty or the scheme masks updates. |
CenteredClippingAggregation
Bases: AggregationStrategy
Byzantine-robust aggregation via iterative centered clipping.
Reference: Karimireddy, He & Jaggi, "Learning from History for Byzantine Robust Optimization", ICML 2021.
Starting from a robust center v (the coordinate-wise median here, a
parameter-free stand-in for the momentum/previous-round estimate the paper
warm-starts from), repeatedly refine
``v ← v + (1/n) Σ_i (x_i - v)·min(1, τ / ‖x_i - v‖)``
Each update's pull on the center is clipped to norm τ, so a Byzantine
update arbitrarily far from v contributes at most τ — bounding its
influence without discarding any honest update. Distinct from selection
(Krum/Bulyan) and order statistics (median/MoM): it is a smooth, clipping
based estimator.
Choose clip_radius on the order of the honest update norms: too small
over-clips and slows convergence, too large lets outliers through.
Requires plaintext (numpy) updates; incompatible with additive-masking
schemes (is_linear_only).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
clip_radius
|
float
|
Clipping threshold |
1.0
|
n_iters
|
int
|
Number of refinement iterations |
3
|
aggregate(encrypted_models, encryption_scheme)
Refine a robust center by iterative centered clipping.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
encrypted_models
|
list[Any]
|
Non-empty list of numpy parameter vectors. |
required |
encryption_scheme
|
EncryptionScheme
|
Active encryption scheme. |
required |
Returns:
| Type | Description |
|---|---|
Any
|
The centered-clipping aggregate as a numpy array. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If encrypted_models is empty or the scheme masks updates. |
Encryption schemes
fed_playground.src.encryption
Encryption scheme abstractions for federated learning.
Defines the :class:EncryptionScheme ABC and the :class:NoEncryption baseline
that passes model parameters through unchanged. Custom schemes (e.g. additive
secret sharing, homomorphic encryption) can be added by subclassing
:class:EncryptionScheme.
EncryptionScheme
Bases: ABC
Abstract base class for encryption schemes used in federated learning.
Every concrete scheme must implement :meth:encrypt, :meth:decrypt, and
:meth:aggregate. The aggregate operation is kept on the scheme because
fully-homomorphic schemes must operate on ciphertexts, whereas plaintext
schemes can delegate to the aggregation strategy.
encrypt(params)
abstractmethod
Encrypt model parameters before transmission.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
params
|
ndarray
|
Flat numpy array of model parameters. |
required |
Returns:
| Type | Description |
|---|---|
Any
|
Encrypted representation of params (type depends on scheme). |
decrypt(encrypted_params)
abstractmethod
Decrypt model parameters after reception.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
encrypted_params
|
Any
|
Encrypted parameters as returned by :meth: |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Flat numpy array of decrypted parameters. |
aggregate(encrypted_params_list)
abstractmethod
Aggregate a list of (possibly encrypted) parameter vectors.
For homomorphic schemes this operates on ciphertexts directly.
For plaintext schemes it is typically a sum — division by N is handled
by the :class:~fed_playground.src.aggregation.AggregationStrategy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
encrypted_params_list
|
list[Any]
|
Non-empty list of encrypted parameter vectors. |
required |
Returns:
| Type | Description |
|---|---|
Any
|
Aggregated result in the same representation as the inputs. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If encrypted_params_list is empty. |
GaussianDPEncryption
Bases: EncryptionScheme
Differential privacy via Gaussian noise injection (local DP).
Before transmission each party's parameters are perturbed with additive
Gaussian noise N(0, σ²). The noise is applied once at encrypt time;
decryption is the identity. Aggregation is element-wise summation.
Choosing σ involves a privacy-utility trade-off: larger σ gives
stronger (ε, δ)-DP guarantees but degrades model accuracy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sigma
|
float
|
Standard deviation of the Gaussian noise (default |
0.1
|
seed
|
int | None
|
Optional RNG seed for reproducibility. |
None
|
encrypt(params)
Add Gaussian noise to params.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
params
|
ndarray
|
Flat numpy array of model parameters. |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Noisy parameter array of the same shape. |
decrypt(encrypted_params)
Return encrypted_params unchanged (noise is not reversible).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
encrypted_params
|
ndarray
|
Noisy parameter array. |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
The same array unmodified. |
aggregate(encrypted_params_list)
Sum noisy parameter vectors element-wise.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
encrypted_params_list
|
list[ndarray]
|
Non-empty list of noisy parameter arrays. |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Element-wise sum. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If encrypted_params_list is empty. |
AdditiveSecretSharing
Bases: EncryptionScheme
Additive secret-sharing via masking, with masks that cancel on sum.
Each party splits its update x_i into n_shares additive shares:
n_shares - 1 random secret shares plus one public share
p_i = x_i - Σ(secret shares). Only the public share leaves the party;
the secret-share total is retained in the (shared) scheme instance, which
plays the role of a separate, non-colluding aggregation server.
Reconstruction happens only in aggregate: summing every party's public
share and adding back the retained secret totals yields Σ x_i exactly,
because each party's mask cancels against its own retained share::
Σ p_i + Σ secret_i = Σ (x_i - secret_i) + Σ secret_i = Σ x_i
Security claim — input-private against the orchestrator / aggregation
strategy, which only ever observes public shares (each x_i masked by
independent secret randomness it never sees). Reconstruction succeeds only
in aggregate, never for an individual party. The protocol is linear
only: order statistics (median, trimmed mean) cannot be computed over
masked shares — use :class:~fed_playground.src.aggregation.MeanAggregation.
Pedagogical caveat — in this single-process simulation the public-share aggregator and the secret-share holder live in the same object, so the trust separation is simulated, not enforced; the information flow (the aggregator never touches a plaintext update) is faithful.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_shares
|
int
|
Additive shares per update; |
2
|
seed
|
int | None
|
Optional RNG seed for reproducibility. |
None
|
encrypt(params)
Split params into additive shares; emit only the public share.
The secret-share total is accumulated internally so it can be cancelled
in :meth:aggregate.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
params
|
ndarray
|
Flat numpy array of model parameters. |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
The public share |
ndarray
|
masked, meaningless in isolation. |
decrypt(encrypted_params)
Return encrypted_params unchanged (reconstruction is in aggregate).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
encrypted_params
|
ndarray
|
A reconstructed/aggregated parameter array. |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
The same array unmodified. |
aggregate(encrypted_params_list)
Reconstruct Σ x_i by summing public shares and cancelling masks.
Adds the retained secret-share total back to the summed public shares, then clears the ledger for the next round.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
encrypted_params_list
|
list[ndarray]
|
Non-empty list of public shares. |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Element-wise sum of the original (unmasked) updates. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If encrypted_params_list is empty. |
NoEncryption
Bases: EncryptionScheme
Passthrough encryption scheme — no cryptographic operations applied.
Useful as a baseline and for debugging. Parameters are returned
unchanged; aggregation is element-wise summation (division by N is
applied by :class:~fed_playground.src.aggregation.MeanAggregation).
encrypt(params)
Return params unchanged.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
params
|
ndarray
|
Flat numpy array of model parameters. |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
The same array unmodified. |
decrypt(encrypted_params)
Return encrypted_params unchanged.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
encrypted_params
|
ndarray
|
Numpy array (already plaintext). |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
The same array unmodified. |
aggregate(encrypted_params_list)
Sum parameter vectors element-wise.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
encrypted_params_list
|
list[ndarray]
|
Non-empty list of numpy parameter arrays. |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Element-wise sum of all arrays. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If encrypted_params_list is empty. |
LaplaceDPEncryption
Bases: EncryptionScheme
Local differential privacy via the Laplace mechanism.
Reference: Dwork, McSherry, Nissim & Smith, "Calibrating Noise to Sensitivity in Private Data Analysis", TCC 2006.
Each party perturbs its update with additive Laplace noise of scale
b = sensitivity / ε before transmission, giving pure ε-differential
privacy (no δ term, unlike the Gaussian mechanism). The Laplace
distribution's heavier tails are the price of that stronger guarantee.
decrypt is the identity (noise is irreversible by design); aggregation
is element-wise summation.
The ε-DP guarantee holds only if each party's update has L1-sensitivity
bounded by sensitivity — i.e. updates are clipped to that L1 norm. When
clip is True this scheme enforces the bound; otherwise the caller is
responsible and the stated ε is only nominal.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
epsilon
|
float
|
Privacy budget |
1.0
|
sensitivity
|
float
|
L1-sensitivity bound of one update (default |
1.0
|
clip
|
bool
|
If |
False
|
seed
|
int | None
|
Optional RNG seed for reproducibility. |
None
|
encrypt(params)
Clip (optionally) then add Laplace(0, sensitivity/ε) noise.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
params
|
ndarray
|
Flat numpy array of model parameters. |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Noisy parameter array of the same shape. |
decrypt(encrypted_params)
Return encrypted_params unchanged (noise is not reversible).
aggregate(encrypted_params_list)
Sum noisy parameter vectors element-wise.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
encrypted_params_list
|
list[ndarray]
|
Non-empty list of noisy parameter arrays. |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Element-wise sum. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If encrypted_params_list is empty. |
PairwiseMaskingEncryption
Bases: EncryptionScheme
Secure aggregation via pairwise additive masks that cancel on sum.
Reference: Bonawitz et al., "Practical Secure Aggregation for Privacy-Preserving Machine Learning", ACM CCS 2017.
Every unordered pair of parties {a, b} shares a per-round random mask
p_ab; party a adds +p_ab and party b adds -p_ab. Each
emitted value x_i + Σ_j ±p_ij is masked into noise, yet summing all
parties' contributions cancels every pairwise term structurally, exactly
recovering Σ x_i — no party, and no reconstructor, can unmask an
individual update. This is the key advantage over a simple additive split:
cancellation needs no trusted dealer.
Security claim — input-private against the orchestrator / aggregation
strategy, which observes only masked values; the sum reveals the aggregate
and nothing else. This is the linear-only regime (is_linear_only):
order statistics cannot be computed over masks.
Simulation caveats (ponytail: named ceilings):
- Assumes exactly
n_parties:meth:encryptcalls per round, in a stable order — true for the :class:~fed_playground.src.orchestrator.Orchestrator. Party index is inferred from the intra-round call counter. - Dropout recovery (the real protocol secret-shares each seed via Shamir so a dropped party's masks can be removed) is not simulated: a missing party in a round breaks cancellation.
- The pairwise mask matrix is
O(n²)vectors per round — fine for the handful of parties in a simulation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_parties
|
int
|
Number of participating parties (must match the simulation). |
required |
mask_scale
|
float
|
Std-dev of each pairwise mask; larger ⇒ stronger statistical
hiding (default |
10.0
|
seed
|
int
|
Base RNG seed for reproducible pairwise masks (default |
0
|
encrypt(params)
Add this party's pairwise-mask total to params.
The party index and round are inferred from the call counter.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
params
|
ndarray
|
Flat numpy array of model parameters. |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Masked parameter array; meaningless in isolation. |
decrypt(encrypted_params)
Return encrypted_params unchanged (reconstruction is in aggregate).
aggregate(encrypted_params_list)
Sum masked updates; pairwise masks cancel, leaving Σ x_i.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
encrypted_params_list
|
list[ndarray]
|
Non-empty list of masked updates. |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Element-wise sum of the original (unmasked) updates. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If encrypted_params_list is empty. |
Attacks
fed_playground.src.attacks
Byzantine attack strategies for benchmarking robust aggregation.
An :class:Attack replaces the updates of the byzantine parties with
malicious ones, given the full set of (plaintext) party updates for the round.
Operating on the whole collection — not one party at a time — is what lets the
omniscient attacks (:class:IPMAttack, :class:ALittleIsEnoughAttack) craft
their payload from the honest parties' statistics, which is exactly how they
defeat distance/median-based defenses.
Attacks assume array-valued (plaintext) updates: they are meaningless over additive-masking schemes, where individual shares carry no information.
Attack
Bases: ABC
Abstract base class for Byzantine update-poisoning attacks.
corrupt(updates, byzantine_ids)
abstractmethod
Return a new update list with the byzantine_ids entries poisoned.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
updates
|
list[ndarray]
|
One plaintext parameter vector per party (honest values). |
required |
byzantine_ids
|
list[int]
|
Indices into updates controlled by the adversary. |
required |
Returns:
| Type | Description |
|---|---|
list[ndarray]
|
A new list; honest entries unchanged, byzantine entries replaced. |
NoAttack
SignFlipAttack
Bases: Attack
Byzantine parties send the negation of their own update, scaled.
A classic model-poisoning attack (Blanchard et al., NeurIPS 2017): pushing in the opposite direction drags the FedAvg mean away from the optimum.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
scale
|
float
|
Multiplier on the flipped update (default |
1.0
|
GaussianAttack
Bases: Attack
Byzantine parties send large random Gaussian noise vectors.
The simplest untargeted attack (Blanchard et al., NeurIPS 2017).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sigma
|
float
|
Std-dev of the injected noise (default |
50.0
|
seed
|
int | None
|
Optional RNG seed for reproducibility. |
None
|
IPMAttack
Bases: Attack
Inner-product manipulation: push against the honest mean.
Reference: Xie, Koyejo & Gupta, "Fall of Empires: Breaking Byzantine-tolerant SGD by Inner Product Manipulation", UAI 2020.
All byzantine parties send -epsilon · mean(honest updates), which keeps a
negative inner product with the true gradient direction while staying small
enough to evade norm/distance filters.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
epsilon
|
float
|
Attack strength (default |
0.1
|
ALittleIsEnoughAttack
Bases: Attack
Stay within the honest distribution to slip past robust defenses.
Reference: Baruch, Baruch & Goldberg, "A Little Is Enough: Circumventing Defenses For Distributed Learning", NeurIPS 2019.
Byzantine parties send mean(honest) - z · std(honest) coordinate-wise —
a small, coordinated perturbation that median/Krum cannot distinguish from an
honest update yet still biases the aggregate.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
z
|
float
|
Std-dev multiplier controlling stealth vs. damage (default |
1.0
|
Benchmark & data
fed_playground.src.benchmark
Grid benchmark runner over the swappable federated-learning components.
One row per (model x aggregation x encryption x attack x n_byzantine) combo.
Reuses :class:~fed_playground.src.environment.Environment for each run, so the
benchmark adds no new simulation logic — just the sweep and a tidy table.
run_benchmark(*, models=None, aggregations=None, encryptions=None, attacks=None, n_byzantine=(0,), n_parties=5, rounds=10, n_features=4, n_samples=500, seed=42, model_params=None, data_loader=None)
Run every component combination and return a tidy results DataFrame.
Each axis defaults to a single sensible value, so you only pass the axes you
want to sweep. Combinations that are incompatible — e.g. a masking
encryption (is_linear_only) paired with a distance/order aggregator —
are caught and recorded with final_loss = NaN and status describing
the error, rather than crashing the sweep. Those NaN cells are the
privacy x robustness frontier.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
models
|
list[type[Model]] | None
|
|
None
|
aggregations
|
list[AggregationStrategy] | None
|
aggregation strategy instances (default |
None
|
encryptions
|
list[EncryptionScheme] | None
|
encryption scheme instances (default |
None
|
attacks
|
list[Attack] | None
|
attack instances (default |
None
|
n_byzantine
|
tuple[int, ...]
|
numbers of Byzantine parties to sweep (default |
(0,)
|
n_parties
|
int
|
parties per run. |
5
|
rounds
|
int
|
federated rounds per run. |
10
|
n_features
|
int
|
synthetic feature count (when no data_loader). |
4
|
n_samples
|
int
|
synthetic sample count (when no data_loader). |
500
|
seed
|
int
|
data/sim seed, threaded into every run for reproducibility. |
42
|
model_params
|
dict[str, Any] | None
|
kwargs forwarded to every model. |
None
|
data_loader
|
DataLoader | None
|
optional shared dataset; when |
None
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
DataFrame with columns ``model, aggregation, encryption, attack, |
DataFrame
|
n_byzantine, final_loss, status``. |
leaderboard(df, *, index='aggregation', columns='attack', values='final_loss', title='')
Render a results DataFrame as a Markdown pivot table (no tabulate).
NaN cells (e.g. incompatible privacy x robustness combos) show as —.
No timestamp is embedded, so the output is byte-stable under a fixed seed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
DataFrame
|
results from :func: |
required |
index
|
str
|
pivot row axis (default |
'aggregation'
|
columns
|
str
|
pivot column axis (default |
'attack'
|
values
|
str
|
cell metric (default |
'final_loss'
|
title
|
str
|
optional heading rendered above the table. |
''
|
Returns:
| Type | Description |
|---|---|
str
|
Markdown string. |
fed_playground.src.dataloader.load_dataset(kind='synthetic', **opts)
Build a :class:DataLoader for a named dataset (the benchmark data layer).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
kind
|
str
|
one of |
'synthetic'
|
**opts
|
object
|
per-kind options —
synthetic: |
{}
|
Returns:
| Type | Description |
|---|---|
DataLoader
|
A ready-to-use |
Raises:
| Type | Description |
|---|---|
ValueError
|
on an unknown kind. |
ImportError
|
if |
fed_playground.src.utils_data.dirichlet_partition(X, y, n_parties, alpha=0.5, random_seed=42)
Non-IID label-skew partition via a Dirichlet distribution.
Reference: Hsu, Qi & Brown, "Measuring the Effects of Non-Identical Data Distribution for Federated Visual Classification", 2019.
For each class, the samples are split among parties in proportions drawn from
Dirichlet(alpha). Small alpha (e.g. 0.1) yields highly skewed
shards (each party sees few classes); large alpha approaches an IID split.
Intended for classification targets (discrete labels).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
Feature matrix of shape |
required |
y
|
ndarray
|
Integer class labels of shape |
required |
n_parties
|
int
|
Number of parties to partition across. |
required |
alpha
|
float
|
Dirichlet concentration; lower = more non-IID (default |
0.5
|
random_seed
|
int | None
|
Seed for reproducibility. |
42
|
Returns:
| Type | Description |
|---|---|
list[tuple[ndarray, ndarray]]
|
List of |
list[tuple[ndarray, ndarray]]
|
extreme skew). |
Raises:
| Type | Description |
|---|---|
ValueError
|
If n_parties < 1. |
Visualizers
fed_playground.src.visualization
Visualizer
Bases: ABC
Abstract base class for visualization components. Follows the library pattern of ABC-based interfaces.
__init__(save_dir=None, figsize=(10, 6))
Initialize visualizer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
save_dir
|
str | None
|
Directory to save plots. If None, plots are displayed. |
None
|
figsize
|
tuple[int, int]
|
Default figure size as (width, height). |
(10, 6)
|
plot(data, **kwargs)
abstractmethod
Create a plot from the provided data.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
Any
|
Input data for visualization (format depends on visualizer type). |
required |
**kwargs
|
Any
|
Additional plotting parameters. |
{}
|
Returns:
| Type | Description |
|---|---|
Figure
|
Matplotlib Figure object. |
save_or_show(fig, filename)
Save figure to file or display it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fig
|
Figure
|
Matplotlib figure to save or show. |
required |
filename
|
str
|
Name of file to save (used if save_dir is set). |
required |
TrainingHistoryVisualizer
Bases: Visualizer
Visualizer for training history metrics over rounds. Plots loss curves, accuracy, or other metrics tracked during training.
plot(data, title='Training History', xlabel='Round', ylabel='Value', filename='training_history.png', **kwargs)
Plot training history metrics.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
dict[str, list[float]]
|
Dictionary mapping metric names to lists of values. Example: {"global_loss": [0.5, 0.3, 0.2], "party_0_loss": [0.6, 0.4, 0.3]} |
required |
title
|
str
|
Plot title. |
'Training History'
|
xlabel
|
str
|
X-axis label. |
'Round'
|
ylabel
|
str
|
Y-axis label. |
'Value'
|
filename
|
str
|
Filename for saving. |
'training_history.png'
|
**kwargs
|
Any
|
Additional matplotlib parameters. |
{}
|
Returns:
| Type | Description |
|---|---|
Figure
|
Matplotlib Figure object. |
ComparisonVisualizer
Bases: Visualizer
Visualizer for comparing metrics across different models or configurations. Creates bar plots, grouped comparisons, or side-by-side visualizations.
plot(data, title='Model Comparison', xlabel='Model', ylabel='Metric', filename='comparison.png', **kwargs)
Create a bar plot comparing metrics across models.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
dict[str, float]
|
Dictionary mapping model names to metric values. Example: {"Federated": 0.25, "Centralized": 0.20, "Local": 0.35} |
required |
title
|
str
|
Plot title. |
'Model Comparison'
|
xlabel
|
str
|
X-axis label. |
'Model'
|
ylabel
|
str
|
Y-axis label. |
'Metric'
|
filename
|
str
|
Filename for saving. |
'comparison.png'
|
**kwargs
|
Any
|
Additional matplotlib parameters. |
{}
|
Returns:
| Type | Description |
|---|---|
Figure
|
Matplotlib Figure object. |
DivergenceVisualizer
Bases: Visualizer
Visualizer for analyzing divergence between federated and centralized models. Specialized for federated learning experiments comparing model parameters and performance.
__init__(save_dir=None, figsize=(15, 10))
Initialize divergence visualizer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
save_dir
|
str | None
|
Directory to save plots. |
None
|
figsize
|
tuple[int, int]
|
Figure size (default larger for complex plots). |
(15, 10)
|
add_result(x_value, metrics_per_round)
Add experimental results for a specific x-axis value.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x_value
|
Any
|
X-axis value (e.g., number of parties, data size). |
required |
metrics_per_round
|
list[dict]
|
List of metric dictionaries, one per round. Expected keys in each dict: - "mse": Aggregated model MSE - "general_mse": Centralized model MSE - "normdiff": Parameter norm difference - "msediff": MSE difference - "mseratio": MSE ratio - "local_mse": List of local model MSEs (optional) - "local_normdiff": List of local norm differences (optional) - "local_msediff": List of local MSE differences (optional) - "local_mseratio": List of local MSE ratios (optional) |
required |
clear_results()
Clear stored results.
plot(x_label='X Value', title_suffix='Experiment', show_local_models=True, metric_configs=None)
Generate divergence plots for all metrics.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x_label
|
str
|
Label for x-axis. |
'X Value'
|
title_suffix
|
str
|
Suffix for plot titles. |
'Experiment'
|
show_local_models
|
bool
|
Whether to show individual local model metrics. |
True
|
metric_configs
|
list[tuple[str, str, str]] | None
|
List of (metric_key, label, filename) tuples. If None, uses default metrics. |
None
|
Returns:
| Type | Description |
|---|---|
list[Figure]
|
List of matplotlib Figure objects. |
PrivacyUtilityVisualizer
Bases: Visualizer
Plot the privacy/utility trade-off curve for differentially-private FL.
Visualises how model utility degrades as the privacy budget ε tightens —
the canonical figure of the DP-ML literature (e.g. Abadi et al., "Deep
Learning with Differential Privacy", ACM CCS 2016): utility on the y-axis
against ε on a logarithmic x-axis, with one line per mechanism and an
optional non-private baseline.
Smaller ε means stronger privacy and more noise, so utility curves
typically worsen to the left — the shape practitioners use to pick an
operating point.
plot(data, title='Privacy-Utility Trade-off', xlabel='Privacy budget ε (log scale)', ylabel='Utility (e.g. test MSE or accuracy)', filename='privacy_utility.png', baseline=None, lower_is_better=True, **kwargs)
Plot utility vs ε for one or more DP mechanisms.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
dict[str, dict[float, float]]
|
Mapping |
required |
title
|
str
|
Plot title. |
'Privacy-Utility Trade-off'
|
xlabel
|
str
|
X-axis label. |
'Privacy budget ε (log scale)'
|
ylabel
|
str
|
Y-axis label. |
'Utility (e.g. test MSE or accuracy)'
|
filename
|
str
|
Filename for saving. |
'privacy_utility.png'
|
baseline
|
float | None
|
Optional non-private utility, drawn as a dashed reference line. |
None
|
lower_is_better
|
bool
|
If |
True
|
**kwargs
|
Any
|
Additional matplotlib line parameters. |
{}
|
Returns:
| Type | Description |
|---|---|
Figure
|
Matplotlib Figure object. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If data is empty. |