View source on GitHub
|
A data class for computations containing a single logical aggregation.
tff.framework.MergeableCompForm(
*,
up_to_merge: tff.Computation,
merge: tff.Computation,
after_merge: tff.Computation
)
MergeableCompForm contains three member computations, up_to_merge
and merge, and after_merge. A computation in MergeableCompForm is
defined to be equivalent to invoking up_to_merge on subsets of
CLIENTS-placed arguments in sequence, invoking merge on the stream of
these results, and passing the resulting value (placed at tff.SERVER) to
after_merge, in addition to the original argument to up_to_merge.
In the case of a no-arg up_to_merge computation, after_merge should accept
only the result of merge. up_to_merge should return a single
tff.SERVER-placed value.
We require that computations in MergeableCompForm contain no AST
nodes with signatures taking arguments at tff.CLIENTS and producing results
at tff.SERVER.
MergeableCompForm computations are often generated by aligning a computation
containing a single logical aggregation on this aggregation, and splitting it
along its merge boundary. That is, since merge can be invoked repeatedly
without changing the results of the computation, a computation of the form:
@tff.federated_computation(...)
def single_aggregation(arg):
result_at_clients = work(arg)
agg_result = tff.federated_aggregate(
result_at_clients, zero, accumulate, merge, report)
return postprocess(arg, agg_result)
can be represented as the MergeableCompForm triplet:
@tff.federated_computation(tff.AbstractType('T'))
def up_to_merge(arg):
result_at_clients = work(arg)
agg_result = tff.federated_aggregate(
result_at_clients, accumulate_zero, accumulate, merge, identity_report)
return agg_result
@tff.federated_computation([up_to_merge.type_signature.result.member,
up_to_merge.type_signature.result.member])
def merge(arg):
return merge(arg[0], arg[1])
@tff.federated_computation(
tff.AbstractType('T'),
tff.FederatedType(merge.type_signature.result, tff.SERVER),
)
def after_merge(original_arg, merged_result):
return postprocess(original_arg, merged_result)
A fair amount of complexity can be hidden in postprocess; it could, for
example, compute some value on clients based on the result of the aggregation.
But the restriction that after_merge can contain no aggregations ensures
that after_merge can also be executed in a subround fashion: a
tff.CLIENTS-placed result can only depend on its own local state and the
result of the aggregation, while a tff.SERVER-placed result can only depend
on the result of the single aggregation or a tff.SERVER-placed value.
View source on GitHub