tff.utils.secure_quantized_sum

Quantizes and sums values securely.

Provided client_value can be either a Tensor or a nested structure of Tensors. If it is a nested structure, lower_bound and upper_bound must be either both scalars, or both have the same structure as client_value, with each element being a scalar, representing the bounds to be used for each corresponding Tensor in client_value.

This method converts each Tensor in provided client_value to appropriate format and uses the tff.federated_secure_sum operator to realize the sum.

The dtype of Tensors in provided client_value can be one of [tf.int32, tf.int64, tf.float32, tf.float64].

If the dtype of client_value is tf.int32 or tf.int64, the summation is possibly exact, depending on lower_bound and upper_bound: In the case that upper_bound - lower_bound < 2**32, the summation will be exact. If it is not, client_value will be quantized to precision of 32 bits, so the worst case error introduced for the value of each client will be approximately (upper_bound - lower_bound) / 2**32. Deterministic rounding to nearest value is used in such cases.

If the dtype of client_value is tf.float32 or tf.float64, the summation is generally not accurate up to full floating point precision. Instead, the values are first clipped to the [lower_bound, upper_bound] range. These values are then uniformly quantized to 32 bit resolution, using deterministic rounding to round the values to the quantization points. Rounding happens roughly as follows (implementation is a bit more complex to mitigate numerical stability issues):

values = tf.round(
    (client_value - lower_bound) * ((2**32 - 1) / (upper_bound - lower_bound))

After summation, the inverse operation if performed, so the return value is of the same dtype as the input client_value.

In terms of accuracy, it is safe to assume accuracy within 7-8 significant digits for tf.float32 inputs, and 8-9 significant digits for tf.float64 inputs, where the significant digits refer to precision relative to the range of the provided bounds. Thus, these bounds should not be set extremely wide. Accuracy losses arise due to (1) quantization within the given clipping range, (2) float precision of final outputs (e.g. tf.float32 has 23 bits in its mantissa), and (3) precision losses that arise in doing math on tf.float32 and tf.float64 inputs.

As a concrete example, if the range is +/- 1000, errors up to 1e-4 per element should be expected for tf.float32 and up to 1e-5 for tf.float64.

client_value A tff.Value placed at tff.CLIENTS.
lower_bound The smallest possible value for client_value (inclusive). Values smaller than this bound will be clipped. Must be either a scalar or a nested structure of scalars, matching the structure of client_value. Must be either a Python constant or a tff.Value placed at tff.SERVER, with dtype matching that of client_value.
upper_bound The largest possible value for client_value (inclusive). Values greater than this bound will be clipped. Must be either a scalar or a nested structure of scalars, matching the structure of client_value. Must be either a Python constant or a tff.Value placed at tff.SERVER, with dtype matching that of client_value.

Summed client_value placed at tff.SERVER, of the same dtype as client_value.

TypeError (or its subclasses): If input arguments do not satisfy the type constraints specified above.