def sequence_reduce(self, value, zero, op): """Implements `sequence_reduce` as defined in `api/intrinsics.py`.""" value = value_impl.to_value(value, None, self._context_stack) zero = value_impl.to_value(zero, None, self._context_stack) op = value_impl.to_value(op, None, self._context_stack) # Check if the value is a federated sequence that should be reduced # under a `federated_map`. if value.type_signature.is_federated(): is_federated_sequence = True value_member_type = value.type_signature.member value_member_type.check_sequence() zero_member_type = zero.type_signature.member else: is_federated_sequence = False value.type_signature.check_sequence() value = value_impl.ValueImpl.get_comp(value) zero = value_impl.ValueImpl.get_comp(zero) op = value_impl.ValueImpl.get_comp(op) if not is_federated_sequence: comp = building_block_factory.create_sequence_reduce(value, zero, op) comp = self._bind_comp_as_reference(comp) return value_impl.ValueImpl(comp, self._context_stack) else: ref_type = computation_types.StructType( [value_member_type, zero_member_type]) ref = building_blocks.Reference('arg', ref_type) arg1 = building_blocks.Selection(ref, index=0) arg2 = building_blocks.Selection(ref, index=1) call = building_block_factory.create_sequence_reduce(arg1, arg2, op) fn = building_blocks.Lambda(ref.name, ref.type_signature, call) fn_value_impl = value_impl.ValueImpl(fn, self._context_stack) args = building_blocks.Struct([value, zero]) return self.federated_map(fn_value_impl, args)
def sequence_reduce(self, value, zero, op): """Implements `sequence_reduce` as defined in `api/intrinsics.py`.""" value = value_impl.to_value(value, None, self._context_stack) zero = value_impl.to_value(zero, None, self._context_stack) op = value_impl.to_value(op, None, self._context_stack) # Check if the value is a federated sequence that should be reduced # under a `federated_map`. if value.type_signature.is_federated(): value_sequence_type = value.type_signature.member needs_map = True else: value_sequence_type = value.type_signature needs_map = False value_sequence_type.check_sequence() element_type = value_sequence_type.element op_type_expected = type_factory.reduction_op(zero.type_signature, element_type) if not op_type_expected.is_assignable_from(op.type_signature): raise TypeError('Expected an operator of type {}, got {}.'.format( op_type_expected, op.type_signature)) value = value_impl.ValueImpl.get_comp(value) zero = value_impl.ValueImpl.get_comp(zero) op = value_impl.ValueImpl.get_comp(op) if not needs_map: comp = building_block_factory.create_sequence_reduce( value, zero, op) comp = self._bind_comp_as_reference(comp) return value_impl.ValueImpl(comp, self._context_stack) else: ref = building_blocks.Reference('arg', value_sequence_type) call = building_block_factory.create_sequence_reduce(ref, zero, op) fn = building_blocks.Lambda(ref.name, ref.type_signature, call) fn_impl = value_impl.ValueImpl(fn, self._context_stack) return self.federated_map(fn_impl, value)
def sequence_reduce(self, value, zero, op): """Implements `sequence_reduce` as defined in `api/intrinsics.py`. Args: value: As in `api/intrinsics.py`. zero: As in `api/intrinsics.py`. op: As in `api/intrinsics.py`. Returns: As in `api/intrinsics.py`. Raises: TypeError: As in `api/intrinsics.py`. """ value = value_impl.to_value(value, None, self._context_stack) zero = value_impl.to_value(zero, None, self._context_stack) op = value_impl.to_value(op, None, self._context_stack) if isinstance(value.type_signature, computation_types.SequenceType): element_type = value.type_signature.element else: py_typecheck.check_type(value.type_signature, computation_types.FederatedType) py_typecheck.check_type(value.type_signature.member, computation_types.SequenceType) element_type = value.type_signature.member.element op_type_expected = type_factory.reduction_op(zero.type_signature, element_type) if not type_utils.is_assignable_from(op_type_expected, op.type_signature): raise TypeError('Expected an operator of type {}, got {}.'.format( op_type_expected, op.type_signature)) value = value_impl.ValueImpl.get_comp(value) zero = value_impl.ValueImpl.get_comp(zero) op = value_impl.ValueImpl.get_comp(op) if isinstance(value.type_signature, computation_types.SequenceType): return building_block_factory.create_sequence_reduce( value, zero, op) else: value_type = computation_types.SequenceType(element_type) intrinsic_type = computation_types.FunctionType(( value_type, zero.type_signature, op.type_signature, ), op.type_signature.result) intrinsic = building_blocks.Intrinsic( intrinsic_defs.SEQUENCE_REDUCE.uri, intrinsic_type) ref = building_blocks.Reference('arg', value_type) tup = building_blocks.Tuple((ref, zero, op)) call = building_blocks.Call(intrinsic, tup) fn = building_blocks.Lambda(ref.name, ref.type_signature, call) fn_impl = value_impl.ValueImpl(fn, self._context_stack) if value.type_signature.placement is placements.SERVER: return self.federated_apply(fn_impl, value) elif value.type_signature.placement is placements.CLIENTS: return self.federated_map(fn_impl, value) else: raise TypeError('Unsupported placement {}.'.format( value.type_signature.placement))
def sequence_reduce(self, value, zero, op): """Implements `sequence_reduce` as defined in `api/intrinsics.py`.""" value = value_impl.to_value(value, None, self._context_stack) zero = value_impl.to_value(zero, None, self._context_stack) op = value_impl.to_value(op, None, self._context_stack) if value.type_signature.is_sequence(): element_type = value.type_signature.element else: py_typecheck.check_type(value.type_signature, computation_types.FederatedType) py_typecheck.check_type(value.type_signature.member, computation_types.SequenceType) element_type = value.type_signature.member.element op_type_expected = type_factory.reduction_op(zero.type_signature, element_type) if not op_type_expected.is_assignable_from(op.type_signature): raise TypeError('Expected an operator of type {}, got {}.'.format( op_type_expected, op.type_signature)) value = value_impl.ValueImpl.get_comp(value) zero = value_impl.ValueImpl.get_comp(zero) op = value_impl.ValueImpl.get_comp(op) if value.type_signature.is_sequence(): comp = building_block_factory.create_sequence_reduce( value, zero, op) comp = self._bind_comp_as_reference(comp) return value_impl.ValueImpl(comp, self._context_stack) else: value_type = computation_types.SequenceType(element_type) intrinsic_type = computation_types.FunctionType(( value_type, zero.type_signature, op.type_signature, ), op.type_signature.result) intrinsic = building_blocks.Intrinsic( intrinsic_defs.SEQUENCE_REDUCE.uri, intrinsic_type) ref = building_blocks.Reference('arg', value_type) tup = building_blocks.Struct((ref, zero, op)) call = building_blocks.Call(intrinsic, tup) fn = building_blocks.Lambda(ref.name, ref.type_signature, call) fn_impl = value_impl.ValueImpl(fn, self._context_stack) return self.federated_map(fn_impl, value)
def sequence_reduce(value, zero, op): """Reduces a TFF sequence `value` given a `zero` and reduction operator `op`. This method reduces a set of elements of a TFF sequence `value`, using a given `zero` in the algebra (i.e., the result of reducing an empty sequence) of some type `U`, and a reduction operator `op` with type signature `(<U,T> -> U)` that incorporates a single `T`-typed element of `value` into the `U`-typed result of partial reduction. In the special case of `T` equal to `U`, this corresponds to the classical notion of reduction of a set using a commutative associative binary operator. The generalized reduction (with `T` not equal to `U`) requires that repeated application of `op` to reduce a set of `T` always yields the same `U`-typed result, regardless of the order in which elements of `T` are processed in the course of the reduction. One can also invoke `sequence_reduce` on a federated sequence, in which case the reductions are performed pointwise; under the hood, we construct an expression of the form `federated_map(x -> sequence_reduce(x, zero, op), value)`. See also the discussion on `sequence_map`. Note: When applied to a federated value this function does the reduce point-wise. Args: value: A value that is either a TFF sequence, or a federated sequence. zero: The result of reducing a sequence with no elements. op: An operator with type signature `(<U,T> -> U)`, where `T` is the type of the elements of the sequence, and `U` is the type of `zero` to be used in performing the reduction. Returns: The `U`-typed result of reducing elements in the sequence, or if the `value` is federated, a federated `U` that represents the result of locally reducing each member constituent of `value`. Raises: TypeError: If the arguments are not of the types specified above. """ value = value_impl.to_value(value, None) zero = value_impl.to_value(zero, None) op = value_impl.to_value(op, None) # Check if the value is a federated sequence that should be reduced # under a `federated_map`. if value.type_signature.is_federated(): is_federated_sequence = True value_member_type = value.type_signature.member value_member_type.check_sequence() zero_member_type = zero.type_signature.member else: is_federated_sequence = False value.type_signature.check_sequence() if not is_federated_sequence: comp = building_block_factory.create_sequence_reduce( value.comp, zero.comp, op.comp) comp = _bind_comp_as_reference(comp) return value_impl.Value(comp) else: ref_type = computation_types.StructType( [value_member_type, zero_member_type]) ref = building_blocks.Reference('arg', ref_type) arg1 = building_blocks.Selection(ref, index=0) arg2 = building_blocks.Selection(ref, index=1) call = building_block_factory.create_sequence_reduce( arg1, arg2, op.comp) fn = building_blocks.Lambda(ref.name, ref.type_signature, call) fn_value_impl = value_impl.Value(fn) args = building_blocks.Struct([value.comp, zero.comp]) return federated_map(fn_value_impl, args)