Skip to content

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:~fed_playground.src.encryption.NoEncryption.

None
aggregation_strategy AggregationStrategy | None

Strategy for combining party updates. Defaults to :class:~fed_playground.src.aggregation.MeanAggregation.

None
n_features int

Number of input features. Required when data_loader is None (synthetic data generation).

0
n_samples int

Number of samples to generate. Required when data_loader is None.

0
model_class type[Model]

:class:~fed_playground.src.models.Model subclass to instantiate for each party and the global model.

LinearRegressionModel
model_params dict[str, Any] | None

Keyword arguments forwarded to model_class (besides input_dim).

None
data_loader DataLoader | None

Optional :class:~fed_playground.src.dataloader.DataLoader that provides training data. When None synthetic data is generated from n_samples and n_features.

None

Raises:

Type Description
ValueError

If n_parties < 1.

ValueError

If data_loader is None and either n_features or n_samples is 0.

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:

  1. Orchestrator broadcasts the current global model to all parties.
  2. Each party trains locally and its loss is recorded.
  3. Orchestrator aggregates party updates into a new global model.
  4. 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:setup has not been called.

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 (X_test, y_test) tuple to override the held-out test set created during :meth:setup.

None

Returns:

Type Description
dict[str, list]

History dictionary with keys "global_loss" and

dict[str, list]

"party_loss", each mapping to a list of per-round values.

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:

  1. :meth:distribute_model — broadcast the current global parameters to all registered parties.
  2. :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 the first aggregation round initialises the global model.

None

register_party(party)

Add a party to the list of participants.

Parameters:

Name Type Description Default
party Party

The :class:~fed_playground.src.party.Party to register.

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:

  1. Receives the global model from the :class:~fed_playground.src.orchestrator.Orchestrator.
  2. Fine-tunes that model on its local data.
  3. Returns (optionally encrypted) parameter updates.

Parameters:

Name Type Description Default
party_id int

Unique integer identifier for this party.

required
model Model

Initialised :class:~fed_playground.src.models.Model instance.

required
data tuple[ndarray, ndarray]

Tuple (X_train, y_train) of local training data.

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:~fed_playground.src.encryption.NoEncryption)

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:~fed_playground.src.models.Model.set_parameters.

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 the local training data is used.

None
y ndarray | None

Target vector for evaluation. When None the local training target is used.

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 (n_samples, n_features).

required
y ndarray

Target vector of shape (n_samples,).

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:get_parameters.

required

predict(X) abstractmethod

Compute predictions for the given feature matrix.

Parameters:

Name Type Description Default
X ndarray

Feature matrix of shape (n_samples, n_features).

required

Returns:

Type Description
ndarray

Predicted values of shape (n_samples,).

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 (n_samples, n_features).

required
y ndarray

True target values of shape (n_samples,).

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).

0.01
epochs int

Number of full passes over the training data per :meth:train call (default 1 for federated settings where data is small).

1

train(X, y)

Train for self.epochs passes using MSE gradient descent.

Parameters:

Name Type Description Default
X ndarray

Feature matrix of shape (n_samples, n_features).

required
y ndarray

Target vector of shape (n_samples,).

required

get_parameters()

Return [weights..., bias] as a flat array.

Returns:

Type Description
ndarray

1-D numpy array of length input_dim + 1.

set_parameters(params)

Load weights and bias from a flat parameter array.

Parameters:

Name Type Description Default
params ndarray

1-D array of length input_dim + 1; last element is bias.

required

predict(X)

Compute linear predictions.

Parameters:

Name Type Description Default
X ndarray

Feature matrix of shape (n_samples, n_features).

required

Returns:

Type Description
ndarray

Predicted values X @ weights + bias.

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).

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 (n_samples, n_features).

required
y ndarray

Target vector of shape (n_samples,).

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 input_dim + 1.

required

predict(X)

Compute linear predictions.

Parameters:

Name Type Description Default
X ndarray

Feature matrix of shape (n_samples, n_features).

required

Returns:

Type Description
ndarray

Predicted values of shape (n_samples,).

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).

0.1
epochs int

Passes over training data per :meth:train call (default 10).

10
threshold float

Decision boundary for binary prediction (default 0.5).

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 (n_samples, n_features).

required
y ndarray

Binary target vector of shape (n_samples,) with values in {0, 1}.

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 input_dim + 1.

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 (n_samples, n_features).

required

Returns:

Type Description
ndarray

Binary predictions of shape (n_samples,).

predict_proba(X)

Return class probabilities P(y=1 | x).

Parameters:

Name Type Description Default
X ndarray

Feature matrix of shape (n_samples, n_features).

required

Returns:

Type Description
ndarray

Probabilities of shape (n_samples,) in [0, 1].

evaluate(X, y)

Compute classification accuracy.

Parameters:

Name Type Description Default
X ndarray

Feature matrix of shape (n_samples, n_features).

required
y ndarray

True binary labels of shape (n_samples,).

required

Returns:

Type Description
float

Accuracy in [0, 1] (fraction of correct predictions).

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).

16
learning_rate float

Gradient descent step size (default 0.01).

0.01
epochs int

Passes over training data per :meth:train call (default 5).

5

train(X, y)

Train for self.epochs passes using MSE backpropagation.

Parameters:

Name Type Description Default
X ndarray

Feature matrix of shape (n_samples, n_features).

required
y ndarray

Target vector of shape (n_samples,).

required

get_parameters()

Return all parameters as a single flat array.

Returns:

Type Description
ndarray

1-D numpy array of length

ndarray

input_dim × hidden_dim + hidden_dim + hidden_dim + 1.

set_parameters(params)

Load all parameters from a flat array.

Parameters:

Name Type Description Default
params ndarray

1-D array produced by :meth:get_parameters.

required

predict(X)

Compute regression predictions.

Parameters:

Name Type Description Default
X ndarray

Feature matrix of shape (n_samples, n_features).

required

Returns:

Type Description
ndarray

Predicted values of shape (n_samples,).

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).

64
n_classes int

Number of output classes (default 10).

10
learning_rate float

SGD step size (default 0.01).

0.01
epochs int

Passes over the local data per :meth:train call (default 3).

3
batch_size int

Mini-batch size (default 64).

64

train(X, y)

Train with mini-batch SGD and cross-entropy loss.

Parameters:

Name Type Description Default
X ndarray

Feature matrix of shape (n_samples, input_dim).

required
y ndarray

Integer class labels of shape (n_samples,) with values in {0, …, n_classes - 1}.

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:get_parameters.

required

predict(X)

Return the predicted class index for each sample.

Parameters:

Name Type Description Default
X ndarray

Feature matrix of shape (n_samples, input_dim).

required

Returns:

Type Description
ndarray

Integer class predictions of shape (n_samples,).

evaluate(X, y)

Compute classification accuracy.

Parameters:

Name Type Description Default
X ndarray

Feature matrix of shape (n_samples, input_dim).

required
y ndarray

True integer class labels of shape (n_samples,).

required

Returns:

Type Description
float

Accuracy in [0, 1].

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 (n_samples, n_features).

required
y ndarray

Target vector of shape (n_samples,).

required

get_parameters()

Return [weights..., bias] as a flat array.

Returns:

Type Description
ndarray

1-D numpy array of length input_dim + 1.

set_parameters(params)

Load parameters from a flat array.

Parameters:

Name Type Description Default
params ndarray

1-D array of length input_dim + 1.

required

predict(X)

Compute linear predictions using the bias-augmented design matrix.

Parameters:

Name Type Description Default
X ndarray

Feature matrix of shape (n_samples, n_features).

required

Returns:

Type Description
ndarray

Predicted values of shape (n_samples,).

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 λ (default 0.01).

0.01
epochs int

Passes over the local data per :meth:train call (default 20).

20
seed int

RNG seed for the sample order (default 0).

0

train(X, y)

Run Pegasos sub-gradient updates over the local data.

Parameters:

Name Type Description Default
X ndarray

Feature matrix of shape (n_samples, n_features).

required
y ndarray

Binary labels of shape (n_samples,) with values in {0, 1}.

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 input_dim + 1.

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 (n_samples, n_features).

required

Returns:

Type Description
ndarray

Real-valued scores of shape (n_samples,).

predict(X)

Return hard binary predictions in {0, 1}.

Parameters:

Name Type Description Default
X ndarray

Feature matrix of shape (n_samples, n_features).

required

Returns:

Type Description
ndarray

Binary predictions of shape (n_samples,).

evaluate(X, y)

Compute classification accuracy.

Parameters:

Name Type Description Default
X ndarray

Feature matrix of shape (n_samples, n_features).

required
y ndarray

True binary labels of shape (n_samples,).

required

Returns:

Type Description
float

Accuracy in [0, 1].

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).

0.1
max_iter int

Maximum coordinate-descent sweeps per :meth:train (default 1000).

1000
tol float

Convergence tolerance on the max coefficient change (default 1e-6).

1e-06

train(X, y)

Fit by cyclic coordinate descent with soft-thresholding.

Parameters:

Name Type Description Default
X ndarray

Feature matrix of shape (n_samples, n_features).

required
y ndarray

Target vector of shape (n_samples,).

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 input_dim + 1.

required

predict(X)

Compute linear predictions X·weights + bias.

Parameters:

Name Type Description Default
X ndarray

Feature matrix of shape (n_samples, n_features).

required

Returns:

Type Description
ndarray

Predicted values of shape (n_samples,).

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).

0.1
l1_ratio float

Mix between L1 (1.0) and L2 (0.0) penalties (default 0.5).

0.5
max_iter int

Maximum coordinate-descent sweeps (default 1000).

1000
tol float

Convergence tolerance on the max coefficient change (default 1e-6).

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 (n_samples, n_features).

required
y ndarray

Target vector of shape (n_samples,).

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 input_dim + 1.

required

predict(X)

Compute linear predictions X·weights + bias.

Parameters:

Name Type Description Default
X ndarray

Feature matrix of shape (n_samples, n_features).

required

Returns:

Type Description
ndarray

Predicted values of shape (n_samples,).

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).

0.01
epochs int

Passes over the data per :meth:train call (default 50).

50
max_exponent float

Clip on the linear predictor to avoid exp overflow (default 30.0).

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 (n_samples, n_features).

required
y ndarray

Non-negative count targets of shape (n_samples,).

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 input_dim + 1.

required

predict(X)

Return the expected count μ = exp(w·x + b) (non-negative).

Parameters:

Name Type Description Default
X ndarray

Feature matrix of shape (n_samples, n_features).

required

Returns:

Type Description
ndarray

Predicted rates of shape (n_samples,).

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 (n_samples, n_features).

required
y ndarray

True counts of shape (n_samples,).

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).

1.0
learning_rate float

Gradient-descent step size (default 0.01).

0.01
epochs int

Passes over the data per :meth:train call (default 100).

100

train(X, y)

Fit by gradient descent on the Huber loss.

Parameters:

Name Type Description Default
X ndarray

Feature matrix of shape (n_samples, n_features).

required
y ndarray

Target vector of shape (n_samples,).

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 input_dim + 1.

required

predict(X)

Compute linear predictions X·weights + bias.

Parameters:

Name Type Description Default
X ndarray

Feature matrix of shape (n_samples, n_features).

required

Returns:

Type Description
ndarray

Predicted values of shape (n_samples,).

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:~fed_playground.src.encryption.NoEncryption,

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). Must satisfy 2 * trim_fraction < 1.

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 f (default 1).

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 n_selected.

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).

100
eps float

Smoothing floor on distances / convergence tolerance (default 1e-8).

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:

  1. Selection — recursively apply Krum to pick m = n - 2f candidate updates (select the Krum-best, remove it, repeat), discarding the most outlying updates.
  2. Coordinate-wise trimmed mean — over the selected set, for each coordinate keep the β = m - 2f values 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 f (default 1).

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 k (default 5); clamped to the party count. Choose k > 2f to tolerate f Byzantine parties.

5
seed int

RNG seed for the random partition (default 0).

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 τ on each update's distance to the center (default 1.0).

1.0
n_iters int

Number of refinement iterations L (default 3).

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:encrypt.

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).

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; n_shares - 1 are secret (default 2 → one secret mask, one public share).

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 params - Σ(secret shares) — statistically

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 ε > 0 (smaller = more private, more noise).

1.0
sensitivity float

L1-sensitivity bound of one update (default 1.0).

1.0
clip bool

If True, clip each update to L1 norm sensitivity so the DP guarantee is actually met (default False, matching the plaintext-magnitude behaviour of :class:GaussianDPEncryption).

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:encrypt calls 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). Does not affect correctness.

10.0
seed int

Base RNG seed for reproducible pairwise masks (default 0).

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

Bases: Attack

Honest baseline — every party reports truthfully.

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).

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).

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).

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). ponytail: the paper derives z from (n, f) via a normal table; the constant default is the simple knob — set it per (n, f) for the optimal attack.

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

Model subclasses to sweep (default [LinearRegressionModel]).

None
aggregations list[AggregationStrategy] | None

aggregation strategy instances (default [MeanAggregation()]).

None
encryptions list[EncryptionScheme] | None

encryption scheme instances (default [NoEncryption()]).

None
attacks list[Attack] | None

attack instances (default [NoAttack()]).

None
n_byzantine tuple[int, ...]

numbers of Byzantine parties to sweep (default (0,)).

(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 each run uses the same seeded synthetic data, so rows are comparable.

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:run_benchmark.

required
index str

pivot row axis (default "aggregation").

'aggregation'
columns str

pivot column axis (default "attack").

'attack'
values str

cell metric (default "final_loss").

'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 | sklearn | openml | csv.

'synthetic'
**opts object

per-kind options — synthetic: n_samples, n_features, seed; sklearn: name in {breast_cancer, diabetes} (offline); openml: MNIST via network (no opts); csv: path (and optional target).

{}

Returns:

Type Description
DataLoader

A ready-to-use DataLoader.

Raises:

Type Description
ValueError

on an unknown kind.

ImportError

if sklearn is needed but not installed ([examples] extra).

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 (n_samples, n_features).

required
y ndarray

Integer class labels of shape (n_samples,).

required
n_parties int

Number of parties to partition across.

required
alpha float

Dirichlet concentration; lower = more non-IID (default 0.5).

0.5
random_seed int | None

Seed for reproducibility.

42

Returns:

Type Description
list[tuple[ndarray, ndarray]]

List of n_parties (X_i, y_i) tuples (a party may be empty under

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 mechanism_name -> {epsilon: utility}. Example: {"Laplace": {0.2: 74.0, 1.0: 3.0, 5.0: 0.13}}.

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 annotate that lower utility is better (MSE/deviance); set False for accuracy-style metrics.

True
**kwargs Any

Additional matplotlib line parameters.

{}

Returns:

Type Description
Figure

Matplotlib Figure object.

Raises:

Type Description
ValueError

If data is empty.