Backend-agnostic: sites are reached through contribute() and the
total is recovered through the decrypt() generic, so the
same body works over CKKSMaster and ThresholdMaster.
Arguments
- master
a Master, wired to its workers — with
set_workers()for a CKKSMaster, or bymake_threshold_master(), which returns one already wired.- theta
the current parameter value (passed through to each worker).
Details
The master broadcasts theta to each worker — and only theta;
each worker supplies its own data. Each worker returns
contribution_fn(data, theta) and the result is
encrypted under the master's public key. The master sums the
encrypted contributions homomorphically and decrypts the total.
This is the topology that mirrors how distcomp, DataSHIELD, and similar federated-analysis frameworks actually deploy: a flat fan-out / fan-in. With a single-decrypter master, the master could in principle decrypt individual contributions; the cryptographic guarantee strengthens when paired with threshold key generation (no single party holds the secret key).
Each worker returns an already encrypted contribution (see
contribute()), so no individual site's cleartext value reaches the
master. Only the aggregate is decrypted.
Two failure modes, deliberately distinct. If a worker returns NA,
theta is non-evaluable there and this function returns NA_real_,
which optimizers read as "back off and try elsewhere". If a worker
signals site_unavailable(), it could not be reached at all; that
condition propagates and aborts the round, because continuing would
sum over a different set of sites and silently change the objective
between optimizer iterations.
NA is the one value that travels in the clear, since CKKS cannot
represent it. A master that chooses theta adaptively therefore
learns which parameter values break which site — a residual side
channel that no amount of encryption here removes.
